/// 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); } // No-OS entrypoint export fn _start() callconv(.withStackAlign(.c, 1)) 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 with the target OS as "freestanding" or "other" 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 = true, .eax = true }); while (true) {} // unreached }