【发布时间】:2019-02-18 22:26:18
【问题描述】:
我正在尝试创建一个包含基类的共享库,以便可以派生它:
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;
}
所以当在可执行文件中创建一个新的实例指针时肯定会出现问题
【问题讨论】:
-
为什么不想链接到共享库?
-
因为我想保持可执行文件不变(相同的哈希值)并继续开发共享库
-
我不确定您的意思,但只要您不更改
Base的声明,使用具有不同版本的共享的相同可执行文件应该没有任何问题图书馆。如果您确实更改了Base的声明,则无论如何都必须重新编译您的可执行文件 -
Alan:如果我将可执行文件链接到共享库并更改库,例如,通过添加一个新类(即不修改 Base),可执行文件将有所不同......我想要避免这种情况
标签: c++ linux shared-libraries