Commit 853ca403c4

Jakub Konka <kubkon@jakubkonka.com>
2024-07-12 15:56:26
macho: bring back relocatable mode for ZigObject
1 parent cba04ff
Changed files (2)
src/link/MachO/relocatable.zig
@@ -372,6 +372,7 @@ fn calcSectionSizes(macho_file: *MachO) !void {
     if (macho_file.getZigObject()) |zo| {
         // TODO this will create a race
         zo.calcNumRelocs(macho_file);
+        zo.calcSymtabSize(macho_file);
     }
 
     if (macho_file.eh_frame_sect_index) |_| {
@@ -390,7 +391,7 @@ fn calcSectionSizes(macho_file: *MachO) !void {
     if (macho_file.unwind_info_sect_index) |_| {
         calcCompactUnwindSize(macho_file);
     }
-    calcSymtabSize(macho_file);
+    try calcSymtabSize(macho_file);
 }
 
 fn calcSectionSize(macho_file: *MachO, sect_id: u8) void {
@@ -445,19 +446,27 @@ fn calcCompactUnwindSize(macho_file: *MachO) void {
     sect.@"align" = 3;
 }
 
-fn calcSymtabSize(macho_file: *MachO) void {
+fn calcSymtabSize(macho_file: *MachO) error{OutOfMemory}!void {
     const tracy = trace(@src());
     defer tracy.end();
 
+    const gpa = macho_file.base.comp.gpa;
+
     var nlocals: u32 = 0;
     var nstabs: u32 = 0;
     var nexports: u32 = 0;
     var nimports: u32 = 0;
     var strsize: u32 = 1;
 
-    for (macho_file.objects.items) |index| {
-        const object = macho_file.getFile(index).?.object;
-        const ctx = &object.output_symtab_ctx;
+    var objects = try std.ArrayList(File.Index).initCapacity(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| {
+        const ctx = switch (macho_file.getFile(index).?) {
+            inline else => |x| &x.output_symtab_ctx,
+        };
         ctx.ilocal = nlocals;
         ctx.istab = nstabs;
         ctx.iexport = nexports;
@@ -470,9 +479,10 @@ fn calcSymtabSize(macho_file: *MachO) void {
         strsize += ctx.strsize;
     }
 
-    for (macho_file.objects.items) |index| {
-        const object = macho_file.getFile(index).?.object;
-        const ctx = &object.output_symtab_ctx;
+    for (objects.items) |index| {
+        const ctx = switch (macho_file.getFile(index).?) {
+            inline else => |x| &x.output_symtab_ctx,
+        };
         ctx.istab += nlocals;
         ctx.iexport += nlocals + nstabs;
         ctx.iimport += nlocals + nstabs + nexports;
@@ -645,6 +655,11 @@ fn writeSections(macho_file: *MachO) !void {
         macho_file.getFile(index).?.writeSymtab(macho_file, macho_file);
     }
 
+    if (macho_file.getZigObject()) |zo| {
+        try zo.writeRelocs(macho_file);
+        zo.writeSymtab(macho_file, macho_file);
+    }
+
     if (macho_file.eh_frame_sect_index) |_| {
         try writeEhFrame(macho_file);
     }
src/link/MachO/ZigObject.zig
@@ -437,9 +437,48 @@ pub fn calcNumRelocs(self: *ZigObject, macho_file: *MachO) void {
     for (self.getAtoms()) |atom_index| {
         const atom = self.getAtom(atom_index) orelse continue;
         if (!atom.flags.alive) continue;
-        if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
         const header = &macho_file.sections.items(.header)[atom.out_n_sect];
-        header.nreloc += atom.calcNumRelocs(macho_file);
+        if (header.isZerofill()) continue;
+        if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
+        const nreloc = atom.calcNumRelocs(macho_file);
+        atom.addExtra(.{ .rel_out_index = header.nreloc, .rel_out_count = nreloc }, macho_file);
+        header.nreloc += nreloc;
+    }
+}
+
+pub fn writeRelocs(self: *ZigObject, macho_file: *MachO) !void {
+    const gpa = macho_file.base.comp.gpa;
+
+    for (self.getAtoms()) |atom_index| {
+        const atom = self.getAtom(atom_index) orelse continue;
+        if (!atom.flags.alive) continue;
+        const header = macho_file.sections.items(.header)[atom.out_n_sect];
+        const relocs = macho_file.sections.items(.relocs)[atom.out_n_sect].items;
+        if (header.isZerofill()) continue;
+        if (!macho_file.isZigSection(atom.out_n_sect) and !macho_file.isDebugSection(atom.out_n_sect)) continue;
+        if (atom.getRelocs(macho_file).len == 0) continue;
+        const extra = atom.getExtra(macho_file);
+        const atom_size = std.math.cast(usize, atom.size) orelse return error.Overflow;
+        const code = try gpa.alloc(u8, atom_size);
+        defer gpa.free(code);
+        self.getAtomData(macho_file, atom.*, code) catch |err| switch (err) {
+            error.InputOutput => {
+                try macho_file.reportUnexpectedError("fetching code for '{s}' failed", .{
+                    atom.getName(macho_file),
+                });
+                return error.FlushFailure;
+            },
+            else => |e| {
+                try macho_file.reportUnexpectedError("unexpected error while fetching code for '{s}': {s}", .{
+                    atom.getName(macho_file),
+                    @errorName(e),
+                });
+                return error.FlushFailure;
+            },
+        };
+        const file_offset = header.offset + atom.value;
+        try atom.writeRelocs(macho_file, code, relocs[extra.rel_out_index..][0..extra.rel_out_count]);
+        try macho_file.base.file.?.pwriteAll(code, file_offset);
     }
 }