Skip to main content

在Qt中使用gcc 4.8.0的地址消毒剂(Address Sanitizer)

Comments

原文链接:Kai Koehne - Using gcc’s 4.8.0 Address Sanitizer with Qt

gcc 4.8的一个很酷的新特性是内建的“地址消毒剂”:C/C++的内存错误检测器,例如,如果您访问了已经删除的内存,它会立刻报告。实际上这是一个来自Clang/LLVM的Google项目,对于LLVM用户这可能不足为奇了,但对我来说不是:)。官方网站上的文档很令人生畏,我在这里讲些怎样更好的使用的要点,特别是在Qt环境中……

它是怎样工作的?

它基本上是重写了malloc和free,并且在访问之前做内存检查(详情请看项目wiki)。很显然它采用了一种非常高效的方式,因为相比于不做检查执行仅缓慢了2倍!也许在未来某个时候,我们可以在Qt-Project CI系统中开启它,谁知道呢?

警告:这种方式目前只能工作在Linux和Mac上。MinGW很不幸。

怎样开启它?

由于它是编译器套件的一部分,开启它很容易:只要在编译器调用中添加-fsanitize=address -fno-omit-frame-pointer,在链接器调用中添加-fsanitize=address。无论怎样,要捕捉内存分配、释放或者被Qt访问的问题,在您的应用程序和Qt中,都需要加上新的编译参数。针对Qt 5.2有一个实验性的补丁使之变得容易:

https://codereview.qt-project.org/#change,43420

因为是一个新特性,它被安排在dev分支(Qt 5.2)。但是您可以把它cherry-pick到Qt 5.0。然后您在配置Qt时使用-address-sanitizer,为您的程序执行qmake CONFIG+=address_sanitizer。

如果您不想cherry-pick,您也可以手动为qmake设置命令行参数MAKE_CXXFLAGS、QMAKE_CFLAGS和QMAKE_LFLAGS:

$ qmake QMAKE_CXXFLAGS+="-fsanitize=address -fno-omit-frame-pointer" \
QMAKE_CFLAGS+="-fsanitize=address -fno-omit-frame-pointer" \
QMAKE_LFLAGS+="-fsanitize=address"

怎样使用它?

只需要运行您的程序!如果您遇到了内存问题,它会终止程序的运行,并且向您显示一个带模块名和地址的栈追踪。您需要一个叫做asan_symbolize.py的工具来获取符号(symbol),之后可能会用到c++filt来解析(de-mangle)C++符号。

演示!

$ mkdir addresssanitizertest

$ echo "
#include
int main(int, char *[]) {
const char *str = QString("Evil!").toLocal8Bit().constData();
qDebug() < addresssanitizertest/main.cpp

$ cd addresssanitizertest && qmake -project && qmake CONFIG+=address_sanitizer

$ ./addresssanitizertest 2>&1 | asan_symbolize.py | c++filt
=================================================================
==32195== ERROR: AddressSanitizer: heap-use-after-free on address 0x600c0000bcd8 at pc 0x4016ce bp 0x7fff7ccd86c0 sp 0x7fff7ccd86b8
READ of size 1 at 0x600c0000bcd8 thread T0
#0 0x4016cd in QString::fromUtf8(char const*, int) /home/kkoehne/dev/qt/qt-5.1-gcc-4.8.0-64/qtbase/include/QtCore/../../../../qt-5.1/qtbase/src/corelib/tools/qstring.h:478
#1 0x401b1e in QDebug::operator<0x0c01ffff9790: fa fa fa fa fa fa fa fa fd fd fd[fd]fd fd fd fd
0x0c01ffff97a0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
0x0c01ffff97b0: 00 00 00 00 00 00 00 fa fa fa fa fa 00 00 00 00
0x0c01ffff97c0: 00 00 00 fa fa fa fa fa 00 00 00 00 00 00 00 fa
0x0c01ffff97d0: fa fa fa fa fd fd fd fd fd fd fd fa fa fa fa fa
0x0c01ffff97e0: 00 00 00 00 00 00 01 fa fa fa fa fa 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap righ redzone: fb
Freed Heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
ASan internal: fe
==32195== ABORTING

享受追踪内存问题的乐趣吧:)

更新:显然,现在的gcc 4.8.0二进制包里的地址消毒剂还有很多问题:libasan没有自动链接、qtbase中会出现一个内部编译器错误等等……我个人现在使用的是gcc的4.8分支

Comments

Subscribe to our blog

Try Qt 6.11 Now!

Download the latest release here: www.qt.io/download

Qt 6.11 is now available, with new features and improvements for application developers and device creators.

We're Hiring

Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.