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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章