diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-01-06 17:24:48 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-01-13 23:54:01 +0100 |
| commit | 5ae82956aac029fb45c09e5da2db8d86b9fa0157 (patch) | |
| tree | a695528ce280c734a182594728356bbbc4536024 /src | |
| parent | f44732c1b0d7516c4a8169f7381cbcf55e1ae460 (diff) | |
| download | zig-5ae82956aac029fb45c09e5da2db8d86b9fa0157.tar.gz zig-5ae82956aac029fb45c09e5da2db8d86b9fa0157.zip | |
macho: write out rebase info
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 121 | ||||
| -rw-r--r-- | src/link/MachO/imports.zig | 43 |
2 files changed, 141 insertions, 23 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 079478ecfd..23ebff1784 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -123,6 +123,8 @@ string_table: std.ArrayListUnmanaged(u8) = .{}, /// Table of trampolines to the actual symbols in __text section. offset_table: std.ArrayListUnmanaged(u64) = .{}, +/// Table of rebase info entries. +rebase_info_table: RebaseInfoTable = .{}, /// Table of binding info entries. binding_info_table: BindingInfoTable = .{}, /// Table of lazy binding info entries. @@ -133,6 +135,7 @@ error_flags: File.ErrorFlags = File.ErrorFlags{}, offset_table_count_dirty: bool = false, header_dirty: bool = false, load_commands_dirty: bool = false, +rebase_info_dirty: bool = false, binding_info_dirty: bool = false, lazy_binding_info_dirty: bool = false, export_info_dirty: bool = false, @@ -400,6 +403,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { main_cmd.entryoff = addr - text_segment.inner.vmaddr; self.load_commands_dirty = true; } + try self.writeRebaseInfoTable(); try self.writeBindingInfoTable(); try self.writeLazyBindingInfoTable(); try self.writeExportTrie(); @@ -425,6 +429,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { .Lib => return error.TODOImplementWritingLibFiles, } + { + const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; + dysymtab.nindirectsyms = 0; + } + try self.writeLoadCommands(); try self.writeHeader(); @@ -439,6 +448,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { assert(!self.offset_table_count_dirty); assert(!self.header_dirty); assert(!self.load_commands_dirty); + assert(!self.rebase_info_dirty); assert(!self.binding_info_dirty); assert(!self.lazy_binding_info_dirty); assert(!self.export_info_dirty); @@ -1289,6 +1299,12 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void { mem.writeIntLittle(u32, cccode[8..12], 0); try self.base.file.?.pwriteAll(&cccode, self.next_stub_helper_off.?); self.next_stub_helper_off = self.next_stub_helper_off.? + 3 * @sizeOf(u32); + + try self.rebase_info_table.symbols.append(self.base.allocator, .{ + .segment = 3, + .offset = 0, + }); + self.rebase_info_dirty = true; } const text_section = text_segment.sections.items[self.text_section_index.?]; @@ -1821,12 +1837,6 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.dyld_info_cmd_index == null) { self.dyld_info_cmd_index = @intCast(u16, self.load_commands.items.len); - // TODO Preallocate rebase, binding, and lazy binding info. - const export_size = 2; - const export_off = self.findFreeSpaceLinkedit(export_size, 1); - - log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + export_size }); - try self.load_commands.append(self.base.allocator, .{ .DyldInfoOnly = .{ .cmd = macho.LC_DYLD_INFO_ONLY, @@ -1839,37 +1849,67 @@ pub fn populateMissingMetadata(self: *MachO) !void { .weak_bind_size = 0, .lazy_bind_off = 0, .lazy_bind_size = 0, - .export_off = @intCast(u32, export_off), - .export_size = export_size, + .export_off = 0, + .export_size = 0, }, }); + + const dyld = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + + // Preallocate rebase, binding, lazy binding info, and export info. + const expected_size = 48; // TODO This is totally random. + const rebase_off = self.findFreeSpaceLinkedit(expected_size, 1); + log.debug("found rebase info free space 0x{x} to 0x{x}", .{ rebase_off, rebase_off + expected_size }); + dyld.rebase_off = @intCast(u32, rebase_off); + dyld.rebase_size = expected_size; + + const bind_off = self.findFreeSpaceLinkedit(expected_size, 1); + log.debug("found binding info free space 0x{x} to 0x{x}", .{ bind_off, bind_off + expected_size }); + dyld.bind_off = @intCast(u32, bind_off); + dyld.bind_size = expected_size; + + const lazy_bind_off = self.findFreeSpaceLinkedit(expected_size, 1); + log.debug("found lazy binding info free space 0x{x} to 0x{x}", .{ lazy_bind_off, lazy_bind_off + expected_size }); + dyld.lazy_bind_off = @intCast(u32, lazy_bind_off); + dyld.lazy_bind_size = expected_size; + + const export_off = self.findFreeSpaceLinkedit(expected_size, 1); + log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + expected_size }); + dyld.export_off = @intCast(u32, export_off); + dyld.export_size = expected_size; + self.header_dirty = true; self.load_commands_dirty = true; } if (self.symtab_cmd_index == null) { self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len); + try self.load_commands.append(self.base.allocator, .{ + .Symtab = .{ + .cmd = macho.LC_SYMTAB, + .cmdsize = @sizeOf(macho.symtab_command), + .symoff = 0, + .nsyms = 0, + .stroff = 0, + .strsize = 0, + }, + }); + + const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab; + const symtab_size = self.base.options.symbol_count_hint * @sizeOf(macho.nlist_64); const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64)); - log.debug("found symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size }); + symtab.symoff = @intCast(u32, symtab_off); + symtab.nsyms = @intCast(u32, self.base.options.symbol_count_hint); try self.string_table.append(self.base.allocator, 0); // Need a null at position 0. const strtab_size = self.string_table.items.len; const strtab_off = self.findFreeSpaceLinkedit(strtab_size, 1); - log.debug("found string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + strtab_size }); + symtab.stroff = @intCast(u32, strtab_off); + symtab.strsize = @intCast(u32, strtab_size); - try self.load_commands.append(self.base.allocator, .{ - .Symtab = .{ - .cmd = macho.LC_SYMTAB, - .cmdsize = @sizeOf(macho.symtab_command), - .symoff = @intCast(u32, symtab_off), - .nsyms = @intCast(u32, self.base.options.symbol_count_hint), - .stroff = @intCast(u32, strtab_off), - .strsize = @intCast(u32, strtab_size), - }, - }); self.header_dirty = true; self.load_commands_dirty = true; self.string_table_dirty = true; @@ -1877,7 +1917,11 @@ pub fn populateMissingMetadata(self: *MachO) !void { if (self.dysymtab_cmd_index == null) { self.dysymtab_cmd_index = @intCast(u16, self.load_commands.items.len); - // TODO Preallocate space for indirect symbol table. + // Preallocate space for indirect symbol table. + const indsymtab_size = self.base.options.symbol_count_hint * @sizeOf(u64); // Each entry is just a u64. + const indsymtab_off = self.findFreeSpaceLinkedit(indsymtab_size, @sizeOf(u64)); + + log.debug("found indirect symbol table free space 0x{x} to 0x{x}", .{ indsymtab_off, indsymtab_off + indsymtab_size }); try self.load_commands.append(self.base.allocator, .{ .Dysymtab = .{ @@ -1895,8 +1939,8 @@ pub fn populateMissingMetadata(self: *MachO) !void { .nmodtab = 0, .extrefsymoff = 0, .nextrefsyms = 0, - .indirectsymoff = 0, - .nindirectsyms = 0, + .indirectsymoff = @intCast(u32, indsymtab_off), + .nindirectsyms = @intCast(u32, self.base.options.symbol_count_hint), .extreloff = 0, .nextrel = 0, .locreloff = 0, @@ -2565,6 +2609,37 @@ fn writeExportTrie(self: *MachO) !void { self.export_info_dirty = false; } +fn writeRebaseInfoTable(self: *MachO) !void { + if (!self.rebase_info_dirty) return; + + const tracy = trace(@src()); + defer tracy.end(); + + const size = try self.rebase_info_table.calcSize(); + var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); + defer self.base.allocator.free(buffer); + + var stream = std.io.fixedBufferStream(buffer); + try self.rebase_info_table.write(stream.writer()); + + const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment; + const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off); + const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); + + if (needed_size > allocated_size) { + dyld_info.rebase_off = 0; + dyld_info.rebase_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1)); + } + + dyld_info.rebase_size = @intCast(u32, needed_size); + log.debug("writing rebase info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + dyld_info.rebase_size }); + + try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); + self.load_commands_dirty = true; + self.rebase_info_dirty = false; +} + fn writeBindingInfoTable(self: *MachO) !void { if (!self.binding_info_dirty) return; diff --git a/src/link/MachO/imports.zig b/src/link/MachO/imports.zig index c7f8d5c6a5..d039359c97 100644 --- a/src/link/MachO/imports.zig +++ b/src/link/MachO/imports.zig @@ -6,6 +6,49 @@ const mem = std.mem; const assert = std.debug.assert; const Allocator = mem.Allocator; +pub const RebaseInfoTable = struct { + rebase_type: u8 = macho.REBASE_TYPE_POINTER, + symbols: std.ArrayListUnmanaged(Symbol) = .{}, + + pub const Symbol = struct { + segment: u8, + offset: i64, + }; + + pub fn deinit(self: *RebaseInfoTable, allocator: *Allocator) void { + self.symbols.deinit(allocator); + } + + /// Write the rebase info table to byte stream. + pub fn write(self: RebaseInfoTable, writer: anytype) !void { + try writer.writeByte(macho.REBASE_OPCODE_SET_TYPE_IMM | @truncate(u4, self.rebase_type)); + + for (self.symbols.items) |symbol| { + try writer.writeByte(macho.REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment)); + try leb.writeILEB128(writer, symbol.offset); + try writer.writeByte(macho.REBASE_OPCODE_DO_REBASE_IMM_TIMES | @truncate(u4, 1)); + } + + try writer.writeByte(macho.REBASE_OPCODE_DONE); + } + + /// Calculate size in bytes of this rebase info table. + pub fn calcSize(self: *RebaseInfoTable) !u64 { + var stream = std.io.countingWriter(std.io.null_writer); + var writer = stream.writer(); + var size: u64 = 1; + + for (self.symbols.items) |symbol| { + size += 1; + try leb.writeILEB128(writer, symbol.offset); + size += 1; + } + + size += 1 + stream.bytes_written; + return size; + } +}; + /// Table of binding info entries used to tell the dyld which /// symbols to bind at loading time. pub const BindingInfoTable = struct { |
