我发这个是为了说两件事:
1)这里给出的大多数答案都非常糟糕,很容易崩溃。如果您使用 C 函数指针(使用函数名),在您的可执行文件的 debug 构建中,并且可能在其他情况下,它可能指向一个 JMP shim函数体本身。这是一个例子。如果我对下面定义的函数执行以下操作:
FARPROC pfn = (FARPROC)some_function_with_possibility_to_get_its_size_at_runtime;
我得到的pfn(例如:0x7FF724241893)会指向这个,这只是一个JMP指令:
此外,编译器可以嵌套多个垫片,或分支您的函数代码,使其具有多个epilogs 或ret 指令。哎呀,它甚至可能不使用ret 指令。然后,无法保证函数本身会按照您在源代码中定义的顺序进行编译和链接。
你可以用 汇编 语言做所有这些事情,但不能用 C 或 C++。
2) 以上是坏消息。好消息是原始问题的答案是,是的,有一种方法(或 hack)可以获得确切的函数大小,但它附带以下限制:
这个概念很简单——利用 x64 Windows 二进制文件中的SEH is implemented 方式。编译器将每个函数的详细信息添加到 PE32+ 头文件中(添加到可选头文件的IMAGE_DIRECTORY_ENTRY_EXCEPTION 目录中),您可以使用它来获取确切的函数大小。 (如果您想知道,此信息用于在 __try/__except/__finally 块中捕获、处理和展开异常。)
这是一个简单的例子:
//You will have to call this when your app initializes and then
//cache the size somewhere in the global variable because it will not
//change after the executable image is built.
size_t fn_size; //Will receive function size in bytes, or 0 if error
some_function_with_possibility_to_get_its_size_at_runtime(&fn_size);
然后:
#include <Windows.h>
//The function itself has to be defined for two types of a call:
// 1) when you call it just to get its size, and
// 2) for its normal operation
bool some_function_with_possibility_to_get_its_size_at_runtime(size_t* p_getSizeOnly = NULL)
{
//This input parameter will define what we want to do:
if(!p_getSizeOnly)
{
//Do this function's normal work
//...
return true;
}
else
{
//Get this function size
//INFO: Works only in 64-bit builds on Windows!
size_t nFnSz = 0;
//One of the reasons why we have to do this at run-time is
//so that we can get the address of a byte inside
//the function body... we'll get it as this thread context:
CONTEXT context = {0};
RtlCaptureContext(&context);
DWORD64 ImgBase = 0;
RUNTIME_FUNCTION* pRTFn = RtlLookupFunctionEntry(context.Rip, &ImgBase, NULL);
if(pRTFn)
{
nFnSz = pRTFn->EndAddress - pRTFn->BeginAddress;
}
*p_getSizeOnly = nFnSz;
return false;
}
}