考虑以下计划。它从命令行获取一个参数,然后将递归函数展开到该限制。
#include <stdio.h>
#include <stdlib.h>
int rec(int x, int limit) {
if (x == limit) {
return limit;
}
int r = rec(x + 1, limit);
return r - 1;
}
int main(int arc, char* argv[]) {
int result, limit;
limit = atoi(argv[1]);
printf("stack: %p\n", &result);
result = rec(0, limit);
printf("%d\n", result);
}
如果我编译它,我希望它用完固定输入参数限制的堆栈。还有其他事情发生了。
dejan@raven:~/test/stack$ gcc stack.c
dejan@raven:~/test/stack$ ./a.out 174580
stack: 0x7fff42fd58f0
Segmentation fault (core dumped)
dejan@raven:~/test/stack$ ./a.out 174580
stack: 0x7ffdd2dd8b20
0
在两个不同的运行中,堆栈大小似乎不同。它似乎不是一个编译器问题,因为clang会发生同样的事情,并且反汇编并不会涉及任何奇怪的事情。
为什么不同运行的堆栈大小不同?
答案 0 :(得分:2)
我已将/proc/self/maps
解析器添加到您的程序中(与@AndrewHenle建议的方法相同,但我在程序开始时执行此操作,并且不调用pmap
):
char* get_stack_bounds() {
FILE* maps = fopen("/proc/self/maps", "r");
static char line[256];
while(!feof(maps)) {
fgets(line, 255, maps);
if(strstr(line, "[stack]")) {
char* space = strchr(line, ' ');
*space = '\0';
fclose(maps);
return line;
}
}
fclose(maps);
return NULL;
}
unsigned long get_stack_right() {
char* bounds = get_stack_bounds();
bounds = strchr(bounds, '-') + 1;
return strtol(bounds, NULL, 16);
}
并在main()
:
printf("&result: %p delta: %ld\n", &result,
get_stack_right() - ((unsigned long) &result));
以下是一些结果:
> ./a.out 104747
&result: 0x7fff3347c7f8 delta: 6152
0
> ./a.out 174580
&result: 0x7fffe43c9b38 delta: 5320
0
> ./a.out 174580
&result: 0x7fff26ad2b28 delta: 9432
Segmentation fault (core dumped)
> ./a.out 174580
&result: 0x7fff145aa5a8 delta: 6744
0
> ./a.out 174580
&result: 0x7fff74fff0b8 delta: 12104
Segmentation fault (core dumped)
我认为delta
(这是result
地址与堆栈基址之间的差异)与分段错误之间的相关性是显而易见的。
您应该注意main()
不是第一个在程序中运行的函数,实际入口点将是来自crt1.o(或其他)的_start()
,因此初始堆栈大小可能不同。 / p>
实际问题是Address space layout randomization 。
以下是fs/binfmt_elf_fdpic.c
关于其用法的评论:
/* In some cases (e.g. Hyper-Threading), we want to avoid L1 evictions
* by the processes running on the same package. One thing we can do is
* to shuffle the initial stack for them, so we give the architecture
* an opportunity to do so here.
*/
sp = arch_align_stack(bprm->p);
以下是x86上arch_align_stack()
的实现:
unsigned long arch_align_stack(unsigned long sp)
{
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
sp -= get_random_int() % 8192;
return sp & ~0xf;
}
答案 1 :(得分:0)
添加SIGSEGV处理程序:
void handler( int sig )
{
char buffer[ 1024 ]
sprintf( buffer, "/path/to/pmap %d", getpid() );
system( buffer );
exit( 0 );
}
int main( int argc, char *argv[] )
{
signal( SIGSEGV, handler );
.
.
.
这样,代替生成核心文件,您的进程将在SEGV时发出其地址空间的映射。
请注意,一般来说这是一种非常危险的做事方式。这不是真正的异步信号安全。但是你没有做任何会导致实际损害的僵局。