Valgrind内存检测工具显示0个泄漏,尽管可能存在内存泄漏。

3
我需要调查我C项目中的内存泄漏问题。但在此之前,我只是编写了一个示例程序,并交叉编译到ARM-v8-a 64位OpenWRT Linux平台上。通过这个程序,我试图理解Valgrind泄漏报告。 不幸的是,在这次valgrind运行中,没有发现任何泄漏,并且期望memcheck工具报告一个50字节的泄漏摘要。
Valgrind工具优化:是的,使用-Os(OpenWRT)和-O2(Valgrind Makefile) 二进制优化:否 使用的libc标准:MUSL OpenWRT ARM 64位
为什么Valgrind无法拦截malloc调用,即使二进制执行了malloc调用?
示例代码:
char *ptr = NULL;
printf("Doing memory alloc\n");
ptr = malloc(50);
if (!ptr) {
    printf("Malloc failure\n");
    return -1;
}
printf("Malloc returned : %p\n", (void *)ptr);
memset(ptr, 0, 50);
strcpy(ptr, "Valgrind leak Analysis!!!\n");
printf("%s\n", ptr);
return 0;

Valgrind 输出:
==481==
--481-- Valgrind options:
--481--    --tool=memcheck
--481--    --leak-check=full
--481--    --show-leak-kinds=all
--481--    --track-origins=yes
--481--    --verbose
--481--    --default-suppressions=no
--481-- Contents of /proc/version:
--481--   Linux version 5.4.164 (renv@bbd-svr-ubt20-004) (gcc version 7.5.0 (OpenWrt GCC 7.5.0 r11-afafb2b1c)) #0 SMP PREEMPT Mon Jun 5 15:00:29 2023
--481--
--481-- Arch and hwcaps: ARM64, LittleEndian, baseline
--481-- Page sizes: currently 4096, max supported 65536
--481-- Valgrind library directory: /usr/lib/valgrind
--481-- Reading syms from /home/superadmin/leaks
--481-- Scheduler: using generic scheduler lock implementation.


Doing memory alloc
Malloc returned : 0x40945e0
Valgrind leak Analysis!!!

==481==
==481== HEAP SUMMARY:
==481==     in use at exit: 0 bytes in 0 blocks
==481==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==481==
==481== All heap blocks were freed -- no leaks are possible
==481==
==481== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

以下是我的二进制文件和valgrind文件类型信息:
root@Bean10:/home/superadmin# file leaks
leaks: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, with debug_info, not stripped
root@Bean10:/home/superadmin#
root@Bean10:/home/superadmin# file /usr/bin/valgrind
/usr/bin/valgrind: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-aarch64.so.1, no section header
root@Bean10:/home/superadmin#
root@Bean10:/home/superadmin# ls /usr/lib/valgrind/
64bit-core-valgrind-s1.xml         64bit-linux-valgrind-s2.xml        memcheck-arm64-linux
64bit-core-valgrind-s2.xml         64bit-linux.xml                    none-arm64-linux
64bit-core.xml                     default.supp                       vgpreload_core-arm64-linux.so
64bit-linux-valgrind-s1.xml        leaks                              vgpreload_memcheck-arm64-linux.so
root@Bean10:/home/superadmin#

使用的编译命令:

请注意,这是我项目中使用的交叉工具链编译器。如何检查它是否隐式地使用了优化标志。

aarch64-openwrt-linux-musl-gcc -g leaks.c

编译器版本详细信息:
aarch64-openwrt-linux-musl-gcc (OpenWrt GCC 7.5.0 r11-afafb2b1c) 7.5.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

来自目标文件的符号信息:
nm显示malloc和其他几个函数为未定义符号。

$ aarch64-openwrt-linux-musl-nm leaks.o
0000000000000000 T main
                 U malloc
                 U memset
                 U printf
                 U puts

$ aarch64-openwrt-linux-musl-strings -a leaks.o
Doing memory alloc
Malloc failure
Malloc returned : %p
Valgrind leak Analysis!!!
GCC: (OpenWrt GCC 7.5.0 r11-afafb2b1c) 7.5.0
leaks.c
main
puts
malloc
printf
memset
.symtab
.strtab
.shstrtab
.rela.text
.data
.bss
.rodata
.comment
.note.GNU-stack
$

objdump 输出包含文本和 PLT 段:

$ aarch64-openwrt-linux-musl-objdump -D leaks

分解 .plt 部分:

.....
0000000000400530 <malloc@plt>:
  400530:   b0000090    adrp    x16, 411000 <printf>
  400534:   f9400a11    ldr x17, [x16, #16]
  400538:   91004210    add x16, x16, #0x10
  40053c:   d61f0220    br  x17

0000000000400540 <memset@plt>:
  400540:   b0000090    adrp    x16, 411000 <printf>
  400544:   f9400e11    ldr x17, [x16, #24]
  400548:   91006210    add x16, x16, #0x18
  40054c:   d61f0220    br  x17

.text部分的拆解:
......
000000000040060c <main>:
  40060c:   a9be7bfd    stp x29, x30, [sp, #-32]!
  400610:   910003fd    mov x29, sp
  400614:   f9000fbf    str xzr, [x29, #24]
  400618:   90000000    adrp    x0, 400000 <_init-0x4d8>
  40061c:   911b0000    add x0, x0, #0x6c0
  400620:   97ffffc0    bl  400520 <puts@plt>
  400624:   d2800640    mov x0, #0x32                   // #50
  400628:   97ffffc2    bl  400530 <malloc@plt>
  40062c:   f9000fa0    str x0, [x29, #24]
  400630:   f9400fa0    ldr x0, [x29, #24]
  400634:   f100001f    cmp x0, #0x0
  400638:   540000c1    b.ne    400650 <main+0x44>  // b.any
  40063c:   90000000    adrp    x0, 400000 <_init-0x4d8>
  400640:   911b6000    add x0, x0, #0x6d8
  400644:   97ffffb7    bl  400520 <puts@plt>
  400648:   12800000    mov w0, #0xffffffff             // #-1
  40064c:   14000016    b   4006a4 <main+0x98>
  400650:   90000000    adrp    x0, 400000 <_init-0x4d8>
  400654:   911ba000    add x0, x0, #0x6e8
  400658:   f9400fa1    ldr x1, [x29, #24]
  40065c:   97ffffad    bl  400510 <printf@plt>
  400660:   d2800642    mov x2, #0x32                   // #50
  400664:   52800001    mov w1, #0x0                    // #0
  400668:   f9400fa0    ldr x0, [x29, #24]
  40066c:   97ffffb5    bl  400540 <memset@plt>
  400670:   f9400fa2    ldr x2, [x29, #24]
  400674:   90000000    adrp    x0, 400000 <_init-0x4d8>
  400678:   911c0001    add x1, x0, #0x700
  40067c:   aa0203e0    mov x0, x2
  400680:   a9400c22    ldp x2, x3, [x1]
  400684:   a9000c02    stp x2, x3, [x0]
  400688:   f9400822    ldr x2, [x1, #16]
  40068c:   f9000802    str x2, [x0, #16]
  400690:   b8417021    ldur    w1, [x1, #23]
  400694:   b8017001    stur    w1, [x0, #23]
  400698:   f9400fa0    ldr x0, [x29, #24]
  40069c:   97ffffa1    bl  400520 <puts@plt>
  4006a0:   52800000    mov w0, #0x0                    // #0
  4006a4:   a8c27bfd    ldp x29, x30, [sp], #32
  4006a8:   d65f03c0    ret

Valgrind是使用以下优化和加固选项构建的。
-O2 -g -Wall -Wmissing-prototypes -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-declarations -Wcast-align -Wcast-qual -Wwrite-strings -Wempty-body -Wformat -Wformat-signedness -Wformat-security -Wignored-qualifiers -Wmissing-parameter-type -Wlogical-op -Wimplicit-fallthrough=2 -Wold-style-declaration -finline-functions -fno-stack-protector -fno-strict-aliasing -fno-builtin  -O2 -Os -pipe -march=armv8-a -mcpu=cortex-a73+crypto -fno-caller-saves -fno-plt -Wa,--noexecstack -fhonour-copts -Wno-error=unused-but-set-variable -Wno-error=unused-result -iremap/home/renv/BBD_R2302OpenWrtB/working/QSDK12/qsdk/build_dir/target-aarch64/valgrind-3.15.0:valgrind-3.15.0 -Wformat -Werror=format-security -fPIC -D_FORTIFY_SOURCE=2 -Wl,-z,now -Wl,-z,relro

期望得到一些帮助/指引以继续前进。

2
@h0r53 - 请在描述中找到更新的objdump详细信息,我注意到转储中有mallocmemset。如果需要更多细节,请留言。 - renga_in_stack
3
假设Valgrind本身没有错误,使用优化或不使用优化编译它都不应该有影响。它是一个操作数据的程序,巧合的是这些数据是另一个正在运行的程序。如果编写正确,使用优化编译Valgrind不应该改变可观察的效果(输出和对测试程序的操作)。唯一重要的是你自己的代码是如何编译的。所以关于Valgrind构建选项的细节最多只能作为脚注,而不是首要提及的内容。哦,但我看到你的问题实际上是在问是否可以使用优化的Valgrind。是的,可以。 - Peter Cordes
3
假设Valgrind本身没有错误,使用优化或不使用优化编译它都不应该有影响。它是一个操作数据的程序,恰好是另一个正在运行的程序。如果编写正确,使用优化编译Valgrind不应改变可观察的效果(输出和对测试程序的操作)。唯一重要的是你自己的代码是如何编译的。所以关于Valgrind构建选项的细节最多只能作为脚注,而不是首要提及。哦,但我看到你的问题实际上是在问是否可以使用优化的Valgrind。是的,可以。 - Peter Cordes
2
不知道。它能检测到任何内存泄漏吗?比如说,你能测试一下 Valgrind 在你的 MUSL 设置中是否完全正常吗?也许可以用一个长时间运行的程序来测试,该程序通过多次调用 malloc 来泄漏内存。而且还要分配多个页面的内存,以防万一,就像如果 malloc 内部只在单个页面内移动 brk 一样。 - Peter Cordes
2
不知道。它能检测到任何内存泄漏吗?比如说,你能测试一下在你的MUSL设置中Valgrind是否完全正常吗?也许可以使用一个长时间运行的程序来泄漏多次调用malloc分配的内存?而且还要分配多个页面的内存,以防万一,就像如果malloc在内部只是在单个页面内移动brk一样。 - Peter Cordes
显示剩余55条评论
1个回答

5
很可能是由于优化而删除了malloc,因为您的ptr变量从未被使用。
以下是使用ARM64 gcc 13.1(linux)提供-O3优化标志时生成的汇编代码。如您所见,malloc调用已被删除。
.LC0:
        .string "Doing memory alloc"
main:
        stp     x29, x30, [sp, -16]!
        adrp    x0, .LC0
        add     x0, x0, :lo12:.LC0
        mov     x29, sp
        bl      puts
        mov     w0, 0
        ldp     x29, x30, [sp], 16
        ret

更新

OP提供了二进制文件的反汇编,显示调用了malloc(50),这表明该程序实际上是未经过优化编译的,OP很可能在valgrind中找到了一个错误。我将保留我的答案供将来遇到类似问题且罪魁祸首是编译器优化的人参考。


1
@h0r53 OP表示:“我在编译过程中没有使用任何优化标志。”所以,不,链接器没有进行任何优化:“在生成最终的二进制文件时,GCC只对包含字节码的文件应用链接时优化。”请注意,OP正在使用GCC 7.5。使用GCC 13.1得出的结论是不相关的。 - Andrew Henle
3
@renga_in_stack,感谢您提供二进制文件的汇编代码。确实,汇编代码显示了对malloc(50)的调用。这表明程序实际上确实调用了malloc,但由于某种原因,valgrind没有报告内存泄漏。 - h0r53
3
@renga_in_stack,感谢您提供二进制文件的汇编代码。确实,汇编代码显示了对malloc(50)的调用。这表明程序实际上确实调用了malloc,但由于某种原因,valgrind没有报告内存泄漏。 - h0r53
3
@renga_in_stack,感谢您提供二进制文件的汇编代码。确实,汇编代码显示了对malloc(50)的调用。这意味着程序实际上确实调用了malloc,但由于某种原因,valgrind没有报告内存泄漏。 - undefined
1
@h0r53- 感谢确认 malloc 被调用。我刚在 Valgrind 社区论坛上搜索了一个类似的 ARM 平台问题 https://bugs.kde.org/show_bug.cgi?id=469565 的报告。 - renga_in_stack
显示剩余35条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接