zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

stack_probe.zig (9539B) - Raw


      1 const std = @import("std");
      2 const builtin = @import("builtin");
      3 const common = @import("common.zig");
      4 const os_tag = builtin.os.tag;
      5 const arch = builtin.cpu.arch;
      6 const abi = builtin.abi;
      7 
      8 pub const panic = common.panic;
      9 
     10 comptime {
     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)
     38 pub 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 
     94 fn 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 
    175 fn 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 
    246 pub fn _chkstk() callconv(.naked) void {
    247     @setRuntimeSafety(false);
    248     @call(.always_inline, win_probe_stack_adjust_sp, .{});
    249 }
    250 pub 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 }
    260 pub fn ___chkstk() callconv(.naked) void {
    261     @setRuntimeSafety(false);
    262     @call(.always_inline, win_probe_stack_adjust_sp, .{});
    263 }
    264 pub fn __chkstk_ms() callconv(.naked) void {
    265     @setRuntimeSafety(false);
    266     @call(.always_inline, win_probe_stack_only, .{});
    267 }
    268 pub fn ___chkstk_ms() callconv(.naked) void {
    269     @setRuntimeSafety(false);
    270     @call(.always_inline, win_probe_stack_only, .{});
    271 }