authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-09 18:42:00+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-17 19:59:13+01:00
logde209afbba0984c66fc5c9d379192edec87f0681
treee0ec7334305776c4cb512d6c675b3a7765e482b4
parent349f878ecf0b6ad5eee6b1cdfdba90014cbcb619

zld: fix TLV initializers


1 files changed, 88 insertions(+), 27 deletions(-)

src/link/MachO/Zld.zig+88-27
...@@ -74,7 +74,8 @@ locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{},...@@ -74,7 +74,8 @@ locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{},
74exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{},74exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{},
75nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},75nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
76lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},76lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{},
77threadlocal_imports: std.StringArrayHashMapUnmanaged(Import) = .{},77tlv_bootstrap: ?Import = null,
78threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{},
78local_rebases: std.ArrayListUnmanaged(Pointer) = .{},79local_rebases: std.ArrayListUnmanaged(Pointer) = .{},
7980
80strtab: std.ArrayListUnmanaged(u8) = .{},81strtab: std.ArrayListUnmanaged(u8) = .{},
...@@ -202,16 +203,13 @@ pub fn init(allocator: *Allocator) Zld {...@@ -202,16 +203,13 @@ pub fn init(allocator: *Allocator) Zld {
202}203}
203204
204pub fn deinit(self: *Zld) void {205pub fn deinit(self: *Zld) void {
206 self.threadlocal_offsets.deinit(self.allocator);
205 self.strtab.deinit(self.allocator);207 self.strtab.deinit(self.allocator);
206 self.local_rebases.deinit(self.allocator);208 self.local_rebases.deinit(self.allocator);
207 for (self.lazy_imports.items()) |*entry| {209 for (self.lazy_imports.items()) |*entry| {
208 self.allocator.free(entry.key);210 self.allocator.free(entry.key);
209 }211 }
210 self.lazy_imports.deinit(self.allocator);212 self.lazy_imports.deinit(self.allocator);
211 for (self.threadlocal_imports.items()) |*entry| {
212 self.allocator.free(entry.key);
213 }
214 self.threadlocal_imports.deinit(self.allocator);
215 for (self.nonlazy_imports.items()) |*entry| {213 for (self.nonlazy_imports.items()) |*entry| {
216 self.allocator.free(entry.key);214 self.allocator.free(entry.key);
217 }215 }
...@@ -780,12 +778,11 @@ fn resolveImports(self: *Zld) !void {...@@ -780,12 +778,11 @@ fn resolveImports(self: *Zld) !void {
780 });778 });
781 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {779 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
782 log.debug("writing threadlocal symbol '{s}'", .{sym_name});780 log.debug("writing threadlocal symbol '{s}'", .{sym_name});
783 const index = @intCast(u32, self.threadlocal_imports.items().len);781 self.tlv_bootstrap = .{
784 try self.threadlocal_imports.putNoClobber(self.allocator, key, .{
785 .symbol = new_sym,782 .symbol = new_sym,
786 .dylib_ordinal = dylib_ordinal,783 .dylib_ordinal = dylib_ordinal,
787 .index = index,784 .index = 0,
788 });785 };
789 } else {786 } else {
790 log.debug("writing lazy symbol '{s}'", .{sym_name});787 log.debug("writing lazy symbol '{s}'", .{sym_name});
791 const index = @intCast(u32, self.lazy_imports.items().len);788 const index = @intCast(u32, self.lazy_imports.items().len);
...@@ -1463,7 +1460,7 @@ fn doRelocs(self: *Zld) !void {...@@ -1463,7 +1460,7 @@ fn doRelocs(self: *Zld) !void {
1463 mem.writeIntLittle(u64, inst, @bitCast(u64, result));1460 mem.writeIntLittle(u64, inst, @bitCast(u64, result));
1464 sub = null;1461 sub = null;
14651462
1466 outer: {1463 rebases: {
1467 var hit: bool = false;1464 var hit: bool = false;
1468 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {1465 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {
1469 if (self.data_section_index) |index| {1466 if (self.data_section_index) |index| {
...@@ -1476,13 +1473,33 @@ fn doRelocs(self: *Zld) !void {...@@ -1476,13 +1473,33 @@ fn doRelocs(self: *Zld) !void {
1476 }1473 }
1477 }1474 }
14781475
1479 if (!hit) break :outer;1476 if (!hit) break :rebases;
14801477
1481 try self.local_rebases.append(self.allocator, .{1478 try self.local_rebases.append(self.allocator, .{
1482 .offset = this_addr - target_seg.inner.vmaddr,1479 .offset = this_addr - target_seg.inner.vmaddr,
1483 .segment_id = target_mapping.target_seg_id,1480 .segment_id = target_mapping.target_seg_id,
1484 });1481 });
1485 }1482 }
1483 // TLV is handled via a separate offset mechanism.
1484 // Calculate the offset to the initializer.
1485 if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
1486 assert(rel.r_extern == 1);
1487 const sym = object.symtab.items[rel.r_symbolnum];
1488 if (isImport(&sym)) break :tlv;
1489
1490 const base_addr = blk: {
1491 if (self.tlv_data_section_index) |index| {
1492 const tlv_data = target_seg.sections.items[index];
1493 break :blk tlv_data.addr;
1494 } else {
1495 const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
1496 break :blk tlv_bss.addr;
1497 }
1498 };
1499 // Since we require TLV data to always preceed TLV bss section, we calculate
1500 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
1501 try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr);
1502 }
1486 },1503 },
1487 2 => {1504 2 => {
1488 const inst = code[off..][0..4];1505 const inst = code[off..][0..4];
...@@ -1646,7 +1663,7 @@ fn doRelocs(self: *Zld) !void {...@@ -1646,7 +1663,7 @@ fn doRelocs(self: *Zld) !void {
1646 mem.writeIntLittle(u64, inst, @bitCast(u64, result));1663 mem.writeIntLittle(u64, inst, @bitCast(u64, result));
1647 sub = null;1664 sub = null;
16481665
1649 outer: {1666 rebases: {
1650 var hit: bool = false;1667 var hit: bool = false;
1651 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {1668 if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) {
1652 if (self.data_section_index) |index| {1669 if (self.data_section_index) |index| {
...@@ -1659,13 +1676,33 @@ fn doRelocs(self: *Zld) !void {...@@ -1659,13 +1676,33 @@ fn doRelocs(self: *Zld) !void {
1659 }1676 }
1660 }1677 }
16611678
1662 if (!hit) break :outer;1679 if (!hit) break :rebases;
16631680
1664 try self.local_rebases.append(self.allocator, .{1681 try self.local_rebases.append(self.allocator, .{
1665 .offset = this_addr - target_seg.inner.vmaddr,1682 .offset = this_addr - target_seg.inner.vmaddr,
1666 .segment_id = target_mapping.target_seg_id,1683 .segment_id = target_mapping.target_seg_id,
1667 });1684 });
1668 }1685 }
1686 // TLV is handled via a separate offset mechanism.
1687 // Calculate the offset to the initializer.
1688 if (target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) tlv: {
1689 assert(rel.r_extern == 1);
1690 const sym = object.symtab.items[rel.r_symbolnum];
1691 if (isImport(&sym)) break :tlv;
1692
1693 const base_addr = blk: {
1694 if (self.tlv_data_section_index) |index| {
1695 const tlv_data = target_seg.sections.items[index];
1696 break :blk tlv_data.addr;
1697 } else {
1698 const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?];
1699 break :blk tlv_bss.addr;
1700 }
1701 };
1702 // Since we require TLV data to always preceed TLV bss section, we calculate
1703 // offsets wrt to the former if it is defined; otherwise, wrt to the latter.
1704 try self.threadlocal_offsets.append(self.allocator, target_addr - base_addr);
1705 }
1669 },1706 },
1670 2 => {1707 2 => {
1671 const inst = code[off..][0..4];1708 const inst = code[off..][0..4];
...@@ -1771,10 +1808,10 @@ fn relocTargetAddr(self: *Zld, object_id: u16, rel: macho.relocation_info) !u64...@@ -1771,10 +1808,10 @@ fn relocTargetAddr(self: *Zld, object_id: u16, rel: macho.relocation_info) !u64
1771 const segment = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;1808 const segment = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1772 const got = segment.sections.items[self.got_section_index.?];1809 const got = segment.sections.items[self.got_section_index.?];
1773 break :blk got.addr + ext.index * @sizeOf(u64);1810 break :blk got.addr + ext.index * @sizeOf(u64);
1774 } else if (self.threadlocal_imports.get(sym_name)) |ext| {1811 } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) {
1775 const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;1812 const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1776 const tlv = segment.sections.items[self.tlv_section_index.?];1813 const tlv = segment.sections.items[self.tlv_section_index.?];
1777 break :blk tlv.addr + ext.index * @sizeOf(u64);1814 break :blk tlv.addr + self.tlv_bootstrap.?.index * @sizeOf(u64);
1778 } else {1815 } else {
1779 log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name});1816 log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name});
1780 return error.FailedToResolveRelocationTarget;1817 return error.FailedToResolveRelocationTarget;
...@@ -2207,11 +2244,33 @@ fn flush(self: *Zld) !void {...@@ -2207,11 +2244,33 @@ fn flush(self: *Zld) !void {
2207 const sect = &seg.sections.items[index];2244 const sect = &seg.sections.items[index];
2208 sect.offset = 0;2245 sect.offset = 0;
2209 }2246 }
2247
2210 if (self.tlv_bss_section_index) |index| {2248 if (self.tlv_bss_section_index) |index| {
2211 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;2249 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2212 const sect = &seg.sections.items[index];2250 const sect = &seg.sections.items[index];
2213 sect.offset = 0;2251 sect.offset = 0;
2214 }2252 }
2253
2254 if (self.tlv_section_index) |index| {
2255 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2256 const sect = &seg.sections.items[index];
2257
2258 var buffer = try self.allocator.alloc(u8, sect.size);
2259 defer self.allocator.free(buffer);
2260 _ = try self.file.?.preadAll(buffer, sect.offset);
2261
2262 var stream = std.io.fixedBufferStream(buffer);
2263 var writer = stream.writer();
2264
2265 const seek_amt = 2 * @sizeOf(u64);
2266 while (self.threadlocal_offsets.popOrNull()) |offset| {
2267 try writer.context.seekBy(seek_amt);
2268 try writer.writeIntLittle(u64, offset);
2269 }
2270
2271 try self.file.?.pwriteAll(buffer, sect.offset);
2272 }
2273
2215 try self.setEntryPoint();2274 try self.setEntryPoint();
2216 try self.writeRebaseInfoTable();2275 try self.writeRebaseInfoTable();
2217 try self.writeBindInfoTable();2276 try self.writeBindInfoTable();
...@@ -2344,9 +2403,9 @@ fn pointerCmp(context: void, a: Pointer, b: Pointer) bool {...@@ -2344,9 +2403,9 @@ fn pointerCmp(context: void, a: Pointer, b: Pointer) bool {
2344fn writeBindInfoTable(self: *Zld) !void {2403fn writeBindInfoTable(self: *Zld) !void {
2345 var pointers = std.ArrayList(Pointer).init(self.allocator);2404 var pointers = std.ArrayList(Pointer).init(self.allocator);
2346 defer pointers.deinit();2405 defer pointers.deinit();
2347 try pointers.ensureCapacity(self.nonlazy_imports.items().len + self.threadlocal_imports.items().len);
23482406
2349 if (self.got_section_index) |idx| {2407 if (self.got_section_index) |idx| {
2408 try pointers.ensureCapacity(pointers.items.len + self.nonlazy_imports.items().len);
2350 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;2409 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2351 const sect = seg.sections.items[idx];2410 const sect = seg.sections.items[idx];
2352 const base_offset = sect.addr - seg.inner.vmaddr;2411 const base_offset = sect.addr - seg.inner.vmaddr;
...@@ -2366,14 +2425,12 @@ fn writeBindInfoTable(self: *Zld) !void {...@@ -2366,14 +2425,12 @@ fn writeBindInfoTable(self: *Zld) !void {
2366 const sect = seg.sections.items[idx];2425 const sect = seg.sections.items[idx];
2367 const base_offset = sect.addr - seg.inner.vmaddr;2426 const base_offset = sect.addr - seg.inner.vmaddr;
2368 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);2427 const segment_id = @intCast(u16, self.data_segment_cmd_index.?);
2369 for (self.threadlocal_imports.items()) |entry| {2428 try pointers.append(.{
2370 pointers.appendAssumeCapacity(.{2429 .offset = base_offset + self.tlv_bootstrap.?.index * @sizeOf(u64),
2371 .offset = base_offset + entry.value.index * @sizeOf(u64),2430 .segment_id = segment_id,
2372 .segment_id = segment_id,2431 .dylib_ordinal = self.tlv_bootstrap.?.dylib_ordinal,
2373 .dylib_ordinal = entry.value.dylib_ordinal,2432 .name = "__tlv_bootstrap",
2374 .name = entry.key,2433 });
2375 });
2376 }
2377 }2434 }
23782435
2379 const size = try bindInfoSize(pointers.items);2436 const size = try bindInfoSize(pointers.items);
...@@ -2701,7 +2758,11 @@ fn writeSymbolTable(self: *Zld) !void {...@@ -2701,7 +2758,11 @@ fn writeSymbolTable(self: *Zld) !void {
2701 exports.appendAssumeCapacity(entry.value);2758 exports.appendAssumeCapacity(entry.value);
2702 }2759 }
27032760
2704 const nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len + self.threadlocal_imports.items().len;2761 const has_tlv: bool = self.tlv_bootstrap != null;
2762
2763 var nundefs = self.lazy_imports.items().len + self.nonlazy_imports.items().len;
2764 if (has_tlv) nundefs += 1;
2765
2705 var undefs = std.ArrayList(macho.nlist_64).init(self.allocator);2766 var undefs = std.ArrayList(macho.nlist_64).init(self.allocator);
2706 defer undefs.deinit();2767 defer undefs.deinit();
27072768
...@@ -2712,8 +2773,8 @@ fn writeSymbolTable(self: *Zld) !void {...@@ -2712,8 +2773,8 @@ fn writeSymbolTable(self: *Zld) !void {
2712 for (self.nonlazy_imports.items()) |entry| {2773 for (self.nonlazy_imports.items()) |entry| {
2713 undefs.appendAssumeCapacity(entry.value.symbol);2774 undefs.appendAssumeCapacity(entry.value.symbol);
2714 }2775 }
2715 for (self.threadlocal_imports.items()) |entry| {2776 if (has_tlv) {
2716 undefs.appendAssumeCapacity(entry.value.symbol);2777 undefs.appendAssumeCapacity(self.tlv_bootstrap.?.symbol);
2717 }2778 }
27182779
2719 const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);2780 const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64);