From 1efdb137d14058f3c428a001838b963de16694ea Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Thu, 9 Sep 2021 14:18:28 +0200 Subject: [PATCH] macho: don't allocate atoms when parsing objects --- src/link/MachO.zig | 170 +++++++++++++++++++++-------------- src/link/MachO/Object.zig | 36 +++++++- src/link/MachO/TextBlock.zig | 54 +++++++++-- 3 files changed, 182 insertions(+), 78 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 909f9b3a9f..4a8df9ae6c 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2594,30 +2594,64 @@ fn resolveDyldStubBinder(self: *MachO) !void { } fn parseTextBlocks(self: *MachO) !void { + var parsed_atoms = Object.ParsedAtoms.init(self.base.allocator); + defer parsed_atoms.deinit(); + + var first_atoms = Object.ParsedAtoms.init(self.base.allocator); + defer first_atoms.deinit(); + var section_metadata = std.AutoHashMap(MatchingSection, struct { size: u64, alignment: u32, }).init(self.base.allocator); defer section_metadata.deinit(); - for (self.objects.items) |object| { - const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - for (seg.sections.items) |sect| { - const match = (try self.getMatchingSection(sect)) orelse { - log.debug("unhandled section", .{}); - continue; - }; - const res = try section_metadata.getOrPut(match); - if (!res.found_existing) { - res.value_ptr.* = .{ + for (self.objects.items) |*object, object_id| { + var atoms_in_objects = try object.parseTextBlocks(self.base.allocator, @intCast(u16, object_id), self); + defer atoms_in_objects.deinit(); + + var it = atoms_in_objects.iterator(); + while (it.next()) |entry| { + const match = entry.key_ptr.*; + const last_atom = entry.value_ptr.*; + var atom = last_atom; + + const metadata = try section_metadata.getOrPut(match); + if (!metadata.found_existing) { + metadata.value_ptr.* = .{ .size = 0, .alignment = 0, }; } - const size = padToIdeal(sect.size); - const alignment = try math.powi(u32, 2, sect.@"align"); - res.value_ptr.size += mem.alignForwardGeneric(u64, size, alignment); - res.value_ptr.alignment = math.max(res.value_ptr.alignment, sect.@"align"); + + while (true) { + const alignment = try math.powi(u32, 2, atom.alignment); + metadata.value_ptr.size += mem.alignForwardGeneric(u64, atom.size, alignment); + metadata.value_ptr.alignment = math.max(metadata.value_ptr.alignment, atom.alignment); + + const sym = self.locals.items[atom.local_sym_index]; + log.debug(" {s}: n_value=0x{x}, size=0x{x}, alignment=0x{x}", .{ + self.getString(sym.n_strx), + sym.n_value, + atom.size, + atom.alignment, + }); + + if (atom.prev) |prev| { + atom = prev; + } else break; + } + + if (parsed_atoms.getPtr(match)) |last| { + last.*.next = atom; + atom.prev = last.*; + last.* = atom; + } + _ = try parsed_atoms.put(match, last_atom); + + if (!first_atoms.contains(match)) { + try first_atoms.putNoClobber(match, atom); + } } } @@ -2625,63 +2659,69 @@ fn parseTextBlocks(self: *MachO) !void { while (it.next()) |entry| { const match = entry.key_ptr.*; const metadata = entry.value_ptr.*; - const seg = self.load_commands.items[match.seg].Segment; - const sect = seg.sections.items[match.sect]; + const seg = &self.load_commands.items[match.seg].Segment; + const sect = &seg.sections.items[match.sect]; log.debug("{s},{s} => size: 0x{x}, alignment: 0x{x}", .{ - commands.segmentName(sect), - commands.sectionName(sect), + commands.segmentName(sect.*), + commands.sectionName(sect.*), metadata.size, metadata.alignment, }); + sect.@"align" = math.max(sect.@"align", metadata.alignment); try self.growSection(match, @intCast(u32, metadata.size)); + + var base_vaddr = if (self.blocks.get(match)) |last| blk: { + const last_atom_sym = self.locals.items[last.local_sym_index]; + break :blk last_atom_sym.n_value + last.size; + } else sect.addr; + const n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); + + var atom = first_atoms.get(match).?; + while (true) { + const alignment = try math.powi(u32, 2, atom.alignment); + base_vaddr = mem.alignForwardGeneric(u64, base_vaddr, alignment); + + const sym = &self.locals.items[atom.local_sym_index]; + sym.n_value = base_vaddr; + sym.n_sect = n_sect; + + log.debug(" {s}: start=0x{x}, end=0x{x}, size=0x{x}, alignment=0x{x}", .{ + self.getString(sym.n_strx), + base_vaddr, + base_vaddr + atom.size, + atom.size, + atom.alignment, + }); + + // Update each alias (if any) + for (atom.aliases.items) |index| { + const alias_sym = &self.locals.items[index]; + alias_sym.n_value = base_vaddr; + alias_sym.n_sect = n_sect; + } + + // Update each symbol contained within the TextBlock + for (atom.contained.items) |sym_at_off| { + const contained_sym = &self.locals.items[sym_at_off.local_sym_index]; + contained_sym.n_value = base_vaddr + sym_at_off.offset; + contained_sym.n_sect = n_sect; + } + + base_vaddr += atom.size; + + if (atom.next) |next| { + atom = next; + } else break; + } + + if (self.blocks.getPtr(match)) |last| { + const first_atom = first_atoms.get(match).?; + last.*.next = first_atom; + first_atom.prev = last.*; + last.* = first_atom; + } + _ = try self.blocks.put(self.base.allocator, match, parsed_atoms.get(match).?); } - - for (self.objects.items) |*object, object_id| { - try object.parseTextBlocks(self.base.allocator, @intCast(u16, object_id), self); - } - - // it = section_metadata.iterator(); - // while (it.next()) |entry| { - // const match = entry.key_ptr.*; - // const metadata = entry.value_ptr.*; - // const seg = self.load_commands.items[match.seg].Segment; - // const sect = seg.sections.items[match.sect]; - - // var buffer = try self.base.allocator.alloc(u8, metadata.size); - // defer self.base.allocator.free(buffer); - // log.warn("{s},{s} buffer size 0x{x}", .{ - // commands.segmentName(sect), - // commands.sectionName(sect), - // metadata.size, - // }); - - // var atom = self.blocks.get(match).?; - - // while (atom.prev) |prev| { - // atom = prev; - // } - - // const base = blk: { - // const sym = self.locals.items[atom.local_sym_index]; - // break :blk sym.n_value; - // }; - - // while (true) { - // const sym = self.locals.items[atom.local_sym_index]; - // const offset = sym.n_value - base; - // try atom.resolveRelocs(self); - // log.warn("writing atom for symbol {s} at buffer offset 0x{x}", .{ - // self.getString(sym.n_strx), - // offset, - // }); - // mem.copy(u8, buffer[offset..][0..atom.code.items.len], atom.code.items); - // atom.dirty = false; - - // if (atom.next) |next| { - // atom = next; - // } else break; - // } - // } } fn addDataInCodeLC(self: *MachO) !void { diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index d94785b377..3bfd6c9f1a 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -317,6 +317,7 @@ const Context = struct { object: *Object, macho_file: *MachO, match: MachO.MatchingSection, + parsed_atoms: *ParsedAtoms, }; const TextBlockParser = struct { @@ -430,6 +431,7 @@ const TextBlockParser = struct { .allocator = context.allocator, .object = context.object, .macho_file = context.macho_file, + .parsed_atoms = context.parsed_atoms, }); if (context.macho_file.has_dices) { @@ -455,12 +457,15 @@ const TextBlockParser = struct { } }; +pub const ParsedAtoms = std.AutoHashMap(MachO.MatchingSection, *TextBlock); + pub fn parseTextBlocks( self: *Object, allocator: *Allocator, object_id: u16, macho_file: *MachO, -) !void { +) !ParsedAtoms { + var parsed_atoms = ParsedAtoms.init(allocator); const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; log.debug("analysing {s}", .{self.name}); @@ -589,6 +594,7 @@ pub fn parseTextBlocks( .allocator = allocator, .object = self, .macho_file = macho_file, + .parsed_atoms = &parsed_atoms, }); if (macho_file.has_dices) { @@ -604,7 +610,13 @@ pub fn parseTextBlocks( } } - _ = try macho_file.allocateAtom(block, match); + if (parsed_atoms.getPtr(match)) |last| { + last.*.next = block; + block.prev = last.*; + last.* = block; + } else { + try parsed_atoms.putNoClobber(match, block); + } try self.text_blocks.append(allocator, block); } @@ -620,6 +632,7 @@ pub fn parseTextBlocks( .object = self, .macho_file = macho_file, .match = match, + .parsed_atoms = &parsed_atoms, })) |block| { const sym = macho_file.locals.items[block.local_sym_index]; const is_ext = blk: { @@ -651,7 +664,13 @@ pub fn parseTextBlocks( } } - _ = try macho_file.allocateAtom(block, match); + if (parsed_atoms.getPtr(match)) |last| { + last.*.next = block; + block.prev = last.*; + last.* = block; + } else { + try parsed_atoms.putNoClobber(match, block); + } try self.text_blocks.append(allocator, block); } @@ -696,6 +715,7 @@ pub fn parseTextBlocks( .allocator = allocator, .object = self, .macho_file = macho_file, + .parsed_atoms = &parsed_atoms, }); if (macho_file.has_dices) { @@ -747,10 +767,18 @@ pub fn parseTextBlocks( }); } - _ = try macho_file.allocateAtom(block, match); + if (parsed_atoms.getPtr(match)) |last| { + last.*.next = block; + block.prev = last.*; + last.* = block; + } else { + try parsed_atoms.putNoClobber(match, block); + } try self.text_blocks.append(allocator, block); } } + + return parsed_atoms; } fn parseSymtab(self: *Object, allocator: *Allocator) !void { diff --git a/src/link/MachO/TextBlock.zig b/src/link/MachO/TextBlock.zig index 97bff8be31..57e93543b1 100644 --- a/src/link/MachO/TextBlock.zig +++ b/src/link/MachO/TextBlock.zig @@ -628,6 +628,7 @@ const RelocContext = struct { allocator: *Allocator, object: *Object, macho_file: *MachO, + parsed_atoms: *Object.ParsedAtoms, }; fn initRelocFromObject(rel: macho.relocation_info, context: RelocContext) !Relocation { @@ -855,7 +856,14 @@ pub fn parseRelocs(self: *TextBlock, relocs: []macho.relocation_info, context: R .seg = context.macho_file.data_const_segment_cmd_index.?, .sect = context.macho_file.got_section_index.?, }; - _ = try context.macho_file.allocateAtom(atom, match); + + if (context.parsed_atoms.getPtr(match)) |last| { + last.*.next = atom; + atom.prev = last.*; + last.* = atom; + } else { + try context.parsed_atoms.putNoClobber(match, atom); + } } else if (parsed_rel.payload == .unsigned) { switch (parsed_rel.where) { .undef => { @@ -918,18 +926,46 @@ pub fn parseRelocs(self: *TextBlock, relocs: []macho.relocation_info, context: R ); const stub_atom = try context.macho_file.createStubAtom(laptr_atom.local_sym_index); try context.macho_file.stubs_map.putNoClobber(context.allocator, parsed_rel.where_index, stub_atom); - _ = try context.macho_file.allocateAtom(stub_helper_atom, .{ + // TODO clean this up! + if (context.parsed_atoms.getPtr(.{ .seg = context.macho_file.text_segment_cmd_index.?, .sect = context.macho_file.stub_helper_section_index.?, - }); - _ = try context.macho_file.allocateAtom(laptr_atom, .{ - .seg = context.macho_file.data_segment_cmd_index.?, - .sect = context.macho_file.la_symbol_ptr_section_index.?, - }); - _ = try context.macho_file.allocateAtom(stub_atom, .{ + })) |last| { + last.*.next = stub_helper_atom; + stub_helper_atom.prev = last.*; + last.* = stub_helper_atom; + } else { + try context.parsed_atoms.putNoClobber(.{ + .seg = context.macho_file.text_segment_cmd_index.?, + .sect = context.macho_file.stub_helper_section_index.?, + }, stub_helper_atom); + } + if (context.parsed_atoms.getPtr(.{ .seg = context.macho_file.text_segment_cmd_index.?, .sect = context.macho_file.stubs_section_index.?, - }); + })) |last| { + last.*.next = stub_atom; + stub_atom.prev = last.*; + last.* = stub_atom; + } else { + try context.parsed_atoms.putNoClobber(.{ + .seg = context.macho_file.text_segment_cmd_index.?, + .sect = context.macho_file.stubs_section_index.?, + }, stub_atom); + } + if (context.parsed_atoms.getPtr(.{ + .seg = context.macho_file.data_segment_cmd_index.?, + .sect = context.macho_file.la_symbol_ptr_section_index.?, + })) |last| { + last.*.next = laptr_atom; + laptr_atom.prev = last.*; + last.* = laptr_atom; + } else { + try context.parsed_atoms.putNoClobber(.{ + .seg = context.macho_file.data_segment_cmd_index.?, + .sect = context.macho_file.la_symbol_ptr_section_index.?, + }, laptr_atom); + } } } }