aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug/SelfInfo/ElfModule.zig
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/debug/SelfInfo/ElfModule.zig')
-rw-r--r--lib/std/debug/SelfInfo/ElfModule.zig189
1 files changed, 118 insertions, 71 deletions
diff --git a/lib/std/debug/SelfInfo/ElfModule.zig b/lib/std/debug/SelfInfo/ElfModule.zig
index 32d767a44f..eead810a86 100644
--- a/lib/std/debug/SelfInfo/ElfModule.zig
+++ b/lib/std/debug/SelfInfo/ElfModule.zig
@@ -3,8 +3,22 @@ name: []const u8,
build_id: ?[]const u8,
gnu_eh_frame: ?[]const u8,
-/// No cache needed, because `dl_iterate_phdr` is already fast.
-pub const LookupCache = void;
+pub const LookupCache = struct {
+ rwlock: std.Thread.RwLock,
+ ranges: std.ArrayList(Range),
+ const Range = struct {
+ start: usize,
+ len: usize,
+ mod: ElfModule,
+ };
+ pub const init: LookupCache = .{
+ .rwlock = .{},
+ .ranges = .empty,
+ };
+ pub fn deinit(lc: *LookupCache, gpa: Allocator) void {
+ lc.ranges.deinit(gpa);
+ }
+};
pub const DebugInfo = struct {
/// Held while checking and/or populating `loaded_elf`/`scanned_dwarf`/`unwind`.
@@ -14,18 +28,24 @@ pub const DebugInfo = struct {
loaded_elf: ?ElfFile,
scanned_dwarf: bool,
- unwind: [2]?Dwarf.Unwind,
+ unwind: if (supports_unwinding) [2]?Dwarf.Unwind else void,
+ unwind_cache: if (supports_unwinding) *UnwindContext.Cache else void,
+
pub const init: DebugInfo = .{
.mutex = .{},
.loaded_elf = null,
.scanned_dwarf = false,
- .unwind = @splat(null),
+ .unwind = if (supports_unwinding) @splat(null),
+ .unwind_cache = undefined,
};
pub fn deinit(di: *DebugInfo, gpa: Allocator) void {
if (di.loaded_elf) |*loaded_elf| loaded_elf.deinit(gpa);
- for (&di.unwind) |*opt_unwind| {
- const unwind = &(opt_unwind.* orelse continue);
- unwind.deinit(gpa);
+ if (supports_unwinding) {
+ if (di.unwind[0] != null) gpa.destroy(di.unwind_cache);
+ for (&di.unwind) |*opt_unwind| {
+ const unwind = &(opt_unwind.* orelse continue);
+ unwind.deinit(gpa);
+ }
}
}
};
@@ -34,75 +54,84 @@ pub fn key(m: ElfModule) usize {
return m.load_offset;
}
pub fn lookup(cache: *LookupCache, gpa: Allocator, address: usize) Error!ElfModule {
- _ = cache;
- _ = gpa;
- const DlIterContext = struct {
- /// input
- address: usize,
- /// output
- module: ElfModule,
+ if (lookupInCache(cache, address)) |m| return m;
- fn callback(info: *std.posix.dl_phdr_info, size: usize, context: *@This()) !void {
- _ = size;
- // The base address is too high
- if (context.address < info.addr)
- return;
+ {
+ // Check a new module hasn't been loaded
+ cache.rwlock.lock();
+ defer cache.rwlock.unlock();
+ const DlIterContext = struct {
+ ranges: *std.ArrayList(LookupCache.Range),
+ gpa: Allocator,
- const phdrs = info.phdr[0..info.phnum];
- for (phdrs) |*phdr| {
- if (phdr.p_type != elf.PT_LOAD) continue;
+ fn callback(info: *std.posix.dl_phdr_info, size: usize, context: *@This()) !void {
+ _ = size;
- // Overflowing addition is used to handle the case of VSDOs having a p_vaddr = 0xffffffffff700000
- const seg_start = info.addr +% phdr.p_vaddr;
- const seg_end = seg_start + phdr.p_memsz;
- if (context.address >= seg_start and context.address < seg_end) {
- context.module = .{
- .load_offset = info.addr,
- // Android libc uses NULL instead of "" to mark the main program
- .name = mem.sliceTo(info.name, 0) orelse "",
- .build_id = null,
- .gnu_eh_frame = null,
- };
- break;
+ var mod: ElfModule = .{
+ .load_offset = info.addr,
+ // Android libc uses NULL instead of "" to mark the main program
+ .name = mem.sliceTo(info.name, 0) orelse "",
+ .build_id = null,
+ .gnu_eh_frame = null,
+ };
+
+ // Populate `build_id` and `gnu_eh_frame`
+ for (info.phdr[0..info.phnum]) |phdr| {
+ switch (phdr.p_type) {
+ elf.PT_NOTE => {
+ // Look for .note.gnu.build-id
+ const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr);
+ var r: std.Io.Reader = .fixed(segment_ptr[0..phdr.p_memsz]);
+ const name_size = r.takeInt(u32, native_endian) catch continue;
+ const desc_size = r.takeInt(u32, native_endian) catch continue;
+ const note_type = r.takeInt(u32, native_endian) catch continue;
+ const name = r.take(name_size) catch continue;
+ if (note_type != elf.NT_GNU_BUILD_ID) continue;
+ if (!mem.eql(u8, name, "GNU\x00")) continue;
+ const desc = r.take(desc_size) catch continue;
+ mod.build_id = desc;
+ },
+ elf.PT_GNU_EH_FRAME => {
+ const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr);
+ mod.gnu_eh_frame = segment_ptr[0..phdr.p_memsz];
+ },
+ else => {},
+ }
}
- } else return;
- for (info.phdr[0..info.phnum]) |phdr| {
- switch (phdr.p_type) {
- elf.PT_NOTE => {
- // Look for .note.gnu.build-id
- const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr);
- var r: std.Io.Reader = .fixed(segment_ptr[0..phdr.p_memsz]);
- const name_size = r.takeInt(u32, native_endian) catch continue;
- const desc_size = r.takeInt(u32, native_endian) catch continue;
- const note_type = r.takeInt(u32, native_endian) catch continue;
- const name = r.take(name_size) catch continue;
- if (note_type != elf.NT_GNU_BUILD_ID) continue;
- if (!mem.eql(u8, name, "GNU\x00")) continue;
- const desc = r.take(desc_size) catch continue;
- context.module.build_id = desc;
- },
- elf.PT_GNU_EH_FRAME => {
- const segment_ptr: [*]const u8 = @ptrFromInt(info.addr + phdr.p_vaddr);
- context.module.gnu_eh_frame = segment_ptr[0..phdr.p_memsz];
- },
- else => {},
+ // Now that `mod` is populated, create the ranges
+ for (info.phdr[0..info.phnum]) |phdr| {
+ if (phdr.p_type != elf.PT_LOAD) continue;
+ try context.ranges.append(context.gpa, .{
+ // Overflowing addition handles VSDOs having p_vaddr = 0xffffffffff700000
+ .start = info.addr +% phdr.p_vaddr,
+ .len = phdr.p_memsz,
+ .mod = mod,
+ });
}
}
+ };
+ cache.ranges.clearRetainingCapacity();
+ var ctx: DlIterContext = .{
+ .ranges = &cache.ranges,
+ .gpa = gpa,
+ };
+ try std.posix.dl_iterate_phdr(&ctx, error{OutOfMemory}, DlIterContext.callback);
+ }
- // Stop the iteration
- return error.Found;
- }
- };
- var ctx: DlIterContext = .{
- .address = address,
- .module = undefined,
- };
- std.posix.dl_iterate_phdr(&ctx, error{Found}, DlIterContext.callback) catch |err| switch (err) {
- error.Found => return ctx.module,
- };
+ if (lookupInCache(cache, address)) |m| return m;
return error.MissingDebugInfo;
}
+fn lookupInCache(cache: *LookupCache, address: usize) ?ElfModule {
+ cache.rwlock.lockShared();
+ defer cache.rwlock.unlockShared();
+ for (cache.ranges.items) |*range| {
+ if (address >= range.start and address < range.start + range.len) {
+ return range.mod;
+ }
+ }
+ return null;
+}
fn loadElf(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void {
std.debug.assert(di.loaded_elf == null);
std.debug.assert(!di.scanned_dwarf);
@@ -199,11 +228,23 @@ pub fn getSymbolAtAddress(module: *const ElfModule, gpa: Allocator, di: *DebugIn
};
}
fn prepareUnwindLookup(unwind: *Dwarf.Unwind, gpa: Allocator) Error!void {
- unwind.prepareLookup(gpa, @sizeOf(usize), native_endian) catch |err| switch (err) {
+ unwind.prepare(gpa, @sizeOf(usize), native_endian, true) 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,
+ error.InvalidDebugInfo,
+ error.MissingDebugInfo,
+ error.OutOfMemory,
+ => |e| return e,
+ error.EndOfStream,
+ error.Overflow,
+ error.StreamTooLong,
+ error.InvalidOperand,
+ error.InvalidOpcode,
+ error.InvalidOperation,
+ => return error.InvalidDebugInfo,
+ error.UnsupportedAddrSize,
+ error.UnsupportedDwarfVersion,
+ error.UnimplementedUserOpcode,
+ => return error.UnsupportedDebugInfo,
};
}
fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Error!void {
@@ -240,12 +281,18 @@ fn loadUnwindInfo(module: *const ElfModule, gpa: Allocator, di: *DebugInfo) Erro
};
errdefer for (unwinds) |*u| u.deinit(gpa);
for (unwinds) |*u| try prepareUnwindLookup(u, gpa);
+
+ const unwind_cache = try gpa.create(UnwindContext.Cache);
+ errdefer gpa.destroy(unwind_cache);
+ unwind_cache.init();
+
switch (unwinds.len) {
0 => unreachable,
1 => di.unwind = .{ unwinds[0], null },
2 => di.unwind = .{ unwinds[0], unwinds[1] },
else => unreachable,
}
+ di.unwind_cache = unwind_cache;
}
pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, context: *UnwindContext) Error!usize {
const unwinds: *const [2]?Dwarf.Unwind = u: {
@@ -257,7 +304,7 @@ pub fn unwindFrame(module: *const ElfModule, gpa: Allocator, di: *DebugInfo, con
};
for (unwinds) |*opt_unwind| {
const unwind = &(opt_unwind.* orelse break);
- return context.unwindFrame(gpa, unwind, module.load_offset, null) catch |err| switch (err) {
+ return context.unwindFrame(di.unwind_cache, gpa, unwind, module.load_offset, null) catch |err| switch (err) {
error.MissingDebugInfo => continue, // try the next one
else => |e| return e,
};