如何从C程序内部或使用内联汇编获得C函数的大小?
假设我有一个如下函数:如何从C程序内部或使用内联汇编获得C函数的大小?,c,gcc,assembly,elf,gnu-assembler,C,Gcc,Assembly,Elf,Gnu Assembler,假设我有一个如下函数: # 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(
# 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?*/
并按如下方式编译:
# 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?*/
获取以下程序集结果:
# 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或内联asm在我的原始C程序中以某种方式获得函数的大小吗?为什么不在函数末尾计算函数指针和当前地址的差值?请查看此问题以恢复当前IP地址:,可能是此代码,:
要做到这一点,有关函数大小的信息存储在对应符号(名称)的ELF属性中。C示例代码如何以编程方式解析此代码位于Solaris手册页的底部(Linux、*BSD和MacOS中也存在libelf,您需要查找
GElf_Sym
结构的st_size
字段),但您也可以使用objdump/elfdump(Solaris)/readelf(Linux)执行以下任务:
$ objdump -h -d --section=.text foo3.o
foo3.o: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000012 0000000000000000 0000000000000000 00000040 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
[ ... ]
Disassembly of section .text:
0000000000000000 <foo>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 89 7d fc mov %edi,0xfffffffffffffffc(%rbp)
7: 89 75 f8 mov %esi,0xfffffffffffffff8(%rbp)
a: 8b 45 f8 mov 0xfffffffffffffff8(%rbp),%eax
d: 03 45 fc add 0xfffffffffffffffc(%rbp),%eax
10: c9 leaveq
11: c3 retq
$objdump-h-d--section=.text foo3.o
foo3.o:文件格式elf64-x86-64
部分:
Idx名称大小VMA LMA文件关闭Algn
0.文本00000012 00000000000000000000000000000000000000000040 2**2
内容、分配、加载、只读、代码
[ ... ]
第节的分解。正文:
0000000000000000 :
0:55推送%rbp
1:48 89 e5 mov%rsp,%rbp
4:89 7d fc mov%edi,0xFFFFFFFFFFFFFC(%rbp)
7:89 75 f8移动%esi,0xFFFFFFFFFFFFF8(%rbp)
a:8b 45 f8 mov 0xFFFFFFFFFFFFF8(%rbp),%eax
d:03 45 fc添加0xFFFFFFFFFFFFFC(%rbp),%eax
10:c9/Q
11:c3 retq
这适用于未优化的代码编译,而优化版本为:
$ objdump -h -d --section=.text foo3.o
foo3.o: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000004 0000000000000000 0000000000000000 00000040 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
[ ... ]
Disassembly of section .text:
0000000000000000 <foo>:
0: 8d 04 37 lea (%rdi,%rsi,1),%eax
3: c3 retq
$objdump-h-d--section=.text foo3.o
foo3.o:文件格式elf64-x86-64
部分:
Idx名称大小VMA LMA文件关闭Algn
0.文本0000000 4 000000000000000000000000000000000000000000000000 40 2**4
内容、分配、加载、只读、代码
[ ... ]
第节的分解。正文:
0000000000000000 :
0:8d 04 37 lea(%rdi,%rsi,1),%eax
3:c3 retq
注意“大小”从0x12
更改为4
?这就是来自.size
汇编指令的内容
尝试使用内联汇编来提供函数大小/代码位置的“技巧”既不考虑编译器生成的粘合代码(函数入口序言/出口尾声,内联代码生成,…),也不考虑编译器对内联汇编进行重新排序(gcc这样做是臭名昭著的),因此相信这一点通常不是一个好主意。最后,这取决于你到底想做什么
编辑:更多的外部和堆栈溢出参考:
这将是非常脆弱的,更不用说可移植性了。我不同意这个想法中的脆弱性,可能是你用来恢复地址的方式,AFAIK gcc应该有一些嵌入式方法来实现这一点,但我不记得名称。如果你改变优化选项或一些代码,您可以得到与实际情况大不相同的结果。
return\u内置\u return\u地址(0)代码>\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu代码>不幸的是\u内置\u对象\u大小
不起作用(至少对于gcc 4.7),符号/对象的大小可能是ELF符号表的一部分。但要使程序在运行时能够访问它,需要链接器,当然还需要汇编程序的支持。我还没有看到GNU汇编程序支持访问符号大小的任何证据。我相信GNU汇编程序和GNU链接器中根本没有支持。大小可能是符号表的一部分,仅用于调试目的。我不明白:您引用的objdump输出显示的是文本部分的大小,而不是单个函数的大小readelf-s
但是确实显示了您提到的符号的st_size
属性。