【发布时间】:2022-01-05 15:13:40
【问题描述】:
在最近的一个项目中,我测试了不同编译器标志和消毒剂的组合,以评估调试我的 C 代码的相关性。通过测试这些组合的影响,我偶然发现了一种我不理解的行为。
复制器
我使用一个包含内存泄漏的小型 hello-world 代码示例来触发地址清理程序 (ASAN):
#include<stdlib.h>
#include<stdio.h>
int main () {
int * memleak = calloc(1, sizeof(int)); // no free -> leaked memory
printf ("A memleaked memory: %d\n", *memleak);
printf ("Hello World\n"); // Note: I found that if I comment out this function, ASAN will also report again
}
观察
我使用了编译器和链接器标志的不同组合,有时我观察到地址清理程序报告了 memleak,而在其他情况下它没有报告 memleak。我已经消除了所有潜在的编译器标志,直到找到影响 ASAN 报告或忽略内存泄漏的最小标志集:
ASAN 使用命令编译时会报告内存泄漏
cc -fsanitize=address -fno-omit-frame-pointer -Og -o main.c.o -c ./main.c && cc -o hello main.c.o -fsanitize=address,undefined && ./hello # returns 1
cc -fsanitize=address,undefined -Og -o main.c.o -c ./main.c && cc -o hello main.c.o -fsanitize=address,undefined && ./hello # returns 1
cc -fsanitize=address,undefined -fno-omit-frame-pointer -o main.c.o -c ./main.c && cc -o hello main.c.o -fsanitize=address,undefined && ./hello # returns 1
ASAN使用命令编译时不会报内存泄漏
cc -fsanitize=address,undefined -fno-omit-frame-pointer -Og -o main.c.o -c ./main.c && cc -o hello main.c.o -fsanitize=address,undefined && ./hello # returns 0
然而
我观察到相同的行为,与使用 GCC 或 clang 无关。因此,我担心这不是由不同消毒剂、优化级别和标志-fno-omit-frame-pointer 之间的意外干扰引起的错误,而是我无法理解的预期行为,因为我的不知道-fno-omit-frame-pointer 的影响是什么。
如果有人可以总结 -fno-omit-frame-pointer/-fomit-frame-pointer 的作用以及在哪些情况下有效,或者解释这个标志对给定示例的影响,或者指出可以找到这些信息的地方,我会感激不尽。
为了完整性
我正在使用 Arch-linux 并且正在运行以下版本的软件:
- GCC 11.1.0-1
- clang 13.0.0-2
- glibc 2.33-5
但是,我刚刚测试并验证了示例和观察结果也适用于来自 docker-hub 的 linux/amd64 的 docker 映像 gcc:bullseye。
【问题讨论】:
-
SO 上已经有很多问题解释什么是帧指针以及省略它(或不省略)意味着什么。参见例如stackoverflow.com/questions/10057443/…、stackoverflow.com/questions/14666665/…、stackoverflow.com/questions/1395591/…、stackoverflow.com/questions/579262/…
-
因此省略帧指针是一种常见的优化,您是在告诉编译器不要使用该优化。目前尚不清楚您为什么要避免它,但无论哪种方式,它都没有充分的理由影响消毒剂,因此这可能是编译器/消毒剂错误。它会影响两个编译器也就不足为奇了,因为 AFAIK 它们共享许多相同的清理代码。
-
我认为消毒剂最初来自 clang。所以我要说的是,当您同时使用消毒剂和
-fomit-frame-pointer时,clang 似乎出现了一个错误。 (我不知道原始错误是如何产生的。)但 GCC 可能继承了相同的错误,因为它们的清理程序代码是从 clang 移植的。 -
@NateEldredge 与 [AMT]san 不同,LeakSanitizer 是一个仅限库的解决方案,因此它不依赖于编译器。
标签: c gcc clang address-sanitizer ubsan