Linux c ++。预载共享库中定义的基类的崩溃调用函数

时间:2018-09-14 06:48:54

标签: c++ linux shared-libraries

我正在尝试创建一个包含基类的共享库,以便可以派生它:

base.h

class Base
{
public:

    virtual ~Base () {}
    virtual void Function ();
};

base.cpp

#include <stdio.h>
#include "base.h"

void Base::Function ()
{
    printf ("base function\n");
}

mybase.so

g++ -fpic -g -shared base.cpp -o libbase.so

main.cpp

#include "base.h"

class Derived : public Base
{
};

int main (int argc, char** argv)
{
    Derived* d = new Derived ();

    d->Function ();

    delete d;

    return 1;
}

我还想避免将可执行文件与共享库链接,因此我通过忽略未解析的符号来创建它:

测试

g++ -fpic -rdynamic -Wl,--unresolved-symbols=ignore-in-object-files -g main.cpp -o test

最后,我使用LD_PRELOAD环境变量在执行之前预加载共享库

LD_PRELOAD=./libbase.so ./test
Segmentation fault (core dumped)

我注意到问题是未为“函数”定义派生对象的虚拟表:

(gdb) info vtbl d
vtable for 'Derived' @ 0x601030 (subobject @ 0x602010):
[0]: 0x400c7e <Derived::~Derived()>
[1]: 0x400cc0 <Derived::~Derived()>
[2]: 0x0

我的猜测是,当可执行文件被加载时,动态链接程序无法解析vtable条目,因为尚未加载共享库。

所以我的问题是:有没有办法使这项工作可行?也许强制以某种方式在可执行文件之前加载共享库...

顺便说一句:通过使“函数”为非虚拟,一切正常,因为派生类不需要vtable。

更新1:使用对象代替指针使main起作用:

int main (int argc, char** argv)
{
    Derived d;

    d.Function ();  // prints "base function"

    return 1;
}

更新2:与main相同,但在第二个共享库中也可以:

mylib.cpp

#include "base.h"

class DerivedLib : public Base
{
};

extern "C" void do_function()
{
    DerivedLib* d = new DerivedLib();

    d->Function(); 

    delete d;
}

mylib.so

g++ -fPIC -g -shared lib.cpp -o libmylib.so 

main.cpp

#include "base.h"
#include <dlfcn.h>

class Derived : public Base
{
};

int main (int argc, char** argv)
{
    void* handle = dlopen("libmylib.so", RTLD_LAZY);

    void (*do_function)();

    do_function = (void (*)())dlsym(handle, "do_function");

    do_function();  // prints "base function"

    Derived* d = new Derived();

    d->Function (); // <- crashes

    delete d;

    return 1;
}

因此,在可执行文件内部创建新的实例指针时肯定会出现问题

2 个答案:

答案 0 :(得分:0)

如果您尝试不链接到共享库的原因是,您想继续更改共享库而不破坏可执行文件,实际上,只要您不更改库类的公共接口,您就可以重新使用。如果您确实进行了更改,则无论您做什么,都将需要重新编译可执行文件。请记住一些事情。

  1. 尝试将可执行文件中包含的头文件尽可能多地保留在实现中。更改头文件将强制执行重新编译,而并非总是如此。

  2. 如果将类添加到共享库,则无需重新编译可执行文件。那就不应该成为问题。

答案 1 :(得分:0)

解决方案(如果有)正在创建一个PIE可执行文件。

测试:

g++ -fpic -pie -fpie -rdynamic -Wl,--unresolved-symbols=ignore-in-object-files -g main.cpp -o test

LD_PRELOAD=./libbase.so ./test
base function