gcc x64堆栈操作

时间:2011-06-16 07:47:23

标签: linux gcc stack

我试着理解gcc x64组织堆栈的方式,一个小程序生成这个asm

(gdb) disassemble *main
Dump of assembler code for function main:
0x0000000000400534 <main+0>:    push   rbp
0x0000000000400535 <main+1>:    mov    rbp,rsp
0x0000000000400538 <main+4>:    sub    rsp,0x30
0x000000000040053c <main+8>:    mov    DWORD PTR [rbp-0x14],edi
0x000000000040053f <main+11>:   mov    QWORD PTR [rbp-0x20],rsi
0x0000000000400543 <main+15>:   mov    DWORD PTR [rsp],0x7
0x000000000040054a <main+22>:   mov    r9d,0x6
0x0000000000400550 <main+28>:   mov    r8d,0x5
0x0000000000400556 <main+34>:   mov    ecx,0x4
0x000000000040055b <main+39>:   mov    edx,0x3
0x0000000000400560 <main+44>:   mov    esi,0x2
0x0000000000400565 <main+49>:   mov    edi,0x1
0x000000000040056a <main+54>:   call   0x4004c7 <addAll>
0x000000000040056f <main+59>:   mov    DWORD PTR [rbp-0x4],eax
0x0000000000400572 <main+62>:   mov    esi,DWORD PTR [rbp-0x4]
0x0000000000400575 <main+65>:   mov    edi,0x400688
0x000000000040057a <main+70>:   mov    eax,0x0
0x000000000040057f <main+75>:   call   0x400398 <printf@plt>
0x0000000000400584 <main+80>:   mov    eax,0x0
0x0000000000400589 <main+85>:   leave
0x000000000040058a <main+86>:   ret
  1. 为什么保留最多0x30字节只是为了保存edirsi
  2. 根据ABI
  3. 的要求,我看不到edirsi的任何恢复值
  4. edirsi保存在delta 0x20 - 0x14 = 0xC的位置,而不是连续区域,是否有意义?
  5. 以下是源代码

    int mix(int a,int b,int c,int d,int e,int f, int g){
        return a | b | c | d | e | f |g;
    }
    int addAll(int a,int b,int c,int d,int e,int f, int g){
        return a+b+c+d+e+f+g+mix(a,b,c,d,e,f,g);
    }
    int main(int argc,char **argv){
        int total;
        total = addAll(1,2,3,4,5,6,7);
        printf("result is %d\n",total);
        return 0;
    }
    

    修改 似乎堆栈已经存储了对addAlltotal的esi,rdi,第7个参数调用,它应该占用4x8 = 32(0x20)字节,由于某些原因它会向上舍入到0x30。

2 个答案:

答案 0 :(得分:2)

  1. 我不知道您的原始代码,但本地也存储在堆栈中,当您有一些局部变量时,空间也被“分配”。同样出于对齐的原因,他可以“舍入”到16的下一个倍数。我猜你有一个本地用于将你的addAll的结果传递给printf,并存储在rbp-04。

  2. 我刚看了你的链接ABI - 它说被调用者必须恢复rdi和rsi?它已在第15页说明,脚注:

      

    请注意,与Intel386 ABI相比,%rdi和%rsi属于被调用函数,而不是   来电者。

    Afaik用于将第一个参数传递给被调用者。

  3. 0xC是12.这也来自对齐,你可以看到,他只需要存储edi而不是rdi,为了对齐目的我假设他将它对齐在4字节边框上,而si是rsi,这是64位并在8字节边界上对齐。

答案 1 :(得分:1)

2:ABI明确表示rdi / rsi不需要被被调用的函数保存;见第15页和脚注5。

1和3:不确定;可能是堆栈对齐问题。