memcpy是否为带符号函数?编译无call指令及GOT相关疑问
原始C代码
#include <stdio.h> #include <string.h> int *add(int a, int b){ int ar[1]; int result = a+b; memcpy(ar, &result, sizeof(int)); return ar; } int main(){ int a = add(1,2)[0]; printf("%i\n",a); }
编译后的汇编代码
.text .globl add .type add, @function add: pushq %rbp # movq %rsp, %rbp #, movl %edi, -20(%rbp) # a, a movl %esi, -24(%rbp) # b, b # a.c:5: int result = a+b; movl -20(%rbp), %edx # a, tmp91 movl -24(%rbp), %eax # b, tmp92 addl %edx, %eax # tmp91, _1 # a.c:5: int result = a+b; movl %eax, -8(%rbp) # _1, result # a.c:6: memcpy(ar, &result, sizeof(int)); ---I SEE NO CALL INSTRUCTION--- movl -8(%rbp), %eax # MEM[(char * {ref-all})&result], _6 movl %eax, -4(%rbp) # _6, MEM[(char * {ref-all})&ar] # a.c:7: return ar; movl $0, %eax #--THE FUNCTION SHOULD RETURN ADDRESS OF ARRAY, NOT 0. OTHERWISE command terminated # lea -4(%rbp), %rax #--ONLY THIS IS CORRECT, NOT `0` # a.c:8: } popq %rbp # ret .size add, .-add .section .rodata .LC0: .string "%i\n" .text .globl main .type main, @function main: pushq %rbp # movq %rsp, %rbp #, subq $16, %rsp #, # a.c:11: int a = add(1,2)[0]; movl $2, %esi #, movl $1, %edi #, call add # # a.c:11: int a = add(1,2)[0]; movl (%rax), %eax # *_1, tmp90 movl %eax, -4(%rbp) # tmp90, a # a.c:12: printf("%i\n",a); movl -4(%rbp), %eax # a, tmp91 movl %eax, %esi # tmp91, leaq .LC0(%rip), %rdi #, movl $0, %eax #, call printf@PLT # movl $0, %eax #, _6 # a.c:13: } leave ret .size main, .-main .ident "GCC: (Debian 8.3.0-6) 8.3.0" .section .note.GNU-stack,"",@progbits
技术疑问解答
1. memcpy是否确实是一个带符号的函数?
没错,memcpy绝对是一个带符号的标准库函数。它是C标准库公开导出的函数,你可以用nm工具查看系统的libc.so文件,能找到标记为T memcpy的符号(T表示该符号位于代码段,是一个函数符号)。你当前没看到对应的call指令,和它是不是符号无关,是编译器优化导致的。
2. 若memcpy是符号,为何未作为call指令的操作数?
这是GCC的内置函数优化在起作用。当编译器能在编译期确定memcpy的操作长度是一个小常量(这里你复制的是sizeof(int),也就是4个字节,编译期就能明确),而且操作逻辑简单时,它会直接生成等价的汇编指令来完成复制——就像你看到的movl -8(%rbp), %eax和movl %eax, -4(%rbp)这两条指令,直接把result的值复制到ar数组里,完全不需要调用memcpy函数。
这种优化能省去函数调用的开销(比如栈帧保存、跳转、返回这些步骤),效率更高。如果你想强制编译器生成call memcpy的指令,只需要用-fno-builtin-memcpy参数编译代码,这时候你就能看到和printf类似的call memcpy@PLT指令了。
3. memcpy是否存在于GOT表中?若存在,其相对GOT的偏移量是多少?
在你当前的编译结果里,因为编译器把memcpy内联成了直接的汇编指令,根本没有生成对memcpy函数的外部调用,所以GOT表中不会有memcpy的条目。但如果你关闭内置优化(用-fno-builtin-memcpy编译),生成的可执行文件就会在GOT表中添加memcpy的条目。
至于具体的偏移量,这个没有固定值——它取决于链接时符号的排列顺序、可执行文件依赖的其他动态符号数量等因素。你可以用readelf -r或者objdump -R命令查看生成的可执行文件,就能找到memcpy对应的GOT偏移信息。
内容的提问来源于stack exchange,提问作者milanHrabos

