mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
131 lines
4.9 KiB
Zig
131 lines
4.9 KiB
Zig
//! Cross-platform abstraction for loading debug information into an in-memory
|
|
//! format that supports queries such as "what is the source location of this
|
|
//! virtual memory address?"
|
|
//!
|
|
//! Unlike `std.debug.SelfInfo`, this API does not assume the debug information
|
|
//! in question happens to match the host CPU architecture, OS, or other target
|
|
//! properties.
|
|
|
|
const std = @import("../std.zig");
|
|
const builtin = @import("builtin");
|
|
const Allocator = std.mem.Allocator;
|
|
const Path = std.Build.Cache.Path;
|
|
const Dwarf = std.debug.Dwarf;
|
|
const assert = std.debug.assert;
|
|
const Coverage = std.debug.Coverage;
|
|
const SourceLocation = std.debug.Coverage.SourceLocation;
|
|
|
|
const Info = @This();
|
|
|
|
/// Sorted by key, ascending.
|
|
address_map: std.AutoArrayHashMapUnmanaged(u64, std.debug.SelfInfo.Module),
|
|
/// Externally managed, outlives this `Info` instance.
|
|
coverage: *Coverage,
|
|
|
|
pub const LoadError = Dwarf.ElfModule.LoadError;
|
|
|
|
pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info {
|
|
var sections: Dwarf.SectionArray = Dwarf.null_section_array;
|
|
var info: Info = .{
|
|
.address_map = .{},
|
|
.coverage = coverage,
|
|
};
|
|
switch (builtin.os.tag) {
|
|
.linux => {
|
|
var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, §ions, null);
|
|
try elf_module.dwarf.populateRanges(gpa);
|
|
try info.address_map.put(gpa, elf_module.base_address, elf_module);
|
|
},
|
|
.macos => {
|
|
const macho_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| switch (err) {
|
|
error.FileNotFound => return error.MissingDebugInfo,
|
|
else => return error.InvalidDebugInfo,
|
|
};
|
|
// readMachoDebugInfo takes ownership of the file
|
|
// defer elf_file.close();
|
|
var module = std.debug.SelfInfo.readMachODebugInfo(gpa, macho_file) catch {
|
|
return error.InvalidDebugInfo;
|
|
};
|
|
|
|
module.base_address = 0;
|
|
module.vmaddr_slide = 0;
|
|
|
|
try info.address_map.put(gpa, 0, module);
|
|
},
|
|
else => @compileError("TODO: implement debug info loading for the target platform"),
|
|
}
|
|
return info;
|
|
}
|
|
|
|
pub fn deinit(info: *Info, gpa: Allocator) void {
|
|
for (info.address_map.values()) |*module| {
|
|
module.deinit(gpa);
|
|
}
|
|
info.address_map.deinit(gpa);
|
|
info.* = undefined;
|
|
}
|
|
|
|
pub const ResolveAddressesError = Coverage.ResolveAddressesDwarfError;
|
|
|
|
/// Given an array of virtual memory addresses, sorted ascending, outputs a
|
|
/// corresponding array of source locations.
|
|
pub fn resolveAddresses(
|
|
info: *Info,
|
|
gpa: Allocator,
|
|
/// Asserts the addresses are in ascending order.
|
|
sorted_pc_addrs: []const u64,
|
|
/// Asserts its length equals length of `sorted_pc_addrs`.
|
|
output: []SourceLocation,
|
|
) ResolveAddressesError!void {
|
|
assert(sorted_pc_addrs.len == output.len);
|
|
if (info.address_map.entries.len != 1) @panic("TODO");
|
|
|
|
switch (builtin.os.tag) {
|
|
else => @compileError("unsupported"),
|
|
.linux => {
|
|
const elf_module = &info.address_map.values()[0];
|
|
return info.coverage.resolveAddressesDwarf(gpa, sorted_pc_addrs, output, &elf_module.dwarf);
|
|
},
|
|
.macos => {
|
|
const module = &info.address_map.values()[0];
|
|
|
|
var idx: usize = 0;
|
|
while (idx < sorted_pc_addrs.len) : (idx += 1) {
|
|
const ofile = (module.getOFileInfoForAddress(gpa, sorted_pc_addrs[idx]) catch return error.InvalidDebugInfo);
|
|
if (ofile.o_file_info.?.di.ranges.items.len == 0) {
|
|
try ofile.o_file_info.?.di.populateRanges(gpa);
|
|
}
|
|
// const last = ofile.ranges.getLastOrNull() orelse return;
|
|
// var end_idx = idx;
|
|
// while (end_idx < sorted_pc_addrs.len and
|
|
// sorted_pc_addrs[end_idx] < last.end) end_idx += 1;
|
|
|
|
// if (end_idx == idx) {
|
|
// std.debug.panic("made no progress", .{});
|
|
// }
|
|
//
|
|
|
|
const stab_symbol = std.mem.sliceTo(module.strings[ofile.symbol.?.strx..], 0);
|
|
const offset = ofile.relocated_address - ofile.symbol.?.addr;
|
|
// Translate again the address, this time into an address inside the
|
|
// .o file
|
|
const relocated_address_o = ofile.o_file_info.?.addr_table.get(stab_symbol) orelse @panic("error");
|
|
|
|
try info.coverage.resolveAddressesDwarf(
|
|
gpa,
|
|
&.{relocated_address_o + offset},
|
|
output[idx..][0..1],
|
|
&ofile.o_file_info.?.di,
|
|
);
|
|
|
|
// std.debug.print("{x} -> {x} -> {}\n", .{
|
|
// sorted_pc_addrs[idx],
|
|
// relocated_address_o + offset,
|
|
// output[idx],
|
|
// });
|
|
|
|
// idx = end_idx;
|
|
}
|
|
},
|
|
}
|
|
}
|