获取C ++函数的大小

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

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

编辑1:澄清我的意思是“大小”我的意思是组成该函数的字节数(机器代码)。
编辑2:有几个意见,问为什么,我打算怎么做这个。 诚实的答案是我没有意图,我真的不知道知道一个函数长度预编译时间的好处。 (虽然我确定有一些)

这对我来说似乎是一个有效的方法,将这项工作?

不,这不行:

  1. 不能保证你的函数只包含一个ret指令。
  2. 即使它只包含一个ret ,也不能只看单个字节,因为相应的值可能只是一个值,而不是一条指令。

第一个问题可能会被解决,如果你限制你的编码风格,比方说,你的函数只有一个返回点,但另一个基本上需要一个反汇编器,所以你可以告诉单独的指令。

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

在这种情况下,还有其他一些有效的问题,比如一个函数所具有的指令总数(可以从程序符号数据库和图像中确定),更重要的是,频繁执行的代码路径中的指令数目是多少功能。 所有这些都是通常在性能测量方面提出的问题,还有一些工具代码可以给出非常详细的答案。

追逐记忆中的指针并寻找ret会让你无处可逃。 现代的代码是比这更复杂的方式。

这将不起作用…如果有一个跳跃,一个虚拟ret ,然后跳转的目标? 你的代码将被愚弄。

一般来说,以100%的精度来做这件事是不可能的 ,因为你必须预测所有的代码路径,就像解决停止问题一样 。 如果你实现自己的反汇编程序,你可以得到“相当不错”的精确度,但是没有解决方案会像你想象的那么容易。

一个“诀窍”是找出哪个函数的代码位于你正在寻找的函数之后 ,假设某些(危险的)假设,这将会产生相当好的结果 但是你必须知道在你的函数之后有什么函数,在优化之后,这个函数很难弄清楚。


编辑1:

如果函数甚至不能以ret指令结束呢? 它可以很好地回到它的调用者(尽管这是不可能的)。


编辑2:

不要忘记x86至少有可变长度的指令


更新:

对于那些认为流程分析与解决停机问题不一样的人来说:

考虑一下当你有这样的代码时会发生什么:

 foo: .... jmp foo 

每次都要跟随跳转来找出函数的结尾,而且不能忽略它,因为你不知道你是否正在处理自修改代码。 (例如,你可以在你的C ++代码中内联程序集来修改它自己)。它可以很好地扩展到其他一些内存,所以你的分析器将会(或者应该)以一个无限循环结束,除非你容忍错误的否定。

是不是像停止问题?

哇,我一直使用函数大小计数,它有很多很多的用途。 它可靠吗? 没门。 它是标准的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; } 

它似乎更好地与静态功能。 全局优化可以杀死它。

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

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

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

另外请注意,我(在嵌入式领域工作)假设您正在讨论编译后分析。 通过编程的方式来检查这些中间文件仍然是可能的,只要:

  • 目标函数是在一个不同的对象
  • 构建系统已被教导的依赖
  • 您肯定知道编译器会构建这些对象文件

请注意,我不确定为什么你想知道这些信息。 我以前需要它,以确保我可以在内存中的特定位置放入特定的代码块。 我不得不承认,我很好奇在一个更一般的桌面操作系统的目标上会有什么目的。

可以在非常有限的情况下工作。 我使用它在我写的代码注入工具的一部分。 我不记得我在哪里找到的信息,但我有以下(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) 

然后在其他一些功能,我有:

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

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

在C ++中,没有函数大小的概念。 除了所提到的所有内容之外,预处理器宏也会造成不确定的大小。 如果要计算指令字的数量,则不能在C ++中执行该操作,因为在编译之前它不存在。

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

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

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

如果要计算出平均情况下针对您的功能调用的指令数量,则可以使用分析器并在呼叫数量上划分已引用指令的数量。

我认为它会在用msvc创建的windows程序上工作,对于分支来说,'ret'似乎总是在最后(即使有分支返回的时间太早也会结束)。 但是,您需要某种反汇编程序库来确定当前的操作码长度,因为它们是x86的可变长度。 如果你不这样做,你会遇到误报。

如果有这种情况,我不会感到惊讶。

标准C ++中没有设施来获取函数的大小或长度。
看到我的答案在这里: 是否有可能加载到一些分配的内存的功能,并从那里运行?

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

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

使用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));