Commit dd5c7588d1

Jakub Konka <kubkon@jakubkonka.com>
2021-07-10 14:07:56
zld: fix resolving TLV offset relocations
1 parent 322be26
Changed files (2)
src
link
src/link/MachO/reloc.zig
@@ -50,7 +50,7 @@ pub const Relocation = struct {
 
         source_sect_addr: ?u64 = null,
 
-        pub fn resolve(self: Unsigned, base: Relocation, source_addr: u64, target_addr: u64) !void {
+        pub fn resolve(self: Unsigned, base: Relocation, _: u64, target_addr: u64) !void {
             const addend = if (self.source_sect_addr) |addr|
                 self.addend - @intCast(i64, addr)
             else
@@ -430,12 +430,43 @@ pub const Relocation = struct {
             }
 
             switch (self.target.payload) {
-                .regular => |reg| break :blk reg.address,
+                .regular => |reg| {
+                    const is_tlv = is_tlv: {
+                        const sym = zld.locals.items[self.block.local_sym_index];
+                        const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
+                        const sect = seg.sections.items[sym.payload.regular.section_id];
+                        break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
+                    };
+                    if (is_tlv) {
+                        // For TLV relocations, the value specified as a relocation is the displacement from the
+                        // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
+                        // defined TLV template init section in the following order:
+                        // * wrt to __thread_data if defined, then
+                        // * wrt to __thread_bss
+                        const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
+                        const base_address = inner: {
+                            if (zld.tlv_data_section_index) |i| {
+                                break :inner seg.sections.items[i].addr;
+                            } else if (zld.tlv_bss_section_index) |i| {
+                                break :inner seg.sections.items[i].addr;
+                            } else {
+                                log.err("threadlocal variables present but no initializer sections found", .{});
+                                log.err("  __thread_data not found", .{});
+                                log.err("  __thread_bss not found", .{});
+                                return error.FailedToResolveRelocationTarget;
+                            }
+                        };
+                        break :blk reg.address - base_address;
+                    }
+
+                    break :blk reg.address;
+                },
                 .proxy => |proxy| {
                     if (mem.eql(u8, self.target.name, "__tlv_bootstrap")) {
-                        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;
+                        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;
@@ -677,14 +708,6 @@ pub const Parser = struct {
                         if (should_rebase) {
                             try self.block.rebases.append(out_rel.offset);
                         }
-
-                        // TLV is handled via a separate offset mechanism.
-                        if (sect_type == macho.S_THREAD_LOCAL_VARIABLES) {
-                            try self.block.tlv_offsets.append(.{
-                                .local_sym_index = out_rel.target.payload.regular.local_sym_index,
-                                .offset = out_rel.offset,
-                            });
-                        }
                     },
                 }
             } else if (out_rel.payload == .branch) blk: {
src/link/MachO/Zld.zig
@@ -131,7 +131,6 @@ pub const TextBlock = struct {
     size: u64,
     alignment: u32,
     rebases: std.ArrayList(u64),
-    tlv_offsets: std.ArrayList(TlvOffset),
     next: ?*TextBlock = null,
     prev: ?*TextBlock = null,
 
@@ -140,11 +139,6 @@ pub const TextBlock = struct {
         offset: u64,
     };
 
-    pub const TlvOffset = struct {
-        local_sym_index: u32,
-        offset: u64,
-    };
-
     pub fn init(allocator: *Allocator) TextBlock {
         return .{
             .allocator = allocator,
@@ -155,7 +149,6 @@ pub const TextBlock = struct {
             .size = undefined,
             .alignment = undefined,
             .rebases = std.ArrayList(u64).init(allocator),
-            .tlv_offsets = std.ArrayList(TextBlock.TlvOffset).init(allocator),
         };
     }
 
@@ -170,7 +163,6 @@ pub const TextBlock = struct {
         self.allocator.free(self.code);
         self.relocs.deinit();
         self.rebases.deinit();
-        self.tlv_offsets.deinit();
     }
 
     pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
@@ -210,9 +202,6 @@ pub const TextBlock = struct {
         if (self.rebases.items.len > 0) {
             log.warn("  | rebases: {any}", .{self.rebases.items});
         }
-        if (self.tlv_offsets.items.len > 0) {
-            log.warn("  | TLV offsets: {any}", .{self.tlv_offsets.items});
-        }
         log.warn("  | size = {}", .{self.size});
         log.warn("  | align = {}", .{self.alignment});
     }
@@ -1120,10 +1109,7 @@ fn writeTextBlocks(self: *Zld) !void {
         var code = try self.allocator.alloc(u8, sect.size);
         defer self.allocator.free(code);
 
-        if (sect_type == macho.S_ZEROFILL or
-            sect_type == macho.S_THREAD_LOCAL_ZEROFILL or
-            sect_type == macho.S_THREAD_LOCAL_VARIABLES)
-        {
+        if (sect_type == macho.S_ZEROFILL or sect_type == macho.S_THREAD_LOCAL_ZEROFILL) {
             mem.set(u8, code, 0);
         } else {
             var base_off: u64 = sect.size;
@@ -2051,41 +2037,6 @@ fn flush(self: *Zld) !void {
         sect.offset = 0;
     }
 
-    if (self.tlv_section_index) |index| {
-        // TODO this should be part of relocation resolution routine.
-        const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
-        const sect = &seg.sections.items[index];
-
-        const base_addr = if (self.tlv_data_section_index) |i|
-            seg.sections.items[i].addr
-        else
-            seg.sections.items[self.tlv_bss_section_index.?].addr;
-
-        var block: *TextBlock = self.blocks.get(.{
-            .seg = self.data_segment_cmd_index.?,
-            .sect = index,
-        }) orelse unreachable;
-
-        var buffer = try self.allocator.alloc(u8, @intCast(usize, sect.size));
-        defer self.allocator.free(buffer);
-        _ = try self.file.?.preadAll(buffer, sect.offset);
-
-        while (true) {
-            for (block.tlv_offsets.items) |tlv_offset| {
-                const sym = self.locals.items[tlv_offset.local_sym_index];
-                assert(sym.payload == .regular);
-                const offset = sym.payload.regular.address - base_addr;
-                mem.writeIntLittle(u64, buffer[tlv_offset.offset..][0..@sizeOf(u64)], offset);
-            }
-
-            if (block.prev) |prev| {
-                block = prev;
-            } else break;
-        }
-
-        try self.file.?.pwriteAll(buffer, sect.offset);
-    }
-
     try self.writeGotEntries();
     try self.setEntryPoint();
     try self.writeRebaseInfoTable();