You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 04:32:09