Lab3 ARM指令

通過C代碼和反彙編工具研究ARM指令。

教程目標:

  1. 生成了Thumb指令還是ARM指令,如何通過編譯參數改變;
  2. 對於ARM指令,能否產生條件執行的指令;
  3. 設計C的代碼場景,觀察是否產生了寄存器移位尋址;
  4. 設計C的代碼場景,觀察一個複雜的32位數是如何裝載到寄存器的;
  5. 寫一個C的多重函數調用的程序,觀察和分析:
    1. 調用時的返回地址在哪裏?
    2. 傳入的參數在哪裏?
    3. 本地變量的堆棧分配是如何做的?
    4. 寄存器是caller保存還是callee保存?是全體保存還是部分保存?
  6. MLA是帶累加的乘法,嘗試要如何寫C的表達式能編譯得到MLA指令。

教程器材及軟件:

  1. 樹莓派的板子。
  2. SD卡(已經有鏡像刷入)。
  3. 電源線及USB充電器。
  4. U盤或USB硬盤
  5. putty和psftp。
  6. 有DHCP的網線。

步驟:

  1. 首先寫一段簡單的C代碼:
    #include<stdio.h>
    
    int main(int argc,char** argv)
    {
        int a=0x12345678;
        printf("a:%d\n",a);
        return 0;
    }
    
  2. 如果要將其編譯成ARM指令的,那麼,默認就好了。然後,再用objdump出來看看。
    gcc -o 1.o -c 1.c
    objdump -d 1.o
  3. 我們可以看到指令是32位的。
    1.o:     file format elf32-littlearm
    
    
    Disassembly of section .text:
    
    00000000 <main>:
       0:   e92d4800        push    {fp, lr}
       4:   e28db004        add     fp, sp, #4
       8:   e24dd010        sub     sp, sp, #16
       c:   e50b0010        str     r0, [fp, #-16]
      10:   e50b1014        str     r1, [fp, #-20]
      14:   e59f3020        ldr     r3, [pc, #32]   ; 3c <main+0x3c>
      18:   e50b3008        str     r3, [fp, #-8]
      1c:   e59f301c        ldr     r3, [pc, #28]   ; 40 <main+0x40>
      20:   e1a00003        mov     r0, r3
      24:   e51b1008        ldr     r1, [fp, #-8]
      28:   ebfffffe        bl      0 <printf>
      2c:   e3a03000        mov     r3, #0
      30:   e1a00003        mov     r0, r3
      34:   e24bd004        sub     sp, fp, #4
      38:   e8bd8800        pop     {fp, pc}
      3c:   12345678        .word   0x12345678
      40:   00000000        .word   0x00000000
    
  4. 如果要將其編譯成Thumb指令的話,就要像下面這樣子。如果,不加-mfloat-abi=softfp,會報錯。好像和浮點運算VFP 的ABI沒有有關係。
    gcc -o 1.o -c 1.c -mthumb -mfloat-abi=softfp
    objdump -d 1.o
  5. 我們可以看到指令是16位的。
    1.o:     file format elf32-littlearm
    
    
    Disassembly of section .text:
    
    00000000 <main>:
       0:   b580            push    {r7, lr}
       2:   b084            sub     sp, #16
       4:   af00            add     r7, sp, #0
       6:   6078            str     r0, [r7, #4]
       8:   6039            str     r1, [r7, #0]
       a:   4b06            ldr     r3, [pc, #24]   ; (24 <main+0x24>)
       c:   60fb            str     r3, [r7, #12]
       e:   4a06            ldr     r2, [pc, #24]   ; (28 <main+0x28>)
      10:   68fb            ldr     r3, [r7, #12]
      12:   1c10            adds    r0, r2, #0
      14:   1c19            adds    r1, r3, #0
      16:   f7ff fffe       bl      0 <printf>
      1a:   2300            movs    r3, #0
      1c:   1c18            adds    r0, r3, #0
      1e:   46bd            mov     sp, r7
      20:   b004            add     sp, #16
      22:   bd80            pop     {r7, pc}
      24:   12345678        .word   0x12345678
      28:   00000000        .word   0x00000000
    

  6. 再寫一個程序2.c:
  7. int max(int a,int b)
    {
        if(a>b)
            return a;
        else
            return b;
    }
    
  8. 2.o:     file format elf32-littlearm
    
    
    Disassembly of section .text:
    
    00000000 <max>:
       0:   e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
       4:   e28db000        add     fp, sp, #0
       8:   e24dd00c        sub     sp, sp, #12
       c:   e50b0008        str     r0, [fp, #-8]
      10:   e50b100c        str     r1, [fp, #-12]
      14:   e51b2008        ldr     r2, [fp, #-8]
      18:   e51b300c        ldr     r3, [fp, #-12]
      1c:   e1520003        cmp     r2, r3
      20:   da000001        ble     2c <max+0x2c>
      24:   e51b3008        ldr     r3, [fp, #-8]
      28:   ea000000        b       30 <max+0x30>
      2c:   e51b300c        ldr     r3, [fp, #-12]
      30:   e1a00003        mov     r0, r3
      34:   e28bd000        add     sp, fp, #0
      38:   e8bd0800        pop     {fp}
      3c:   e12fff1e        bx      lr
    
    上面有些跳轉指令,但是沒有條件執行指令。
  9. 我們讓gcc對代碼進行優化:
    gcc -o 2.o -c 2.c -O1
  10. 2.o:     file format elf32-littlearm
    
    
    Disassembly of section .text:
    
    00000000 <max>:
       0:   e1510000        cmp     r1, r0
       4:   a1a00001        movge   r0, r1
       8:   b1a00000        movlt   r0, r0
       c:   e12fff1e        bx      lr
    
    代碼就變得非常短了,而且也可以明顯的看到,條件執行指令。

  11. 寫一個簡單的程序3.c:
    int fun(int p[],int index)
    {
        return p[index];
    }
    
  12. gcc -o 3.o -c 3.c -O1
    objdump -d 3.o
    3.o:     file format elf32-littlearm
    
    
    Disassembly of section .text:
    
    00000000 <fun>:
       0:   e7900101        ldr     r0, [r0, r1, lsl #2]
       4:   e12fff1e        bx      lr
    

  13. 再寫一個簡單的程序4.c:
    int fun(void)
    {
        return 0x12345678;
    }
    
  14. gcc -o 4.o -c 4.c -O1
    objdump -d 4.o
    
    4.o:     file format elf32-littlearm
    
    
    Disassembly of section .text:
    
    00000000 <fun>:
       0:   e59f0000        ldr     r0, [pc]        ; 8 <fun+0x8>
       4:   e12fff1e        bx      lr
       8:   12345678        .word   0x12345678
    
    它的做法很簡單,將32位數放在指令的附近,然後load一下就可以了。加上有cache的存在,這樣的方案可能比將數字拆分成16位再load進來要快,而且它只執行了1條指令。實驗了一下,發現load64位數,它也是將數放在指令附近然後load兩次。

  15. 再寫一個不簡單的程序5.c(gcc的優化能力實在是太強了,要寫一個程序就看出所有的這些,真是不容易啊。):
  16. #include<stdio.h>
    int bb(int a,int b,int c,int d,int e,int f)
    {
        printf("Hello world!\n");
        return a*b*c*d*e*f;
    }
    int cc(int a,int b,int c,int d,int e,int f,int g,int h,int i,int j,int k)
    {
        int t1=a+b;
        int t2=c+d;
    
        int t3=e+f;
        int t4=g+h;
        int t5=i+j;
    
        bb(1,2,3,4,5,6);
        int t6=t1*t2;
        int t7=t3*t4;
        int t8=t6-t7;
        int t9=t8*t5*k;
    
        return t9;
        //return a*b*c*d*e*f*g*h*i*j*k;
    }
    
  17. gcc -o 5.o -c 5.c -O1
    objdump -d 5.o
    5_1.o:     file format elf32-littlearm
    
    
    Disassembly of section .text:
    
    00000000 <bb>:
       0:   e92d40f8        push    {r3, r4, r5, r6, r7, lr}
       4:   e1a04000        mov     r4, r0//r0-r3會被用作作爲傳參數的寄存器,如果不夠就會用堆棧裏的。
       8:   e1a05001        mov     r5, r1
       c:   e1a06002        mov     r6, r2
      10:   e1a07003        mov     r7, r3
      14:   e59f0020        ldr     r0, [pc, #32]   ; 3c <bb+0x3c>
      18:   ebfffffe        bl      0 <puts>
      1c:   e0040495        mul     r4, r5, r4
      20:   e0060496        mul     r6, r6, r4
      24:   e0070697        mul     r7, r7, r6
      28:   e59d6018        ldr     r6, [sp, #24]
      2c:   e0070796        mul     r7, r6, r7
      30:   e59d001c        ldr     r0, [sp, #28]
      34:   e0000790        mul     r0, r0, r7
      38:   e8bd80f8        pop     {r3, r4, r5, r6, r7, pc}
      3c:   00000000        .word   0x00000000
    
    00000040 <cc>:
      40:   e92d41f0        push    {r4, r5, r6, r7, r8, lr}//這個說明caller save r0-r3,lr,callee save r4-r8,
    //另外,返回地址就在lr上,如果該函數要表用別的函數的話,lr會被推入堆棧。
      44:   e24dd008        sub     sp, sp, #8
      48:   e0804001        add     r4, r0, r1
      4c:   e0825003        add     r5, r2, r3
      50:   e59d3024        ldr     r3, [sp, #36]   ; 0x24
      54:   e59d7020        ldr     r7, [sp, #32]
      58:   e0877003        add     r7, r7, r3
      5c:   e59d302c        ldr     r3, [sp, #44]   ; 0x2c
      60:   e59d6028        ldr     r6, [sp, #40]   ; 0x28
      64:   e0866003        add     r6, r6, r3
      68:   e59d3034        ldr     r3, [sp, #52]   ; 0x34
      6c:   e59d8030        ldr     r8, [sp, #48]   ; 0x30
      70:   e0888003        add     r8, r8, r3
      74:   e3a03005        mov     r3, #5
      78:   e58d3000        str     r3, [sp]
      7c:   e3a03006        mov     r3, #6
      80:   e58d3004        str     r3, [sp, #4]
      84:   e3a00001        mov     r0, #1
      88:   e3a01002        mov     r1, #2
      8c:   e3a02003        mov     r2, #3
      90:   e3a03004        mov     r3, #4
      94:   ebfffffe        bl      0 <bb>
      98:   e0040495        mul     r4, r5, r4
      9c:   e0060796        mul     r6, r6, r7
      a0:   e0664004        rsb     r4, r6, r4
      a4:   e0080498        mul     r8, r8, r4
      a8:   e59d0038        ldr     r0, [sp, #56]   ; 0x38
      ac:   e0000890        mul     r0, r0, r8
      b0:   e28dd008        add     sp, sp, #8
      b4:   e8bd81f0        pop     {r4, r5, r6, r7, r8, pc}
    //至於本地變量的存放問題,因爲,開啓了優化,本地變量都放在寄存器裏面了。如果,不要優化,就可以看到它是先用低地址,再用高地址。
    

  18. 再寫一個簡單的程序6.c:
  19. int fun(int a,int b,int c)
    {
        return a*b+c;
    }
    
  20. gcc -o 6.o -c 6.c -O1
    objdump -d 6.o
    
    6.o:     file format elf32-littlearm
    
    
    Disassembly of section .text:
    
    00000000 <fun>:
       0:   e0202091        mla     r0, r1, r0, r2
       4:   e12fff1e        bx      lr
    

備註:

此爲浙江大學計算機學院嵌入式系統實驗報告。
發佈了35 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章