| ... | ... | @@ -74,7 +74,8 @@ locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{}, |
| 74 | 74 | exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{}, |
| 75 | 75 | nonlazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, |
| 76 | 76 | lazy_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, |
| 77 | | threadlocal_imports: std.StringArrayHashMapUnmanaged(Import) = .{}, |
| 77 | tlv_bootstrap: ?Import = null, |
| 78 | threadlocal_offsets: std.ArrayListUnmanaged(u64) = .{}, |
| 78 | 79 | local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, |
| 79 | 80 | |
| 80 | 81 | strtab: std.ArrayListUnmanaged(u8) = .{}, |
| ... | ... | @@ -202,16 +203,13 @@ pub fn init(allocator: *Allocator) Zld { |
| 202 | 203 | } |
| 203 | 204 | |
| 204 | 205 | pub fn deinit(self: *Zld) void { |
| 206 | self.threadlocal_offsets.deinit(self.allocator); |
| 205 | 207 | self.strtab.deinit(self.allocator); |
| 206 | 208 | self.local_rebases.deinit(self.allocator); |
| 207 | 209 | for (self.lazy_imports.items()) |*entry| { |
| 208 | 210 | self.allocator.free(entry.key); |
| 209 | 211 | } |
| 210 | 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 | 213 | for (self.nonlazy_imports.items()) |*entry| { |
| 216 | 214 | self.allocator.free(entry.key); |
| 217 | 215 | } |
| ... | ... | @@ -780,12 +778,11 @@ fn resolveImports(self: *Zld) !void { |
| 780 | 778 | }); |
| 781 | 779 | } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) { |
| 782 | 780 | log.debug("writing threadlocal symbol '{s}'", .{sym_name}); |
| 783 | | const index = @intCast(u32, self.threadlocal_imports.items().len); |
| 784 | | try self.threadlocal_imports.putNoClobber(self.allocator, key, .{ |
| 781 | self.tlv_bootstrap = .{ |
| 785 | 782 | .symbol = new_sym, |
| 786 | 783 | .dylib_ordinal = dylib_ordinal, |
| 787 | | .index = index, |
| 788 | | }); |
| 784 | .index = 0, |
| 785 | }; |
| 789 | 786 | } else { |
| 790 | 787 | log.debug("writing lazy symbol '{s}'", .{sym_name}); |
| 791 | 788 | const index = @intCast(u32, self.lazy_imports.items().len); |
| ... | ... | @@ -1463,7 +1460,7 @@ fn doRelocs(self: *Zld) !void { |
| 1463 | 1460 | mem.writeIntLittle(u64, inst, @bitCast(u64, result)); |
| 1464 | 1461 | sub = null; |
| 1465 | 1462 | |
| 1466 | | outer: { |
| 1463 | rebases: { |
| 1467 | 1464 | var hit: bool = false; |
| 1468 | 1465 | if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) { |
| 1469 | 1466 | if (self.data_section_index) |index| { |
| ... | ... | @@ -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 | 1478 | try self.local_rebases.append(self.allocator, .{ |
| 1482 | 1479 | .offset = this_addr - target_seg.inner.vmaddr, |
| 1483 | 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 | 1504 | 2 => { |
| 1488 | 1505 | const inst = code[off..][0..4]; |
| ... | ... | @@ -1646,7 +1663,7 @@ fn doRelocs(self: *Zld) !void { |
| 1646 | 1663 | mem.writeIntLittle(u64, inst, @bitCast(u64, result)); |
| 1647 | 1664 | sub = null; |
| 1648 | 1665 | |
| 1649 | | outer: { |
| 1666 | rebases: { |
| 1650 | 1667 | var hit: bool = false; |
| 1651 | 1668 | if (target_mapping.target_seg_id == self.data_segment_cmd_index.?) { |
| 1652 | 1669 | if (self.data_section_index) |index| { |
| ... | ... | @@ -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 | 1681 | try self.local_rebases.append(self.allocator, .{ |
| 1665 | 1682 | .offset = this_addr - target_seg.inner.vmaddr, |
| 1666 | 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 | 1707 | 2 => { |
| 1671 | 1708 | const inst = code[off..][0..4]; |
| ... | ... | @@ -1771,10 +1808,10 @@ fn relocTargetAddr(self: *Zld, object_id: u16, rel: macho.relocation_info) !u64 |
| 1771 | 1808 | const segment = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 1772 | 1809 | const got = segment.sections.items[self.got_section_index.?]; |
| 1773 | 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 | 1812 | const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 1776 | 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 | 1815 | } else { |
| 1779 | 1816 | log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); |
| 1780 | 1817 | return error.FailedToResolveRelocationTarget; |
| ... | ... | @@ -2207,11 +2244,33 @@ fn flush(self: *Zld) !void { |
| 2207 | 2244 | const sect = &seg.sections.items[index]; |
| 2208 | 2245 | sect.offset = 0; |
| 2209 | 2246 | } |
| 2247 | |
| 2210 | 2248 | if (self.tlv_bss_section_index) |index| { |
| 2211 | 2249 | const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; |
| 2212 | 2250 | const sect = &seg.sections.items[index]; |
| 2213 | 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 | 2274 | try self.setEntryPoint(); |
| 2216 | 2275 | try self.writeRebaseInfoTable(); |
| 2217 | 2276 | try self.writeBindInfoTable(); |
| ... | ... | @@ -2344,9 +2403,9 @@ fn pointerCmp(context: void, a: Pointer, b: Pointer) bool { |
| 2344 | 2403 | fn writeBindInfoTable(self: *Zld) !void { |
| 2345 | 2404 | var pointers = std.ArrayList(Pointer).init(self.allocator); |
| 2346 | 2405 | defer pointers.deinit(); |
| 2347 | | try pointers.ensureCapacity(self.nonlazy_imports.items().len + self.threadlocal_imports.items().len); |
| 2348 | 2406 | |
| 2349 | 2407 | if (self.got_section_index) |idx| { |
| 2408 | try pointers.ensureCapacity(pointers.items.len + self.nonlazy_imports.items().len); |
| 2350 | 2409 | const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; |
| 2351 | 2410 | const sect = seg.sections.items[idx]; |
| 2352 | 2411 | const base_offset = sect.addr - seg.inner.vmaddr; |
| ... | ... | @@ -2366,14 +2425,12 @@ fn writeBindInfoTable(self: *Zld) !void { |
| 2366 | 2425 | const sect = seg.sections.items[idx]; |
| 2367 | 2426 | const base_offset = sect.addr - seg.inner.vmaddr; |
| 2368 | 2427 | const segment_id = @intCast(u16, self.data_segment_cmd_index.?); |
| 2369 | | for (self.threadlocal_imports.items()) |entry| { |
| 2370 | | pointers.appendAssumeCapacity(.{ |
| 2371 | | .offset = base_offset + entry.value.index * @sizeOf(u64), |
| 2372 | | .segment_id = segment_id, |
| 2373 | | .dylib_ordinal = entry.value.dylib_ordinal, |
| 2374 | | .name = entry.key, |
| 2375 | | }); |
| 2376 | | } |
| 2428 | try pointers.append(.{ |
| 2429 | .offset = base_offset + self.tlv_bootstrap.?.index * @sizeOf(u64), |
| 2430 | .segment_id = segment_id, |
| 2431 | .dylib_ordinal = self.tlv_bootstrap.?.dylib_ordinal, |
| 2432 | .name = "__tlv_bootstrap", |
| 2433 | }); |
| 2377 | 2434 | } |
| 2378 | 2435 | |
| 2379 | 2436 | const size = try bindInfoSize(pointers.items); |
| ... | ... | @@ -2701,7 +2758,11 @@ fn writeSymbolTable(self: *Zld) !void { |
| 2701 | 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 | 2766 | var undefs = std.ArrayList(macho.nlist_64).init(self.allocator); |
| 2706 | 2767 | defer undefs.deinit(); |
| 2707 | 2768 | |
| ... | ... | @@ -2712,8 +2773,8 @@ fn writeSymbolTable(self: *Zld) !void { |
| 2712 | 2773 | for (self.nonlazy_imports.items()) |entry| { |
| 2713 | 2774 | undefs.appendAssumeCapacity(entry.value.symbol); |
| 2714 | 2775 | } |
| 2715 | | for (self.threadlocal_imports.items()) |entry| { |
| 2716 | | undefs.appendAssumeCapacity(entry.value.symbol); |
| 2776 | if (has_tlv) { |
| 2777 | undefs.appendAssumeCapacity(self.tlv_bootstrap.?.symbol); |
| 2717 | 2778 | } |
| 2718 | 2779 | |
| 2719 | 2780 | const locals_off = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64); |