ARM代码底层剖析——机器码,汇编,C

时间:2022-05-14 00:55:17

从ARM9裸机程序led程序讲起,一个例子是汇编调用了C函数,这涉及函数的返回以及汇编和C的数据空间的分布情况,比较复杂一些。所以先从一个纯汇编语言例子开始。

软件平台:Fedora9 交叉编译环境4.4.3;开发板为友善之臂的2440。

先看汇编程序:

.text

.global _start

_start:    

           LDR     R0,=0x56000010      @ R0设为GPBCON寄存器。此寄存器

                                        @ 用于选择端口B各引脚的功能:

                                       @ 是输出、是输入、还是其他

            MOV     R1,#0x00000400       

            STR     R1,[R0]             @ 设置GPB5为输出口, 位[10:9]=0b01

            LDR     R0,=0x56000014      @ R0设为GPBDAT寄存器。此寄存器

                                        @ 用于读/写端口B各引脚的数据

            MOV     R1,#0x00000000      @ 此值改为0x00000020,

                                        @ 可让LED1熄灭

            STR     R1,[R0]             @ GPB5输出0,LED1点亮

MAIN_LOOP:

            B       MAIN_LOOP

 

对于上述的程序的各个寄存器以及对寄存器操作参看注释,重点是对其汇编代码对应的机器码的分析。

使用arm-linux-objdump 命令查看下载进开发板中的二进制代码对应的格式。

arm-linux-objdump -D -b binary -m armled_on.bin

led_on.bin:     file format binary

 

 

Disassembly of section .data:

 

00000000 <.data>:

   0:        e59f0014         ldr    r0, [pc, #20]     ; 0x1c

   4:        e3a01b01        mov r1, #1024 ; 0x400

   8:        e5801000        str    r1, [r0]

   c:        e59f000c         ldr    r0, [pc, #12]     ; 0x20

  10:        e3a01000        mov r1, #0

  14:        e5801000        str    r1, [r0]

  18:        eafffffe    b       0x18

  1c:        56000010        undefined instruction 0x56000010

  20:        56000014        undefined instruction 0x56000014


 左边的一列0,4,8,…,20是十六进制数,表示的是地址偏移量,可以使用arm-linux-ld命令来改变,在使用SDRAM时,会将程序拷贝进SDRAM运行,但是s3c2440能接SDRAM的只有bank六和七,其地址其实就不是0x0了,这是arm-linux-ld就比较有用了。

arm-linux-ld -Ttext 0x0000000 -g led_on.o-o led_on_elf


 e59f0014对应的就是机器码了,但是需要注意的是前面提到代码和数据段的问题,数据段是接着代码进行存放的,所以后面的显示undefined instruction其实不是指令,对地址,细心的话能够发现,56000010其实是GPBCON寄存器,二56000014则是对应的GPBDAT数据寄存器。

   18:        eafffffe    b       0x18 这个意识是跳转到0x18地址处执行,而这句前面的地址就是0x18,所以对应的汇编代码是:

MAIN_LOOP:

           B       MAIN_LOOP

 

ARM的跳转作指令编码格式如下:

ARM代码底层剖析——机器码,汇编,C

将eafffffe按上述格式展开:

1110_101_0_111111111111111111111110;

可以发现其27-25确实为101。其24位的L,表示是否使用link(R14)寄存器拷贝pc的内容,这个是为了程序返回时的准备,注意该拷贝过程是由硬件自动完成的,该位置一,表示复制,为0表示不复制,该位一个典型应用是中断返回地址的copy,那么31-28的1110对应的意义为cond,不仅仅跳转指令有cond,数据操作类指令机器码中均存在该位,并且意义是相同的。其每位对应的意义参看如下。其实就是无条件执行的意思。

ARM代码底层剖析——机器码,汇编,C

 ARM的数据操作指令编码格式如下:

ARM代码底层剖析——机器码,汇编,C

其它的指令参看上述就能明白,但是   0:        e59f0014         ldr    r0, [pc, #20]     ; 0x1c

可能不太明白,

需要说明的是ldr指令有两种用法,其中一种表示的是伪指令,而不是ARM质量。

 ARM指令集中,LDR通常都是作加载指令的,但是它也可以作伪指令。

 (1)LDR   r0,=name,像这种带等号的是伪指令,而不是ARM指令,LDR 伪指令用于加载立即数或一个地址值到指定寄存器.

 *如果name是立即数的话:LDRR0,=0X123;//将0X123存入R0

 *如果name是个标识符:LDRR0,=NAME;//将NAME的地址存入R0

         在SDRAM中跑程序时,使用的跳转指令的地址涉及的偏移和绝对,偏移指的是相对当前指令而言,绝对指的是绝对地址,如0x30000000,该地址可以使用上述的arm-linux-ld制定。可以使用objdump指令查看。

下面是C版本的,前面也同样期初是汇编,然后使用汇编调用了C函数的。

 

   0:        e59f0010         ldr    r0, [pc, #16]     ; 0x18

   4:        e3a01000        mov r1, #0

   8:        e5801000        str    r1, [r0]

   c:        e3a0da01        mov sp, #4096          ; 0x1000

  10:        eb000001        bl      0x1c

  14:        eafffffe    b       0x14

  18:        56000010        undefined instruction 0x56000010

  1c:        e52db004        push          {fp}            ; (str fp, [sp, #-4]!)

  20:        e28db000        add  fp, sp, #0

  24:        e59f3024         ldr    r3, [pc, #36]     ; 0x50

  28:        e3a02b01        mov r2, #1024 ; 0x400

  2c:        e5832000        str    r2, [r3]

  30:        e59f301c         ldr    r3, [pc, #28]     ; 0x54

  34:        e3a02000        mov r2, #0

  38:        e5832000        str    r2, [r3]

  3c:        e3a03000        mov r3, #0

  40:        e1a00003        mov r0, r3

  44:        e28bd000        add  sp, fp, #0

  48:        e8bd0800        pop  {fp}

  4c:        e12fff1e bx     lr

  50:        56000010        undefined instruction 0x56000010

  54:        56000014        undefined instruction 0x56000014

 

第一列0-18对应于如下汇编代码,其中18地址处是WATCHDOG的地址,及存的是数据。

.text

.global _start

_start:

  ldr     r0, =0x56000010     @ WATCHDOG寄存器地址

  mov     r1, #0x0                    

  str   r1, [r0]              @ 写入0,禁止WATCHDOG,否则CPU会不断重启

   ldr    sp, =1024*4    @ 设置堆栈,注意:不能大于4k,因为现在可用的内存只有4K

                      @ nand flash中的代码在复位后会移到内部ram中,此ram只有4K

  bl      main                @ 调用C程序中的main函数

halt_loop:

  b       halt_loop

 

剩下的对应于如下C代码:

#define GPBCON     (*(volatile unsigned long *)0x56000010)

#define GPBDAT     (*(volatile unsigned long *)0x56000014)

 

int main()

{

    GPBCON =0x00000400;    // 设置GPB5为输出口, 位[11:10]=0b01

    GPBDAT =0x00000000;    // GPB5输出0,LED1点亮

 

    return 0;

}

在反汇编的中的bl       0x1c,即是跳转到C执行,但是在C真正的代码运行之前和结束时都对相应的寄存器进行了适当的保护和返回。注意观察这里的bl对应机器码的link位的设置。这些是编译程序生成的,体现在机器码中。