commit 8e6a62ba10326e48eaefd40f89c9452d92f39c9d (tree)
parent 6d87bb370a6d9075b2b6628f5f8c09171f25e4e9
Author: kcbanner <kcbanner@gmail.com>
Date: Wed, 19 Jul 2023 02:06:17 -0400
test: disable omit_frame_pointer unwinding tests on aarch64-macos
dwarf: handle signal frame CIE flag
Diffstat:
6 files changed, 119 insertions(+), 110 deletions(-)
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
@@ -882,7 +882,7 @@ 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}:{}` was not available ({}), trace may be incomplete\n\n", .{ module_name, address, err });
+ try out_stream.print("Unwind information for `{s}:0x{x}` was not available ({}), trace may be incomplete\n\n", .{ module_name, address, err });
try tty_config.setColor(out_stream, .reset);
}
diff --git a/lib/std/dwarf.zig b/lib/std/dwarf.zig
@@ -1659,8 +1659,6 @@ pub const DwarfInfo = struct {
if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;
if (context.pc == 0) return 0;
- // TODO: Handle unwinding from a signal frame (ie. use_prev_instr in libunwind)
-
// Find the FDE and CIE
var cie: CommonInformationEntry = undefined;
var fde: FrameDescriptionEntry = undefined;
@@ -1828,11 +1826,16 @@ pub const DwarfInfo = struct {
(try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc;
- // The call instruction will have pushed the address of the instruction that follows the call as the return address
- // However, this return address may be past the end of the function if the caller was `noreturn`. By subtracting one,
- // then `context.pc` will always point to an instruction within the FDE for the previous function.
+ // The call instruction will have pushed the address of the instruction that follows the call as the return address.
+ // This next instruction may be past the end of the function if the caller was `noreturn` (ie. the last instruction in
+ // the function was the call). If we were to look up an FDE entry using the return address directly, it could end up
+ // either not finding an FDE at all, or using the next FDE in the program, producing incorrect results. To prevent this,
+ // we subtract one so that the next lookup is guaranteed to land inside the
+ //
+ // The exception to this rule is signal frames, where we return execution would be returned to the instruction
+ // that triggered the handler.
const return_address = context.pc;
- if (context.pc > 0) context.pc -= 1;
+ if (context.pc > 0 and !cie.isSignalFrame()) context.pc -= 1;
return return_address;
}
diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig
@@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
- // Unwinding pure zig code, with a frame pointer
+ // Unwinding with a frame pointer
//
// getcontext version: zig std
//
@@ -18,8 +18,8 @@ pub fn build(b: *std.Build) void {
// - aarch64: FRAME, DWARF
{
const exe = b.addExecutable(.{
- .name = "zig_unwind_fp",
- .root_source_file = .{ .path = "zig_unwind.zig" },
+ .name = "unwind_fp",
+ .root_source_file = .{ .path = "unwind.zig" },
.target = target,
.optimize = optimize,
});
@@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void {
test_step.dependOn(&run_cmd.step);
}
- // Unwinding pure zig code, without a frame pointer.
+ // Unwinding without a frame pointer
//
// getcontext version: zig std
//
@@ -42,13 +42,12 @@ pub fn build(b: *std.Build) void {
// - aarch64: FRAMELESS, DWARF
{
const exe = b.addExecutable(.{
- .name = "zig_unwind_nofp",
- .root_source_file = .{ .path = "zig_unwind.zig" },
+ .name = "unwind_nofp",
+ .root_source_file = .{ .path = "unwind.zig" },
.target = target,
.optimize = optimize,
});
- if (target.isDarwin()) exe.unwind_tables = true;
exe.omit_frame_pointer = true;
exe.unwind_tables = true;
diff --git a/test/standalone/stack_iterator/shared_lib_unwind.zig b/test/standalone/stack_iterator/shared_lib_unwind.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const builtin = @import("builtin");
const debug = std.debug;
const testing = std.testing;
@@ -34,6 +35,9 @@ extern fn frame0(
) void;
pub fn main() !void {
+ // Disabled until the DWARF unwinder bugs on .aarch64 are solved
+ if (builtin.omit_frame_pointer and comptime builtin.target.isDarwin() and builtin.cpu.arch == .aarch64) return;
+
if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
var expected: [5]usize = undefined;
diff --git a/test/standalone/stack_iterator/unwind.zig b/test/standalone/stack_iterator/unwind.zig
@@ -0,0 +1,99 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const debug = std.debug;
+const testing = std.testing;
+
+noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
+ expected[0] = @returnAddress();
+
+ var context: debug.ThreadContext = undefined;
+ testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
+
+ var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
+ var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext");
+ defer it.deinit();
+
+ for (unwound) |*addr| {
+ if (it.next()) |return_address| addr.* = return_address;
+ }
+}
+
+noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
+ // Excercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored
+ if (builtin.target.ofmt != .c) {
+ switch (builtin.cpu.arch) {
+ .x86 => {
+ if (builtin.omit_frame_pointer) {
+ asm volatile (
+ \\movl $3, %%ebx
+ \\movl $1, %%ecx
+ \\movl $2, %%edx
+ \\movl $7, %%edi
+ \\movl $6, %%esi
+ \\movl $5, %%ebp
+ ::: "ebx", "ecx", "edx", "edi", "esi", "ebp");
+ } else {
+ asm volatile (
+ \\movl $3, %%ebx
+ \\movl $1, %%ecx
+ \\movl $2, %%edx
+ \\movl $7, %%edi
+ \\movl $6, %%esi
+ ::: "ebx", "ecx", "edx", "edi", "esi");
+ }
+ },
+ .x86_64 => {
+ if (builtin.omit_frame_pointer) {
+ asm volatile (
+ \\movq $3, %%rbx
+ \\movq $12, %%r12
+ \\movq $13, %%r13
+ \\movq $14, %%r14
+ \\movq $15, %%r15
+ \\movq $6, %%rbp
+ ::: "rbx", "r12", "r13", "r14", "r15", "rbp");
+ } else {
+ asm volatile (
+ \\movq $3, %%rbx
+ \\movq $12, %%r12
+ \\movq $13, %%r13
+ \\movq $14, %%r14
+ \\movq $15, %%r15
+ ::: "rbx", "r12", "r13", "r14", "r15");
+ }
+ },
+ else => {},
+ }
+ }
+
+ expected[1] = @returnAddress();
+ frame3(expected, unwound);
+}
+
+noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
+ expected[2] = @returnAddress();
+
+ // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
+ // to exercise the stack-indirect encoding path
+ var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
+ _ = pad;
+
+ frame2(expected, unwound);
+}
+
+noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {
+ expected[3] = @returnAddress();
+ frame1(expected, unwound);
+}
+
+pub fn main() !void {
+ // Disabled until the DWARF unwinder bugs on .aarch64 are solved
+ if (builtin.omit_frame_pointer and comptime builtin.target.isDarwin() and builtin.cpu.arch == .aarch64) return;
+
+ if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
+
+ var expected: [4]usize = undefined;
+ var unwound: [4]usize = undefined;
+ frame0(&expected, &unwound);
+ try testing.expectEqual(expected, unwound);
+}
diff --git a/test/standalone/stack_iterator/zig_unwind.zig b/test/standalone/stack_iterator/zig_unwind.zig
@@ -1,96 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-const debug = std.debug;
-const testing = std.testing;
-
-noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
- expected[0] = @returnAddress();
-
- var context: debug.ThreadContext = undefined;
- testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
-
- var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
- var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext");
- defer it.deinit();
-
- for (unwound) |*addr| {
- if (it.next()) |return_address| addr.* = return_address;
- }
-}
-
-noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
- // Excercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored
- if (builtin.target.ofmt != .c) {
- switch (builtin.cpu.arch) {
- .x86 => {
- if (builtin.omit_frame_pointer) {
- asm volatile (
- \\movl $3, %%ebx
- \\movl $1, %%ecx
- \\movl $2, %%edx
- \\movl $7, %%edi
- \\movl $6, %%esi
- \\movl $5, %%ebp
- ::: "ebx", "ecx", "edx", "edi", "esi", "ebp");
- } else {
- asm volatile (
- \\movl $3, %%ebx
- \\movl $1, %%ecx
- \\movl $2, %%edx
- \\movl $7, %%edi
- \\movl $6, %%esi
- ::: "ebx", "ecx", "edx", "edi", "esi");
- }
- },
- .x86_64 => {
- if (builtin.omit_frame_pointer) {
- asm volatile (
- \\movq $3, %%rbx
- \\movq $12, %%r12
- \\movq $13, %%r13
- \\movq $14, %%r14
- \\movq $15, %%r15
- \\movq $6, %%rbp
- ::: "rbx", "r12", "r13", "r14", "r15", "rbp");
- } else {
- asm volatile (
- \\movq $3, %%rbx
- \\movq $12, %%r12
- \\movq $13, %%r13
- \\movq $14, %%r14
- \\movq $15, %%r15
- ::: "rbx", "r12", "r13", "r14", "r15");
- }
- },
- else => {},
- }
- }
-
- expected[1] = @returnAddress();
- frame3(expected, unwound);
-}
-
-noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
- expected[2] = @returnAddress();
-
- // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
- // to exercise the stack-indirect encoding path
- var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
- _ = pad;
-
- frame2(expected, unwound);
-}
-
-noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {
- expected[3] = @returnAddress();
- frame1(expected, unwound);
-}
-
-pub fn main() !void {
- if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
-
- var expected: [4]usize = undefined;
- var unwound: [4]usize = undefined;
- frame0(&expected, &unwound);
- try testing.expectEqual(expected, unwound);
-}