Commit 352941b030

Jakub Konka <kubkon@jakubkonka.com>
2024-02-01 12:13:46
macho: emit incomplete object file
1 parent f2e249e
Changed files (2)
src/link/MachO/relocatable.zig
@@ -12,7 +12,7 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
 
     if (module_obj_path) |path| try positionals.append(.{ .path = path });
 
-    if (positionals.items.len == 1) {
+    if (macho_file.getZigObject() == null and positionals.items.len == 1) {
         // Instead of invoking a full-blown `-r` mode on the input which sadly will strip all
         // debug info segments/sections (this is apparently by design by Apple), we copy
         // the *only* input file over.
@@ -26,6 +26,11 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
         return;
     }
 
+    if (macho_file.getZigObject() != null and positionals.items.len > 0) {
+        try macho_file.reportUnexpectedError("TODO: build-obj for ZigObject and input object files", .{});
+        return error.FlushFailure;
+    }
+
     for (positionals.items) |obj| {
         macho_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
             error.MalformedObject,
@@ -46,8 +51,8 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
 
     try macho_file.addUndefinedGlobals();
     try macho_file.resolveSymbols();
-    markExports(macho_file);
-    claimUnresolved(macho_file);
+    try markExports(macho_file);
+    try claimUnresolved(macho_file);
     try initOutputSections(macho_file);
     try macho_file.sortSections();
     try macho_file.addAtomsToSections();
@@ -109,8 +114,13 @@ pub fn flush(macho_file: *MachO, comp: *Compilation, module_obj_path: ?[]const u
     try writeHeader(macho_file, ncmds, sizeofcmds);
 }
 
-fn markExports(macho_file: *MachO) void {
-    for (macho_file.objects.items) |index| {
+fn markExports(macho_file: *MachO) error{OutOfMemory}!void {
+    var objects = try std.ArrayList(File.Index).initCapacity(macho_file.base.comp.gpa, macho_file.objects.items.len + 1);
+    defer objects.deinit();
+    if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
+    objects.appendSliceAssumeCapacity(macho_file.objects.items);
+
+    for (objects.items) |index| {
         for (macho_file.getFile(index).?.getSymbols()) |sym_index| {
             const sym = macho_file.getSymbol(sym_index);
             const file = sym.getFile(macho_file) orelse continue;
@@ -122,13 +132,22 @@ fn markExports(macho_file: *MachO) void {
     }
 }
 
-fn claimUnresolved(macho_file: *MachO) void {
-    for (macho_file.objects.items) |index| {
-        const object = macho_file.getFile(index).?.object;
+fn claimUnresolved(macho_file: *MachO) error{OutOfMemory}!void {
+    var objects = try std.ArrayList(File.Index).initCapacity(macho_file.base.comp.gpa, macho_file.objects.items.len + 1);
+    defer objects.deinit();
+    if (macho_file.getZigObject()) |zo| objects.appendAssumeCapacity(zo.index);
+    objects.appendSliceAssumeCapacity(macho_file.objects.items);
 
-        for (object.symbols.items, 0..) |sym_index, i| {
+    for (objects.items) |index| {
+        const file = macho_file.getFile(index).?;
+
+        for (file.getSymbols(), 0..) |sym_index, i| {
             const nlist_idx = @as(Symbol.Index, @intCast(i));
-            const nlist = object.symtab.items(.nlist)[nlist_idx];
+            const nlist = switch (file) {
+                .object => |x| x.symtab.items(.nlist)[nlist_idx],
+                .zig_object => |x| x.symtab.items(.nlist)[nlist_idx],
+                else => unreachable,
+            };
             if (!nlist.ext()) continue;
             if (!nlist.undf()) continue;
 
@@ -290,7 +309,7 @@ fn writeAtoms(macho_file: *MachO) !void {
             assert(atom.flags.alive);
             const off = math.cast(usize, atom.value - header.addr) orelse return error.Overflow;
             const atom_size = math.cast(usize, atom.size) orelse return error.Overflow;
-            try atom.getFile(macho_file).object.getAtomData(atom.*, code[off..][0..atom_size]);
+            try atom.getData(macho_file, code[off..][0..atom_size]);
             try atom.writeRelocs(macho_file, code[off..][0..atom_size], &relocs);
         }
 
@@ -501,5 +520,6 @@ const trace = @import("../../tracy.zig").trace;
 
 const Atom = @import("Atom.zig");
 const Compilation = @import("../../Compilation.zig");
+const File = @import("file.zig").File;
 const MachO = @import("../MachO.zig");
 const Symbol = @import("Symbol.zig");
src/link/MachO.zig
@@ -285,8 +285,7 @@ pub fn createEmpty(
                     };
                     try self.d_sym.?.initMetadata(self);
                 } else {
-                    try self.reportUnexpectedError("TODO: implement generating and emitting __DWARF in .o file", .{});
-                    return error.Unexpected;
+                    @panic("TODO: implement generating and emitting __DWARF in .o file");
                 },
                 .code_view => unreachable,
             }
@@ -2025,7 +2024,7 @@ pub fn sortSections(self: *MachO) !void {
 
         for (zo.symtab.items(.nlist)) |*sym| {
             if (sym.sect()) {
-                sym.n_sect = backlinks[sym.n_sect];
+                sym.n_sect = backlinks[sym.n_sect - 1] + 1;
             }
         }
 
@@ -3391,8 +3390,6 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
                 .prot = macho.PROT.READ | macho.PROT.WRITE,
             });
         }
-    } else {
-        @panic("TODO initMetadata when relocatable");
     }
 
     const appendSect = struct {
@@ -3415,7 +3412,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
             },
             .flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
         });
-        appendSect(self, self.zig_text_sect_index.?, self.zig_text_seg_index.?);
+        if (!self.base.isRelocatable()) appendSect(self, self.zig_text_sect_index.?, self.zig_text_seg_index.?);
     }
 
     if (!self.base.isRelocatable()) {
@@ -3427,33 +3424,35 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
 
     {
         self.zig_const_sect_index = try self.addSection("__CONST_ZIG", "__const_zig", .{});
-        appendSect(self, self.zig_const_sect_index.?, self.zig_const_seg_index.?);
+        if (!self.base.isRelocatable()) appendSect(self, self.zig_const_sect_index.?, self.zig_const_seg_index.?);
     }
 
     {
         self.zig_data_sect_index = try self.addSection("__DATA_ZIG", "__data_zig", .{});
-        appendSect(self, self.zig_data_sect_index.?, self.zig_data_seg_index.?);
+        if (!self.base.isRelocatable()) appendSect(self, self.zig_data_sect_index.?, self.zig_data_seg_index.?);
     }
 
     {
         self.zig_bss_sect_index = try self.addSection("__BSS_ZIG", "__bss_zig", .{
             .flags = macho.S_ZEROFILL,
         });
-        appendSect(self, self.zig_bss_sect_index.?, self.zig_bss_seg_index.?);
+        if (!self.base.isRelocatable()) appendSect(self, self.zig_bss_sect_index.?, self.zig_bss_seg_index.?);
     }
 }
 
 pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {
     const sect = &self.sections.items(.header)[sect_index];
-    const seg_id = self.sections.items(.segment_id)[sect_index];
-    const seg = &self.segments.items[seg_id];
 
     if (needed_size > self.allocatedSize(sect.offset) and !sect.isZerofill()) {
         const existing_size = sect.size;
         sect.size = 0;
 
         // Must move the entire section.
-        const new_offset = self.findFreeSpace(needed_size, self.getPageSize());
+        const alignment = if (self.base.isRelocatable())
+            try math.powi(u32, 2, sect.@"align")
+        else
+            self.getPageSize();
+        const new_offset = self.findFreeSpace(needed_size, alignment);
 
         log.debug("new '{s},{s}' file offset 0x{x} to 0x{x}", .{
             sect.segName(),
@@ -3465,26 +3464,32 @@ pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {
         try self.copyRangeAllZeroOut(sect.offset, new_offset, existing_size);
 
         sect.offset = @intCast(new_offset);
-        seg.fileoff = new_offset;
     }
 
     sect.size = needed_size;
-    if (!sect.isZerofill()) {
-        seg.filesize = needed_size;
-    }
 
-    const mem_capacity = self.allocatedVirtualSize(seg.vmaddr);
-    if (needed_size > mem_capacity) {
-        var err = try self.addErrorWithNotes(2);
-        try err.addMsg(self, "fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
-            seg_id,
-            seg.segName(),
-        });
-        try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
-        try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
-    }
+    if (!self.base.isRelocatable()) {
+        const seg_id = self.sections.items(.segment_id)[sect_index];
+        const seg = &self.segments.items[seg_id];
+        seg.fileoff = sect.offset;
 
-    seg.vmsize = needed_size;
+        if (!sect.isZerofill()) {
+            seg.filesize = needed_size;
+        }
+
+        const mem_capacity = self.allocatedVirtualSize(seg.vmaddr);
+        if (needed_size > mem_capacity) {
+            var err = try self.addErrorWithNotes(2);
+            try err.addMsg(self, "fatal linker error: cannot expand segment seg({d})({s}) in virtual memory", .{
+                seg_id,
+                seg.segName(),
+            });
+            try err.addNote(self, "TODO: emit relocations to memory locations in self-hosted backends", .{});
+            try err.addNote(self, "as a workaround, try increasing pre-allocated virtual memory of each segment", .{});
+        }
+
+        seg.vmsize = needed_size;
+    }
 }
 
 pub fn getTarget(self: MachO) std.Target {