Commit 1ff3ebffa3

Jakub Konka <kubkon@jakubkonka.com>
2021-06-22 11:17:43
zld: handle dynamic binding of proxies for objc correctly
1 parent 0736365
Changed files (2)
src
src/link/MachO/Symbol.zig
@@ -85,6 +85,11 @@ pub const Regular = struct {
 pub const Proxy = struct {
     base: Symbol,
 
+    bind_info: std.ArrayListUnmanaged(struct {
+        segment_id: u16,
+        address: u64,
+    }) = .{},
+
     /// Dylib or stub where to locate this symbol.
     /// null means self-reference.
     file: ?union(enum) {
src/link/MachO/Zld.zig
@@ -263,6 +263,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
     self.allocateLinkeditSegment();
     try self.allocateSymbols();
     try self.allocateTentativeSymbols();
+    try self.allocateProxiesBindAddresses();
     try self.flush();
 }
 
@@ -1346,6 +1347,30 @@ fn allocateTentativeSymbols(self: *Zld) !void {
     }
 }
 
+fn allocateProxiesBindAddresses(self: *Zld) !void {
+    for (self.objects.items) |object| {
+        for (object.sections.items) |sect| {
+            const relocs = sect.relocs orelse continue;
+
+            for (relocs) |rel| {
+                if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased
+                if (rel.target != .symbol) continue;
+
+                const sym = rel.target.symbol.getTopmostAlias();
+                if (sym.cast(Symbol.Proxy)) |proxy| {
+                    const target_map = sect.target_map orelse continue;
+                    const target_seg = self.load_commands.items[target_map.segment_id].Segment;
+                    const target_sect = target_seg.sections.items[target_map.section_id];
+                    try proxy.bind_info.append(self.allocator, .{
+                        .segment_id = target_map.segment_id,
+                        .address = target_sect.addr + target_map.offset + rel.offset,
+                    });
+                }
+            }
+        }
+    }
+}
+
 fn writeStubHelperCommon(self: *Zld) !void {
     const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
     const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
@@ -1863,6 +1888,7 @@ fn resolveStubsAndGotEntries(self: *Zld) !void {
             const relocs = sect.relocs orelse continue;
             for (relocs) |rel| {
                 switch (rel.@"type") {
+                    .unsigned => continue,
                     .got_page, .got_page_off, .got_load, .got, .pointer_to_got => {
                         const sym = rel.target.symbol.getTopmostAlias();
                         if (sym.got_index != null) continue;
@@ -2090,7 +2116,10 @@ fn relocTargetAddr(self: *Zld, object: *const Object, target: reloc.Relocation.T
                     const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
                     const stubs = segment.sections.items[self.stubs_section_index.?];
                     const stubs_index = proxy.base.stubs_index orelse {
-                        log.err("expected stubs index when relocating symbol '{s}'", .{final.name});
+                        if (proxy.bind_info.items.len > 0) {
+                            break :blk 0; // Dynamically bound by dyld.
+                        }
+                        log.err("expected stubs index or dynamic bind address when relocating symbol '{s}'", .{final.name});
                         log.err("this is an internal linker error", .{});
                         return error.FailedToResolveRelocationTarget;
                     };
@@ -2640,6 +2669,20 @@ fn writeBindInfoTable(self: *Zld) !void {
         }
     }
 
+    for (self.imports.values()) |sym| {
+        if (sym.cast(Symbol.Proxy)) |proxy| {
+            for (proxy.bind_info.items) |info| {
+                const seg = self.load_commands.items[info.segment_id].Segment;
+                try pointers.append(.{
+                    .offset = info.address - seg.inner.vmaddr,
+                    .segment_id = info.segment_id,
+                    .dylib_ordinal = proxy.dylibOrdinal(),
+                    .name = proxy.base.name,
+                });
+            }
+        }
+    }
+
     if (self.tlv_section_index) |idx| {
         const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
         const sect = seg.sections.items[idx];