Commit ec874a9b2b

Jakub Konka <kubkon@jakubkonka.com>
2021-07-15 06:47:13
zld: move tracking binding for proxies into TextBlock
which is the source of binding rather than its target. That is, we now track by source.
1 parent 0135b46
Changed files (3)
src/link/MachO/reloc.zig
@@ -449,22 +449,13 @@ pub const Relocation = struct {
                 .proxy => |proxy| {
                     if (mem.eql(u8, self.target.name, "__tlv_bootstrap")) {
                         break :blk 0; // Dynamically bound by dyld.
-                        // const segment = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
-                        // const tlv = segment.sections.items[zld.tlv_section_index.?];
-                        // break :blk tlv.addr;
                     }
 
                     const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
                     const stubs = segment.sections.items[zld.stubs_section_index.?];
                     const stubs_index = self.target.stubs_index orelse {
-                        if (proxy.bind_info.items.len > 0) {
-                            break :blk 0; // Dynamically bound by dyld.
-                        }
-                        log.err("expected stubs index or dynamic bind address for symbol '{s}'", .{
-                            self.target.name,
-                        });
-                        log.err("  this is an internal linker error", .{});
-                        return error.FailedToResolveRelocationTarget;
+                        // TODO verify in TextBlock that the symbol is indeed dynamically bound.
+                        break :blk 0; // Dynamically bound by dyld.
                     };
                     break :blk stubs.addr + stubs_index * stubs.reserved2;
                 },
@@ -647,9 +638,9 @@ pub const Parser = struct {
             } else if (out_rel.payload == .unsigned) {
                 const sym = out_rel.target;
                 switch (sym.payload) {
-                    .proxy => {
-                        try sym.payload.proxy.bind_info.append(self.zld.allocator, .{
-                            .local_sym_index = self.block.local_sym_index,
+                    .proxy => |proxy| {
+                        try self.block.bindings.append(.{
+                            .local_sym_index = proxy.local_sym_index,
                             .offset = out_rel.offset,
                         });
                     },
src/link/MachO/Symbol.zig
@@ -121,20 +121,11 @@ pub const Tentative = struct {
 };
 
 pub const Proxy = struct {
-    /// Dynamic binding info - spots within the final
-    /// executable where this proxy is referenced from.
-    bind_info: std.ArrayListUnmanaged(struct {
-        local_sym_index: u32,
-        offset: u32,
-    }) = .{},
-
     /// Dylib where to locate this symbol.
     /// null means self-reference.
     file: ?*Dylib = null,
 
-    pub fn deinit(proxy: *Proxy, allocator: *Allocator) void {
-        proxy.bind_info.deinit(allocator);
-    }
+    local_sym_index: u32 = 0,
 
     pub fn dylibOrdinal(proxy: Proxy) u16 {
         const dylib = proxy.file orelse return 0;
@@ -145,13 +136,10 @@ pub const Proxy = struct {
         _ = fmt;
         _ = options;
         try std.fmt.format(writer, "Proxy {{ ", .{});
-        if (self.bind_info.items.len > 0) {
-            // TODO
-            try std.fmt.format(writer, ".bind_info = {}, ", .{self.bind_info.items.len});
-        }
         if (self.file) |file| {
             try std.fmt.format(writer, ".file = {s}, ", .{file.name.?});
         }
+        try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index});
         try std.fmt.format(writer, "}}", .{});
     }
 };
@@ -284,11 +272,6 @@ pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 {
 
 pub fn deinit(symbol: *Symbol, allocator: *Allocator) void {
     allocator.free(symbol.name);
-
-    switch (symbol.payload) {
-        .proxy => |*proxy| proxy.deinit(allocator),
-        else => {},
-    }
 }
 
 pub fn isStab(sym: macho.nlist_64) bool {
src/link/MachO/Zld.zig
@@ -136,6 +136,7 @@ pub const TextBlock = struct {
     size: u64,
     alignment: u32,
     rebases: std.ArrayList(u64),
+    bindings: std.ArrayList(SymbolAtOffset),
     dices: std.ArrayList(macho.data_in_code_entry),
     next: ?*TextBlock = null,
     prev: ?*TextBlock = null,
@@ -226,6 +227,7 @@ pub const TextBlock = struct {
             .size = undefined,
             .alignment = undefined,
             .rebases = std.ArrayList(u64).init(allocator),
+            .bindings = std.ArrayList(SymbolAtOffset).init(allocator),
             .dices = std.ArrayList(macho.data_in_code_entry).init(allocator),
         };
     }
@@ -239,6 +241,7 @@ pub const TextBlock = struct {
         self.allocator.free(self.code);
         self.relocs.deinit();
         self.rebases.deinit();
+        self.bindings.deinit();
         self.dices.deinit();
     }
 
@@ -293,6 +296,9 @@ pub const TextBlock = struct {
         if (self.rebases.items.len > 0) {
             log.warn("  rebases: {any}", .{self.rebases.items});
         }
+        if (self.bindings.items.len > 0) {
+            log.warn("  bindings: {any}", .{self.bindings.items});
+        }
         if (self.dices.items.len > 0) {
             log.warn("  dices: {any}", .{self.dices.items});
         }
@@ -1745,9 +1751,11 @@ fn resolveSymbols(self: *Zld) !void {
             if (!dylib.symbols.contains(symbol.name)) continue;
 
             try referenced.put(dylib, {});
+            const index = @intCast(u32, self.imports.items.len);
             symbol.payload = .{
                 .proxy = .{
                     .file = dylib,
+                    .local_sym_index = index,
                 },
             };
             try self.imports.append(self.allocator, symbol);
@@ -2341,23 +2349,37 @@ fn writeBindInfoTable(self: *Zld) !void {
         }
     }
 
-    for (self.globals.values()) |sym| {
-        if (sym.payload != .proxy) continue;
+    {
+        var it = self.blocks.iterator();
+        while (it.next()) |entry| {
+            const match = entry.key_ptr.*;
+            var block: *TextBlock = entry.value_ptr.*;
 
-        const proxy = sym.payload.proxy;
-        for (proxy.bind_info.items) |info| {
-            const bind_sym = self.locals.items[info.local_sym_index];
-            assert(bind_sym.payload == .regular);
-            const reg = bind_sym.payload.regular;
-            const base_address = self.load_commands.items[reg.segment_id].Segment.inner.vmaddr;
-            const offset = reg.address + info.offset - base_address;
+            if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable
 
-            try pointers.append(.{
-                .offset = offset,
-                .segment_id = reg.segment_id,
-                .dylib_ordinal = proxy.dylibOrdinal(),
-                .name = sym.name,
-            });
+            const seg = self.load_commands.items[match.seg].Segment;
+
+            while (true) {
+                const sym = self.locals.items[block.local_sym_index];
+                assert(sym.payload == .regular);
+                const base_offset = sym.payload.regular.address - seg.inner.vmaddr;
+
+                for (block.bindings.items) |binding| {
+                    const bind_sym = self.imports.items[binding.local_sym_index];
+                    const proxy = bind_sym.payload.proxy;
+
+                    try pointers.append(.{
+                        .offset = binding.offset + base_offset,
+                        .segment_id = match.seg,
+                        .dylib_ordinal = proxy.dylibOrdinal(),
+                        .name = bind_sym.name,
+                    });
+                }
+
+                if (block.prev) |prev| {
+                    block = prev;
+                } else break;
+            }
         }
     }