macho: write symbol and string tables to dSym
This commit is contained in:
@@ -301,6 +301,9 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
|
||||
try self.populateMissingMetadata();
|
||||
try self.d_sym.?.populateMissingMetadata(allocator);
|
||||
|
||||
try self.writeLocalSymbol(0);
|
||||
try self.d_sym.?.writeLocalSymbol(0);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -1123,6 +1126,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
|
||||
symbol.n_desc = 0;
|
||||
|
||||
try self.writeLocalSymbol(decl.link.macho.local_sym_index);
|
||||
try self.d_sym.?.writeLocalSymbol(decl.link.macho.local_sym_index);
|
||||
} else {
|
||||
const decl_name = mem.spanZ(decl.name);
|
||||
const name_str_index = try self.makeString(decl_name);
|
||||
@@ -1140,6 +1144,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
|
||||
self.offset_table.items[decl.link.macho.offset_table_index] = addr;
|
||||
|
||||
try self.writeLocalSymbol(decl.link.macho.local_sym_index);
|
||||
try self.d_sym.?.writeLocalSymbol(decl.link.macho.local_sym_index);
|
||||
try self.writeOffsetTableEntry(decl.link.macho.offset_table_index);
|
||||
}
|
||||
|
||||
@@ -1517,7 +1522,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
.strsize = @intCast(u32, strtab_size),
|
||||
},
|
||||
});
|
||||
try self.writeLocalSymbol(0);
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
self.string_table_dirty = true;
|
||||
@@ -1795,6 +1799,7 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {
|
||||
self.string_table.appendSliceAssumeCapacity(bytes);
|
||||
self.string_table.appendAssumeCapacity(0);
|
||||
self.string_table_dirty = true;
|
||||
self.d_sym.?.string_table_dirty = true;
|
||||
return @intCast(u32, result);
|
||||
}
|
||||
|
||||
@@ -2247,7 +2252,6 @@ fn writeStringTable(self: *MachO) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
|
||||
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
|
||||
const needed_size = mem.alignForwardGeneric(u64, self.string_table.items.len, @alignOf(u64));
|
||||
|
||||
@@ -10,7 +10,11 @@ const DW = std.dwarf;
|
||||
const leb = std.leb;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
const MachO = @import("../MachO.zig");
|
||||
const satMul = MachO.satMul;
|
||||
const alloc_num = MachO.alloc_num;
|
||||
const alloc_den = MachO.alloc_den;
|
||||
|
||||
usingnamespace @import("commands.zig");
|
||||
|
||||
@@ -40,8 +44,12 @@ uuid_cmd_index: ?u16 = null,
|
||||
/// Index into __TEXT,__text section.
|
||||
text_section_index: ?u16 = null,
|
||||
|
||||
linkedit_off: u16 = 0x1000,
|
||||
linkedit_size: u16 = 0x1000,
|
||||
|
||||
header_dirty: bool = false,
|
||||
load_commands_dirty: bool = false,
|
||||
string_table_dirty: bool = false,
|
||||
|
||||
/// You must call this function *after* `MachO.populateMissingMetadata()`
|
||||
/// has been called to get a viable debug symbols output.
|
||||
@@ -61,22 +69,6 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
|
||||
self.header = header;
|
||||
self.header_dirty = true;
|
||||
}
|
||||
if (self.pagezero_segment_cmd_index == null) {
|
||||
self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].Segment;
|
||||
try self.copySegmentCommand(allocator, base_cmd);
|
||||
}
|
||||
if (self.text_segment_cmd_index == null) {
|
||||
self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].Segment;
|
||||
try self.copySegmentCommand(allocator, base_cmd);
|
||||
}
|
||||
if (self.data_segment_cmd_index == null) outer: {
|
||||
if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
|
||||
self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].Segment;
|
||||
try self.copySegmentCommand(allocator, base_cmd);
|
||||
}
|
||||
if (self.uuid_cmd_index == null) {
|
||||
const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?];
|
||||
self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
@@ -84,13 +76,79 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
|
||||
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);
|
||||
const base_cmd = self.base.load_commands.items[self.base.symtab_cmd_index.?].Symtab;
|
||||
const symtab_size = base_cmd.nsyms * @sizeOf(macho.nlist_64);
|
||||
const symtab_off = self.findFreeSpaceLinkedit(symtab_size, @sizeOf(macho.nlist_64));
|
||||
|
||||
log.debug("found dSym symbol table free space 0x{x} to 0x{x}", .{ symtab_off, symtab_off + symtab_size });
|
||||
|
||||
const strtab_off = self.findFreeSpaceLinkedit(base_cmd.strsize, 1);
|
||||
|
||||
log.debug("found dSym string table free space 0x{x} to 0x{x}", .{ strtab_off, strtab_off + base_cmd.strsize });
|
||||
|
||||
try self.load_commands.append(allocator, .{
|
||||
.Symtab = .{
|
||||
.cmd = macho.LC_SYMTAB,
|
||||
.cmdsize = @sizeOf(macho.symtab_command),
|
||||
.symoff = @intCast(u32, symtab_off),
|
||||
.nsyms = base_cmd.nsyms,
|
||||
.stroff = @intCast(u32, strtab_off),
|
||||
.strsize = base_cmd.strsize,
|
||||
},
|
||||
});
|
||||
try self.writeLocalSymbol(0);
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
self.string_table_dirty = true;
|
||||
}
|
||||
if (self.pagezero_segment_cmd_index == null) {
|
||||
self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].Segment;
|
||||
const cmd = try self.copySegmentCommand(allocator, base_cmd);
|
||||
try self.load_commands.append(allocator, .{ .Segment = cmd });
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
if (self.text_segment_cmd_index == null) {
|
||||
self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].Segment;
|
||||
const cmd = try self.copySegmentCommand(allocator, base_cmd);
|
||||
try self.load_commands.append(allocator, .{ .Segment = cmd });
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
if (self.data_segment_cmd_index == null) outer: {
|
||||
if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
|
||||
self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].Segment;
|
||||
const cmd = try self.copySegmentCommand(allocator, base_cmd);
|
||||
try self.load_commands.append(allocator, .{ .Segment = cmd });
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
if (self.linkedit_segment_cmd_index == null) {
|
||||
self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
|
||||
const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].Segment;
|
||||
var cmd = try self.copySegmentCommand(allocator, base_cmd);
|
||||
cmd.inner.vmsize = self.linkedit_size;
|
||||
cmd.inner.fileoff = self.linkedit_off;
|
||||
cmd.inner.filesize = self.linkedit_size;
|
||||
try self.load_commands.append(allocator, .{ .Segment = cmd });
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(self: *DebugSymbols, allocator: *Allocator) !void {
|
||||
try self.writeStringTable();
|
||||
try self.writeLoadCommands(allocator);
|
||||
try self.writeHeader();
|
||||
|
||||
assert(!self.header_dirty);
|
||||
assert(!self.load_commands_dirty);
|
||||
assert(!self.string_table_dirty);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
|
||||
@@ -100,7 +158,7 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
|
||||
self.file.close();
|
||||
}
|
||||
|
||||
fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !void {
|
||||
fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand {
|
||||
var cmd = SegmentCommand.empty(.{
|
||||
.cmd = macho.LC_SEGMENT_64,
|
||||
.cmdsize = base_cmd.inner.cmdsize,
|
||||
@@ -142,9 +200,7 @@ fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: Segm
|
||||
cmd.sections.appendAssumeCapacity(sect);
|
||||
}
|
||||
|
||||
try self.load_commands.append(allocator, .{ .Segment = cmd });
|
||||
self.header_dirty = true;
|
||||
self.load_commands_dirty = true;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/// Writes all load commands and section headers.
|
||||
@@ -182,3 +238,115 @@ fn writeHeader(self: *DebugSymbols) !void {
|
||||
try self.file.pwriteAll(mem.asBytes(&self.header.?), 0);
|
||||
self.header_dirty = false;
|
||||
}
|
||||
|
||||
fn allocatedSizeLinkedit(self: *DebugSymbols, start: u64) u64 {
|
||||
assert(start > 0);
|
||||
var min_pos: u64 = std.math.maxInt(u64);
|
||||
|
||||
if (self.symtab_cmd_index) |idx| {
|
||||
const symtab = self.load_commands.items[idx].Symtab;
|
||||
if (symtab.symoff >= start and symtab.symoff < min_pos) min_pos = symtab.symoff;
|
||||
if (symtab.stroff >= start and symtab.stroff < min_pos) min_pos = symtab.stroff;
|
||||
}
|
||||
|
||||
return min_pos - start;
|
||||
}
|
||||
|
||||
fn detectAllocCollisionLinkedit(self: *DebugSymbols, start: u64, size: u64) ?u64 {
|
||||
const end = start + satMul(size, alloc_num) / alloc_den;
|
||||
|
||||
if (self.symtab_cmd_index) |idx| outer: {
|
||||
if (self.load_commands.items.len == idx) break :outer;
|
||||
const symtab = self.load_commands.items[idx].Symtab;
|
||||
{
|
||||
// Symbol table
|
||||
const symsize = symtab.nsyms * @sizeOf(macho.nlist_64);
|
||||
const increased_size = satMul(symsize, alloc_num) / alloc_den;
|
||||
const test_end = symtab.symoff + increased_size;
|
||||
if (end > symtab.symoff and start < test_end) {
|
||||
return test_end;
|
||||
}
|
||||
}
|
||||
{
|
||||
// String table
|
||||
const increased_size = satMul(symtab.strsize, alloc_num) / alloc_den;
|
||||
const test_end = symtab.stroff + increased_size;
|
||||
if (end > symtab.stroff and start < test_end) {
|
||||
return test_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fn findFreeSpaceLinkedit(self: *DebugSymbols, object_size: u64, min_alignment: u16) u64 {
|
||||
var start: u64 = self.linkedit_off;
|
||||
while (self.detectAllocCollisionLinkedit(start, object_size)) |item_end| {
|
||||
start = mem.alignForwardGeneric(u64, item_end, min_alignment);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
fn relocateSymbolTable(self: *DebugSymbols) !void {
|
||||
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
const nlocals = self.base.local_symbols.items.len;
|
||||
const nglobals = self.base.global_symbols.items.len;
|
||||
const nsyms = nlocals + nglobals;
|
||||
|
||||
if (symtab.nsyms < nsyms) {
|
||||
const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
|
||||
const needed_size = nsyms * @sizeOf(macho.nlist_64);
|
||||
if (needed_size > self.allocatedSizeLinkedit(symtab.symoff)) {
|
||||
// Move the entire symbol table to a new location
|
||||
const new_symoff = self.findFreeSpaceLinkedit(needed_size, @alignOf(macho.nlist_64));
|
||||
const existing_size = symtab.nsyms * @sizeOf(macho.nlist_64);
|
||||
|
||||
assert(new_symoff + existing_size <= self.linkedit_off + self.linkedit_size);
|
||||
log.debug("relocating dSym symbol table from 0x{x}-0x{x} to 0x{x}-0x{x}", .{
|
||||
symtab.symoff,
|
||||
symtab.symoff + existing_size,
|
||||
new_symoff,
|
||||
new_symoff + existing_size,
|
||||
});
|
||||
|
||||
const amt = try self.file.copyRangeAll(symtab.symoff, self.file, new_symoff, existing_size);
|
||||
if (amt != existing_size) return error.InputOutput;
|
||||
symtab.symoff = @intCast(u32, new_symoff);
|
||||
}
|
||||
symtab.nsyms = @intCast(u32, nsyms);
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writeLocalSymbol(self: *DebugSymbols, index: usize) !void {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
try self.relocateSymbolTable();
|
||||
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
const off = symtab.symoff + @sizeOf(macho.nlist_64) * index;
|
||||
log.debug("writing dSym local symbol {} at 0x{x}", .{ index, off });
|
||||
try self.file.pwriteAll(mem.asBytes(&self.base.local_symbols.items[index]), off);
|
||||
}
|
||||
|
||||
pub fn writeStringTable(self: *DebugSymbols) !void {
|
||||
if (!self.string_table_dirty) return;
|
||||
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const symtab = &self.load_commands.items[self.symtab_cmd_index.?].Symtab;
|
||||
const allocated_size = self.allocatedSizeLinkedit(symtab.stroff);
|
||||
const needed_size = mem.alignForwardGeneric(u64, self.base.string_table.items.len, @alignOf(u64));
|
||||
|
||||
if (needed_size > allocated_size) {
|
||||
symtab.strsize = 0;
|
||||
symtab.stroff = @intCast(u32, self.findFreeSpaceLinkedit(needed_size, 1));
|
||||
}
|
||||
symtab.strsize = @intCast(u32, needed_size);
|
||||
log.debug("writing dSym string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
|
||||
|
||||
try self.file.pwriteAll(self.base.string_table.items, symtab.stroff);
|
||||
self.load_commands_dirty = true;
|
||||
self.string_table_dirty = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user