【问题标题】:Get memory address of member function?获取成员函数的内存地址?
【发布时间】:2011-12-28 14:39:00
【问题描述】:

如何获取 C++ 中成员函数的绝对地址? (我需要这个来思考。)

成员函数指针不起作用,因为我无法将它们转换为绝对地址(void *)——我需要知道内存中实际函数的地址,而不仅仅是相对于类型的地址。

【问题讨论】:

  • 可能值得一提的是,这不仅适用于 Visual C++ 的标签,还可以避免忽略它们的人浪费时间在明显的答案上,“你不能”。
  • @thiton:假设 Mehrdad 想要的实际位置的函数恰好是一个虚函数。然后基类实现在可执行文件的某个地方仍然有一个入口点,但是该函数的指向成员的指针不会引用该入口点,因为通过它的调用使用虚拟机制。
  • @thiton:它没有那么简单。虚函数和多重继承增加了复杂性。
  • 出于兴趣,为什么你需要地址来进行thunking?那就是:为什么不能只保留成员函数指针,因为无论如何都需要保留实例指针才能使用它?
  • @Omnifarious:我想你可能误解了这个问题?因为我试图将指针“放入”void *。相反,我试图获取成员函数的内存地址。不管我们是在谈论C 函数还是一些虚拟菱形多重虚拟继承函数(或其他)的覆盖,都没有关系。仍然只有 1 个函数,它必须从内存中的某个位置开始,可以用 void * 表示。与传统成员函数指针有关的所有其他令人头疼的问题都是动态调度,我不需要。

标签: c++ visual-c++ memory-address member-functions


【解决方案1】:

在 MSVC 中存在获取成员函数地址的语法(从 MSVC 2005 恕我直言开始)。但这很棘手。而且,获得的指针不可能通过常规方式转换为其他指针类型。尽管有一种方法可以做到这一点。

示例如下:

// class declaration
class MyClass
{
public:
    void Func();
    void Func(int a, int b);
};

// get the pointer to the member function
void (__thiscall MyClass::* pFunc)(int, int) = &MyClass::Func;

// naive pointer cast
void* pPtr = (void*) pFunc; // oops! this doesn't compile!

// another try
void* pPtr = reinterpret_cast<void*>(pFunc); // Damn! Still doesn't compile (why?!)

// tricky cast
void* pPtr = (void*&) pFunc; // this works

即使使用reinterpret_cast,传统演员表也不起作用这一事实可能意味着 MS 并不强烈推荐这种演员表。

尽管如此,您仍然可以这样做。当然这都是依赖于实现的,你必须知道适当的调用约定来做 thunking + 有适当的汇编技能。

【讨论】:

  • 您看到的是依赖于实现的行为并且是不可移植的。这种技术可能随时停止工作。至于“为什么?”这是因为成员函数指针不是函数指针。它们是奇怪复杂的东西。
  • @Raymond Chen:当然是“依赖于实现”和“不可移植”。我提到了这一点。但这并不一定意味着应该永远这样做。随意这样做,只要注意后果。
  • 如果Func 是一个虚函数,你可能会感到惊讶。
  • 我不确定这条线 void* pPtr = (void*&amp;) pFunc; 是否可移植,但可以在 gcc 和 msvs 中使用。我最想知道为什么(void*&amp;) pFunc;(void*) pFunc; 不起作用? .除了添加左值引用之外,两者几乎是一样的。你能解释一下为什么一个有效而另一个无效吗?
  • 提前 7 年,它仍然有效...(在 Visual Studio 2019 上)
【解决方案2】:

默认情况下,C++ 成员函数使用 __thiscall 调用 习俗。为了绕开一个成员函数,蹦床 并且 detour 必须具有与 目标函数。不幸的是,VC 编译器不支持 __thiscall,因此创建合法的 detour 和 trampoline 函数的唯一方法是让它们成为“detour”类的类成员。

另外,C++ 不支持将指针转换为成员 函数指向任意指针。 要获取原始指针,地址 必须将成员函数的一部分移动到临时成员函数中 指针,然后通过获取它的地址传递,然后取消引用它。 幸运的是,编译器会优化代码以去除多余的 指针操作。

来自 Microsoft Detour 库。他们处理代码注入并讨论获取非虚拟成员函数的地址。当然这是编译器实现特定的东西。

你可以在这里找到图书馆http://research.microsoft.com/en-us/downloads/d36340fb-4d3c-4ddd-bf5b-1db25d03713d/default.aspx

【讨论】:

    【解决方案3】:

    试试这个。应该让你把任何东西都投射到任何东西上:)

    template<typename OUT, typename IN>
    OUT ForceCast( IN in )
    {
        union
        {
            IN  in;
            OUT out;
        }
        u = { in };
    
        return u.out;
    };
    

    然后

    void* member_address = ForceCast<void*>(&SomeClass::SomeMethod);
    

    【讨论】:

    • 仅作记录,这是未定义的行为。这使用类型双关语,仅定义为转换为 char*
    • @StephenEckels:我认为对这个问题的 any 答案将是 C++ 标准未定义的行为(这很好,因为问题是特定于实现的)。
    【解决方案4】:

    对于仍在寻找获取函数内存地址(而不是跳转)的答案的任何人,这是我的解决方案:

    UINT32 RelativityToTrampoline = *(UINT32*)((UINT64)&Func + 1);
    UINT64 RealFuncAddr = (UINT64)&Func + RelativityToTrampoline + 5;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-27
      • 1970-01-01
      • 2011-06-27
      相关资源
      最近更新 更多