macho: write out LC_FUNCTION_START data

This commit is contained in:
Jakub Konka 2021-12-08 17:08:40 +01:00
parent 98e36ea6b9
commit 64e2bfaa23

View File

@ -4369,6 +4369,19 @@ fn populateMissingMetadata(self: *MachO) !void {
self.load_commands_dirty = true;
}
if (self.function_starts_cmd_index == null) {
self.function_starts_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
.LinkeditData = .{
.cmd = macho.LC_FUNCTION_STARTS,
.cmdsize = @sizeOf(macho.linkedit_data_command),
.dataoff = 0,
.datasize = 0,
},
});
self.load_commands_dirty = true;
}
if (self.data_in_code_cmd_index == null) {
self.data_in_code_cmd_index = @intCast(u16, self.load_commands.items.len);
try self.load_commands.append(self.base.allocator, .{
@ -5324,6 +5337,95 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void {
}
}
fn writeFunctionStarts(self: *MachO) !void {
var atom = self.atoms.get(.{
.seg = self.text_segment_cmd_index orelse return,
.sect = self.text_section_index orelse return,
}) orelse return;
const tracy = trace(@src());
defer tracy.end();
while (atom.prev) |prev| {
atom = prev;
}
var offsets = std.ArrayList(u32).init(self.base.allocator);
defer offsets.deinit();
const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
var last_off: u32 = 0;
while (true) {
const atom_sym = self.locals.items[atom.local_sym_index];
if (atom_sym.n_strx != 0) blk: {
if (self.symbol_resolver.get(atom_sym.n_strx)) |resolv| {
assert(resolv.where == .global);
if (resolv.local_sym_index != atom.local_sym_index) break :blk;
}
const offset = @intCast(u32, atom_sym.n_value - text_seg.inner.vmaddr);
const diff = offset - last_off;
if (diff == 0) break :blk;
try offsets.append(diff);
last_off = offset;
}
for (atom.contained.items) |cont| {
const cont_sym = self.locals.items[cont.local_sym_index];
if (cont_sym.n_strx == 0) continue;
if (self.symbol_resolver.get(cont_sym.n_strx)) |resolv| {
assert(resolv.where == .global);
if (resolv.local_sym_index != cont.local_sym_index) continue;
}
const offset = @intCast(u32, cont_sym.n_value - text_seg.inner.vmaddr);
const diff = offset - last_off;
if (diff == 0) continue;
try offsets.append(diff);
last_off = offset;
}
if (atom.next) |next| {
atom = next;
} else break;
}
const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64));
var buffer = try self.base.allocator.alloc(u8, max_size);
defer self.base.allocator.free(buffer);
mem.set(u8, buffer, 0);
var stream = std.io.fixedBufferStream(buffer);
var writer = stream.writer();
for (offsets.items) |offset| {
try std.leb.writeULEB128(writer, offset);
}
const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, stream.pos, @sizeOf(u64)));
const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].LinkeditData;
fn_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize);
fn_cmd.datasize = needed_size;
seg.inner.filesize += needed_size;
log.debug("writing function starts info from 0x{x} to 0x{x}", .{
fn_cmd.dataoff,
fn_cmd.dataoff + fn_cmd.datasize,
});
try self.base.file.?.pwriteAll(buffer[0..needed_size], fn_cmd.dataoff);
self.load_commands_dirty = true;
}
fn writeDices(self: *MachO) !void {
if (!self.has_dices) return;
@ -5591,6 +5693,7 @@ fn writeLinkeditSegment(self: *MachO) !void {
seg.inner.filesize = 0;
try self.writeDyldInfoData();
try self.writeFunctionStarts();
try self.writeDices();
try self.writeSymbolTable();
try self.writeStringTable();