authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-13 15:11:35-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-01-13 15:11:35-08:00
logec1541de26137cf5d1e353023adbf92e1be8ef16
tree8477de646121c223dad7cf30c6c90bfda92fb89c
parent5487dd13ea23ad7e547995b9a088ba37bfe17737
parent212814932578f5916bff2dd04d501e1d30be740c
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7746 from kubkon/macho-extern-fn

macho: extern functions come to MachO!

6 files changed, 958 insertions(+), 392 deletions(-)

src/codegen.zig+41-2
......@@ -1859,8 +1859,47 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
18591859 },
18601860 else => unreachable, // unsupported architecture on MachO
18611861 }
1862 } else if (func_value.castTag(.extern_fn)) |_| {
1863 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});
1862 } else if (func_value.castTag(.extern_fn)) |func_payload| {
1863 const decl = func_payload.data;
1864 // We don't free the decl_name immediately unless it already exists.
1865 // If it doesn't, it will get autofreed when we clean up the extern symbol table.
1866 const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
1867 const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);
1868 const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index| blk: {
1869 self.bin_file.allocator.free(decl_name);
1870 break :blk @intCast(u32, index);
1871 } else blk: {
1872 const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len);
1873 try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{
1874 .name = decl_name,
1875 .dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.
1876 });
1877 break :blk index;
1878 };
1879 const start = self.code.items.len;
1880 const len: usize = blk: {
1881 switch (arch) {
1882 .x86_64 => {
1883 // callq
1884 try self.code.ensureCapacity(self.code.items.len + 5);
1885 self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 });
1886 break :blk 5;
1887 },
1888 .aarch64 => {
1889 // bl
1890 writeInt(u32, try self.code.addManyAsArray(4), 0);
1891 break :blk 4;
1892 },
1893 else => unreachable, // unsupported architecture on MachO
1894 }
1895 };
1896 try macho_file.stub_fixups.append(self.bin_file.allocator, .{
1897 .symbol = symbol,
1898 .already_defined = already_defined,
1899 .start = start,
1900 .len = len,
1901 });
1902 // We mark the space and fix it up later.
18641903 } else {
18651904 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
18661905 }
src/link/MachO.zig+757-110
......@@ -52,6 +52,8 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
5252pagezero_segment_cmd_index: ?u16 = null,
5353/// __TEXT segment
5454text_segment_cmd_index: ?u16 = null,
55/// __DATA_CONST segment
56data_const_segment_cmd_index: ?u16 = null,
5557/// __DATA segment
5658data_segment_cmd_index: ?u16 = null,
5759/// __LINKEDIT segment
......@@ -87,22 +89,34 @@ code_signature_cmd_index: ?u16 = null,
8789text_section_index: ?u16 = null,
8890/// Index into __TEXT,__ziggot section.
8991got_section_index: ?u16 = null,
92/// Index into __TEXT,__stubs section.
93stubs_section_index: ?u16 = null,
94/// Index into __TEXT,__stub_helper section.
95stub_helper_section_index: ?u16 = null,
96/// Index into __DATA_CONST,__got section.
97data_got_section_index: ?u16 = null,
98/// Index into __DATA,__la_symbol_ptr section.
99la_symbol_ptr_section_index: ?u16 = null,
100/// Index into __DATA,__data section.
101data_section_index: ?u16 = null,
90102/// The absolute address of the entry point.
91103entry_addr: ?u64 = null,
92104
93105/// Table of all local symbols
94106/// Internally references string table for names (which are optional).
95107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
96/// Table of all defined global symbols
108/// Table of all global symbols
97109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
98/// Table of all undefined symbols
99undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
110/// Table of all extern nonlazy symbols, indexed by name.
111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
112/// Table of all extern lazy symbols, indexed by name.
113extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
100114
101115local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
102116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
103117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
104118
105dyld_stub_binder_index: ?u16 = null,
119stub_helper_stubs_start_off: ?u64 = null,
106120
107121/// Table of symbol names aka the string table.
108122string_table: std.ArrayListUnmanaged(u8) = .{},
......@@ -110,16 +124,12 @@ string_table: std.ArrayListUnmanaged(u8) = .{},
110124/// Table of trampolines to the actual symbols in __text section.
111125offset_table: std.ArrayListUnmanaged(u64) = .{},
112126
113/// Table of binding info entries.
114binding_info_table: BindingInfoTable = .{},
115/// Table of lazy binding info entries.
116lazy_binding_info_table: LazyBindingInfoTable = .{},
117
118127error_flags: File.ErrorFlags = File.ErrorFlags{},
119128
120129offset_table_count_dirty: bool = false,
121130header_dirty: bool = false,
122131load_commands_dirty: bool = false,
132rebase_info_dirty: bool = false,
123133binding_info_dirty: bool = false,
124134lazy_binding_info_dirty: bool = false,
125135export_info_dirty: bool = false,
......@@ -149,6 +159,12 @@ last_text_block: ?*TextBlock = null,
149159/// prior to calling `generateSymbol`, and then immediately deallocated
150160/// rather than sitting in the global scope.
151161pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
162/// A list of all stub (extern decls) fixups required for this run of the linker.
163/// Warning, this is currently NOT thread-safe. See the TODO below.
164/// TODO Move this list inside `updateDecl` where it should be allocated
165/// prior to calling `generateSymbol`, and then immediately deallocated
166/// rather than sitting in the global scope.
167stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
152168
153169pub const PieFixup = struct {
154170 /// Target address we wanted to address in absolute terms.
......@@ -160,6 +176,19 @@ pub const PieFixup = struct {
160176 len: usize,
161177};
162178
179pub const StubFixup = struct {
180 /// Id of extern (lazy) symbol.
181 symbol: u32,
182 /// Signals whether the symbol has already been declared before. If so,
183 /// then there is no need to rewrite the stub entry and related.
184 already_defined: bool,
185 /// Where in the byte stream we should perform the fixup.
186 start: usize,
187 /// The length of the byte stream. For x86_64, this will be
188 /// variable. For aarch64, it will be fixed at 4 bytes.
189 len: usize,
190};
191
163192/// `alloc_num / alloc_den` is the factor of padding when allocating.
164193pub const alloc_num = 4;
165194pub const alloc_den = 3;
......@@ -379,10 +408,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
379408 main_cmd.entryoff = addr - text_segment.inner.vmaddr;
380409 self.load_commands_dirty = true;
381410 }
411 try self.writeRebaseInfoTable();
382412 try self.writeBindingInfoTable();
383413 try self.writeLazyBindingInfoTable();
384414 try self.writeExportTrie();
385415 try self.writeAllGlobalAndUndefSymbols();
416 try self.writeIndirectSymbolTable();
386417 try self.writeStringTable();
387418 try self.updateLinkeditSegmentSizes();
388419
......@@ -418,6 +449,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
418449 assert(!self.offset_table_count_dirty);
419450 assert(!self.header_dirty);
420451 assert(!self.load_commands_dirty);
452 assert(!self.rebase_info_dirty);
421453 assert(!self.binding_info_dirty);
422454 assert(!self.lazy_binding_info_dirty);
423455 assert(!self.export_info_dirty);
......@@ -891,42 +923,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
891923 return error.NoSymbolTableFound;
892924 }
893925
894 // Parse dyld info
895 try self.parseBindingInfoTable();
896 try self.parseLazyBindingInfoTable();
897
898 // Update the dylib ordinals.
899 self.binding_info_table.dylib_ordinal = next_ordinal;
900 for (self.lazy_binding_info_table.symbols.items) |*symbol| {
901 symbol.dylib_ordinal = next_ordinal;
902 }
903
904 // Write updated dyld info.
905 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
906 {
907 const size = try self.binding_info_table.calcSize();
908 assert(dyld_info.bind_size >= size);
909
910 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
911 defer self.base.allocator.free(buffer);
912
913 var stream = std.io.fixedBufferStream(buffer);
914 try self.binding_info_table.write(stream.writer());
915
916 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
917 }
918 {
919 const size = try self.lazy_binding_info_table.calcSize();
920 assert(dyld_info.lazy_bind_size >= size);
921
922 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
923 defer self.base.allocator.free(buffer);
924
925 var stream = std.io.fixedBufferStream(buffer);
926 try self.lazy_binding_info_table.write(stream.writer());
927
928 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
929 }
926 // Patch dyld info
927 try self.fixupBindInfo(next_ordinal);
928 try self.fixupLazyBindInfo(next_ordinal);
930929
931930 // Write updated load commands and the header
932931 try self.writeLoadCommands();
......@@ -1008,14 +1007,20 @@ pub fn deinit(self: *MachO) void {
10081007 if (self.d_sym) |*ds| {
10091008 ds.deinit(self.base.allocator);
10101009 }
1011 self.binding_info_table.deinit(self.base.allocator);
1012 self.lazy_binding_info_table.deinit(self.base.allocator);
1010 for (self.extern_lazy_symbols.items()) |*entry| {
1011 entry.value.deinit(self.base.allocator);
1012 }
1013 self.extern_lazy_symbols.deinit(self.base.allocator);
1014 for (self.extern_nonlazy_symbols.items()) |*entry| {
1015 entry.value.deinit(self.base.allocator);
1016 }
1017 self.extern_nonlazy_symbols.deinit(self.base.allocator);
10131018 self.pie_fixups.deinit(self.base.allocator);
1019 self.stub_fixups.deinit(self.base.allocator);
10141020 self.text_block_free_list.deinit(self.base.allocator);
10151021 self.offset_table.deinit(self.base.allocator);
10161022 self.offset_table_free_list.deinit(self.base.allocator);
10171023 self.string_table.deinit(self.base.allocator);
1018 self.undef_symbols.deinit(self.base.allocator);
10191024 self.global_symbols.deinit(self.base.allocator);
10201025 self.global_symbol_free_list.deinit(self.base.allocator);
10211026 self.local_symbols.deinit(self.base.allocator);
......@@ -1211,7 +1216,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12111216 }
12121217
12131218 // Perform PIE fixups (if any)
1214 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1219 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
12151220 const got_section = text_segment.sections.items[self.got_section_index.?];
12161221 while (self.pie_fixups.popOrNull()) |fixup| {
12171222 const target_addr = fixup.address;
......@@ -1231,6 +1236,38 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12311236 }
12321237 }
12331238
1239 // Resolve stubs (if any)
1240 const stubs = text_segment.sections.items[self.stubs_section_index.?];
1241 for (self.stub_fixups.items) |fixup| {
1242 const stub_addr = stubs.addr + fixup.symbol * stubs.reserved2;
1243 const text_addr = symbol.n_value + fixup.start;
1244 switch (self.base.options.target.cpu.arch) {
1245 .x86_64 => {
1246 const displacement = @intCast(u32, stub_addr - text_addr - fixup.len);
1247 var placeholder = code_buffer.items[fixup.start + fixup.len - @sizeOf(u32) ..][0..@sizeOf(u32)];
1248 mem.writeIntSliceLittle(u32, placeholder, displacement);
1249 },
1250 .aarch64 => {
1251 const displacement = @intCast(u32, stub_addr - text_addr);
1252 var placeholder = code_buffer.items[fixup.start..][0..fixup.len];
1253 mem.writeIntSliceLittle(u32, placeholder, aarch64.Instruction.bl(@intCast(i28, displacement)).toU32());
1254 },
1255 else => unreachable, // unsupported target architecture
1256 }
1257 if (!fixup.already_defined) {
1258 try self.writeStub(fixup.symbol);
1259 try self.writeStubInStubHelper(fixup.symbol);
1260 try self.writeLazySymbolPointer(fixup.symbol);
1261
1262 const extern_sym = &self.extern_lazy_symbols.items()[fixup.symbol].value;
1263 extern_sym.segment = self.data_segment_cmd_index.?;
1264 extern_sym.offset = fixup.symbol * @sizeOf(u64);
1265 self.rebase_info_dirty = true;
1266 self.lazy_binding_info_dirty = true;
1267 }
1268 }
1269 self.stub_fixups.shrinkRetainingCapacity(0);
1270
12341271 const text_section = text_segment.sections.items[self.text_section_index.?];
12351272 const section_offset = symbol.n_value - text_section.addr;
12361273 const file_offset = text_section.offset + section_offset;
......@@ -1435,7 +1472,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14351472
14361473 const program_code_size_hint = self.base.options.program_code_size_hint;
14371474 const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;
1438 const ideal_size = self.header_pad + program_code_size_hint + offset_table_size_hint;
1475 const ideal_size = self.header_pad + program_code_size_hint + 3 * offset_table_size_hint;
14391476 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size);
14401477
14411478 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });
......@@ -1492,9 +1529,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14921529 }
14931530 if (self.got_section_index == null) {
14941531 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1495 const text_section = &text_segment.sections.items[self.text_section_index.?];
14961532 self.got_section_index = @intCast(u16, text_segment.sections.items.len);
14971533
1534 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
1535 .x86_64 => 0,
1536 .aarch64 => 2,
1537 else => unreachable, // unhandled architecture type
1538 };
14981539 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
14991540 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
15001541 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
......@@ -1508,7 +1549,220 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15081549 .addr = text_segment.inner.vmaddr + off,
15091550 .size = needed_size,
15101551 .offset = @intCast(u32, off),
1511 .@"align" = 3, // 2^@sizeOf(u64)
1552 .@"align" = alignment,
1553 .reloff = 0,
1554 .nreloc = 0,
1555 .flags = flags,
1556 .reserved1 = 0,
1557 .reserved2 = 0,
1558 .reserved3 = 0,
1559 });
1560 self.header_dirty = true;
1561 self.load_commands_dirty = true;
1562 }
1563 if (self.stubs_section_index == null) {
1564 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1565 self.stubs_section_index = @intCast(u16, text_segment.sections.items.len);
1566
1567 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
1568 .x86_64 => 0,
1569 .aarch64 => 2,
1570 else => unreachable, // unhandled architecture type
1571 };
1572 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
1573 .x86_64 => 6,
1574 .aarch64 => 2 * @sizeOf(u32),
1575 else => unreachable, // unhandled architecture type
1576 };
1577 const flags = macho.S_SYMBOL_STUBS | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1578 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1579 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
1580 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
1581
1582 log.debug("found __stubs section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
1583
1584 try text_segment.addSection(self.base.allocator, .{
1585 .sectname = makeStaticString("__stubs"),
1586 .segname = makeStaticString("__TEXT"),
1587 .addr = text_segment.inner.vmaddr + off,
1588 .size = needed_size,
1589 .offset = @intCast(u32, off),
1590 .@"align" = alignment,
1591 .reloff = 0,
1592 .nreloc = 0,
1593 .flags = flags,
1594 .reserved1 = 0,
1595 .reserved2 = stub_size,
1596 .reserved3 = 0,
1597 });
1598 self.header_dirty = true;
1599 self.load_commands_dirty = true;
1600 }
1601 if (self.stub_helper_section_index == null) {
1602 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1603 self.stub_helper_section_index = @intCast(u16, text_segment.sections.items.len);
1604
1605 const alignment: u2 = switch (self.base.options.target.cpu.arch) {
1606 .x86_64 => 0,
1607 .aarch64 => 2,
1608 else => unreachable, // unhandled architecture type
1609 };
1610 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1611 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1612 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
1613 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
1614
1615 log.debug("found __stub_helper section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
1616
1617 try text_segment.addSection(self.base.allocator, .{
1618 .sectname = makeStaticString("__stub_helper"),
1619 .segname = makeStaticString("__TEXT"),
1620 .addr = text_segment.inner.vmaddr + off,
1621 .size = needed_size,
1622 .offset = @intCast(u32, off),
1623 .@"align" = alignment,
1624 .reloff = 0,
1625 .nreloc = 0,
1626 .flags = flags,
1627 .reserved1 = 0,
1628 .reserved2 = 0,
1629 .reserved3 = 0,
1630 });
1631 self.header_dirty = true;
1632 self.load_commands_dirty = true;
1633 }
1634 if (self.data_const_segment_cmd_index == null) {
1635 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1636 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
1637 const initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE;
1638 const address_and_offset = self.nextSegmentAddressAndOffset();
1639
1640 const ideal_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1641 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size);
1642
1643 log.debug("found __DATA_CONST segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
1644
1645 try self.load_commands.append(self.base.allocator, .{
1646 .Segment = SegmentCommand.empty(.{
1647 .cmd = macho.LC_SEGMENT_64,
1648 .cmdsize = @sizeOf(macho.segment_command_64),
1649 .segname = makeStaticString("__DATA_CONST"),
1650 .vmaddr = address_and_offset.address,
1651 .vmsize = needed_size,
1652 .fileoff = address_and_offset.offset,
1653 .filesize = needed_size,
1654 .maxprot = maxprot,
1655 .initprot = initprot,
1656 .nsects = 0,
1657 .flags = 0,
1658 }),
1659 });
1660 self.header_dirty = true;
1661 self.load_commands_dirty = true;
1662 }
1663 if (self.data_got_section_index == null) {
1664 const dc_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
1665 self.data_got_section_index = @intCast(u16, dc_segment.sections.items.len);
1666
1667 const flags = macho.S_NON_LAZY_SYMBOL_POINTERS;
1668 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1669 const off = dc_segment.findFreeSpace(needed_size, @alignOf(u64), null);
1670 assert(off + needed_size <= dc_segment.inner.fileoff + dc_segment.inner.filesize); // TODO Must expand __DATA_CONST segment.
1671
1672 log.debug("found __got section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
1673
1674 try dc_segment.addSection(self.base.allocator, .{
1675 .sectname = makeStaticString("__got"),
1676 .segname = makeStaticString("__DATA_CONST"),
1677 .addr = dc_segment.inner.vmaddr + off - dc_segment.inner.fileoff,
1678 .size = needed_size,
1679 .offset = @intCast(u32, off),
1680 .@"align" = 3, // 2^3 = @sizeOf(u64)
1681 .reloff = 0,
1682 .nreloc = 0,
1683 .flags = flags,
1684 .reserved1 = 0,
1685 .reserved2 = 0,
1686 .reserved3 = 0,
1687 });
1688 self.header_dirty = true;
1689 self.load_commands_dirty = true;
1690 }
1691 if (self.data_segment_cmd_index == null) {
1692 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
1693 const maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE;
1694 const initprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE;
1695 const address_and_offset = self.nextSegmentAddressAndOffset();
1696
1697 const ideal_size = 2 * @sizeOf(u64) * self.base.options.symbol_count_hint;
1698 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size);
1699
1700 log.debug("found __DATA segment free space 0x{x} to 0x{x}", .{ address_and_offset.offset, address_and_offset.offset + needed_size });
1701
1702 try self.load_commands.append(self.base.allocator, .{
1703 .Segment = SegmentCommand.empty(.{
1704 .cmd = macho.LC_SEGMENT_64,
1705 .cmdsize = @sizeOf(macho.segment_command_64),
1706 .segname = makeStaticString("__DATA"),
1707 .vmaddr = address_and_offset.address,
1708 .vmsize = needed_size,
1709 .fileoff = address_and_offset.offset,
1710 .filesize = needed_size,
1711 .maxprot = maxprot,
1712 .initprot = initprot,
1713 .nsects = 0,
1714 .flags = 0,
1715 }),
1716 });
1717 self.header_dirty = true;
1718 self.load_commands_dirty = true;
1719 }
1720 if (self.la_symbol_ptr_section_index == null) {
1721 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1722 self.la_symbol_ptr_section_index = @intCast(u16, data_segment.sections.items.len);
1723
1724 const flags = macho.S_LAZY_SYMBOL_POINTERS;
1725 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1726 const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null);
1727 assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment.
1728
1729 log.debug("found __la_symbol_ptr section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
1730
1731 try data_segment.addSection(self.base.allocator, .{
1732 .sectname = makeStaticString("__la_symbol_ptr"),
1733 .segname = makeStaticString("__DATA"),
1734 .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff,
1735 .size = needed_size,
1736 .offset = @intCast(u32, off),
1737 .@"align" = 3, // 2^3 = @sizeOf(u64)
1738 .reloff = 0,
1739 .nreloc = 0,
1740 .flags = flags,
1741 .reserved1 = 0,
1742 .reserved2 = 0,
1743 .reserved3 = 0,
1744 });
1745 self.header_dirty = true;
1746 self.load_commands_dirty = true;
1747 }
1748 if (self.data_section_index == null) {
1749 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
1750 self.data_section_index = @intCast(u16, data_segment.sections.items.len);
1751
1752 const flags = macho.S_REGULAR;
1753 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1754 const off = data_segment.findFreeSpace(needed_size, @alignOf(u64), null);
1755 assert(off + needed_size <= data_segment.inner.fileoff + data_segment.inner.filesize); // TODO Must expand __DATA segment.
1756
1757 log.debug("found __data section free space 0x{x} to 0x{x}", .{ off, off + needed_size });
1758
1759 try data_segment.addSection(self.base.allocator, .{
1760 .sectname = makeStaticString("__data"),
1761 .segname = makeStaticString("__DATA"),
1762 .addr = data_segment.inner.vmaddr + off - data_segment.inner.fileoff,
1763 .size = needed_size,
1764 .offset = @intCast(u32, off),
1765 .@"align" = 3, // 2^3 = @sizeOf(u64)
15121766 .reloff = 0,
15131767 .nreloc = 0,
15141768 .flags = flags,
......@@ -1549,12 +1803,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15491803 if (self.dyld_info_cmd_index == null) {
15501804 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
15511805
1552 // TODO Preallocate rebase, binding, and lazy binding info.
1553 const export_size = 2;
1554 const export_off = self.findFreeSpaceLinkedit(export_size, 1);
1555
1556 log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
1557
15581806 try self.load_commands.append(self.base.allocator, .{
15591807 .DyldInfoOnly = .{
15601808 .cmd = macho.LC_DYLD_INFO_ONLY,
......@@ -1567,37 +1815,67 @@ pub fn populateMissingMetadata(self: *MachO) !void {
15671815 .weak_bind_size = 0,
15681816 .lazy_bind_off = 0,
15691817 .lazy_bind_size = 0,
1570 .export_off = @intCast(u32, export_off),
1571 .export_size = export_size,
1818 .export_off = 0,
1819 .export_size = 0,
15721820 },
15731821 });
1822
1823 const dyld = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
1824
1825 // Preallocate rebase, binding, lazy binding info, and export info.
1826 const expected_size = 48; // TODO This is totally random.
1827 const rebase_off = self.findFreeSpaceLinkedit(expected_size, 1);
1828 log.debug("found rebase info free space 0x{x} to 0x{x}", .{ rebase_off, rebase_off + expected_size });
1829 dyld.rebase_off = @intCast(u32, rebase_off);
1830 dyld.rebase_size = expected_size;
1831
1832 const bind_off = self.findFreeSpaceLinkedit(expected_size, 1);
1833 log.debug("found binding info free space 0x{x} to 0x{x}", .{ bind_off, bind_off + expected_size });
1834 dyld.bind_off = @intCast(u32, bind_off);
1835 dyld.bind_size = expected_size;
1836
1837 const lazy_bind_off = self.findFreeSpaceLinkedit(expected_size, 1);
1838 log.debug("found lazy binding info free space 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + expected_size });
1839 dyld.lazy_bind_off = @intCast(u32, lazy_bind_off);
1840 dyld.lazy_bind_size = expected_size;
1841
1842 const export_off = self.findFreeSpaceLinkedit(expected_size, 1);
1843 log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + expected_size });
1844 dyld.export_off = @intCast(u32, export_off);
1845 dyld.export_size = expected_size;
1846
15741847 self.header_dirty = true;
15751848 self.load_commands_dirty = true;
15761849 }
15771850 if (self.symtab_cmd_index == null) {
15781851 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
15791852
1853 try self.load_commands.append(self.base.allocator, .{
1854 .Symtab = .{
1855 .cmd = macho.LC_SYMTAB,
1856 .cmdsize = @sizeOf(macho.symtab_command),
1857 .symoff = 0,
1858 .nsyms = 0,
1859 .stroff = 0,
1860 .strsize = 0,
1861 },
1862 });
1863
1864 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1865
15801866 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);
15811867 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
1582
15831868 log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });
1869 symtab.symoff = @intCast(u32, symtab_off);
1870 symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint);
15841871
15851872 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.
15861873 const strtab_size = self.string_table.items.len;
15871874 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1);
1588
15891875 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });
1876 symtab.stroff = @intCast(u32, strtab_off);
1877 symtab.strsize = @intCast(u32, strtab_size);
15901878
1591 try self.load_commands.append(self.base.allocator, .{
1592 .Symtab = .{
1593 .cmd = macho.LC_SYMTAB,
1594 .cmdsize = @sizeOf(macho.symtab_command),
1595 .symoff = @intCast(u32, symtab_off),
1596 .nsyms = @intCast(u32, self.base.options.symbol_count_hint),
1597 .stroff = @intCast(u32, strtab_off),
1598 .strsize = @intCast(u32, strtab_size),
1599 },
1600 });
16011879 self.header_dirty = true;
16021880 self.load_commands_dirty = true;
16031881 self.string_table_dirty = true;
......@@ -1605,7 +1883,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {
16051883 if (self.dysymtab_cmd_index == null) {
16061884 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
16071885
1608 // TODO Preallocate space for indirect symbol table.
1886 // Preallocate space for indirect symbol table.
1887 const indsymtab_size = self.base.options.symbol_count_hint * @sizeOf(u64); // Each entry is just a u64.
1888 const indsymtab_off = self.findFreeSpaceLinkedit(indsymtab_size, @sizeOf(u64));
1889
1890 log.debug("found indirect symbol table free space 0x{x} to 0x{x}", .{ indsymtab_off, indsymtab_off + indsymtab_size });
16091891
16101892 try self.load_commands.append(self.base.allocator, .{
16111893 .Dysymtab = .{
......@@ -1623,8 +1905,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
16231905 .nmodtab = 0,
16241906 .extrefsymoff = 0,
16251907 .nextrefsyms = 0,
1626 .indirectsymoff = 0,
1627 .nindirectsyms = 0,
1908 .indirectsymoff = @intCast(u32, indsymtab_off),
1909 .nindirectsyms = @intCast(u32, self.base.options.symbol_count_hint),
16281910 .extreloff = 0,
16291911 .nextrel = 0,
16301912 .locreloff = 0,
......@@ -1752,16 +2034,73 @@ pub fn populateMissingMetadata(self: *MachO) !void {
17522034 self.header_dirty = true;
17532035 self.load_commands_dirty = true;
17542036 }
1755 if (self.dyld_stub_binder_index == null) {
1756 self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len);
1757 const name = try self.makeString("dyld_stub_binder");
1758 try self.undef_symbols.append(self.base.allocator, .{
1759 .n_strx = name,
1760 .n_type = macho.N_UNDF | macho.N_EXT,
1761 .n_sect = 0,
1762 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,
1763 .n_value = 0,
2037 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {
2038 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);
2039 const name = try std.fmt.allocPrint(self.base.allocator, "dyld_stub_binder", .{});
2040 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
2041 .name = name,
2042 .dylib_ordinal = 1, // TODO this is currently hardcoded.
2043 .segment = self.data_const_segment_cmd_index.?,
2044 .offset = index * @sizeOf(u64),
17642045 });
2046 self.binding_info_dirty = true;
2047 }
2048 if (self.stub_helper_stubs_start_off == null) {
2049 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2050 const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?];
2051 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2052 const data = &data_segment.sections.items[self.data_section_index.?];
2053 const data_const_segment = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2054 const got = &data_const_segment.sections.items[self.data_got_section_index.?];
2055 switch (self.base.options.target.cpu.arch) {
2056 .x86_64 => {
2057 const code_size = 15;
2058 var code: [code_size]u8 = undefined;
2059 // lea %r11, [rip + disp]
2060 code[0] = 0x4c;
2061 code[1] = 0x8d;
2062 code[2] = 0x1d;
2063 {
2064 const displacement = @intCast(u32, data.addr - stub_helper.addr - 7);
2065 mem.writeIntLittle(u32, code[3..7], displacement);
2066 }
2067 // push %r11
2068 code[7] = 0x41;
2069 code[8] = 0x53;
2070 // jmp [rip + disp]
2071 code[9] = 0xff;
2072 code[10] = 0x25;
2073 {
2074 const displacement = @intCast(u32, got.addr - stub_helper.addr - code_size);
2075 mem.writeIntLittle(u32, code[11..], displacement);
2076 }
2077 self.stub_helper_stubs_start_off = stub_helper.offset + code_size;
2078 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2079 },
2080 .aarch64 => {
2081 var code: [4 * @sizeOf(u32)]u8 = undefined;
2082 {
2083 const displacement = data.addr - stub_helper.addr;
2084 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.adr(.x17, @intCast(i21, displacement)).toU32());
2085 }
2086 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.stp(
2087 .x16,
2088 .x17,
2089 aarch64.Register.sp,
2090 aarch64.Instruction.LoadStorePairOffset.pre_index(-16),
2091 ).toU32());
2092 {
2093 const displacement = got.addr - stub_helper.addr - 2 * @sizeOf(u32);
2094 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.ldr(.x16, .{
2095 .literal = @intCast(u19, displacement / 4),
2096 }).toU32());
2097 }
2098 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.br(.x16).toU32());
2099 self.stub_helper_stubs_start_off = stub_helper.offset + 4 * @sizeOf(u32);
2100 try self.base.file.?.pwriteAll(&code, stub_helper.offset);
2101 },
2102 else => unreachable,
2103 }
17652104 }
17662105}
17672106
......@@ -1877,7 +2216,7 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 {
18772216 return buf;
18782217}
18792218
1880fn makeString(self: *MachO, bytes: []const u8) !u32 {
2219pub fn makeString(self: *MachO, bytes: []const u8) !u32 {
18812220 try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);
18822221 const result = @intCast(u32, self.string_table.items.len);
18832222 self.string_table.appendSliceAssumeCapacity(bytes);
......@@ -1907,16 +2246,13 @@ const NextSegmentAddressAndOffset = struct {
19072246};
19082247
19092248fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
1910 const prev_segment_idx = blk: {
1911 if (self.data_segment_cmd_index) |idx| {
1912 break :blk idx;
1913 } else if (self.text_segment_cmd_index) |idx| {
1914 break :blk idx;
1915 } else {
1916 unreachable; // unhandled LC_SEGMENT_64 load command before __TEXT
2249 var prev_segment_idx: ?usize = null; // We use optional here for safety.
2250 for (self.load_commands.items) |cmd, i| {
2251 if (cmd == .Segment) {
2252 prev_segment_idx = i;
19172253 }
1918 };
1919 const prev_segment = self.load_commands.items[prev_segment_idx].Segment;
2254 }
2255 const prev_segment = self.load_commands.items[prev_segment_idx.?].Segment;
19202256 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;
19212257 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;
19222258 return .{
......@@ -2100,11 +2436,98 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
21002436 try self.base.file.?.pwriteAll(&code, off);
21012437}
21022438
2439fn writeLazySymbolPointer(self: *MachO, index: u32) !void {
2440 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2441 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
2442 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2443 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2444
2445 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
2446 .x86_64 => 10,
2447 .aarch64 => 3 * @sizeOf(u32),
2448 else => unreachable,
2449 };
2450 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
2451 const end = stub_helper.addr + stub_off - stub_helper.offset;
2452 var buf: [@sizeOf(u64)]u8 = undefined;
2453 mem.writeIntLittle(u64, &buf, end);
2454 const off = la_symbol_ptr.offset + index * @sizeOf(u64);
2455 log.debug("writing lazy symbol pointer entry 0x{x} at 0x{x}", .{ end, off });
2456 try self.base.file.?.pwriteAll(&buf, off);
2457}
2458
2459fn writeStub(self: *MachO, index: u32) !void {
2460 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2461 const stubs = text_segment.sections.items[self.stubs_section_index.?];
2462 const data_segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2463 const la_symbol_ptr = data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2464
2465 const stub_off = stubs.offset + index * stubs.reserved2;
2466 const stub_addr = stubs.addr + index * stubs.reserved2;
2467 const la_ptr_addr = la_symbol_ptr.addr + index * @sizeOf(u64);
2468 log.debug("writing stub at 0x{x}", .{stub_off});
2469 var code = try self.base.allocator.alloc(u8, stubs.reserved2);
2470 defer self.base.allocator.free(code);
2471 switch (self.base.options.target.cpu.arch) {
2472 .x86_64 => {
2473 const displacement = @intCast(u32, la_ptr_addr - stub_addr - stubs.reserved2);
2474 // jmp
2475 code[0] = 0xff;
2476 code[1] = 0x25;
2477 mem.writeIntLittle(u32, code[2..][0..4], displacement);
2478 },
2479 .aarch64 => {
2480 const displacement = la_ptr_addr - stub_addr;
2481 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.x16, .{
2482 .literal = @intCast(u19, displacement / 4),
2483 }).toU32());
2484 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.br(.x16).toU32());
2485 },
2486 else => unreachable,
2487 }
2488 try self.base.file.?.pwriteAll(code, stub_off);
2489}
2490
2491fn writeStubInStubHelper(self: *MachO, index: u32) !void {
2492 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2493 const stub_helper = text_segment.sections.items[self.stub_helper_section_index.?];
2494
2495 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
2496 .x86_64 => 10,
2497 .aarch64 => 3 * @sizeOf(u32),
2498 else => unreachable,
2499 };
2500 const stub_off = self.stub_helper_stubs_start_off.? + index * stub_size;
2501 var code = try self.base.allocator.alloc(u8, stub_size);
2502 defer self.base.allocator.free(code);
2503 switch (self.base.options.target.cpu.arch) {
2504 .x86_64 => {
2505 const displacement = @intCast(i32, @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - stub_size);
2506 // pushq
2507 code[0] = 0x68;
2508 mem.writeIntLittle(u32, code[1..][0..4], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2509 // jmpq
2510 code[5] = 0xe9;
2511 mem.writeIntLittle(u32, code[6..][0..4], @bitCast(u32, displacement));
2512 },
2513 .aarch64 => {
2514 const displacement = @intCast(i64, stub_helper.offset) - @intCast(i64, stub_off) - 4;
2515 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.ldr(.w16, .{
2516 .literal = 0x2,
2517 }).toU32());
2518 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(@intCast(i28, displacement)).toU32());
2519 mem.writeIntLittle(u32, code[8..12], 0x0); // Just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
2520 },
2521 else => unreachable,
2522 }
2523 try self.base.file.?.pwriteAll(code, stub_off);
2524}
2525
21032526fn relocateSymbolTable(self: *MachO) !void {
21042527 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
21052528 const nlocals = self.local_symbols.items.len;
21062529 const nglobals = self.global_symbols.items.len;
2107 const nundefs = self.undef_symbols.items.len;
2530 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;
21082531 const nsyms = nlocals + nglobals + nundefs;
21092532
21102533 if (symtab.nsyms < nsyms) {
......@@ -2149,7 +2572,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
21492572 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
21502573 const nlocals = self.local_symbols.items.len;
21512574 const nglobals = self.global_symbols.items.len;
2152 const nundefs = self.undef_symbols.items.len;
2575
2576 const nundefs = self.extern_lazy_symbols.items().len + self.extern_nonlazy_symbols.items().len;
2577 var undefs = std.ArrayList(macho.nlist_64).init(self.base.allocator);
2578 defer undefs.deinit();
2579 try undefs.ensureCapacity(nundefs);
2580 for (self.extern_lazy_symbols.items()) |entry| {
2581 const name = try self.makeString(entry.key);
2582 undefs.appendAssumeCapacity(.{
2583 .n_strx = name,
2584 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2585 .n_sect = 0,
2586 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
2587 .n_value = 0,
2588 });
2589 }
2590 for (self.extern_nonlazy_symbols.items()) |entry| {
2591 const name = try self.makeString(entry.key);
2592 undefs.appendAssumeCapacity(.{
2593 .n_strx = name,
2594 .n_type = std.macho.N_UNDF | std.macho.N_EXT,
2595 .n_sect = 0,
2596 .n_desc = std.macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | std.macho.N_SYMBOL_RESOLVER,
2597 .n_value = 0,
2598 });
2599 }
21532600
21542601 const locals_off = symtab.symoff;
21552602 const locals_size = nlocals * @sizeOf(macho.nlist_64);
......@@ -2161,8 +2608,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
21612608
21622609 const undefs_off = globals_off + globals_size;
21632610 const undefs_size = nundefs * @sizeOf(macho.nlist_64);
2164 log.debug("writing undef symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
2165 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.undef_symbols.items), undefs_off);
2611 log.debug("writing extern symbols from 0x{x} to 0x{x}", .{ undefs_off, undefs_size + undefs_off });
2612 try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
21662613
21672614 // Update dynamic symbol table.
21682615 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
......@@ -2174,6 +2621,49 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
21742621 self.load_commands_dirty = true;
21752622}
21762623
2624fn writeIndirectSymbolTable(self: *MachO) !void {
2625 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
2626 const stubs = &text_segment.sections.items[self.stubs_section_index.?];
2627 const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2628 const got = &data_const_seg.sections.items[self.data_got_section_index.?];
2629 const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
2630 const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
2631 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
2632 dysymtab.nindirectsyms = 0;
2633 // TODO check if we have allocated enough size.
2634
2635 var buf: [@sizeOf(u32)]u8 = undefined;
2636 var off = dysymtab.indirectsymoff;
2637
2638 stubs.reserved1 = 0;
2639 for (self.extern_lazy_symbols.items()) |_, i| {
2640 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2641 mem.writeIntLittle(u32, &buf, symtab_idx);
2642 try self.base.file.?.pwriteAll(&buf, off);
2643 off += @sizeOf(u32);
2644 dysymtab.nindirectsyms += 1;
2645 }
2646
2647 const base_id = @intCast(u32, self.extern_lazy_symbols.items().len);
2648 got.reserved1 = base_id;
2649 for (self.extern_nonlazy_symbols.items()) |_, i| {
2650 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
2651 mem.writeIntLittle(u32, &buf, symtab_idx);
2652 try self.base.file.?.pwriteAll(&buf, off);
2653 off += @sizeOf(u32);
2654 dysymtab.nindirectsyms += 1;
2655 }
2656
2657 la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len);
2658 for (self.extern_lazy_symbols.items()) |_, i| {
2659 const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
2660 mem.writeIntLittle(u32, &buf, symtab_idx);
2661 try self.base.file.?.pwriteAll(&buf, off);
2662 off += @sizeOf(u32);
2663 dysymtab.nindirectsyms += 1;
2664 }
2665}
2666
21772667fn writeCodeSignaturePadding(self: *MachO) !void {
21782668 const tracy = trace(@src());
21792669 defer tracy.end();
......@@ -2272,18 +2762,63 @@ fn writeExportTrie(self: *MachO) !void {
22722762 self.export_info_dirty = false;
22732763}
22742764
2765fn writeRebaseInfoTable(self: *MachO) !void {
2766 if (!self.rebase_info_dirty) return;
2767
2768 const tracy = trace(@src());
2769 defer tracy.end();
2770
2771 var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len);
2772 defer self.base.allocator.free(symbols);
2773
2774 for (self.extern_lazy_symbols.items()) |*entry, i| {
2775 symbols[i] = &entry.value;
2776 }
2777
2778 const size = try rebaseInfoSize(symbols);
2779 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2780 defer self.base.allocator.free(buffer);
2781
2782 var stream = std.io.fixedBufferStream(buffer);
2783 try writeRebaseInfo(symbols, stream.writer());
2784
2785 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2786 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2787 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);
2788 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
2789
2790 if (needed_size > allocated_size) {
2791 dyld_info.rebase_off = 0;
2792 dyld_info.rebase_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
2793 }
2794
2795 dyld_info.rebase_size = @intCast(u32, needed_size);
2796 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });
2797
2798 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);
2799 self.load_commands_dirty = true;
2800 self.rebase_info_dirty = false;
2801}
2802
22752803fn writeBindingInfoTable(self: *MachO) !void {
22762804 if (!self.binding_info_dirty) return;
22772805
22782806 const tracy = trace(@src());
22792807 defer tracy.end();
22802808
2281 const size = try self.binding_info_table.calcSize();
2809 var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_nonlazy_symbols.items().len);
2810 defer self.base.allocator.free(symbols);
2811
2812 for (self.extern_nonlazy_symbols.items()) |*entry, i| {
2813 symbols[i] = &entry.value;
2814 }
2815
2816 const size = try bindInfoSize(symbols);
22822817 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
22832818 defer self.base.allocator.free(buffer);
22842819
22852820 var stream = std.io.fixedBufferStream(buffer);
2286 try self.binding_info_table.write(stream.writer());
2821 try writeBindInfo(symbols, stream.writer());
22872822
22882823 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
22892824 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
......@@ -2306,12 +2841,19 @@ fn writeBindingInfoTable(self: *MachO) !void {
23062841fn writeLazyBindingInfoTable(self: *MachO) !void {
23072842 if (!self.lazy_binding_info_dirty) return;
23082843
2309 const size = try self.lazy_binding_info_table.calcSize();
2844 var symbols = try self.base.allocator.alloc(*const ExternSymbol, self.extern_lazy_symbols.items().len);
2845 defer self.base.allocator.free(symbols);
2846
2847 for (self.extern_lazy_symbols.items()) |*entry, i| {
2848 symbols[i] = &entry.value;
2849 }
2850
2851 const size = try lazyBindInfoSize(symbols);
23102852 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
23112853 defer self.base.allocator.free(buffer);
23122854
23132855 var stream = std.io.fixedBufferStream(buffer);
2314 try self.lazy_binding_info_table.write(stream.writer());
2856 try writeLazyBindInfo(symbols, stream.writer());
23152857
23162858 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
23172859 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
......@@ -2327,10 +2869,78 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
23272869 log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size });
23282870
23292871 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
2872 try self.populateLazyBindOffsetsInStubHelper(buffer);
23302873 self.load_commands_dirty = true;
23312874 self.lazy_binding_info_dirty = false;
23322875}
23332876
2877fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
2878 if (self.extern_lazy_symbols.items().len == 0) return;
2879
2880 var stream = std.io.fixedBufferStream(buffer);
2881 var reader = stream.reader();
2882 var offsets = std.ArrayList(u32).init(self.base.allocator);
2883 try offsets.append(0);
2884 defer offsets.deinit();
2885 var valid_block = false;
2886
2887 while (true) {
2888 const inst = reader.readByte() catch |err| switch (err) {
2889 error.EndOfStream => break,
2890 else => return err,
2891 };
2892 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
2893 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
2894
2895 switch (opcode) {
2896 macho.BIND_OPCODE_DO_BIND => {
2897 valid_block = true;
2898 },
2899 macho.BIND_OPCODE_DONE => {
2900 if (valid_block) {
2901 const offset = try stream.getPos();
2902 try offsets.append(@intCast(u32, offset));
2903 }
2904 valid_block = false;
2905 },
2906 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
2907 var next = try reader.readByte();
2908 while (next != @as(u8, 0)) {
2909 next = try reader.readByte();
2910 }
2911 },
2912 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
2913 _ = try std.leb.readULEB128(u64, reader);
2914 },
2915 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
2916 _ = try std.leb.readULEB128(u64, reader);
2917 },
2918 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
2919 _ = try std.leb.readILEB128(i64, reader);
2920 },
2921 else => {},
2922 }
2923 }
2924 assert(self.extern_lazy_symbols.items().len <= offsets.items.len);
2925
2926 const stub_size: u4 = switch (self.base.options.target.cpu.arch) {
2927 .x86_64 => 10,
2928 .aarch64 => 3 * @sizeOf(u32),
2929 else => unreachable,
2930 };
2931 const off: u4 = switch (self.base.options.target.cpu.arch) {
2932 .x86_64 => 1,
2933 .aarch64 => 2 * @sizeOf(u32),
2934 else => unreachable,
2935 };
2936 var buf: [@sizeOf(u32)]u8 = undefined;
2937 for (self.extern_lazy_symbols.items()) |_, i| {
2938 const placeholder_off = self.stub_helper_stubs_start_off.? + i * stub_size + off;
2939 mem.writeIntLittle(u32, &buf, offsets.items[i]);
2940 try self.base.file.?.pwriteAll(&buf, placeholder_off);
2941 }
2942}
2943
23342944fn writeStringTable(self: *MachO) !void {
23352945 if (!self.string_table_dirty) return;
23362946
......@@ -2438,8 +3048,6 @@ fn writeHeader(self: *MachO) !void {
24383048}
24393049
24403050/// Parse MachO contents from existing binary file.
2441/// TODO This method is incomplete and currently parses only the header
2442/// plus the load commands.
24433051fn parseFromFile(self: *MachO, file: fs.File) !void {
24443052 self.base.file = file;
24453053 var reader = file.reader();
......@@ -2464,6 +3072,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
24643072 }
24653073 } else if (parseAndCmpName(&x.inner.segname, "__DATA")) {
24663074 self.data_segment_cmd_index = i;
3075 } else if (parseAndCmpName(&x.inner.segname, "__DATA_CONST")) {
3076 self.data_const_segment_cmd_index = i;
24673077 }
24683078 },
24693079 macho.LC_DYLD_INFO_ONLY => {
......@@ -2549,24 +3159,61 @@ fn parseStringTable(self: *MachO) !void {
25493159 self.string_table.appendSliceAssumeCapacity(buffer);
25503160}
25513161
2552fn parseBindingInfoTable(self: *MachO) !void {
3162fn fixupBindInfo(self: *MachO, dylib_ordinal: u32) !void {
25533163 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
25543164 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);
25553165 defer self.base.allocator.free(buffer);
25563166 const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off);
25573167 assert(nread == buffer.len);
2558
2559 var stream = std.io.fixedBufferStream(buffer);
2560 try self.binding_info_table.read(stream.reader(), self.base.allocator);
3168 try self.fixupInfoCommon(buffer, dylib_ordinal);
3169 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
25613170}
25623171
2563fn parseLazyBindingInfoTable(self: *MachO) !void {
3172fn fixupLazyBindInfo(self: *MachO, dylib_ordinal: u32) !void {
25643173 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
25653174 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);
25663175 defer self.base.allocator.free(buffer);
25673176 const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off);
25683177 assert(nread == buffer.len);
3178 try self.fixupInfoCommon(buffer, dylib_ordinal);
3179 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
3180}
25693181
3182fn fixupInfoCommon(self: *MachO, buffer: []u8, dylib_ordinal: u32) !void {
25703183 var stream = std.io.fixedBufferStream(buffer);
2571 try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);
3184 var reader = stream.reader();
3185
3186 while (true) {
3187 const inst = reader.readByte() catch |err| switch (err) {
3188 error.EndOfStream => break,
3189 else => return err,
3190 };
3191 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
3192 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
3193
3194 switch (opcode) {
3195 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
3196 var next = try reader.readByte();
3197 while (next != @as(u8, 0)) {
3198 next = try reader.readByte();
3199 }
3200 },
3201 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
3202 _ = try std.leb.readULEB128(u64, reader);
3203 },
3204 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
3205 // Perform the fixup.
3206 try stream.seekBy(-1);
3207 var writer = stream.writer();
3208 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, dylib_ordinal));
3209 },
3210 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
3211 _ = try std.leb.readULEB128(u64, reader);
3212 },
3213 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
3214 _ = try std.leb.readILEB128(i64, reader);
3215 },
3216 else => {},
3217 }
3218 }
25723219}
src/link/MachO/DebugSymbols.zig+11
......@@ -39,6 +39,8 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
3939pagezero_segment_cmd_index: ?u16 = null,
4040/// __TEXT segment
4141text_segment_cmd_index: ?u16 = null,
42/// __DATA_CONST segment
43data_const_segment_cmd_index: ?u16 = null,
4244/// __DATA segment
4345data_segment_cmd_index: ?u16 = null,
4446/// __LINKEDIT segment
......@@ -171,6 +173,15 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
171173 self.header_dirty = true;
172174 self.load_commands_dirty = true;
173175 }
176 if (self.data_const_segment_cmd_index == null) outer: {
177 if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional
178 self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
179 const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].Segment;
180 const cmd = try self.copySegmentCommand(allocator, base_cmd);
181 try self.load_commands.append(allocator, .{ .Segment = cmd });
182 self.header_dirty = true;
183 self.load_commands_dirty = true;
184 }
174185 if (self.data_segment_cmd_index == null) outer: {
175186 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
176187 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
src/link/MachO/imports.zig+113-280
......@@ -6,323 +6,156 @@ const mem = std.mem;
66const assert = std.debug.assert;
77const Allocator = mem.Allocator;
88
9/// Table of binding info entries used to tell the dyld which
10/// symbols to bind at loading time.
11pub const BindingInfoTable = struct {
9pub const ExternSymbol = struct {
10 /// Symbol name.
11 /// We own the memory, therefore we'll need to free it by calling `deinit`.
12 /// In self-hosted, we don't expect it to be null ever.
13 /// However, this is for backwards compatibility with LLD when
14 /// we'll be patching things up post mortem.
15 name: ?[]u8 = null,
16
1217 /// Id of the dynamic library where the specified entries can be found.
18 /// Id of 0 means self.
19 /// TODO this should really be an id into the table of all defined
20 /// dylibs.
1321 dylib_ordinal: i64 = 0,
1422
15 /// Binding type; defaults to pointer type.
16 binding_type: u8 = macho.BIND_TYPE_POINTER,
17
18 symbols: std.ArrayListUnmanaged(Symbol) = .{},
19
20 pub const Symbol = struct {
21 /// Symbol name.
22 name: ?[]u8 = null,
23
24 /// Id of the segment where to bind this symbol to.
25 segment: u8,
23 /// Id of the segment where this symbol is defined (will have its address
24 /// resolved).
25 segment: u16 = 0,
2626
27 /// Offset of this symbol wrt to the segment id encoded in `segment`.
28 offset: i64,
27 /// Offset relative to the start address of the `segment`.
28 offset: u32 = 0,
2929
30 /// Addend value (if any).
31 addend: ?i64 = null,
32 };
33
34 pub fn deinit(self: *BindingInfoTable, allocator: *Allocator) void {
35 for (self.symbols.items) |*symbol| {
36 if (symbol.name) |name| {
37 allocator.free(name);
38 }
30 pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void {
31 if (self.name) |name| {
32 allocator.free(name);
3933 }
40 self.symbols.deinit(allocator);
4134 }
35};
4236
43 /// Parse the binding info table from byte stream.
44 pub fn read(self: *BindingInfoTable, reader: anytype, allocator: *Allocator) !void {
45 var symbol: Symbol = .{
46 .segment = 0,
47 .offset = 0,
48 };
49
50 var dylib_ordinal_set = false;
51 var done = false;
52 while (true) {
53 const inst = reader.readByte() catch |err| switch (err) {
54 error.EndOfStream => break,
55 else => return err,
56 };
57 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
58 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
37pub fn rebaseInfoSize(symbols: []*const ExternSymbol) !u64 {
38 var stream = std.io.countingWriter(std.io.null_writer);
39 var writer = stream.writer();
40 var size: u64 = 0;
5941
60 switch (opcode) {
61 macho.BIND_OPCODE_DO_BIND => {
62 try self.symbols.append(allocator, symbol);
63 symbol = .{
64 .segment = 0,
65 .offset = 0,
66 };
67 },
68 macho.BIND_OPCODE_DONE => {
69 done = true;
70 break;
71 },
72 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
73 var name = std.ArrayList(u8).init(allocator);
74 var next = try reader.readByte();
75 while (next != @as(u8, 0)) {
76 try name.append(next);
77 next = try reader.readByte();
78 }
79 symbol.name = name.toOwnedSlice();
80 },
81 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
82 symbol.segment = imm;
83 symbol.offset = try leb.readILEB128(i64, reader);
84 },
85 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
86 assert(!dylib_ordinal_set);
87 self.dylib_ordinal = imm;
88 },
89 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
90 assert(!dylib_ordinal_set);
91 self.dylib_ordinal = try leb.readILEB128(i64, reader);
92 },
93 macho.BIND_OPCODE_SET_TYPE_IMM => {
94 self.binding_type = imm;
95 },
96 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
97 symbol.addend = try leb.readILEB128(i64, reader);
98 },
99 else => {
100 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
101 },
102 }
103 }
104 assert(done);
42 for (symbols) |symbol| {
43 size += 2;
44 try leb.writeILEB128(writer, symbol.offset);
45 size += 1;
10546 }
10647
107 /// Write the binding info table to byte stream.
108 pub fn write(self: BindingInfoTable, writer: anytype) !void {
109 if (self.dylib_ordinal > 15) {
110 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
111 try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));
112 } else if (self.dylib_ordinal > 0) {
113 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal)));
114 } else {
115 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, self.dylib_ordinal)));
116 }
117 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, self.binding_type));
118
119 for (self.symbols.items) |symbol| {
120 if (symbol.name) |name| {
121 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
122 try writer.writeAll(name);
123 try writer.writeByte(0);
124 }
48 size += 1 + stream.bytes_written;
49 return size;
50}
12551
126 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
127 try leb.writeILEB128(writer, symbol.offset);
128
129 if (symbol.addend) |addend| {
130 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
131 try leb.writeILEB128(writer, addend);
132 }
133
134 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
135 }
136
137 try writer.writeByte(macho.BIND_OPCODE_DONE);
52pub fn writeRebaseInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
53 for (symbols) |symbol| {
54 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
55 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
56 try leb.writeILEB128(writer, symbol.offset);
57 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
13858 }
59 try writer.writeByte(macho.REBASE_OPCODE_DONE);
60}
13961
140 /// Calculate size in bytes of this binding info table.
141 pub fn calcSize(self: *BindingInfoTable) !u64 {
142 var stream = std.io.countingWriter(std.io.null_writer);
143 var writer = stream.writer();
144 var size: u64 = 1;
62pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {
63 var stream = std.io.countingWriter(std.io.null_writer);
64 var writer = stream.writer();
65 var size: u64 = 0;
14566
146 if (self.dylib_ordinal > 15) {
147 try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));
67 for (symbols) |symbol| {
68 size += 1;
69 if (symbol.dylib_ordinal > 15) {
70 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
14871 }
149
15072 size += 1;
15173
152 for (self.symbols.items) |symbol| {
153 if (symbol.name) |name| {
154 size += 1;
155 size += name.len;
156 size += 1;
157 }
158
74 if (symbol.name) |name| {
15975 size += 1;
160 try leb.writeILEB128(writer, symbol.offset);
161
162 if (symbol.addend) |addend| {
163 size += 1;
164 try leb.writeILEB128(writer, addend);
165 }
166
76 size += name.len;
16777 size += 1;
16878 }
16979
170 size += 1 + stream.bytes_written;
171 return size;
80 size += 1;
81 try leb.writeILEB128(writer, symbol.offset);
82 size += 2;
17283 }
173};
174
175/// Table of lazy binding info entries used to tell the dyld which
176/// symbols to lazily bind at first load of a dylib.
177pub const LazyBindingInfoTable = struct {
178 symbols: std.ArrayListUnmanaged(Symbol) = .{},
17984
180 pub const Symbol = struct {
181 /// Symbol name.
182 name: ?[]u8 = null,
85 size += stream.bytes_written;
86 return size;
87}
18388
184 /// Offset of this symbol wrt to the segment id encoded in `segment`.
185 offset: i64,
186
187 /// Id of the dylib where this symbol is expected to reside.
188 /// Positive ordinals point at dylibs imported with LC_LOAD_DYLIB,
189 /// 0 means this binary, -1 the main executable, and -2 flat lookup.
190 dylib_ordinal: i64,
191
192 /// Id of the segment where to bind this symbol to.
193 segment: u8,
194
195 /// Addend value (if any).
196 addend: ?i64 = null,
197 };
198
199 pub fn deinit(self: *LazyBindingInfoTable, allocator: *Allocator) void {
200 for (self.symbols.items) |*symbol| {
201 if (symbol.name) |name| {
202 allocator.free(name);
203 }
89pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
90 for (symbols) |symbol| {
91 if (symbol.dylib_ordinal > 15) {
92 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
93 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
94 } else if (symbol.dylib_ordinal > 0) {
95 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
96 } else {
97 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
20498 }
205 self.symbols.deinit(allocator);
206 }
99 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
207100
208 /// Parse the binding info table from byte stream.
209 pub fn read(self: *LazyBindingInfoTable, reader: anytype, allocator: *Allocator) !void {
210 var symbol: Symbol = .{
211 .offset = 0,
212 .segment = 0,
213 .dylib_ordinal = 0,
214 };
215
216 var done = false;
217 while (true) {
218 const inst = reader.readByte() catch |err| switch (err) {
219 error.EndOfStream => break,
220 else => return err,
221 };
222 const imm: u8 = inst & macho.BIND_IMMEDIATE_MASK;
223 const opcode: u8 = inst & macho.BIND_OPCODE_MASK;
224
225 switch (opcode) {
226 macho.BIND_OPCODE_DO_BIND => {
227 try self.symbols.append(allocator, symbol);
228 },
229 macho.BIND_OPCODE_DONE => {
230 done = true;
231 symbol = .{
232 .offset = 0,
233 .segment = 0,
234 .dylib_ordinal = 0,
235 };
236 },
237 macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
238 var name = std.ArrayList(u8).init(allocator);
239 var next = try reader.readByte();
240 while (next != @as(u8, 0)) {
241 try name.append(next);
242 next = try reader.readByte();
243 }
244 symbol.name = name.toOwnedSlice();
245 },
246 macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
247 symbol.segment = imm;
248 symbol.offset = try leb.readILEB128(i64, reader);
249 },
250 macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM, macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
251 symbol.dylib_ordinal = imm;
252 },
253 macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
254 symbol.dylib_ordinal = try leb.readILEB128(i64, reader);
255 },
256 macho.BIND_OPCODE_SET_ADDEND_SLEB => {
257 symbol.addend = try leb.readILEB128(i64, reader);
258 },
259 else => {
260 std.log.warn("unhandled BIND_OPCODE_: 0x{x}", .{opcode});
261 },
262 }
101 if (symbol.name) |name| {
102 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
103 try writer.writeAll(name);
104 try writer.writeByte(0);
263105 }
264 assert(done);
265 }
266106
267 /// Write the binding info table to byte stream.
268 pub fn write(self: LazyBindingInfoTable, writer: anytype) !void {
269 for (self.symbols.items) |symbol| {
270 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
271 try leb.writeILEB128(writer, symbol.offset);
272
273 if (symbol.addend) |addend| {
274 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
275 try leb.writeILEB128(writer, addend);
276 }
277
278 if (symbol.dylib_ordinal > 15) {
279 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
280 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
281 } else if (symbol.dylib_ordinal > 0) {
282 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
283 } else {
284 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
285 }
107 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
108 try leb.writeILEB128(writer, symbol.offset);
109 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
110 try writer.writeByte(macho.BIND_OPCODE_DONE);
111 }
112}
286113
287 if (symbol.name) |name| {
288 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
289 try writer.writeAll(name);
290 try writer.writeByte(0);
291 }
114pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {
115 var stream = std.io.countingWriter(std.io.null_writer);
116 var writer = stream.writer();
117 var size: u64 = 0;
292118
293 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
294 try writer.writeByte(macho.BIND_OPCODE_DONE);
119 for (symbols) |symbol| {
120 size += 1;
121 try leb.writeILEB128(writer, symbol.offset);
122 size += 1;
123 if (symbol.dylib_ordinal > 15) {
124 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
295125 }
126 if (symbol.name) |name| {
127 size += 1;
128 size += name.len;
129 size += 1;
130 }
131 size += 2;
296132 }
297133
298 /// Calculate size in bytes of this binding info table.
299 pub fn calcSize(self: *LazyBindingInfoTable) !u64 {
300 var stream = std.io.countingWriter(std.io.null_writer);
301 var writer = stream.writer();
302 var size: u64 = 0;
134 size += stream.bytes_written;
135 return size;
136}
303137
304 for (self.symbols.items) |symbol| {
305 size += 1;
306 try leb.writeILEB128(writer, symbol.offset);
138pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
139 for (symbols) |symbol| {
140 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
141 try leb.writeILEB128(writer, symbol.offset);
307142
308 if (symbol.addend) |addend| {
309 size += 1;
310 try leb.writeILEB128(writer, addend);
311 }
143 if (symbol.dylib_ordinal > 15) {
144 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
145 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
146 } else if (symbol.dylib_ordinal > 0) {
147 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
148 } else {
149 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
150 }
312151
313 size += 1;
314 if (symbol.dylib_ordinal > 15) {
315 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
316 }
317 if (symbol.name) |name| {
318 size += 1;
319 size += name.len;
320 size += 1;
321 }
322 size += 2;
152 if (symbol.name) |name| {
153 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
154 try writer.writeAll(name);
155 try writer.writeByte(0);
323156 }
324157
325 size += stream.bytes_written;
326 return size;
158 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
159 try writer.writeByte(macho.BIND_OPCODE_DONE);
327160 }
328};
161}
test/stage2/aarch64.zig+18
......@@ -199,4 +199,22 @@ pub fn addCases(ctx: *TestContext) !void {
199199 "",
200200 );
201201 }
202
203 {
204 var case = ctx.exe("hello world linked to libc", macos_aarch64);
205
206 // TODO rewrite this test once we handle more int conversions and return args.
207 case.addCompareOutput(
208 \\extern "c" fn write(usize, usize, usize) void;
209 \\extern "c" fn exit(usize) noreturn;
210 \\
211 \\export fn _start() noreturn {
212 \\ write(1, @ptrToInt("Hello,"), 6);
213 \\ write(1, @ptrToInt(" World!\n,"), 8);
214 \\ exit(0);
215 \\}
216 ,
217 "Hello, World!\n",
218 );
219 }
202220}
test/stage2/test.zig+18
......@@ -1495,4 +1495,22 @@ pub fn addCases(ctx: *TestContext) !void {
14951495 \\}
14961496 , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"});
14971497 }
1498
1499 {
1500 var case = ctx.exe("hello world linked to libc", macosx_x64);
1501
1502 // TODO rewrite this test once we handle more int conversions and return args.
1503 case.addCompareOutput(
1504 \\extern "c" fn write(usize, usize, usize) void;
1505 \\extern "c" fn exit(usize) noreturn;
1506 \\
1507 \\export fn _start() noreturn {
1508 \\ write(1, @ptrToInt("Hello,"), 6);
1509 \\ write(1, @ptrToInt(" World!\n,"), 8);
1510 \\ exit(0);
1511 \\}
1512 ,
1513 "Hello, World!\n",
1514 );
1515 }
14981516}