macho: don't allocate atoms when parsing objects

This commit is contained in:
Jakub Konka 2021-09-09 14:18:28 +02:00
parent 23be9cae34
commit 1efdb137d1
3 changed files with 182 additions and 78 deletions

View File

@ -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 {

View File

@ -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 {

View File

@ -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);
}
}
}
}