-finstrument-functions 不适用于动态加载的 g++ 共享对象 (.so)

-finstrument-functions doesn't work with dynamically loaded g++ shared objects (.so)

本文关键字:对象 共享 so g++ 不适用 适用于 动态 加载 -finstrument-functions      更新时间:2023-10-16

这些天我正在 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

我的图书馆.cpp

#include "MyLib.h"
#include <iostream>
using namespace std;
void MyLib::sayHello()
{
    cout<<"Hello"<<endl;
}

MyLibStub.cpp(.so 的 C 接口(

#include "MyLib.h"
extern "C" void LoadMyLib ()
{
    MyLib().sayHello();
}

跟踪.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 %pn", (int*)this_fn);
}
void __cyg_profile_func_exit(void* this_fn, void* call_site)
{
    printf("exiting %pn", (int*)this_fn);
}

主静态.cpp

#include <iostream>
using namespace std;
extern "C" void LoadMyLib ();
int main()
{
    LoadMyLib();
    return 0;
}

主动态.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;
}

并使用以下命令(在 Ubuntu 11.10 下(进行编译:

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一起调用时

它给出类似的东西:

entering 0xb777693f
entering 0xb777689b
exiting 0xb777689b
exiting 0xb777693f
entering 0xb7776998
entering 0xb777680c
Hello
exiting 0xb777680c
exiting 0xb7776998

但是,当与./MainDynamic调用时

它只给出一个"你好"。

Hello

这里有人知道为什么静态链接库和动态链接库之间存在如此大的差异吗?有没有任何解决方案可以使它在动态加载时也能工作?提前谢谢。

此行为是预期行为。

为了理解它,您首先需要知道动态加载器使用链表搜索符号,按照加载不同ELF图像的顺序。该列表的顶部是主可执行文件本身,其次是直接链接到它的所有库。当您dlopen()某个库时,它会附加到列表的部。

因此,当您刚刚加载的库中的代码调用__cyg_profile_func_enter时,加载器会在列表中搜索该函数的第一个定义。第一个定义恰好是默认定义,由libc.so.6提供,它靠近列表的末尾,但在dlopen() ed库之前

您可以通过运行以下命令来观察所有这些:

LD_DEBUG=symbols,bindings ./MainDynamic

并在输出中查找__cyg_profile_func_enter

那么,您必须执行哪些操作才能查看您的仪器?你必须libc.so.6之前得到自己的__cyg_profile_func_enter.一种方法是将其链接到主可执行文件中。或者将其链接到直接链接到可执行文件的共享库(即不是dlopen() d 一个(。

一旦你这样做了,你的实现将是列表中的第一个,它将胜过libc.so.6中的那个,你会看到它生成的输出。

你可以

dlopen(pszLibName, RTLD_NOW | RTLD_DEEPBIND);

然后,库中定义的符号将优先于全局符号