如何从 C 程序内部或使用内联汇编获取 C 函数的大小?

Posted

技术标签:

【中文标题】如何从 C 程序内部或使用内联汇编获取 C 函数的大小?【英文标题】:How to get the size of a C function from inside a C program or with inline assembly? 【发布时间】:2012-07-09 17:39:57 【问题描述】:

假设我有如下函数:

# cat 003.c

int foo(int a, int b)

    return a+b;

并像这样编译它:

gcc -S 003.c

得到如下组装结果:

     .file   "003.c"
     .text
 .globl foo
     .type   foo, @function
 foo:
 .LFB2:
     pushq   %rbp
 .LCFI0:
     movq    %rsp, %rbp
 .LCFI1:
     movl    %edi, -4(%rbp)
     movl    %esi, -8(%rbp)
     movl    -8(%rbp), %edx
     movl    -4(%rbp), %eax
     addl    %edx, %eax
     leave
     ret
 .LFE2:
     .size   foo, .-foo /* size of the function foo, how to get it?*/

上面的最后一行确实得到了函数的大小。编译器在哪里存储大小?我可以在我的原始 C 程序中使用 C 或内联 asm 以某种方式获取函数的大小吗?

【问题讨论】:

不幸的是__builtin_object_size 不起作用(至少对于 gcc 4.7) 符号/对象的大小可能是 ELF 符号表的一部分。但要使其在运行时可供程序访问,则需要链接器,当然还有汇编器支持。我还没有看到任何证据表明 GNU 汇编器支持访问符号的大小。我相信 GNU 汇编器和 GNU 链接器根本不支持。大小可能只是符号表的一部分,仅用于调试目的。 【参考方案1】:

为什么不取函数指针和函数末尾当前地址的差呢?看看这个问题恢复当前IP地址:Get address of current instruction for x86,可能是这个代码,stolen form one of the reply:

unsigned long get_PC()

    unsigned long current_instruction;

    __asm__ __volatile__
    (
        "movq 8(%rbp), %rax\n\t"
        : "=a" (current_instruction)
    );

    return current_instruction;

会成功的,

【讨论】:

这将是非常脆弱的,更不用说便携了。 不同意这个想法的脆弱性,也许是你用来恢复地址的方式,AFAIK gcc 应该有一些嵌入的方式来获得这个,但我不记得名字了。 如果你改变优化选项或一些代码,你会得到与现实大不相同的结果。 return __builtin_return_address(0);gcc.gnu.org/onlinedocs/gcc/Return-Address.html __attribute__((__noinline__))asm(""); gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html【参考方案2】:

有关函数大小的信息存储在相应符号(名称)的ELF 属性 中。 C 示例代码如何以编程方式解析此代码位于 Solaris 手册页底部 forgelf_getsym(3ELF)(Linux、*BSD 和 MacOS 中确实存在 libelf,您需要查找 GElf_Sym 结构的 st_size 字段),但您也可以使用 objdump / elfdump (Solaris) / readelf (Linux) 来完成任务:

$ objdump -h -d --section=.text foo3.o

foo3.o:文件格式elf64-x86-64

部分:
Idx 名称大小 VMA LMA 文件关闭 Algn
  0 .文本 00000012 0000000000000000 0000000000000000 00000040 2**2
                  内容、分配、加载、只读、代码
[ ... ]
部分.text的反汇编:

0000000000000000 :
   0: 55 推送 %rbp
   1: 48 89 e5 移动 %rsp,%rbp
   4: 89 7d fc mov %edi,0xfffffffffffffffc(%rbp)
   7: 89 75 f8 移动 %esi,0xffffffffffffff8(%rbp)
   a: 8b 45 f8 mov 0xfffffffffffffff8(%rbp),%eax
   d: 03 45 fc 添加 0xfffffffffffffffc(%rbp),%eax
  10:c9请假
  11:c3 retq

这是针对您的代码的未优化编译,而优化版本是:

$ objdump -h -d --section=.text foo3.o

foo3.o:文件格式elf64-x86-64

部分:
Idx 名称大小 VMA LMA 文件关闭 Algn
  0 .文本 00000004 0000000000000000 0000000000000000 00000040 2**4
                  内容、分配、加载、只读、代码
[ ... ]
部分.text的反汇编:

0000000000000000 :
   0: 8d 04 37 lea (%rdi,%rsi,1),%eax
   3:c3 retq

注意“大小”从 0x124 的变化?这就是来自 .size 汇编程序指令的内容。

尝试使用内联汇编为您提供函数大小/代码位置的“技巧”不考虑编译器生成的胶水代码(函数入口序言/退出尾声,内联代码生成......),也不考虑编译器重新排序内联汇编(gcc 这样做是臭名昭著的),因此相信这一点通常不是一个好主意。最后,这取决于你到底想做什么......

编辑:更多引用,外部以及 ***:

    来自 gcc 邮件列表,thread on sizeof(function) what does sizeof (function name) return? Find size of a function in C LibELF by example sourceforge 项目(这是文档/教程)

【讨论】:

我不明白:您引用的 objdump 输出显示的是文本部分的大小,而不是单个函数。 readelf -s 但是确实显示了您提到的符号的 st_size 属性。

以上是关于如何从 C 程序内部或使用内联汇编获取 C 函数的大小?的主要内容,如果未能解决你的问题,请参考以下文章

MDK中嵌入汇编方法

在 C 内联汇编中将 Segfault 推送到堆栈

升级版 | C和汇编互相调用及规则

如何使用 Clang 在 C 程序中嵌入 LLVM 程序集或内在函数?

C语言进阶——内联汇编

C语言进阶——内联汇编