调用另一个 DLL 中的虚拟函数时出现段错误
我有一个由两部分组成的程序,其中主核心需要注册一个适配器,然后回调到寄存器中。这些部分位于单独的 DLL 中,并且核心不知道适配器的细节(除了提前的方法和参数)。我尝试使用以下代码进行设置,并在每次核心尝试调用适配器方法时收到段错误:
Core.hpp/cpp(组合和简化):
class Core
{
public:
Core()
: mAdapter(NULL)
{ }
void DoStuff(int param)
{
if ( this->mAdapter )
{
this->mAdapter->Prepare(param);
}
}
void Register(Adapter * adapter)
{
this->mAdapter = adapter;
}
private:
Adapter * mAdapter;
};
Adapter.hpp /cpp(在核心库中):
class Adapter
{
public:
Adapter(Core * core) { }
virtual void Prepare(int param)
{
// For testing, this is defined here
throw("Non-overridden adapter function called.");
}
};
AdapterSpecific.hpp/cpp(第二个库):
class Adapter_Specific
: Adapter
{
public:
Adapter_Specific(Core * core)
{
core->Register(this);
}
void Prepare(int param) { ... }
};
在构建第一个模块(核心)和核心时,所有类和方法都标记为 DLL 导出在构建适配器时将其标记为导出,将适配器标记为导入。
代码运行良好,直到调用 Core::DoStuff 为止。从整个程序集来看,它似乎解析了 vftable 中的函数,但最终的地址是 0x0013nnnn,而我的模块在 0x0084nnnn 及以上范围内。 Visual Studio 的源调试器和智能感知显示 vftable 中的条目是相同的,并且相应的条目确实转到非常低的地址(也有一个转到 0xF-something,这看起来同样奇怪)。
为清楚起见进行编辑:执行永远不会重新进入适配器特定的类或模块。假设的调用地址无效,并且执行会在那里丢失,从而导致段错误。适配器类中的任何代码都不是问题,这就是我将其排除在外的原因。
我在调试模式下多次重建了这两个库。这是纯 C++,没什么花哨的。我不确定为什么它不起作用,但我需要回调到另一个类,并且宁愿避免使用函数 ptrs 的结构。
有没有办法在模块之间使用像这样的简洁回调,或者它在某种程度上是不可能的?
I have a two-part program where the main core needs to have an adapter registered and then call back into the register. The parts are in separate DLLs, and the core doesn't know the adapter's specifics (besides methods and parameters ahead of time). I've tried setting it up with the following code and recieve a segfault every time the core tries to call an adapter method:
Core.hpp/cpp (combined and simplified):
class Core
{
public:
Core()
: mAdapter(NULL)
{ }
void DoStuff(int param)
{
if ( this->mAdapter )
{
this->mAdapter->Prepare(param);
}
}
void Register(Adapter * adapter)
{
this->mAdapter = adapter;
}
private:
Adapter * mAdapter;
};
Adapter.hpp/cpp (in the core library):
class Adapter
{
public:
Adapter(Core * core) { }
virtual void Prepare(int param)
{
// For testing, this is defined here
throw("Non-overridden adapter function called.");
}
};
AdapterSpecific.hpp/cpp (second library):
class Adapter_Specific
: Adapter
{
public:
Adapter_Specific(Core * core)
{
core->Register(this);
}
void Prepare(int param) { ... }
};
All classes and methods are marked as DLL export while building the first module (core) and the core is marked as export, the adapter as import while building the adapter.
The code runs fine up until the point where Core::DoStuff is called. From walking through the assembly, it appears that it resolves the function from the vftable, but the address it ends up with is an 0x0013nnnn, and my modules are in the 0x0084nnnn and above range. Visual Studio's source debugger and the intellisense shows the the entries in the vftable are the same and the appropriate one does go to a very low address (one also goes to 0xF-something, which seems equally odd).
Edit for clarity: Execution never re-enters the adapter_specific class or module. The supposed address for the call is invalid and execution gets lost there, resulting in the segfault. It's not an issue with any code in the adapter class, which is why I left that out.
I've rebuilt both libraries more than once, in debug mode. This is pure C++, nothing fancy. I'm not sure why it won't work, but I need to call back into the other class and would rather avoid using a struct of function ptrs.
Is there a way to use neat callbacks like this between modules or is it somehow impossible?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
你说你的所有方法都声明为 DLL 导出。方法(成员函数)不必以这种方式标记,导出类就足够了。我不知道如果这样做是否有害。
You said all your methods are declared DLL exports. Methods (member functions) do not have to be marked that way ,exporting the class is sufficient. I don't know if it's harmfull if you do.
这个问题最终成为我的一个愚蠢的错误。
在代码中的另一个函数中,我不小心向函数提供了
Core *
而不是Adapter *
,并且在我的通读中没有捕获它。不知何故,编译器也没有捕获它(它应该失败,但没有给出隐式强制转换警告,可能是因为它是引用计数点)。它试图将 Core * 转换为适配器并从该突变对象中获取 vftable,但结果惨败并导致了段错误。我将其修复为正确的类型,现在一切正常。
The problem ended up being a stupid mistake on my part.
In another function in the code, I was accidentally feeding a function a
Core *
instead of theAdapter *
and didn't catch it in my read-through. Somehow the compiler didn't catch it either (it should have failed, but no implicit cast warning was given, possibly because it was a reference-counted point).That tried to turn the
Core *
into an Adapter and get the vftable from that mutant object, which failed miserably and resulted in a segfault. I fixed it to be the proper type and all works fine now.对类和类成员使用
__declspec(dllexport)
是一个非常糟糕的主意。最好使用接口(仅包含虚函数的基类,它本质上与您不想要的“函数指针结构”相同,除了编译器处理所有细节),并使用 __declspec(dllexport ) 仅适用于全局函数,例如工厂函数。特别是不要跨 DLL 边界直接调用构造函数和析构函数,因为您会得到不匹配的分配器,暴露包装特殊函数的普通函数。__declspec(dllexport)
on classes and class members is a really bad idea. It's much better to use an interface (base class containing only virtual functions, it's essentially the same as the "struct of function pointers" you didn't want, except the compiler handles all the details), and use__declspec(dllexport)
only for global functions such as factory functions. Especially don't call constructors and destructors directly across DLL boundaries because you'll get mismatched allocators, expose an ordinary function which wraps the special functions.