From a84826115fa06b8f2da2b3cf0f182c4b75706e2c Mon Sep 17 00:00:00 2001 From: kcbanner Date: Sun, 23 Jul 2023 14:06:35 -0400 Subject: [PATCH 1/5] debug: print unwind errors if they occur on the first iteration, and differentiate between missing info and an actual unwind error in the message --- lib/std/debug.zig | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 948a76c2df..05657a2fbf 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -252,6 +252,9 @@ pub fn dumpStackTraceFromBase(context: *const ThreadContext) void { // same behaviour for x86-windows-msvc const address = if (return_address == 0) return_address else return_address - 1; printSourceAtAddress(debug_info, stderr, address, tty_config) catch return; + } else { + if (it.getLastError()) |unwind_error| + printUnwindError(debug_info, stderr, unwind_error.address, unwind_error.err, tty_config) catch {}; } } } @@ -741,6 +744,9 @@ pub fn writeCurrentStackTrace( // same behaviour for x86-windows-msvc const address = if (return_address == 0) return_address else return_address - 1; try printSourceAtAddress(debug_info, out_stream, address, tty_config); + } else { + if (it.getLastError()) |unwind_error| + try printUnwindError(debug_info, out_stream, unwind_error.address, unwind_error.err, tty_config); } } @@ -882,7 +888,11 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void { const module_name = debug_info.getModuleNameForAddress(address) orelse "???"; try tty_config.setColor(out_stream, .dim); - try out_stream.print("Unwind information for `{s}:0x{x}` was not available ({}), trace may be incomplete\n\n", .{ module_name, address, err }); + if (err == error.MissingDebugInfo) { + try out_stream.print("Unwind information for `{s}:0x{x}` was not available, trace may be incomplete\n\n", .{ module_name, address }); + } else { + try out_stream.print("Unwind error at address `{s}:0x{x}` ({}), trace may be incomplete\n\n", .{ module_name, address, err }); + } try tty_config.setColor(out_stream, .reset); } From f2aab12a8627b3afd1a4d9c38157968b2f012e0c Mon Sep 17 00:00:00 2001 From: kcbanner Date: Sun, 23 Jul 2023 14:07:14 -0400 Subject: [PATCH 2/5] compilation: change compiler_rt to inherit `strip` and `want_unwind_tables` from the parent compilation --- src/Compilation.zig | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 4019e43c8d..2e269a7e80 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -5491,6 +5491,7 @@ fn buildOutputFromZig( .omit_frame_pointer = comp.bin_file.options.omit_frame_pointer, .want_valgrind = false, .want_tsan = false, + .want_unwind_tables = comp.bin_file.options.eh_frame_hdr, .want_pic = comp.bin_file.options.pic, .want_pie = comp.bin_file.options.pie, .emit_h = null, @@ -5639,9 +5640,5 @@ pub fn compilerRtOptMode(comp: Compilation) std.builtin.Mode { /// This decides whether to strip debug info for all zig-provided libraries, including /// compiler-rt, libcxx, libc, libunwind, etc. pub fn compilerRtStrip(comp: Compilation) bool { - if (comp.debug_compiler_runtime_libs) { - return comp.bin_file.options.strip; - } else { - return true; - } + return comp.bin_file.options.strip; } From 661028a907a66ad94b6c052286a59da3bcd6f287 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Wed, 26 Jul 2023 01:26:26 -0400 Subject: [PATCH 3/5] target: emit unwind tables if the platform supports unwinding --- lib/std/dwarf.zig | 2 +- lib/std/dwarf/abi.zig | 25 ++++++++++++++++++------- src/target.zig | 2 +- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig index 58db4a1698..ac2e5c813c 100644 --- a/lib/std/dwarf.zig +++ b/lib/std/dwarf.zig @@ -1656,7 +1656,7 @@ pub const DwarfInfo = struct { /// `explicit_fde_offset` is for cases where the FDE offset is known, such as when __unwind_info /// defers unwinding to DWARF. This is an offset into the `.eh_frame` section. pub fn unwindFrame(di: *const DwarfInfo, context: *UnwindContext, explicit_fde_offset: ?usize) !usize { - if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture; + if (!comptime abi.supportsUnwinding(builtin.target)) return error.UnsupportedCpuArchitecture; if (context.pc == 0) return 0; // Find the FDE and CIE diff --git a/lib/std/dwarf/abi.zig b/lib/std/dwarf/abi.zig index 7f349d97ad..6fbd1fba41 100644 --- a/lib/std/dwarf/abi.zig +++ b/lib/std/dwarf/abi.zig @@ -3,13 +3,24 @@ const std = @import("../std.zig"); const os = std.os; const mem = std.mem; -pub fn isSupportedArch(arch: std.Target.Cpu.Arch) bool { - return switch (arch) { - .x86, - .x86_64, - .arm, - .aarch64, - => true, +pub fn supportsUnwinding(target: std.Target) bool { + return switch (target.cpu.arch) { + .x86 => switch (target.os.tag) { + .linux, .netbsd, .solaris => true, + else => false, + }, + .x86_64 => switch (target.os.tag) { + .linux, .netbsd, .freebsd, .openbsd, .macos, .solaris => true, + else => false, + }, + .arm => switch (target.os.tag) { + .linux => true, + else => false, + }, + .aarch64 => switch (target.os.tag) { + .linux, .netbsd, .freebsd, .macos => true, + else => false, + }, else => false, }; } diff --git a/src/target.zig b/src/target.zig index 030cad6bdc..3076a0b0da 100644 --- a/src/target.zig +++ b/src/target.zig @@ -510,7 +510,7 @@ pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool { } pub fn needUnwindTables(target: std.Target) bool { - return target.os.tag == .windows or target.isDarwin(); + return target.os.tag == .windows or target.isDarwin() or std.dwarf.abi.supportsUnwinding(target); } pub fn defaultAddressSpace( From 78449b6d980fc78d418f9b44d815781eda7587f7 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Wed, 26 Jul 2023 22:10:22 -0400 Subject: [PATCH 4/5] debug: skip unwind error printing on platforms that don't have_ucontext --- lib/std/debug.zig | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 05657a2fbf..ddd0f0ec95 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -242,8 +242,7 @@ pub fn dumpStackTraceFromBase(context: *const ThreadContext) void { printSourceAtAddress(debug_info, stderr, it.unwind_state.?.dwarf_context.pc, tty_config) catch return; while (it.next()) |return_address| { - if (it.getLastError()) |unwind_error| - printUnwindError(debug_info, stderr, unwind_error.address, unwind_error.err, tty_config) catch {}; + printLastUnwindError(&it, debug_info, stderr, tty_config); // On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS, // therefore, we do a check for `return_address == 0` before subtracting 1 from it to avoid @@ -252,10 +251,7 @@ pub fn dumpStackTraceFromBase(context: *const ThreadContext) void { // same behaviour for x86-windows-msvc const address = if (return_address == 0) return_address else return_address - 1; printSourceAtAddress(debug_info, stderr, address, tty_config) catch return; - } else { - if (it.getLastError()) |unwind_error| - printUnwindError(debug_info, stderr, unwind_error.address, unwind_error.err, tty_config) catch {}; - } + } else printLastUnwindError(&it, debug_info, stderr, tty_config); } } @@ -734,8 +730,7 @@ pub fn writeCurrentStackTrace( defer it.deinit(); while (it.next()) |return_address| { - if (it.getLastError()) |unwind_error| - try printUnwindError(debug_info, out_stream, unwind_error.address, unwind_error.err, tty_config); + printLastUnwindError(&it, debug_info, out_stream, tty_config); // On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS, // therefore, we do a check for `return_address == 0` before subtracting 1 from it to avoid @@ -744,10 +739,7 @@ pub fn writeCurrentStackTrace( // same behaviour for x86-windows-msvc const address = if (return_address == 0) return_address else return_address - 1; try printSourceAtAddress(debug_info, out_stream, address, tty_config); - } else { - if (it.getLastError()) |unwind_error| - try printUnwindError(debug_info, out_stream, unwind_error.address, unwind_error.err, tty_config); - } + } else printLastUnwindError(&it, debug_info, out_stream, tty_config); } pub noinline fn walkStackWindows(addresses: []usize, existing_context: ?*const windows.CONTEXT) usize { @@ -885,7 +877,14 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz ); } -pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void { +fn printLastUnwindError(it: *StackIterator, debug_info: *DebugInfo, out_stream: anytype, tty_config: io.tty.Config) void { + if (!have_ucontext) return; + if (it.getLastError()) |unwind_error| { + printUnwindError(debug_info, out_stream, unwind_error.address, unwind_error.err, tty_config) catch {}; + } +} + +fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void { const module_name = debug_info.getModuleNameForAddress(address) orelse "???"; try tty_config.setColor(out_stream, .dim); if (err == error.MissingDebugInfo) { From 8b9627f01d44b94fad744f6d515dc32438130628 Mon Sep 17 00:00:00 2001 From: kcbanner Date: Thu, 27 Jul 2023 02:53:40 -0400 Subject: [PATCH 5/5] test: add a test that verifies no debug handlers get pulled into compiler_rt build: fix CheckObject checkNotPresent only checking a single line of the haystack --- lib/std/Build/Step/CheckObject.zig | 3 +-- test/standalone.zig | 4 ++++ test/standalone/compiler_rt_panic/build.zig | 26 +++++++++++++++++++++ test/standalone/compiler_rt_panic/main.c | 11 +++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 test/standalone/compiler_rt_panic/build.zig create mode 100644 test/standalone/compiler_rt_panic/main.c diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index 5a816b4103..e79468ec9b 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -478,8 +478,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }, .not_present => { while (it.next()) |line| { - if (act.notPresent(b, step, line)) break; - } else { + if (act.notPresent(b, step, line)) continue; return step.fail( \\ \\========= expected not to find: =================== diff --git a/test/standalone.zig b/test/standalone.zig index 7cbe452934..3725456fa1 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -241,6 +241,10 @@ pub const build_cases = [_]BuildCase{ .build_root = "test/standalone/coff_dwarf", .import = @import("standalone/coff_dwarf/build.zig"), }, + .{ + .build_root = "test/standalone/compiler_rt_panic", + .import = @import("standalone/compiler_rt_panic/build.zig"), + }, }; const std = @import("std"); diff --git a/test/standalone/compiler_rt_panic/build.zig b/test/standalone/compiler_rt_panic/build.zig new file mode 100644 index 0000000000..9bfe7f73fa --- /dev/null +++ b/test/standalone/compiler_rt_panic/build.zig @@ -0,0 +1,26 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + if (target.getObjectFormat() != .elf) return; + + const exe = b.addExecutable(.{ + .name = "main", + .optimize = optimize, + .target = target, + }); + exe.addCSourceFile("main.c", &.{}); + exe.link_gc_sections = false; + exe.bundle_compiler_rt = true; + + // Verify compiler_rt hasn't pulled in any debug handlers + const check_exe = exe.checkObject(); + check_exe.checkInSymtab(); + check_exe.checkNotPresent("debug.readElfDebugInfo"); + test_step.dependOn(&check_exe.step); +} diff --git a/test/standalone/compiler_rt_panic/main.c b/test/standalone/compiler_rt_panic/main.c new file mode 100644 index 0000000000..be64216ab7 --- /dev/null +++ b/test/standalone/compiler_rt_panic/main.c @@ -0,0 +1,11 @@ +#include + +void* __memset(void* dest, char c, size_t n, size_t dest_n); + +char foo[128]; + +int main() { + __memset(&foo[0], 0xff, 128, 128); + return foo[64]; +} +