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 }