Commit a503724801

Jakub Konka <kubkon@jakubkonka.com>
2023-04-04 19:31:26
macho: reapply relocation dirtying logic from coff linker
1 parent f372995
Changed files (3)
src/link/MachO/Atom.zig
@@ -179,12 +179,15 @@ pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !
     try gop.value_ptr.append(gpa, binding);
 }
 
-pub fn resolveRelocations(macho_file: *MachO, atom_index: Index, relocs: []Relocation, code: []u8) void {
+pub fn resolveRelocations(
+    macho_file: *MachO,
+    atom_index: Index,
+    relocs: []*const Relocation,
+    code: []u8,
+) void {
     log.debug("relocating '{s}'", .{macho_file.getAtom(atom_index).getName(macho_file)});
-    for (relocs) |*reloc| {
-        if (!reloc.dirty) continue;
+    for (relocs) |reloc| {
         reloc.resolve(macho_file, atom_index, code);
-        reloc.dirty = false;
     }
 }
 
src/link/MachO/Relocation.zig
@@ -21,6 +21,12 @@ pcrel: bool,
 length: u2,
 dirty: bool = true,
 
+/// Returns true if and only if the reloc is dirty AND the target address is available.
+pub fn isResolvable(self: Relocation, macho_file: *MachO) bool {
+    _ = self.getTargetAtomIndex(macho_file) orelse return false;
+    return self.dirty;
+}
+
 pub fn fmtType(self: Relocation, target: std.Target) []const u8 {
     switch (target.cpu.arch) {
         .aarch64 => return @tagName(@intToEnum(macho.reloc_type_arm64, self.type)),
@@ -56,7 +62,7 @@ pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, cod
     const source_sym = atom.getSymbol(macho_file);
     const source_addr = source_sym.n_value + self.offset;
 
-    const target_atom_index = self.getTargetAtomIndex(macho_file) orelse return;
+    const target_atom_index = self.getTargetAtomIndex(macho_file).?; // Oops, you didn't check if the relocation can be resolved with isResolvable().
     const target_atom = macho_file.getAtom(target_atom_index);
     const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend;
 
src/link/MachO.zig
@@ -1128,8 +1128,15 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
     const file_offset = section.header.offset + sym.n_value - section.header.addr;
     log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
 
-    if (self.relocs.get(atom_index)) |relocs| {
-        Atom.resolveRelocations(self, atom_index, relocs.items, code);
+    // Gather relocs which can be resolved.
+    var relocs = std.ArrayList(*Relocation).init(self.base.allocator);
+    defer relocs.deinit();
+
+    if (self.relocs.getPtr(atom_index)) |rels| {
+        try relocs.ensureTotalCapacityPrecise(rels.items.len);
+        for (rels.items) |*reloc| {
+            if (reloc.isResolvable(self)) relocs.appendAssumeCapacity(reloc);
+        }
     }
 
     if (is_hot_update_compatible) {
@@ -1144,7 +1151,13 @@ pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []u8) !void {
         }
     }
 
+    Atom.resolveRelocations(self, atom_index, relocs.items, code);
     try self.base.file.?.pwriteAll(code, file_offset);
+
+    // Now we can mark the relocs as resolved.
+    while (relocs.popOrNull()) |reloc| {
+        reloc.dirty = false;
+    }
 }
 
 fn updateAtomInMemory(self: *MachO, task: std.os.darwin.MachTask, segment_index: u8, addr: u64, code: []const u8) !void {