authorgravatar for pat.github@tullmann.orgPat Tullmann <pat.github@tullmann.org> 2024-07-21 16:03:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-07-23 11:47:29-07:00
loge8503ecb6552087d6954423e59f3dfa8ca4a1b09
treefb48a161f4071dae76a3bef0af2fda6f3804b216
parent33c79841832b575c2dacc7ec07ac9536798be4e3

Default std.posix.system.ucontext_t is void

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 #20710

3 files changed, 94 insertions(+), 1 deletions(-)

lib/std/posix.zig+3-1
......@@ -45,7 +45,9 @@ pub const system = if (use_libc)
4545else switch (native_os) {
4646 .linux => linux,
4747 .plan9 => std.os.plan9,
48 else => struct {},
48 else => struct {
49 pub const ucontext_t = void;
50 },
4951};
5052
5153pub const AF = system.AF;
test/standalone/stack_iterator/build.zig+27
......@@ -1,4 +1,5 @@
11const std = @import("std");
2const builtin = @import("builtin");
23
34pub fn build(b: *std.Build) void {
45 const test_step = b.step("test", "Test it");
......@@ -93,4 +94,30 @@ pub fn build(b: *std.Build) void {
9394 const run_cmd = b.addRunArtifact(exe);
9495 test_step.dependOn(&run_cmd.step);
9596 }
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 }
96123}
test/standalone/stack_iterator/unwind_freestanding.zig created+64
......@@ -0,0 +1,64 @@
1/// Test StackIterator on 'freestanding' target. Based on unwind.zig.
2const std = @import("std");
3const builtin = @import("builtin");
4const debug = std.debug;
5
6noinline 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
18noinline fn frame2(expected: *[4]usize, unwound: *[4]usize) void {
19 expected[1] = @returnAddress();
20 frame3(expected, unwound);
21}
22
23noinline 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
34noinline fn frame0(expected: *[4]usize, unwound: *[4]usize) void {
35 expected[3] = @returnAddress();
36 frame1(expected, unwound);
37}
38
39// Freestanding entrypoint
40export 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}