authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-10-14 08:20:33+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-10-14 08:20:33+02:00
log68b31c59a6fd64607d501ab1f397b846e41930e1
tree25d667ef09095440acea3455109cee39dc5efc78
parent548fd6e87bb8edf20f5ea69b866f8b13f9ad2787
parentadfd298e44be707541b13e2d8f1edc5b237fa674
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #6650 from kubkon/macho-incremental

stage2: enable incremental MachO linking

2 files changed, 330 insertions(+), 45 deletions(-)

src/link/MachO.zig+263-45
......@@ -116,7 +116,9 @@ global_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
116116/// Table of all undefined symbols
117117undef_symbols: std.ArrayListUnmanaged(macho.nlist_64) = .{},
118118
119local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
119120global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
121offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
120122
121123dyld_stub_binder_index: ?u16 = null,
122124
......@@ -131,7 +133,25 @@ offset_table: std.ArrayListUnmanaged(u64) = .{},
131133error_flags: File.ErrorFlags = File.ErrorFlags{},
132134
133135cmd_table_dirty: bool = false,
134
136dylinker_cmd_dirty: bool = false,
137libsystem_cmd_dirty: bool = false,
138
139/// A list of text blocks that have surplus capacity. This list can have false
140/// positives, as functions grow and shrink over time, only sometimes being added
141/// or removed from the freelist.
142///
143/// A text block has surplus capacity when its overcapacity value is greater than
144/// minimum_text_block_size * alloc_num / alloc_den. That is, when it has so
145/// much extra capacity, that we could fit a small new symbol in it, itself with
146/// ideal_capacity or more.
147///
148/// Ideal capacity is defined by size * alloc_num / alloc_den.
149///
150/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
151/// overcapacity can be negative. A simple way to have negative overcapacity is to
152/// allocate a fresh text block, which will have ideal capacity, and then grow it
153/// by 1 byte. It will then have -1 overcapacity.
154text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
135155/// Pointer to the last allocated text block
136156last_text_block: ?*TextBlock = null,
137157
......@@ -153,6 +173,12 @@ const LIB_SYSTEM_NAME: [*:0]const u8 = "System";
153173/// TODO we should search for libSystem and fail if it doesn't exist, instead of hardcoding it
154174const LIB_SYSTEM_PATH: [*:0]const u8 = DEFAULT_LIB_SEARCH_PATH ++ "/libSystem.B.dylib";
155175
176/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
177/// it as a possible place to put new symbols, it must have enough room for this many bytes
178/// (plus extra for reserved capacity).
179const minimum_text_block_size = 64;
180const min_text_capacity = minimum_text_block_size * alloc_num / alloc_den;
181
156182pub const TextBlock = struct {
157183 /// Each decl always gets a local symbol with the fully qualified name.
158184 /// The vaddr and size are found here directly.
......@@ -179,6 +205,33 @@ pub const TextBlock = struct {
179205 .prev = null,
180206 .next = null,
181207 };
208
209 /// Returns how much room there is to grow in virtual address space.
210 /// File offset relocation happens transparently, so it is not included in
211 /// this calculation.
212 fn capacity(self: TextBlock, macho_file: MachO) u64 {
213 const self_sym = macho_file.local_symbols.items[self.local_sym_index];
214 if (self.next) |next| {
215 const next_sym = macho_file.local_symbols.items[next.local_sym_index];
216 return next_sym.n_value - self_sym.n_value;
217 } else {
218 // We are the last block.
219 // The capacity is limited only by virtual address space.
220 return std.math.maxInt(u64) - self_sym.n_value;
221 }
222 }
223
224 fn freeListEligible(self: TextBlock, macho_file: MachO) bool {
225 // No need to keep a free list node for the last block.
226 const next = self.next orelse return false;
227 const self_sym = macho_file.local_symbols.items[self.local_sym_index];
228 const next_sym = macho_file.local_symbols.items[next.local_sym_index];
229 const cap = next_sym.n_value - self_sym.n_value;
230 const ideal_cap = self.size * alloc_num / alloc_den;
231 if (cap <= ideal_cap) return false;
232 const surplus = cap - ideal_cap;
233 return surplus >= min_text_capacity;
234 }
182235};
183236
184237pub const Export = struct {
......@@ -267,14 +320,12 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
267320 .Exe => {
268321 // Write export trie.
269322 try self.writeExportTrie();
270
271323 if (self.entry_addr) |addr| {
272 // Update LC_MAIN with entry offset
324 // Update LC_MAIN with entry offset.
273325 const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
274326 const main_cmd = &self.load_commands.items[self.main_cmd_index.?].EntryPoint;
275327 main_cmd.entryoff = addr - text_segment.vmaddr;
276328 }
277
278329 {
279330 // Update dynamic symbol table.
280331 const nlocals = @intCast(u32, self.local_symbols.items.len);
......@@ -287,7 +338,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
287338 dysymtab.iundefsym = nlocals + nglobals;
288339 dysymtab.nundefsym = nundefs;
289340 }
290 {
341 if (self.dylinker_cmd_dirty) {
291342 // Write path to dyld loader.
292343 var off: usize = @sizeOf(macho.mach_header_64);
293344 for (self.load_commands.items) |cmd| {
......@@ -298,8 +349,9 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
298349 off += cmd.name;
299350 log.debug("writing LC_LOAD_DYLINKER path to dyld at 0x{x}\n", .{off});
300351 try self.base.file.?.pwriteAll(mem.spanZ(DEFAULT_DYLD_PATH), off);
352 self.dylinker_cmd_dirty = false;
301353 }
302 {
354 if (self.libsystem_cmd_dirty) {
303355 // Write path to libSystem.
304356 var off: usize = @sizeOf(macho.mach_header_64);
305357 for (self.load_commands.items) |cmd| {
......@@ -310,14 +362,13 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
310362 off += cmd.dylib.name;
311363 log.debug("writing LC_LOAD_DYLIB path to libSystem at 0x{x}\n", .{off});
312364 try self.base.file.?.pwriteAll(mem.spanZ(LIB_SYSTEM_PATH), off);
365 self.libsystem_cmd_dirty = false;
313366 }
314367 },
315368 .Obj => {},
316369 .Lib => return error.TODOImplementWritingLibFiles,
317370 }
318371
319 if (self.cmd_table_dirty) try self.writeCmdHeaders();
320
321372 {
322373 // Update symbol table.
323374 const nlocals = @intCast(u32, self.local_symbols.items.len);
......@@ -327,14 +378,23 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
327378 symtab.nsyms = nlocals + nglobals + nundefs;
328379 }
329380
381 if (self.cmd_table_dirty) {
382 try self.writeCmdHeaders();
383 try self.writeMachOHeader();
384 self.cmd_table_dirty = false;
385 }
386
330387 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
331388 log.debug("flushing. no_entry_point_found = true\n", .{});
332389 self.error_flags.no_entry_point_found = true;
333390 } else {
334391 log.debug("flushing. no_entry_point_found = false\n", .{});
335392 self.error_flags.no_entry_point_found = false;
336 try self.writeMachOHeader();
337393 }
394
395 assert(!self.cmd_table_dirty);
396 assert(!self.dylinker_cmd_dirty);
397 assert(!self.libsystem_cmd_dirty);
338398}
339399
340400fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
......@@ -720,28 +780,95 @@ fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 {
720780}
721781
722782pub fn deinit(self: *MachO) void {
783 self.text_block_free_list.deinit(self.base.allocator);
723784 self.offset_table.deinit(self.base.allocator);
785 self.offset_table_free_list.deinit(self.base.allocator);
724786 self.string_table.deinit(self.base.allocator);
725787 self.undef_symbols.deinit(self.base.allocator);
726788 self.global_symbols.deinit(self.base.allocator);
727789 self.global_symbol_free_list.deinit(self.base.allocator);
728790 self.local_symbols.deinit(self.base.allocator);
791 self.local_symbol_free_list.deinit(self.base.allocator);
729792 self.sections.deinit(self.base.allocator);
730793 self.load_commands.deinit(self.base.allocator);
731794}
732795
796fn freeTextBlock(self: *MachO, text_block: *TextBlock) void {
797 var already_have_free_list_node = false;
798 {
799 var i: usize = 0;
800 // TODO turn text_block_free_list into a hash map
801 while (i < self.text_block_free_list.items.len) {
802 if (self.text_block_free_list.items[i] == text_block) {
803 _ = self.text_block_free_list.swapRemove(i);
804 continue;
805 }
806 if (self.text_block_free_list.items[i] == text_block.prev) {
807 already_have_free_list_node = true;
808 }
809 i += 1;
810 }
811 }
812 // TODO process free list for dbg info just like we do above for vaddrs
813
814 if (self.last_text_block == text_block) {
815 // TODO shrink the __text section size here
816 self.last_text_block = text_block.prev;
817 }
818
819 if (text_block.prev) |prev| {
820 prev.next = text_block.next;
821
822 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
823 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
824 // the OOM here.
825 self.text_block_free_list.append(self.base.allocator, prev) catch {};
826 }
827 } else {
828 text_block.prev = null;
829 }
830
831 if (text_block.next) |next| {
832 next.prev = text_block.prev;
833 } else {
834 text_block.next = null;
835 }
836}
837
838fn shrinkTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64) void {
839 // TODO check the new capacity, and if it crosses the size threshold into a big enough
840 // capacity, insert a free list node for it.
841}
842
843fn growTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
844 const sym = self.local_symbols.items[text_block.local_sym_index];
845 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
846 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
847 if (!need_realloc) return sym.n_value;
848 return self.allocateTextBlock(text_block, new_block_size, alignment);
849}
850
733851pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {
734852 if (decl.link.macho.local_sym_index != 0) return;
735853
736854 try self.local_symbols.ensureCapacity(self.base.allocator, self.local_symbols.items.len + 1);
737855 try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1);
738856
739 log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
740 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);
741 _ = self.local_symbols.addOneAssumeCapacity();
857 if (self.local_symbol_free_list.popOrNull()) |i| {
858 log.debug("reusing symbol index {} for {}\n", .{ i, decl.name });
859 decl.link.macho.local_sym_index = i;
860 } else {
861 log.debug("allocating symbol index {} for {}\n", .{ self.local_symbols.items.len, decl.name });
862 decl.link.macho.local_sym_index = @intCast(u32, self.local_symbols.items.len);
863 _ = self.local_symbols.addOneAssumeCapacity();
864 }
742865
743 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
744 _ = self.offset_table.addOneAssumeCapacity();
866 if (self.offset_table_free_list.popOrNull()) |i| {
867 decl.link.macho.offset_table_index = i;
868 } else {
869 decl.link.macho.offset_table_index = @intCast(u32, self.offset_table.items.len);
870 _ = self.offset_table.addOneAssumeCapacity();
871 }
745872
746873 self.local_symbols.items[decl.link.macho.local_sym_index] = .{
747874 .n_strx = 0,
......@@ -774,24 +901,51 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
774901 };
775902
776903 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
904 assert(decl.link.macho.local_sym_index != 0); // Caller forgot to call allocateDeclIndexes()
777905 const symbol = &self.local_symbols.items[decl.link.macho.local_sym_index];
778906
779 const decl_name = mem.spanZ(decl.name);
780 const name_str_index = try self.makeString(decl_name);
781 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
782 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });
783
784 symbol.* = .{
785 .n_strx = name_str_index,
786 .n_type = macho.N_SECT,
787 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
788 .n_desc = 0,
789 .n_value = addr,
790 };
791 self.offset_table.items[decl.link.macho.offset_table_index] = addr;
907 if (decl.link.macho.size != 0) {
908 const capacity = decl.link.macho.capacity(self.*);
909 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, symbol.n_value, required_alignment);
910 if (need_realloc) {
911 const vaddr = try self.growTextBlock(&decl.link.macho, code.len, required_alignment);
912 log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, symbol.n_value, vaddr });
913 if (vaddr != symbol.n_value) {
914 symbol.n_value = vaddr;
915
916 log.debug(" (writing new offset table entry)\n", .{});
917 self.offset_table.items[decl.link.macho.offset_table_index] = vaddr;
918 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
919 }
920 } else if (code.len < decl.link.macho.size) {
921 self.shrinkTextBlock(&decl.link.macho, code.len);
922 }
923 decl.link.macho.size = code.len;
924 symbol.n_strx = try self.updateString(symbol.n_strx, mem.spanZ(decl.name));
925 symbol.n_type = macho.N_SECT;
926 symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
927 symbol.n_desc = 0;
928 // TODO this write could be avoided if no fields of the symbol were changed.
929 try self.writeSymbol(decl.link.macho.local_sym_index);
930 } else {
931 const decl_name = mem.spanZ(decl.name);
932 const name_str_index = try self.makeString(decl_name);
933 const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
934 log.debug("allocated text block for {} at 0x{x}\n", .{ decl_name, addr });
935 errdefer self.freeTextBlock(&decl.link.macho);
936
937 symbol.* = .{
938 .n_strx = name_str_index,
939 .n_type = macho.N_SECT,
940 .n_sect = @intCast(u8, self.text_section_index.?) + 1,
941 .n_desc = 0,
942 .n_value = addr,
943 };
944 self.offset_table.items[decl.link.macho.offset_table_index] = addr;
792945
793 try self.writeSymbol(decl.link.macho.local_sym_index);
794 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
946 try self.writeSymbol(decl.link.macho.local_sym_index);
947 try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
948 }
795949
796950 const text_section = self.sections.items[self.text_section_index.?];
797951 const section_offset = symbol.n_value - text_section.addr;
......@@ -835,6 +989,7 @@ pub fn updateDeclExports(
835989 .Strong => blk: {
836990 if (mem.eql(u8, exp.options.name, "_start")) {
837991 self.entry_addr = decl_sym.n_value;
992 self.cmd_table_dirty = true; // TODO This should be handled more granularly instead of invalidating all commands.
838993 }
839994 break :blk macho.REFERENCE_FLAG_DEFINED;
840995 },
......@@ -883,7 +1038,18 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
8831038 self.global_symbols.items[sym_index].n_type = 0;
8841039}
8851040
886pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {}
1041pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
1042 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1043 self.freeTextBlock(&decl.link.macho);
1044 if (decl.link.macho.local_sym_index != 0) {
1045 self.local_symbol_free_list.append(self.base.allocator, decl.link.macho.local_sym_index) catch {};
1046 self.offset_table_free_list.append(self.base.allocator, decl.link.macho.offset_table_index) catch {};
1047
1048 self.local_symbols.items[decl.link.macho.local_sym_index].n_type = 0;
1049
1050 decl.link.macho.local_sym_index = 0;
1051 }
1052}
8871053
8881054pub fn getDeclVAddr(self: *MachO, decl: *const Module.Decl) u64 {
8891055 assert(decl.link.macho.local_sym_index != 0);
......@@ -965,6 +1131,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
9651131
9661132 text_segment.vmsize = file_size + off; // We add off here since __TEXT segment includes everything prior to __text section.
9671133 text_segment.filesize = file_size + off;
1134 self.cmd_table_dirty = true;
9681135 }
9691136 if (self.data_segment_cmd_index == null) {
9701137 self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1017,6 +1184,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
10171184 data_segment.vmsize = segment_size;
10181185 data_segment.filesize = segment_size;
10191186 data_segment.fileoff = off;
1187 self.cmd_table_dirty = true;
10201188 }
10211189 if (self.linkedit_segment_cmd_index == null) {
10221190 self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1112,6 +1280,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
11121280 },
11131281 });
11141282 self.cmd_table_dirty = true;
1283 self.dylinker_cmd_dirty = true;
11151284 }
11161285 if (self.libsystem_cmd_index == null) {
11171286 self.libsystem_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1133,6 +1302,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
11331302 },
11341303 });
11351304 self.cmd_table_dirty = true;
1305 self.libsystem_cmd_dirty = true;
11361306 }
11371307 if (self.main_cmd_index == null) {
11381308 self.main_cmd_index = @intCast(u16, self.load_commands.items.len);
......@@ -1205,18 +1375,61 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
12051375 const text_section = &self.sections.items[self.text_section_index.?];
12061376 const new_block_ideal_capacity = new_block_size * alloc_num / alloc_den;
12071377
1378 // We use these to indicate our intention to update metadata, placing the new block,
1379 // and possibly removing a free list node.
1380 // It would be simpler to do it inside the for loop below, but that would cause a
1381 // problem if an error was returned later in the function. So this action
1382 // is actually carried out at the end of the function, when errors are no longer possible.
12081383 var block_placement: ?*TextBlock = null;
1209 const addr = blk: {
1210 if (self.last_text_block) |last| {
1384 var free_list_removal: ?usize = null;
1385
1386 // First we look for an appropriately sized free list node.
1387 // The list is unordered. We'll just take the first thing that works.
1388 const vaddr = blk: {
1389 var i: usize = 0;
1390 while (i < self.text_block_free_list.items.len) {
1391 const big_block = self.text_block_free_list.items[i];
1392 // We now have a pointer to a live text block that has too much capacity.
1393 // Is it enough that we could fit this new text block?
1394 const sym = self.local_symbols.items[big_block.local_sym_index];
1395 const capacity = big_block.capacity(self.*);
1396 const ideal_capacity = capacity * alloc_num / alloc_den;
1397 const ideal_capacity_end_vaddr = sym.n_value + ideal_capacity;
1398 const capacity_end_vaddr = sym.n_value + capacity;
1399 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
1400 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
1401 if (new_start_vaddr < ideal_capacity_end_vaddr) {
1402 // Additional bookkeeping here to notice if this free list node
1403 // should be deleted because the block that it points to has grown to take up
1404 // more of the extra capacity.
1405 if (!big_block.freeListEligible(self.*)) {
1406 _ = self.text_block_free_list.swapRemove(i);
1407 } else {
1408 i += 1;
1409 }
1410 continue;
1411 }
1412 // At this point we know that we will place the new block here. But the
1413 // remaining question is whether there is still yet enough capacity left
1414 // over for there to still be a free list node.
1415 const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
1416 const keep_free_list_node = remaining_capacity >= min_text_capacity;
1417
1418 // Set up the metadata to be updated, after errors are no longer possible.
1419 block_placement = big_block;
1420 if (!keep_free_list_node) {
1421 free_list_removal = i;
1422 }
1423 break :blk new_start_vaddr;
1424 } else if (self.last_text_block) |last| {
12111425 const last_symbol = self.local_symbols.items[last.local_sym_index];
1212 // TODO pad out with NOPs and reenable
1213 // const ideal_capacity = last.size * alloc_num / alloc_den;
1214 // const ideal_capacity_end_addr = last_symbol.n_value + ideal_capacity;
1215 // const new_start_addr = mem.alignForwardGeneric(u64, ideal_capacity_end_addr, alignment);
1216 const end_addr = last_symbol.n_value + last.size;
1217 const new_start_addr = mem.alignForwardGeneric(u64, end_addr, alignment);
1426 // TODO We should pad out the excess capacity with NOPs. For executables,
1427 // no padding seems to be OK, but it will probably not be for objects.
1428 const ideal_capacity = last.size * alloc_num / alloc_den;
1429 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
1430 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
12181431 block_placement = last;
1219 break :blk new_start_addr;
1432 break :blk new_start_vaddr;
12201433 } else {
12211434 break :blk text_section.addr;
12221435 }
......@@ -1225,11 +1438,13 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
12251438 const expand_text_section = block_placement == null or block_placement.?.next == null;
12261439 if (expand_text_section) {
12271440 const text_capacity = self.allocatedSize(text_section.offset);
1228 const needed_size = (addr + new_block_size) - text_section.addr;
1229 assert(needed_size <= text_capacity); // TODO handle growth
1441 const needed_size = (vaddr + new_block_size) - text_section.addr;
1442 assert(needed_size <= text_capacity); // TODO must move the entire text section.
12301443
12311444 self.last_text_block = text_block;
1232 text_section.size = needed_size; // TODO temp until we pad out with NOPs
1445 text_section.size = needed_size;
1446
1447 self.cmd_table_dirty = true; // TODO Make more granular.
12331448 }
12341449 text_block.size = new_block_size;
12351450
......@@ -1248,8 +1463,11 @@ fn allocateTextBlock(self: *MachO, text_block: *TextBlock, new_block_size: u64,
12481463 text_block.prev = null;
12491464 text_block.next = null;
12501465 }
1466 if (free_list_removal) |i| {
1467 _ = self.text_block_free_list.swapRemove(i);
1468 }
12511469
1252 return addr;
1470 return vaddr;
12531471}
12541472
12551473fn makeStaticString(comptime bytes: []const u8) [16]u8 {
......@@ -1479,7 +1697,7 @@ fn writeCmdHeaders(self: *MachO) !void {
14791697 return error.TODOImplementWritingObjFiles;
14801698 };
14811699 const idx = self.text_section_index.?;
1482 log.debug("writing text section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off });
1700 log.debug("writing text section header at 0x{x}\n", .{off});
14831701 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
14841702 }
14851703 {
......@@ -1497,7 +1715,7 @@ fn writeCmdHeaders(self: *MachO) !void {
14971715 return error.TODOImplementWritingObjFiles;
14981716 };
14991717 const idx = self.got_section_index.?;
1500 log.debug("writing got section {} at 0x{x}\n", .{ self.sections.items[idx .. idx + 1], off });
1718 log.debug("writing got section header at 0x{x}\n", .{off});
15011719 try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.sections.items[idx .. idx + 1]), off);
15021720 }
15031721}
test/stage2/test.zig+67
......@@ -186,6 +186,73 @@ pub fn addCases(ctx: *TestContext) !void {
186186 ,
187187 "Hello, World!\n",
188188 );
189 // Now change the message only
190 case.addCompareOutput(
191 \\export fn _start() noreturn {
192 \\ print();
193 \\
194 \\ exit();
195 \\}
196 \\
197 \\fn print() void {
198 \\ asm volatile ("syscall"
199 \\ :
200 \\ : [number] "{rax}" (0x2000004),
201 \\ [arg1] "{rdi}" (1),
202 \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
203 \\ [arg3] "{rdx}" (104)
204 \\ : "memory"
205 \\ );
206 \\ return;
207 \\}
208 \\
209 \\fn exit() noreturn {
210 \\ asm volatile ("syscall"
211 \\ :
212 \\ : [number] "{rax}" (0x2000001),
213 \\ [arg1] "{rdi}" (0)
214 \\ : "memory"
215 \\ );
216 \\ unreachable;
217 \\}
218 ,
219 "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
220 );
221 // Now we print it twice.
222 case.addCompareOutput(
223 \\export fn _start() noreturn {
224 \\ print();
225 \\ print();
226 \\
227 \\ exit();
228 \\}
229 \\
230 \\fn print() void {
231 \\ asm volatile ("syscall"
232 \\ :
233 \\ : [number] "{rax}" (0x2000004),
234 \\ [arg1] "{rdi}" (1),
235 \\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
236 \\ [arg3] "{rdx}" (104)
237 \\ : "memory"
238 \\ );
239 \\ return;
240 \\}
241 \\
242 \\fn exit() noreturn {
243 \\ asm volatile ("syscall"
244 \\ :
245 \\ : [number] "{rax}" (0x2000001),
246 \\ [arg1] "{rdi}" (0)
247 \\ : "memory"
248 \\ );
249 \\ unreachable;
250 \\}
251 ,
252 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
253 \\What is up? This is a longer message that will force the data to be relocated in virtual address space.
254 \\
255 );
189256 }
190257
191258 {