Class conflicts in loaded shared libraries
说我的主要过程有一个课:
1 2 3 4 5 6 7 | class MyClass { void doStuff(); int myThing; int mySecondThing; bool myThirdThing; }; |
然后我加载一个共享库
1 2 3 4 5 6 7 8 9 | class MyClass { void doStuff(); int myThing; int mySecondThing; bool myThirdThing; std::string myFourthThing; u32 myFifthThing; }; |
当我创建MyClass实例/在库函数和主要可执行文件的函数之间传递现有实例时,会发生什么?
我知道这两个库位于不同的地址空间中,但是让我在库和可执行文件之间传递数据使我感到困惑。
使用
使用MyClass对象时可能会出现问题。
这取决于您如何使用它们。
采取以下情况(此处为伪代码)。
1 2 3 4 5 6 7 8 9 10 11 12 | MyClass* ptr = SharedLibHandle->CreateMyClass(); ptr->doStuffNonVirtual(); //1 this might work fine ptr->doStuffVirtual(); //2 this will work fine ptr->myThing= 5; // 3 this might work fine MyClass* localAllocPtr = new MyClass(); SharedLibHandle()->DoSomethingWithTheClass(localAllocPtr); ... void DoSomethingWithTheClass(MyClass* ptr) { ptr->myFourthThing =" mama" ; // 4 this might seem to work fine } |
在上面的示例中,根据实例化和用法的位置,有几种可能的用例:
ptr处理以下情况:在so中使用so定义的大小实例化该类,然后在其中定义大小的可执行文件使用该实例。
localAllocPtr处理相反的情况(在您的可执行文件中实例化的类,然后传递给.so)。
每一个:
在编译时不会解析任何虚函数,这意味着,如果可执行文件中有不同的代码实现,则堆栈指针将跳转到您的函数实现,而不是.so中的那个。如果两个可执行文件和so中的代码都相同,它将按预期工作,并且结构对齐方式保持相同(很可能)。
这将很好地工作,因为它将跳转到一个vftable中,然后跳转到.so中的正确内存地址。 .so初始化了该类,因此偏移,跳转和所有内容都将合法。
仅当myThing在结构内部具有相同的对齐方式(这表示他在结构内部的偏移量为*(ptr 0))时,此方法才能正常工作。如果您的类中myThing首先是mySecondThing第二,而.so中mySecondThing首先是myThing第二,则.so您将更改错误的参数。具有讽刺意味的是,如果您继续在可执行文件中使用该类并将其不传递回.so(让我们说无知是一种幸福),那将无效。
当您的可执行文件分配localAllocPtr时,它将以您的可执行文件中定义的sizeof(MyClass)进行分配。在可执行文件中,该类未定义字符串和u32。当将此分配的结构传递给.so时,.so将根据其定义将类视为具有成员和大小。访问myFourthThing时,它将访问通常为*(ptr 8)的内存区域。如果正在使用该内存区域(有人在那儿分配了内存),您将超出ptr范围写入其他人的内存,这似乎可以正常工作,但最终会发现最困难的错误之一。如果在*(ptr 8)之后未分配任何内容,则您会很幸运并遇到细分错误。
为了避免描述这种问题,您要描述的一种通用方法是pImpl习惯用法,它可以使特定于类的实现私有化,因此您可以添加虚拟函数和成员变量,同时保持暴露的类定义一样。