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(...@@ -3950,7 +3950,7 @@ fn genVarDbgInfo(
3950 leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;3950 leb128.writeILEB128(dbg_info.writer(), -off) catch unreachable;
3951 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);3951 dbg_info.items[fixup] += @intCast(u8, dbg_info.items.len - fixup - 2);
3952 },3952 },
3953 .memory => |addr| {3953 .memory, .got_load, .direct_load => {
3954 const endian = self.target.cpu.arch.endian();3954 const endian = self.target.cpu.arch.endian();
3955 const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));3955 const ptr_width = @intCast(u8, @divExact(self.target.cpu.arch.ptrBitWidth(), 8));
3956 const is_ptr = switch (tag) {3956 const is_ptr = switch (tag) {
...@@ -3963,6 +3963,11 @@ fn genVarDbgInfo(...@@ -3963,6 +3963,11 @@ fn genVarDbgInfo(
3963 1 + ptr_width + @boolToInt(is_ptr),3963 1 + ptr_width + @boolToInt(is_ptr),
3964 DW.OP.addr, // literal address3964 DW.OP.addr, // literal address
3965 });3965 });
3966 const offset = @intCast(u32, dbg_info.items.len);
3967 const addr = switch (mcv) {
3968 .memory => |addr| addr,
3969 else => 0,
3970 };
3966 switch (ptr_width) {3971 switch (ptr_width) {
3967 0...4 => {3972 0...4 => {
3968 try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian);3973 try dbg_info.writer().writeInt(u32, @intCast(u32, addr), endian);
...@@ -3976,6 +3981,10 @@ fn genVarDbgInfo(...@@ -3976,6 +3981,10 @@ fn genVarDbgInfo(
3976 // We need deref the address as we point to the value via GOT entry.3981 // We need deref the address as we point to the value via GOT entry.
3977 try dbg_info.append(DW.OP.deref);3982 try dbg_info.append(DW.OP.deref);
3978 }3983 }
3984 switch (mcv) {
3985 .got_load, .direct_load => |index| try dw.addExprlocReloc(index, offset, is_ptr),
3986 else => {},
3987 }
3979 },3988 },
3980 else => {3989 else => {
3981 log.debug("TODO generate debug info for {}", .{mcv});3990 log.debug("TODO generate debug info for {}", .{mcv});
src/link/Dwarf.zig+43
...@@ -79,6 +79,7 @@ pub const DeclState = struct {...@@ -79,6 +79,7 @@ pub const DeclState = struct {
79 std.hash_map.default_max_load_percentage,79 std.hash_map.default_max_load_percentage,
80 ) = .{},80 ) = .{},
81 abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},81 abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
82 exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{},
8283
83 fn init(gpa: Allocator, target: std.Target) DeclState {84 fn init(gpa: Allocator, target: std.Target) DeclState {
84 return .{85 return .{
...@@ -97,6 +98,16 @@ pub const DeclState = struct {...@@ -97,6 +98,16 @@ pub const DeclState = struct {
97 self.abbrev_table.deinit(self.gpa);98 self.abbrev_table.deinit(self.gpa);
98 self.abbrev_resolver.deinit(self.gpa);99 self.abbrev_resolver.deinit(self.gpa);
99 self.abbrev_relocs.deinit(self.gpa);100 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 });
100 }111 }
101112
102 pub fn addTypeReloc(113 pub fn addTypeReloc(
...@@ -549,6 +560,18 @@ pub const AbbrevRelocation = struct {...@@ -549,6 +560,18 @@ pub const AbbrevRelocation = struct {
549 addend: u32,560 addend: u32,
550};561};
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
552pub const SrcFn = struct {575pub const SrcFn = struct {
553 /// Offset from the beginning of the Debug Line Program header that contains this function.576 /// Offset from the beginning of the Debug Line Program header that contains this function.
554 off: u32,577 off: u32,
...@@ -1009,6 +1032,26 @@ pub fn commitDeclState(...@@ -1009,6 +1032,26 @@ pub fn commitDeclState(
1009 }1032 }
1010 }1033 }
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
1012 try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items);1055 try self.writeDeclDebugInfo(file, atom, dbg_info_buffer.items);
1013}1056}
10141057
src/link/MachO.zig+8
...@@ -3472,6 +3472,9 @@ pub fn closeFiles(self: MachO) void {...@@ -3472,6 +3472,9 @@ pub fn closeFiles(self: MachO) void {
3472 for (self.dylibs.items) |dylib| {3472 for (self.dylibs.items) |dylib| {
3473 dylib.file.close();3473 dylib.file.close();
3474 }3474 }
3475 if (self.d_sym) |ds| {
3476 ds.file.close();
3477 }
3475}3478}
34763479
3477fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void {3480fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void {
...@@ -4274,6 +4277,11 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {...@@ -4274,6 +4277,11 @@ pub fn freeDecl(self: *MachO, decl: *Module.Decl) void {
4274 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};4277 self.got_entries_free_list.append(self.base.allocator, @intCast(u32, got_index)) catch {};
4275 self.got_entries.items[got_index] = .{ .target = .{ .local = 0 }, .atom = undefined };4278 self.got_entries.items[got_index] = .{ .target = .{ .local = 0 }, .atom = undefined };
4276 _ = self.got_entries_table.swapRemove(.{ .local = decl.link.macho.local_sym_index });4279 _ = 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
4277 log.debug(" adding GOT index {d} to free list (target local@{d})", .{4285 log.debug(" adding GOT index {d} to free list (target local@{d})", .{
4278 got_index,4286 got_index,
4279 decl.link.macho.local_sym_index,4287 decl.link.macho.local_sym_index,
src/link/MachO/DebugSymbols.zig+51-1
...@@ -59,6 +59,19 @@ debug_aranges_section_dirty: bool = false,...@@ -59,6 +59,19 @@ debug_aranges_section_dirty: bool = false,
59debug_info_header_dirty: bool = false,59debug_info_header_dirty: bool = false,
60debug_line_header_dirty: bool = false,60debug_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
62/// You must call this function *after* `MachO.populateMissingMetadata()`75/// You must call this function *after* `MachO.populateMissingMetadata()`
63/// has been called to get a viable debug symbols output.76/// has been called to get a viable debug symbols output.
64pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {77pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {
...@@ -254,6 +267,30 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti...@@ -254,6 +267,30 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
254 // Zig source code.267 // Zig source code.
255 const module = options.module orelse return error.LinkingWithoutZigSourceUnimplemented;268 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
257 if (self.debug_abbrev_section_dirty) {294 if (self.debug_abbrev_section_dirty) {
258 try self.dwarf.writeDbgAbbrev(&self.base.base);295 try self.dwarf.writeDbgAbbrev(&self.base.base);
259 self.load_commands_dirty = true;296 self.load_commands_dirty = true;
...@@ -330,7 +367,20 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {...@@ -330,7 +367,20 @@ pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
330 }367 }
331 self.load_commands.deinit(allocator);368 self.load_commands.deinit(allocator);
332 self.dwarf.deinit();369 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 }
334}384}
335385
336fn copySegmentCommand(386fn copySegmentCommand(