是否可以识别一个地址引用是否属于进程地址space中的static/heap/stack
Is it possible to identify whether an address reference belongs to static/heap/stack in the process address space
我们有一种机制可以监控捕获引用地址的加载和存储指令。我想对地址进行分类,它们是属于堆栈,堆还是分配静态变量的区域。有没有办法以编程方式进行这种分类?
我最初的想法是在进程启动后立即执行带有小内存请求(1?)的 malloc() 运行ning 以便我可以捕获 "base address"(或启动地址)堆。这样,我就可以区分那些静态分配的变量和其他变量。对于那些不属于静态区域的引用(那些是堆和堆栈),我该如何区分它们?
一些小测试表明下面的简单代码(运行 in Linux 3.18/x86-64 compiled with gcc 4.8.4)
#include <stdio.h>
#include <stdlib.h>
int x;
int foo (void)
{
int s;
int *h = malloc (sizeof(int));
printf ("x = %p, *s = %p, h = %p\n", &x, &s, h);
}
int main (int argc, char *argv[])
{
foo();
return 0;
}
显示了地址的一些随机化 space (不是在静态变量中,而是在其余部分——堆和堆栈中)这可能会增加一些不确定性,但也许是一种找到这些区域的限制的方法地址 space.
我想为了获得正确的结果,您应该在 Linux 上解析 /proc/<pid>/maps
文件。示例内容:
# cat maps
00400000-00407000 r-xp 00000000 fc:02 1837717 /sbin/getty
00606000-00607000 r--p 00006000 fc:02 1837717 /sbin/getty
00607000-00608000 rw-p 00007000 fc:02 1837717 /sbin/getty
00608000-0060a000 rw-p 00000000 00:00 0
0252e000-0254f000 rw-p 00000000 00:00 0 [heap]
7f3ca601f000-7f3ca6833000 r--p 00000000 fc:02 2105304 /usr/lib/locale/locale-archive
...
7f3ca7656000-7f3ca7657000 r--p 00022000 fc:02 1711858 /lib/x86_64-linux-gnu/ld-2.19.so
7f3ca7657000-7f3ca7658000 rw-p 00023000 fc:02 1711858 /lib/x86_64-linux-gnu/ld-2.19.so
7f3ca7658000-7f3ca7659000 rw-p 00000000 00:00 0
7fffbbcf2000-7fffbbd13000 rw-p 00000000 00:00 0 [stack]
7fffbbdfc000-7fffbbdfe000 r-xp 00000000 00:00 0 [vdso]
7fffbbdfe000-7fffbbe00000 r--p 00000000 00:00 0 [vvar]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
参考proc(5)
.
对此没有标准的 C API,这意味着所有可能的解决方案都将基于特定于平台的 hack。此外,此答案仅限于单线程应用程序。
- 如何识别栈地址?
堆栈是一个连续的内存区域。因此,您只需要知道两个数字:栈顶和栈底。栈顶基本上受限于当前函数的栈帧。但是,由于无法从 C 代码访问当前堆栈帧的大小,因此很难判断当前帧的确切结束位置。这里的技巧是从当前调用另一个函数,并使用被调用函数堆栈帧中的地址作为 stack_top
.
的边界值
学习栈底更简单——它的值在程序执行期间保持不变,并以入口函数的栈帧为界(main()
在C程序中)。因此,在 main()
函数中取一些局部变量的地址是一个足够的近似值。
还有一点需要注意的是x86
栈是向后增长的,这意味着栈顶的地址比栈底的地址小。这段代码总结起来:
void *stack_bottom;
bool IS_IN_STACK(void *x) __attribute__((noinline));
bool IS_IN_STACK(void *x) {
void *stack_top = &stack_top;
return x <= stack_bottom && x >= stack_top;
}
int main (int argc, char *argv[]) {
int x;
stack_bottom = &x;
...
- 如何识别静态变量的地址?
这里的逻辑更简单。静态变量分配在以固定的、特定于平台的地址开始的内存区域中。通常这个区域先于内存中的所有其他区域。因此,唯一需要学习的是这个静态内存区域的 end 地址。
幸运的是,GCC 链接器 provides symbols end
、edata
和 etext
表示 .bss
、.data
和 .text
段分别。静态变量分配在 .bss
或 .data
段中,因此此检查在大多数平台上应该足够了:
#define IS_STATIC(x) ((void*)(x) <= (void*)&end || (void*)(x) <= (void*)&edata)
此宏同时检查 edata
和 end
,以避免假设 .bss
和 .data
中的哪一个在内存中排在第一位。
- 堆地址。
堆变量通常直接分配在 .data
和 .bss
区域地址之后的地址中。但是,有时堆地址可能属于不连续的内存范围。因此,您在这里可以做的最好的事情是阅读 Linux 进程文件以找出其他答案中建议的内存映射。或者,只需检查 IS_IN_STACK
和 IS_STATIC
return 是否都为 false.
使用这些宏的完整程序:
int x;
extern int end, edata;
void *stack_bottom;
bool IS_IN_STACK(void *x) __attribute__((noinline));
bool IS_IN_STACK(void *x) {
void *stack_top = &stack_top;
return x <= stack_bottom && x >= stack_top;
}
#define IS_STATIC(x) ((void*)(x) <= (void*)&end || (void*)(x) <= (void*)&edata)
int foo (void)
{
int s;
int *h = malloc (sizeof(int));
printf ("x = %p, *s = %p, h = %p\n", &x, &s, h);
// prints 0 1 0
printf ("%d %d %d\n", IS_IN_STACK(&x), IS_IN_STACK(&s), IS_IN_STACK(h));
// prints 1 0 0
printf ("%d %d %d\n", IS_STATIC(&x), IS_STATIC(&s), IS_STATIC(h));
}
int main (int argc, char *argv[])
{
int x;
stack_bottom = &x;
foo();
return 0;
}
我们有一种机制可以监控捕获引用地址的加载和存储指令。我想对地址进行分类,它们是属于堆栈,堆还是分配静态变量的区域。有没有办法以编程方式进行这种分类?
我最初的想法是在进程启动后立即执行带有小内存请求(1?)的 malloc() 运行ning 以便我可以捕获 "base address"(或启动地址)堆。这样,我就可以区分那些静态分配的变量和其他变量。对于那些不属于静态区域的引用(那些是堆和堆栈),我该如何区分它们?
一些小测试表明下面的简单代码(运行 in Linux 3.18/x86-64 compiled with gcc 4.8.4)
#include <stdio.h>
#include <stdlib.h>
int x;
int foo (void)
{
int s;
int *h = malloc (sizeof(int));
printf ("x = %p, *s = %p, h = %p\n", &x, &s, h);
}
int main (int argc, char *argv[])
{
foo();
return 0;
}
显示了地址的一些随机化 space (不是在静态变量中,而是在其余部分——堆和堆栈中)这可能会增加一些不确定性,但也许是一种找到这些区域的限制的方法地址 space.
我想为了获得正确的结果,您应该在 Linux 上解析 /proc/<pid>/maps
文件。示例内容:
# cat maps
00400000-00407000 r-xp 00000000 fc:02 1837717 /sbin/getty
00606000-00607000 r--p 00006000 fc:02 1837717 /sbin/getty
00607000-00608000 rw-p 00007000 fc:02 1837717 /sbin/getty
00608000-0060a000 rw-p 00000000 00:00 0
0252e000-0254f000 rw-p 00000000 00:00 0 [heap]
7f3ca601f000-7f3ca6833000 r--p 00000000 fc:02 2105304 /usr/lib/locale/locale-archive
...
7f3ca7656000-7f3ca7657000 r--p 00022000 fc:02 1711858 /lib/x86_64-linux-gnu/ld-2.19.so
7f3ca7657000-7f3ca7658000 rw-p 00023000 fc:02 1711858 /lib/x86_64-linux-gnu/ld-2.19.so
7f3ca7658000-7f3ca7659000 rw-p 00000000 00:00 0
7fffbbcf2000-7fffbbd13000 rw-p 00000000 00:00 0 [stack]
7fffbbdfc000-7fffbbdfe000 r-xp 00000000 00:00 0 [vdso]
7fffbbdfe000-7fffbbe00000 r--p 00000000 00:00 0 [vvar]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
参考proc(5)
.
对此没有标准的 C API,这意味着所有可能的解决方案都将基于特定于平台的 hack。此外,此答案仅限于单线程应用程序。
- 如何识别栈地址?
堆栈是一个连续的内存区域。因此,您只需要知道两个数字:栈顶和栈底。栈顶基本上受限于当前函数的栈帧。但是,由于无法从 C 代码访问当前堆栈帧的大小,因此很难判断当前帧的确切结束位置。这里的技巧是从当前调用另一个函数,并使用被调用函数堆栈帧中的地址作为 stack_top
.
学习栈底更简单——它的值在程序执行期间保持不变,并以入口函数的栈帧为界(main()
在C程序中)。因此,在 main()
函数中取一些局部变量的地址是一个足够的近似值。
还有一点需要注意的是x86
栈是向后增长的,这意味着栈顶的地址比栈底的地址小。这段代码总结起来:
void *stack_bottom;
bool IS_IN_STACK(void *x) __attribute__((noinline));
bool IS_IN_STACK(void *x) {
void *stack_top = &stack_top;
return x <= stack_bottom && x >= stack_top;
}
int main (int argc, char *argv[]) {
int x;
stack_bottom = &x;
...
- 如何识别静态变量的地址?
这里的逻辑更简单。静态变量分配在以固定的、特定于平台的地址开始的内存区域中。通常这个区域先于内存中的所有其他区域。因此,唯一需要学习的是这个静态内存区域的 end 地址。
幸运的是,GCC 链接器 provides symbols end
、edata
和 etext
表示 .bss
、.data
和 .text
段分别。静态变量分配在 .bss
或 .data
段中,因此此检查在大多数平台上应该足够了:
#define IS_STATIC(x) ((void*)(x) <= (void*)&end || (void*)(x) <= (void*)&edata)
此宏同时检查 edata
和 end
,以避免假设 .bss
和 .data
中的哪一个在内存中排在第一位。
- 堆地址。
堆变量通常直接分配在 .data
和 .bss
区域地址之后的地址中。但是,有时堆地址可能属于不连续的内存范围。因此,您在这里可以做的最好的事情是阅读 Linux 进程文件以找出其他答案中建议的内存映射。或者,只需检查 IS_IN_STACK
和 IS_STATIC
return 是否都为 false.
使用这些宏的完整程序:
int x;
extern int end, edata;
void *stack_bottom;
bool IS_IN_STACK(void *x) __attribute__((noinline));
bool IS_IN_STACK(void *x) {
void *stack_top = &stack_top;
return x <= stack_bottom && x >= stack_top;
}
#define IS_STATIC(x) ((void*)(x) <= (void*)&end || (void*)(x) <= (void*)&edata)
int foo (void)
{
int s;
int *h = malloc (sizeof(int));
printf ("x = %p, *s = %p, h = %p\n", &x, &s, h);
// prints 0 1 0
printf ("%d %d %d\n", IS_IN_STACK(&x), IS_IN_STACK(&s), IS_IN_STACK(h));
// prints 1 0 0
printf ("%d %d %d\n", IS_STATIC(&x), IS_STATIC(&s), IS_STATIC(h));
}
int main (int argc, char *argv[])
{
int x;
stack_bottom = &x;
foo();
return 0;
}