authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2019-05-07 00:49:49+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-05-08 12:36:54-04:00
logbe7cacfbbec8aed234a0316d11a9ae0e8cda0286
tree65e070e9954eb8bfe7dbc95a4069d7f8fafe20ec
parent24ee7653184e6fbac0d05227723ddff2d716a28a

Implement stack probes for x86/x86_64

Enabled on non-Windows systems only since it already requires stack probes.

5 files changed, 239 insertions(+), 110 deletions(-)

CMakeLists.txt+1
......@@ -644,6 +644,7 @@ set(ZIG_STD_FILES
644644 "special/build_runner.zig"
645645 "special/builtin.zig"
646646 "special/compiler_rt.zig"
647 "special/compiler_rt/stack_probe.zig"
647648 "special/compiler_rt/arm/aeabi_fcmp.zig"
648649 "special/compiler_rt/arm/aeabi_dcmp.zig"
649650 "special/compiler_rt/addXf3.zig"
src/codegen.cpp+11
......@@ -399,6 +399,15 @@ static void add_uwtable_attr(CodeGen *g, LLVMValueRef fn_val) {
399399 }
400400}
401401
402static void add_probe_stack_attr(CodeGen *g, LLVMValueRef fn_val) {
403 // Windows already emits its own stack probes
404 if (g->zig_target->os != OsWindows &&
405 (g->zig_target->arch == ZigLLVM_x86 ||
406 g->zig_target->arch == ZigLLVM_x86_64)) {
407 addLLVMFnAttrStr(fn_val, "probe-stack", "__zig_probe_stack");
408 }
409}
410
402411static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
403412 switch (id) {
404413 case GlobalLinkageIdInternal:
......@@ -587,6 +596,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
587596 addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong");
588597 addLLVMFnAttrStr(fn_table_entry->llvm_value, "stack-protector-buffer-size", "4");
589598 }
599
600 add_probe_stack_attr(g, fn_table_entry->llvm_value);
590601 }
591602 } else {
592603 maybe_import_dll(g, fn_table_entry->llvm_value, linkage);
src/main.cpp+6
......@@ -18,6 +18,12 @@
1818
1919#include <stdio.h>
2020
21// Define this symbol here so that we can link with the zig objects during the
22// compiler bootstrap phase
23extern "C" {
24 void __zig_probe_stack(void) { }
25}
26
2127static int print_error_usage(const char *arg0) {
2228 fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0);
2329 return EXIT_FAILURE;
std/special/compiler_rt.zig+15-110
......@@ -1,10 +1,17 @@
11const builtin = @import("builtin");
22const is_test = builtin.is_test;
33
4const stack_probe = @import("compiler_rt/stack_probe.zig");
5
46comptime {
57 const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak;
68 const strong_linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong;
79
10 switch (builtin.arch) {
11 .i386, .x86_64 => @export("__zig_probe_stack", @import("compiler_rt/stack_probe.zig").zig_probe_stack, linkage),
12 else => { }
13 }
14
815 @export("__lesf2", @import("compiler_rt/comparesf2.zig").__lesf2, linkage);
916 @export("__ledf2", @import("compiler_rt/comparedf2.zig").__ledf2, linkage);
1017 @export("__letf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage);
......@@ -191,20 +198,20 @@ comptime {
191198 @export("__aeabi_dcmpun", @import("compiler_rt/comparedf2.zig").__unorddf2, linkage);
192199 }
193200 if (builtin.os == builtin.Os.windows) {
201 if (!builtin.link_libc) {
202 @export("_chkstk", @import("compiler_rt/stack_probe.zig")._chkstk, strong_linkage);
203 @export("__chkstk", @import("compiler_rt/stack_probe.zig").__chkstk, strong_linkage);
204 @export("___chkstk", @import("compiler_rt/stack_probe.zig").___chkstk, strong_linkage);
205 @export("__chkstk_ms", @import("compiler_rt/stack_probe.zig").__chkstk_ms, strong_linkage);
206 @export("___chkstk_ms", @import("compiler_rt/stack_probe.zig").___chkstk_ms, strong_linkage);
207 }
208
194209 switch (builtin.arch) {
195210 builtin.Arch.i386 => {
196 if (!builtin.link_libc) {
197 @export("_chkstk", _chkstk, strong_linkage);
198 @export("__chkstk_ms", __chkstk_ms, linkage);
199 }
200211 @export("_aulldiv", @import("compiler_rt/aulldiv.zig")._aulldiv, strong_linkage);
201212 @export("_aullrem", @import("compiler_rt/aullrem.zig")._aullrem, strong_linkage);
202213 },
203214 builtin.Arch.x86_64 => {
204 if (!builtin.link_libc) {
205 @export("__chkstk", __chkstk, strong_linkage);
206 @export("___chkstk_ms", ___chkstk_ms, linkage);
207 }
208215 // The "ti" functions must use @Vector(2, u64) parameter types to adhere to the ABI
209216 // that LLVM expects compiler-rt to have.
210217 @export("__divti3", @import("compiler_rt/divti3.zig").__divti3_windows_x86_64, linkage);
......@@ -492,108 +499,6 @@ nakedcc fn __aeabi_memcmp() noreturn {
492499 unreachable;
493500}
494501
495// _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments,
496// then decrement %esp by %eax. Preserves all registers except %esp and flags.
497// This routine is windows specific
498// http://msdn.microsoft.com/en-us/library/ms648426.aspx
499nakedcc fn _chkstk() align(4) void {
500 @setRuntimeSafety(false);
501
502 asm volatile (
503 \\ push %%ecx
504 \\ push %%eax
505 \\ cmp $0x1000,%%eax
506 \\ lea 12(%%esp),%%ecx
507 \\ jb 1f
508 \\ 2:
509 \\ sub $0x1000,%%ecx
510 \\ test %%ecx,(%%ecx)
511 \\ sub $0x1000,%%eax
512 \\ cmp $0x1000,%%eax
513 \\ ja 2b
514 \\ 1:
515 \\ sub %%eax,%%ecx
516 \\ test %%ecx,(%%ecx)
517 \\ pop %%eax
518 \\ pop %%ecx
519 \\ ret
520 );
521}
522
523nakedcc fn __chkstk() align(4) void {
524 @setRuntimeSafety(false);
525
526 asm volatile (
527 \\ push %%rcx
528 \\ push %%rax
529 \\ cmp $0x1000,%%rax
530 \\ lea 24(%%rsp),%%rcx
531 \\ jb 1f
532 \\2:
533 \\ sub $0x1000,%%rcx
534 \\ test %%rcx,(%%rcx)
535 \\ sub $0x1000,%%rax
536 \\ cmp $0x1000,%%rax
537 \\ ja 2b
538 \\1:
539 \\ sub %%rax,%%rcx
540 \\ test %%rcx,(%%rcx)
541 \\ pop %%rax
542 \\ pop %%rcx
543 \\ ret
544 );
545}
546
547// _chkstk routine
548// This routine is windows specific
549// http://msdn.microsoft.com/en-us/library/ms648426.aspx
550nakedcc fn __chkstk_ms() align(4) void {
551 @setRuntimeSafety(false);
552
553 asm volatile (
554 \\ push %%ecx
555 \\ push %%eax
556 \\ cmp $0x1000,%%eax
557 \\ lea 12(%%esp),%%ecx
558 \\ jb 1f
559 \\ 2:
560 \\ sub $0x1000,%%ecx
561 \\ test %%ecx,(%%ecx)
562 \\ sub $0x1000,%%eax
563 \\ cmp $0x1000,%%eax
564 \\ ja 2b
565 \\ 1:
566 \\ sub %%eax,%%ecx
567 \\ test %%ecx,(%%ecx)
568 \\ pop %%eax
569 \\ pop %%ecx
570 \\ ret
571 );
572}
573
574nakedcc fn ___chkstk_ms() align(4) void {
575 @setRuntimeSafety(false);
576
577 asm volatile (
578 \\ push %%rcx
579 \\ push %%rax
580 \\ cmp $0x1000,%%rax
581 \\ lea 24(%%rsp),%%rcx
582 \\ jb 1f
583 \\2:
584 \\ sub $0x1000,%%rcx
585 \\ test %%rcx,(%%rcx)
586 \\ sub $0x1000,%%rax
587 \\ cmp $0x1000,%%rax
588 \\ ja 2b
589 \\1:
590 \\ sub %%rax,%%rcx
591 \\ test %%rcx,(%%rcx)
592 \\ pop %%rax
593 \\ pop %%rcx
594 \\ ret
595 );
596}
597502
598503extern fn __divmodsi4(a: i32, b: i32, rem: *i32) i32 {
599504 @setRuntimeSafety(is_test);
std/special/compiler_rt/stack_probe.zig created+206
......@@ -0,0 +1,206 @@
1const builtin = @import("builtin");
2
3// Zig's own stack-probe routine (available only on x86 and x86_64)
4pub nakedcc fn zig_probe_stack() void {
5 @setRuntimeSafety(false);
6
7 // Versions of the Linux kernel before 5.1 treat any access below SP as
8 // invalid so let's update it on the go, otherwise we'll get a segfault
9 // instead of triggering the stack growth.
10
11 switch (builtin.arch) {
12 .x86_64 => {
13 // %rax = probe length, %rsp = stack pointer
14 asm volatile (
15 \\ push %%rcx
16 \\ mov %%rax, %%rcx
17 \\ cmp $0x1000,%%rcx
18 \\ jb 2f
19 \\ 1:
20 \\ sub $0x1000,%%rsp
21 \\ orl $0,16(%%rsp)
22 \\ sub $0x1000,%%rcx
23 \\ cmp $0x1000,%%rcx
24 \\ ja 1b
25 \\ 2:
26 \\ sub %%rcx, %%rsp
27 \\ orl $0,16(%%rsp)
28 \\ add %%rax,%%rsp
29 \\ pop %%rcx
30 \\ ret
31 );
32 },
33 .i386 => {
34 // %eax = probe length, %esp = stack pointer
35 asm volatile (
36 \\ push %%ecx
37 \\ mov %%eax, %%ecx
38 \\ cmp $0x1000,%%ecx
39 \\ jb 2f
40 \\ 1:
41 \\ sub $0x1000,%%esp
42 \\ orl $0,8(%%esp)
43 \\ sub $0x1000,%%ecx
44 \\ cmp $0x1000,%%ecx
45 \\ ja 1b
46 \\ 2:
47 \\ sub %%ecx, %%esp
48 \\ orl $0,8(%%esp)
49 \\ add %%eax,%%esp
50 \\ pop %%ecx
51 \\ ret
52 );
53 },
54 else => { }
55 }
56
57 unreachable;
58}
59
60fn win_probe_stack_only() void {
61 @setRuntimeSafety(false);
62
63 switch (builtin.arch) {
64 .x86_64 => {
65 asm volatile (
66 \\ push %%rcx
67 \\ push %%rax
68 \\ cmp $0x1000,%%rax
69 \\ lea 24(%%rsp),%%rcx
70 \\ jb 1f
71 \\ 2:
72 \\ sub $0x1000,%%rcx
73 \\ test %%rcx,(%%rcx)
74 \\ sub $0x1000,%%rax
75 \\ cmp $0x1000,%%rax
76 \\ ja 2b
77 \\ 1:
78 \\ sub %%rax,%%rcx
79 \\ test %%rcx,(%%rcx)
80 \\ pop %%rax
81 \\ pop %%rcx
82 \\ ret
83 );
84 },
85 .i386 => {
86 asm volatile (
87 \\ push %%ecx
88 \\ push %%eax
89 \\ cmp $0x1000,%%eax
90 \\ lea 12(%%esp),%%ecx
91 \\ jb 1f
92 \\ 2:
93 \\ sub $0x1000,%%ecx
94 \\ test %%ecx,(%%ecx)
95 \\ sub $0x1000,%%eax
96 \\ cmp $0x1000,%%eax
97 \\ ja 2b
98 \\ 1:
99 \\ sub %%eax,%%ecx
100 \\ test %%ecx,(%%ecx)
101 \\ pop %%eax
102 \\ pop %%ecx
103 \\ ret
104 );
105 },
106 else => { }
107 }
108
109 unreachable;
110}
111
112fn win_probe_stack_adjust_sp() void {
113 @setRuntimeSafety(false);
114
115 switch (builtin.arch) {
116 .x86_64 => {
117 asm volatile (
118 \\ push %%rcx
119 \\ cmp $0x1000,%%rax
120 \\ lea 16(%%rsp),%%rcx
121 \\ jb 1f
122 \\ 2:
123 \\ sub $0x1000,%%rcx
124 \\ test %%rcx,(%%rcx)
125 \\ sub $0x1000,%%rax
126 \\ cmp $0x1000,%%rax
127 \\ ja 2b
128 \\ 1:
129 \\ sub %%rax,%%rcx
130 \\ test %%rcx,(%%rcx)
131 \\
132 \\ lea 8(%%rsp),%%rax
133 \\ mov %%rcx,%%rsp
134 \\ mov -8(%%rax),%%rcx
135 \\ push (%%rax)
136 \\ sub %%rsp,%%rax
137 \\ ret
138 );
139 },
140 .i386 => {
141 asm volatile (
142 \\ push %%ecx
143 \\ cmp $0x1000,%%eax
144 \\ lea 8(%%esp),%%ecx
145 \\ jb 1f
146 \\ 2:
147 \\ sub $0x1000,%%ecx
148 \\ test %%ecx,(%%ecx)
149 \\ sub $0x1000,%%eax
150 \\ cmp $0x1000,%%eax
151 \\ ja 2b
152 \\ 1:
153 \\ sub %%eax,%%ecx
154 \\ test %%ecx,(%%ecx)
155 \\
156 \\ lea 4(%%esp),%%eax
157 \\ mov %%ecx,%%esp
158 \\ mov -4(%%eax),%%ecx
159 \\ push (%%eax)
160 \\ sub %%esp,%%eax
161 \\ ret
162 );
163 },
164 else => { },
165 }
166
167 unreachable;
168}
169
170// Windows has a multitude of stack-probing functions with similar names and
171// slightly different behaviours: some behave as alloca() and update the stack
172// pointer after probing the stack, other do not.
173//
174// Function name | Adjusts the SP? |
175// | x86 | x86_64 |
176// ----------------------------------------
177// _chkstk (_alloca) | yes | yes |
178// __chkstk | yes | no |
179// __chkstk_ms | no | no |
180// ___chkstk (__alloca) | yes | yes |
181// ___chkstk_ms | no | no |
182
183pub nakedcc fn _chkstk() void {
184 @setRuntimeSafety(false);
185 @inlineCall(win_probe_stack_adjust_sp);
186}
187pub nakedcc fn __chkstk() void {
188 @setRuntimeSafety(false);
189 switch (builtin.arch) {
190 .i386 => @inlineCall(win_probe_stack_adjust_sp),
191 .x86_64 => @inlineCall(win_probe_stack_only),
192 else => unreachable
193 }
194}
195pub nakedcc fn ___chkstk() void {
196 @setRuntimeSafety(false);
197 @inlineCall(win_probe_stack_adjust_sp);
198}
199pub nakedcc fn __chkstk_ms() void {
200 @setRuntimeSafety(false);
201 @inlineCall(win_probe_stack_only);
202}
203pub nakedcc fn ___chkstk_ms() void {
204 @setRuntimeSafety(false);
205 @inlineCall(win_probe_stack_only);
206}