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) = .{},
123123/// Table of trampolines to the actual symbols in __text section.
124124offset_table: std.ArrayListUnmanaged(u64) = .{},
125125
126/// Table of rebase info entries.
127rebase_info_table: RebaseInfoTable = .{},
126128/// Table of binding info entries.
127129binding_info_table: BindingInfoTable = .{},
128130/// Table of lazy binding info entries.
......@@ -133,6 +135,7 @@ error_flags: File.ErrorFlags = File.ErrorFlags{},
133135offset_table_count_dirty: bool = false,
134136header_dirty: bool = false,
135137load_commands_dirty: bool = false,
138rebase_info_dirty: bool = false,
136139binding_info_dirty: bool = false,
137140lazy_binding_info_dirty: bool = false,
138141export_info_dirty: bool = false,
......@@ -400,6 +403,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
400403 main_cmd.entryoff = addr - text_segment.inner.vmaddr;
401404 self.load_commands_dirty = true;
402405 }
406 try self.writeRebaseInfoTable();
403407 try self.writeBindingInfoTable();
404408 try self.writeLazyBindingInfoTable();
405409 try self.writeExportTrie();
......@@ -425,6 +429,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
425429 .Lib => return error.TODOImplementWritingLibFiles,
426430 }
427431
432 {
433 const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
434 dysymtab.nindirectsyms = 0;
435 }
436
428437 try self.writeLoadCommands();
429438 try self.writeHeader();
430439
......@@ -439,6 +448,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
439448 assert(!self.offset_table_count_dirty);
440449 assert(!self.header_dirty);
441450 assert(!self.load_commands_dirty);
451 assert(!self.rebase_info_dirty);
442452 assert(!self.binding_info_dirty);
443453 assert(!self.lazy_binding_info_dirty);
444454 assert(!self.export_info_dirty);
......@@ -1289,6 +1299,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
12891299 mem.writeIntLittle(u32, cccode[8..12], 0);
12901300 try self.base.file.?.pwriteAll(&cccode, self.next_stub_helper_off.?);
12911301 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;
12921308 }
12931309
12941310 const text_section = text_segment.sections.items[self.text_section_index.?];
......@@ -1821,12 +1837,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18211837 if (self.dyld_info_cmd_index == null) {
18221838 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
18301840 try self.load_commands.append(self.base.allocator, .{
18311841 .DyldInfoOnly = .{
18321842 .cmd = macho.LC_DYLD_INFO_ONLY,
......@@ -1839,37 +1849,67 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18391849 .weak_bind_size = 0,
18401850 .lazy_bind_off = 0,
18411851 .lazy_bind_size = 0,
1842 .export_off = @intCast(u32, export_off),
1843 .export_size = export_size,
1852 .export_off = 0,
1853 .export_size = 0,
18441854 },
18451855 });
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
18461881 self.header_dirty = true;
18471882 self.load_commands_dirty = true;
18481883 }
18491884 if (self.symtab_cmd_index == null) {
18501885 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
18521900 const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64);
18531901 const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
1854
18551902 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
18571906 try self.string_table.append(self.base.allocator, 0); // Need a null at position 0.
18581907 const strtab_size = self.string_table.items.len;
18591908 const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1);
1860
18611909 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 });
18731913 self.header_dirty = true;
18741914 self.load_commands_dirty = true;
18751915 self.string_table_dirty = true;
......@@ -1877,7 +1917,11 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18771917 if (self.dysymtab_cmd_index == null) {
18781918 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
18821926 try self.load_commands.append(self.base.allocator, .{
18831927 .Dysymtab = .{
......@@ -1895,8 +1939,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
18951939 .nmodtab = 0,
18961940 .extrefsymoff = 0,
18971941 .nextrefsyms = 0,
1898 .indirectsymoff = 0,
1899 .nindirectsyms = 0,
1942 .indirectsymoff = @intCast(u32, indsymtab_off),
1943 .nindirectsyms = @intCast(u32, self.base.options.symbol_count_hint),
19001944 .extreloff = 0,
19011945 .nextrel = 0,
19021946 .locreloff = 0,
......@@ -2565,6 +2609,37 @@ fn writeExportTrie(self: *MachO) !void {
25652609 self.export_info_dirty = false;
25662610}
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
25682643fn writeBindingInfoTable(self: *MachO) !void {
25692644 if (!self.binding_info_dirty) return;
25702645
src/link/MachO/imports.zig+43
......@@ -6,6 +6,49 @@ const mem = std.mem;
66const assert = std.debug.assert;
77const 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
952/// Table of binding info entries used to tell the dyld which
1053/// symbols to bind at loading time.
1154pub const BindingInfoTable = struct {