authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-06 17:24:48+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-01-13 23:54:01+01:00
log5ae82956aac029fb45c09e5da2db8d86b9fa0157
treea695528ce280c734a182594728356bbbc4536024
parentf44732c1b0d7516c4a8169f7381cbcf55e1ae460

macho: write out rebase info


2 files changed, 141 insertions(+), 23 deletions(-)

src/link/MachO.zig+98-23
...@@ -123,6 +123,8 @@ string_table: std.ArrayListUnmanaged(u8) = .{},...@@ -123,6 +123,8 @@ string_table: std.ArrayListUnmanaged(u8) = .{},
123/// Table of trampolines to the actual symbols in __text section.123/// Table of trampolines to the actual symbols in __text section.
124offset_table: std.ArrayListUnmanaged(u64) = .{},124offset_table: std.ArrayListUnmanaged(u64) = .{},
125125
126/// Table of rebase info entries.
127rebase_info_table: RebaseInfoTable = .{},
126/// Table of binding info entries.128/// Table of binding info entries.
127binding_info_table: BindingInfoTable = .{},129binding_info_table: BindingInfoTable = .{},
128/// Table of lazy binding info entries.130/// Table of lazy binding info entries.
...@@ -133,6 +135,7 @@ error_flags: File.ErrorFlags = File.ErrorFlags{},...@@ -133,6 +135,7 @@ error_flags: File.ErrorFlags = File.ErrorFlags{},
133offset_table_count_dirty: bool = false,135offset_table_count_dirty: bool = false,
134header_dirty: bool = false,136header_dirty: bool = false,
135load_commands_dirty: bool = false,137load_commands_dirty: bool = false,
138rebase_info_dirty: bool = false,
136binding_info_dirty: bool = false,139binding_info_dirty: bool = false,
137lazy_binding_info_dirty: bool = false,140lazy_binding_info_dirty: bool = false,
138export_info_dirty: bool = false,141export_info_dirty: bool = false,
...@@ -400,6 +403,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -400,6 +403,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
400 main_cmd.entryoff = addr - text_segment.inner.vmaddr;403 main_cmd.entryoff = addr - text_segment.inner.vmaddr;
401 self.load_commands_dirty = true;404 self.load_commands_dirty = true;
402 }405 }
406 try self.writeRebaseInfoTable();
403 try self.writeBindingInfoTable();407 try self.writeBindingInfoTable();
404 try self.writeLazyBindingInfoTable();408 try self.writeLazyBindingInfoTable();
405 try self.writeExportTrie();409 try self.writeExportTrie();
...@@ -425,6 +429,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -425,6 +429,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
425 .Lib => return error.TODOImplementWritingLibFiles,429 .Lib => return error.TODOImplementWritingLibFiles,
426 }430 }
427431
432 {
433 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
434 dysymtab.nindirectsyms = 0;
435 }
436
428 try self.writeLoadCommands();437 try self.writeLoadCommands();
429 try self.writeHeader();438 try self.writeHeader();
430439
...@@ -439,6 +448,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -439,6 +448,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
439 assert(!self.offset_table_count_dirty);448 assert(!self.offset_table_count_dirty);
440 assert(!self.header_dirty);449 assert(!self.header_dirty);
441 assert(!self.load_commands_dirty);450 assert(!self.load_commands_dirty);
451 assert(!self.rebase_info_dirty);
442 assert(!self.binding_info_dirty);452 assert(!self.binding_info_dirty);
443 assert(!self.lazy_binding_info_dirty);453 assert(!self.lazy_binding_info_dirty);
444 assert(!self.export_info_dirty);454 assert(!self.export_info_dirty);
...@@ -1289,6 +1299,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {...@@ -1289,6 +1299,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
1289 mem.writeIntLittle(u32, cccode[8..12], 0);1299 mem.writeIntLittle(u32, cccode[8..12], 0);
1290 try self.base.file.?.pwriteAll(&cccode, self.next_stub_helper_off.?);1300 try self.base.file.?.pwriteAll(&cccode, self.next_stub_helper_off.?);
1291 self.next_stub_helper_off = self.next_stub_helper_off.? + 3 * @sizeOf(u32);1301 self.next_stub_helper_off = self.next_stub_helper_off.? + 3 * @sizeOf(u32);
1302
1303 try self.rebase_info_table.symbols.append(self.base.allocator, .{
1304 .segment = 3,
1305 .offset = 0,
1306 });
1307 self.rebase_info_dirty = true;
1292 }1308 }
12931309
1294 const text_section = text_segment.sections.items[self.text_section_index.?];1310 const text_section = text_segment.sections.items[self.text_section_index.?];
...@@ -1821,12 +1837,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1821,12 +1837,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1821 if (self.dyld_info_cmd_index == null) {1837 if (self.dyld_info_cmd_index == null) {
1822 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);1838 self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len);
18231839
1824 // TODO Preallocate rebase, binding, and lazy binding info.
1825 const export_size = 2;
1826 const export_off = self.findFreeSpaceLinkedit(export_size, 1);
1827
1828 log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + export_size });
1829
1830 try self.load_commands.append(self.base.allocator, .{1840 try self.load_commands.append(self.base.allocator, .{
1831 .DyldInfoOnly = .{1841 .DyldInfoOnly = .{
1832 .cmd = macho.LC_DYLD_INFO_ONLY,1842 .cmd = macho.LC_DYLD_INFO_ONLY,
...@@ -1839,37 +1849,67 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1839,37 +1849,67 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1839 .weak_bind_size = 0,1849 .weak_bind_size = 0,
1840 .lazy_bind_off = 0,1850 .lazy_bind_off = 0,
1841 .lazy_bind_size = 0,1851 .lazy_bind_size = 0,
1842 .export_off = @intCast(u32, export_off),1852 .export_off = 0,
1843 .export_size = export_size,1853 .export_size = 0,
1844 },1854 },
1845 });1855 });
1856
1857 const dyld = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
1858
1859 // Preallocate rebase, binding, lazy binding info, and export info.
1860 const expected_size = 48; // TODO This is totally random.
1861 const rebase_off = self.findFreeSpaceLinkedit(expected_size, 1);
1862 log.debug("found rebase info free space 0x{x} to 0x{x}", .{ rebase_off, rebase_off + expected_size });
1863 dyld.rebase_off = @intCast(u32, rebase_off);
1864 dyld.rebase_size = expected_size;
1865
1866 const bind_off = self.findFreeSpaceLinkedit(expected_size, 1);
1867 log.debug("found binding info free space 0x{x} to 0x{x}", .{ bind_off, bind_off + expected_size });
1868 dyld.bind_off = @intCast(u32, bind_off);
1869 dyld.bind_size = expected_size;
1870
1871 const lazy_bind_off = self.findFreeSpaceLinkedit(expected_size, 1);
1872 log.debug("found lazy binding info free space 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + expected_size });
1873 dyld.lazy_bind_off = @intCast(u32, lazy_bind_off);
1874 dyld.lazy_bind_size = expected_size;
1875
1876 const export_off = self.findFreeSpaceLinkedit(expected_size, 1);
1877 log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + expected_size });
1878 dyld.export_off = @intCast(u32, export_off);
1879 dyld.export_size = expected_size;
1880
1846 self.header_dirty = true;1881 self.header_dirty = true;
1847 self.load_commands_dirty = true;1882 self.load_commands_dirty = true;
1848 }1883 }
1849 if (self.symtab_cmd_index == null) {1884 if (self.symtab_cmd_index == null) {
1850 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);1885 self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
18511886
1887 try self.load_commands.append(self.base.allocator, .{
1888 .Symtab = .{
1889 .cmd = macho.LC_SYMTAB,
1890 .cmdsize = @sizeOf(macho.symtab_command),
1891 .symoff = 0,
1892 .nsyms = 0,
1893 .stroff = 0,
1894 .strsize = 0,
1895 },
1896 });
1897
1898 const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
1899
1852 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);1900 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);
1853 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));1901 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
1854
1855 log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });1902 log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });
1903 symtab.symoff = @intCast(u32, symtab_off);
1904 symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint);
18561905
1857 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.1906 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.
1858 const strtab_size = self.string_table.items.len;1907 const strtab_size = self.string_table.items.len;
1859 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1);1908 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1);
1860
1861 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });1909 log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size });
1910 symtab.stroff = @intCast(u32, strtab_off);
1911 symtab.strsize = @intCast(u32, strtab_size);
18621912
1863 try self.load_commands.append(self.base.allocator, .{
1864 .Symtab = .{
1865 .cmd = macho.LC_SYMTAB,
1866 .cmdsize = @sizeOf(macho.symtab_command),
1867 .symoff = @intCast(u32, symtab_off),
1868 .nsyms = @intCast(u32, self.base.options.symbol_count_hint),
1869 .stroff = @intCast(u32, strtab_off),
1870 .strsize = @intCast(u32, strtab_size),
1871 },
1872 });
1873 self.header_dirty = true;1913 self.header_dirty = true;
1874 self.load_commands_dirty = true;1914 self.load_commands_dirty = true;
1875 self.string_table_dirty = true;1915 self.string_table_dirty = true;
...@@ -1877,7 +1917,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1877,7 +1917,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1877 if (self.dysymtab_cmd_index == null) {1917 if (self.dysymtab_cmd_index == null) {
1878 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);1918 self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len);
18791919
1880 // TODO Preallocate space for indirect symbol table.1920 // Preallocate space for indirect symbol table.
1921 const indsymtab_size = self.base.options.symbol_count_hint * @sizeOf(u64); // Each entry is just a u64.
1922 const indsymtab_off = self.findFreeSpaceLinkedit(indsymtab_size, @sizeOf(u64));
1923
1924 log.debug("found indirect symbol table free space 0x{x} to 0x{x}", .{ indsymtab_off, indsymtab_off + indsymtab_size });
18811925
1882 try self.load_commands.append(self.base.allocator, .{1926 try self.load_commands.append(self.base.allocator, .{
1883 .Dysymtab = .{1927 .Dysymtab = .{
...@@ -1895,8 +1939,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1895,8 +1939,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1895 .nmodtab = 0,1939 .nmodtab = 0,
1896 .extrefsymoff = 0,1940 .extrefsymoff = 0,
1897 .nextrefsyms = 0,1941 .nextrefsyms = 0,
1898 .indirectsymoff = 0,1942 .indirectsymoff = @intCast(u32, indsymtab_off),
1899 .nindirectsyms = 0,1943 .nindirectsyms = @intCast(u32, self.base.options.symbol_count_hint),
1900 .extreloff = 0,1944 .extreloff = 0,
1901 .nextrel = 0,1945 .nextrel = 0,
1902 .locreloff = 0,1946 .locreloff = 0,
...@@ -2565,6 +2609,37 @@ fn writeExportTrie(self: *MachO) !void {...@@ -2565,6 +2609,37 @@ fn writeExportTrie(self: *MachO) !void {
2565 self.export_info_dirty = false;2609 self.export_info_dirty = false;
2566}2610}
25672611
2612fn writeRebaseInfoTable(self: *MachO) !void {
2613 if (!self.rebase_info_dirty) return;
2614
2615 const tracy = trace(@src());
2616 defer tracy.end();
2617
2618 const size = try self.rebase_info_table.calcSize();
2619 var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size));
2620 defer self.base.allocator.free(buffer);
2621
2622 var stream = std.io.fixedBufferStream(buffer);
2623 try self.rebase_info_table.write(stream.writer());
2624
2625 const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
2626 const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly;
2627 const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off);
2628 const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64));
2629
2630 if (needed_size > allocated_size) {
2631 dyld_info.rebase_off = 0;
2632 dyld_info.rebase_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
2633 }
2634
2635 dyld_info.rebase_size = @intCast(u32, needed_size);
2636 log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size });
2637
2638 try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off);
2639 self.load_commands_dirty = true;
2640 self.rebase_info_dirty = false;
2641}
2642
2568fn writeBindingInfoTable(self: *MachO) !void {2643fn writeBindingInfoTable(self: *MachO) !void {
2569 if (!self.binding_info_dirty) return;2644 if (!self.binding_info_dirty) return;
25702645
src/link/MachO/imports.zig+43
...@@ -6,6 +6,49 @@ const mem = std.mem;...@@ -6,6 +6,49 @@ const mem = std.mem;
6const assert = std.debug.assert;6const assert = std.debug.assert;
7const Allocator = mem.Allocator;7const Allocator = mem.Allocator;
88
9pub const RebaseInfoTable = struct {
10 rebase_type: u8 = macho.REBASE_TYPE_POINTER,
11 symbols: std.ArrayListUnmanaged(Symbol) = .{},
12
13 pub const Symbol = struct {
14 segment: u8,
15 offset: i64,
16 };
17
18 pub fn deinit(self: *RebaseInfoTable, allocator: *Allocator) void {
19 self.symbols.deinit(allocator);
20 }
21
22 /// Write the rebase info table to byte stream.
23 pub fn write(self: RebaseInfoTable, writer: anytype) !void {
24 try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, self.rebase_type));
25
26 for (self.symbols.items) |symbol| {
27 try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
28 try leb.writeILEB128(writer, symbol.offset);
29 try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1));
30 }
31
32 try writer.writeByte(macho.REBASE_OPCODE_DONE);
33 }
34
35 /// Calculate size in bytes of this rebase info table.
36 pub fn calcSize(self: *RebaseInfoTable) !u64 {
37 var stream = std.io.countingWriter(std.io.null_writer);
38 var writer = stream.writer();
39 var size: u64 = 1;
40
41 for (self.symbols.items) |symbol| {
42 size += 1;
43 try leb.writeILEB128(writer, symbol.offset);
44 size += 1;
45 }
46
47 size += 1 + stream.bytes_written;
48 return size;
49 }
50};
51
9/// Table of binding info entries used to tell the dyld which52/// Table of binding info entries used to tell the dyld which
10/// symbols to bind at loading time.53/// symbols to bind at loading time.
11pub const BindingInfoTable = struct {54pub const BindingInfoTable = struct {