C 从进程本身获取对进程堆元数据的访问
虽然我可以编写合理的C代码,但我的专业知识主要是Java,因此如果这个问题毫无意义,我深表歉意 我正在编写一些代码来帮助我进行堆分析。我是通过LLVM的仪器来实现的。我要寻找的是一种从进程内部访问进程堆元数据的方法。这可能吗?我知道关于堆的信息存储在许多C 从进程本身获取对进程堆元数据的访问,c,malloc,heap-memory,glibc,libc,C,Malloc,Heap Memory,Glibc,Libc,虽然我可以编写合理的C代码,但我的专业知识主要是Java,因此如果这个问题毫无意义,我深表歉意 我正在编写一些代码来帮助我进行堆分析。我是通过LLVM的仪器来实现的。我要寻找的是一种从进程内部访问进程堆元数据的方法。这可能吗?我知道关于堆的信息存储在许多malloc\u statestructs中(例如main\u arena)。如果我可以访问main_arena,我可以开始枚举不同的竞技场、堆、箱等。据我所知,这些变量都是静态定义的,因此无法访问 但是有没有办法得到这些信息呢?例如,我可以使用
malloc\u state
structs中(例如main\u arena
)。如果我可以访问main_arena
,我可以开始枚举不同的竞技场、堆、箱等。据我所知,这些变量都是静态定义的,因此无法访问
但是有没有办法得到这些信息呢?例如,我可以使用/proc/$pid/mem
以某种方式泄漏信息吗
一旦我有了这些信息,我想基本上得到所有不同自由列表的信息。所以我想知道,对于每种类型的垃圾箱,垃圾箱中的垃圾块的数量和大小。对于快速、小型和tcache垃圾箱,我知道我只需要索引来计算大小。我已经了解了这些结构是如何实现的,以及如何遍历它们。所以我所需要的就是进入这些内部结构
我已经查看了malloc\u info
,这是我的退路,但我也想获得有关tcache
的信息,我认为malloc\u info
中不包括这些信息
我考虑过的一个选项是,构建一个自定义版本的glibc,并以非静态方式声明malloc\u结构
变量。但是从我所看到的,构建您自己的定制glibc并不是很简单,因为您必须构建整个工具链。我使用的是clang,所以我必须根据我的自定义glibc从源代码构建LLVM(至少这是我从研究这种方法中了解到的)
我正在编写一些代码来帮助我进行堆分析
什么样的堆分析
我想基本上得到所有不同自由列表的信息。所以我想知道,对于每种类型的垃圾箱,垃圾箱中的垃圾块的数量和大小。对于快速、小型和tcache垃圾箱,我知道我只需要索引来计算大小
仅当您计划更改malloc
实现时,此信息才有意义。如果您的目标是分析或提高应用程序的堆使用率,那么尝试收集它是没有意义的,因此听起来您有一个
此外,像bin和tcache这样的东西只有在特定的malloc
实现的上下文中才有意义(TCMalloc和jemalloc不会有任何bin)
对于应用程序堆使用情况的分析,您可能希望使用TCmalloc,因为它提供了许多用于分析和反省的工具。我最近也有类似的要求,因此我确实认为能够访问给定进程的
main_arena
确实有其价值,例如事后内存使用情况分析
使用dl\u iterate\u phdr
和elf.h
,基于本地符号解析main\u arena
相对简单:
#define _GNU_SOURCE
#include <fcntl.h>
#include <link.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
// Ignored:
// - Non-x86_64 architectures
// - Resource and error handling
// - Style
static int cb(struct dl_phdr_info *info, size_t size, void *data)
{
if (strcmp(info->dlpi_name, "/lib64/libc.so.6") == 0) {
int fd = open(info->dlpi_name, O_RDONLY);
struct stat stat;
fstat(fd, &stat);
char *base = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
Elf64_Ehdr *header = (Elf64_Ehdr *)base;
Elf64_Shdr *secs = (Elf64_Shdr*)(base+header->e_shoff);
for (unsigned secinx = 0; secinx < header->e_shnum; secinx++) {
if (secs[secinx].sh_type == SHT_SYMTAB) {
Elf64_Sym *symtab = (Elf64_Sym *)(base+secs[secinx].sh_offset);
char *symnames = (char *)(base + secs[secs[secinx].sh_link].sh_offset);
unsigned symcount = secs[secinx].sh_size/secs[secinx].sh_entsize;
for (unsigned syminx = 0; syminx < symcount; syminx++) {
if (strcmp(symnames+symtab[syminx].st_name, "main_arena") == 0) {
void *mainarena = ((char *)info->dlpi_addr)+symtab[syminx].st_value;
printf("main_arena found: %p\n", mainarena);
raise(SIGTRAP);
return 0;
}
}
}
}
}
return 0;
}
int main()
{
dl_iterate_phdr(cb, NULL);
return 0;
}
该值与main_arena
的值匹配,因此找到了正确的地址
还有其他方法可以到达主竞技场
,而无需依赖库本身。例如,遍历现有堆可以发现main_arena
,但这种策略要简单得多
当然,一旦您拥有了
main\u arena
,您就需要所有内部类型定义来检查数据。您应该为glibc
提取源代码。在malloc
子目录下,可以检查所有源代码。特别是,在hooks.c中,有一个公共[非静态]函数:void*\uu malloc\u get\u state(void)
,它将各种子结构中的大量信息序列化为struct malloc\u save\u state
指针[这是内部分配的,因此必须释放]。由于存在一个恢复状态:\uuuu malloc\u set\u state
,我假定给定的结构具有人们可能关心的所有内容(例如主竞技场、fastbins、binmap、bins等等)glibc
还具有各种钩子函数。我相信,通过上面的钩子和其他钩子,您可以得到您想要的[和/或需要的]。我以前使用过钩子,我认为它们比尝试使用/proc/self/mem
[只映射原始内存--不太有用]更有用。如果您需要查找未导出的内容,则最好解析readelf libc.so
输出或使用ELF[orbfd
]库查找符号,并与公共符号的地址(例如malloc
)合并/重新定位以查找正确的地址。如果您需要的基址,那么
加载,您可能需要解析/proc/self/maps
@CraigEstey不幸的是malloc{get,set}\u状态
在相当长一段时间前在glibc 2.25中被删除。“你看,”当然是马尔科博内利。。。它们很有用。我在fedorafc29
家里,glibc 2.28,但我在看一个更老的(fc22
glibc源代码)。刚刚拉了fc29,是的,它不见了。它对调试很有用,但看起来它是为了实现某种针对emacs
[per comments]的黑客攻击。尽管如此,ELF还是将main_arena
作为一个本地符号,因此可以通过maps/bfd获取它的地址,并从glibc源代码中获取必要的struct
定义来拼凑一些东西。我并不是真的试图通过应用程序提高堆的使用率。我只是在收集关于每次免费后免费列表的统计数据。我还针对一个特定的malloc
实现——即glibc中使用bin和tcache的实现。这有意义吗?非常感谢。
(gdb) run
Starting program: a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0x7ffff77f0700 (LWP 24834)]
main_arena found: 0x7ffff7baec60
Thread 1 "a.out" received signal SIGTRAP, Trace/breakpoint trap.
raise (sig=5) at ../sysdeps/unix/sysv/linux/raise.c:50
50 return ret;
(gdb) select 1
(gdb) print mainarena
$1 = (void *) 0x7ffff7baec60 <main_arena>
(gdb) print &main_arena
$3 = (struct malloc_state *) 0x7ffff7baec60 <main_arena>