从可执行文件调用成员函数

时间:2015-07-30 12:26:13

标签: c++

我正在尝试在游戏服务器中实现一些我有PDB文件的功能。 所有修改都编码在我挂钩的DLL中。

Finish

当我尝试调用成员函数时发生了问题。我读了一些关于引用成员函数不同的东西。我在Class 1中使用的函数都有效,但是当我尝试从Class 2调用函数时,游戏服务器崩溃了。

为什么Class 1的功能有效?也许是因为Class 1有构造函数Class 2不?所有功能都是公开的。

有人可以帮助我吗? 谢谢,抱歉我的英语。

1 个答案:

答案 0 :(得分:3)

C ++非静态成员函数不仅仅是常规函数,因为:

  • 需要隐式传递的“this”参数
  • 可能需要动态查找虚拟方法

同样在C ++中,您不能声明“指向给定整数的任何类的任何方法返回整数的指针”。所以理论上指向一个方法的指针可能只是一个小整数,描述了你感兴趣的方法(鉴于该类是固定的并且在编译时已知)。

实际上,对于g ++(Linux 64位)中的非虚方法,似乎指向方法的指针只是指向常规函数的指针,该函数在所有其他参数之前接受额外的指针参数,遵循标准x86-64 ABI。

例如在班级

struct Foo {
    int k;
    Foo(int k) : k(k) {}
    int square(int x) { return k*x*x; }
    int cube(int x) { return k*x*x*x; }
};

square的代码只是:

00000000004006d0 <_ZN3Foo6squareEi>:
  4006d0:   8b 07       mov    (%rdi),%eax ; get this->k in eax
  4006d2:   0f af c6    imul   %esi,%eax   ; times x
  4006d5:   0f af c6    imul   %esi,%eax   ; times x
  4006d8:   c3          retq   

其中%edi是隐式this参数,%esi是输入x参数,代码与声明为

的C函数相同
int meth(Foo *this_pointer, int x);

使用g ++,方法指针最终实际上是指向函数代码的指针(在特定情况下,值为0x00000000004006d0)。

当然请注意,这仅适用于Linux 64位上此版本的g ++。调用泛型类的泛型方法在概念上是不可能的,因为类的类型是方法签名的一部分,因此该区域中的所有内容都是依赖于实现的。

我希望其他C ++编译器也采用类似的方法(使用实际代码地址可以提高调用效率),但是您需要检查特定的编译器/环境。

使用g ++

的示例

作为一个简单的例子,请考虑这段代码

#include <stdio.h>

struct MyClass {
    int x;

    MyClass(int x) : x(x) {
    }

    void dump(int y) {
        printf("dump() called: x = %i, y = %i\n", x, y);
    }
};

MyClass class1(1111);
MyClass class2(2222);

MyClass *getClass(int x) {
    if (x == 1) return &class1;
    if (x == 2) return &class2;
    return NULL;
}

void (MyClass::*aptr)(int) = &MyClass::dump; // NEEDED

编译为共享库:

g++ -Wall -O3 -fPIC -shared mylib.cpp -o mylib.so

并考虑此计划

#include <stdio.h>
#include <dlfcn.h>

int main(int argc, const char *argv[]) {
    void *p = dlopen("./prg.so", RTLD_LAZY);
    printf("p = %p\n", p);
    if (p) {
        void *(*geti)(int);
        geti = (void *(*)(int)) dlsym(p, "_Z8getClassi");
        void (*dump)(void *, int);
        dump = (void (*)(void *, int)) dlsym(p, "_ZN7MyClass4dumpEi");
        printf("geti = %p\n", geti);
        printf("dump = %p\n", dump);
        if (geti) {
            for (int i=0; i<4; i++) {
                void *q = geti(i);
                printf("geti(%i) = %p\n", i, q);
                if (q && dump) {
                    printf("  calling dump on the instance:\n");
                    dump(q, 33);
                }
            }
        }
    }
    return 0;
}

通常编译为独立的可执行C程序(gcc,而不是g ++)。

程序将加载C ++库,并在两个实例上调用dump方法,将它们声明为接受额外this参数的普通C函数。但请注意

  1. 所有这些都是高度不可移植,并且即使使用g ++ / gcc,它也可能在更复杂的情况下无效。似乎也可以混合g ++和clang ++,但对于这个简单的案例来说可能是巧合。
  2. 您需要知道您想要调用方法的对象实例的地址...这里我使用了getClass函数
  3. 您需要知道要调用的函数的错位名称
  4. 可能并非所有类的方法都暴露出来;例如,我不得不在这个玩具测试中强迫它,因为否则dump根本就不存在于共享库中(参见“需要”评论)。
  5. 总结一下......你真的确定要弄脏它吗?