From 7e966de45e06c42b40f5fd4b862c3d320a21a486 Mon Sep 17 00:00:00 2001 From: Xavier Bouchoux Date: Wed, 28 Feb 2024 08:15:40 +0100 Subject: [PATCH 1/2] std.debug.Dwarf: fix loading external debuginfo in the ".debuglink" case. - look up the debuglink file in the directory of the executable file (instead of the cwd) - fix parsing of debuglink section (the 4-byte alignement is within the file, unrelated to the in-memory address) --- lib/std/debug/Dwarf.zig | 47 +++++++++++++----------- test/standalone/stack_iterator/build.zig | 15 ++++++++ 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index e3d4ab1a8f..eb14b3168d 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -239,7 +239,7 @@ pub const Die = struct { return switch (form_value.*) { .addr => |value| value, .addrx => |index| di.readDebugAddr(compile_unit, index), - else => error.InvalidDebugInfo, + else => bad(), }; } @@ -252,7 +252,7 @@ pub const Die = struct { const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { .Const => |value| value.asUnsignedLe(), - else => error.InvalidDebugInfo, + else => bad(), }; } @@ -260,7 +260,7 @@ pub const Die = struct { const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; return switch (form_value.*) { .ref => |value| value, - else => error.InvalidDebugInfo, + else => bad(), }; } @@ -2159,7 +2159,7 @@ pub const ElfModule = struct { if (mem.eql(u8, name, ".gnu_debuglink")) { const gnu_debuglink = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size); const debug_filename = mem.sliceTo(@as([*:0]const u8, @ptrCast(gnu_debuglink.ptr)), 0); - const crc_offset = mem.alignForward(usize, @intFromPtr(&debug_filename[debug_filename.len]) + 1, 4) - @intFromPtr(gnu_debuglink.ptr); + const crc_offset = mem.alignForward(usize, debug_filename.len + 1, 4); const crc_bytes = gnu_debuglink[crc_offset..][0..4]; separate_debug_crc = mem.readInt(u32, crc_bytes, native_endian); separate_debug_filename = debug_filename; @@ -2253,25 +2253,30 @@ pub const ElfModule = struct { if (elf_filename != null and mem.eql(u8, elf_filename.?, separate_filename)) return error.MissingDebugInfo; - // / - if (loadPath( - gpa, - .{ - .root_dir = std.Build.Cache.Directory.cwd(), - .sub_path = separate_filename, - }, - null, - separate_debug_crc, - §ions, - mapped_mem, - )) |debug_info| { - return debug_info; - } else |_| {} + 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; + var exe_dir = std.fs.openDirAbsolute(exe_dir_path, .{}) catch break :exe_dir; + defer exe_dir.close(); - // /.debug/ - { + // / + if (loadPath( + gpa, + .{ + .root_dir = .{ .path = null, .handle = exe_dir }, + .sub_path = separate_filename, + }, + null, + separate_debug_crc, + §ions, + mapped_mem, + )) |debug_info| { + return debug_info; + } else |_| {} + + // /.debug/ const path: Path = .{ - .root_dir = std.Build.Cache.Directory.cwd(), + .root_dir = .{ .path = null, .handle = exe_dir }, .sub_path = try std.fs.path.join(gpa, &.{ ".debug", separate_filename }), }; defer gpa.free(path.sub_path); diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig index a989828a8c..d69d590912 100644 --- a/test/standalone/stack_iterator/build.zig +++ b/test/standalone/stack_iterator/build.zig @@ -93,6 +93,21 @@ pub fn build(b: *std.Build) void { const run_cmd = b.addRunArtifact(exe); test_step.dependOn(&run_cmd.step); + + // Separate debug info ELF file + if (target.result.ofmt == .elf) { + const filename = b.fmt("{s}_stripped", .{exe.out_filename}); + const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{ + .basename = filename, // set the name for the debuglink + .compress_debug = true, + .strip = .debug, + .extract_to_separate_file = true, + }); + + const run_stripped = std.Build.Step.Run.create(b, b.fmt("run {s}", .{filename})); + run_stripped.addFileArg(stripped_exe.getOutput()); + test_step.dependOn(&run_stripped.step); + } } // Unwinding without libc/posix From cbdf9bf5eee3200efbcf7e1ca65dc93e34f94508 Mon Sep 17 00:00:00 2001 From: Xavier Bouchoux Date: Wed, 7 Aug 2024 08:57:25 +0200 Subject: [PATCH 2/2] std.debug.Dwarf: try to load the debuginfo from the debuginfod cache. The previous mecanism for linux distributions to delivers debug info into `/usr/lib/debug` no longer seems in use. the current mecanism often is using `debuginfod` (https://sourceware.org/elfutils/Debuginfod.html) This commit only tries to load already available debuginfo but does not try to make any download requests. the user can manually run `debuginfod-find debuginfo PATH` to populate the cache. --- lib/std/debug/Dwarf.zig | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index eb14b3168d..6bbe43274f 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -2215,6 +2215,46 @@ pub const ElfModule = struct { return error.MissingDebugInfo; } + // $XDG_CACHE_HOME/debuginfod_client//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; + } + if (std.posix.getenv("XDG_CACHE_HOME")) |cache_path| { + 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; + }, + }; + defer debuginfod_dir.close(); + + const filename = std.fmt.allocPrint( + gpa, + "{s}/debuginfo", + .{std.fmt.fmtSliceHexLower(id)}, + ) catch break :blk; + defer gpa.free(filename); + + 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; + } + const global_debug_directories = [_][]const u8{ "/usr/lib/debug", };