调用另一个 DLL 中的虚拟函数时出现段错误

发布于 2024-10-04 21:44:05 字数 1553 浏览 0 评论 0原文

我有一个由两部分组成的程序,其中主核心需要注册一个适配器,然后回调到寄存器中。这些部分位于单独的 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

絕版丫頭 2024-10-11 21:44:05

你说你的所有方法都声明为 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.

青春有你 2024-10-11 21:44:05
  1. 您使用了很多指针。他们的生命周期在哪里管理?
  2. 在您的示例中,Adapter_Specific 从 Adapter 继承私有。
  3. 适配器有一个虚拟方法,因此可能也需要一个虚拟析构函数。
  4. 适配器也没有默认构造函数,因此 Adapter_Specific 的构造函数将无法编译。您可能希望它使用相同的参数构造基类。这不会自动发生。不过,请参阅第 6 点。
  5. 仅采用一个参数的构造函数(复制构造函数除外)通常应显式声明。
  6. 基类 Adapter 采用一个它不使用的参数。
  1. You are using a lot of pointers. Where are their lifetimes managed?
  2. Adapter_Specific is inheriting private from Adapter in your example
  3. Adapter has a virtual method so probably needs a virtual destructor too
  4. Adapter also has no default constructor so the constructor of Adapter_Specific won't compile. You might want it to construct the base class with the same parameter. This does not happen automatically. However see point 6.
  5. Constructors that take exactly one parameter (other than the copy constructor) should normally be declared explicit.
  6. The base class Adapter takes a parameter it does not use.
国际总奸 2024-10-11 21:44:05

这个问题最终成为我的一个愚蠢的错误。

在代码中的另一个函数中,我不小心向函数提供了 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 the Adapter * 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.

一页 2024-10-11 21:44:05

对类和类成员使用 __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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文