从进程内部获得对进程堆元数据的访问权。

问题描述 投票:1回答:2

虽然我可以写出合理的C代码,但我的专业知识主要是Java,所以如果这个问题没有意义,我很抱歉。

我正在写一些代码来帮助我做堆分析。我是通过LLVM的工具来完成的。我需要的是一种从进程内部访问堆元数据的方法。这样的事情是可能的吗?我知道关于堆的信息存储在许多的 malloc_state 结构(main_arena 比如说)。) 如果我可以访问 main_arena我可以开始列举不同的竞技场、堆、垃圾箱等。据我所知,这些变量都是静态定义的,所以不能被访问。

但是有没有什么方法可以获得这些信息呢?例如,我可以使用 /proc/$pid/mem 以某种方式泄露信息?

一旦我有了这些信息,我想基本上得到所有不同自由列表的信息。所以,我想要,对于每一个bin类型的bin,bin中的chunks数量和它们的大小。对于快、小、tcache的bin,我知道我只需要索引就可以算出大小。我已经研究了这些结构是如何实现的,以及如何在其中进行迭代。所以我需要的就是获得对这些内部结构的访问权。

我已经研究了 malloc_info 这是我的退路,但我也想得到有关的信息。tcache 而我认为这并不包括在... ... malloc_info.

我考虑的一个方案是建立一个自定义版本的glibc有 malloc_struct 非静态声明的变量。但从我所看到的情况来看,构建自己的自定义glibc并不是很直接,因为你必须构建整个工具链。我使用的是clang,所以我必须针对我的自定义glibc从源头构建LLVM(至少这是我从研究这种方法中了解到的)。

c malloc heap-memory glibc libc
2个回答
1
投票

我最近有一个类似的需求,所以我确实认为,能够进入到 main_arena 对于一个给定的进程来说,确实有它的价值,其中一个例子是事后的内存使用分析。

使用 dl_iterate_phdrelf.h,它是比较直接地解决了 main_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;
}

dl_iterate_phdr 是用来获取映射的glibc的基地址。该映射不包含所需的符号表(.symtab),所以必须再次对库进行映射。最后的地址是由基址加上符号值决定的。

(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>

符号值与 main_arena所以找到了正确的地址。

还有其他 方式 要到 main_arena 而不依赖于库本身。在现有的堆中行走,可以发现 main_arena比如说,但这个策略就不那么简单了。

当然,一旦你有了 main_arena,你需要所有的内部类型定义才能检查数据。


0
投票

我正在写一些代码来帮助我做堆分析。

什么样的堆分析?

我想基本得到所有不同自由列表的信息。所以我想,对于每一个bin类型的bin,bin中的chunks数量和它们的大小。对于快、小、tcache的bin,我知道我只需要索引来计算大小。

这些信息 只是 如果你打算改变 malloc 落实。它确实 如果你的目标是分析或改进堆的使用情况,那么尝试收集它是有意义的。申请所以,听起来你有一个 XY问题.

此外,像bin和tcache这样的东西只有在特定的上下文中才有意义。malloc (TCMalloc和jemalloc不会有任何仓位)。

为了分析应用程序堆的使用情况,你可能需要使用 TCmalloc,因为它提供了一个 大量 的工具 堆剖析 和反省。

© www.soinside.com 2019 - 2024. All rights reserved.