mirror of
https://github.com/ziglang/zig.git
synced 2025-12-25 23:53:15 +00:00
macho,x64: resolve debug info relocs for RIP-based addressing
Sometimes we will want to generate debug info for a constant that has been lowered to memory and not copied anywhere else. For this we will need to defer resolution on PIE platforms until all locals (including GOT entries) have been allocated.
This commit is contained in:
parent
3f912430bd
commit
edb428fae4
@ -3950,7 +3950,7 @@ fn genVarDbgInfo(
|
||||
leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;
|
||||
dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
|
||||
},
|
||||
.memory => |addr| {
|
||||
.memory, .got_load, .direct_load => {
|
||||
const endian = self.target.cpu.arch.endian();
|
||||
const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));
|
||||
const is_ptr = switch (tag) {
|
||||
@ -3963,6 +3963,11 @@ fn genVarDbgInfo(
|
||||
1 + ptr_width + @boolToInt(is_ptr),
|
||||
DW.OP.addr, // literal address
|
||||
});
|
||||
const offset = @intCast(u32, dbg_info.items.len);
|
||||
const addr = switch (mcv) {
|
||||
.memory => |addr| addr,
|
||||
else => 0,
|
||||
};
|
||||
switch (ptr_width) {
|
||||
0...4 => {
|
||||
try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian);
|
||||
@ -3976,6 +3981,10 @@ fn genVarDbgInfo(
|
||||
// We need deref the address as we point to the value via GOT entry.
|
||||
try dbg_info.append(DW.OP.deref);
|
||||
}
|
||||
switch (mcv) {
|
||||
.got_load, .direct_load => |index| try dw.addExprlocReloc(index, offset, is_ptr),
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
else => {
|
||||
log.debug("TODO generate debug info for {}", .{mcv});
|
||||
|
||||
@ -79,6 +79,7 @@ pub const DeclState = struct {
|
||||
std.hash_map.default_max_load_percentage,
|
||||
) = .{},
|
||||
abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
|
||||
exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{},
|
||||
|
||||
fn init(gpa: Allocator, target: std.Target) DeclState {
|
||||
return .{
|
||||
@ -97,6 +98,16 @@ pub const DeclState = struct {
|
||||
self.abbrev_table.deinit(self.gpa);
|
||||
self.abbrev_resolver.deinit(self.gpa);
|
||||
self.abbrev_relocs.deinit(self.gpa);
|
||||
self.exprloc_relocs.deinit(self.gpa);
|
||||
}
|
||||
|
||||
pub fn addExprlocReloc(self: *DeclState, target: u32, offset: u32, is_ptr: bool) !void {
|
||||
log.debug("{x}: target sym @{d}, via GOT {}", .{ offset, target, is_ptr });
|
||||
try self.exprloc_relocs.append(self.gpa, .{
|
||||
.@"type" = if (is_ptr) .got_load else .direct_load,
|
||||
.target = target,
|
||||
.offset = offset,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn addTypeReloc(
|
||||
@ -549,6 +560,18 @@ pub const AbbrevRelocation = struct {
|
||||
addend: u32,
|
||||
};
|
||||
|
||||
pub const ExprlocRelocation = struct {
|
||||
/// Type of the relocation: direct load ref, or GOT load ref (via GOT table)
|
||||
@"type": enum {
|
||||
direct_load,
|
||||
got_load,
|
||||
},
|
||||
/// Index of the target in the linker's locals symbol table.
|
||||
target: u32,
|
||||
/// Offset within the debug info buffer where to patch up the address value.
|
||||
offset: u32,
|
||||
};
|
||||
|
||||
pub const SrcFn = struct {
|
||||
/// Offset from the beginning of the Debug Line Program header that contains this function.
|
||||
off: u32,
|
||||
@ -1009,6 +1032,26 @@ pub fn commitDeclState(
|
||||
}
|
||||
}
|
||||
|
||||
while (decl_state.exprloc_relocs.popOrNull()) |reloc| {
|
||||
switch (self.tag) {
|
||||
.macho => {
|
||||
const macho_file = file.cast(File.MachO).?;
|
||||
const d_sym = &macho_file.d_sym.?;
|
||||
try d_sym.relocs.append(d_sym.base.base.allocator, .{
|
||||
.@"type" = switch (reloc.@"type") {
|
||||
.direct_load => .direct_load,
|
||||
.got_load => .got_load,
|
||||
},
|
||||
.target = reloc.target,
|
||||
.offset = reloc.offset + atom.off,
|
||||
.addend = 0,
|
||||
.prev_vaddr = 0,
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items);
|
||||
}
|
||||
|
||||
|
||||
@ -3472,6 +3472,9 @@ pub fn closeFiles(self: MachO) void {
|
||||
for (self.dylibs.items) |dylib| {
|
||||
dylib.file.close();
|
||||
}
|
||||
if (self.d_sym) |ds| {
|
||||
ds.file.close();
|
||||
}
|
||||
}
|
||||
|
||||
fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void {
|
||||
@ -4274,6 +4277,11 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
|
||||
self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};
|
||||
self.got_entries.items[got_index] = .{ .target = .{ .local = 0 }, .atom = undefined };
|
||||
_ = self.got_entries_table.swapRemove(.{ .local = decl.link.macho.local_sym_index });
|
||||
|
||||
if (self.d_sym) |*d_sym| {
|
||||
d_sym.swapRemoveRelocs(decl.link.macho.local_sym_index);
|
||||
}
|
||||
|
||||
log.debug(" adding GOT index {d} to free list (target local@{d})", .{
|
||||
got_index,
|
||||
decl.link.macho.local_sym_index,
|
||||
|
||||
@ -59,6 +59,19 @@ debug_aranges_section_dirty: bool = false,
|
||||
debug_info_header_dirty: bool = false,
|
||||
debug_line_header_dirty: bool = false,
|
||||
|
||||
relocs: std.ArrayListUnmanaged(Reloc) = .{},
|
||||
|
||||
pub const Reloc = struct {
|
||||
@"type": enum {
|
||||
direct_load,
|
||||
got_load,
|
||||
},
|
||||
target: u32,
|
||||
offset: u64,
|
||||
addend: u32,
|
||||
prev_vaddr: u64,
|
||||
};
|
||||
|
||||
/// You must call this function *after* `MachO.populateMissingMetadata()`
|
||||
/// has been called to get a viable debug symbols output.
|
||||
pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {
|
||||
@ -254,6 +267,30 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
|
||||
// Zig source code.
|
||||
const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
|
||||
|
||||
for (self.relocs.items) |*reloc| {
|
||||
const sym = switch (reloc.@"type") {
|
||||
.direct_load => self.base.locals.items[reloc.target],
|
||||
.got_load => blk: {
|
||||
const got_index = self.base.got_entries_table.get(.{ .local = reloc.target }).?;
|
||||
const got_entry = self.base.got_entries.items[got_index];
|
||||
break :blk self.base.locals.items[got_entry.atom.local_sym_index];
|
||||
},
|
||||
};
|
||||
if (sym.n_value == reloc.prev_vaddr) continue;
|
||||
|
||||
const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
|
||||
const sect = &seg.sections.items[self.debug_info_section_index.?];
|
||||
const file_offset = sect.offset + reloc.offset;
|
||||
log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
|
||||
reloc.target,
|
||||
sym.n_value,
|
||||
self.base.getString(sym.n_strx),
|
||||
file_offset,
|
||||
});
|
||||
try self.file.pwriteAll(mem.asBytes(&sym.n_value), file_offset);
|
||||
reloc.prev_vaddr = sym.n_value;
|
||||
}
|
||||
|
||||
if (self.debug_abbrev_section_dirty) {
|
||||
try self.dwarf.writeDbgAbbrev(&self.base.base);
|
||||
self.load_commands_dirty = true;
|
||||
@ -330,7 +367,20 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
|
||||
}
|
||||
self.load_commands.deinit(allocator);
|
||||
self.dwarf.deinit();
|
||||
self.file.close();
|
||||
self.relocs.deinit(allocator);
|
||||
}
|
||||
|
||||
pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
|
||||
// TODO re-implement using a hashmap with free lists
|
||||
var last_index: usize = 0;
|
||||
while (last_index < self.relocs.items.len) {
|
||||
const reloc = self.relocs.items[last_index];
|
||||
if (reloc.target == target) {
|
||||
_ = self.relocs.swapRemove(last_index);
|
||||
} else {
|
||||
last_index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn copySegmentCommand(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user