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