diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-09-03 17:12:39 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-09-03 17:12:39 +0200 |
| commit | 80e1c244b6c9d5f3e855878d92ecc09dc8eb970a (patch) | |
| tree | ef93e21cde0713b0749e29951b2d54bfe000575d /src | |
| parent | 1d2199b71c06e97be407c2bd0b05c07c8bd6cd2c (diff) | |
| download | zig-80e1c244b6c9d5f3e855878d92ecc09dc8eb970a.tar.gz zig-80e1c244b6c9d5f3e855878d92ecc09dc8eb970a.zip | |
macho: dyld info subsections need to follow in strict order
MachO, why are doing this to me?
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 362 | ||||
| -rw-r--r-- | src/link/MachO/TextBlock.zig | 4 |
2 files changed, 115 insertions, 251 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index cd5a0218d1..c1fb6977c5 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -162,10 +162,7 @@ stubs_map: std.AutoArrayHashMapUnmanaged(u32, *TextBlock) = .{}, error_flags: File.ErrorFlags = File.ErrorFlags{}, 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, +dyld_info_dirty: bool = false, strtab_dirty: bool = false, strtab_needs_relocation: bool = false, @@ -814,10 +811,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { defer tracy.end(); try self.setEntryPoint(); - try self.writeRebaseInfoTable(); - try self.writeBindInfoTable(); - try self.writeLazyBindInfoTable(); - try self.writeExportInfo(); + try self.writeDyldInfoData(); try self.writeAllGlobalAndUndefSymbols(); try self.writeIndirectSymbolTable(); try self.writeStringTable(); @@ -849,10 +843,7 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { } 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); + assert(!self.dyld_info_dirty); assert(!self.strtab_dirty); assert(!self.strtab_needs_relocation); @@ -2111,7 +2102,7 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, lazy_binding_sym .local_sym_index = lazy_binding_sym_index, .offset = 0, }); - self.lazy_binding_info_dirty = true; + self.dyld_info_dirty = true; return atom; } @@ -2312,7 +2303,7 @@ fn resolveSymbolsInObject( .local_sym_index = local_sym_index, .file = object_id, }; - self.export_info_dirty = true; + self.dyld_info_dirty = true; } else if (symbolIsTentative(sym)) { // Symbol is a tentative definition. const resolv = self.symbol_resolver.getPtr(n_strx) orelse { @@ -2647,7 +2638,7 @@ fn resolveDyldStubBinder(self: *MachO) !void { .sect = self.got_section_index.?, }; _ = try self.allocateAtom(atom, match); - self.binding_info_dirty = true; + self.dyld_info_dirty = true; } fn parseTextBlocks(self: *MachO) !void { @@ -2946,7 +2937,7 @@ pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void { }; const got_atom = try self.createGotAtom(key); try self.got_entries_map.put(self.base.allocator, key, got_atom); - self.rebase_info_dirty = true; + self.dyld_info_dirty = true; } pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { @@ -3266,7 +3257,7 @@ pub fn updateDeclExports( const name_str_index = try self.makeString(exp_name); const i = if (self.globals_free_list.popOrNull()) |i| i else blk: { _ = self.globals.addOneAssumeCapacity(); - self.export_info_dirty = true; + self.dyld_info_dirty = true; break :blk @intCast(u32, self.globals.items.len - 1); }; self.globals.items[i] = .{ @@ -3648,29 +3639,34 @@ pub fn populateMissingMetadata(self: *MachO) !void { }, }); + // Preallocate rebase, binding, lazy binding info, and export info. const dyld = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; + const subsection_size = 128; // TODO this is totally random + const needed_size = 4 * subsection_size; + const offset = self.findFreeSpaceLinkedit(needed_size, 1, null); + + const rebase_off = @intCast(u32, offset); + log.debug("found rebase info free space 0x{x} to 0x{x}", .{ rebase_off, rebase_off + subsection_size }); + dyld.rebase_off = rebase_off; + dyld.rebase_size = subsection_size; + + const bind_off = rebase_off + subsection_size; + log.debug("found binding info free space 0x{x} to 0x{x}", .{ bind_off, bind_off + subsection_size }); + dyld.bind_off = bind_off; + dyld.bind_size = subsection_size; + + const lazy_bind_off = bind_off + subsection_size; + log.debug("found lazy binding info free space 0x{x} to 0x{x}", .{ + lazy_bind_off, + lazy_bind_off + subsection_size, + }); + dyld.lazy_bind_off = lazy_bind_off; + dyld.lazy_bind_size = subsection_size; - // 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, null); - 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, null); - 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, null); - 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, null); - 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; + const export_off = lazy_bind_off + subsection_size; + log.debug("found export info free space 0x{x} to 0x{x}", .{ export_off, export_off + subsection_size }); + dyld.export_off = export_off; + dyld.export_size = subsection_size; self.load_commands_dirty = true; } @@ -4097,10 +4093,6 @@ fn allocatedSizeLinkedit(self: *MachO, start: u64) u64 { if (self.dyld_info_cmd_index) |idx| { const dyld_info = self.load_commands.items[idx].DyldInfoOnly; if (dyld_info.rebase_off > start and dyld_info.rebase_off < min_pos) min_pos = dyld_info.rebase_off; - if (dyld_info.bind_off > start and dyld_info.bind_off < min_pos) min_pos = dyld_info.bind_off; - if (dyld_info.weak_bind_off > start and dyld_info.weak_bind_off < min_pos) min_pos = dyld_info.weak_bind_off; - if (dyld_info.lazy_bind_off > start and dyld_info.lazy_bind_off < min_pos) min_pos = dyld_info.lazy_bind_off; - if (dyld_info.export_off > start and dyld_info.export_off < min_pos) min_pos = dyld_info.export_off; } if (self.function_starts_cmd_index) |idx| { @@ -4141,27 +4133,14 @@ fn detectAllocCollisionLinkedit(self: *MachO, start: u64, size: u64) ?u64 { // __LINKEDIT is a weird segment where sections get their own load commands so we // special-case it. - if (self.dyld_info_cmd_index) |idx| outer: { - if (self.load_commands.items.len == idx) break :outer; + if (self.dyld_info_cmd_index) |idx| { const dyld_info = self.load_commands.items[idx].DyldInfoOnly; - if (checkForCollision(start, end, dyld_info.rebase_off, dyld_info.rebase_size)) |pos| { - return pos; - } - // Binding info - if (checkForCollision(start, end, dyld_info.bind_off, dyld_info.bind_size)) |pos| { - return pos; - } - // Weak binding info - if (checkForCollision(start, end, dyld_info.weak_bind_off, dyld_info.weak_bind_size)) |pos| { - return pos; - } - // Lazy binding info - if (checkForCollision(start, end, dyld_info.lazy_bind_off, dyld_info.lazy_bind_size)) |pos| { - return pos; - } - // Export info - if (checkForCollision(start, end, dyld_info.export_off, dyld_info.export_size)) |pos| { - return pos; + const offset = dyld_info.rebase_off; + const actual_size = dyld_info.export_off + dyld_info.export_size - offset; + const increased_size = padToIdeal(actual_size); + const test_end = offset + increased_size; + if (end > offset and start < test_end) { + return test_end; } } @@ -4483,145 +4462,53 @@ fn writeCodeSignature(self: *MachO) !void { try self.base.file.?.pwriteAll(buffer, code_sig_cmd.dataoff); } -fn writeExportInfo(self: *MachO) !void { - if (!self.export_info_dirty) return; - if (self.globals.items.len == 0) return; - - const tracy = trace(@src()); - defer tracy.end(); - - var trie: Trie = .{}; - defer trie.deinit(self.base.allocator); - - const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const base_address = text_segment.inner.vmaddr; - - // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. - log.debug("writing export trie", .{}); - - for (self.globals.items) |sym| { - const sym_name = self.getString(sym.n_strx); - log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value }); - - try trie.put(self.base.allocator, .{ - .name = sym_name, - .vmaddr_offset = sym.n_value - base_address, - .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, - }); - } - try trie.finalize(self.base.allocator); - - var buffer = try self.base.allocator.alloc(u8, @intCast(usize, trie.size)); - defer self.base.allocator.free(buffer); - var stream = std.io.fixedBufferStream(buffer); - const nwritten = try trie.write(stream.writer()); - assert(nwritten == trie.size); - - const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; - const allocated_size = self.allocatedSizeLinkedit(dyld_info.export_off); - const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); - - if (needed_size > allocated_size) { - dyld_info.export_off = 0; - dyld_info.export_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null)); - // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too. - } - dyld_info.export_size = @intCast(u32, needed_size); - log.debug("writing export info from 0x{x} to 0x{x}", .{ dyld_info.export_off, dyld_info.export_off + dyld_info.export_size }); - - try self.base.file.?.pwriteAll(buffer, dyld_info.export_off); - self.load_commands_dirty = true; - self.export_info_dirty = false; -} - -fn writeRebaseInfoTable(self: *MachO) !void { - if (!self.rebase_info_dirty) return; +fn writeDyldInfoData(self: *MachO) !void { + if (!self.dyld_info_dirty) return; const tracy = trace(@src()); defer tracy.end(); - var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); - defer pointers.deinit(); + var rebase_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); + defer rebase_pointers.deinit(); + var bind_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); + defer bind_pointers.deinit(); + var lazy_bind_pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); + defer lazy_bind_pointers.deinit(); { var it = self.blocks.iterator(); while (it.next()) |entry| { const match = entry.key_ptr.*; - var block: *TextBlock = entry.value_ptr.*; + var atom: *TextBlock = entry.value_ptr.*; if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable const seg = self.load_commands.items[match.seg].Segment; while (true) { - const sym = self.locals.items[block.local_sym_index]; + const sym = self.locals.items[atom.local_sym_index]; const base_offset = sym.n_value - seg.inner.vmaddr; - for (block.rebases.items) |offset| { - try pointers.append(.{ + for (atom.rebases.items) |offset| { + try rebase_pointers.append(.{ .offset = base_offset + offset, .segment_id = match.seg, }); } - if (block.prev) |prev| { - block = prev; - } else break; - } - } - } - - const size = try bind.rebaseInfoSize(pointers.items); - var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); - defer self.base.allocator.free(buffer); - - var stream = std.io.fixedBufferStream(buffer); - try bind.writeRebaseInfo(pointers.items, stream.writer()); - - 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, null)); - // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too. - } - - 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 writeBindInfoTable(self: *MachO) !void { - if (!self.binding_info_dirty) return; - - const tracy = trace(@src()); - defer tracy.end(); - - var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); - defer pointers.deinit(); - - { - var it = self.blocks.iterator(); - while (it.next()) |entry| { - const match = entry.key_ptr.*; - var block: *TextBlock = entry.value_ptr.*; - - if (match.seg == self.text_segment_cmd_index.?) continue; // __TEXT is non-writable - - const seg = self.load_commands.items[match.seg].Segment; - - while (true) { - const sym = self.locals.items[block.local_sym_index]; - const base_offset = sym.n_value - seg.inner.vmaddr; + for (atom.bindings.items) |binding| { + const bind_sym = self.undefs.items[binding.local_sym_index]; + try bind_pointers.append(.{ + .offset = binding.offset + base_offset, + .segment_id = match.seg, + .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER), + .name = self.getString(bind_sym.n_strx), + }); + } - for (block.bindings.items) |binding| { + for (atom.lazy_bindings.items) |binding| { const bind_sym = self.undefs.items[binding.local_sym_index]; - try pointers.append(.{ + try lazy_bind_pointers.append(.{ .offset = binding.offset + base_offset, .segment_id = match.seg, .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER), @@ -4629,97 +4516,76 @@ fn writeBindInfoTable(self: *MachO) !void { }); } - if (block.prev) |prev| { - block = prev; + if (atom.prev) |prev| { + atom = prev; } else break; } } } - const size = try bind.bindInfoSize(pointers.items); - var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); - defer self.base.allocator.free(buffer); + var trie: Trie = .{}; + defer trie.deinit(self.base.allocator); - var stream = std.io.fixedBufferStream(buffer); - try bind.writeBindInfo(pointers.items, stream.writer()); + { + // TODO handle macho.EXPORT_SYMBOL_FLAGS_REEXPORT and macho.EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER. + log.debug("writing export trie", .{}); + const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const base_address = text_segment.inner.vmaddr; + + for (self.globals.items) |sym| { + const sym_name = self.getString(sym.n_strx); + log.debug(" (putting '{s}' defined at 0x{x})", .{ sym_name, sym.n_value }); + + try trie.put(self.base.allocator, .{ + .name = sym_name, + .vmaddr_offset = sym.n_value - base_address, + .export_flags = macho.EXPORT_SYMBOL_FLAGS_KIND_REGULAR, + }); + } + + try trie.finalize(self.base.allocator); + } const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; - const allocated_size = self.allocatedSizeLinkedit(dyld_info.bind_off); - const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); + const allocated_size = self.allocatedSizeLinkedit(dyld_info.rebase_off); + const rebase_size = @intCast(u32, try bind.rebaseInfoSize(rebase_pointers.items)); + const bind_size = @intCast(u32, try bind.bindInfoSize(bind_pointers.items)); + const lazy_bind_size = @intCast(u32, try bind.lazyBindInfoSize(lazy_bind_pointers.items)); + const export_size = @intCast(u32, trie.size); + const total_size = rebase_size + bind_size + lazy_bind_size + export_size; + const needed_size = mem.alignForwardGeneric(u64, total_size, @alignOf(u64)); if (needed_size > allocated_size) { - dyld_info.bind_off = 0; - dyld_info.bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null)); - // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too. + dyld_info.rebase_off = 0; + dyld_info.rebase_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null)); } - dyld_info.bind_size = @intCast(u32, needed_size); - log.debug("writing binding info from 0x{x} to 0x{x}", .{ dyld_info.bind_off, dyld_info.bind_off + dyld_info.bind_size }); + dyld_info.rebase_size = rebase_size; + dyld_info.bind_off = dyld_info.rebase_off + dyld_info.rebase_size; + dyld_info.bind_size = bind_size; + dyld_info.lazy_bind_off = dyld_info.bind_off + dyld_info.bind_size; + dyld_info.lazy_bind_size = lazy_bind_size; + dyld_info.export_off = dyld_info.lazy_bind_off + dyld_info.lazy_bind_size; + dyld_info.export_size = export_size; - try self.base.file.?.pwriteAll(buffer, dyld_info.bind_off); - self.load_commands_dirty = true; - self.binding_info_dirty = false; -} - -fn writeLazyBindInfoTable(self: *MachO) !void { - if (!self.lazy_binding_info_dirty) return; - - const tracy = trace(@src()); - defer tracy.end(); - - var pointers = std.ArrayList(bind.Pointer).init(self.base.allocator); - defer pointers.deinit(); - - if (self.la_symbol_ptr_section_index) |sect| blk: { - var atom = self.blocks.get(.{ - .seg = self.data_segment_cmd_index.?, - .sect = sect, - }) orelse break :blk; - const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; - - while (true) { - const sym = self.locals.items[atom.local_sym_index]; - const base_offset = sym.n_value - seg.inner.vmaddr; - - for (atom.lazy_bindings.items) |binding| { - const bind_sym = self.undefs.items[binding.local_sym_index]; - try pointers.append(.{ - .offset = binding.offset + base_offset, - .segment_id = self.data_segment_cmd_index.?, - .dylib_ordinal = @divExact(bind_sym.n_desc, macho.N_SYMBOL_RESOLVER), - .name = self.getString(bind_sym.n_strx), - }); - } - if (atom.prev) |prev| { - atom = prev; - } else break; - } - } - - const size = try bind.lazyBindInfoSize(pointers.items); - var buffer = try self.base.allocator.alloc(u8, @intCast(usize, size)); + var buffer = try self.base.allocator.alloc(u8, needed_size); defer self.base.allocator.free(buffer); + mem.set(u8, buffer, 0); var stream = std.io.fixedBufferStream(buffer); - try bind.writeLazyBindInfo(pointers.items, stream.writer()); + const writer = stream.writer(); - const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].DyldInfoOnly; - const allocated_size = self.allocatedSizeLinkedit(dyld_info.lazy_bind_off); - const needed_size = mem.alignForwardGeneric(u64, buffer.len, @alignOf(u64)); + try bind.writeRebaseInfo(rebase_pointers.items, writer); + try bind.writeBindInfo(bind_pointers.items, writer); + try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer); + _ = try trie.write(writer); - if (needed_size > allocated_size) { - dyld_info.lazy_bind_off = 0; - dyld_info.lazy_bind_off = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1, null)); - // TODO this might require relocating all following LC_DYLD_INFO_ONLY sections too. - } - - dyld_info.lazy_bind_size = @intCast(u32, needed_size); - log.debug("writing lazy binding info from 0x{x} to 0x{x}", .{ dyld_info.lazy_bind_off, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size }); + log.debug("writing dyld info from 0x{x} to 0x{x}", .{ dyld_info.rebase_off, dyld_info.rebase_off + needed_size }); - try self.base.file.?.pwriteAll(buffer, dyld_info.lazy_bind_off); - try self.populateLazyBindOffsetsInStubHelper(buffer); + try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); + try self.populateLazyBindOffsetsInStubHelper(buffer[rebase_size + bind_size ..][0..lazy_bind_size]); self.load_commands_dirty = true; - self.lazy_binding_info_dirty = false; + self.dyld_info_dirty = false; } fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { diff --git a/src/link/MachO/TextBlock.zig b/src/link/MachO/TextBlock.zig index af9bf5b3eb..e6ceed9c55 100644 --- a/src/link/MachO/TextBlock.zig +++ b/src/link/MachO/TextBlock.zig @@ -844,9 +844,7 @@ pub fn parseRelocs(self: *TextBlock, relocs: []macho.relocation_info, context: R .sect = context.macho_file.got_section_index.?, }; _ = try context.macho_file.allocateAtom(atom, match); - // TODO don't need both at once - context.macho_file.rebase_info_dirty = true; - context.macho_file.binding_info_dirty = true; + context.macho_file.dyld_info_dirty = true; } else if (parsed_rel.payload == .unsigned) { switch (parsed_rel.where) { .undef => { |
