mil*_*bos -2 c x86 assembly 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);
}
Run Code Online (Sandbox Code Playgroud)
编译成这样:
.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
Run Code Online (Sandbox Code Playgroud)
stdlib 中的每个函数都类似于printf或来自 GOT(即寄存器保存 GOT 的地址)。但不是,它就像“汇编内联指令”而不是常规调用地址。那么它甚至是一个符号吗?如果是这样,为什么它不作为 的论据?是否在 GOT 表中?如果是这样,从 GOT 到该符号的偏移量是多少?putscall%ripmemcpymemcpycallmemcpy
所以首先,你有一个错误:
$ cc -O2 -S test.c
test.c: In function ‘add’:
test.c:7:12: warning: function returns address of local variable
Run Code Online (Sandbox Code Playgroud)
返回局部变量的地址具有未定义的行为,当且仅当调用者使用该值时;这就是为什么你的编译器生成返回空指针的代码,如果使用它会使程序崩溃,但否则无害。事实上,我的 GCC 副本只为add:
add:
xorl %eax, %eax
ret
Run Code Online (Sandbox Code Playgroud)
因为对返回值的处理使其他操作add成为死代码。
(“仅在使用时”限制也是我的编译器生成警告而不是硬错误的原因。)
现在,如果我修改您的程序以具有明确定义的行为,例如
#include <stdio.h>
#include <string.h>
void add(int *sum, int a, int b)
{
int result = a+b;
memcpy(sum, &result, sizeof(int));
}
int main(void)
{
int a;
add(&a, 1, 2);
printf("%i\n",a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
然后我确实看到了其中memcpy调用已被内联代码替换的汇编代码:
add:
addl %edx, %esi
movl %esi, (%rdi)
ret
Run Code Online (Sandbox Code Playgroud)
这是许多现代 C 编译器的一个特性:它们知道某些 C 库函数的作用,并且可以在有意义的时候内联它们。(您可以看到,在这种情况下,生成的代码比实际调用 时更小、更快memcpy。)
GCC 允许我使用命令行选项关闭此功能:
$ gcc -O2 -ffreestanding test.c
$ sed -ne '/^add:/,/cfi_endproc/{; /^\.LF[BE]/d; /\.cfi_/d; p; }' test.s
add:
subq $24, %rsp
addl %edx, %esi
movl $4, %edx
movl %esi, 12(%rsp)
leaq 12(%rsp), %rsi
call memcpy@PLT
addq $24, %rsp
ret
Run Code Online (Sandbox Code Playgroud)
在此模式下,对memcpyinadd的调用与对printfin的调用相同main。您的编译器可能有类似的选项。