我想了解C调用约定。为此,我编写了以下代码:
#include <stdio.h>
#include <stdlib.h>
struct tstStruct
{
void *sp;
int k;
};
void my_func(struct tstStruct*);
typedef struct tstStruct strc;
int main()
{
char a;
a = 'b';
strc* t1 = (strc*) malloc(sizeof(strc));
t1 -> sp = &a;
t1 -> k = 40;
my_func(t1);
return 0;
}
void my_func(strc* s1)
{
void* n = s1 -> sp + 121;
int d = s1 -> k + 323;
}
然后,我将GCC与以下命令结合使用:
gcc -S test3.c
并提出了它的组装。我不会显示完整的代码,而是粘贴函数my_func的代码。就是这个:
my_func:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -24(%rbp)
movq -24(%rbp), %rax
movq (%rax), %rax
addq $121, %rax
movq %rax, -16(%rbp)
movq -24(%rbp), %rax
movl 8(%rax), %eax
addl $323, %eax
movl %eax, -4(%rbp)
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
据我了解,这是发生了什么:
首先,将调用者的基本指针压入堆栈,并使其堆栈指针成为新的基本指针,以为新功能设置堆栈。但是剩下的我不明白。据我所知,参数(或指向该参数的指针)存储在堆栈中。如果是这样,第二条指令的目的是什么,
movq -24(%rbp), %rax
此处,%rax寄存器的内容被移到距离寄存器%rbp中的地址24个字节的地址。但是%rax是什么????最初什么都没有存储在那里???我觉得我很困惑。请帮助了解此功能的工作原理。
提前致谢!
最佳答案
您将AT&T语法与Intel语法混淆了。
movq -24(%rbp),%rax
用英特尔的语法是
mov rax,[rbp-24]
因此,它将rbp
寻址的数据移动到rax
,反之亦然。在AT&T语法中,操作数的顺序是src,dest,而在Intel语法中,操作数的顺序是dest,src。
然后,为了摆脱GAS指令以使反汇编更容易阅读,我将gcc的代码简单地与gcc test3.c
组装在一起,并将其与ndisasm -b 64 a.out
一起反汇编。请注意,以下由NDISASM生成的my_func
函数的反汇编是使用Intel语法的:
000005EF 55推Rbp
000005F0 4889E5 mov rbp,rsp;创建堆栈框架。
000005F3 48897DE8 mov [rbp-0x18],rdi; s1转换为局部变量。
000005F7 488B45E8 mov rax,[rbp-0x18]; rax = s1(它是一个指针)
000005FB 488B00 mov rax,[rax];取消引用rax,将其存储到rax中。
000005FE 4883C079添加rax,字节+ 0x79; rax = rax + 121
00000602 488945F8 mov [rbp-0x8],rax;无效* n = s1-> sp + 121
00000606 488B45E8 mov rax,[rbp-0x18]; rax =指向s1的指针
0000060A 8B4008 mov eax,[rax + 0x8];取消引用rax + 8,存储到eax中。
0000060D 0543010000添加eax,0x143; eax = eax + 323
00000612 8945F4 mov [rbp-0xc],eax;整数d = s1-> k + 323
00000615 5D流行RBP
00000616 C3退出
有关Linux x86-64调用约定(系统V ABI)的信息,请参阅What are the calling conventions for UNIX & Linux system calls on x86-64的答案。
关于c - 了解C反汇编的调用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16088946/