| author | |
| committer | |
| log | e8503ecb6552087d6954423e59f3dfa8ca4a1b09 |
| tree | fb48a161f4071dae76a3bef0af2fda6f3804b216 |
| parent | 33c79841832b575c2dacc7ec07ac9536798be4e3 |
PR https://github.com/ziglang/zig/pull/20679 ("std.c reorganization")
switched feature-detection code to use "T != void" checks in place of
"@hasDecl". However, the std.posix.system struct is empty, so
compile-time feature detection against symbols in there (specifically
`std.posix.system.ucontext_t` in this case), fail at compile time on
freestanding targets.
This PR adds a void ucontext_t into the std.posix.system default.
This PR also adds pseudo-"freestanding" variation of the StackIterator
"unwind" test. It is sort of hacky (its freestanding, but assumes it can
invoke a Linux exit syscall), but it does detect this problem.
Fixes #207103 files changed, 94 insertions(+), 1 deletions(-)
lib/std/posix.zig+3-1| ... | ... | @@ -45,7 +45,9 @@ pub const system = if (use_libc) |
| 45 | 45 | else switch (native_os) { |
| 46 | 46 | .linux => linux, |
| 47 | 47 | .plan9 => std.os.plan9, |
| 48 | else => struct {}, | |
| 48 | else => struct { | |
| 49 | pub const ucontext_t = void; | |
| 50 | }, | |
| 49 | 51 | }; |
| 50 | 52 | |
| 51 | 53 | pub const AF = system.AF; |
test/standalone/stack_iterator/build.zig+27| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | |
| 2 | 3 | |
| 3 | 4 | pub fn build(b: *std.Build) void { |
| 4 | 5 | const test_step = b.step("test", "Test it"); |
| ... | ... | @@ -93,4 +94,30 @@ pub fn build(b: *std.Build) void { |
| 93 | 94 | const run_cmd = b.addRunArtifact(exe); |
| 94 | 95 | test_step.dependOn(&run_cmd.step); |
| 95 | 96 | } |
| 97 | ||
| 98 | // Unwinding without libc/posix | |
| 99 | // | |
| 100 | // No "getcontext" or "ucontext_t" | |
| 101 | { | |
| 102 | const exe = b.addExecutable(.{ | |
| 103 | .name = "unwind_freestanding", | |
| 104 | .root_source_file = b.path("unwind_freestanding.zig"), | |
| 105 | .target = b.resolveTargetQuery(.{ | |
| 106 | .cpu_arch = .x86_64, | |
| 107 | .os_tag = .freestanding, | |
| 108 | }), | |
| 109 | .optimize = optimize, | |
| 110 | .unwind_tables = null, | |
| 111 | .omit_frame_pointer = false, | |
| 112 | }); | |
| 113 | ||
| 114 | // This "freestanding" binary is runnable because it invokes the | |
| 115 | // Linux exit syscall directly. | |
| 116 | if (builtin.os.tag == .linux and builtin.cpu.arch == .x86_64) { | |
| 117 | const run_cmd = b.addRunArtifact(exe); | |
| 118 | test_step.dependOn(&run_cmd.step); | |
| 119 | } else { | |
| 120 | test_step.dependOn(&exe.step); | |
| 121 | } | |
| 122 | } | |
| 96 | 123 | } |
test/standalone/stack_iterator/unwind_freestanding.zig created+64| ... | ... | @@ -0,0 +1,64 @@ |
| 1 | /// Test StackIterator on 'freestanding' target. Based on unwind.zig. | |
| 2 | const std = @import("std"); | |
| 3 | const builtin = @import("builtin"); | |
| 4 | const debug = std.debug; | |
| 5 | ||
| 6 | noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void { | |
| 7 | expected[0] = @returnAddress(); | |
| 8 | ||
| 9 | var it = debug.StackIterator.init(@returnAddress(), @frameAddress()); | |
| 10 | defer it.deinit(); | |
| 11 | ||
| 12 | // Save StackIterator's frame addresses into `unwound`: | |
| 13 | for (unwound) |*addr| { | |
| 14 | if (it.next()) |return_address| addr.* = return_address; | |
| 15 | } | |
| 16 | } | |
| 17 | ||
| 18 | noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void { | |
| 19 | expected[1] = @returnAddress(); | |
| 20 | frame3(expected, unwound); | |
| 21 | } | |
| 22 | ||
| 23 | noinline fn frame1(expected: *[4]usize, unwound: *[4]usize) void { | |
| 24 | expected[2] = @returnAddress(); | |
| 25 | ||
| 26 | // Use a stack frame that is too big to encode in __unwind_info's stack-immediate encoding | |
| 27 | // to exercise the stack-indirect encoding path | |
| 28 | var pad: [std.math.maxInt(u8) * @sizeOf(usize) + 1]u8 = undefined; | |
| 29 | _ = std.mem.doNotOptimizeAway(&pad); | |
| 30 | ||
| 31 | frame2(expected, unwound); | |
| 32 | } | |
| 33 | ||
| 34 | noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void { | |
| 35 | expected[3] = @returnAddress(); | |
| 36 | frame1(expected, unwound); | |
| 37 | } | |
| 38 | ||
| 39 | // Freestanding entrypoint | |
| 40 | export fn _start() callconv(.C) noreturn { | |
| 41 | var expected: [4]usize = undefined; | |
| 42 | var unwound: [4]usize = undefined; | |
| 43 | frame0(&expected, &unwound); | |
| 44 | ||
| 45 | // Verify result (no std.testing in freestanding) | |
| 46 | var missed: c_int = 0; | |
| 47 | for (expected, unwound) |expectFA, actualFA| { | |
| 48 | if (expectFA != actualFA) { | |
| 49 | missed += 1; | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 53 | // Need to compile as "freestanding" to exercise the StackIterator code, but when run as a | |
| 54 | // regression test need to actually exit. So assume we're running on x86_64-linux ... | |
| 55 | asm volatile ( | |
| 56 | \\movl $60, %%eax | |
| 57 | \\syscall | |
| 58 | : | |
| 59 | : [missed] "{edi}" (missed), | |
| 60 | : "edi", "eax" | |
| 61 | ); | |
| 62 | ||
| 63 | while (true) {} // unreached | |
| 64 | } |