authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-13 01:14:31-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:16-04:00
logec96095efd8671ae280df15eaf73f63bf029fbfa
tree06308ef842ad3f6984f673d607fabebcbed28a7b
parent7d8b4234774200ff071103399613ed444280a8d0

compilation: pass omit_frame_pointer through to builtin.zig

Renamed dwarf_unwinding -> stack_iterator to better reflect that it's not just DWARF unwinding. Added a test for unwinding with a frame pointer.

11 files changed, 236 insertions(+), 198 deletions(-)

src/Compilation.zig+2
...@@ -5288,6 +5288,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca...@@ -5288,6 +5288,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
5288 \\pub const position_independent_executable = {};5288 \\pub const position_independent_executable = {};
5289 \\pub const strip_debug_info = {};5289 \\pub const strip_debug_info = {};
5290 \\pub const code_model = std.builtin.CodeModel.{};5290 \\pub const code_model = std.builtin.CodeModel.{};
5291 \\pub const omit_frame_pointer = {};
5291 \\5292 \\
5292 , .{5293 , .{
5293 std.zig.fmtId(@tagName(target.ofmt)),5294 std.zig.fmtId(@tagName(target.ofmt)),
...@@ -5301,6 +5302,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca...@@ -5301,6 +5302,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
5301 comp.bin_file.options.pie,5302 comp.bin_file.options.pie,
5302 comp.bin_file.options.strip,5303 comp.bin_file.options.strip,
5303 std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),5304 std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),
5305 comp.bin_file.options.omit_frame_pointer,
5304 });5306 });
53055307
5306 if (target.os.tag == .wasi) {5308 if (target.os.tag == .wasi) {
src/target.zig+1-1
...@@ -510,7 +510,7 @@ pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool {...@@ -510,7 +510,7 @@ pub fn clangAssemblerSupportsMcpuArg(target: std.Target) bool {
510}510}
511511
512pub fn needUnwindTables(target: std.Target) bool {512pub fn needUnwindTables(target: std.Target) bool {
513 return target.os.tag == .windows or target.ofmt == .macho;513 return target.os.tag == .windows or target.isDarwin();
514}514}
515515
516pub fn defaultAddressSpace(516pub fn defaultAddressSpace(
test/standalone.zig+2-2
...@@ -231,8 +231,8 @@ pub const build_cases = [_]BuildCase{...@@ -231,8 +231,8 @@ pub const build_cases = [_]BuildCase{
231 .import = @import("standalone/zerolength_check/build.zig"),231 .import = @import("standalone/zerolength_check/build.zig"),
232 },232 },
233 .{233 .{
234 .build_root = "test/standalone/dwarf_unwinding",234 .build_root = "test/standalone/stack_iterator",
235 .import = @import("standalone/dwarf_unwinding/build.zig"),235 .import = @import("standalone/stack_iterator/build.zig"),
236 },236 },
237};237};
238238
test/standalone/dwarf_unwinding/build.zig deleted-54
...@@ -1,54 +0,0 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const target = b.standardTargetOptions(.{});
8 const optimize = b.standardOptimizeOption(.{});
9
10 // Test unwinding pure zig code (no libc)
11 {
12 const exe = b.addExecutable(.{
13 .name = "zig_unwind",
14 .root_source_file = .{ .path = "zig_unwind.zig" },
15 .target = target,
16 .optimize = optimize,
17 });
18
19 if (target.isDarwin()) exe.unwind_tables = true;
20 exe.omit_frame_pointer = true;
21
22 const run_cmd = b.addRunArtifact(exe);
23 test_step.dependOn(&run_cmd.step);
24 }
25
26 // Test unwinding through a C shared library
27 {
28 const c_shared_lib = b.addSharedLibrary(.{
29 .name = "c_shared_lib",
30 .target = target,
31 .optimize = optimize,
32 });
33
34 if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");
35
36 c_shared_lib.strip = false;
37 c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"});
38 c_shared_lib.linkLibC();
39
40 const exe = b.addExecutable(.{
41 .name = "shared_lib_unwind",
42 .root_source_file = .{ .path = "shared_lib_unwind.zig" },
43 .target = target,
44 .optimize = optimize,
45 });
46
47 if (target.isDarwin()) exe.unwind_tables = true;
48 exe.omit_frame_pointer = true;
49 exe.linkLibrary(c_shared_lib);
50
51 const run_cmd = b.addRunArtifact(exe);
52 test_step.dependOn(&run_cmd.step);
53 }
54}
test/standalone/dwarf_unwinding/shared_lib.c deleted-22
...@@ -1,22 +0,0 @@
1#include <stdint.h>
2
3#ifndef LIB_API
4#define LIB_API
5#endif
6
7__attribute__((noinline)) void frame1(
8 void** expected,
9 void** unwound,
10 void (*frame2)(void** expected, void** unwound)) {
11 expected[3] = __builtin_extract_return_addr(__builtin_return_address(0));
12 frame2(expected, unwound);
13}
14
15LIB_API void frame0(
16 void** expected,
17 void** unwound,
18 void (*frame2)(void** expected, void** unwound)) {
19 expected[4] = __builtin_extract_return_addr(__builtin_return_address(0));
20 frame1(expected, unwound, frame2);
21}
22
test/standalone/dwarf_unwinding/shared_lib_unwind.zig deleted-43
...@@ -1,43 +0,0 @@
1const std = @import("std");
2const debug = std.debug;
3const testing = std.testing;
4
5noinline fn frame4(expected: *[5]usize, unwound: *[5]usize) void {
6 expected[0] = @returnAddress();
7
8 var context: debug.ThreadContext = undefined;
9 testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
10
11 var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
12 var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext");
13 defer it.deinit();
14
15 for (unwound) |*addr| {
16 if (it.next()) |return_address| addr.* = return_address;
17 }
18}
19
20noinline fn frame3(expected: *[5]usize, unwound: *[5]usize) void {
21 expected[1] = @returnAddress();
22 frame4(expected, unwound);
23}
24
25fn frame2(expected: *[5]usize, unwound: *[5]usize) callconv(.C) void {
26 expected[2] = @returnAddress();
27 frame3(expected, unwound);
28}
29
30extern fn frame0(
31 expected: *[5]usize,
32 unwound: *[5]usize,
33 frame_2: *const fn (expected: *[5]usize, unwound: *[5]usize) callconv(.C) void,
34) void;
35
36pub fn main() !void {
37 if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
38
39 var expected: [5]usize = undefined;
40 var unwound: [5]usize = undefined;
41 frame0(&expected, &unwound, &frame2);
42 try testing.expectEqual(expected, unwound);
43}
test/standalone/dwarf_unwinding/zig_unwind.zig deleted-76
...@@ -1,76 +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 asm volatile (
27 \\movl $3, %%ebx
28 \\movl $1, %%ecx
29 \\movl $2, %%edx
30 \\movl $7, %%edi
31 \\movl $6, %%esi
32 \\movl $5, %%ebp
33 ::: "ebx", "ecx", "edx", "edi", "esi", "ebp");
34 },
35 .x86_64 => {
36 asm volatile (
37 \\movq $3, %%rbx
38 \\movq $12, %%r12
39 \\movq $13, %%r13
40 \\movq $14, %%r14
41 \\movq $15, %%r15
42 \\movq $6, %%rbp
43 ::: "rbx", "r12", "r13", "r14", "r15", "rbp");
44 },
45 else => {},
46 }
47 }
48
49 expected[1] = @returnAddress();
50 frame3(expected, unwound);
51}
52
53noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void {
54 expected[2] = @returnAddress();
55
56 // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding
57 // to exercise the stack-indirect encoding path
58 var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined;
59 _ = pad;
60
61 frame2(expected, unwound);
62}
63
64noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {
65 expected[3] = @returnAddress();
66 frame1(expected, unwound);
67}
68
69pub fn main() !void {
70 if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
71
72 var expected: [4]usize = undefined;
73 var unwound: [4]usize = undefined;
74 frame0(&expected, &unwound);
75 try testing.expectEqual(expected, unwound);
76}
test/standalone/stack_iterator/build.zig created+70
...@@ -0,0 +1,70 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const test_step = b.step("test", "Test it");
5 b.default_step = test_step;
6
7 const target = b.standardTargetOptions(.{});
8 const optimize = b.standardOptimizeOption(.{});
9
10 // Unwinding pure zig code, with a frame pointer
11 {
12 const exe = b.addExecutable(.{
13 .name = "zig_unwind_fp",
14 .root_source_file = .{ .path = "zig_unwind.zig" },
15 .target = target,
16 .optimize = optimize,
17 });
18
19 if (target.isDarwin()) exe.unwind_tables = true;
20 exe.omit_frame_pointer = false;
21
22 const run_cmd = b.addRunArtifact(exe);
23 test_step.dependOn(&run_cmd.step);
24 }
25
26 // Unwinding pure zig code, without a frame pointer
27 {
28 const exe = b.addExecutable(.{
29 .name = "zig_unwind_nofp",
30 .root_source_file = .{ .path = "zig_unwind.zig" },
31 .target = target,
32 .optimize = optimize,
33 });
34
35 if (target.isDarwin()) exe.unwind_tables = true;
36 exe.omit_frame_pointer = true;
37
38 const run_cmd = b.addRunArtifact(exe);
39 test_step.dependOn(&run_cmd.step);
40 }
41
42 // Unwinding through a C shared library without a frame pointer (libc)
43 {
44 const c_shared_lib = b.addSharedLibrary(.{
45 .name = "c_shared_lib",
46 .target = target,
47 .optimize = optimize,
48 });
49
50 if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");
51
52 c_shared_lib.strip = false;
53 c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"});
54 c_shared_lib.linkLibC();
55
56 const exe = b.addExecutable(.{
57 .name = "shared_lib_unwind",
58 .root_source_file = .{ .path = "shared_lib_unwind.zig" },
59 .target = target,
60 .optimize = optimize,
61 });
62
63 if (target.isDarwin()) exe.unwind_tables = true;
64 exe.omit_frame_pointer = true;
65 exe.linkLibrary(c_shared_lib);
66
67 const run_cmd = b.addRunArtifact(exe);
68 test_step.dependOn(&run_cmd.step);
69 }
70}
test/standalone/stack_iterator/shared_lib.c created+22
...@@ -0,0 +1,22 @@
1#include <stdint.h>
2
3#ifndef LIB_API
4#define LIB_API
5#endif
6
7__attribute__((noinline)) void frame1(
8 void** expected,
9 void** unwound,
10 void (*frame2)(void** expected, void** unwound)) {
11 expected[3] = __builtin_extract_return_addr(__builtin_return_address(0));
12 frame2(expected, unwound);
13}
14
15LIB_API void frame0(
16 void** expected,
17 void** unwound,
18 void (*frame2)(void** expected, void** unwound)) {
19 expected[4] = __builtin_extract_return_addr(__builtin_return_address(0));
20 frame1(expected, unwound, frame2);
21}
22
test/standalone/stack_iterator/shared_lib_unwind.zig created+43
...@@ -0,0 +1,43 @@
1const std = @import("std");
2const debug = std.debug;
3const testing = std.testing;
4
5noinline fn frame4(expected: *[5]usize, unwound: *[5]usize) void {
6 expected[0] = @returnAddress();
7
8 var context: debug.ThreadContext = undefined;
9 testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
10
11 var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
12 var it = debug.StackIterator.initWithContext(expected[0], debug_info, &context) catch @panic("failed to initWithContext");
13 defer it.deinit();
14
15 for (unwound) |*addr| {
16 if (it.next()) |return_address| addr.* = return_address;
17 }
18}
19
20noinline fn frame3(expected: *[5]usize, unwound: *[5]usize) void {
21 expected[1] = @returnAddress();
22 frame4(expected, unwound);
23}
24
25fn frame2(expected: *[5]usize, unwound: *[5]usize) callconv(.C) void {
26 expected[2] = @returnAddress();
27 frame3(expected, unwound);
28}
29
30extern fn frame0(
31 expected: *[5]usize,
32 unwound: *[5]usize,
33 frame_2: *const fn (expected: *[5]usize, unwound: *[5]usize) callconv(.C) void,
34) void;
35
36pub fn main() !void {
37 if (!std.debug.have_ucontext or !std.debug.have_getcontext) return;
38
39 var expected: [5]usize = undefined;
40 var unwound: [5]usize = undefined;
41 frame0(&expected, &unwound, &frame2);
42 try testing.expectEqual(expected, unwound);
43}
test/standalone/stack_iterator/zig_unwind.zig created+96
...@@ -0,0 +1,96 @@
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}