Linux下getcontext返回错误栈状态的原因及栈信息获取方法
关于Linux
getcontext系统调用中uc_stack的疑问 我尝试用Linux的getcontext系统调用获取当前上下文,尤其是当前栈。我知道信号处理场景下上下文可能不同,但在仅包含main及少量函数的简单程序里,ucontext_t的栈指向哪里?
以下是我运行的示例代码:
#define _GNU_SOURCE #include <inttypes.h> #include <sys/types.h> #include <unistd.h> #include <ucontext.h> // Unnecessary #include <linux/limits.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> void print_ucstack(stack_t *ustk) { printf("ss_sp = %p, ss_size = 0x%lx, ss_flags = %x\n", ustk->ss_sp, ustk->ss_size, ustk->ss_flags); } int print_context() { ucontext_t ctx; if(getcontext(&ctx) != 0) return -1; greg_t *regs = ctx.uc_mcontext.gregs; printf("Actual stack pointer = %llx\n", regs[REG_RSP]); print_ucstack(&ctx.uc_stack); return 0; } void print_map() { char filebuf[4096]; int fd = open("/proc/self/maps", O_RDONLY); int nbytes; if(fd != -1) { while((nbytes = read(fd, filebuf, sizeof(filebuf))) != 0) printf("%.*s", nbytes, filebuf); } close(fd); } int main() { print_map(); print_context(); }
我打印了/proc/self/maps来获取进程布局,同时从uc_mcontext中取出真实栈指针,最后打印uc_stack.ss_sp的值。示例输出如下:
558e93a85000-558e93a86000 r--p 00000000 103:06 1449251 /tmp/stackrelocate/stackrelocate 558e93a86000-558e93a87000 r-xp 00001000 103:06 1449251 /tmp/stackrelocate/stackrelocate 558e93a87000-558e93a88000 r--p 00002000 103:06 1449251 /tmp/stackrelocate/stackrelocate 558e93a88000-558e93a89000 r--p 00002000 103:06 1449251 /tmp/stackrelocate/stackrelocate 558e93a89000-558e93a8a000 rw-p 00003000 103:06 1449251 /tmp/stackrelocate/stackrelocate 7fcb74663000-7fcb74685000 r--p 00000000 103:06 1311717 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fcb74685000-7fcb747fd000 r-xp 00022000 103:06 1311717 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fcb747fd000-7fcb7484b000 r--p 0019a000 103:06 1311717 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fcb7484b000-7fcb7484f000 r--p 001e7000 103:06 1311717 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fcb7484f000-7fcb74851000 rw-p 001eb000 103:06 1311717 /usr/lib/x86_64-linux-gnu/libc-2.31.so 7fcb74851000-7fcb74857000 rw-p 00000000 00:00 0 7fcb7487c000-7fcb7487d000 r--p 00000000 103:06 1311305 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fcb7487d000-7fcb748a0000 r-xp 00001000 103:06 1311305 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fcb748a0000-7fcb748a8000 r--p 00024000 103:06 1311305 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fcb748a9000-7fcb748aa000 r--p 0002c000 103:06 1311305 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fcb748aa000-7fcb748ab000 rw-p 0002d000 103:06 1311305 /usr/lib/x86_64-linux-gnu/ld-2.31.so 7fcb748ab000-7fcb748ac000 rw-p 00000000 00:00 0 7ffdb8771000-7ffdb8792000 rw-p 00000000 00:00 0 [stack] 7ffdb87fb000-7ffdb87fe000 r--p 00000000 00:00 0 [vvar] 7ffdb87fe000-7ffdb87ff000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] Actual stack pointer = 7ffdb8790260 ss_sp = 0x7fcb746682d0, ss_size = 0x7ffdb87904b0, ss_flags = 74855000
可以看到,实际栈指针在栈VMA范围内,但uc_stack.ss_sp指向libc.so的可执行VMA,size值也不对。我有三个疑问:
- 为什么
uc_stack成员没反映正确值? uc_stack里的值对应什么?- 怎么不读取
/proc/self/maps,通过系统调用获取栈区域的正确限制(基址、大小)?
解答
1. 为什么uc_stack成员没反映正确值?
因为getcontext在非信号处理场景下不会初始化uc_stack字段。这个字段是专门为信号上下文设计的——只有当你在信号处理函数中调用getcontext时,它才会填充当前信号栈的信息;在普通用户代码里调用时,这个字段属于未定义状态,存储的是内存残留的垃圾值,自然和真实栈信息不匹配。
2. uc_stack里的值对应什么?
你看到的是随机的垃圾数据,没有实际意义。因为getcontext没有对这个字段做赋值操作,它只是ucontext_t结构体在栈上的残留数据,刚好落在libc.so的地址范围内而已,本质就是未初始化的内存内容。
3. 怎么不读取/proc/self/maps,通过系统调用获取栈区域的正确限制?
可以用getrlimit系统调用结合当前栈指针来计算:
- 首先调用
getrlimit(RLIMIT_STACK, &rlim)获取栈的最大允许大小(rlim.rlim_cur)。 - x86_64系统的栈是向下生长的,用当前栈指针
RSP减去栈大小,再对齐到4KB页面边界(x86_64默认页面大小为4096字节),就能得到栈的基址。
示例代码片段:
#include <sys/resource.h> #include <stdint.h> #include <stdio.h> #include <errno.h> void get_stack_info() { struct rlimit rlim; if (getrlimit(RLIMIT_STACK, &rlim) == -1) { perror("getrlimit failed"); return; } uintptr_t current_rsp; __asm__ volatile ("mov %%rsp, %0" : "=r" (current_rsp)); // 对齐到4KB页面边界 uintptr_t stack_base = (current_rsp - rlim.rlim_cur) & ~(4095UL); printf("Stack base: 0x%lx, Stack size: 0x%lx\n", stack_base, rlim.rlim_cur); }
另外,也可以通过线程本地存储(TLS)获取栈信息,但这个方法依赖libc的具体实现,通用性不如getrlimit。
内容的提问来源于stack exchange,提问作者TSG
相关产品推荐
相关产品推荐

