x86_64匯編基礎


1、AT&T格式匯編

在 Unix 和 Linux 系統中,更多采用的還是 AT&T 格式,兩者在語法格式上有着很大的不同:

  1. 在 AT&T 匯編格式中,寄存器名要加上 '%' 作為前綴;而在 Intel 匯編格式中,寄存器名不需要加前綴。例如:

    AT&T 格式 Intel 格式
    pushl %eax push eax
  2. 在 AT&T 匯編格式中,用 '$' 前綴表示一個立即操作數;而在 Intel 匯編格式中,立即數的表示不用帶任何前綴。例如:

    AT&T 格式 Intel 格式
    pushl $1 push 1
  3. AT&T 和 Intel 格式中的源操作數和目標操作數的位置正好相反。在 Intel 匯編格式中,目標操作數在源操作數的左邊;而在 AT&T 匯編格式中,目標操作數在源操作數的右邊。例如:

    AT&T 格式 Intel 格式
    addl $1, %eax add eax, 1
  4. 在 AT&T 匯編格式中,操作數的字長由操作符的最后一個字母決定,后綴'b'、'w'、'l'分別表示操作數為字節(byte,8 比特)、字(word,16 比特)和長字(long,32比特);而在 Intel 匯編格式中,操作數的字長是用 "byte ptr" 和 "word ptr" 等前綴來表示的。例如:

    AT&T 格式 Intel 格式
    movb val, %al mov al, byte ptr val
  5. 在 AT&T 匯編格式中,絕對轉移和調用指令(jump/call)的操作數前要加上'*'作為前綴,而在 Intel 格式中則不需要。
  6. 遠程轉移指令和遠程子調用指令的操作碼,在 AT&T 匯編格式中為 "ljump" 和 "lcall",而在 Intel 匯編格式中則為 "jmp far" 和 "call far",即:

    AT&T 格式 Intel 格式
    ljump $section, $offset jmp far section:offset
    lcall $section, $offset call far section:offset

    與之相應的遠程返回指令則為:

    AT&T 格式 Intel 格式
    lret $stack_adjust ret far stack_adjust
  7. 在 AT&T 匯編格式中,內存操作數的尋址方式是

    section:disp(base, index, scale)

    而在 Intel 匯編格式中,內存操作數的尋址方式為:

    section:[base + index*scale + disp]

    由於 Linux 工作在保護模式下,用的是 32 位線性地址,所以在計算地址時不用考慮段基址和偏移量,而是采用如下的地址計算方法:

    disp + base + index * scale

    下面是一些內存操作數的例子:

    AT&T 格式 Intel 格式
    movl -4(%ebp), %eax mov eax, [ebp - 4]
    movl array(, %eax, 4), %eax mov eax, [eax*4 + array]
    movw array(%ebx, %eax, 4), %cx mov cx, [ebx + 4*eax + array]
    movb $4, %fs:(%eax) mov fs:eax, 4
 

2、寄存器

X84中原有8個32位通用寄存器%eax,%ebx,%ecx,%edx,%esi,%edi,%ebp,%esp, 在X86_64中

分別被擴展為64位,並且多了8個寄存器。因此X86_64的寄存器如下:

  • rax, eax, ax, ah, al;
  • rbx, ebx, bx, bh, bl;
  • rcx, ecx, cx, ch, cl;
  • rdx, edx, dx, dh, dl;
  • rsi, esi, si;
  • rdi, edi, di;
  • rbp, ebp;
  • rsp, esp;
  • r8-r15;

GCC中對這些寄存器的調用規則如下:

  • %rax 作為函數返回值使用。
  • %rsp 棧指針寄存器,指向棧頂
  • %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函數參數,依次對應第1參數,第2參數。。。
  • %rbx,%rbp,%r12,%r13,%14,%15 用作數據存儲,遵循被調用者使用規則,簡單說就是隨便用,調用子函數之前要備份它,以防他被修改
  • %r10,%r11 用作數據存儲,遵循調用者使用規則,簡單說就是使用之前要先保存原值

3、棧幀

以一個簡單的add函數為例:

 1 #include <stdio.h>
 2 
 3 int add(int a, int b)
 4 {
 5     return a + b;
 6 }
 7 
 8 int main(void)
 9 {
10     int c, a = 1, b = 2;
11 
12     c = add(a, b); 
13 
14     return c;
15 }

編譯並反匯編,

1 scripts$ gcc -g test.c -o test
2 scripts$ objdump -S -d test > test.s
3 scripts$ vim test.s
 1 0000000000400474 <add>:
 2 #include <stdio.h>
 3 
 4 int add(int a, int b)
 5 {
 6   400474:   55                      push   %rbp
 7   400475:   48 89 e5                mov    %rsp,%rbp
 8   400478:   89 7d fc                mov    %edi,-0x4(%rbp)
 9   40047b:   89 75 f8                mov    %esi,-0x8(%rbp)
10     return a + b;
11   40047e:   8b 45 f8                mov    -0x8(%rbp),%eax
12   400481:   8b 55 fc                mov    -0x4(%rbp),%edx
13   400484:   8d 04 02                lea    (%rdx,%rax,1),%eax
14 }
15   400487:   c9                      leaveq 
16   400488:   c3                      retq   
17 
18 0000000000400489 <main>:
19 
20 int main(void)
21 {
22   400489:   55                      push   %rbp
23   40048a:   48 89 e5                mov    %rsp,%rbp
24   40048d:   48 83 ec 10             sub    $0x10,%rsp
25     int c, a = 1, b = 2;
26   400491:   c7 45 f8 01 00 00 00    movl   $0x1,-0x8(%rbp)
27   400498:   c7 45 fc 02 00 00 00    movl   $0x2,-0x4(%rbp)
28 
29     c = add(a, b); 
30   40049f:   8b 55 fc                mov    -0x4(%rbp),%edx
31   4004a2:   8b 45 f8                mov    -0x8(%rbp),%eax
32   4004a5:   89 d6                   mov    %edx,%esi
33   4004a7:   89 c7                   mov    %eax,%edi
34   4004a9:   e8 c6 ff ff ff          callq  400474 <add>
35   4004ae:   89 45 f4                mov    %eax,-0xc(%rbp)
36 
37     return c;
38   4004b1:   8b 45 f4                mov    -0xc(%rbp),%eax
39 }

從32、33行可知,edi<- a,  esi<- b, 然后 call add,此時main函數中的棧幀結構如下(某個寄存器為x,則xH表示高4位):

然后跳入add中執行:

從4-12行完畢,eax存儲b, edx存儲a, 此時整體的棧幀結構:

之后是指令:

lea    (%rdx,%rax,1),%eax

lea是load effective address,加載有效地址,類似於C語言中的“&”取地址符的作用,本例中是將 rdx + rax * 1 ==> eax, 即 eax = a + b,

然后rax自動作為返回值。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM