获取C ++函数的大小

时间:2021-12-09 21:36:49

I was reading this question because I'm trying to find the size of a function in a C++ program, It is hinted at that there may be a way that is platform specific. My targeted platform is windows

我正在阅读这个问题,因为我试图在C ++程序中找到一个函数的大小,暗示可能有一种特定于平台的方法。我的目标平台是windows

The method I currently have in my head is the following:
1. Obtain a pointer to the function
2. Increment the Pointer (& counter) until I reach the machine code value for ret
3. The counter will be the size of the function?

我目前掌握的方法如下:1。获取指向函数的指针2.递增指针(&计数器),直到达到ret 3的机器代码值。计数器将是函数的大小?

Edit1: To clarify what I mean by 'size' I mean the number of bytes (machine code) that make up the function.
Edit2: There have been a few comments asking why or what do I plan to do with this. The honest answer is I have no intention, and I can't really see the benefits of knowing a functions length pre-compile time. (although I'm sure there are some)

编辑1:澄清我的意思'大小'我的意思是组成函数的字节数(机器代码)。编辑2:有一些评论询问为什么或我打算用这个做什么。诚实的答案是我没有意图,我无法真正看到了解函数长度预编译时间的好处。 (虽然我确定有一些)

This seems like a valid method to me, will this work?

这对我来说似乎是一种有效的方法,这会有用吗?

14 个解决方案

#1


10  

No, this will not work:

不,这不起作用:

  1. There is no guarantee that your function only contains a single ret instruction.
  2. 无法保证您的函数只包含一条ret指令。

  3. Even if it only does contain a single ret, you can't just look at the individual bytes - because the corresponding value could appear as simply a value, rather than an instruction.
  4. 即使它只包含一个ret,你也不能只查看单个字节 - 因为相应的值可能只是一个值,而不是一条指令。

The first problem can possibly be worked around if you restrict your coding style to, say, only have a single point of return in your function, but the other basically requires a disassembler so you can tell the individual instructions apart.

如果你将编码风格限制在你的函数中只有一个单一的返回点,那么第一个问题可能会解决,但另一个基本上需要一个反汇编程序,这样你就可以区分各个指令。

#2


12  

It is possible to obtain all blocks of a function, but is an unnatural question to ask what is the 'size' of a function. Optimized code will rearrange code blocks in the order of execution and will move seldom used blocks (exception paths) into outer parts of the module. For more details, see Profile-Guided Optimizations for example how Visual C++ achieves this in link time code generation. So a function can start at address 0x00001000, branch at 0x00001100 into a jump at 0x20001000 and a ret, and have some exception handling code 0x20001000. At 0x00001110 another function starts. What is the 'size' of your function? It does span from 0x00001000 to +0x20001000, but it 'owns' only few blocks in that span. So your question should be unasked.

可以获得函数的所有块,但是问一个函数的“大小”是一个不自然的问题。优化的代码将按执行顺序重新排列代码块,并将很少使用的块(异常路径)移动到模块的外部部分。有关更多详细信息,请参阅配置文件引导优化,例如Visual C ++如何在链接时代码生成中实现此目的。因此,函数可以从地址0x00001000开始,在0x00001100处跳转到0x20001000处的跳转和ret,并且具有一些异常处理代码0x20001000。在0x00001110,另一个函数启动。你的功能的“大小”是多少?它确实从0x00001000到+ 0x20001000,但它'拥有'在该范围内只有几个块。所以你的问题应该是没有问题的。

There are other valid questions in this context, like the total number of instructions a function has (can be determined from the program symbol database and from the image), and more importantly, what is the number of instructions in the frequent executed code path inside the function. All these are questions normally asked in the context of performance measurement and there are tools that instrument code and can give very detailed answers.

在此上下文中还有其他有效问题,例如函数的指令总数(可以从程序符号数据库和图像中确定),更重要的是,内部频繁执行的代码路径中的指令数是多少功能。所有这些都是通常在绩效衡量的背景下提出的问题,并且有一些工具可以用来代码,并且可以提供非常详细的答案。

Chasing pointers in memory and searching for ret will get you nowhere I'm afraid. Modern code is way way way more complex than that.

在内存中追逐指针并搜索ret会让你无处可去。现代代码比这更复杂。

#3


7  

This won't work... what if there's a jump, a dummy ret, and then the target of the jump? Your code will be fooled.

这不起作用......如果有跳跃,虚拟后退,然后是跳跃的目标呢?你的代码会被愚弄。

In general, it's impossible to do this with 100% accuracy because you have to predict all code paths, which is like solving the halting problem. You can get "pretty good" accuracy if you implement your own disassembler, but no solution will be nearly as easy as you imagine.

通常,不可能以100%的准确率执行此操作,因为您必须预测所有代码路径,这就像解决暂停问题一样。如果你实现自己的反汇编程序,你可以获得“非常好”的准确性,但没有任何解决方案几乎像你想象的那样容易。

A "trick" would be to find out which function's code is after the function that you're looking for, which would give pretty good results assuming certain (dangerous) assumptions. But then you'd have to know what function comes after your function, which, after optimizations, is pretty hard to figure out.

一个“技巧”是找出你正在寻找的函数之后哪个函数的代码,假设某些(危险的)假设,这将给出相当好的结果。但是你必须知道在你的函数之后出现了什么函数,在优化之后很难弄明白。


Edit 1:

What if the function doesn't even end with a ret instruction at all? It could very well just jmp back to its caller (though it's unlikely).

如果函数甚至没有以ret指令结束怎么办?它很可能只是jmp回到它的调用者(虽然它不太可能)。


Edit 2:

Don't forget that x86, at least, has variable-length instructions...

别忘了x86至少有可变长度指令......


Update:

For those saying that flow analysis isn't the same as solving the halting problem:

对于那些说流量分析与解决暂停问题不同的人:

Consider what happens when you have code like:

考虑当您拥有以下代码时会发生什么:

foo:
    ....
    jmp foo

You will have to follow the jump each time to figure out the end of the function, and you cannot ignore it past the first time because you don't know whether or not you're dealing with self-modifying code. (You could have inline assembly in your C++ code that modifies itself, for instance.) It could very well extend to some other place of memory, so your analyzer will (or should) end in an infinite loop, unless you tolerate false negatives.

每次都必须按照跳转来确定函数的结束,并且你不能在第一次之后忽略它,因为你不知道你是否正在处理自修改代码。 (例如,您可以在C ++代码中使用内联汇编来修改自身。)它可以很好地扩展到其他内存位置,因此您的分析器将(或应该)以无限循环结束,除非您容忍漏报。

Isn't that like the halting problem?

这不是停止问题吗?

#4


7  

Wow, I use function size counting all the time and it has lots and lots of uses. Is it reliable? No way. Is it standard c++? No way. But that's why you need to check it in the disassembler to make sure it worked, every time that you release a new version. Compiler flags can mess up the ordering.

哇,我一直使用函数大小计数,它有很多很多用途。它可靠吗?没门。它是标准的c ++吗?没门。但这就是为什么每次发布新版本时都需要在反汇编程序中检查它以确保它有效。编译器标志可能会破坏排序。

static void funcIwantToCount()
{
   // do stuff
}
static void funcToDelimitMyOtherFunc()
{
   __asm _emit 0xCC
   __asm _emit 0xCC
   __asm _emit 0xCC
   __asm _emit 0xCC
}

int getlength( void *funcaddress )
{
   int length = 0;
   for(length = 0; *((UINT32 *)(&((unsigned char *)funcaddress)[length])) != 0xCCCCCCCC; ++length);
   return length;
}

It seems to work better with static functions. Global optimizations can kill it.

它似乎与静态函数更好地工作。全局优化可以杀死它。

P.S. I hate people, asking why you want to do this and it's impossible, etc. Stop asking these questions, please. Makes you sound stupid. Programmers are often asked to do non-standard things, because new products almost always push the limits of what's availble. If they don't, your product is probably a rehash of what's already been done. Boring!!!

附:我讨厌别人,问你为什么要这样做,这是不可能的等等。请不要再问这些问题了。让你听起来很愚蠢。程序员经常被要求做非标准的事情,因为新产品几乎总是突破了可用的限制。如果他们不这样做,那么您的产品可能是对已经完成的工作的重复。无聊!

#5


2  

The real solution to this is to dig into your compiler's documentation. The ARM compiler we use can be made to produce an assembly dump (code.dis), from which it's fairly trivial to subtract the offsets between a given mangled function label and the next mangled function label.

真正的解决方案是深入了解编译器的文档。我们使用的ARM编译器可以生成一个汇编转储(code.dis),从中减去给定的受损函数标签和下一个受损函数标签之间的偏移量是相当简单的。

I'm not certain which tools you will need for this with a windows target, however. It looks like the tools listed in the answer to this question might be what you're looking for.

但是,我不确定使用Windows目标需要哪些工具。看起来这个问题的答案中列出的工具可能正是您正在寻找的。

Also note that I (working in the embedded space) assumed you were talking about post-compile-analysis. It still might be possible to examine these intermediate files programmatically as part of a build provided that:

另请注意,我(在嵌入式空间中工作)假设您正在讨论编译后分析。仍然可以以编程方式检查这些中间文件作为构建的一部分,前提是:

  • The target function is in a different object
  • 目标函数位于不同的对象中

  • The build system has been taught the dependencies
  • 构建系统已经被教导了依赖关系

  • You know for sure that the compiler will build these object files
  • 您确定编译器将构建这些目标文件

Note that I'm not sure entirely WHY you want to know this information. I've needed it in the past to be sure that I can fit a particular chunk of code in a very particular place in memory. I have to admit I'm curious what purpose this would have on a more general desktop-OS target.

请注意,我不确定您想知道这些信息的原因。我过去需要它,以确保我可以在内存中的特定位置放入特定的代码块。我不得不承认,我很奇怪这会对更通用的桌面操作系统目标产生什么目的。

#6


2  

This can work in very limited scenarios. I use it in part of a code injection utility I wrote. I don't remember where I found the information, but I have the following (C++ in VS2005):

这可以在非常有限的情况下工作。我在我编写的代码注入实用程序的一部分中使用它。我不记得我在哪里找到了这些信息,但我有以下内容(VS2005中的C ++):

#pragma runtime_checks("", off)

static DWORD WINAPI InjectionProc(LPVOID lpvParameter)
{
    // do something
    return 0;
}

static DWORD WINAPI InjectionProcEnd()
{
    return 0;
}

#pragma runtime_checks("", on)

And then in some other function I have:

然后在我的其他一些功能中:

size_t cbInjectionProc = (size_t)InjectionProcEnd - (size_t)InjectionProc;

You have to turn off some optimizations and declare the functions as static to get this to work; I don't recall the specifics. I don't know if this is an exact byte count, but it is close enough. The size is only that of the immediate function; it doesn't include any other functions that may be called by that function. Aside from extreme edge cases like this, "the size of a function" is meaningless and useless.

你必须关闭一些优化并将函数声明为静态以使其工作;我不记得具体细节。我不知道这是否是一个确切的字节数,但它足够接近。大小只是直接功能的大小;它不包括该函数可能调用的任何其他函数。除了像这样的极端边缘情况,“函数的大小”是没有意义和无用的。

#7


1  

In C++, the there is no notion of function size. In addition to everything else mentioned, preprocessor macros also make for an indeterminate size. If you want to count number of instruction words, you can't do that in C++, because it doesn't exist until it's been compiled.

在C ++中,没有函数大小的概念。除了提到的所有其他内容之外,预处理器宏也会产生不确定的大小。如果要计算指令字数,则不能在C ++中这样做,因为它在编译之前不存在。

#8


1  

What do you mean "size of a function"?

你是什​​么意思“功能的大小”?

If you mean a function pointer than it is always just 4 bytes for 32bits systems.

如果你的意思是一个函数指针,那么32位系统总是只有4个字节。

If you mean the size of the code than you should just disassemble generated code and find the entry point and closest ret call. One way to do it is to read the instruction pointer register at the beginning and at the end of your function.

如果你的意思是代码的大小,你应该只是反汇编生成的代码并找到入口点和最近的ret调用。一种方法是在函数的开头和结尾读取指令指针寄存器。

If you want to figure out the number of instructions called in the average case for your function you can use profilers and divide the number of retired instructions on the number of calls.

如果要计算在函数的平均情况下调用的指令数,可以使用分析器并将已退出指令的数量除以调用次数。

#9


1  

I think it will work on windows programs created with msvc, as for branches the 'ret' seems to always come at the end (even if there are branches that return early it does a jne to go the end). However you will need some kind of disassembler library to figure the current opcode length as they are variable length for x86. If you don't do this you'll run into false positives.

我认为它将适用于使用msvc创建的Windows程序,对于分支,'ret'似乎总是在结尾处(即使有早期返回的分支,它也会完成jne)。但是,您需要某种反汇编程序库来计算当前的操作码长度,因为它们是x86的可变长度。如果你不这样做,你会遇到误报。

I would not be surprised if there are cases this doesn't catch.

如果有些情况没有发现,我不会感到惊讶。

#10


1  

There is no facilities in Standard C++ to obtain the size or length of a function.
See my answer here: Is it possible to load a function into some allocated memory and run it from there?

标准C ++中没有用于获取函数大小或长度的工具。请参阅我的答案:是否可以将函数加载到某个已分配的内存中并从那里运行它?

In general, knowing the size of a function is used in embedded systems when copying executable code from a read-only source (or a slow memory device, such as a serial Flash) into RAM. Desktop and other operating systems load functions into memory using other techniques, such as dynamic or shared libraries.

通常,在将可执行代码从只读源(或慢速存储器设备,例如串行闪存)复制到RAM中时,在嵌入式系统中使用了解函数的大小。桌面和其他操作系统使用其他技术将功能加载到内存中,例如动态或共享库。

#11


1  

Just set PAGE_EXECUTE_READWRITE at the address where you got your function. Then read every byte. When you got byte "0xCC" it means that the end of function is actual_reading_address - 1.

只需在您获得功能的地址设置PAGE_EXECUTE_READWRITE即可。然后读取每个字节。当你得到字节“0xCC”时,意味着函数的结尾是actual_reading_address - 1。

#12


0  

Using GCC, not so hard at all.

使用GCC,根本不是那么难。

void do_something(void) { 
   printf("%s!", "Hello your name is Cemetech"); 
   do_something_END: 
} 

... 

   printf("size of function do_something: %i", (int)(&&do_something_END - (int)do_something));

#13


0  

below code the get the accurate function block size, it works fine with my test runtime_checks disable _RTC_CheckEsp in debug mode

在代码下面得到准确的功能块大小,它在我的测试中运行正常运行时_check在调试模式下禁用_RTC_CheckEsp

    #pragma runtime_checks("", off)
DWORD __stdcall loadDll(char* pDllFullPath)
{  
    OutputDebugStringA(pDllFullPath);
    //OutputDebugStringA("loadDll...................\r\n");
    return 0;
    //return test(pDllFullPath);
}
#pragma runtime_checks("", restore)

DWORD __stdcall getFuncSize_loadDll()
{
    DWORD maxSize=(PBYTE)getFuncSize_loadDll-(PBYTE)loadDll;
    PBYTE pTail=(PBYTE)getFuncSize_loadDll-1;
    while(*pTail != 0xC2 && *pTail != 0xC3) --pTail;
    if (*pTail==0xC2)
    {   //0xC3          : ret
        //0xC2 04 00    : ret 4
        pTail +=3;
    }

    return pTail-(PBYTE)loadDll;
};

#14


0  

I'm posting this to say two things:

我发布这个说两件事:

1) Most of the answers given here are really bad and will break easily. If you use the C function pointer (using the function name), in a debug build of your executable, and possibly in other circumstances, it may point to a JMP shim that will not have the function body itself. Here's an example. If I do the following for the function I defined below:

1)这里给出的大部分答案都非常糟糕,很容易破解。如果您使用C函数指针(使用函数名称),在可执行文件的调试版本中,并且可能在其他情况下,它可能指向一个不具有函数体本身的JMP填充程序。这是一个例子。如果我为以下定义的函数执行以下操作:

FARPROC pfn = (FARPROC)some_function_with_possibility_to_get_its_size_at_runtime;

the pfn I get (for example: 0x7FF724241893) will point to this, which is just a JMP instruction:

我得到的pfn(例如:0x7FF724241893)将指向此,这只是一个JMP指令:

获取C ++函数的大小

Additionally, a compiler can nest several of those shims, or branch your function code so that it will have multiple epilogs, or ret instructions. Heck, it may not even use a ret instruction. Then, there's no guarantee that functions themselves will be compiled and linked in the order you define them in the source code.

此外,编译器可以嵌套其中几个填充程序,或者分支您的功能代码,以便它具有多个epilog或ret指令。哎呀,甚至可能都没有使用ret指令。然后,无法保证函数本身将按照您在源代码中定义它们的顺序进行编译和链接。

You can do all that stuff in assembly language, but not in C or C++.

你可以用汇编语言完成所有这些工作,但不能用C或C ++。

2) So that above was the bad news. The good news is that the answer to the original question is, yes, there's a way (or a hack) to get the exact function size, but it comes with the following limitations:

2)所以上面是坏消息。好消息是,原始问题的答案是,是的,有一种方法(或黑客)来获得确切的函数大小,但它有以下限制:

  • It works in 64-bit executables on Windows only.

    它仅适用于Windows上的64位可执行文件。

  • It is obviously Microsoft specific and is not portable.

    它显然是微软特有的,不可移植。

  • You have to do this at run-time.

    您必须在运行时执行此操作。

The concept is simple -- utilize the way SEH is implemented in x64 Windows binaries. Compiler adds details of each function into the PE32+ header (into the IMAGE_DIRECTORY_ENTRY_EXCEPTION directory of the optional header) that you can use to obtain the exact function size. (In case you're wondering, this information is used for catching, handling and unwinding of exceptions in the __try/__except/__finally blocks.)

这个概念很简单 - 利用SEH在x64 Windows二进制文件中的实现方式。编译器将每个函数的详细信息添加到PE32 +标头(可选标头的IMAGE_DIRECTORY_ENTRY_EXCEPTION目录)中,您可以使用它来获取确切的函数大小。 (如果您想知道,此信息用于捕获,处理和展开__try / __除了/ __ finally块之外的异常。)

Here's a quick example:

这是一个简单的例子:

//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);

and then:

#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;
    }
}

#1


10  

No, this will not work:

不,这不起作用:

  1. There is no guarantee that your function only contains a single ret instruction.
  2. 无法保证您的函数只包含一条ret指令。

  3. Even if it only does contain a single ret, you can't just look at the individual bytes - because the corresponding value could appear as simply a value, rather than an instruction.
  4. 即使它只包含一个ret,你也不能只查看单个字节 - 因为相应的值可能只是一个值,而不是一条指令。

The first problem can possibly be worked around if you restrict your coding style to, say, only have a single point of return in your function, but the other basically requires a disassembler so you can tell the individual instructions apart.

如果你将编码风格限制在你的函数中只有一个单一的返回点,那么第一个问题可能会解决,但另一个基本上需要一个反汇编程序,这样你就可以区分各个指令。

#2


12  

It is possible to obtain all blocks of a function, but is an unnatural question to ask what is the 'size' of a function. Optimized code will rearrange code blocks in the order of execution and will move seldom used blocks (exception paths) into outer parts of the module. For more details, see Profile-Guided Optimizations for example how Visual C++ achieves this in link time code generation. So a function can start at address 0x00001000, branch at 0x00001100 into a jump at 0x20001000 and a ret, and have some exception handling code 0x20001000. At 0x00001110 another function starts. What is the 'size' of your function? It does span from 0x00001000 to +0x20001000, but it 'owns' only few blocks in that span. So your question should be unasked.

可以获得函数的所有块,但是问一个函数的“大小”是一个不自然的问题。优化的代码将按执行顺序重新排列代码块,并将很少使用的块(异常路径)移动到模块的外部部分。有关更多详细信息,请参阅配置文件引导优化,例如Visual C ++如何在链接时代码生成中实现此目的。因此,函数可以从地址0x00001000开始,在0x00001100处跳转到0x20001000处的跳转和ret,并且具有一些异常处理代码0x20001000。在0x00001110,另一个函数启动。你的功能的“大小”是多少?它确实从0x00001000到+ 0x20001000,但它'拥有'在该范围内只有几个块。所以你的问题应该是没有问题的。

There are other valid questions in this context, like the total number of instructions a function has (can be determined from the program symbol database and from the image), and more importantly, what is the number of instructions in the frequent executed code path inside the function. All these are questions normally asked in the context of performance measurement and there are tools that instrument code and can give very detailed answers.

在此上下文中还有其他有效问题,例如函数的指令总数(可以从程序符号数据库和图像中确定),更重要的是,内部频繁执行的代码路径中的指令数是多少功能。所有这些都是通常在绩效衡量的背景下提出的问题,并且有一些工具可以用来代码,并且可以提供非常详细的答案。

Chasing pointers in memory and searching for ret will get you nowhere I'm afraid. Modern code is way way way more complex than that.

在内存中追逐指针并搜索ret会让你无处可去。现代代码比这更复杂。

#3


7  

This won't work... what if there's a jump, a dummy ret, and then the target of the jump? Your code will be fooled.

这不起作用......如果有跳跃,虚拟后退,然后是跳跃的目标呢?你的代码会被愚弄。

In general, it's impossible to do this with 100% accuracy because you have to predict all code paths, which is like solving the halting problem. You can get "pretty good" accuracy if you implement your own disassembler, but no solution will be nearly as easy as you imagine.

通常,不可能以100%的准确率执行此操作,因为您必须预测所有代码路径,这就像解决暂停问题一样。如果你实现自己的反汇编程序,你可以获得“非常好”的准确性,但没有任何解决方案几乎像你想象的那样容易。

A "trick" would be to find out which function's code is after the function that you're looking for, which would give pretty good results assuming certain (dangerous) assumptions. But then you'd have to know what function comes after your function, which, after optimizations, is pretty hard to figure out.

一个“技巧”是找出你正在寻找的函数之后哪个函数的代码,假设某些(危险的)假设,这将给出相当好的结果。但是你必须知道在你的函数之后出现了什么函数,在优化之后很难弄明白。


Edit 1:

What if the function doesn't even end with a ret instruction at all? It could very well just jmp back to its caller (though it's unlikely).

如果函数甚至没有以ret指令结束怎么办?它很可能只是jmp回到它的调用者(虽然它不太可能)。


Edit 2:

Don't forget that x86, at least, has variable-length instructions...

别忘了x86至少有可变长度指令......


Update:

For those saying that flow analysis isn't the same as solving the halting problem:

对于那些说流量分析与解决暂停问题不同的人:

Consider what happens when you have code like:

考虑当您拥有以下代码时会发生什么:

foo:
    ....
    jmp foo

You will have to follow the jump each time to figure out the end of the function, and you cannot ignore it past the first time because you don't know whether or not you're dealing with self-modifying code. (You could have inline assembly in your C++ code that modifies itself, for instance.) It could very well extend to some other place of memory, so your analyzer will (or should) end in an infinite loop, unless you tolerate false negatives.

每次都必须按照跳转来确定函数的结束,并且你不能在第一次之后忽略它,因为你不知道你是否正在处理自修改代码。 (例如,您可以在C ++代码中使用内联汇编来修改自身。)它可以很好地扩展到其他内存位置,因此您的分析器将(或应该)以无限循环结束,除非您容忍漏报。

Isn't that like the halting problem?

这不是停止问题吗?

#4


7  

Wow, I use function size counting all the time and it has lots and lots of uses. Is it reliable? No way. Is it standard c++? No way. But that's why you need to check it in the disassembler to make sure it worked, every time that you release a new version. Compiler flags can mess up the ordering.

哇,我一直使用函数大小计数,它有很多很多用途。它可靠吗?没门。它是标准的c ++吗?没门。但这就是为什么每次发布新版本时都需要在反汇编程序中检查它以确保它有效。编译器标志可能会破坏排序。

static void funcIwantToCount()
{
   // do stuff
}
static void funcToDelimitMyOtherFunc()
{
   __asm _emit 0xCC
   __asm _emit 0xCC
   __asm _emit 0xCC
   __asm _emit 0xCC
}

int getlength( void *funcaddress )
{
   int length = 0;
   for(length = 0; *((UINT32 *)(&((unsigned char *)funcaddress)[length])) != 0xCCCCCCCC; ++length);
   return length;
}

It seems to work better with static functions. Global optimizations can kill it.

它似乎与静态函数更好地工作。全局优化可以杀死它。

P.S. I hate people, asking why you want to do this and it's impossible, etc. Stop asking these questions, please. Makes you sound stupid. Programmers are often asked to do non-standard things, because new products almost always push the limits of what's availble. If they don't, your product is probably a rehash of what's already been done. Boring!!!

附:我讨厌别人,问你为什么要这样做,这是不可能的等等。请不要再问这些问题了。让你听起来很愚蠢。程序员经常被要求做非标准的事情,因为新产品几乎总是突破了可用的限制。如果他们不这样做,那么您的产品可能是对已经完成的工作的重复。无聊!

#5


2  

The real solution to this is to dig into your compiler's documentation. The ARM compiler we use can be made to produce an assembly dump (code.dis), from which it's fairly trivial to subtract the offsets between a given mangled function label and the next mangled function label.

真正的解决方案是深入了解编译器的文档。我们使用的ARM编译器可以生成一个汇编转储(code.dis),从中减去给定的受损函数标签和下一个受损函数标签之间的偏移量是相当简单的。

I'm not certain which tools you will need for this with a windows target, however. It looks like the tools listed in the answer to this question might be what you're looking for.

但是,我不确定使用Windows目标需要哪些工具。看起来这个问题的答案中列出的工具可能正是您正在寻找的。

Also note that I (working in the embedded space) assumed you were talking about post-compile-analysis. It still might be possible to examine these intermediate files programmatically as part of a build provided that:

另请注意,我(在嵌入式空间中工作)假设您正在讨论编译后分析。仍然可以以编程方式检查这些中间文件作为构建的一部分,前提是:

  • The target function is in a different object
  • 目标函数位于不同的对象中

  • The build system has been taught the dependencies
  • 构建系统已经被教导了依赖关系

  • You know for sure that the compiler will build these object files
  • 您确定编译器将构建这些目标文件

Note that I'm not sure entirely WHY you want to know this information. I've needed it in the past to be sure that I can fit a particular chunk of code in a very particular place in memory. I have to admit I'm curious what purpose this would have on a more general desktop-OS target.

请注意,我不确定您想知道这些信息的原因。我过去需要它,以确保我可以在内存中的特定位置放入特定的代码块。我不得不承认,我很奇怪这会对更通用的桌面操作系统目标产生什么目的。

#6


2  

This can work in very limited scenarios. I use it in part of a code injection utility I wrote. I don't remember where I found the information, but I have the following (C++ in VS2005):

这可以在非常有限的情况下工作。我在我编写的代码注入实用程序的一部分中使用它。我不记得我在哪里找到了这些信息,但我有以下内容(VS2005中的C ++):

#pragma runtime_checks("", off)

static DWORD WINAPI InjectionProc(LPVOID lpvParameter)
{
    // do something
    return 0;
}

static DWORD WINAPI InjectionProcEnd()
{
    return 0;
}

#pragma runtime_checks("", on)

And then in some other function I have:

然后在我的其他一些功能中:

size_t cbInjectionProc = (size_t)InjectionProcEnd - (size_t)InjectionProc;

You have to turn off some optimizations and declare the functions as static to get this to work; I don't recall the specifics. I don't know if this is an exact byte count, but it is close enough. The size is only that of the immediate function; it doesn't include any other functions that may be called by that function. Aside from extreme edge cases like this, "the size of a function" is meaningless and useless.

你必须关闭一些优化并将函数声明为静态以使其工作;我不记得具体细节。我不知道这是否是一个确切的字节数,但它足够接近。大小只是直接功能的大小;它不包括该函数可能调用的任何其他函数。除了像这样的极端边缘情况,“函数的大小”是没有意义和无用的。

#7


1  

In C++, the there is no notion of function size. In addition to everything else mentioned, preprocessor macros also make for an indeterminate size. If you want to count number of instruction words, you can't do that in C++, because it doesn't exist until it's been compiled.

在C ++中,没有函数大小的概念。除了提到的所有其他内容之外,预处理器宏也会产生不确定的大小。如果要计算指令字数,则不能在C ++中这样做,因为它在编译之前不存在。

#8


1  

What do you mean "size of a function"?

你是什​​么意思“功能的大小”?

If you mean a function pointer than it is always just 4 bytes for 32bits systems.

如果你的意思是一个函数指针,那么32位系统总是只有4个字节。

If you mean the size of the code than you should just disassemble generated code and find the entry point and closest ret call. One way to do it is to read the instruction pointer register at the beginning and at the end of your function.

如果你的意思是代码的大小,你应该只是反汇编生成的代码并找到入口点和最近的ret调用。一种方法是在函数的开头和结尾读取指令指针寄存器。

If you want to figure out the number of instructions called in the average case for your function you can use profilers and divide the number of retired instructions on the number of calls.

如果要计算在函数的平均情况下调用的指令数,可以使用分析器并将已退出指令的数量除以调用次数。

#9


1  

I think it will work on windows programs created with msvc, as for branches the 'ret' seems to always come at the end (even if there are branches that return early it does a jne to go the end). However you will need some kind of disassembler library to figure the current opcode length as they are variable length for x86. If you don't do this you'll run into false positives.

我认为它将适用于使用msvc创建的Windows程序,对于分支,'ret'似乎总是在结尾处(即使有早期返回的分支,它也会完成jne)。但是,您需要某种反汇编程序库来计算当前的操作码长度,因为它们是x86的可变长度。如果你不这样做,你会遇到误报。

I would not be surprised if there are cases this doesn't catch.

如果有些情况没有发现,我不会感到惊讶。

#10


1  

There is no facilities in Standard C++ to obtain the size or length of a function.
See my answer here: Is it possible to load a function into some allocated memory and run it from there?

标准C ++中没有用于获取函数大小或长度的工具。请参阅我的答案:是否可以将函数加载到某个已分配的内存中并从那里运行它?

In general, knowing the size of a function is used in embedded systems when copying executable code from a read-only source (or a slow memory device, such as a serial Flash) into RAM. Desktop and other operating systems load functions into memory using other techniques, such as dynamic or shared libraries.

通常,在将可执行代码从只读源(或慢速存储器设备,例如串行闪存)复制到RAM中时,在嵌入式系统中使用了解函数的大小。桌面和其他操作系统使用其他技术将功能加载到内存中,例如动态或共享库。

#11


1  

Just set PAGE_EXECUTE_READWRITE at the address where you got your function. Then read every byte. When you got byte "0xCC" it means that the end of function is actual_reading_address - 1.

只需在您获得功能的地址设置PAGE_EXECUTE_READWRITE即可。然后读取每个字节。当你得到字节“0xCC”时,意味着函数的结尾是actual_reading_address - 1。

#12


0  

Using GCC, not so hard at all.

使用GCC,根本不是那么难。

void do_something(void) { 
   printf("%s!", "Hello your name is Cemetech"); 
   do_something_END: 
} 

... 

   printf("size of function do_something: %i", (int)(&&do_something_END - (int)do_something));

#13


0  

below code the get the accurate function block size, it works fine with my test runtime_checks disable _RTC_CheckEsp in debug mode

在代码下面得到准确的功能块大小,它在我的测试中运行正常运行时_check在调试模式下禁用_RTC_CheckEsp

    #pragma runtime_checks("", off)
DWORD __stdcall loadDll(char* pDllFullPath)
{  
    OutputDebugStringA(pDllFullPath);
    //OutputDebugStringA("loadDll...................\r\n");
    return 0;
    //return test(pDllFullPath);
}
#pragma runtime_checks("", restore)

DWORD __stdcall getFuncSize_loadDll()
{
    DWORD maxSize=(PBYTE)getFuncSize_loadDll-(PBYTE)loadDll;
    PBYTE pTail=(PBYTE)getFuncSize_loadDll-1;
    while(*pTail != 0xC2 && *pTail != 0xC3) --pTail;
    if (*pTail==0xC2)
    {   //0xC3          : ret
        //0xC2 04 00    : ret 4
        pTail +=3;
    }

    return pTail-(PBYTE)loadDll;
};

#14


0  

I'm posting this to say two things:

我发布这个说两件事:

1) Most of the answers given here are really bad and will break easily. If you use the C function pointer (using the function name), in a debug build of your executable, and possibly in other circumstances, it may point to a JMP shim that will not have the function body itself. Here's an example. If I do the following for the function I defined below:

1)这里给出的大部分答案都非常糟糕,很容易破解。如果您使用C函数指针(使用函数名称),在可执行文件的调试版本中,并且可能在其他情况下,它可能指向一个不具有函数体本身的JMP填充程序。这是一个例子。如果我为以下定义的函数执行以下操作:

FARPROC pfn = (FARPROC)some_function_with_possibility_to_get_its_size_at_runtime;

the pfn I get (for example: 0x7FF724241893) will point to this, which is just a JMP instruction:

我得到的pfn(例如:0x7FF724241893)将指向此,这只是一个JMP指令:

获取C ++函数的大小

Additionally, a compiler can nest several of those shims, or branch your function code so that it will have multiple epilogs, or ret instructions. Heck, it may not even use a ret instruction. Then, there's no guarantee that functions themselves will be compiled and linked in the order you define them in the source code.

此外,编译器可以嵌套其中几个填充程序,或者分支您的功能代码,以便它具有多个epilog或ret指令。哎呀,甚至可能都没有使用ret指令。然后,无法保证函数本身将按照您在源代码中定义它们的顺序进行编译和链接。

You can do all that stuff in assembly language, but not in C or C++.

你可以用汇编语言完成所有这些工作,但不能用C或C ++。

2) So that above was the bad news. The good news is that the answer to the original question is, yes, there's a way (or a hack) to get the exact function size, but it comes with the following limitations:

2)所以上面是坏消息。好消息是,原始问题的答案是,是的,有一种方法(或黑客)来获得确切的函数大小,但它有以下限制:

  • It works in 64-bit executables on Windows only.

    它仅适用于Windows上的64位可执行文件。

  • It is obviously Microsoft specific and is not portable.

    它显然是微软特有的,不可移植。

  • You have to do this at run-time.

    您必须在运行时执行此操作。

The concept is simple -- utilize the way SEH is implemented in x64 Windows binaries. Compiler adds details of each function into the PE32+ header (into the IMAGE_DIRECTORY_ENTRY_EXCEPTION directory of the optional header) that you can use to obtain the exact function size. (In case you're wondering, this information is used for catching, handling and unwinding of exceptions in the __try/__except/__finally blocks.)

这个概念很简单 - 利用SEH在x64 Windows二进制文件中的实现方式。编译器将每个函数的详细信息添加到PE32 +标头(可选标头的IMAGE_DIRECTORY_ENTRY_EXCEPTION目录)中,您可以使用它来获取确切的函数大小。 (如果您想知道,此信息用于捕获,处理和展开__try / __除了/ __ finally块之外的异常。)

Here's a quick example:

这是一个简单的例子:

//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);

and then:

#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;
    }
}