ARM中的汇编和机器码

我们以S3C2440的GPIO汇编代码为例:

.text
.global _start
_start:
//把0x100写到地址0x56000050上,配置gpf4为输出引脚,
    ldr r1, =0x56000050
	ldr r0,=0x100
	str r0,[r1]
//把0x00写到地址0x56000054上,设置gpf4输出低电平, 
    ldr r1, =0x56000054
	ldr r0,=0x00
	str r0,[r1]
	
	//死循环
halt:
    b halt

在上面的代码中,我们使用了伪指令ldr把值存放到r0,r1寄存器里面,所谓伪指令就是假的指令,它并不是真正的ARM指令,我们通过对elf文件进行反汇编看一下伪指令ldr对应哪些真正的汇编指令,我们修改下我们之前用的makefile文件对elf文件进行反汇编,修改后的makefile内容如下:

all:
	arm-linux-gcc -c -o led_on.o led_on.S
	arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
	arm-linux-objcopy -O binary -S  led_on.elf led_on.bin
	arm-linux-objdump -D led_on.elf  > led_on.dis
clean:
	rm *.bin  *.o  *.elf

反汇编之后得到的dis文件如下:


led_on.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:
   0:	e59f1014 	ldr	r1, [pc, #20]	; 1c <.text+0x1c>
   4:	e3a00c01 	mov	r0, #256	; 0x100
   8:	e5810000 	str	r0, [r1]
   c:	e59f100c 	ldr	r1, [pc, #12]	; 20 <.text+0x20>
  10:	e3a00000 	mov	r0, #0	; 0x0
  14:	e5810000 	str	r0, [r1]

00000018 <halt>:
  18:	eafffffe 	b	18 <halt>
  1c:	56000050 	undefined
  20:	56000054 	undefined

ldr r1, =0x56000050被转换成了ldr    r1, [pc, #20],从内存中读取值,把1c地址对应的值56000050存放到r1里面。

ldr r0,=0x100 直接被转换成了mov指令。

ldr r1, =0x56000054被转换成了ldr    r1, [pc, #12]    ; 20 <.text+0x20>,把20地址对应的值56000054存放到r1里面。

上面最左边的0 4 8这一列代表地址,第二列代表机器码,第三列代表汇编码。

我们之前说s3c2440里面有cpu,cpu里面一共更有r0-r15一共16个寄存器,这里我们使用pc值表示寄存器。我们看如下表格

左边是R0-R15,右边a1,a2....是它的别名。

pc是指program counter程序计数器,当你把一个地址写到PC寄存器的时候,程序就会跳到那个地址去,pc的值是当前指令的地址加上8,为什么要加上8,是因为ARM系统里面CPU是以流水线的方式去执行的,流水线只是当前执行地址A的指令,那么已经在对地址A+4的指令进行译码,已经在读取地址A+8的指令,这个A+8就是PC的值。 

扫描二维码关注公众号,回复: 12892797 查看本文章

lr是link register,用来保存返回地址,我们使用函数调用的时候,执行完这个函数要跳回原来的地方,这个原来的地方就保存在这个lr寄存器里面。

sp是stack pointer,栈指针。

我们再看前面反汇编生成的代码,第一行, 

0:    e59f1014     ldr    r1, [pc, #20]    ; 1c <.text+0x1c>

r1等于PC+20,当前指令是0,PC 是当前指令的地址加上8也就是8,那么r1=8+20=28=0x1c,然后去0x1c这个地址去读取内存的值0x56000050写入到r1里面。

另外我们看到反汇编生成的文件的第二列是机器码,也就是bin文件里面的内容,但是我们用notpad++打开led_on.bin之后发现并不是e5 9f 10 14这种,这是因为notpad++这个软件的插件有问题,我们用ultraedit软件打开bin文件之后就好了。

下面我们修改程序,点亮LED2,

方法1:修改之前的汇编代码点亮LED2

我们打开芯片手册,搜索GPFCON,发现只要把0x400写到GPFCON寄存器就可以把gpf5设置为输出引脚了,

.text
.global _start
_start:
//把0x400写到地址0x56000050上,配置gpf5为输出引脚,
    ldr r1, =0x56000050
	ldr r0,=0x400
	str r0,[r1]
//把0x00写到地址0x56000054上,设置gpf5输出低电平, 
    ldr r1, =0x56000054
	ldr r0,=0x00
	str r0,[r1]
	
	//死循环
halt:
    b halt

然后我们重新编译即可。

方法二:直接修改之前控制LED时的led_on.bin,点亮LED2。


led_on.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:
   0:	e59f1014 	ldr	r1, [pc, #20]	; 1c <.text+0x1c>
   4:	e3a00c01 	mov	r0, #256	; 0x100
   8:	e5810000 	str	r0, [r1]
   c:	e59f100c 	ldr	r1, [pc, #12]	; 20 <.text+0x20>
  10:	e3a00000 	mov	r0, #0	; 0x0
  14:	e5810000 	str	r0, [r1]

00000018 <halt>:
  18:	eafffffe 	b	18 <halt>
  1c:	56000050 	undefined
  20:	56000054 	undefined

我们只要把上面mov    r0, #256    ; 0x100  对应的机器码e3a00c01修改为mov    r0, #1024    ; 0x400对应的机器码就可以了,那么我们要知道机器码e3a00c01里面哪一部分对应的是0x100,然后修改成0x400就可以了,那么我们看一下MOV指令对应的机器码的格式,

mov    r0, #256    ; 0x100的时候对应的机器码是e3a00c01=1110,0011,1010,0000,0000,1100,0000,0001。

前面bit24-21是1101用来表示MOV指令,bit15-12是0000表示r0,后面的bit11-0就用来表示0x100. 那么我们只需要修改低12位就可以表示mov    r0, #1024。

那么低12位怎么表示一个立即数呢,这12位拆分成高四位和低八位。,表示方式如下:

我们看一下0x100对应的机器码的低12位是1100,0000,0001,也就是0000,0001右移2*1100,也就是0000,0001右移24位. 

0000,0001循环右移1位成为1000,0000,0000,0000,0000,0000,0000,0000

然后再循环右移23位成为0000,0000,0000,0000,0000,0001,0000,0000=0x100. 

那么0x400=0000,0000,0000,0000,0000,0100,0000,0000,那么它可以由0000,0001右移22位得到。那么可以得到0x400对应的rotate=22/2=11=1011,

那么可以得到0x400对应的机器码的12位是1011,0000,0001,

那么由mov    r0, #256    ; 0x100的时候对应的机器码是e3a00c01=1110,0011,1010,0000,0000,1100,0000,0001。可以知道

mov    r0, #1024    ; 0x400对应的机器码是1110,0011,1010,0000,0000,1011,0000,0001=e3a00b01.

通过上面的对比可以发现,显然是直接修改汇编代码比较简单,修改机器码的方法太麻烦了。

猜你喜欢

转载自blog.csdn.net/u013171226/article/details/113913467