| ... | @@ -74,7 +74,8 @@ locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{}, | ... | @@ -74,7 +74,8 @@ locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{}, |
| 74 | exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{}, | 74 | exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{}, |
| 75 | nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, | 75 | nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, |
| 76 | lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, | 76 | lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, |
| 77 | threadlocal_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, | 77 | tlv_bootstrap: ?Import = null, |
| | 78 | threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, |
| 78 | local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, | 79 | local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, |
| 79 | | 80 | |
| 80 | strtab: std.ArrayListUnmanaged(u8) = .{}, | 81 | strtab: 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 | } |
| 203 | | 204 | |
| 204 | pub fn deinit(self: *Zld) void { | 205 | pub 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; |
| 1465 | | 1462 | |
| 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 | } |
| 1478 | | 1475 | |
| 1479 | if (!hit) break :outer; | 1476 | if (!hit) break :rebases; |
| 1480 | | 1477 | |
| 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; |
| 1648 | | 1665 | |
| 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 | } |
| 1661 | | 1678 | |
| 1662 | if (!hit) break :outer; | 1679 | if (!hit) break :rebases; |
| 1663 | | 1680 | |
| 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 { |
| 2344 | fn writeBindInfoTable(self: *Zld) !void { | 2403 | fn 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); | | |
| 2348 | | 2406 | |
| 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 | } |
| 2378 | | 2435 | |
| 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 | } |
| 2703 | | 2760 | |
| 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(); |
| 2707 | | 2768 | |
| ... | @@ -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 | } |
| 2718 | | 2779 | |
| 2719 | const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); | 2780 | const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); |