生成代码以确保堆栈不会超出特定范围值,即寄存器的值或符号的地址。如果一个需要更大的堆栈,运行时会发出信号。对于大多数目标,信号被引发在堆栈超出边界之前,因此可以捕捉信号而无需特别预防措施。
例如,如果堆栈从绝对地址“ 0x80000000”开始,向下生长,您可以使用标志-fstack-limit-symbol = __ stack_limit和-Wl,-defsym,__ stack_limit = 0x7ffe0000以强制执行128KB的堆栈限制。请注意,这仅适用于GNU链接器。
您可以使用no_stack_limit函数属性(请参见函数属性)。
什么是“信号”?是否需要某些OS界面?是否应该提供有关此信号具有什么ID的文档,以便我可以捕获它?
我刚才遇到了同样的问题。
对于x86,对于非法指令,信号将为“ SIGILL”。这是因为GCC将“ trap_if” RTL原语映射到x86 ud2
指令,该指令一旦执行将生成信号。
例如,这是一些源代码:
ud2
用#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#ifndef N
#define N 100000
#endif
register uintptr_t stack_limit asm ("r12");
void foo();
__attribute__((no_stack_limit))
int main(int argc, char **argv) {
int ret = 0;
stack_limit = (uintptr_t)(&ret - 1000);
foo();
return ret;
}
void foo()
{
char *ch = (char *)alloca(N);
printf("Now in foo\n");
}
编译
[根据实验,gcc -ggdb -O0 -fstack-limit-register=r12 -o stack-lim stack-lim.c -fstack-usage -DN=3922
可以正常工作,-DN=3921
可以触发它(在我的机器上),考虑到它的粗略程度,它在正确的位置。
这是-DN=3922
中的后续拆卸过程。注意“ ud2”指令是由gdb
之前的条件执行的。
jae
这是同一程序集的相关代码段,使用 ...
B+>│0x555555554707 <foo+8> mov %fs:0x28,%rax
│0x555555554710 <foo+17> mov %rax,-0x8(%rbp)
│0x555555554714 <foo+21> xor %eax,%eax
│0x555555554716 <foo+23> mov $0x10,%eax
│0x55555555471b <foo+28> sub $0x1,%rax
│0x55555555471f <foo+32> add $0xf62,%rax
│0x555555554725 <foo+38> mov $0x10,%ecx
│0x55555555472a <foo+43> mov $0x0,%edx
│0x55555555472f <foo+48> div %rcx
│0x555555554732 <foo+51> imul $0x10,%rax,%rax
│0x555555554736 <foo+55> mov %rsp,%rdx
│0x555555554739 <foo+58> sub %r12,%rdx
│0x55555555473c <foo+61> cmp %rax,%rdx
│0x55555555473f <foo+64> jae 0x555555554743 <foo+68>
│0x555555554741 <foo+66> ud2
│0x555555554743 <foo+68> sub %rax,%rsp
│0x555555554746 <foo+71> mov %rsp,%rax
...
选项用RTL进行注释,如下所示:
-fdump-rtl -dP
注意,$ gcc -ggdb -O0 -fstack-limit-register=r12 -S -o stack-lim.s stack-lim.c -fstack-usage -DN=3922 -dP -fdump-rtl-final
变为(trap_if)
:
ud2
我怀疑它没有被很好地记录的原因是因为它是特定于机器的,用于发出哪个信号,并且实际上是否完全实现了这种功能。尽管我确实同意应该记录每个目标的行为。