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,...@@ -135,6 +135,8 @@ lazy_binding_info_dirty: bool = false,
135export_info_dirty: bool = false,135export_info_dirty: bool = false,
136string_table_dirty: bool = false,136string_table_dirty: bool = false,
137137
138string_table_needs_relocation: bool = false,
139
138/// A list of text blocks that have surplus capacity. This list can have false140/// A list of text blocks that have surplus capacity. This list can have false
139/// positives, as functions grow and shrink over time, only sometimes being added141/// positives, as functions grow and shrink over time, only sometimes being added
140/// or removed from the freelist.142/// or removed from the freelist.
...@@ -454,6 +456,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -454,6 +456,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
454 assert(!self.lazy_binding_info_dirty);456 assert(!self.lazy_binding_info_dirty);
455 assert(!self.export_info_dirty);457 assert(!self.export_info_dirty);
456 assert(!self.string_table_dirty);458 assert(!self.string_table_dirty);
459 assert(!self.string_table_needs_relocation);
457460
458 if (target.cpu.arch == .aarch64) {461 if (target.cpu.arch == .aarch64) {
459 switch (output_mode) {462 switch (output_mode) {
...@@ -1824,22 +1827,22 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1824,22 +1827,22 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18241827
1825 // Preallocate rebase, binding, lazy binding info, and export info.1828 // Preallocate rebase, binding, lazy binding info, and export info.
1826 const expected_size = 48; // TODO This is totally random.1829 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);
1828 log.debug("found rebase info free space 0x{x} to 0x{x}", .{ rebase_off, rebase_off + expected_size });1831 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);1832 dyld.rebase_off = @intCast(u32, rebase_off);
1830 dyld.rebase_size = expected_size;1833 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);
1833 log.debug("found binding info free space 0x{x} to 0x{x}", .{ bind_off, bind_off + expected_size });1836 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);1837 dyld.bind_off = @intCast(u32, bind_off);
1835 dyld.bind_size = expected_size;1838 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);
1838 log.debug("found lazy binding info free space 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + expected_size });1841 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);1842 dyld.lazy_bind_off = @intCast(u32, lazy_bind_off);
1840 dyld.lazy_bind_size = expected_size;1843 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);
1843 log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + expected_size });1846 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);1847 dyld.export_off = @intCast(u32, export_off);
1845 dyld.export_size = expected_size;1848 dyld.export_size = expected_size;
...@@ -1864,14 +1867,14 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1864,14 +1867,14 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1864 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;1867 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
18651868
1866 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);1869 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);
1868 log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });1871 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);1872 symtab.symoff = @intCast(u32, symtab_off);
1870 symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint);1873 symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint);
18711874
1872 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.1875 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.
1873 const strtab_size = self.string_table.items.len;1876 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);
1875 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });1878 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);1879 symtab.stroff = @intCast(u32, strtab_off);
1877 symtab.strsize = @intCast(u32, strtab_size);1880 symtab.strsize = @intCast(u32, strtab_size);
...@@ -1885,7 +1888,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1885,7 +1888,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18851888
1886 // Preallocate space for indirect symbol table.1889 // Preallocate space for indirect symbol table.
1887 const indsymtab_size = self.base.options.symbol_count_hint * @sizeOf(u64); // Each entry is just a u64.1890 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
1890 log.debug("found indirect symbol table free space 0x{x} to 0x{x}", .{ indsymtab_off, indsymtab_off + indsymtab_size });1893 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 {...@@ -2383,13 +2386,13 @@ fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 {
2383 return null;2386 return null;
2384}2387}
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 {
2387 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;2390 const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2388 var start: u64 = linkedit.inner.fileoff;2391 var st: u64 = start orelse linkedit.inner.fileoff;
2389 while (self.detectAllocCollisionLinkedit(start, object_size)) |item_end| {2392 while (self.detectAllocCollisionLinkedit(st, object_size)) |item_end| {
2390 start = mem.alignForwardGeneric(u64, item_end, min_alignment);2393 st = mem.alignForwardGeneric(u64, item_end, min_alignment);
2391 }2394 }
2392 return start;2395 return st;
2393}2396}
23942397
2395/// Saturating multiplication2398/// Saturating multiplication
...@@ -2535,7 +2538,7 @@ fn relocateSymbolTable(self: *MachO) !void {...@@ -2535,7 +2538,7 @@ fn relocateSymbolTable(self: *MachO) !void {
2535 const needed_size = nsyms * @sizeOf(macho.nlist_64);2538 const needed_size = nsyms * @sizeOf(macho.nlist_64);
2536 if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) {2539 if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) {
2537 // Move the entire symbol table to a new location2540 // 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);
2539 const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64);2542 const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64);
25402543
2541 log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{2544 log.debug("relocating symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{
...@@ -2548,6 +2551,7 @@ fn relocateSymbolTable(self: *MachO) !void {...@@ -2548,6 +2551,7 @@ fn relocateSymbolTable(self: *MachO) !void {
2548 const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size);2551 const amt = try self.base.file.?.copyRangeAll(symtab.symoff, self.base.file.?, new_symoff, existing_size);
2549 if (amt != existing_size) return error.InputOutput;2552 if (amt != existing_size) return error.InputOutput;
2550 symtab.symoff = @intCast(u32, new_symoff);2553 symtab.symoff = @intCast(u32, new_symoff);
2554 self.string_table_needs_relocation = true;
2551 }2555 }
2552 symtab.nsyms = @intCast(u32, nsyms);2556 symtab.nsyms = @intCast(u32, nsyms);
2553 self.load_commands_dirty = true;2557 self.load_commands_dirty = true;
...@@ -2757,7 +2761,8 @@ fn writeExportTrie(self: *MachO) !void {...@@ -2757,7 +2761,8 @@ fn writeExportTrie(self: *MachO) !void {
27572761
2758 if (needed_size > allocated_size) {2762 if (needed_size > allocated_size) {
2759 dyld_info.export_off = 0;2763 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.
2761 }2766 }
2762 dyld_info.export_size = @intCast(u32, needed_size);2767 dyld_info.export_size = @intCast(u32, needed_size);
2763 log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size });2768 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 {...@@ -2794,7 +2799,8 @@ fn writeRebaseInfoTable(self: *MachO) !void {
27942799
2795 if (needed_size > allocated_size) {2800 if (needed_size > allocated_size) {
2796 dyld_info.rebase_off = 0;2801 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.
2798 }2804 }
27992805
2800 dyld_info.rebase_size = @intCast(u32, needed_size);2806 dyld_info.rebase_size = @intCast(u32, needed_size);
...@@ -2832,7 +2838,8 @@ fn writeBindingInfoTable(self: *MachO) !void {...@@ -2832,7 +2838,8 @@ fn writeBindingInfoTable(self: *MachO) !void {
28322838
2833 if (needed_size > allocated_size) {2839 if (needed_size > allocated_size) {
2834 dyld_info.bind_off = 0;2840 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.
2836 }2843 }
28372844
2838 dyld_info.bind_size = @intCast(u32, needed_size);2845 dyld_info.bind_size = @intCast(u32, needed_size);
...@@ -2867,7 +2874,8 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {...@@ -2867,7 +2874,8 @@ fn writeLazyBindingInfoTable(self: *MachO) !void {
28672874
2868 if (needed_size > allocated_size) {2875 if (needed_size > allocated_size) {
2869 dyld_info.lazy_bind_off = 0;2876 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.
2871 }2879 }
28722880
2873 dyld_info.lazy_bind_size = @intCast(u32, needed_size);2881 dyld_info.lazy_bind_size = @intCast(u32, needed_size);
...@@ -2956,9 +2964,10 @@ fn writeStringTable(self: *MachO) !void {...@@ -2956,9 +2964,10 @@ fn writeStringTable(self: *MachO) !void {
2956 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);2964 const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
2957 const needed_size = mem.alignForwardGeneric(u64, self.string_table.items.len, @alignOf(u64));2965 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) {
2960 symtab.strsize = 0;2968 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;
2962 }2971 }
2963 symtab.strsize = @intCast(u32, needed_size);2972 symtab.strsize = @intCast(u32, needed_size);
2964 log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });2973 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 {...@@ -217,4 +217,20 @@ pub fn addCases(ctx: *TestContext) !void {
217 "Hello, World!\n",217 "Hello, World!\n",
218 );218 );
219 }219 }
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 }
220}236}
test/stage2/test.zig+16
...@@ -1513,4 +1513,20 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1513,4 +1513,20 @@ pub fn addCases(ctx: *TestContext) !void {
1513 "Hello, World!\n",1513 "Hello, World!\n",
1514 );1514 );
1515 }1515 }
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 }
1516}1532}