Commit e8503ecb65
Changed files (3)
lib
std
test
standalone
stack_iterator
lib/std/posix.zig
@@ -45,7 +45,9 @@ pub const system = if (use_libc)
else switch (native_os) {
.linux => linux,
.plan9 => std.os.plan9,
- else => struct {},
+ else => struct {
+ pub const ucontext_t = void;
+ },
};
pub const AF = system.AF;
test/standalone/stack_iterator/build.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const builtin = @import("builtin");
pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
@@ -93,4 +94,30 @@ pub fn build(b: *std.Build) void {
const run_cmd = b.addRunArtifact(exe);
test_step.dependOn(&run_cmd.step);
}
+
+ // Unwinding without libc/posix
+ //
+ // No "getcontext" or "ucontext_t"
+ {
+ const exe = b.addExecutable(.{
+ .name = "unwind_freestanding",
+ .root_source_file = b.path("unwind_freestanding.zig"),
+ .target = b.resolveTargetQuery(.{
+ .cpu_arch = .x86_64,
+ .os_tag = .freestanding,
+ }),
+ .optimize = optimize,
+ .unwind_tables = null,
+ .omit_frame_pointer = false,
+ });
+
+ // This "freestanding" binary is runnable because it invokes the
+ // Linux exit syscall directly.
+ if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) {
+ const run_cmd = b.addRunArtifact(exe);
+ test_step.dependOn(&run_cmd.step);
+ } else {
+ test_step.dependOn(&exe.step);
+ }
+ }
}
test/standalone/stack_iterator/unwind_freestanding.zig
@@ -0,0 +1,64 @@
+/// Test StackIterator on 'freestanding' target. Based on unwind.zig.
+const std = @import("std");
+const builtin = @import("builtin");
+const debug = std.debug;
+
+noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
+ expected[0] = @returnAddress();
+
+ var it = debug.StackIterator.init(@returnAddress(), @frameAddress());
+ defer it.deinit();
+
+ // Save StackIterator's frame addresses into `unwound`:
+ for (unwound) |*addr| {
+ if (it.next()) |return_address| addr.* = return_address;
+ }
+}
+
+noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
+ 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;
+ _ = std.mem.doNotOptimizeAway(&pad);
+
+ frame2(expected, unwound);
+}
+
+noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {
+ expected[3] = @returnAddress();
+ frame1(expected, unwound);
+}
+
+// Freestanding entrypoint
+export fn _start() callconv(.C) noreturn {
+ var expected: [4]usize = undefined;
+ var unwound: [4]usize = undefined;
+ frame0(&expected, &unwound);
+
+ // Verify result (no std.testing in freestanding)
+ var missed: c_int = 0;
+ for (expected, unwound) |expectFA, actualFA| {
+ if (expectFA != actualFA) {
+ missed += 1;
+ }
+ }
+
+ // Need to compile as "freestanding" to exercise the StackIterator code, but when run as a
+ // regression test need to actually exit. So assume we're running on x86_64-linux ...
+ asm volatile (
+ \\movl $60, %%eax
+ \\syscall
+ :
+ : [missed] "{edi}" (missed),
+ : "edi", "eax"
+ );
+
+ while (true) {} // unreached
+}