mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
the world if ElfModule didn't suck:
This commit is contained in:
parent
55a7affea4
commit
84b65860cf
5 changed files with 126 additions and 137 deletions
|
|
@ -153,10 +153,9 @@ pub const SourceLocation = struct {
|
|||
};
|
||||
|
||||
pub const Symbol = struct {
|
||||
// MLUGG TODO: remove the defaults and audit everywhere. also grep for '???' across std
|
||||
name: []const u8 = "???",
|
||||
compile_unit_name: []const u8 = "???",
|
||||
source_location: ?SourceLocation = null,
|
||||
name: ?[]const u8,
|
||||
compile_unit_name: ?[]const u8,
|
||||
source_location: ?SourceLocation,
|
||||
};
|
||||
|
||||
/// Deprecated because it returns the optimization mode of the standard
|
||||
|
|
@ -1040,10 +1039,11 @@ fn printLastUnwindError(it: *StackIterator, debug_info: *SelfInfo, writer: *Writ
|
|||
|
||||
fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwind_err: UnwindError, tty_config: tty.Config) !void {
|
||||
const module_name = debug_info.getModuleNameForAddress(getDebugInfoAllocator(), address) catch |err| switch (err) {
|
||||
error.Unexpected, error.OutOfMemory => |e| return e,
|
||||
error.MissingDebugInfo => "???",
|
||||
error.Unexpected, error.OutOfMemory => |e| return e,
|
||||
};
|
||||
try tty_config.setColor(writer, .dim);
|
||||
// MLUGG TODO this makes no sense given that MissingUnwindInfo exists?
|
||||
if (unwind_err == error.MissingDebugInfo) {
|
||||
try writer.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address });
|
||||
} else {
|
||||
|
|
@ -1054,35 +1054,27 @@ fn printUnwindError(debug_info: *SelfInfo, writer: *Writer, address: usize, unwi
|
|||
|
||||
pub fn printSourceAtAddress(debug_info: *SelfInfo, writer: *Writer, address: usize, tty_config: tty.Config) !void {
|
||||
const gpa = getDebugInfoAllocator();
|
||||
if (debug_info.getSymbolAtAddress(gpa, address)) |symbol_info| {
|
||||
defer if (symbol_info.source_location) |sl| gpa.free(sl.file_name);
|
||||
return printLineInfo(
|
||||
writer,
|
||||
symbol_info.source_location,
|
||||
address,
|
||||
symbol_info.name,
|
||||
symbol_info.compile_unit_name,
|
||||
tty_config,
|
||||
);
|
||||
} else |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => {},
|
||||
const symbol: Symbol = debug_info.getSymbolAtAddress(gpa, address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo, error.InvalidDebugInfo => .{
|
||||
.name = null,
|
||||
.compile_unit_name = null,
|
||||
.source_location = null,
|
||||
},
|
||||
else => |e| return e,
|
||||
}
|
||||
// Unknown source location, but perhaps we can at least get a module name
|
||||
const compile_unit_name = debug_info.getModuleNameForAddress(getDebugInfoAllocator(), address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo => "???",
|
||||
error.Unexpected, error.OutOfMemory => |e| return e,
|
||||
};
|
||||
defer if (symbol.source_location) |sl| gpa.free(sl.file_name);
|
||||
return printLineInfo(
|
||||
writer,
|
||||
null,
|
||||
symbol.source_location,
|
||||
address,
|
||||
"???",
|
||||
compile_unit_name,
|
||||
symbol.name orelse "???",
|
||||
symbol.compile_unit_name orelse debug_info.getModuleNameForAddress(gpa, address) catch |err| switch (err) {
|
||||
error.MissingDebugInfo => "???",
|
||||
error.Unexpected, error.OutOfMemory => |e| return e,
|
||||
},
|
||||
tty_config,
|
||||
);
|
||||
}
|
||||
|
||||
fn printLineInfo(
|
||||
writer: *Writer,
|
||||
source_location: ?SourceLocation,
|
||||
|
|
|
|||
|
|
@ -1487,20 +1487,42 @@ pub const ElfModule = struct {
|
|||
MemoryMappingNotSupported,
|
||||
} || Allocator.Error || std.fs.File.OpenError || OpenError;
|
||||
|
||||
/// Reads debug info from an already mapped ELF file.
|
||||
/// Reads debug info from an ELF file given its path.
|
||||
///
|
||||
/// If the required sections aren't present but a reference to external debug
|
||||
/// info is, then this this function will recurse to attempt to load the debug
|
||||
/// sections from an external file.
|
||||
pub fn load(
|
||||
gpa: Allocator,
|
||||
mapped_mem: []align(std.heap.page_size_min) const u8,
|
||||
elf_file_path: Path,
|
||||
build_id: ?[]const u8,
|
||||
expected_crc: ?u32,
|
||||
parent_sections: ?*Dwarf.SectionArray,
|
||||
parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
|
||||
elf_filename: ?[]const u8,
|
||||
) LoadError!ElfModule {
|
||||
const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: {
|
||||
const elf_file = try elf_file_path.root_dir.handle.openFile(elf_file_path.sub_path, .{});
|
||||
defer elf_file.close();
|
||||
|
||||
const file_len = cast(
|
||||
usize,
|
||||
elf_file.getEndPos() catch return bad(),
|
||||
) orelse return error.Overflow;
|
||||
|
||||
break :mapped std.posix.mmap(
|
||||
null,
|
||||
file_len,
|
||||
std.posix.PROT.READ,
|
||||
.{ .TYPE = .SHARED },
|
||||
elf_file.handle,
|
||||
0,
|
||||
) catch |err| switch (err) {
|
||||
error.MappingAlreadyExists => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
};
|
||||
errdefer std.posix.munmap(mapped_mem);
|
||||
|
||||
if (expected_crc) |crc| if (crc != std.hash.crc.Crc32.hash(mapped_mem)) return error.InvalidDebugInfo;
|
||||
|
||||
const hdr: *const elf.Ehdr = @ptrCast(&mapped_mem[0]);
|
||||
|
|
@ -1606,39 +1628,36 @@ pub const ElfModule = struct {
|
|||
// $XDG_CACHE_HOME/debuginfod_client/<buildid>/debuginfo
|
||||
// This only opportunisticly tries to load from the debuginfod cache, but doesn't try to populate it.
|
||||
// One can manually run `debuginfod-find debuginfo PATH` to download the symbols
|
||||
if (build_id) |id| blk: {
|
||||
var debuginfod_dir: std.fs.Dir = switch (builtin.os.tag) {
|
||||
.wasi, .windows => break :blk,
|
||||
else => dir: {
|
||||
if (std.posix.getenv("DEBUGINFOD_CACHE_PATH")) |path| {
|
||||
break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk;
|
||||
debuginfod: {
|
||||
const id = build_id orelse break :debuginfod;
|
||||
switch (builtin.os.tag) {
|
||||
.wasi, .windows => break :debuginfod,
|
||||
else => {},
|
||||
}
|
||||
const id_dir_path: []u8 = p: {
|
||||
if (std.posix.getenv("DEBUGINFOD_CACHE_PATH")) |path| {
|
||||
break :p try std.fmt.allocPrint(gpa, "{s}/{x}", .{ path, id });
|
||||
}
|
||||
if (std.posix.getenv("XDG_CACHE_HOME")) |cache_path| {
|
||||
if (cache_path.len > 0) {
|
||||
break :p try std.fmt.allocPrint(gpa, "{s}/debuginfod_client/{x}", .{ cache_path, id });
|
||||
}
|
||||
if (std.posix.getenv("XDG_CACHE_HOME")) |cache_path| {
|
||||
if (cache_path.len > 0) {
|
||||
const path = std.fs.path.join(gpa, &[_][]const u8{ cache_path, "debuginfod_client" }) catch break :blk;
|
||||
defer gpa.free(path);
|
||||
break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk;
|
||||
}
|
||||
}
|
||||
if (std.posix.getenv("HOME")) |home_path| {
|
||||
const path = std.fs.path.join(gpa, &[_][]const u8{ home_path, ".cache", "debuginfod_client" }) catch break :blk;
|
||||
defer gpa.free(path);
|
||||
break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk;
|
||||
}
|
||||
break :blk;
|
||||
},
|
||||
}
|
||||
if (std.posix.getenv("HOME")) |home_path| {
|
||||
break :p try std.fmt.allocPrint(gpa, "{s}/.cache/debuginfod_client/{x}", .{ home_path, id });
|
||||
}
|
||||
break :debuginfod;
|
||||
};
|
||||
defer debuginfod_dir.close();
|
||||
defer gpa.free(id_dir_path);
|
||||
if (!std.fs.path.isAbsolute(id_dir_path)) break :debuginfod;
|
||||
|
||||
const filename = std.fmt.allocPrint(gpa, "{x}/debuginfo", .{id}) catch break :blk;
|
||||
defer gpa.free(filename);
|
||||
var id_dir = std.fs.openDirAbsolute(id_dir_path, .{}) catch break :debuginfod;
|
||||
defer id_dir.close();
|
||||
|
||||
const path: Path = .{
|
||||
.root_dir = .{ .path = null, .handle = debuginfod_dir },
|
||||
.sub_path = filename,
|
||||
};
|
||||
|
||||
return loadPath(gpa, path, null, separate_debug_crc, §ions, mapped_mem) catch break :blk;
|
||||
return load(gpa, .{
|
||||
.root_dir = .{ .path = id_dir_path, .handle = id_dir },
|
||||
.sub_path = "debuginfo",
|
||||
}, null, separate_debug_crc, §ions, mapped_mem) catch break :debuginfod;
|
||||
}
|
||||
|
||||
const global_debug_directories = [_][]const u8{
|
||||
|
|
@ -1659,33 +1678,37 @@ pub const ElfModule = struct {
|
|||
|
||||
for (global_debug_directories) |global_directory| {
|
||||
const path: Path = .{
|
||||
.root_dir = std.Build.Cache.Directory.cwd(),
|
||||
.root_dir = .cwd(),
|
||||
.sub_path = try std.fs.path.join(gpa, &.{
|
||||
global_directory, ".build-id", &id_prefix_buf, filename,
|
||||
}),
|
||||
};
|
||||
defer gpa.free(path.sub_path);
|
||||
|
||||
return loadPath(gpa, path, null, separate_debug_crc, §ions, mapped_mem) catch continue;
|
||||
return load(gpa, path, null, separate_debug_crc, §ions, mapped_mem) catch continue;
|
||||
}
|
||||
}
|
||||
|
||||
// use the path from .gnu_debuglink, in the same search order as gdb
|
||||
if (separate_debug_filename) |separate_filename| blk: {
|
||||
if (elf_filename != null and mem.eql(u8, elf_filename.?, separate_filename))
|
||||
separate: {
|
||||
const separate_filename = separate_debug_filename orelse break :separate;
|
||||
if (mem.eql(u8, std.fs.path.basename(elf_file_path.sub_path), separate_filename))
|
||||
return error.MissingDebugInfo;
|
||||
|
||||
exe_dir: {
|
||||
var exe_dir_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const exe_dir_path = std.fs.selfExeDirPath(&exe_dir_buf) catch break :exe_dir;
|
||||
const exe_dir_path = try std.fs.path.resolve(gpa, &.{
|
||||
elf_file_path.root_dir.path orelse ".",
|
||||
std.fs.path.dirname(elf_file_path.sub_path) orelse ".",
|
||||
});
|
||||
defer gpa.free(exe_dir_path);
|
||||
var exe_dir = std.fs.openDirAbsolute(exe_dir_path, .{}) catch break :exe_dir;
|
||||
defer exe_dir.close();
|
||||
|
||||
// <exe_dir>/<gnu_debuglink>
|
||||
if (loadPath(
|
||||
if (load(
|
||||
gpa,
|
||||
.{
|
||||
.root_dir = .{ .path = null, .handle = exe_dir },
|
||||
.root_dir = .{ .path = exe_dir_path, .handle = exe_dir },
|
||||
.sub_path = separate_filename,
|
||||
},
|
||||
null,
|
||||
|
|
@ -1698,27 +1721,27 @@ pub const ElfModule = struct {
|
|||
|
||||
// <exe_dir>/.debug/<gnu_debuglink>
|
||||
const path: Path = .{
|
||||
.root_dir = .{ .path = null, .handle = exe_dir },
|
||||
.root_dir = .{ .path = exe_dir_path, .handle = exe_dir },
|
||||
.sub_path = try std.fs.path.join(gpa, &.{ ".debug", separate_filename }),
|
||||
};
|
||||
defer gpa.free(path.sub_path);
|
||||
|
||||
if (loadPath(gpa, path, null, separate_debug_crc, §ions, mapped_mem)) |em| {
|
||||
if (load(gpa, path, null, separate_debug_crc, §ions, mapped_mem)) |em| {
|
||||
return em;
|
||||
} else |_| {}
|
||||
}
|
||||
|
||||
var cwd_buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const cwd_path = std.posix.realpath(".", &cwd_buf) catch break :blk;
|
||||
const cwd_path = std.posix.realpath(".", &cwd_buf) catch break :separate;
|
||||
|
||||
// <global debug directory>/<absolute folder of current binary>/<gnu_debuglink>
|
||||
for (global_debug_directories) |global_directory| {
|
||||
const path: Path = .{
|
||||
.root_dir = std.Build.Cache.Directory.cwd(),
|
||||
.root_dir = .cwd(),
|
||||
.sub_path = try std.fs.path.join(gpa, &.{ global_directory, cwd_path, separate_filename }),
|
||||
};
|
||||
defer gpa.free(path.sub_path);
|
||||
if (loadPath(gpa, path, null, separate_debug_crc, §ions, mapped_mem)) |em| {
|
||||
if (load(gpa, path, null, separate_debug_crc, §ions, mapped_mem)) |em| {
|
||||
return em;
|
||||
} else |_| {}
|
||||
}
|
||||
|
|
@ -1735,47 +1758,6 @@ pub const ElfModule = struct {
|
|||
.dwarf = dwarf,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn loadPath(
|
||||
gpa: Allocator,
|
||||
elf_file_path: Path,
|
||||
build_id: ?[]const u8,
|
||||
expected_crc: ?u32,
|
||||
parent_sections: *Dwarf.SectionArray,
|
||||
parent_mapped_mem: ?[]align(std.heap.page_size_min) const u8,
|
||||
) LoadError!ElfModule {
|
||||
const elf_file = elf_file_path.root_dir.handle.openFile(elf_file_path.sub_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => return missing(),
|
||||
else => return err,
|
||||
};
|
||||
defer elf_file.close();
|
||||
|
||||
const end_pos = elf_file.getEndPos() catch return bad();
|
||||
const file_len = cast(usize, end_pos) orelse return error.Overflow;
|
||||
|
||||
const mapped_mem = std.posix.mmap(
|
||||
null,
|
||||
file_len,
|
||||
std.posix.PROT.READ,
|
||||
.{ .TYPE = .SHARED },
|
||||
elf_file.handle,
|
||||
0,
|
||||
) catch |err| switch (err) {
|
||||
error.MappingAlreadyExists => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
errdefer std.posix.munmap(mapped_mem);
|
||||
|
||||
return load(
|
||||
gpa,
|
||||
mapped_mem,
|
||||
build_id,
|
||||
expected_crc,
|
||||
parent_sections,
|
||||
parent_mapped_mem,
|
||||
elf_file_path.sub_path,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn getSymbol(di: *Dwarf, allocator: Allocator, endian: Endian, address: u64) !std.debug.Symbol {
|
||||
|
|
|
|||
|
|
@ -41,8 +41,6 @@ const SortedFdeEntry = struct {
|
|||
|
||||
const Section = enum { debug_frame, eh_frame };
|
||||
|
||||
// MLUGG TODO deinit?
|
||||
|
||||
/// Initialize with unwind information from the contents of a `.debug_frame` or `.eh_frame` section.
|
||||
///
|
||||
/// If the `.eh_frame_hdr` section is available, consider instead using `initEhFrameHdr`. This
|
||||
|
|
@ -78,6 +76,13 @@ pub fn initEhFrameHdr(header: EhFrameHeader, section_vaddr: u64, section_bytes_p
|
|||
};
|
||||
}
|
||||
|
||||
pub fn deinit(unwind: *Unwind, gpa: Allocator) void {
|
||||
if (unwind.lookup) |lookup| switch (lookup) {
|
||||
.eh_frame_hdr => {},
|
||||
.sorted_fdes => |fdes| gpa.free(fdes),
|
||||
};
|
||||
}
|
||||
|
||||
/// This represents the decoded .eh_frame_hdr header
|
||||
pub const EhFrameHeader = struct {
|
||||
eh_frame_vaddr: u64,
|
||||
|
|
@ -205,8 +210,6 @@ pub const EntryHeader = union(enum) {
|
|||
const unit_header = try Dwarf.readUnitHeader(r, endian);
|
||||
if (unit_header.unit_length == 0) return .terminator;
|
||||
|
||||
// TODO MLUGG: seriously, just... check the formats of everything in BOTH LSB Core and DWARF. this is a fucking *mess*. maybe add spec references.
|
||||
|
||||
// Next is a value which will disambiguate CIEs and FDEs. Annoyingly, LSB Core makes this
|
||||
// value always 4-byte, whereas DWARF makes it depend on the `dwarf.Format`.
|
||||
const cie_ptr_or_id_size: u8 = switch (section) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ 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 elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, §ions, null);
|
||||
var elf_module = try Dwarf.ElfModule.load(gpa, path, null, null, §ions, null);
|
||||
try elf_module.dwarf.populateRanges(gpa);
|
||||
var info: Info = .{
|
||||
.address_map = .{},
|
||||
|
|
|
|||
|
|
@ -156,11 +156,7 @@ const Module = switch (native_os) {
|
|||
return error.MissingDebugInfo;
|
||||
}
|
||||
fn loadLocationInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {
|
||||
const mapped_mem = mapFileOrSelfExe(module.name) catch |err| switch (err) {
|
||||
error.FileNotFound => return error.MissingDebugInfo,
|
||||
error.FileTooBig => return error.InvalidDebugInfo,
|
||||
else => |e| return e,
|
||||
};
|
||||
const mapped_mem = try mapDebugInfoFile(module.name);
|
||||
errdefer posix.munmap(mapped_mem);
|
||||
|
||||
const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr));
|
||||
|
|
@ -311,7 +307,6 @@ const Module = switch (native_os) {
|
|||
gop.value_ptr.* = DebugInfo.loadOFile(gpa, o_file_path) catch |err| {
|
||||
defer _ = di.full.?.ofiles.pop().?;
|
||||
switch (err) {
|
||||
error.FileNotFound,
|
||||
error.MissingDebugInfo,
|
||||
error.InvalidDebugInfo,
|
||||
=> return sym_only_result,
|
||||
|
|
@ -402,7 +397,7 @@ const Module = switch (native_os) {
|
|||
}
|
||||
|
||||
fn loadOFile(gpa: Allocator, o_file_path: []const u8) !OFile {
|
||||
const mapped_mem = try mapFileOrSelfExe(o_file_path);
|
||||
const mapped_mem = try mapDebugInfoFile(o_file_path);
|
||||
errdefer posix.munmap(mapped_mem);
|
||||
|
||||
if (mapped_mem.len < @sizeOf(macho.mach_header_64)) return error.InvalidDebugInfo;
|
||||
|
|
@ -595,14 +590,27 @@ const Module = switch (native_os) {
|
|||
return error.MissingDebugInfo;
|
||||
}
|
||||
fn loadLocationInfo(module: *const Module, gpa: Allocator, di: *Module.DebugInfo) !void {
|
||||
const filename: ?[]const u8 = if (module.name.len > 0) module.name else null;
|
||||
const mapped_mem = mapFileOrSelfExe(filename) catch |err| switch (err) {
|
||||
error.FileNotFound => return error.MissingDebugInfo,
|
||||
error.FileTooBig => return error.InvalidDebugInfo,
|
||||
else => |e| return e,
|
||||
};
|
||||
errdefer posix.munmap(mapped_mem);
|
||||
di.em = try .load(gpa, mapped_mem, module.build_id, null, null, null, filename);
|
||||
if (module.name.len > 0) {
|
||||
di.em = Dwarf.ElfModule.load(gpa, .{
|
||||
.root_dir = .cwd(),
|
||||
.sub_path = module.name,
|
||||
}, module.build_id, null, null, null) catch |err| switch (err) {
|
||||
error.FileNotFound => return error.MissingDebugInfo,
|
||||
error.Overflow => return error.InvalidDebugInfo,
|
||||
else => |e| return e,
|
||||
};
|
||||
} else {
|
||||
const path = try std.fs.selfExePathAlloc(gpa);
|
||||
defer gpa.free(path);
|
||||
di.em = Dwarf.ElfModule.load(gpa, .{
|
||||
.root_dir = .cwd(),
|
||||
.sub_path = path,
|
||||
}, module.build_id, null, null, null) catch |err| switch (err) {
|
||||
error.FileNotFound => return error.MissingDebugInfo,
|
||||
error.Overflow => return error.InvalidDebugInfo,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
}
|
||||
fn getSymbolAtAddress(module: *const Module, gpa: Allocator, di: *DebugInfo, address: usize) !std.debug.Symbol {
|
||||
if (di.em == null) try module.loadLocationInfo(gpa, di);
|
||||
|
|
@ -1247,14 +1255,18 @@ fn applyOffset(base: usize, offset: i64) !usize {
|
|||
}
|
||||
|
||||
/// Uses `mmap` to map the file at `opt_path` (or, if `null`, the self executable image) into memory.
|
||||
fn mapFileOrSelfExe(opt_path: ?[]const u8) ![]align(std.heap.page_size_min) const u8 {
|
||||
const file = if (opt_path) |path|
|
||||
try fs.cwd().openFile(path, .{})
|
||||
fn mapDebugInfoFile(opt_path: ?[]const u8) ![]align(std.heap.page_size_min) const u8 {
|
||||
const open_result = if (opt_path) |path|
|
||||
fs.cwd().openFile(path, .{})
|
||||
else
|
||||
try fs.openSelfExe(.{});
|
||||
fs.openSelfExe(.{});
|
||||
const file = open_result catch |err| switch (err) {
|
||||
error.FileNotFound => return error.MissingDebugInfo,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer file.close();
|
||||
|
||||
const file_len = math.cast(usize, try file.getEndPos()) orelse return error.FileTooBig;
|
||||
const file_len = math.cast(usize, try file.getEndPos()) orelse return error.InvalidDebugInfo;
|
||||
|
||||
return posix.mmap(
|
||||
null,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue