代码之家  ›  专栏  ›  技术社区  ›  Elektito

变量地址不在/proc/self/maps中的堆栈范围内

  •  3
  • Elektito  · 技术社区  · 6 月前

    我有一个非常简单的程序来测试这个:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char *argv[]) {
        int ss;
        int xx = system("cat /proc/self/maps | grep stack");
        printf("&ss=%p\n", &ss);
        return 0;
    }
    

    我编译并运行了这个(在Linux上,在x86_64 cpu上),得到了这样的结果:

    7ffc63ee7000-7ffc63f09000 rw-p 00000000 00:00 0                          [stack]
    &ss=0x7ffe3e13d8a0
    

    如果你计算一下,你会发现0x7ffe3e13d8a0不在7ffc63ee7000-7ffc63f09000的范围内。不管我跑了多少次,情况似乎都是这样。我不明白为什么。

    我已经在SO上看到了一个类似的问题: Address of local variable not within the address range of stack shown by smaps

    提供的答案对我不起作用:

    1. 显然,这个程序中没有线程。
    2. 没有协程式编程、longjmp等。

    任何帮助都将不胜感激。

    只是为了澄清我需要这样做的原因。我正在编写一个保守的垃圾收集器,我想通过获取中变量的地址来获取要扫描的堆栈范围 main 另一个在当前功能中。

    2 回复  |  直到 6 月前
        1
  •  6
  •   chqrlie    6 月前

    由生成的内存映射 system("cat /proc/self/maps | grep stack") 这不是你的计划,而是 cat 打开文件的过程 /proc/self/maps .

    您应该从自己进程的pid构造命令行:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[]) {
        int ss;
        char cmd[64];
        snprintf(cmd, sizeof cmd, "cat /proc/%d/maps | grep stack", getpid());
        system(cmd);
        printf("&ss=%p\n", (void *)&ss);
        return 0;
    }
    

    输出:

    chqrlie@linuxbox:~/dev/stackoverflow$ ./test
    7ffd9ffb5000-7ffd9ffd6000 rw-p 00000000 00:00 0                          [stack]
    &ss=0x7ffd9ffd368c
    

    为了您的GC目的,使用 /proc/self/maps 可以,因为您将从同一进程打开伪文件。然而,请注意,此功能是特定于linux的,其他unix或posix系统可能不提供此功能,使用不同的API获取信息,或者信息可能根本不可用。

    以下是一个具有单个流程的实现:

    #include <errno.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(int argc, char *argv[]) {
        FILE *fp = fopen("/proc/self/maps", "r");
        if (fp) {
            char buf[200];
            int ss;
            while (fgets(buf, sizeof buf, fp)) {
                if (strstr(buf, "stack"))
                    fputs(buf, stdout);
            }
            fclose(fp);
            printf("&ss=%p\n", (void *)&ss);
            return 0;
        } else {
            fprintf(stderr, "cannot open /proc/self/maps: %s\n", strerror(errno));
            return 1;
        }
    }
    

    还要注意另一个不明显的事实:在你的测试中,你的过程和 ,与其说是因为它们是不同的进程,不如说主要是因为操作系统执行地址空间随机化作为对抗简单攻击向量的对策。这些地址位于单独的虚拟内存空间中,因此它们可能是偶然匹配的,在较旧的32位linux系统上也可能是如此。

        2
  •  -1
  •   Kenzo    6 月前

    这主要是由于 ASLR (Address Space Layout Randomization)

    要暂时禁用它,您可以使用:

    sudo sysctl kernel.randomize_va_space=0

    禁用它并重新运行程序以查看正确的堆栈地址分配。