aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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(