authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-08 11:06:14-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-08-08 11:06:14-07:00
log8031251c33bc66c5648122c0dcd89a8b00c42229
tree12e95b16866f74542587f37abfe9c1d0b36cca01
parentcf87a1a7cf57123aad533a73d8e0fa4d4916a674
parentcbdf9bf5eee3200efbcf7e1ca65dc93e34f94508
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20997 from xxxbxxx/debuglink

std.debug.Dwarf: improve loading debug symbol from separate files

2 files changed, 82 insertions(+), 22 deletions(-)

lib/std/debug/Dwarf.zig+67-22
...@@ -239,7 +239,7 @@ pub const Die = struct {...@@ -239,7 +239,7 @@ pub const Die = struct {
239 return switch (form_value.*) {239 return switch (form_value.*) {
240 .addr => |value| value,240 .addr => |value| value,
241 .addrx => |index| di.readDebugAddr(compile_unit, index),241 .addrx => |index| di.readDebugAddr(compile_unit, index),
242 else => error.InvalidDebugInfo,242 else => bad(),
243 };243 };
244 }244 }
245245
...@@ -252,7 +252,7 @@ pub const Die = struct {...@@ -252,7 +252,7 @@ pub const Die = struct {
252 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;252 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
253 return switch (form_value.*) {253 return switch (form_value.*) {
254 .Const => |value| value.asUnsignedLe(),254 .Const => |value| value.asUnsignedLe(),
255 else => error.InvalidDebugInfo,255 else => bad(),
256 };256 };
257 }257 }
258258
...@@ -260,7 +260,7 @@ pub const Die = struct {...@@ -260,7 +260,7 @@ pub const Die = struct {
260 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;260 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
261 return switch (form_value.*) {261 return switch (form_value.*) {
262 .ref => |value| value,262 .ref => |value| value,
263 else => error.InvalidDebugInfo,263 else => bad(),
264 };264 };
265 }265 }
266266
...@@ -2159,7 +2159,7 @@ pub const ElfModule = struct {...@@ -2159,7 +2159,7 @@ pub const ElfModule = struct {
2159 if (mem.eql(u8, name, ".gnu_debuglink")) {2159 if (mem.eql(u8, name, ".gnu_debuglink")) {
2160 const gnu_debuglink = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);2160 const gnu_debuglink = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
2161 const debug_filename = mem.sliceTo(@as([*:0]const u8, @ptrCast(gnu_debuglink.ptr)), 0);2161 const debug_filename = mem.sliceTo(@as([*:0]const u8, @ptrCast(gnu_debuglink.ptr)), 0);
2162 const crc_offset = mem.alignForward(usize, @intFromPtr(&debug_filename[debug_filename.len]) + 1, 4) - @intFromPtr(gnu_debuglink.ptr);2162 const crc_offset = mem.alignForward(usize, debug_filename.len + 1, 4);
2163 const crc_bytes = gnu_debuglink[crc_offset..][0..4];2163 const crc_bytes = gnu_debuglink[crc_offset..][0..4];
2164 separate_debug_crc = mem.readInt(u32, crc_bytes, native_endian);2164 separate_debug_crc = mem.readInt(u32, crc_bytes, native_endian);
2165 separate_debug_filename = debug_filename;2165 separate_debug_filename = debug_filename;
...@@ -2215,6 +2215,46 @@ pub const ElfModule = struct {...@@ -2215,6 +2215,46 @@ pub const ElfModule = struct {
2215 return error.MissingDebugInfo;2215 return error.MissingDebugInfo;
2216 }2216 }
22172217
2218 // $XDG_CACHE_HOME/debuginfod_client/<buildid>/debuginfo
2219 // This only opportunisticly tries to load from the debuginfod cache, but doesn't try to populate it.
2220 // One can manually run `debuginfod-find debuginfo PATH` to download the symbols
2221 if (build_id) |id| blk: {
2222 var debuginfod_dir: std.fs.Dir = switch (builtin.os.tag) {
2223 .wasi, .windows => break :blk,
2224 else => dir: {
2225 if (std.posix.getenv("DEBUGINFOD_CACHE_PATH")) |path| {
2226 break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk;
2227 }
2228 if (std.posix.getenv("XDG_CACHE_HOME")) |cache_path| {
2229 const path = std.fs.path.join(gpa, &[_][]const u8{ cache_path, "debuginfod_client" }) catch break :blk;
2230 defer gpa.free(path);
2231 break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk;
2232 }
2233 if (std.posix.getenv("HOME")) |home_path| {
2234 const path = std.fs.path.join(gpa, &[_][]const u8{ home_path, ".cache", "debuginfod_client" }) catch break :blk;
2235 defer gpa.free(path);
2236 break :dir std.fs.openDirAbsolute(path, .{}) catch break :blk;
2237 }
2238 break :blk;
2239 },
2240 };
2241 defer debuginfod_dir.close();
2242
2243 const filename = std.fmt.allocPrint(
2244 gpa,
2245 "{s}/debuginfo",
2246 .{std.fmt.fmtSliceHexLower(id)},
2247 ) catch break :blk;
2248 defer gpa.free(filename);
2249
2250 const path: Path = .{
2251 .root_dir = .{ .path = null, .handle = debuginfod_dir },
2252 .sub_path = filename,
2253 };
2254
2255 return loadPath(gpa, path, null, separate_debug_crc, &sections, mapped_mem) catch break :blk;
2256 }
2257
2218 const global_debug_directories = [_][]const u8{2258 const global_debug_directories = [_][]const u8{
2219 "/usr/lib/debug",2259 "/usr/lib/debug",
2220 };2260 };
...@@ -2253,25 +2293,30 @@ pub const ElfModule = struct {...@@ -2253,25 +2293,30 @@ pub const ElfModule = struct {
2253 if (elf_filename != null and mem.eql(u8, elf_filename.?, separate_filename))2293 if (elf_filename != null and mem.eql(u8, elf_filename.?, separate_filename))
2254 return error.MissingDebugInfo;2294 return error.MissingDebugInfo;
22552295
2256 // <cwd>/<gnu_debuglink>2296 exe_dir: {
2257 if (loadPath(2297 var exe_dir_buf: [std.fs.max_path_bytes]u8 = undefined;
2258 gpa,2298 const exe_dir_path = std.fs.selfExeDirPath(&exe_dir_buf) catch break :exe_dir;
2259 .{2299 var exe_dir = std.fs.openDirAbsolute(exe_dir_path, .{}) catch break :exe_dir;
2260 .root_dir = std.Build.Cache.Directory.cwd(),2300 defer exe_dir.close();
2261 .sub_path = separate_filename,2301
2262 },2302 // <exe_dir>/<gnu_debuglink>
2263 null,2303 if (loadPath(
2264 separate_debug_crc,2304 gpa,
2265 &sections,2305 .{
2266 mapped_mem,2306 .root_dir = .{ .path = null, .handle = exe_dir },
2267 )) |debug_info| {2307 .sub_path = separate_filename,
2268 return debug_info;2308 },
2269 } else |_| {}2309 null,
22702310 separate_debug_crc,
2271 // <cwd>/.debug/<gnu_debuglink>2311 &sections,
2272 {2312 mapped_mem,
2313 )) |debug_info| {
2314 return debug_info;
2315 } else |_| {}
2316
2317 // <exe_dir>/.debug/<gnu_debuglink>
2273 const path: Path = .{2318 const path: Path = .{
2274 .root_dir = std.Build.Cache.Directory.cwd(),2319 .root_dir = .{ .path = null, .handle = exe_dir },
2275 .sub_path = try std.fs.path.join(gpa, &.{ ".debug", separate_filename }),2320 .sub_path = try std.fs.path.join(gpa, &.{ ".debug", separate_filename }),
2276 };2321 };
2277 defer gpa.free(path.sub_path);2322 defer gpa.free(path.sub_path);
test/standalone/stack_iterator/build.zig+15
...@@ -93,6 +93,21 @@ pub fn build(b: *std.Build) void {...@@ -93,6 +93,21 @@ pub fn build(b: *std.Build) void {
9393
94 const run_cmd = b.addRunArtifact(exe);94 const run_cmd = b.addRunArtifact(exe);
95 test_step.dependOn(&run_cmd.step);95 test_step.dependOn(&run_cmd.step);
96
97 // Separate debug info ELF file
98 if (target.result.ofmt == .elf) {
99 const filename = b.fmt("{s}_stripped", .{exe.out_filename});
100 const stripped_exe = b.addObjCopy(exe.getEmittedBin(), .{
101 .basename = filename, // set the name for the debuglink
102 .compress_debug = true,
103 .strip = .debug,
104 .extract_to_separate_file = true,
105 });
106
107 const run_stripped = std.Build.Step.Run.create(b, b.fmt("run {s}", .{filename}));
108 run_stripped.addFileArg(stripped_exe.getOutput());
109 test_step.dependOn(&run_stripped.step);
110 }
96 }111 }
97112
98 // Unwinding without libc/posix113 // Unwinding without libc/posix