在 valgrind 下 运行 时分配内存的地址不一样

Address of allocated memory not the same when running under valgrind

我正在测试 valgrind 并且有这个泄漏 4 个字节的小 C 程序:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int* x = malloc(sizeof(int));
    printf( "Address: %p\n", x);
    return 0;
}

我编译它:gcc -g -o leak leak.c,运行它:

$ leak
Address: 0x55a72e303260
$ leak
Address: 0x55f370273260

所以它为两个单独的 运行 显示了两个不同的地址。但是,如果我 运行 它在 valgrind 下它总是显示相同的地址:0x4a66040:

$ valgrind --leak-check=full --show-leak-kinds=all  leak
==8186== Memcheck, a memory error detector
==8186== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8186== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==8186== Command: leak
==8186== 
Address: 0x4a66040
==8186== 
==8186== HEAP SUMMARY:
==8186==     in use at exit: 4 bytes in 1 blocks
==8186==   total heap usage: 2 allocs, 1 frees, 1,028 bytes allocated
==8186== 
==8186== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==8186==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==8186==    by 0x109156: main (leak.c:6)
==8186== 
==8186== LEAK SUMMARY:
==8186==    definitely lost: 4 bytes in 1 blocks
==8186==    indirectly lost: 0 bytes in 0 blocks
==8186==      possibly lost: 0 bytes in 0 blocks
==8186==    still reachable: 0 bytes in 0 blocks
==8186==         suppressed: 0 bytes in 0 blocks
==8186== 
==8186== For counts of detected and suppressed errors, rerun with: -v
==8186== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

为什么会这样?并且valgrind是否可以显示内存的真实地址?

分配内存的"real"地址。

Valgrind 将您的 malloc() 实现替换为它自己的版本,以便它可以做它做的事情,请参阅以下行:

==8186==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)

因此,malloc() 的 "normal" libc 实现与 valgrind 的实现之间存在差异的是获取内存的底层调用。可能 libc 使用 brk() 而 valgrind 使用 mmap().

brk() 遵循 Address Space Layout Randomization 的规则,而 mmap() 允许显式选择要映射新分配内存的虚拟内存地址。

编辑:strace 两个版本,显示:

libc:

brk(NULL)                               = 0x5611e25ec000
brk(0x5611e260d000)                     = 0x5611e260d000
fstat(1, {st_dev=makedev(0, 22), st_ino=67, st_mode=S_IFCHR|0620, st_nlink=1, st_uid=1000, st_gid=5, st_blksize=1024, st_blocks=0, st_rdev=makedev(136, 64), st_atime=1566292432 /* 2019-08-20T11:13:52.993864629+0200 */, st_atime_nsec=993864629, st_mtime=1566292432 /* 2019-08-20T11:13:52.993864629+0200 */, st_mtime_nsec=993864629, st_ctime=1564479628 /* 2019-07-30T11:40:28.009864433+0200 */, st_ctime_nsec=9864433}) = 0
write(1, "Address: 0x5611e25ec260\n", 24) = 24

valgrind:

mmap(0x4c2c000, 2158912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x4c2c000
...
write(1027, "==18842==    at 0x4C2FB0F: mallo"..., 91) = 91

所以实际上 Valgrind 明确选择了在哪里映射它的内存池。