Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 从进程本身获取对进程堆元数据的访问_C_Malloc_Heap Memory_Glibc_Libc - Fatal编程技术网

C 从进程本身获取对进程堆元数据的访问

C 从进程本身获取对进程堆元数据的访问,c,malloc,heap-memory,glibc,libc,C,Malloc,Heap Memory,Glibc,Libc,虽然我可以编写合理的C代码,但我的专业知识主要是Java,因此如果这个问题毫无意义,我深表歉意 我正在编写一些代码来帮助我进行堆分析。我是通过LLVM的仪器来实现的。我要寻找的是一种从进程内部访问进程堆元数据的方法。这可能吗?我知道关于堆的信息存储在许多malloc\u statestructs中(例如main\u arena)。如果我可以访问main_arena,我可以开始枚举不同的竞技场、堆、箱等。据我所知,这些变量都是静态定义的,因此无法访问 但是有没有办法得到这些信息呢?例如,我可以使用

虽然我可以编写合理的C代码,但我的专业知识主要是Java,因此如果这个问题毫无意义,我深表歉意

我正在编写一些代码来帮助我进行堆分析。我是通过LLVM的仪器来实现的。我要寻找的是一种从进程内部访问进程堆元数据的方法。这可能吗?我知道关于堆的信息存储在许多
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[or
bfd
]库查找符号,并与公共符号的地址(例如
malloc
)合并/重新定位以查找正确的地址。如果您需要
的基址,那么
加载,您可能需要解析
/proc/self/maps
@CraigEstey不幸的是
malloc{get,set}\u状态
在相当长一段时间前在glibc 2.25中被删除。“你看,”当然是马尔科博内利。。。它们很有用。我在fedora
fc29
家里,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>