- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我在所有项目中都使用 AddressSanitizer 来检测内存泄漏、堆损坏等。但是,在运行时通过 dlopen 加载动态库时,AddressSanitizer 的输出有很多不足之处。我写了一个简单的测试程序来说明这个问题。代码本身并不有趣,只是两个库,一个在编译时通过 -l 链接,另一个在运行时使用 dlopen 加载。为了完整起见,这里是我用于测试的代码:
// ----------------------------------------------------------------------------
// dllHelper.hpp
#pragma once
#include <string>
#include <sstream>
#include <iostream>
#include <errno.h>
#include <dlfcn.h>
// Generic helper definitions for shared library support
#if defined WIN32
#define MY_DLL_EXPORT __declspec(dllexport)
#define MY_DLL_IMPORT __declspec(dllimport)
#define MY_DLL_LOCAL
#define MY_DLL_INTERNAL
#else
#if __GNUC__ >= 4
#define MY_DLL_EXPORT __attribute__ ((visibility ("default")))
#define MY_DLL_IMPORT __attribute__ ((visibility ("default")))
#define MY_DLL_LOCAL __attribute__ ((visibility ("hidden")))
#define MY_DLL_INTERNAL __attribute__ ((visibility ("internal")))
#else
#define MY_DLL_IMPORT
#define MY_DLL_EXPORT
#define MY_DLL_LOCAL
#define MY_DLL_INTERNAL
#endif
#endif
void* loadLibrary(const std::string& filename) {
void* module = dlopen(filename.c_str(), RTLD_NOW | RTLD_GLOBAL);
if(module == nullptr) {
char* error = dlerror();
std::stringstream stream;
stream << "Error trying to load the library. Filename: " << filename << " Error: " << error;
std::cout << stream.str() << std::endl;
}
return module;
}
void unloadLibrary(void* module) {
dlerror(); //clear all errors
int result = dlclose(module);
if(result != 0) {
char* error = dlerror();
std::stringstream stream;
stream << "Error trying to free the library. Error code: " << error;
std::cout << stream.str() << std::endl;
}
}
void* loadFunction(void* module, const std::string& functionName) {
if(!module) {
std::cerr << "Invalid module" << std::endl;
return nullptr;
}
dlerror(); //clear all errors
#ifdef __GNUC__
__extension__
#endif
void* result = dlsym(module, functionName.c_str());
char* error;
if((error = dlerror()) != nullptr) {
std::stringstream stream;
stream << "Error trying to get address of function \"" << functionName << "\" from the library. Error code: " << error;
std::cout << stream.str() << std::endl;
}
return result;
}
// ----------------------------------------------------------------------------
// testLib.hpp
#pragma once
#include "dllHelper.hpp"
#ifdef TESTLIB
#define TESTLIB_EXPORT MY_DLL_EXPORT
#else
#define TESTLIB_EXPORT MY_DLL_IMPORT
#endif
namespace TestLib {
// will be linked at compile time
class TESTLIB_EXPORT LeakerTestLib {
public:
void leak();
};
}
// ----------------------------------------------------------------------------
// testLib.cpp
#include "testLib.hpp"
namespace TestLib {
void LeakerTestLib::leak() {
volatile char* myLeak = new char[10];
(void)myLeak;
}
}
// ----------------------------------------------------------------------------
// testLibRuntime.hpp
#pragma once
#include "dllHelper.hpp"
#ifdef TESTLIBRUNTIME
#define TESTLIBRUNTIME_EXPORT MY_DLL_EXPORT
#else
#define TESTLIBRUNTIME_EXPORT MY_DLL_IMPORT
#endif
namespace TestLibRuntime {
// will be loaded via dlopen at runtime
class TESTLIBRUNTIME_EXPORT LeakerTestLib {
public:
void leak();
};
}
extern "C" {
TestLibRuntime::LeakerTestLib* TESTLIBRUNTIME_EXPORT createInstance();
void TESTLIBRUNTIME_EXPORT freeInstance(TestLibRuntime::LeakerTestLib* instance);
void TESTLIBRUNTIME_EXPORT performLeak(TestLibRuntime::LeakerTestLib* instance);
}
// ----------------------------------------------------------------------------
// testLibRuntime.cpp
#include "testLibRuntime.hpp"
namespace TestLibRuntime {
void LeakerTestLib::leak() {
volatile char* myLeak = new char[10];
(void)myLeak;
}
extern "C" {
LeakerTestLib* createInstance() {
return new LeakerTestLib();
}
void freeInstance(LeakerTestLib* instance) {
delete instance;
}
void performLeak(LeakerTestLib* instance) {
if(instance) {
instance->leak();
}
}
}
}
// ----------------------------------------------------------------------------
// main.cpp
#include "testLib.hpp"
#include "testLibRuntime.hpp"
#define LEAK_TESTLIB
#define LEAK_TESTLIBRUNTIME
int main(int argc, char** argv) {
#ifdef LEAK_TESTLIBRUNTIME
void* testLibRuntimeModule = loadLibrary("libtestLibRuntime.so");
if(!testLibRuntimeModule) {
return -1;
}
TestLibRuntime::LeakerTestLib* testLibRuntime = nullptr;
auto createInstance = (TestLibRuntime::LeakerTestLib * (*)())loadFunction(testLibRuntimeModule, "createInstance");
if(!createInstance) {
return -1;
}
auto freeInstance = (void(*)(TestLibRuntime::LeakerTestLib*))loadFunction(testLibRuntimeModule, "freeInstance");
if(!freeInstance) {
return -1;
}
auto performLeak = (void(*)(TestLibRuntime::LeakerTestLib*))loadFunction(testLibRuntimeModule, "performLeak");
if(!performLeak) {
return -1;
}
testLibRuntime = createInstance();
performLeak(testLibRuntime);
freeInstance(testLibRuntime);
#endif
#ifdef LEAK_TESTLIB
TestLib::LeakerTestLib testLib;
testLib.leak();
#endif
#ifdef LEAK_TESTLIBRUNTIME
unloadLibrary(testLibRuntimeModule);
#endif
return 0;
}
我使用以下命令编译了上面的代码:
clang++ -std=c++11 -O0 -g -ggdb -Wl,-undefined -Wl,dynamic_lookup -fsanitize=address -fsanitize-recover=address -fno-omit-frame-pointer -fsanitize-address-use-after-scope -DTESTLIB -shared -fPIC -o libtestLib.so testLib.cpp -ldl -shared-libasan
clang++ -std=c++11 -O0 -g -ggdb -Wl,-undefined -Wl,dynamic_lookup -fsanitize=address -fsanitize-recover=address -fno-omit-frame-pointer -fsanitize-address-use-after-scope -DTESTLIBRUNTIME -shared -fPIC -o libtestLibRuntime.so testLibRuntime.cpp -ldl -shared-libasan
clang++ -std=c++11 -O0 -g -ggdb -Wl,-undefined -Wl,dynamic_lookup -fsanitize=address -fsanitize-recover=address -fno-omit-frame-pointer -fsanitize-address-use-after-scope -o leak main.cpp -ldl -L./ -ltestLib -shared-libasan
当我运行该程序时,我得到以下输出(我必须事先导出 LD_LIBRARY_PATH 才能找到 libasan):
$ export LD_LIBRARY_PATH=/usr/lib/clang/4.0.0/lib/linux/:./
$ ./leak
=================================================================
==4210==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 10 byte(s) in 1 object(s) allocated from:
#0 0x7fb665a210f0 in operator new[](unsigned long) (/usr/lib/clang/4.0.0/lib/linux/libclang_rt.asan-x86_64.so+0x10e0f0)
#1 0x7fb66550d58a in TestLib::LeakerTestLib::leak() /home/jae/projects/clang_memcheck/testLib.cpp:6:29
#2 0x402978 in main /home/jae/projects/clang_memcheck/main.cpp:37:13
#3 0x7fb6648d4439 in __libc_start_main (/usr/lib/libc.so.6+0x20439)
Direct leak of 10 byte(s) in 1 object(s) allocated from:
#0 0x7fb665a210f0 in operator new[](unsigned long) (/usr/lib/clang/4.0.0/lib/linux/libclang_rt.asan-x86_64.so+0x10e0f0)
#1 0x7fb6617fd6da (<unknown module>)
#2 0x7fb6617fd75f (<unknown module>)
#3 0x402954 in main /home/jae/projects/clang_memcheck/main.cpp:31:5
#4 0x7fb6648d4439 in __libc_start_main (/usr/lib/libc.so.6+0x20439)
SUMMARY: AddressSanitizer: 20 byte(s) leaked in 2 allocation(s).
虽然检测到泄漏,但 AddressSanitizer 似乎无法解析通过 dlopen 加载的库的模块名称、函数名称和行号(而是打印 (
是否可以使用一些编译器开关来解决此问题,或者当涉及到使用 dlopen 加载的库时,是否无法使用 AddressSanitizer 获取更多信息?显然可以找到 llvm-symbolizer,否则不会有其他库的行号。用
运行程序ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolizer ./leak
不会导致不同的输出。我改用 g++ 编译程序,但输出保持不变。我还通过 asan_symbolize.py 传输了输出,但没有任何改变。我不知道接下来要看哪里。我的想法有根本性的错误吗?在动态加载库方面,我不是专家。
最佳答案
在跟踪动态加载库中的此类问题时,我一直在走捷径,但出于测试目的,我只是省略了库卸载代码,因此当程序终止时,符号仍可用于清理器(和 valgrind)。虽然这样做可能会导致一些错误的泄漏检测,因为 dlopen 分配的人员不会被释放。
这个问题似乎没有合适的解决方案,因为从技术上讲,在卸载库之后,没有什么可以阻止在同一地址加载另一个库。
关于c++ - AddressSanitizer 和运行时加载动态库 -> (<unknown module>),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44627258/
所以我正在使用 AddressSanitizer。但是在描述问题时它使用了一些密集的术语。 Shadow bytes around the buggy address: 0x0c067fff7fb
想要抑制 AddressSanitizer 检测到的以下错误。 ==114064==ERROR: AddressSanitizer: stack-use-after-scope on address
我试图抑制 clang/gcc 中地址清理程序发出的警告 我的源文件如下所示: int foo(){ double bar[] = {7,8}; return bar[3]; } int ma
我有一个管理容器的类。我的 insert-a-value 函数返回一个对象,其析构函数删除了插入的值。这个想法是我可以在容器中注册一个值,保存结果 Registration对象,然后让该对象超出范围以
我这样编译代码来启用 Asan: g++ -O0 -g -fsanitize=address -fno-omit-frame-pointer 但它永远不会生成核心转储,以便我稍后可以检查错误的详细信息
似乎 AddressSanitizer 检测到 heap-buffer-overflow 但输出不那么清晰,例如 here . 如何获取发生溢出的行? 我该如何解释这个结果? [1m[31m==274
我的项目广泛使用 SDL2-2.0.8 来显示来自各种科学成像相机的数据帧。我的实际项目使用的是 wxWidgets 3.1.1 和 SDL_CreateWindowFrom(xid),而不是 SDL
我正在开发一个 C++ cmake 项目。除了我自己的源代码外,我的项目还使用了很多第三方库。因此,我正在使用/usr/local/lib 中存在的共享库(带有 .so 扩展名),对于某些代码,它们存
我最近开始使用 XCode AddressSanitizer,我认为它是在 XCode 7 中引入的(参见例如 Apple WWDC presentation ),以帮助解决难以发现的奇怪错误。 sa
以下代码计算两个相邻元素之间的最大气体。输入 vector 未排序。 class Solution { public: int maximumGap(vector& nums) { int m
我试图让 ASAN 使用一个程序,但我所做的任何事情都会导致 ASAN:DEADLYSIGNAL ,所以我尝试缩小范围并使用只有几个编译器选项的小型测试程序,只是为了看看它是否可以工作: $ cat
我应该使用哪些 Asan 标志来检测更多错误?目前,我使用 ASAN_OPTIONS=detect_stack_use_after_return=1 . 最佳答案 来自 Asan FAQ : Q: C
为了能够对整个 Linux 发行版进行调试和模糊测试,我想将 ASAN(AddressSanitizer,https://en.wikipedia.org/wiki/AddressSanitizer)
我正在查看我的一个守护进程的问题,它非常耗费内存。看起来某些部分永远不会释放内存,即使当我让那个守护进程干净地退出时,也没有泄漏。 我想看看在守护进程运行时添加和保留了哪些缓冲区。由于我已经使用 -f
AddressSanitizer 默认情况下会将所有错误抛给 shell 本身,因此我尝试使用以下命令运行我的 ASAN 构建; >MCTester_ASAN>asan.log ==15619==ER
我在所有项目中都使用 AddressSanitizer 来检测内存泄漏、堆损坏等。但是,在运行时通过 dlopen 加载动态库时,AddressSanitizer 的输出有很多不足之处。我写了一个简单
AddressSanitizer 和 MemorySanitizer 是非常有用的工具,但它们需要对整个程序进行适当的检测。 (至少,对于 AddressSanitizer 的 Clang 版本;请参
我有几个版本的项目结帐和编译。如果我发现错误,我会比较版本以缩小问题范围。有时我会启用像 AddressSanitizer 这样的 sanitizer 。如果我重新使用一个可执行文件,我不记得它是否是
我有一个自定义 assert() - 类似宏调用 abort()失败。使用 AddressSanitizer 时,我更喜欢在断言失败时打印通常的漂亮堆栈跟踪。如何做到这一点? 是否可以在 abort(
或者我必须使用单独的版本吗? -fsanitize 标志仅允许地址或线程,但是否允许多个? 问候 最佳答案 不,不可能将 AddressSanitizer 和 ThreadSanitizer 组合到一
我是一名优秀的程序员,十分优秀!