commit ec96095efd8671ae280df15eaf73f63bf029fbfa (tree)
parent 7d8b4234774200ff071103399613ed444280a8d0
Author: kcbanner <kcbanner@gmail.com>
Date: Thu, 13 Jul 2023 01:14:31 -0400
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.
Diffstat:
9 files changed, 171 insertions(+), 133 deletions(-)
diff --git a/src/Compilation.zig b/src/Compilation.zig
@@ -5288,6 +5288,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
\\pub const position_independent_executable = {};
\\pub const strip_debug_info = {};
\\pub const code_model = std.builtin.CodeModel.{};
+ \\pub const omit_frame_pointer = {};
\\
, .{
std.zig.fmtId(@tagName(target.ofmt)),
@@ -5301,6 +5302,7 @@ pub fn generateBuiltinZigSource(comp: *Compilation, allocator: Allocator) Alloca
comp.bin_file.options.pie,
comp.bin_file.options.strip,
std.zig.fmtId(@tagName(comp.bin_file.options.machine_code_model)),
+ comp.bin_file.options.omit_frame_pointer,
});
if (target.os.tag == .wasi) {
diff --git 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.ofmt == .macho;
+ return target.os.tag == .windows or target.isDarwin();
}
pub fn defaultAddressSpace(
diff --git a/test/standalone.zig b/test/standalone.zig
@@ -231,8 +231,8 @@ pub const build_cases = [_]BuildCase{
.import = @import("standalone/zerolength_check/build.zig"),
},
.{
- .build_root = "test/standalone/dwarf_unwinding",
- .import = @import("standalone/dwarf_unwinding/build.zig"),
+ .build_root = "test/standalone/stack_iterator",
+ .import = @import("standalone/stack_iterator/build.zig"),
},
};
diff --git a/test/standalone/dwarf_unwinding/build.zig b/test/standalone/dwarf_unwinding/build.zig
@@ -1,54 +0,0 @@
-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(.{});
-
- // Test unwinding pure zig code (no libc)
- {
- const exe = b.addExecutable(.{
- .name = "zig_unwind",
- .root_source_file = .{ .path = "zig_unwind.zig" },
- .target = target,
- .optimize = optimize,
- });
-
- if (target.isDarwin()) exe.unwind_tables = true;
- exe.omit_frame_pointer = true;
-
- const run_cmd = b.addRunArtifact(exe);
- test_step.dependOn(&run_cmd.step);
- }
-
- // Test unwinding through a C shared library
- {
- const c_shared_lib = b.addSharedLibrary(.{
- .name = "c_shared_lib",
- .target = target,
- .optimize = optimize,
- });
-
- if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");
-
- c_shared_lib.strip = false;
- c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"});
- c_shared_lib.linkLibC();
-
- const exe = b.addExecutable(.{
- .name = "shared_lib_unwind",
- .root_source_file = .{ .path = "shared_lib_unwind.zig" },
- .target = target,
- .optimize = optimize,
- });
-
- if (target.isDarwin()) exe.unwind_tables = true;
- exe.omit_frame_pointer = true;
- exe.linkLibrary(c_shared_lib);
-
- const run_cmd = b.addRunArtifact(exe);
- test_step.dependOn(&run_cmd.step);
- }
-}
diff --git a/test/standalone/dwarf_unwinding/zig_unwind.zig b/test/standalone/dwarf_unwinding/zig_unwind.zig
@@ -1,76 +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 => {
- 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");
- },
- .x86_64 => {
- 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 => {},
- }
- }
-
- 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);
-}
diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig
@@ -0,0 +1,70 @@
+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(.{});
+
+ // Unwinding pure zig code, with a frame pointer
+ {
+ const exe = b.addExecutable(.{
+ .name = "zig_unwind_fp",
+ .root_source_file = .{ .path = "zig_unwind.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ if (target.isDarwin()) exe.unwind_tables = true;
+ exe.omit_frame_pointer = false;
+
+ const run_cmd = b.addRunArtifact(exe);
+ test_step.dependOn(&run_cmd.step);
+ }
+
+ // Unwinding pure zig code, without a frame pointer
+ {
+ const exe = b.addExecutable(.{
+ .name = "zig_unwind_nofp",
+ .root_source_file = .{ .path = "zig_unwind.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ if (target.isDarwin()) exe.unwind_tables = true;
+ exe.omit_frame_pointer = true;
+
+ const run_cmd = b.addRunArtifact(exe);
+ test_step.dependOn(&run_cmd.step);
+ }
+
+ // Unwinding through a C shared library without a frame pointer (libc)
+ {
+ const c_shared_lib = b.addSharedLibrary(.{
+ .name = "c_shared_lib",
+ .target = target,
+ .optimize = optimize,
+ });
+
+ if (target.isWindows()) c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)");
+
+ c_shared_lib.strip = false;
+ c_shared_lib.addCSourceFile("shared_lib.c", &.{"-fomit-frame-pointer"});
+ c_shared_lib.linkLibC();
+
+ const exe = b.addExecutable(.{
+ .name = "shared_lib_unwind",
+ .root_source_file = .{ .path = "shared_lib_unwind.zig" },
+ .target = target,
+ .optimize = optimize,
+ });
+
+ if (target.isDarwin()) exe.unwind_tables = true;
+ exe.omit_frame_pointer = true;
+ exe.linkLibrary(c_shared_lib);
+
+ const run_cmd = b.addRunArtifact(exe);
+ test_step.dependOn(&run_cmd.step);
+ }
+}
diff --git a/test/standalone/dwarf_unwinding/shared_lib.c b/test/standalone/stack_iterator/shared_lib.c
diff --git a/test/standalone/dwarf_unwinding/shared_lib_unwind.zig b/test/standalone/stack_iterator/shared_lib_unwind.zig
diff --git a/test/standalone/stack_iterator/zig_unwind.zig b/test/standalone/stack_iterator/zig_unwind.zig
@@ -0,0 +1,96 @@
+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);
+}