mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
SelfInfo: load eh_frame/debug_frame from ELF file if eh_frame_hdr omitted
This commit is contained in:
parent
c895aa7a35
commit
405075f745
@ -3,6 +3,13 @@
|
||||
|
||||
dwarf: Dwarf,
|
||||
|
||||
/// If we encounter a `.eh_frame` section while loading the ELF module, it is stored here and may be
|
||||
/// used with `Dwarf.Unwind` for call stack unwinding.
|
||||
eh_frame: ?UnwindSection,
|
||||
/// If we encounter a `.debug_frame` section while loading the ELF module, it is stored here and may
|
||||
/// be used with `Dwarf.Unwind` for call stack unwinding.
|
||||
debug_frame: ?UnwindSection,
|
||||
|
||||
/// The memory-mapped ELF file, which is referenced by `dwarf`. This field is here only so that
|
||||
/// this memory can be unmapped by `ElfModule.deinit`.
|
||||
mapped_file: []align(std.heap.page_size_min) const u8,
|
||||
@ -11,10 +18,18 @@ mapped_file: []align(std.heap.page_size_min) const u8,
|
||||
/// be unmapped by `ElfModule.deinit`.
|
||||
mapped_debug_file: ?[]align(std.heap.page_size_min) const u8,
|
||||
|
||||
pub fn deinit(em: *ElfModule, allocator: Allocator) void {
|
||||
em.dwarf.deinit(allocator);
|
||||
pub const UnwindSection = struct {
|
||||
vaddr: u64,
|
||||
bytes: []const u8,
|
||||
owned: bool,
|
||||
};
|
||||
|
||||
pub fn deinit(em: *ElfModule, gpa: Allocator) void {
|
||||
em.dwarf.deinit(gpa);
|
||||
std.posix.munmap(em.mapped_file);
|
||||
if (em.mapped_debug_file) |m| std.posix.munmap(m);
|
||||
if (em.eh_frame) |s| if (s.owned) gpa.free(s.bytes);
|
||||
if (em.debug_frame) |s| if (s.owned) gpa.free(s.bytes);
|
||||
}
|
||||
|
||||
pub const LoadError = error{
|
||||
@ -98,7 +113,6 @@ pub fn load(
|
||||
)[0..hdr.e_shnum];
|
||||
|
||||
var sections: Dwarf.SectionArray = @splat(null);
|
||||
|
||||
// Combine section list. This takes ownership over any owned sections from the parent scope.
|
||||
if (parent_sections) |ps| {
|
||||
for (ps, §ions) |*parent, *section_elem| {
|
||||
@ -110,6 +124,12 @@ pub fn load(
|
||||
}
|
||||
errdefer for (sections) |opt_section| if (opt_section) |s| if (s.owned) gpa.free(s.data);
|
||||
|
||||
var eh_frame_section: ?UnwindSection = null;
|
||||
errdefer if (eh_frame_section) |s| if (s.owned) gpa.free(s.bytes);
|
||||
|
||||
var debug_frame_section: ?UnwindSection = null;
|
||||
errdefer if (debug_frame_section) |s| if (s.owned) gpa.free(s.bytes);
|
||||
|
||||
var separate_debug_filename: ?[]const u8 = null;
|
||||
var separate_debug_crc: ?u32 = null;
|
||||
|
||||
@ -128,17 +148,35 @@ pub fn load(
|
||||
continue;
|
||||
}
|
||||
|
||||
var section_index: ?usize = null;
|
||||
inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields, 0..) |sect, i| {
|
||||
if (mem.eql(u8, "." ++ sect.name, name)) section_index = i;
|
||||
const section_id: union(enum) {
|
||||
dwarf: Dwarf.Section.Id,
|
||||
eh_frame,
|
||||
debug_frame,
|
||||
} = s: {
|
||||
inline for (@typeInfo(Dwarf.Section.Id).@"enum".fields) |s| {
|
||||
if (mem.eql(u8, "." ++ s.name, name)) {
|
||||
break :s .{ .dwarf = @enumFromInt(s.value) };
|
||||
}
|
||||
}
|
||||
if (mem.eql(u8, ".eh_frame", name)) break :s .eh_frame;
|
||||
if (mem.eql(u8, ".debug_frame", name)) break :s .debug_frame;
|
||||
continue;
|
||||
};
|
||||
|
||||
switch (section_id) {
|
||||
.dwarf => |i| if (sections[@intFromEnum(i)] != null) continue,
|
||||
.eh_frame => if (eh_frame_section != null) continue,
|
||||
.debug_frame => if (debug_frame_section != null) continue,
|
||||
}
|
||||
if (section_index == null) continue;
|
||||
if (sections[section_index.?] != null) continue;
|
||||
|
||||
if (mapped_mem.len < shdr.sh_offset + shdr.sh_size) return error.InvalidDebugInfo;
|
||||
const section_bytes = mapped_mem[@intCast(shdr.sh_offset)..][0..@intCast(shdr.sh_size)];
|
||||
sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: {
|
||||
var section_reader: Reader = .fixed(section_bytes);
|
||||
const raw_section_bytes = mapped_mem[@intCast(shdr.sh_offset)..][0..@intCast(shdr.sh_size)];
|
||||
|
||||
const section_bytes: []const u8, const section_owned: bool = section: {
|
||||
if ((shdr.sh_flags & elf.SHF_COMPRESSED) == 0) {
|
||||
break :section .{ raw_section_bytes, false };
|
||||
}
|
||||
var section_reader: Reader = .fixed(raw_section_bytes);
|
||||
const chdr = section_reader.takeStruct(elf.Chdr, endian) catch continue;
|
||||
if (chdr.ch_type != .ZLIB) continue;
|
||||
|
||||
@ -153,14 +191,24 @@ pub fn load(
|
||||
Dwarf.invalidDebugInfoDetected();
|
||||
continue;
|
||||
}
|
||||
break :blk .{
|
||||
.data = try decompressed_section.toOwnedSlice(gpa),
|
||||
.owned = true,
|
||||
};
|
||||
} else .{
|
||||
.data = section_bytes,
|
||||
.owned = false,
|
||||
break :section .{ try decompressed_section.toOwnedSlice(gpa), true };
|
||||
};
|
||||
switch (section_id) {
|
||||
.dwarf => |id| sections[@intFromEnum(id)] = .{
|
||||
.data = section_bytes,
|
||||
.owned = section_owned,
|
||||
},
|
||||
.eh_frame => eh_frame_section = .{
|
||||
.vaddr = shdr.sh_addr,
|
||||
.bytes = section_bytes,
|
||||
.owned = section_owned,
|
||||
},
|
||||
.debug_frame => debug_frame_section = .{
|
||||
.vaddr = shdr.sh_addr,
|
||||
.bytes = section_bytes,
|
||||
.owned = section_owned,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const missing_debug_info =
|
||||
@ -305,9 +353,11 @@ pub fn load(
|
||||
var dwarf: Dwarf = .{ .sections = sections };
|
||||
try dwarf.open(gpa, endian);
|
||||
return .{
|
||||
.dwarf = dwarf,
|
||||
.eh_frame = eh_frame_section,
|
||||
.debug_frame = debug_frame_section,
|
||||
.mapped_file = parent_mapped_mem orelse mapped_mem,
|
||||
.mapped_debug_file = if (parent_mapped_mem != null) mapped_mem else null,
|
||||
.dwarf = dwarf,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,10 +8,10 @@ pub const LookupCache = void;
|
||||
|
||||
pub const DebugInfo = struct {
|
||||
loaded_elf: ?Dwarf.ElfModule,
|
||||
unwind: ?Dwarf.Unwind,
|
||||
unwind: [2]?Dwarf.Unwind,
|
||||
pub const init: DebugInfo = .{
|
||||
.loaded_elf = null,
|
||||
.unwind = null,
|
||||
.unwind = @splat(null),
|
||||
};
|
||||
pub fn deinit(di: *DebugInfo, gpa: Allocator) void {
|
||||
if (di.loaded_elf) |*loaded_elf| loaded_elf.deinit(gpa);
|
||||
@ -143,30 +143,67 @@ pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugIn
|
||||
=> return error.InvalidDebugInfo,
|
||||
};
|
||||
}
|
||||
fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void {
|
||||
const section_bytes = module.gnu_eh_frame orelse return error.MissingDebugInfo; // MLUGG TODO: load from file
|
||||
|
||||
const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset;
|
||||
const header = Dwarf.Unwind.EhFrameHeader.parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian) catch |err| switch (err) {
|
||||
error.ReadFailed => unreachable, // it's all fixed buffers
|
||||
error.InvalidDebugInfo => |e| return e,
|
||||
error.EndOfStream, error.Overflow => return error.InvalidDebugInfo,
|
||||
error.UnsupportedAddrSize => return error.UnsupportedDebugInfo,
|
||||
};
|
||||
|
||||
var unwind: Dwarf.Unwind = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr));
|
||||
fn prepareUnwindLookup(unwind: *Dwarf.Unwind, gpa: Allocator) Error!void {
|
||||
unwind.prepareLookup(gpa, @sizeOf(usize), native_endian) catch |err| switch (err) {
|
||||
error.ReadFailed => unreachable, // it's all fixed buffers
|
||||
error.InvalidDebugInfo, error.MissingDebugInfo, error.OutOfMemory => |e| return e,
|
||||
error.EndOfStream, error.Overflow, error.StreamTooLong => return error.InvalidDebugInfo,
|
||||
error.UnsupportedAddrSize, error.UnsupportedDwarfVersion => return error.UnsupportedDebugInfo,
|
||||
};
|
||||
|
||||
di.unwind = unwind;
|
||||
}
|
||||
fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void {
|
||||
var buf: [2]Dwarf.Unwind = undefined;
|
||||
const unwinds: []Dwarf.Unwind = if (module.gnu_eh_frame) |section_bytes| unwinds: {
|
||||
const section_vaddr: u64 = @intFromPtr(section_bytes.ptr) - module.load_offset;
|
||||
const header = Dwarf.Unwind.EhFrameHeader.parse(section_vaddr, section_bytes, @sizeOf(usize), native_endian) catch |err| switch (err) {
|
||||
error.ReadFailed => unreachable, // it's all fixed buffers
|
||||
error.InvalidDebugInfo => |e| return e,
|
||||
error.EndOfStream, error.Overflow => return error.InvalidDebugInfo,
|
||||
error.UnsupportedAddrSize => return error.UnsupportedDebugInfo,
|
||||
};
|
||||
buf[0] = .initEhFrameHdr(header, section_vaddr, @ptrFromInt(module.load_offset + header.eh_frame_vaddr));
|
||||
break :unwinds buf[0..1];
|
||||
} else unwinds: {
|
||||
// There is no `.eh_frame_hdr` section. There may still be an `.eh_frame` or `.debug_frame`
|
||||
// section, but we'll have to load the binary to get at it.
|
||||
try module.loadDwarf(gpa, di);
|
||||
const opt_debug_frame = &di.loaded_elf.?.debug_frame;
|
||||
const opt_eh_frame = &di.loaded_elf.?.eh_frame;
|
||||
// If both are present, we can't just pick one -- the info could be split between them.
|
||||
// `.debug_frame` is likely to be the more complete section, so we'll prioritize that one.
|
||||
if (opt_debug_frame.*) |*debug_frame| {
|
||||
buf[0] = .initSection(.debug_frame, debug_frame.vaddr, debug_frame.bytes);
|
||||
if (opt_eh_frame.*) |*eh_frame| {
|
||||
buf[1] = .initSection(.eh_frame, eh_frame.vaddr, eh_frame.bytes);
|
||||
break :unwinds buf[0..2];
|
||||
}
|
||||
break :unwinds buf[0..1];
|
||||
} else if (opt_eh_frame.*) |eh_frame| {
|
||||
buf[0] = .initSection(.eh_frame, eh_frame.vaddr, eh_frame.bytes);
|
||||
break :unwinds buf[0..1];
|
||||
}
|
||||
return error.MissingDebugInfo;
|
||||
};
|
||||
errdefer for (unwinds) |*u| u.deinit(gpa);
|
||||
for (unwinds) |*u| try prepareUnwindLookup(u, gpa);
|
||||
switch (unwinds.len) {
|
||||
0 => unreachable,
|
||||
1 => di.unwind = .{ unwinds[0], null },
|
||||
2 => di.unwind = .{ unwinds[0], unwinds[1] },
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize {
|
||||
if (di.unwind == null) try module.loadUnwindInfo(gpa, di);
|
||||
return context.unwindFrameDwarf(&di.unwind.?, module.load_offset, null);
|
||||
if (di.unwind[0] == null) try module.loadUnwindInfo(gpa, di);
|
||||
std.debug.assert(di.unwind[0] != null);
|
||||
for (&di.unwind) |*opt_unwind| {
|
||||
const unwind = &(opt_unwind.* orelse break);
|
||||
return context.unwindFrameDwarf(unwind, module.load_offset, null) catch |err| switch (err) {
|
||||
error.MissingDebugInfo => continue, // try the next one
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
return error.MissingDebugInfo;
|
||||
}
|
||||
|
||||
const ElfModule = @This();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user