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

从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的跳转作指令编码格式如下:


将eafffffe按上述格式展开:

1110_101_0_111111111111111111111110;

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

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


其它的指令参看上述就能明白,但是   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位的设置。这些是编译程序生成的,体现在机器码中。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
ARM汇编指令转换为机器码是将人类可读的汇编指令翻译成机器可执行的二进制码的过程。由于机器码是计算机硬件能够直接理解的指令形式,所以在执行指令时,计算机需要将汇编指令转换为机器码。下面是一种简单的方法将ARM汇编指令转换为机器码。 首先,需要根据ARM指令的格式和规范理解每个指令的含义和操作码。ARM指令由32位组成,其中包含操作码、操作数和其他参数。 其次,根据具体指令的格式,将每个部分转换为对应的二进制表示形式。例如,操作码通常表示为特定的二进制位模式。 然后,根据指令的不同寻找对应的编码方式。不同的指令有不同的编码方式,例如立即数、寄存器和内存地址等等。根据具体的指令要求和参数,转换相应的部分。 最后,将所有部分组合成一个32位的二进制序列,这就是转换后的机器码。转换后的机器码可以被计算机硬件直接识别和执行。 需要注意的是,ARM汇编指令转换为机器码的过程需要具备一定的知识和经验。对于复杂的指令或有特殊要求的指令,可能需要查阅相关的ARM体系结构手册或其他资料来获得准确的转换方式。此外,在转换过程中还应该注意指令的字节顺序(即大端序或小端序)以及特殊的对齐要求。 总之,将ARM汇编指令转换为机器码是一项需要仔细思考和操作的任务。掌握适当的知识和技巧可以帮助我们完成这个过程,以便更好地理解和使用ARM指令集。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shichaog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值