使用Gcc

时间:2015-10-03 10:40:57

标签: c++ gcc mingw

问题

我目前正在开发一个插件库,其中一个人不仅可以导入C-Linkage符号,还可以导入所有导入的东西。

到目前为止,虽然问题在于,gcc screw成员函数调用了。

如果我导出以下内容:

static member_function(Class* c)
{ c->method();}

它可以正常工作,我可以访问类成员。但如果我做以下事情:

void (Class ::*p)() = import("Class::method");
(x.*p)();

我得到了正确的指针,也能够调用函数和传递的参数,但是这个指针指向了必杀技。我认为gcc从堆栈的错误位置或类似的东西中取出它。

它适用于MSVC。

我正在使用mingw-w64 5.1。

有谁知道错误是什么?

简单示例:

plugin.cpp

#include <iostream>    

namespace space {
class __declspec(dllexport) SomeExportThingy
{
    int i = 42;
  public:
    virtual void __declspec(dllexport) Method(int*) const
    {
         using namespace std;
         cout << "Calling Method" << endl;
         cout << pi << endl;
         cout << *pi << endl;
         cout << this << endl;
         cout << this->i << endl;
    }
}
}

loader.cpp

namespace space {
class SomeExportThingy
{
///dummy to have some data in the address
    int dummy[20];
};

int main()
{
    auto h = LoadLibrary("plugin.dll");
    auto p = GetProcAddress(h, "_ZNK5space16SomeExportThingy6MethodEPi");

    typedef void (space::SomeExportThingy::*mptr)(int*) const;

    ///used because posix passed void*
    auto fp = *reinterpret_cast<mptr*>(&p);

    space::SomeExportThingy st;
    int value = 22;

    cout << "ValueLoc: " << &value << endl;
    cout << "StLoc: " << &st << endl;


    (st.*fp)(&value);

}

结果

现在发生的是,调用该函数并正确传递指向pi的指针。但是,这个指针完全搞砸了。 再说一遍:它适用于MSVC,它正确地得到了这个指针,但是gcc得到了这个错误。 我不知道为什么会发生这种情况,从方法中删除虚拟也不会改变它。 我不知道是什么导致这种情况,所以也许有人知道ABI在这里做了什么。

以下是我得到的指示:

  • 0x00400000 == GetModuleHandleA(NULL)
  • 0x61840000 == GetModuleHandleA(&#34; plugin.dll&#34;)
  • 0x0029fcc4 == _&amp; st
  • 0x00ddcd60 == this

我无法找到值之间的任何关系

3 个答案:

答案 0 :(得分:3)

这不适用于GCC:

typedef void (space::SomeExportThingy::*mptr)(int*) const;

///used because posix passed void*
auto fp = *reinterpret_cast<mptr*>(&p);

指向成员的指针的表示是普通函数指针(或void*)的两倍,因此您从只包含一个单词的内存位置读取两个单词。第二个单词(告诉编译器如何调整调用的this指针)是垃圾,它只是在堆栈上p之后发生的任何事情。

请参阅https://gcc.gnu.org/onlinedocs/gcc/Bound-member-functions.html

  

在C ++中,指向成员函数(PMF)的指针是使用宽泛的指针来实现的,以处理所有可能的调用机制; PMF需要存储有关如何调整'this'指针的信息,

  • pvoid*所以它是堆栈上占用sizeof(void*)个字节的内存位置。
  • &p是指向该内存位置的指针。
  • reinterpret_cast<mptr*>(&p)是指向同一地址的2*sizeof(void*)个字节的指针。
  • *reinterpret_cast<mptr*>(&p)从内存位置读取2*sizeof(void*)个字节,其大小仅为sizeof(void*)个字节。
  • 糟糕的事情发生。

答案 1 :(得分:1)

对于linux,动态函数加载的函数是:dlopen(),dlsym()和dlclose()。请参考:dlopen() man page

考虑到C ++方法名称已经被破坏了。并且他们有一个看不见的&#39; *这个&#39;参数在所有其他参数之前传递。这两个问题共同使得在使用动态链接时尝试直接访问C ++对象并非易事。

我发现最简单的解决方案是使用&#39; C&#39;公开对C ++对象实例的访问的函数。

其次,当实例化的代码在.so库对象中时,C ++对象的内存管理并不简单,尽管引用代码来自用户的应用程序。

关于为什么避免使用指向C ++成员方法的指针很困难,请参考:ISO CPP Reference, Pointers to Methods

/** File: MyClass.h **/

// Explicitly ensure 'MyClassLoaderFunc' is NOT name mangled.
extern 'C' MyClass* MyClassLoaderFunc(p1, p2 ,p3, etc );
extern 'C' MyClass* MyClassDestroyerFunc(MyClass* p);

// Create function pointer typedef named 'LoaderFuncPtr'
typedef MyClass*(MyClassLoaderFunc* LoaderFuncPtr)(p1,p2,p3,etc);

// Define MyClass
class MyClass
{  
   /** methods & members for the class go here **/      
   char dummy[25];
   int method( const char *data);
};    
/** File: MyClass.cpp **/
#include "MyClass.h"

MyClass* MyLoaderFunc(p1, p2 ,p3, etc) {
    MyClass* newInstance = new MyClass::CreateInstance( p1, p2, p3, etc);
    /** Do something with newInstance **/
    return newInstance;
}

MyClass::method(const char* data)
{
}
/** File: MyProgram.cpp **/
#include "MyClass.h"
main()
{
    // Dynamically load in the library containing the object's code.
    void *myClassLibrary = dlopen("path/to/MyClass.so",RTLD_LOCAL);

    // Dynamically resolve the unmangled 'C' function name that 
    // provides the bootstrap access to the MyClass*
    LoaderFuncPtr loaderPtr = dlsym(myClassLibrary,"MyClassLoaderFunc");
    DestroyFuncPtr destroyerPtr = dlsym(myClassLibrary,"MyClassDestroyerFunc");

    // Use dynamic function to retrieve an instance of MyClass.
    MyClass* myClassPtr = loadPtr(p1,p2,p3,etc);

    // Do something with MyClass
    myClassPtr->method();

    // Cleanup of object should happen within original .cpp file
    destroyPtr(myClassPtr);
    myClassPtr = NULL;

    // Release resources
    dlclose(myClassLibrary);

    return 0;
}

希望这会有所帮助..

我还建议将工厂范例作为一种更强大的解决方案,我将留给读者进行探索。

答案 2 :(得分:0)

正如Jonathan所指出的,指向成员的指针比正常的函数指针更大 最简单的解决方案是保留并初始化额外的空间。

typedef void (space::SomeExportThingy::*mptr)(int*) const;

union {
  mptr fp;
  struct {
    FARPROC function;
    size_t offset;
  };
} combFp;
combFp.function = p;
combFp.offset = 0;

auto fp = combFp.fp;