authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-19 02:06:17-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:16-04:00
log8e6a62ba10326e48eaefd40f89c9452d92f39c9d
tree9d0baca4c2209676996c3ce0a0530541f989ae51
parent6d87bb370a6d9075b2b6628f5f8c09171f25e4e9

test: disable omit_frame_pointer unwinding tests on aarch64-macos

dwarf: handle signal frame CIE flag

6 files changed, 119 insertions(+), 110 deletions(-)

lib/std/debug.zig+1-1
...@@ -882,7 +882,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz...@@ -882,7 +882,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz
882pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void {882pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void {
883 const module_name = debug_info.getModuleNameForAddress(address) orelse "???";883 const module_name = debug_info.getModuleNameForAddress(address) orelse "???";
884 try tty_config.setColor(out_stream, .dim);884 try tty_config.setColor(out_stream, .dim);
885 try out_stream.print("Unwind information for `{s}:{}` was not available ({}), trace may be incomplete\n\n", .{ module_name, address, err });885 try out_stream.print("Unwind information for `{s}:0x{x}` was not available ({}), trace may be incomplete\n\n", .{ module_name, address, err });
886 try tty_config.setColor(out_stream, .reset);886 try tty_config.setColor(out_stream, .reset);
887}887}
888888
lib/std/dwarf.zig+9-6
...@@ -1659,8 +1659,6 @@ pub const DwarfInfo = struct {...@@ -1659,8 +1659,6 @@ pub const DwarfInfo = struct {
1659 if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;1659 if (!comptime abi.isSupportedArch(builtin.target.cpu.arch)) return error.UnsupportedCpuArchitecture;
1660 if (context.pc == 0) return 0;1660 if (context.pc == 0) return 0;
16611661
1662 // TODO: Handle unwinding from a signal frame (ie. use_prev_instr in libunwind)
1663
1664 // Find the FDE and CIE1662 // Find the FDE and CIE
1665 var cie: CommonInformationEntry = undefined;1663 var cie: CommonInformationEntry = undefined;
1666 var fde: FrameDescriptionEntry = undefined;1664 var fde: FrameDescriptionEntry = undefined;
...@@ -1828,11 +1826,16 @@ pub const DwarfInfo = struct {...@@ -1828,11 +1826,16 @@ pub const DwarfInfo = struct {
18281826
1829 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc;1827 (try abi.regValueNative(usize, context.thread_context, abi.ipRegNum(), context.reg_context)).* = context.pc;
18301828
1831 // The call instruction will have pushed the address of the instruction that follows the call as the return address1829 // The call instruction will have pushed the address of the instruction that follows the call as the return address.
1832 // However, this return address may be past the end of the function if the caller was `noreturn`. By subtracting one,1830 // This next instruction may be past the end of the function if the caller was `noreturn` (ie. the last instruction in
1833 // then `context.pc` will always point to an instruction within the FDE for the previous function.1831 // the function was the call). If we were to look up an FDE entry using the return address directly, it could end up
1832 // either not finding an FDE at all, or using the next FDE in the program, producing incorrect results. To prevent this,
1833 // we subtract one so that the next lookup is guaranteed to land inside the
1834 //
1835 // The exception to this rule is signal frames, where we return execution would be returned to the instruction
1836 // that triggered the handler.
1834 const return_address = context.pc;1837 const return_address = context.pc;
1835 if (context.pc > 0) context.pc -= 1;1838 if (context.pc > 0 and !cie.isSignalFrame()) context.pc -= 1;
18361839
1837 return return_address;1840 return return_address;
1838 }1841 }
test/standalone/stack_iterator/build.zig+6-7
...@@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void {...@@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void {
7 const target = b.standardTargetOptions(.{});7 const target = b.standardTargetOptions(.{});
8 const optimize = b.standardOptimizeOption(.{});8 const optimize = b.standardOptimizeOption(.{});
99
10 // Unwinding pure zig code, with a frame pointer10 // Unwinding with a frame pointer
11 //11 //
12 // getcontext version: zig std12 // getcontext version: zig std
13 //13 //
...@@ -18,8 +18,8 @@ pub fn build(b: *std.Build) void {...@@ -18,8 +18,8 @@ pub fn build(b: *std.Build) void {
18 // - aarch64: FRAME, DWARF18 // - aarch64: FRAME, DWARF
19 {19 {
20 const exe = b.addExecutable(.{20 const exe = b.addExecutable(.{
21 .name = "zig_unwind_fp",21 .name = "unwind_fp",
22 .root_source_file = .{ .path = "zig_unwind.zig" },22 .root_source_file = .{ .path = "unwind.zig" },
23 .target = target,23 .target = target,
24 .optimize = optimize,24 .optimize = optimize,
25 });25 });
...@@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void {...@@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void {
31 test_step.dependOn(&run_cmd.step);31 test_step.dependOn(&run_cmd.step);
32 }32 }
3333
34 // Unwinding pure zig code, without a frame pointer.34 // Unwinding without a frame pointer
35 //35 //
36 // getcontext version: zig std36 // getcontext version: zig std
37 //37 //
...@@ -42,13 +42,12 @@ pub fn build(b: *std.Build) void {...@@ -42,13 +42,12 @@ pub fn build(b: *std.Build) void {
42 // - aarch64: FRAMELESS, DWARF42 // - aarch64: FRAMELESS, DWARF
43 {43 {
44 const exe = b.addExecutable(.{44 const exe = b.addExecutable(.{
45 .name = "zig_unwind_nofp",45 .name = "unwind_nofp",
46 .root_source_file = .{ .path = "zig_unwind.zig" },46 .root_source_file = .{ .path = "unwind.zig" },
47 .target = target,47 .target = target,
48 .optimize = optimize,48 .optimize = optimize,
49 });49 });
5050
51 if (target.isDarwin()) exe.unwind_tables = true;
52 exe.omit_frame_pointer = true;51 exe.omit_frame_pointer = true;
53 exe.unwind_tables = true;52 exe.unwind_tables = true;
5453
test/standalone/stack_iterator/shared_lib_unwind.zig+4
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const debug = std.debug;3const debug = std.debug;
3const testing = std.testing;4const testing = std.testing;
45
...@@ -34,6 +35,9 @@ extern fn frame0(...@@ -34,6 +35,9 @@ extern fn frame0(
34) void;35) void;
3536
36pub fn main() !void {37pub fn main() !void {
38 // Disabled until the DWARF unwinder bugs on .aarch64 are solved
39 if (builtin.omit_frame_pointer and comptime builtin.target.isDarwin() and builtin.cpu.arch == .aarch64) return;
40
37 if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;41 if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
3842
39 var expected: [5]usize = undefined;43 var expected: [5]usize = undefined;
test/standalone/stack_iterator/unwind.zig created+99
...@@ -0,0 +1,99 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const debug = std.debug;
4const testing = std.testing;
5
6noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
7 expected[0] = @returnAddress();
8
9 var context: debug.ThreadContext = undefined;
10 testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
11
12 var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
13 var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext");
14 defer it.deinit();
15
16 for (unwound) |*addr| {
17 if (it.next()) |return_address| addr.* = return_address;
18 }
19}
20
21noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
22 // Excercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored
23 if (builtin.target.ofmt != .c) {
24 switch (builtin.cpu.arch) {
25 .x86 => {
26 if (builtin.omit_frame_pointer) {
27 asm volatile (
28 \\movl $3, %%ebx
29 \\movl $1, %%ecx
30 \\movl $2, %%edx
31 \\movl $7, %%edi
32 \\movl $6, %%esi
33 \\movl $5, %%ebp
34 ::: "ebx", "ecx", "edx", "edi", "esi", "ebp");
35 } else {
36 asm volatile (
37 \\movl $3, %%ebx
38 \\movl $1, %%ecx
39 \\movl $2, %%edx
40 \\movl $7, %%edi
41 \\movl $6, %%esi
42 ::: "ebx", "ecx", "edx", "edi", "esi");
43 }
44 },
45 .x86_64 => {
46 if (builtin.omit_frame_pointer) {
47 asm volatile (
48 \\movq $3, %%rbx
49 \\movq $12, %%r12
50 \\movq $13, %%r13
51 \\movq $14, %%r14
52 \\movq $15, %%r15
53 \\movq $6, %%rbp
54 ::: "rbx", "r12", "r13", "r14", "r15", "rbp");
55 } else {
56 asm volatile (
57 \\movq $3, %%rbx
58 \\movq $12, %%r12
59 \\movq $13, %%r13
60 \\movq $14, %%r14
61 \\movq $15, %%r15
62 ::: "rbx", "r12", "r13", "r14", "r15");
63 }
64 },
65 else => {},
66 }
67 }
68
69 expected[1] = @returnAddress();
70 frame3(expected, unwound);
71}
72
73noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
74 expected[2] = @returnAddress();
75
76 // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
77 // to exercise the stack-indirect encoding path
78 var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
79 _ = pad;
80
81 frame2(expected, unwound);
82}
83
84noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {
85 expected[3] = @returnAddress();
86 frame1(expected, unwound);
87}
88
89pub fn main() !void {
90 // Disabled until the DWARF unwinder bugs on .aarch64 are solved
91 if (builtin.omit_frame_pointer and comptime builtin.target.isDarwin() and builtin.cpu.arch == .aarch64) return;
92
93 if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
94
95 var expected: [4]usize = undefined;
96 var unwound: [4]usize = undefined;
97 frame0(&expected, &unwound);
98 try testing.expectEqual(expected, unwound);
99}
test/standalone/stack_iterator/zig_unwind.zig deleted-96
...@@ -1,96 +0,0 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const debug = std.debug;
4const testing = std.testing;
5
6noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
7 expected[0] = @returnAddress();
8
9 var context: debug.ThreadContext = undefined;
10 testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
11
12 var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
13 var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext");
14 defer it.deinit();
15
16 for (unwound) |*addr| {
17 if (it.next()) |return_address| addr.* = return_address;
18 }
19}
20
21noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
22 // Excercise different __unwind_info / DWARF CFI encodings by forcing some registers to be restored
23 if (builtin.target.ofmt != .c) {
24 switch (builtin.cpu.arch) {
25 .x86 => {
26 if (builtin.omit_frame_pointer) {
27 asm volatile (
28 \\movl $3, %%ebx
29 \\movl $1, %%ecx
30 \\movl $2, %%edx
31 \\movl $7, %%edi
32 \\movl $6, %%esi
33 \\movl $5, %%ebp
34 ::: "ebx", "ecx", "edx", "edi", "esi", "ebp");
35 } else {
36 asm volatile (
37 \\movl $3, %%ebx
38 \\movl $1, %%ecx
39 \\movl $2, %%edx
40 \\movl $7, %%edi
41 \\movl $6, %%esi
42 ::: "ebx", "ecx", "edx", "edi", "esi");
43 }
44 },
45 .x86_64 => {
46 if (builtin.omit_frame_pointer) {
47 asm volatile (
48 \\movq $3, %%rbx
49 \\movq $12, %%r12
50 \\movq $13, %%r13
51 \\movq $14, %%r14
52 \\movq $15, %%r15
53 \\movq $6, %%rbp
54 ::: "rbx", "r12", "r13", "r14", "r15", "rbp");
55 } else {
56 asm volatile (
57 \\movq $3, %%rbx
58 \\movq $12, %%r12
59 \\movq $13, %%r13
60 \\movq $14, %%r14
61 \\movq $15, %%r15
62 ::: "rbx", "r12", "r13", "r14", "r15");
63 }
64 },
65 else => {},
66 }
67 }
68
69 expected[1] = @returnAddress();
70 frame3(expected, unwound);
71}
72
73noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
74 expected[2] = @returnAddress();
75
76 // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
77 // to exercise the stack-indirect encoding path
78 var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
79 _ = pad;
80
81 frame2(expected, unwound);
82}
83
84noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {
85 expected[3] = @returnAddress();
86 frame1(expected, unwound);
87}
88
89pub fn main() !void {
90 if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
91
92 var expected: [4]usize = undefined;
93 var unwound: [4]usize = undefined;
94 frame0(&expected, &unwound);
95 try testing.expectEqual(expected, unwound);
96}