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}