mar*_*ric 7 c++ linux profiling g++ shared-libraries
这些天我在Ubuntu上用g ++共享对象(.so)文件测试-finstrument-functions.我发现了一个奇怪的行为 - 只有在静态链接库时,-finstrument-functions似乎才有效.如果我用dlopen/dlsym等链接到库,代码的功能仍然有效,但它不会调用__cyg_profile*函数.
以下是一些快速重现问题的代码:
MyLib.h
#ifndef __MYLIB_H__
#define __MYLIB_H__
class MyLib
{
public:
void sayHello();
};
#endif
Run Code Online (Sandbox Code Playgroud)
MyLib.cpp
#include "MyLib.h"
#include <iostream>
using namespace std;
void MyLib::sayHello()
{
cout<<"Hello"<<endl;
}
Run Code Online (Sandbox Code Playgroud)
MyLibStub.cpp(.so的C接口)
#include "MyLib.h"
extern "C" void LoadMyLib ()
{
MyLib().sayHello();
}
Run Code Online (Sandbox Code Playgroud)
Trace.cpp
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
void __cyg_profile_func_enter(void *this_fn, void *call_site)
__attribute__((no_instrument_function));
void __cyg_profile_func_exit(void *this_fn, void *call_site)
__attribute__((no_instrument_function));
}
#endif
void __cyg_profile_func_enter(void* this_fn, void* call_site)
{
printf("entering %p\n", (int*)this_fn);
}
void __cyg_profile_func_exit(void* this_fn, void* call_site)
{
printf("exiting %p\n", (int*)this_fn);
}
Run Code Online (Sandbox Code Playgroud)
MainStatic.cpp
#include <iostream>
using namespace std;
extern "C" void LoadMyLib ();
int main()
{
LoadMyLib();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
MainDynamic.cpp
#include <iostream>
#include <dlfcn.h>
const char* pszLibName = "libMyLib.so.0.0";
const char* pszFuncName = "LoadMyLib";
int main()
{
void* pLibHandle = dlopen(pszLibName, RTLD_NOW);
if(!pLibHandle) {
return 1;
}
void (*pFuncLoad)() = 0;
//Resolve the function in MyLibStub.cpp
pFuncLoad = (void (*)())dlsym(pLibHandle, pszFuncName);
if(!pFuncLoad) {
return 1;
}
pFuncLoad();
dlclose(pLibHandle);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
并使用以下命令进行编译(在Ubuntu 11.10下):
Run Code Online (Sandbox Code Playgroud)g++ -g -finstrument-functions -Wall -Wl,-soname,libMyLib.so.0 -shared -fPIC -rdynamic MyLib.cpp MyLibStub.cpp Trace.cpp -o libMyLib.so.0.0 ln -s libMyLib.so.0.0 libMyLib.so.0 ln -s libMyLib.so.0.0 libMyLib.so g++ MainStatic.cpp -g -Wall -lMyLib -L./ -o MainStatic g++ MainDynamic.cpp -g -Wall -ldl -o MainDynamic
什么时候打电话 ./MainStatic
它给出了类似的东西:
Run Code Online (Sandbox Code Playgroud)entering 0xb777693f entering 0xb777689b exiting 0xb777689b exiting 0xb777693f entering 0xb7776998 entering 0xb777680c Hello exiting 0xb777680c exiting 0xb7776998
然而,当被召唤时 ./MainDynamic
它只给出一个"你好".
Run Code Online (Sandbox Code Playgroud)Hello
这里有人知道为什么静态和动态链接库之间存在这种差异吗?是否有任何解决方案使其即使在动态加载时也能正常工作?提前致谢.
此行为是预期的.
为了理解它,首先需要知道动态加载器按照ELF
加载不同图像的顺序使用链表搜索符号.在该列表的头部是主要的可执行文件本身,然后是直接链接到它的所有库.当您使用dlopen()
某个库时,它会附加到列表的尾部.
因此,当您刚加载调用的库中的代码时__cyg_profile_func_enter
,加载程序将在列表中搜索该函数的第一个定义.第一个定义恰好是默认值,由libc.so.6提供,它靠近列表的末尾,但在您的dlopen()
ed库之前.
你可以通过运行来观察所有这些:
LD_DEBUG=symbols,bindings ./MainDynamic
Run Code Online (Sandbox Code Playgroud)
并__cyg_profile_func_enter
在输出中寻找.
那么,为了看到你的仪器你需要做什么?你必须让自己的__cyg_profile_func_enter
地方之前,从一个libc.so.6
.一种方法是将其链接到您的主要可执行文件.或者将其链接到直接链接到您的可执行文件的共享库(即不是 dlopen()
d).
一旦你这样做,你的实现将是列表中的第一个,它将赢得一个libc.so.6
,你将看到它生成的输出.