aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2022-04-13 19:05:19 +0200
committerJakub Konka <kubkon@jakubkonka.com>2022-04-13 19:50:23 +0200
commitedb428fae42ea82c49347fce6d48d80f1fed6ef1 (patch)
tree2a09c14b9138d511148160265a2a1dbaff47252e /src
parent3f912430bdddede8c3f6a9555b76499aa2dabb7e (diff)
downloadzig-edb428fae42ea82c49347fce6d48d80f1fed6ef1.tar.gz
zig-edb428fae42ea82c49347fce6d48d80f1fed6ef1.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/arch/x86_64/CodeGen.zig11
-rw-r--r--src/link/Dwarf.zig43
-rw-r--r--src/link/MachO.zig8
-rw-r--r--src/link/MachO/DebugSymbols.zig52
4 files changed, 112 insertions, 2 deletions
diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig
index 32065fa1bb..53a6bfc4d9 100644
--- a/src/arch/x86_64/CodeGen.zig
+++ b/src/arch/x86_64/CodeGen.zig
@@ -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});
diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig
index bd7f429177..248521c544 100644
--- a/src/link/Dwarf.zig
+++ b/src/link/Dwarf.zig
@@ -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);
}
diff --git a/src/link/MachO.zig b/src/link/MachO.zig
index b193068361..d359a3fd5d 100644
--- a/src/link/MachO.zig
+++ b/src/link/MachO.zig
@@ -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,
diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig
index 885f0ca6a8..aa7a29fcd1 100644
--- a/src/link/MachO/DebugSymbols.zig
+++ b/src/link/MachO/DebugSymbols.zig
@@ -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(