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 {...@@ -1859,8 +1859,47 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1859 },1859 },
1860 else => unreachable, // unsupported architecture on MachO1860 else => unreachable, // unsupported architecture on MachO
1861 }1861 }
1862 } else if (func_value.castTag(.extern_fn)) |_| {1862 } else if (func_value.castTag(.extern_fn)) |func_payload| {
1863 return self.fail(inst.base.src, "TODO implement calling extern functions", .{});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.
1864 } else {1903 } else {
1865 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});1904 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
1866 }1905 }
src/link/MachO.zig+757-110
...@@ -52,6 +52,8 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},...@@ -52,6 +52,8 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
52pagezero_segment_cmd_index: ?u16 = null,52pagezero_segment_cmd_index: ?u16 = null,
53/// __TEXT segment53/// __TEXT segment
54text_segment_cmd_index: ?u16 = null,54text_segment_cmd_index: ?u16 = null,
55/// __DATA_CONST segment
56data_const_segment_cmd_index: ?u16 = null,
55/// __DATA segment57/// __DATA segment
56data_segment_cmd_index: ?u16 = null,58data_segment_cmd_index: ?u16 = null,
57/// __LINKEDIT segment59/// __LINKEDIT segment
...@@ -87,22 +89,34 @@ code_signature_cmd_index: ?u16 = null,...@@ -87,22 +89,34 @@ code_signature_cmd_index: ?u16 = null,
87text_section_index: ?u16 = null,89text_section_index: ?u16 = null,
88/// Index into __TEXT,__ziggot section.90/// Index into __TEXT,__ziggot section.
89got_section_index: ?u16 = null,91got_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,
90/// The absolute address of the entry point.102/// The absolute address of the entry point.
91entry_addr: ?u64 = null,103entry_addr: ?u64 = null,
92104
93/// Table of all local symbols105/// Table of all local symbols
94/// Internally references string table for names (which are optional).106/// Internally references string table for names (which are optional).
95local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},107local_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
96/// Table of all defined global symbols108/// Table of all global symbols
97global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},109global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
98/// Table of all undefined symbols110/// Table of all extern nonlazy symbols, indexed by name.
99undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},111extern_nonlazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
112/// Table of all extern lazy symbols, indexed by name.
113extern_lazy_symbols: std.StringArrayHashMapUnmanaged(ExternSymbol) = .{},
100114
101local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},115local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
102global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},116global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
103offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},117offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
104118
105dyld_stub_binder_index: ?u16 = null,119stub_helper_stubs_start_off: ?u64 = null,
106120
107/// Table of symbol names aka the string table.121/// Table of symbol names aka the string table.
108string_table: std.ArrayListUnmanaged(u8) = .{},122string_table: std.ArrayListUnmanaged(u8) = .{},
...@@ -110,16 +124,12 @@ string_table: std.ArrayListUnmanaged(u8) = .{},...@@ -110,16 +124,12 @@ string_table: std.ArrayListUnmanaged(u8) = .{},
110/// Table of trampolines to the actual symbols in __text section.124/// Table of trampolines to the actual symbols in __text section.
111offset_table: std.ArrayListUnmanaged(u64) = .{},125offset_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
118error_flags: File.ErrorFlags = File.ErrorFlags{},127error_flags: File.ErrorFlags = File.ErrorFlags{},
119128
120offset_table_count_dirty: bool = false,129offset_table_count_dirty: bool = false,
121header_dirty: bool = false,130header_dirty: bool = false,
122load_commands_dirty: bool = false,131load_commands_dirty: bool = false,
132rebase_info_dirty: bool = false,
123binding_info_dirty: bool = false,133binding_info_dirty: bool = false,
124lazy_binding_info_dirty: bool = false,134lazy_binding_info_dirty: bool = false,
125export_info_dirty: bool = false,135export_info_dirty: bool = false,
...@@ -149,6 +159,12 @@ last_text_block: ?*TextBlock = null,...@@ -149,6 +159,12 @@ last_text_block: ?*TextBlock = null,
149/// prior to calling `generateSymbol`, and then immediately deallocated159/// prior to calling `generateSymbol`, and then immediately deallocated
150/// rather than sitting in the global scope.160/// rather than sitting in the global scope.
151pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},161pie_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
153pub const PieFixup = struct {169pub const PieFixup = struct {
154 /// Target address we wanted to address in absolute terms.170 /// Target address we wanted to address in absolute terms.
...@@ -160,6 +176,19 @@ pub const PieFixup = struct {...@@ -160,6 +176,19 @@ pub const PieFixup = struct {
160 len: usize,176 len: usize,
161};177};
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
163/// `alloc_num / alloc_den` is the factor of padding when allocating.192/// `alloc_num / alloc_den` is the factor of padding when allocating.
164pub const alloc_num = 4;193pub const alloc_num = 4;
165pub const alloc_den = 3;194pub const alloc_den = 3;
...@@ -379,10 +408,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -379,10 +408,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
379 main_cmd.entryoff = addr - text_segment.inner.vmaddr;408 main_cmd.entryoff = addr - text_segment.inner.vmaddr;
380 self.load_commands_dirty = true;409 self.load_commands_dirty = true;
381 }410 }
411 try self.writeRebaseInfoTable();
382 try self.writeBindingInfoTable();412 try self.writeBindingInfoTable();
383 try self.writeLazyBindingInfoTable();413 try self.writeLazyBindingInfoTable();
384 try self.writeExportTrie();414 try self.writeExportTrie();
385 try self.writeAllGlobalAndUndefSymbols();415 try self.writeAllGlobalAndUndefSymbols();
416 try self.writeIndirectSymbolTable();
386 try self.writeStringTable();417 try self.writeStringTable();
387 try self.updateLinkeditSegmentSizes();418 try self.updateLinkeditSegmentSizes();
388419
...@@ -418,6 +449,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -418,6 +449,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
418 assert(!self.offset_table_count_dirty);449 assert(!self.offset_table_count_dirty);
419 assert(!self.header_dirty);450 assert(!self.header_dirty);
420 assert(!self.load_commands_dirty);451 assert(!self.load_commands_dirty);
452 assert(!self.rebase_info_dirty);
421 assert(!self.binding_info_dirty);453 assert(!self.binding_info_dirty);
422 assert(!self.lazy_binding_info_dirty);454 assert(!self.lazy_binding_info_dirty);
423 assert(!self.export_info_dirty);455 assert(!self.export_info_dirty);
...@@ -891,42 +923,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {...@@ -891,42 +923,9 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
891 return error.NoSymbolTableFound;923 return error.NoSymbolTableFound;
892 }924 }
893925
894 // Parse dyld info926 // Patch dyld info
895 try self.parseBindingInfoTable();927 try self.fixupBindInfo(next_ordinal);
896 try self.parseLazyBindingInfoTable();928 try self.fixupLazyBindInfo(next_ordinal);
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 }
930929
931 // Write updated load commands and the header930 // Write updated load commands and the header
932 try self.writeLoadCommands();931 try self.writeLoadCommands();
...@@ -1008,14 +1007,20 @@ pub fn deinit(self: *MachO) void {...@@ -1008,14 +1007,20 @@ pub fn deinit(self: *MachO) void {
1008 if (self.d_sym) |*ds| {1007 if (self.d_sym) |*ds| {
1009 ds.deinit(self.base.allocator);1008 ds.deinit(self.base.allocator);
1010 }1009 }
1011 self.binding_info_table.deinit(self.base.allocator);1010 for (self.extern_lazy_symbols.items()) |*entry| {
1012 self.lazy_binding_info_table.deinit(self.base.allocator);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);
1013 self.pie_fixups.deinit(self.base.allocator);1018 self.pie_fixups.deinit(self.base.allocator);
1019 self.stub_fixups.deinit(self.base.allocator);
1014 self.text_block_free_list.deinit(self.base.allocator);1020 self.text_block_free_list.deinit(self.base.allocator);
1015 self.offset_table.deinit(self.base.allocator);1021 self.offset_table.deinit(self.base.allocator);
1016 self.offset_table_free_list.deinit(self.base.allocator);1022 self.offset_table_free_list.deinit(self.base.allocator);
1017 self.string_table.deinit(self.base.allocator);1023 self.string_table.deinit(self.base.allocator);
1018 self.undef_symbols.deinit(self.base.allocator);
1019 self.global_symbols.deinit(self.base.allocator);1024 self.global_symbols.deinit(self.base.allocator);
1020 self.global_symbol_free_list.deinit(self.base.allocator);1025 self.global_symbol_free_list.deinit(self.base.allocator);
1021 self.local_symbols.deinit(self.base.allocator);1026 self.local_symbols.deinit(self.base.allocator);
...@@ -1211,7 +1216,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1211,7 +1216,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1211 }1216 }
12121217
1213 // Perform PIE fixups (if any)1218 // 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;
1215 const got_section = text_segment.sections.items[self.got_section_index.?];1220 const got_section = text_segment.sections.items[self.got_section_index.?];
1216 while (self.pie_fixups.popOrNull()) |fixup| {1221 while (self.pie_fixups.popOrNull()) |fixup| {
1217 const target_addr = fixup.address;1222 const target_addr = fixup.address;
...@@ -1231,6 +1236,38 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1231,6 +1236,38 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1231 }1236 }
1232 }1237 }
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
1234 const text_section = text_segment.sections.items[self.text_section_index.?];1271 const text_section = text_segment.sections.items[self.text_section_index.?];
1235 const section_offset = symbol.n_value - text_section.addr;1272 const section_offset = symbol.n_value - text_section.addr;
1236 const file_offset = text_section.offset + section_offset;1273 const file_offset = text_section.offset + section_offset;
...@@ -1435,7 +1472,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1435,7 +1472,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
14351472
1436 const program_code_size_hint = self.base.options.program_code_size_hint;1473 const program_code_size_hint = self.base.options.program_code_size_hint;
1437 const offset_table_size_hint = @sizeOf(u64) * self.base.options.symbol_count_hint;1474 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;
1439 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size);1476 const needed_size = mem.alignForwardGeneric(u64, satMul(ideal_size, alloc_num) / alloc_den, self.page_size);
14401477
1441 log.debug("found __TEXT segment free space 0x{x} to 0x{x}", .{ 0, needed_size });1478 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 {...@@ -1492,9 +1529,13 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1492 }1529 }
1493 if (self.got_section_index == null) {1530 if (self.got_section_index == null) {
1494 const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;1531 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.?];
1496 self.got_section_index = @intCast(u16, text_segment.sections.items.len);1532 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 };
1498 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;1539 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1499 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;1540 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1500 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);1541 const off = text_segment.findFreeSpace(needed_size, @alignOf(u64), self.header_pad);
...@@ -1508,7 +1549,220 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1508,7 +1549,220 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1508 .addr = text_segment.inner.vmaddr + off,1549 .addr = text_segment.inner.vmaddr + off,
1509 .size = needed_size,1550 .size = needed_size,
1510 .offset = @intCast(u32, off),1551 .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)
1512 .reloff = 0,1766 .reloff = 0,
1513 .nreloc = 0,1767 .nreloc = 0,
1514 .flags = flags,1768 .flags = flags,
...@@ -1549,12 +1803,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1549,12 +1803,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1549 if (self.dyld_info_cmd_index == null) {1803 if (self.dyld_info_cmd_index == null) {
1550 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);1804 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
1558 try self.load_commands.append(self.base.allocator, .{1806 try self.load_commands.append(self.base.allocator, .{
1559 .DyldInfoOnly = .{1807 .DyldInfoOnly = .{
1560 .cmd = macho.LC_DYLD_INFO_ONLY,1808 .cmd = macho.LC_DYLD_INFO_ONLY,
...@@ -1567,37 +1815,67 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1567,37 +1815,67 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1567 .weak_bind_size = 0,1815 .weak_bind_size = 0,
1568 .lazy_bind_off = 0,1816 .lazy_bind_off = 0,
1569 .lazy_bind_size = 0,1817 .lazy_bind_size = 0,
1570 .export_off = @intCast(u32, export_off),1818 .export_off = 0,
1571 .export_size = export_size,1819 .export_size = 0,
1572 },1820 },
1573 });1821 });
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
1574 self.header_dirty = true;1847 self.header_dirty = true;
1575 self.load_commands_dirty = true;1848 self.load_commands_dirty = true;
1576 }1849 }
1577 if (self.symtab_cmd_index == null) {1850 if (self.symtab_cmd_index == null) {
1578 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);1851 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
1580 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);1866 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);
1581 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));1867 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
1582
1583 log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });1868 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
1585 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.1872 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.
1586 const strtab_size = self.string_table.items.len;1873 const strtab_size = self.string_table.items.len;
1587 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1);1874 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1);
1588
1589 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });1875 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 });
1601 self.header_dirty = true;1879 self.header_dirty = true;
1602 self.load_commands_dirty = true;1880 self.load_commands_dirty = true;
1603 self.string_table_dirty = true;1881 self.string_table_dirty = true;
...@@ -1605,7 +1883,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1605,7 +1883,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1605 if (self.dysymtab_cmd_index == null) {1883 if (self.dysymtab_cmd_index == null) {
1606 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);1884 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
1610 try self.load_commands.append(self.base.allocator, .{1892 try self.load_commands.append(self.base.allocator, .{
1611 .Dysymtab = .{1893 .Dysymtab = .{
...@@ -1623,8 +1905,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1623,8 +1905,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1623 .nmodtab = 0,1905 .nmodtab = 0,
1624 .extrefsymoff = 0,1906 .extrefsymoff = 0,
1625 .nextrefsyms = 0,1907 .nextrefsyms = 0,
1626 .indirectsymoff = 0,1908 .indirectsymoff = @intCast(u32, indsymtab_off),
1627 .nindirectsyms = 0,1909 .nindirectsyms = @intCast(u32, self.base.options.symbol_count_hint),
1628 .extreloff = 0,1910 .extreloff = 0,
1629 .nextrel = 0,1911 .nextrel = 0,
1630 .locreloff = 0,1912 .locreloff = 0,
...@@ -1752,16 +2034,73 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1752,16 +2034,73 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1752 self.header_dirty = true;2034 self.header_dirty = true;
1753 self.load_commands_dirty = true;2035 self.load_commands_dirty = true;
1754 }2036 }
1755 if (self.dyld_stub_binder_index == null) {2037 if (!self.extern_nonlazy_symbols.contains("dyld_stub_binder")) {
1756 self.dyld_stub_binder_index = @intCast(u16, self.undef_symbols.items.len);2038 const index = @intCast(u32, self.extern_nonlazy_symbols.items().len);
1757 const name = try self.makeString("dyld_stub_binder");2039 const name = try std.fmt.allocPrint(self.base.allocator, "dyld_stub_binder", .{});
1758 try self.undef_symbols.append(self.base.allocator, .{2040 try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
1759 .n_strx = name,2041 .name = name,
1760 .n_type = macho.N_UNDF | macho.N_EXT,2042 .dylib_ordinal = 1, // TODO this is currently hardcoded.
1761 .n_sect = 0,2043 .segment = self.data_const_segment_cmd_index.?,
1762 .n_desc = macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY | macho.N_SYMBOL_RESOLVER,2044 .offset = index * @sizeOf(u64),
1763 .n_value = 0,
1764 });2045 });
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 }
1765 }2104 }
1766}2105}
17672106
...@@ -1877,7 +2216,7 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 {...@@ -1877,7 +2216,7 @@ pub fn makeStaticString(comptime bytes: []const u8) [16]u8 {
1877 return buf;2216 return buf;
1878}2217}
18792218
1880fn makeString(self: *MachO, bytes: []const u8) !u32 {2219pub fn makeString(self: *MachO, bytes: []const u8) !u32 {
1881 try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);2220 try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);
1882 const result = @intCast(u32, self.string_table.items.len);2221 const result = @intCast(u32, self.string_table.items.len);
1883 self.string_table.appendSliceAssumeCapacity(bytes);2222 self.string_table.appendSliceAssumeCapacity(bytes);
...@@ -1907,16 +2246,13 @@ const NextSegmentAddressAndOffset = struct {...@@ -1907,16 +2246,13 @@ const NextSegmentAddressAndOffset = struct {
1907};2246};
19082247
1909fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {2248fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
1910 const prev_segment_idx = blk: {2249 var prev_segment_idx: ?usize = null; // We use optional here for safety.
1911 if (self.data_segment_cmd_index) |idx| {2250 for (self.load_commands.items) |cmd, i| {
1912 break :blk idx;2251 if (cmd == .Segment) {
1913 } else if (self.text_segment_cmd_index) |idx| {2252 prev_segment_idx = i;
1914 break :blk idx;
1915 } else {
1916 unreachable; // unhandled LC_SEGMENT_64 load command before __TEXT
1917 }2253 }
1918 };2254 }
1919 const prev_segment = self.load_commands.items[prev_segment_idx].Segment;2255 const prev_segment = self.load_commands.items[prev_segment_idx.?].Segment;
1920 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;2256 const address = prev_segment.inner.vmaddr + prev_segment.inner.vmsize;
1921 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;2257 const offset = prev_segment.inner.fileoff + prev_segment.inner.filesize;
1922 return .{2258 return .{
...@@ -2100,11 +2436,98 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {...@@ -2100,11 +2436,98 @@ fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
2100 try self.base.file.?.pwriteAll(&code, off);2436 try self.base.file.?.pwriteAll(&code, off);
2101}2437}
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
2103fn relocateSymbolTable(self: *MachO) !void {2526fn relocateSymbolTable(self: *MachO) !void {
2104 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2527 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2105 const nlocals = self.local_symbols.items.len;2528 const nlocals = self.local_symbols.items.len;
2106 const nglobals = self.global_symbols.items.len;2529 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;
2108 const nsyms = nlocals + nglobals + nundefs;2531 const nsyms = nlocals + nglobals + nundefs;
21092532
2110 if (symtab.nsyms < nsyms) {2533 if (symtab.nsyms < nsyms) {
...@@ -2149,7 +2572,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2149,7 +2572,31 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
2149 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;2572 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
2150 const nlocals = self.local_symbols.items.len;2573 const nlocals = self.local_symbols.items.len;
2151 const nglobals = self.global_symbols.items.len;2574 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
2154 const locals_off = symtab.symoff;2601 const locals_off = symtab.symoff;
2155 const locals_size = nlocals * @sizeOf(macho.nlist_64);2602 const locals_size = nlocals * @sizeOf(macho.nlist_64);
...@@ -2161,8 +2608,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2161,8 +2608,8 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
21612608
2162 const undefs_off = globals_off + globals_size;2609 const undefs_off = globals_off + globals_size;
2163 const undefs_size = nundefs * @sizeOf(macho.nlist_64);2610 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 });2611 log.debug("writing extern 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);2612 try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off);
21662613
2167 // Update dynamic symbol table.2614 // Update dynamic symbol table.
2168 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;2615 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
...@@ -2174,6 +2621,49 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {...@@ -2174,6 +2621,49 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
2174 self.load_commands_dirty = true;2621 self.load_commands_dirty = true;
2175}2622}
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
2177fn writeCodeSignaturePadding(self: *MachO) !void {2667fn writeCodeSignaturePadding(self: *MachO) !void {
2178 const tracy = trace(@src());2668 const tracy = trace(@src());
2179 defer tracy.end();2669 defer tracy.end();
...@@ -2272,18 +2762,63 @@ fn writeExportTrie(self: *MachO) !void {...@@ -2272,18 +2762,63 @@ fn writeExportTrie(self: *MachO) !void {
2272 self.export_info_dirty = false;2762 self.export_info_dirty = false;
2273}2763}
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
2275fn writeBindingInfoTable(self: *MachO) !void {2803fn writeBindingInfoTable(self: *MachO) !void {
2276 if (!self.binding_info_dirty) return;2804 if (!self.binding_info_dirty) return;
22772805
2278 const tracy = trace(@src());2806 const tracy = trace(@src());
2279 defer tracy.end();2807 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);
2282 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));2817 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2283 defer self.base.allocator.free(buffer);2818 defer self.base.allocator.free(buffer);
22842819
2285 var stream = std.io.fixedBufferStream(buffer);2820 var stream = std.io.fixedBufferStream(buffer);
2286 try self.binding_info_table.write(stream.writer());2821 try writeBindInfo(symbols, stream.writer());
22872822
2288 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;2823 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2289 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2824 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
...@@ -2306,12 +2841,19 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2306,12 +2841,19 @@ fn writeBindingInfoTable(self: *MachO) !void {
2306fn writeLazyBindingInfoTable(self: *MachO) !void {2841fn writeLazyBindingInfoTable(self: *MachO) !void {
2307 if (!self.lazy_binding_info_dirty) return;2842 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);
2310 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));2852 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2311 defer self.base.allocator.free(buffer);2853 defer self.base.allocator.free(buffer);
23122854
2313 var stream = std.io.fixedBufferStream(buffer);2855 var stream = std.io.fixedBufferStream(buffer);
2314 try self.lazy_binding_info_table.write(stream.writer());2856 try writeLazyBindInfo(symbols, stream.writer());
23152857
2316 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;2858 const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2317 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;2859 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
...@@ -2327,10 +2869,78 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {...@@ -2327,10 +2869,78 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
2327 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 });2869 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
2329 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);2871 try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off);
2872 try self.populateLazyBindOffsetsInStubHelper(buffer);
2330 self.load_commands_dirty = true;2873 self.load_commands_dirty = true;
2331 self.lazy_binding_info_dirty = false;2874 self.lazy_binding_info_dirty = false;
2332}2875}
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
2334fn writeStringTable(self: *MachO) !void {2944fn writeStringTable(self: *MachO) !void {
2335 if (!self.string_table_dirty) return;2945 if (!self.string_table_dirty) return;
23362946
...@@ -2438,8 +3048,6 @@ fn writeHeader(self: *MachO) !void {...@@ -2438,8 +3048,6 @@ fn writeHeader(self: *MachO) !void {
2438}3048}
24393049
2440/// Parse MachO contents from existing binary file.3050/// Parse MachO contents from existing binary file.
2441/// TODO This method is incomplete and currently parses only the header
2442/// plus the load commands.
2443fn parseFromFile(self: *MachO, file: fs.File) !void {3051fn parseFromFile(self: *MachO, file: fs.File) !void {
2444 self.base.file = file;3052 self.base.file = file;
2445 var reader = file.reader();3053 var reader = file.reader();
...@@ -2464,6 +3072,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {...@@ -2464,6 +3072,8 @@ fn parseFromFile(self: *MachO, file: fs.File) !void {
2464 }3072 }
2465 } else if (parseAndCmpName(&x.inner.segname, "__DATA")) {3073 } else if (parseAndCmpName(&x.inner.segname, "__DATA")) {
2466 self.data_segment_cmd_index = i;3074 self.data_segment_cmd_index = i;
3075 } else if (parseAndCmpName(&x.inner.segname, "__DATA_CONST")) {
3076 self.data_const_segment_cmd_index = i;
2467 }3077 }
2468 },3078 },
2469 macho.LC_DYLD_INFO_ONLY => {3079 macho.LC_DYLD_INFO_ONLY => {
...@@ -2549,24 +3159,61 @@ fn parseStringTable(self: *MachO) !void {...@@ -2549,24 +3159,61 @@ fn parseStringTable(self: *MachO) !void {
2549 self.string_table.appendSliceAssumeCapacity(buffer);3159 self.string_table.appendSliceAssumeCapacity(buffer);
2550}3160}
25513161
2552fn parseBindingInfoTable(self: *MachO) !void {3162fn fixupBindInfo(self: *MachO, dylib_ordinal: u32) !void {
2553 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3163 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2554 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);3164 var buffer = try self.base.allocator.alloc(u8, dyld_info.bind_size);
2555 defer self.base.allocator.free(buffer);3165 defer self.base.allocator.free(buffer);
2556 const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off);3166 const nread = try self.base.file.?.preadAll(buffer, dyld_info.bind_off);
2557 assert(nread == buffer.len);3167 assert(nread == buffer.len);
25583168 try self.fixupInfoCommon(buffer, dylib_ordinal);
2559 var stream = std.io.fixedBufferStream(buffer);3169 try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off);
2560 try self.binding_info_table.read(stream.reader(), self.base.allocator);
2561}3170}
25623171
2563fn parseLazyBindingInfoTable(self: *MachO) !void {3172fn fixupLazyBindInfo(self: *MachO, dylib_ordinal: u32) !void {
2564 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;3173 const dyld_info = self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2565 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);3174 var buffer = try self.base.allocator.alloc(u8, dyld_info.lazy_bind_size);
2566 defer self.base.allocator.free(buffer);3175 defer self.base.allocator.free(buffer);
2567 const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off);3176 const nread = try self.base.file.?.preadAll(buffer, dyld_info.lazy_bind_off);
2568 assert(nread == buffer.len);3177 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 {
2570 var stream = std.io.fixedBufferStream(buffer);3183 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 }
2572}3219}
src/link/MachO/DebugSymbols.zig+11
...@@ -39,6 +39,8 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},...@@ -39,6 +39,8 @@ load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
39pagezero_segment_cmd_index: ?u16 = null,39pagezero_segment_cmd_index: ?u16 = null,
40/// __TEXT segment40/// __TEXT segment
41text_segment_cmd_index: ?u16 = null,41text_segment_cmd_index: ?u16 = null,
42/// __DATA_CONST segment
43data_const_segment_cmd_index: ?u16 = null,
42/// __DATA segment44/// __DATA segment
43data_segment_cmd_index: ?u16 = null,45data_segment_cmd_index: ?u16 = null,
44/// __LINKEDIT segment46/// __LINKEDIT segment
...@@ -171,6 +173,15 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void...@@ -171,6 +173,15 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
171 self.header_dirty = true;173 self.header_dirty = true;
172 self.load_commands_dirty = true;174 self.load_commands_dirty = true;
173 }175 }
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 }
174 if (self.data_segment_cmd_index == null) outer: {185 if (self.data_segment_cmd_index == null) outer: {
175 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional186 if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
176 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);187 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;...@@ -6,323 +6,156 @@ const mem = std.mem;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const Allocator = mem.Allocator;7const Allocator = mem.Allocator;
88
9/// Table of binding info entries used to tell the dyld which9pub const ExternSymbol = struct {
10/// symbols to bind at loading time.10 /// Symbol name.
11pub const BindingInfoTable = struct {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
12 /// Id of the dynamic library where the specified entries can be found.17 /// 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.
13 dylib_ordinal: i64 = 0,21 dylib_ordinal: i64 = 0,
1422
15 /// Binding type; defaults to pointer type.23 /// Id of the segment where this symbol is defined (will have its address
16 binding_type: u8 = macho.BIND_TYPE_POINTER,24 /// resolved).
1725 segment: u16 = 0,
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,
2626
27 /// Offset of this symbol wrt to the segment id encoded in `segment`.27 /// Offset relative to the start address of the `segment`.
28 offset: i64,28 offset: u32 = 0,
2929
30 /// Addend value (if any).30 pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void {
31 addend: ?i64 = null,31 if (self.name) |name| {
32 };32 allocator.free(name);
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 }
39 }33 }
40 self.symbols.deinit(allocator);
41 }34 }
35};
4236
43 /// Parse the binding info table from byte stream.37pub fn rebaseInfoSize(symbols: []*const ExternSymbol) !u64 {
44 pub fn read(self: *BindingInfoTable, reader: anytype, allocator: *Allocator) !void {38 var stream = std.io.countingWriter(std.io.null_writer);
45 var symbol: Symbol = .{39 var writer = stream.writer();
46 .segment = 0,40 var size: u64 = 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;
5941
60 switch (opcode) {42 for (symbols) |symbol| {
61 macho.BIND_OPCODE_DO_BIND => {43 size += 2;
62 try self.symbols.append(allocator, symbol);44 try leb.writeILEB128(writer, symbol.offset);
63 symbol = .{45 size += 1;
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);
105 }46 }
10647
107 /// Write the binding info table to byte stream.48 size += 1 + stream.bytes_written;
108 pub fn write(self: BindingInfoTable, writer: anytype) !void {49 return size;
109 if (self.dylib_ordinal > 15) {50}
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 }
12551
126 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));52pub fn writeRebaseInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
127 try leb.writeILEB128(writer, symbol.offset);53 for (symbols) |symbol| {
12854 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.REBASE_TYPE_POINTER));
129 if (symbol.addend) |addend| {55 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
130 try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);56 try leb.writeILEB128(writer, symbol.offset);
131 try leb.writeILEB128(writer, addend);57 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
132 }
133
134 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
135 }
136
137 try writer.writeByte(macho.BIND_OPCODE_DONE);
138 }58 }
59 try writer.writeByte(macho.REBASE_OPCODE_DONE);
60}
13961
140 /// Calculate size in bytes of this binding info table.62pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {
141 pub fn calcSize(self: *BindingInfoTable) !u64 {63 var stream = std.io.countingWriter(std.io.null_writer);
142 var stream = std.io.countingWriter(std.io.null_writer);64 var writer = stream.writer();
143 var writer = stream.writer();65 var size: u64 = 0;
144 var size: u64 = 1;
14566
146 if (self.dylib_ordinal > 15) {67 for (symbols) |symbol| {
147 try leb.writeULEB128(writer, @bitCast(u64, self.dylib_ordinal));68 size += 1;
69 if (symbol.dylib_ordinal > 15) {
70 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
148 }71 }
149
150 size += 1;72 size += 1;
15173
152 for (self.symbols.items) |symbol| {74 if (symbol.name) |name| {
153 if (symbol.name) |name| {
154 size += 1;
155 size += name.len;
156 size += 1;
157 }
158
159 size += 1;75 size += 1;
160 try leb.writeILEB128(writer, symbol.offset);76 size += name.len;
161
162 if (symbol.addend) |addend| {
163 size += 1;
164 try leb.writeILEB128(writer, addend);
165 }
166
167 size += 1;77 size += 1;
168 }78 }
16979
170 size += 1 + stream.bytes_written;80 size += 1;
171 return size;81 try leb.writeILEB128(writer, symbol.offset);
82 size += 2;
172 }83 }
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 {85 size += stream.bytes_written;
181 /// Symbol name.86 return size;
182 name: ?[]u8 = null,87}
18388
184 /// Offset of this symbol wrt to the segment id encoded in `segment`.89pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
185 offset: i64,90 for (symbols) |symbol| {
18691 if (symbol.dylib_ordinal > 15) {
187 /// Id of the dylib where this symbol is expected to reside.92 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
188 /// Positive ordinals point at dylibs imported with LC_LOAD_DYLIB,93 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
189 /// 0 means this binary, -1 the main executable, and -2 flat lookup.94 } else if (symbol.dylib_ordinal > 0) {
190 dylib_ordinal: i64,95 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
19196 } else {
192 /// Id of the segment where to bind this symbol to.97 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_SPECIAL_IMM | @truncate(u4, @bitCast(u64, symbol.dylib_ordinal)));
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 }
204 }98 }
205 self.symbols.deinit(allocator);99 try writer.writeByte(macho.BIND_OPCODE_SET_TYPE_IMM | @truncate(u4, macho.BIND_TYPE_POINTER));
206 }
207100
208 /// Parse the binding info table from byte stream.101 if (symbol.name) |name| {
209 pub fn read(self: *LazyBindingInfoTable, reader: anytype, allocator: *Allocator) !void {102 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
210 var symbol: Symbol = .{103 try writer.writeAll(name);
211 .offset = 0,104 try writer.writeByte(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 }
263 }105 }
264 assert(done);
265 }
266106
267 /// Write the binding info table to byte stream.107 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
268 pub fn write(self: LazyBindingInfoTable, writer: anytype) !void {108 try leb.writeILEB128(writer, symbol.offset);
269 for (self.symbols.items) |symbol| {109 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
270 try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));110 try writer.writeByte(macho.BIND_OPCODE_DONE);
271 try leb.writeILEB128(writer, symbol.offset);111 }
272112}
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 }
286113
287 if (symbol.name) |name| {114pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {
288 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.115 var stream = std.io.countingWriter(std.io.null_writer);
289 try writer.writeAll(name);116 var writer = stream.writer();
290 try writer.writeByte(0);117 var size: u64 = 0;
291 }
292118
293 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);119 for (symbols) |symbol| {
294 try writer.writeByte(macho.BIND_OPCODE_DONE);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));
295 }125 }
126 if (symbol.name) |name| {
127 size += 1;
128 size += name.len;
129 size += 1;
130 }
131 size += 2;
296 }132 }
297133
298 /// Calculate size in bytes of this binding info table.134 size += stream.bytes_written;
299 pub fn calcSize(self: *LazyBindingInfoTable) !u64 {135 return size;
300 var stream = std.io.countingWriter(std.io.null_writer);136}
301 var writer = stream.writer();
302 var size: u64 = 0;
303137
304 for (self.symbols.items) |symbol| {138pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
305 size += 1;139 for (symbols) |symbol| {
306 try leb.writeILEB128(writer, symbol.offset);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| {143 if (symbol.dylib_ordinal > 15) {
309 size += 1;144 try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
310 try leb.writeILEB128(writer, addend);145 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
311 }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;152 if (symbol.name) |name| {
314 if (symbol.dylib_ordinal > 15) {153 try writer.writeByte(macho.BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM); // TODO Sometimes we might want to add flags.
315 try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));154 try writer.writeAll(name);
316 }155 try writer.writeByte(0);
317 if (symbol.name) |name| {
318 size += 1;
319 size += name.len;
320 size += 1;
321 }
322 size += 2;
323 }156 }
324157
325 size += stream.bytes_written;158 try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
326 return size;159 try writer.writeByte(macho.BIND_OPCODE_DONE);
327 }160 }
328};161}
test/stage2/aarch64.zig+18
...@@ -199,4 +199,22 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -199,4 +199,22 @@ pub fn addCases(ctx: *TestContext) !void {
199 "",199 "",
200 );200 );
201 }201 }
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 }
202}220}
test/stage2/test.zig+18
...@@ -1495,4 +1495,22 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1495,4 +1495,22 @@ pub fn addCases(ctx: *TestContext) !void {
1495 \\}1495 \\}
1496 , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"});1496 , &[_][]const u8{":8:10: error: evaluation exceeded 1000 backwards branches"});
1497 }1497 }
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 }
1498}1516}