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

如何获取运行中ELF二进制进程自身的堆段与栈段起始地址

错误原因分析

pidfd_open() 返回的是进程管理类文件描述符,仅用于进程信号发送、状态等待等进程控制操作,它并不指向磁盘上的ELF可执行文件。而libelf的elf_begin()接口要求传入的是符合ELF格式规范的普通文件描述符,二者类型完全不匹配,因此会触发无效描述符报错。

运行时堆、栈基地址获取方案

Linux环境下最可靠、兼容性最好的实现方式是解析/proc/self/maps文件,该文件由内核动态生成,记录了当前进程所有内存映射段的地址范围、权限、归属路径等信息,堆段标记为[heap],主线程栈段标记为[stack]。
示例实现代码如下:

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>

/**
 * 获取当前进程堆、主线程栈的地址边界
 * @param heap_start 输出参数,堆段起始地址
 * @param heap_end 输出参数,堆段结束地址
 * @param stack_start 输出参数,栈段起始地址(低地址)
 * @param stack_end 输出参数,栈段结束地址(高地址,即栈基地址)
 * @return 0成功,-1失败
 */
int get_runtime_segment_bounds(uintptr_t *heap_start, uintptr_t *heap_end, 
                               uintptr_t *stack_start, uintptr_t *stack_end)
{
    if (!heap_start || !heap_end || !stack_start || !stack_end) {
        errno = EINVAL;
        return -1;
    }
    *heap_start = *heap_end = *stack_start = *stack_end = 0;

    FILE *maps_fp = fopen("/proc/self/maps", "r");
    if (!maps_fp) {
        return -1;
    }

    char line_buf[512], perm_buf[32], path_buf[256];
    uintptr_t seg_start, seg_end;

    while (fgets(line_buf, sizeof(line_buf), maps_fp)) {
        if (sscanf(line_buf, "%lx-%lx %s %*x %*x:%*x %*u %255s", 
            &seg_start, &seg_end, perm_buf, path_buf) < 4) {
            continue;
        }
        if (strcmp(path_buf, "[heap]") == 0) {
            *heap_start = seg_start;
            *heap_end = seg_end;
        } else if (strcmp(path_buf, "[stack]") == 0) {
            *stack_start = seg_start;
            *stack_end = seg_end;
        }
    }

    fclose(maps_fp);
    return 0;
}

注意事项

  • Linux栈是从高地址向低地址增长的,因此栈的基地址是上述代码输出的stack_end,而非stack_start。
  • 多线程场景下,[stack]仅对应主线程的栈,子线程的栈由pthread库通过mmap分配,无固定标记,需要在线程创建时主动记录地址。
  • 如果进程从未申请过堆内存,[heap]段可能不存在,对应输出参数会保持为0,符合实际运行状态。
替代方案说明

堆地址获取

你尝试的sbrk(0)方案也可以使用,该接口返回当前堆的program break(堆顶边界)。如果要获取堆的初始基地址,只需在程序启动后、第一次调用malloc前执行sbrk(0)保存结果即可。该方案的局限是仅对glibc默认malloc实现有效,如果使用了自定义分配器、大内存mmap分配等场景,堆可能存在多个不连续映射,sbrk返回结果会不准确。

栈地址获取

也可以在程序入口处取局部变量的地址,再按内存页大小对齐得到近似的栈使用边界,但该方法无法获取完整的栈段边界,仅可做测试参考,不建议正式环境使用。


内容的提问来源于stack exchange,提问作者clive

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 16:24:05