authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-15 17:33:36+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-15 23:22:28+01:00
logb204ea0349d5a580fc8ba9d8059c520301072072
tree85869d15d95240e60f33ffc268a66b57d68554c1
parentf7d7cb62684e7012bb5dafdebbaab3a8ef4e0a30

macho: ensure that strtab always follows symtab

In rare occassions, it may happen that string table is allocated free space preceeding symbol table. This is an error in the eyes of the `dyld` dynamic loader and thus has to forbidden by the linker.

3 files changed, 60 insertions(+), 19 deletions(-)

src/link/MachO.zig+28-19
......@@ -135,6 +135,8 @@ lazy_binding_info_dirty: bool = false,
135135export_info_dirty: bool = false,
136136string_table_dirty: bool = false,
137137
138string_table_needs_relocation: bool = false,
139
138140/// A list of text blocks that have surplus capacity. This list can have false
139141/// positives, as functions grow and shrink over time, only sometimes being added
140142/// or removed from the freelist.
......@@ -454,6 +456,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
454456 assert(!self.lazy_binding_info_dirty);
455457 assert(!self.export_info_dirty);
456458 assert(!self.string_table_dirty);
459 assert(!self.string_table_needs_relocation);
457460
458461 if (target.cpu.arch == .aarch64) {
459462 switch (output_mode) {
......@@ -1824,22 +1827,22 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18241827
18251828 // Preallocate rebase, binding, lazy binding info, and export info.
18261829 const expected_size = 48; // TODO This is totally random.
1827 const rebase_off = self.findFreeSpaceLinkedit(expected_size, 1);
1830 const rebase_off = self.findFreeSpaceLinkedit(expected_size, 1, null);
18281831 log.debug("found rebase info free space 0x{x} to 0x{x}", .{ rebase_off, rebase_off + expected_size });
18291832 dyld.rebase_off = @intCast(u32, rebase_off);
18301833 dyld.rebase_size = expected_size;
18311834
1832 const bind_off = self.findFreeSpaceLinkedit(expected_size, 1);
1835 const bind_off = self.findFreeSpaceLinkedit(expected_size, 1, null);
18331836 log.debug("found binding info free space 0x{x} to 0x{x}", .{ bind_off, bind_off + expected_size });
18341837 dyld.bind_off = @intCast(u32, bind_off);
18351838 dyld.bind_size = expected_size;
18361839
1837 const lazy_bind_off = self.findFreeSpaceLinkedit(expected_size, 1);
1840 const lazy_bind_off = self.findFreeSpaceLinkedit(expected_size, 1, null);
18381841 log.debug("found lazy binding info free space 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + expected_size });
18391842 dyld.lazy_bind_off = @intCast(u32, lazy_bind_off);
18401843 dyld.lazy_bind_size = expected_size;
18411844
1842 const export_off = self.findFreeSpaceLinkedit(expected_size, 1);
1845 const export_off = self.findFreeSpaceLinkedit(expected_size, 1, null);
18431846 log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + expected_size });
18441847 dyld.export_off = @intCast(u32, export_off);
18451848 dyld.export_size = expected_size;
......@@ -1864,14 +1867,14 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18641867 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
18651868
18661869 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);
1867 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
1870 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64), null);
18681871 log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });
18691872 symtab.symoff = @intCast(u32, symtab_off);
18701873 symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint);
18711874
18721875 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.
18731876 const strtab_size = self.string_table.items.len;
1874 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1);
1877 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1, symtab_off);
18751878 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });
18761879 symtab.stroff = @intCast(u32, strtab_off);
18771880 symtab.strsize = @intCast(u32, strtab_size);
......@@ -1885,7 +1888,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18851888
18861889 // Preallocate space for indirect symbol table.
18871890 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));
1891 const indsymtab_off = self.findFreeSpaceLinkedit(indsymtab_size, @sizeOf(u64), null);
18891892
18901893 log.debug("found indirect symbol table free space 0x{x} to 0x{x}", .{ indsymtab_off, indsymtab_off + indsymtab_size });
18911894
......@@ -2383,13 +2386,13 @@ fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 {
23832386 return null;
23842387}
23852388
2386fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16) u64 {
2389fn findFreeSpaceLinkedit(self: *MachO, object_size: u64, min_alignment: u16, start: ?u64) u64 {
23872390 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2388 var start: u64 = linkedit.inner.fileoff;
2389 while (self.detectAllocCollisionLinkedit(start, object_size)) |item_end| {
2390 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
2391 var st: u64 = start orelse linkedit.inner.fileoff;
2392 while (self.detectAllocCollisionLinkedit(st, object_size)) |item_end| {
2393 st = mem.alignForwardGeneric(u64, item_end, min_alignment);
23912394 }
2392 return start;
2395 return st;
23932396}
23942397
23952398/// Saturating multiplication
......@@ -2535,7 +2538,7 @@ fn relocateSymbolTable(self: *MachO) !void {
25352538 const needed_size = nsyms * @sizeOf(macho.nlist_64);
25362539 if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) {
25372540 // Move the entire symbol table to a new location
2538 const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64));
2541 const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64), null);
25392542 const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64);
25402543
25412544 log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{
......@@ -2548,6 +2551,7 @@ fn relocateSymbolTable(self: *MachO) !void {
25482551 const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size);
25492552 if (amt != existing_size) return error.InputOutput;
25502553 symtab.symoff = @intCast(u32, new_symoff);
2554 self.string_table_needs_relocation = true;
25512555 }
25522556 symtab.nsyms = @intCast(u32, nsyms);
25532557 self.load_commands_dirty = true;
......@@ -2757,7 +2761,8 @@ fn writeExportTrie(self: *MachO) !void {
27572761
27582762 if (needed_size > allocated_size) {
27592763 dyld_info.export_off = 0;
2760 dyld_info.export_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
2764 dyld_info.export_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null));
2765 // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too.
27612766 }
27622767 dyld_info.export_size = @intCast(u32, needed_size);
27632768 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });
......@@ -2794,7 +2799,8 @@ fn writeRebaseInfoTable(self: *MachO) !void {
27942799
27952800 if (needed_size > allocated_size) {
27962801 dyld_info.rebase_off = 0;
2797 dyld_info.rebase_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
2802 dyld_info.rebase_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null));
2803 // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too.
27982804 }
27992805
28002806 dyld_info.rebase_size = @intCast(u32, needed_size);
......@@ -2832,7 +2838,8 @@ fn writeBindingInfoTable(self: *MachO) !void {
28322838
28332839 if (needed_size > allocated_size) {
28342840 dyld_info.bind_off = 0;
2835 dyld_info.bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
2841 dyld_info.bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null));
2842 // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too.
28362843 }
28372844
28382845 dyld_info.bind_size = @intCast(u32, needed_size);
......@@ -2867,7 +2874,8 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
28672874
28682875 if (needed_size > allocated_size) {
28692876 dyld_info.lazy_bind_off = 0;
2870 dyld_info.lazy_bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
2877 dyld_info.lazy_bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null));
2878 // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too.
28712879 }
28722880
28732881 dyld_info.lazy_bind_size = @intCast(u32, needed_size);
......@@ -2956,9 +2964,10 @@ fn writeStringTable(self: *MachO) !void {
29562964 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
29572965 const needed_size = mem.alignForwardGeneric(u64, self.string_table.items.len, @alignOf(u64));
29582966
2959 if (needed_size > allocated_size) {
2967 if (needed_size > allocated_size or self.string_table_needs_relocation) {
29602968 symtab.strsize = 0;
2961 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
2969 symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, symtab.symoff));
2970 self.string_table_needs_relocation = false;
29622971 }
29632972 symtab.strsize = @intCast(u32, needed_size);
29642973 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
test/stage2/aarch64.zig+16
......@@ -217,4 +217,20 @@ pub fn addCases(ctx: *TestContext) !void {
217217 "Hello, World!\n",
218218 );
219219 }
220
221 {
222 var case = ctx.exe("only libc exit", macos_aarch64);
223
224 // This test case covers an infrequent scenarion where the string table *may* be relocated
225 // into the position preceeding the symbol table which results in a dyld error.
226 case.addCompareOutput(
227 \\extern "c" fn exit(usize) noreturn;
228 \\
229 \\export fn _start() noreturn {
230 \\ exit(0);
231 \\}
232 ,
233 "",
234 );
235 }
220236}
test/stage2/test.zig+16
......@@ -1513,4 +1513,20 @@ pub fn addCases(ctx: *TestContext) !void {
15131513 "Hello, World!\n",
15141514 );
15151515 }
1516
1517 {
1518 var case = ctx.exe("only libc exit", macosx_x64);
1519
1520 // This test case covers an infrequent scenarion where the string table *may* be relocated
1521 // into the position preceeding the symbol table which results in a dyld error.
1522 case.addCompareOutput(
1523 \\extern "c" fn exit(usize) noreturn;
1524 \\
1525 \\export fn _start() noreturn {
1526 \\ exit(0);
1527 \\}
1528 ,
1529 "",
1530 );
1531 }
15161532}