通过GCC -fstack-check选项在C中引发了什么异常 [英] What exception is raised in C by GCC -fstack-check option
问题描述
根据gcc文件
-fstack-check
生成代码以验证您是否超出堆栈边界。请注意,此开关实际上不会导致检查完成;操作系统必须这样做。该开关导致代码生成,以确保操作系统看到堆栈被扩展。
我的假设是这个额外的代码将生成让OS知道的例外。
当使用C语言时,我需要知道额外代码产生了什么异常。
Google也没什么帮助。关闭我知道的是,它在Ada语言的情况下会生成Storage_Error异常(参考)。
我正在处理那些需要捕获此异常的小OS /调度程序。我使用C / C ++。
我的GCC版本3.4.4
它不直接生成任何异常。它生成的代码在堆栈被多个页面放大时,会为新分配的区域中的每个页面生成读写访问权限。这就是它的全部 。示例:
extern void bar(char *);
void foo(void)
{
char buf [4096 * 8];
bar(buf);
编译(使用gcc 4.9,在x86-64上,在<$ c $
foo:
pushq%rbp
movq $ -32768,%r11
movq%rsp,%rbp
subq $ 4128,%rsp
addq%rsp,%r11
.LPSRL0:
cmpq% r11,%rsp
je .LPSRE0
subq $ 4096,%rsp
orq $ 0,(%rsp)
jmp .LPSRL0
.LPSRE0:
addq $ 4128,%rsp
leaq -32768(%rbp),%rdi
呼叫栏
离开
ret
orq $ 0,(%rsp)
对处的内存内容没有影响( %rsp)
,但CPU将它视为对该地址的读写访问权限。 (我不知道为什么GCC在循环期间偏移%rsp
4128字节,或者为什么它认为帧指针是必需的。)
理论是操作系统可以注意到这些访问并且在堆栈变得太大时做适当的事情。对于符合POSIX标准的操作系统,这将是一个 SIGSEGV
信号的交付。
您可能想知道<如何操作系统可以注意到这样的事情。硬件允许操作系统将地址空间的页面指定为完全不可访问;任何试图在这些页面上读或写内存的尝试都会触发操作系统可以处理的硬件故障(如果符合POSIX标准的操作系统,则交付 SIGSEGV
) 。这可以用来立即放置一个保护区,超过为堆栈预留的空间的末端。这就是为什么每个页面访问一次就足够了。
-fstack-check
是为了保护您,避免请注意,保护区非常小 - 也许只有一页 - 因此在堆栈上分配一个大缓冲区会将堆栈指针移动到该区域并进入另一个可访问RAM区域。如果程序发生后永远不会触及保护区内的存储器,则不会立即崩溃,但是您将在其他任何区域上乱涂乱画,导致延迟动作故障。
as per gcc documentation
-fstack-check
Generate code to verify that you do not go beyond the boundary of the stack. Note that this switch does not actually cause checking to be done; the operating system must do that. The switch causes generation of code to ensure that the operating system sees the stack being extended.
My assumption is that this extra code will generate exception to let OS know.
When using C language I need to know what exception is being generated by the extra code.
Google is also not helping much. Close I came to know is that it generates Storage_Error exception in case of Ada language (Reference).
I am working on sort of small OS/scheduler where I need to catch this exception. I am using C/C++.
My GCC version 3.4.4
解决方案 It doesn't generate any exception directly. It generates code which, when the stack is enlarged by more than one page, generates a read-write access to each page in the newly allocated region. That's all it does. Example:
extern void bar(char *);
void foo(void)
{
char buf[4096 * 8];
bar(buf);
}
compiles (with gcc 4.9, on x86-64, at -O2
) to:
foo:
pushq %rbp
movq $-32768, %r11
movq %rsp, %rbp
subq $4128, %rsp
addq %rsp, %r11
.LPSRL0:
cmpq %r11, %rsp
je .LPSRE0
subq $4096, %rsp
orq $0, (%rsp)
jmp .LPSRL0
.LPSRE0:
addq $4128, %rsp
leaq -32768(%rbp), %rdi
call bar
leave
ret
orq $0, (%rsp)
has no effect on the contents of the memory at (%rsp)
, but the CPU treats it as a read-write access to that address anyway. (I don't know why GCC offsets %rsp
by 4128 bytes during the loop, or why it thinks a frame pointer is necessary.)
The theory is that the OS can notice these accesses and do something appropriate if the stack has become too large. For a POSIX-compliant operating system, that would be delivery of a SIGSEGV
signal.
You may be wondering how the OS can notice such a thing. The hardware allows the OS to designate pages of address space as completely inaccessible; any attempt to read or write memory in those pages triggers a hardware fault which the OS can process as it sees fit (again, for a POSIX-compliant OS, delivery of SIGSEGV
). This can be used to place a "guard area" immediately past the end of the space reserved for the stack. That's why one access per page is sufficient.
What -fstack-check
is meant to protect you from, to be clear, is the situation where the "guard area" is very small - perhaps just one page - so allocating a large buffer on the stack moves the stack pointer past that area and into another region of accessible RAM. If the program then happens never to touch memory within the guard area, you won't get a prompt crash, but you will scribble on whatever that other region is, causing a delayed-action malfunction.
这篇关于通过GCC -fstack-check选项在C中引发了什么异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!