| author | |
| committer | |
| log | edb428fae42ea82c49347fce6d48d80f1fed6ef1 |
| tree | 2a09c14b9138d511148160265a2a1dbaff47252e |
| parent | 3f912430bdddede8c3f6a9555b76499aa2dabb7e |
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 address | 3964 | 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) = .{}, | ||
| 82 | 83 | ||
| 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 | } |
| 101 | 112 | ||
| 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 | }; |
| 551 | 562 | ||
| 563 | pub 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 | |||
| 552 | pub const SrcFn = struct { | 575 | pub 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 | } |
| 1011 | 1034 | ||
| 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 | } |
| 1014 | 1057 |
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 | } |
| 3476 | 3479 | ||
| 3477 | fn freeAtom(self: *MachO, atom: *Atom, match: MatchingSection, owns_atom: bool) void { | 3480 | 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 { | ... | @@ -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, |
| 59 | debug_info_header_dirty: bool = false, | 59 | debug_info_header_dirty: bool = false, |
| 60 | debug_line_header_dirty: bool = false, | 60 | debug_line_header_dirty: bool = false, |
| 61 | 61 | ||
| 62 | relocs: std.ArrayListUnmanaged(Reloc) = .{}, | ||
| 63 | |||
| 64 | pub 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. |
| 64 | pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void { | 77 | pub 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; |
| 256 | 269 | ||
| 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 | |||
| 373 | pub 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 | } |
| 335 | 385 | ||
| 336 | fn copySegmentCommand( | 386 | fn copySegmentCommand( |