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

Call member function from executable

本文关键字:函数 成员 调用 可执行文件      更新时间:2023-10-16

我正在尝试在我拥有PDB文件的游戏服务器中执行一些功能。所有的修改都在我的DLL中进行编码。

I'm refering global functions using it pointer like this :
.cpp file
pXXX YYY = (pXXX) 0x00403B7F;
.h file
typedef <return_type_function>(*pXXX)(<args>);
extern pXXX YYY;

当我试图调用一个成员函数时,问题发生了。我读了一些关于引用成员函数是不同的。我在类1中使用的函数都可以工作,但是当我试图调用类2中的函数时,游戏服务器崩溃了。

为什么第一类的函数可以工作?也许是因为类1有构造函数,类2没有?所有函数都是公共的

有人能帮帮我吗?

c++非静态成员函数不仅仅是普通函数,因为:

  • 要求隐式传递"this"参数
  • 可能需要动态查找虚方法

同样在c++中,你不能声明"指向任何类的任何方法的指针,如果给定一个整数,则返回一个整数"。因此,理论上,指向方法的指针可以只是一个小整数,描述您感兴趣的方法是哪个(假设类是固定的,并且在编译时已知)。

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

当然,这只对64位Linux上的g++版本有效。在可移植的c++中,调用泛型类的泛型方法在概念上是不可能的,因为类的类型是方法签名的一部分,所以这方面的一切都是依赖于实现的。

我希望其他c++编译器也采用类似的方法(使用真实的代码地址使调用更有效),但您需要检查您的特定编译器/环境。

使用g++

的示例作为一个简单的例子,考虑以下代码
#include <stdio.h>
struct MyClass {
    int x;
    MyClass(int x) : x(x) {
    }
    void dump(int y) {
        printf("dump() called: x = %i, y = %in", 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 = %pn", 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 = %pn", geti);
        printf("dump = %pn", dump);
        if (geti) {
            for (int i=0; i<4; i++) {
                void *q = geti(i);
                printf("geti(%i) = %pn", 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根本不存在于共享库中(参见"NEEDED"注释)。

总结起来…你真的真的想弄得这么脏吗?