authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-04-13 19:05:19+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-04-13 19:50:23+02:00
logedb428fae42ea82c49347fce6d48d80f1fed6ef1
tree2a09c14b9138d511148160265a2a1dbaff47252e
parent3f912430bdddede8c3f6a9555b76499aa2dabb7e

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.

4 files changed, 112 insertions(+), 2 deletions(-)

src/arch/x86_64/CodeGen.zig+10-1
......@@ -3950,7 +3950,7 @@ fn genVarDbgInfo(
39503950 leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;
39513951 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
39523952 },
3953 .memory => |addr| {
3953 .memory, .got_load, .direct_load => {
39543954 const endian = self.target.cpu.arch.endian();
39553955 const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));
39563956 const is_ptr = switch (tag) {
......@@ -3963,6 +3963,11 @@ fn genVarDbgInfo(
39633963 1 + ptr_width + @boolToInt(is_ptr),
39643964 DW.OP.addr, // literal address
39653965 });
3966 const offset = @intCast(u32, dbg_info.items.len);
3967 const addr = switch (mcv) {
3968 .memory => |addr| addr,
3969 else => 0,
3970 };
39663971 switch (ptr_width) {
39673972 0...4 => {
39683973 try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian);
......@@ -3976,6 +3981,10 @@ fn genVarDbgInfo(
39763981 // We need deref the address as we point to the value via GOT entry.
39773982 try dbg_info.append(DW.OP.deref);
39783983 }
3984 switch (mcv) {
3985 .got_load, .direct_load => |index| try dw.addExprlocReloc(index, offset, is_ptr),
3986 else => {},
3987 }
39793988 },
39803989 else => {
39813990 log.debug("TODO generate debug info for {}", .{mcv});
src/link/Dwarf.zig+43
......@@ -79,6 +79,7 @@ pub const DeclState = struct {
7979 std.hash_map.default_max_load_percentage,
8080 ) = .{},
8181 abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
82 exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{},
8283
8384 fn init(gpa: Allocator, target: std.Target) DeclState {
8485 return .{
......@@ -97,6 +98,16 @@ pub const DeclState = struct {
9798 self.abbrev_table.deinit(self.gpa);
9899 self.abbrev_resolver.deinit(self.gpa);
99100 self.abbrev_relocs.deinit(self.gpa);
101 self.exprloc_relocs.deinit(self.gpa);
102 }
103
104 pub fn addExprlocReloc(self: *DeclState, target: u32, offset: u32, is_ptr: bool) !void {
105 log.debug("{x}: target sym @{d}, via GOT {}", .{ offset, target, is_ptr });
106 try self.exprloc_relocs.append(self.gpa, .{
107 .@"type" = if (is_ptr) .got_load else .direct_load,
108 .target = target,
109 .offset = offset,
110 });
100111 }
101112
102113 pub fn addTypeReloc(
......@@ -549,6 +560,18 @@ pub const AbbrevRelocation = struct {
549560 addend: u32,
550561};
551562
563pub const ExprlocRelocation = struct {
564 /// Type of the relocation: direct load ref, or GOT load ref (via GOT table)
565 @"type": enum {
566 direct_load,
567 got_load,
568 },
569 /// Index of the target in the linker's locals symbol table.
570 target: u32,
571 /// Offset within the debug info buffer where to patch up the address value.
572 offset: u32,
573};
574
552575pub const SrcFn = struct {
553576 /// Offset from the beginning of the Debug Line Program header that contains this function.
554577 off: u32,
......@@ -1009,6 +1032,26 @@ pub fn commitDeclState(
10091032 }
10101033 }
10111034
1035 while (decl_state.exprloc_relocs.popOrNull()) |reloc| {
1036 switch (self.tag) {
1037 .macho => {
1038 const macho_file = file.cast(File.MachO).?;
1039 const d_sym = &macho_file.d_sym.?;
1040 try d_sym.relocs.append(d_sym.base.base.allocator, .{
1041 .@"type" = switch (reloc.@"type") {
1042 .direct_load => .direct_load,
1043 .got_load => .got_load,
1044 },
1045 .target = reloc.target,
1046 .offset = reloc.offset + atom.off,
1047 .addend = 0,
1048 .prev_vaddr = 0,
1049 });
1050 },
1051 else => unreachable,
1052 }
1053 }
1054
10121055 try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items);
10131056}
10141057
src/link/MachO.zig+8
......@@ -3472,6 +3472,9 @@ pub fn closeFiles(self: MachO) void {
34723472 for (self.dylibs.items) |dylib| {
34733473 dylib.file.close();
34743474 }
3475 if (self.d_sym) |ds| {
3476 ds.file.close();
3477 }
34753478}
34763479
34773480fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void {
......@@ -4274,6 +4277,11 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
42744277 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};
42754278 self.got_entries.items[got_index] = .{ .target = .{ .local = 0 }, .atom = undefined };
42764279 _ = self.got_entries_table.swapRemove(.{ .local = decl.link.macho.local_sym_index });
4280
4281 if (self.d_sym) |*d_sym| {
4282 d_sym.swapRemoveRelocs(decl.link.macho.local_sym_index);
4283 }
4284
42774285 log.debug(" adding GOT index {d} to free list (target local@{d})", .{
42784286 got_index,
42794287 decl.link.macho.local_sym_index,
src/link/MachO/DebugSymbols.zig+51-1
......@@ -59,6 +59,19 @@ debug_aranges_section_dirty: bool = false,
5959debug_info_header_dirty: bool = false,
6060debug_line_header_dirty: bool = false,
6161
62relocs: std.ArrayListUnmanaged(Reloc) = .{},
63
64pub const Reloc = struct {
65 @"type": enum {
66 direct_load,
67 got_load,
68 },
69 target: u32,
70 offset: u64,
71 addend: u32,
72 prev_vaddr: u64,
73};
74
6275/// You must call this function *after* `MachO.populateMissingMetadata()`
6376/// has been called to get a viable debug symbols output.
6477pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {
......@@ -254,6 +267,30 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
254267 // Zig source code.
255268 const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
256269
270 for (self.relocs.items) |*reloc| {
271 const sym = switch (reloc.@"type") {
272 .direct_load => self.base.locals.items[reloc.target],
273 .got_load => blk: {
274 const got_index = self.base.got_entries_table.get(.{ .local = reloc.target }).?;
275 const got_entry = self.base.got_entries.items[got_index];
276 break :blk self.base.locals.items[got_entry.atom.local_sym_index];
277 },
278 };
279 if (sym.n_value == reloc.prev_vaddr) continue;
280
281 const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
282 const sect = &seg.sections.items[self.debug_info_section_index.?];
283 const file_offset = sect.offset + reloc.offset;
284 log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
285 reloc.target,
286 sym.n_value,
287 self.base.getString(sym.n_strx),
288 file_offset,
289 });
290 try self.file.pwriteAll(mem.asBytes(&sym.n_value), file_offset);
291 reloc.prev_vaddr = sym.n_value;
292 }
293
257294 if (self.debug_abbrev_section_dirty) {
258295 try self.dwarf.writeDbgAbbrev(&self.base.base);
259296 self.load_commands_dirty = true;
......@@ -330,7 +367,20 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
330367 }
331368 self.load_commands.deinit(allocator);
332369 self.dwarf.deinit();
333 self.file.close();
370 self.relocs.deinit(allocator);
371}
372
373pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
374 // TODO re-implement using a hashmap with free lists
375 var last_index: usize = 0;
376 while (last_index < self.relocs.items.len) {
377 const reloc = self.relocs.items[last_index];
378 if (reloc.target == target) {
379 _ = self.relocs.swapRemove(last_index);
380 } else {
381 last_index += 1;
382 }
383 }
334384}
335385
336386fn copySegmentCommand(