【问题标题】:How to get the length of a function in bytes?如何以字节为单位获取函数的长度?
【发布时间】:2010-11-11 16:20:50
【问题描述】:

我想知道运行时 C 函数(由我编写)的长度。有什么方法可以得到吗?看来 sizeof 在这里不起作用。

【问题讨论】:

  • 准确地定义“C 函数的长度”
  • 我的意思是这个函数的代码占用的内存。
  • 如果您问这个问题的原因与我多年前想知道的相同,请让我指出Writing a New Jit
  • 你可以通过减去你需要的函数的指针来计算得到长度和下一个函数。但这取决于编译器。我不确定优化参数是否也不会影响安排。所以这不是你问题的答案。
  • 请注意,函数不是对象(标准明确指出),您无法获取它们的大小。

标签: c


【解决方案1】:

有一种方法可以确定函数的大小。命令是:

 nm -S <object_file_name>

这将返回目标文件中每个函数的大小。使用 'man nm' 查阅 GNU 的手册页以收集更多信息。

【讨论】:

  • nm -S --size-sort -t d &lt;objfile&gt; | grep &lt;pattern&gt; 显示名称中包含 pattern 的符号的以 10 为底的大小(-t d 表示 十进制),按大小排序。很酷。
【解决方案2】:

如果您使用自定义链接器脚本,您可以从链接器获取此信息。为给定的函数添加一个链接器部分,两边都有链接器符号:

mysec_start = .;
*(.mysection)
mysec_end = .;

然后您可以将功能专门分配给该部分。符号之间的区别在于函数的长度:

#include <stdio.h>

int i;

 __attribute__((noinline, section(".mysection"))) void test_func (void)
{
    i++;
}

int main (void)
{
    extern unsigned char mysec_start[];
    extern unsigned char mysec_end[];

    printf ("Func len: %lu\n", mysec_end - mysec_start);
    test_func ();

    return 0;
}

此示例适用于 GCC,但任何 C 工具链都应该有一种方法来指定将函数分配给哪个部分。我会根据组装清单检查结果,以验证它是否按照您希望的方式工作。

【讨论】:

  • 您可以在没有自定义链接器脚本的情况下执行此操作,方法是使用有效的 C 标识符名称调用该部分,例如 mysection 而不是 .mysection,并使用 __start_mysection__stop_mysection 而不是mysec_startmysec_end。链接器会自动为遇到的每个部分定义这些符号。
  • 太棒了!我使用有效的标识符名称为自己尝试了它,并且确实它适用于没有链接器脚本的普通 GCC。 acapola's answer 有一个工作示例,改编自这个。
【解决方案3】:

在标准 C 中无法获取函数占用的内存量。

【讨论】:

  • Erhm.. 是的,它被称为指针运算。 :P
  • @ChristofferBubach Ummm, that's wrong。在符合标准的 C 语言中,您只能对指向同一对象或对象末尾后一项的指针进行指针运算。您不能对指向不同对象的指针进行指针运算。而且函数指针无论如何都不会引用对象。
  • 正如其他人指出的那样,可以使用链接描述文件来完成。该问题不限于 C 标准,因此基于链接器的解决方案是有效的
  • 指针是数字,数字可以用来做数学。看?哈哈,公平地说,我应该猜到它被认为是“未定义的”,但真的 - 为什么它不起作用 - 知道平台也包含分段或者......?
【解决方案4】:

我刚刚为完全相同的问题想出了一个解决方案,但我编写的代码取决于平台。

背后的想法,将已知操作码放在函数的末尾,并在计算我们跳过的字节时从头开始搜索它们。 这是我用一些代码解释的媒体链接 https://medium.com/@gurhanpolat/calculate-c-function-size-x64-x86-c1f49921aa1a

【讨论】:

  • 有没有办法不为此修改代码?
【解决方案5】:

可执行文件(至少已剥离调试信息的可执行文件)不会以任何方式存储函数长度。所以不可能在运行时自行解析这些信息。如果您必须使用函数进行操作,您应该在链接阶段对您的对象做一些事情,或者通过从您的可执行文件中将它们作为文件访问。例如,您可以告诉链接器将符号表作为普通数据段链接到可执行文件中,为它们分配一些名称,并在程序运行时进行解析。但请记住,这将特定于您的链接器和对象格式。

另请注意,函数布局也是特定于平台的,并且有些事情使术语“函数长度”不清楚:

  1. 函数可能将使用的常量直接存储在函数代码之后的代码段中,并使用 PC 相对寻址(ARM 编译器执行此操作)访问它们。
  2. 函数可能有“序言”和“结语”,这可能是多个函数共有的,因此位于主体之外。
  3. 函数代码可以内联其他函数代码

它们都可以计入函数长度,也可以不计入函数长度。

函数也可能被编译器完全内联,所以它会松散它的身体。

【讨论】:

  • 他们可以,但是这些函数仍然有固定的长度,如果没有这些外部依赖,它们只会行为不端。这些函数可以通过将它们的依赖项作为参数来重建为自包含,许多函数,尤其是回调函数,正是这样。
【解决方案6】:

在例如Codewarrior,您可以在函数周围放置标签,例如

label1:
void someFunc()
{
    /* code goes here. */
}
label2:

然后像(int)(label2-label1)一样计算大小,但这显然非常依赖于编译器。根据您的系统和编译器,您可能需要破解链接器脚本等。

【讨论】:

  • 不知道。有趣的。有点。
  • 有一个类似的GCC extension,使用它你可以计算出&amp;&amp;label2 - &amp;&amp;label1的大小
  • 我希望这能工作,但它似乎不能在 Visual C++(C 语言)中工作。
【解决方案7】:

函数的开头是函数指针,你已经知道了。

问题是要找到终点,但可以这样做:

#include <time.h>

int foo(void)
{
   int i = 0;
   ++i + time(0); // time(0) is to prevent optimizer from just doing: return 1;
   return i;
}

int main(int argc, char *argv[])
{
   return (int)((long)main - (long)foo);
}

它在这里工作是因为程序只有两个函数,所以如果代码被重新排序(主要在 foo 之前实现),那么你会得到一个不相关的(负)计算,让你知道它不是这样工作的,但是如果您将 foo() 代码移动到 main() 中,它会起作用 - 只需减去最初否定答复获得的 main() 大小。

如果结果是肯定的,那么它就是正确的——如果没有进行填充(是的,一些编译器很乐意对代码进行膨胀,无论是为了对齐还是其他不太明显的原因)。

结尾的 (int)(long) 强制转换是为了在 32 位和 64 位代码之间移植(函数指针在 64 位平台上会更长)。

这是不便携的,应该可以很好地工作。

【讨论】:

    【解决方案8】:

    一个完整的解决方案,没有链接器或肮脏的平台依赖技巧:

    #include <stdio.h>
    
    int i;
    
     __attribute__((noinline, section("mysec"))) void test_func (void)
    {
        i++;
    }
    
    int main (void)
    {
        extern char __start_mysec[];
        extern char __stop_mysec[];
    
        printf ("Func len: %lu\n", __stop_mysec - __start_mysec);
        test_func ();
    
        return 0;
    }
    

    这就是您阅读FazJaxton's answerjakobbotsch's comment 时所得到的结果

    【讨论】:

    • 如果您使用链接器脚本,您可能仍需要修改该链接器脚本才能使此技巧发挥作用,因此您不妨使用 FazJaxton 的答案。
    【解决方案9】:

    C 语言本身没有定义返回函数长度的工具;涉及的变量太多(编译器、目标指令集、目标文件/可执行文件格式、优化设置、调试设置等)。相同的源代码可能会导致不同系统的功能大小不同。

    C 根本不提供任何类型的反射功能来支持此类信息(尽管个别编译器可能会提供扩展,例如 sskuce 引用的 Codewarrior 示例)。如果您需要知道函数在内存中占用了多少字节,则必须直接检查生成的对象或可执行文件。

    sizeof func 不起作用,因为表达式 func 被视为指向函数的指针,因此您获得的是指针值的大小,而不是函数本身。

    【讨论】:

    • 在符合标准的编译器中,sizeof func 不起作用,因为函数指示符不是 sizeof 运算符的有效操作数。
    • @JohnBode 虽然您是对的,但此信息不可移植;编译器在编译时仍然很清楚所有这些因素,并且结果对于该特定平台非常有意义。知道函数的大小在该平台上是有意义的,因为它允许您存储“只读”函数的 shellcode,这具有非常有趣且一致的含义,如果没有这样的功能我们就无法研究。
    【解决方案10】:

    只需从下一个函数的地址中减去你的函数的地址。但请注意,它可能不适用于您的系统,因此请仅在您 100% 确定:

    #include <stdint.h>
    
    int function() {
        return 0;
    }
    
    int function_end() {
        return 0;
    }
    
    int main(void) {
        intptr_t size = (intptr_t) function_end - (intptr_t) function;
    }
    

    【讨论】:

      【解决方案11】:

      在 C 或 C++ 中都没有标准的方法。可能自然存在实现/特定于平台的方法,但我不知道有任何

      【讨论】:

        【解决方案12】:
        int GetFuncSizeX86(unsigned char* Func)
         {
            if (!Func)
            {
                printf("x86Helper : Function Ptr NULL\n");
                return 0;
            }
        
            for (int count = 0; ; count++)
            {
                if (Func[count] == 0xC3)
                {
                    unsigned char prevInstruc = *(Func - 1);
                    if (Func[1] == 0xCC // int3
                        || prevInstruc == 0x5D//  pop    ebp
                        || prevInstruc == 0x5B//  pop    ebx
                        || prevInstruc == 0x5E//  pop    esi
                        || prevInstruc == 0x5F//  pop    edi
                        || prevInstruc == 0xCC//  int3
                        || prevInstruc == 0xC9)// leave
                        return count++;
                }
            }
        }`
        

        只需将函数转换为 char*

        【讨论】:

          【解决方案13】:

          您可以通过减去函数的地址来找到 C 函数的长度。 给你举个例子

          int function1()
              {
              } 
          
          int function2()
          {
              int a,b;    //just defining some variable for increasing the memory size
              printf("This function would take more memory than earlier function i.e function01 ");
          }
          
          int main()
          {
              printf("Printing the address of function01 %p\n",function01);
              printf("Printing the address of function02 %p\n",function02);
              printf("Printing the address of main %p\n",main);
              return 0;
          }
          

          希望您在编译后得到答案。编译后可以看到 function01 和 function2 的大小差异。

          注意:通常一个函数和另一个函数之间有 16 字节的差异。

          【讨论】:

          • 你的例子没有证明任何东西 :) 你只是在打印函数地址!
          猜你喜欢
          • 2015-03-05
          • 1970-01-01
          • 2012-04-09
          • 1970-01-01
          • 1970-01-01
          • 2013-05-07
          • 2013-06-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多