master
1const std = @import("std");
2const builtin = @import("builtin");
3const common = @import("common.zig");
4const os_tag = builtin.os.tag;
5const arch = builtin.cpu.arch;
6const abi = builtin.abi;
7
8pub const panic = common.panic;
9
10comptime {
11 if (builtin.os.tag == .windows) {
12 // Default stack-probe functions emitted by LLVM
13 if (builtin.target.isMinGW()) {
14 @export(&_chkstk, .{ .name = "_alloca", .linkage = common.linkage, .visibility = common.visibility });
15 @export(&__chkstk, .{ .name = "__chkstk", .linkage = common.linkage, .visibility = common.visibility });
16 @export(&___chkstk, .{ .name = "__alloca", .linkage = common.linkage, .visibility = common.visibility });
17 @export(&___chkstk, .{ .name = "___chkstk", .linkage = common.linkage, .visibility = common.visibility });
18 @export(&__chkstk_ms, .{ .name = "__chkstk_ms", .linkage = common.linkage, .visibility = common.visibility });
19 @export(&___chkstk_ms, .{ .name = "___chkstk_ms", .linkage = common.linkage, .visibility = common.visibility });
20 } else if (!builtin.link_libc) {
21 // This symbols are otherwise exported by MSVCRT.lib
22 @export(&_chkstk, .{ .name = "_chkstk", .linkage = common.linkage, .visibility = common.visibility });
23 @export(&__chkstk, .{ .name = "__chkstk", .linkage = common.linkage, .visibility = common.visibility });
24 }
25 }
26
27 switch (arch) {
28 .x86,
29 .x86_64,
30 => {
31 @export(&zig_probe_stack, .{ .name = "__zig_probe_stack", .linkage = common.linkage, .visibility = common.visibility });
32 },
33 else => {},
34 }
35}
36
37// Zig's own stack-probe routine (available only on x86 and x86_64)
38pub fn zig_probe_stack() callconv(.naked) void {
39 @setRuntimeSafety(false);
40
41 // Versions of the Linux kernel before 5.1 treat any access below SP as
42 // invalid so let's update it on the go, otherwise we'll get a segfault
43 // instead of triggering the stack growth.
44
45 switch (arch) {
46 .x86_64 => {
47 // %rax = probe length, %rsp = stack pointer
48 asm volatile (
49 \\ push %%rcx
50 \\ mov %%rax, %%rcx
51 \\ cmp $0x1000,%%rcx
52 \\ jb 2f
53 \\ 1:
54 \\ sub $0x1000,%%rsp
55 \\ orl $0,16(%%rsp)
56 \\ sub $0x1000,%%rcx
57 \\ cmp $0x1000,%%rcx
58 \\ ja 1b
59 \\ 2:
60 \\ sub %%rcx, %%rsp
61 \\ orl $0,16(%%rsp)
62 \\ add %%rax,%%rsp
63 \\ pop %%rcx
64 \\ ret
65 );
66 },
67 .x86 => {
68 // %eax = probe length, %esp = stack pointer
69 asm volatile (
70 \\ push %%ecx
71 \\ mov %%eax, %%ecx
72 \\ cmp $0x1000,%%ecx
73 \\ jb 2f
74 \\ 1:
75 \\ sub $0x1000,%%esp
76 \\ orl $0,8(%%esp)
77 \\ sub $0x1000,%%ecx
78 \\ cmp $0x1000,%%ecx
79 \\ ja 1b
80 \\ 2:
81 \\ sub %%ecx, %%esp
82 \\ orl $0,8(%%esp)
83 \\ add %%eax,%%esp
84 \\ pop %%ecx
85 \\ ret
86 );
87 },
88 else => {},
89 }
90
91 unreachable;
92}
93
94fn win_probe_stack_only() void {
95 @setRuntimeSafety(false);
96
97 switch (arch) {
98 .thumb => {
99 asm volatile (
100 \\ lsl r4, r4, #2
101 \\ mov r12, sp
102 \\ push {r5, r6}
103 \\ mov r5, r4
104 \\1:
105 \\ sub r12, r12, #4096
106 \\ subs r5, r5, #4096
107 \\ ldr r6, [r12]
108 \\ bgt 1b
109 \\ pop {r5, r6}
110 \\ bx lr
111 );
112 },
113 .aarch64 => {
114 asm volatile (
115 \\ lsl x16, x15, #4
116 \\ mov x17, sp
117 \\1:
118 \\
119 \\ sub x17, x17, 4096
120 \\ subs x16, x16, 4096
121 \\ ldr xzr, [x17]
122 \\ b.gt 1b
123 \\
124 \\ ret
125 );
126 },
127 .x86_64 => {
128 asm volatile (
129 \\ pushq %%rcx
130 \\ pushq %%rax
131 \\ cmpq $0x1000,%%rax
132 \\ leaq 24(%%rsp),%%rcx
133 \\ jb 1f
134 \\ 2:
135 \\ subq $0x1000,%%rcx
136 \\ testq %%rcx,(%%rcx)
137 \\ subq $0x1000,%%rax
138 \\ cmpq $0x1000,%%rax
139 \\ ja 2b
140 \\ 1:
141 \\ subq %%rax,%%rcx
142 \\ testq %%rcx,(%%rcx)
143 \\ popq %%rax
144 \\ popq %%rcx
145 \\ retq
146 );
147 },
148 .x86 => {
149 asm volatile (
150 \\ push %%ecx
151 \\ push %%eax
152 \\ cmp $0x1000,%%eax
153 \\ lea 12(%%esp),%%ecx
154 \\ jb 1f
155 \\ 2:
156 \\ sub $0x1000,%%ecx
157 \\ test %%ecx,(%%ecx)
158 \\ sub $0x1000,%%eax
159 \\ cmp $0x1000,%%eax
160 \\ ja 2b
161 \\ 1:
162 \\ sub %%eax,%%ecx
163 \\ test %%ecx,(%%ecx)
164 \\ pop %%eax
165 \\ pop %%ecx
166 \\ ret
167 );
168 },
169 else => {},
170 }
171
172 unreachable;
173}
174
175fn win_probe_stack_adjust_sp() void {
176 @setRuntimeSafety(false);
177
178 switch (arch) {
179 .x86_64 => {
180 asm volatile (
181 \\ pushq %%rcx
182 \\ cmpq $0x1000,%%rax
183 \\ leaq 16(%%rsp),%%rcx
184 \\ jb 1f
185 \\ 2:
186 \\ subq $0x1000,%%rcx
187 \\ testq %%rcx,(%%rcx)
188 \\ subq $0x1000,%%rax
189 \\ cmpq $0x1000,%%rax
190 \\ ja 2b
191 \\ 1:
192 \\ subq %%rax,%%rcx
193 \\ testq %%rcx,(%%rcx)
194 \\
195 \\ leaq 8(%%rsp),%%rax
196 \\ movq %%rcx,%%rsp
197 \\ movq -8(%%rax),%%rcx
198 \\ pushq (%%rax)
199 \\ subq %%rsp,%%rax
200 \\ retq
201 );
202 },
203 .x86 => {
204 asm volatile (
205 \\ push %%ecx
206 \\ cmp $0x1000,%%eax
207 \\ lea 8(%%esp),%%ecx
208 \\ jb 1f
209 \\ 2:
210 \\ sub $0x1000,%%ecx
211 \\ test %%ecx,(%%ecx)
212 \\ sub $0x1000,%%eax
213 \\ cmp $0x1000,%%eax
214 \\ ja 2b
215 \\ 1:
216 \\ sub %%eax,%%ecx
217 \\ test %%ecx,(%%ecx)
218 \\
219 \\ lea 4(%%esp),%%eax
220 \\ mov %%ecx,%%esp
221 \\ mov -4(%%eax),%%ecx
222 \\ push (%%eax)
223 \\ sub %%esp,%%eax
224 \\ ret
225 );
226 },
227 else => {},
228 }
229
230 unreachable;
231}
232
233// Windows has a multitude of stack-probing functions with similar names and
234// slightly different behaviours: some behave as alloca() and update the stack
235// pointer after probing the stack, other do not.
236//
237// Function name | Adjusts the SP? |
238// | x86 | x86_64 |
239// ----------------------------------------
240// _chkstk (_alloca) | yes | yes |
241// __chkstk | yes | no |
242// __chkstk_ms | no | no |
243// ___chkstk (__alloca) | yes | yes |
244// ___chkstk_ms | no | no |
245
246pub fn _chkstk() callconv(.naked) void {
247 @setRuntimeSafety(false);
248 @call(.always_inline, win_probe_stack_adjust_sp, .{});
249}
250pub fn __chkstk() callconv(.naked) void {
251 @setRuntimeSafety(false);
252 if (arch == .thumb or arch == .aarch64) {
253 @call(.always_inline, win_probe_stack_only, .{});
254 } else switch (arch) {
255 .x86 => @call(.always_inline, win_probe_stack_adjust_sp, .{}),
256 .x86_64 => @call(.always_inline, win_probe_stack_only, .{}),
257 else => unreachable,
258 }
259}
260pub fn ___chkstk() callconv(.naked) void {
261 @setRuntimeSafety(false);
262 @call(.always_inline, win_probe_stack_adjust_sp, .{});
263}
264pub fn __chkstk_ms() callconv(.naked) void {
265 @setRuntimeSafety(false);
266 @call(.always_inline, win_probe_stack_only, .{});
267}
268pub fn ___chkstk_ms() callconv(.naked) void {
269 @setRuntimeSafety(false);
270 @call(.always_inline, win_probe_stack_only, .{});
271}