【问题标题】:Calling functions with different arg count调用具有不同 arg 计数的函数
【发布时间】:2018-06-24 11:50:15
【问题描述】:

我正在反编译一款非常古老的游戏,我的目标是恢复 100% 原生代码。因此,我需要尽可能地保留现有架构(例如,仅使用 std::function 并没有真正的帮助)。

我有一组具有不同数量参数的 静态 函数。它们存储在这样的结构中:

struct ScriptFunction 
{
    const wchar_t* m_name;
    void* m_func;  // ptr to specific function 
    void* m_returnType;
    int m_argCount;
};

并且像这样填充:

ScriptFunction s_functions[206] = {
    {L"AddMoveAroundDesire", &ScriptFunctions::AddMoveAroundDesire, &CScriptType::s_void, 2},
    {L"AddDoNothingDesire", &ScriptFunctions::AddDoNothingDesire, &CScriptType::s_void, 2},
    {L"AddAttackDesire", &ScriptFunctions::AddAttackDesire, &CScriptType::s_void, 3},
    {L"AddAttackDesireEx", &ScriptFunctions::AddAttackDesireEx, &CScriptType::s_void, 4},
    {L"AddGetItemDesire", &ScriptFunctions::AddGetItemDesire, &CScriptType::s_void, 2},
 ...

那么,如何使用给定的 m_argCountvoid** 参数调用 ScriptFunction::m_func ?我必须以某种方式在堆栈上推送参数并调用 func-call,但我不知道。

一些汇编代码? va_list?但是如何在运行时填充呢?

UPD: Invoker 看起来像这样

void CScriptAccessible::InvokeInternal(ScriptFunction* scriptFunc, void** args)
{
    // here I have to push params from **args** to stack 
    // and somehow call ScriptFunction::m_func
    // In assembly I could see pushes to stack in a loop (driven by 
    // ScriptFunction::m_argCount). But I'm wondering, whether it's 
    // possible to do in C++ (push args on stack and call function by just address)
}

【问题讨论】:

  • 我为你删除了不需要的标签。如果你能解释一下为什么有帮助,请随时重新添加它,连同解释一起,并接受我的道歉。
  • 你如何获得论点?你想如何通过它们?
  • ScriptFunctions 函数(Scriptfunctions::AddMoveAroundDesire 等)是静态的还是非静态的?每个人如何使用提供的ScriptFunction?这些问题对于选择调用/调用它们的选项是相当基础的,因为只有一个名为 m_func 的成员是一个 void * 和一组 void ** 参数。
  • ScriptFunctions::* 是静态的(免费)。改变了描述。唯一的单一用法是在CScriptAccessible::InvokeInternal 内部。在汇编中,我可以看到推动堆栈循环(由ScriptFunction::m_argCount 驱动)。但我想知道,是否可以在 C++ 中执行(将参数推入堆栈并仅通过地址调用函数)
  • 您可能还想看看this answer

标签: c++ function-pointers


【解决方案1】:

在语言中,只有switch/cast 解决方案。这让编译器生成正确的调用约定:例如,在 x86-64 Linux 上,前几个参数在预定义的寄存器中传递。 (优化器可能会将不同的路径合并为跳转到您所期望的那种展开的循环中。)还要记住,仅在 C/C++ 中,根本没有“堆栈”。

顺便说一句,也不能保证将void* 用于函数(因为它可以指向任何对象,但它们不是对象),尽管它通常可以工作(并且POSIX 要求它用于dlsym )。

【讨论】:

  • 我最终得到了switch/case/cast-to-proper-func-prototype 解决方案。我认为,在原始代码中他们使用 asm,因为从二进制代码中很清楚。
猜你喜欢
  • 2019-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-26
  • 1970-01-01
  • 2011-12-11
  • 2022-11-01
  • 1970-01-01
相关资源
最近更新 更多