12.ARM伪指令操作
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了12.ARM伪指令操作相关的知识,希望对你有一定的参考价值。
12.ARM伪指令操作
首先ARM伪指令包括:
- ARM机器码
- 定义类伪指令
- 操作类伪指令
?
ARM机器码:
其实任何一种处理器可以运行的叫机器码,机器码是从汇编程序通过汇编器转换来的。接下来看看机器码的信息。流程:图1-1.
图1-1
在上一节里,建立好了一个简单的汇编工程,在start.S只有三行代码:图1-2:
图1-2
接下来对产生的elf文件来进行反汇编,命令是:
arm-linux-objdump -D -S gboot.elf >dump,将反汇编的代码存到dump文件,dump文件的内容是:
?
gboot.elf: file format elf32-littlearm
?
Disassembly of section .text:
?
50008000 <_start>:
.text
.global _start
_start:
????mov r1,r2
50008000:????e1a01002 ????mov????r1, r2
????moveq r2,#0xee
50008004:????03a020ee ????moveq????r2, #238????; 0xee
????mov r3,#0x1
50008008:????e3a03001 ????mov????r3, #1????; 0x1
Disassembly of section .debug_aranges:
?
00000000 <.debug_aranges>:
0:????0000001c ????andeq????r0, r0, ip, lsl r0
4:????00000002 ????andeq????r0, r0, r2
8:????00040000 ????andeq????r0, r4, r0
c:????00000000 ????andeq????r0, r0, r0
10:????50008000 ????andpl????r8, r0, r0
14:????0000000c ????andeq????r0, r0, ip
????...
Disassembly of section .debug_info:
?
00000000 <.debug_info>:
0:????00000045 ????andeq????r0, r0, r5, asr #32
4:????00000002 ????andeq????r0, r0, r2
8:????01040000 ????tsteq????r4, r0
c:????00000000 ????andeq????r0, r0, r0
10:????50008000 ????andpl????r8, r0, r0
14:????5000800c ????andpl????r8, r0, ip
18:????72617473 ????rsbvc????r7, r1, #1929379840????; 0x73000000
1c:????00532e74 ????subseq????r2, r3, r4, ror lr
20:????6d6f682f ????stclvs????8, cr6, [pc, #-188]!
24:????61732f65 ????cmnvs????r3, r5, ror #30
28:????2f61626d ????svccs????0x0061626d
2c:????34364b4f ????ldrtcc????r4, [r6], #-2895
30:????625f3031 ????subsvs????r3, pc, #49????; 0x31
34:????00657261 ????rsbeq????r7, r5, r1, ror #4
38:????20554e47 ????subscs????r4, r5, r7, asr #28
3c:????32205341 ????eorcc????r5, r0, #67108865????; 0x4000001
40:????2e38312e ????rsfcsep????f3, f0, #0.5
44:????01003035 ????tsteq????r0, r5, lsr r0
48:????Address 0x00000048 is out of bounds.
?
Disassembly of section .debug_abbrev:
?
00000000 <.debug_abbrev>:
0:????10001101 ????andne????r1, r0, r1, lsl #2
4:????12011106 ????andne????r1, r1, #-2147483647????; 0x80000001
8:????1b080301 ????blne????200c14 <_start-0x4fe073ec>
c:????13082508 ????movwne????r2, #34056????; 0x8508
10:????00000005 ????andeq????r0, r0, r5
Disassembly of section .debug_line:
?
00000000 <.debug_line>:
0:????00000033 ????andeq????r0, r0, r3, lsr r0
4:????001e0002 ????andseq????r0, lr, r2
8:????01020000 ????tsteq????r2, r0
c:????000d0efb ????strdeq????r0, [sp], -fp
10:????01010101 ????tsteq????r1, r1, lsl #2
14:????01000000 ????tsteq????r0, r0
18:????00010000 ????andeq????r0, r1, r0
1c:????72617473 ????rsbvc????r7, r1, #1929379840????; 0x73000000
20:????00532e74 ????subseq????r2, r3, r4, ror lr
24:????00000000 ????andeq????r0, r0, r0
28:????00020500 ????andeq????r0, r2, r0, lsl #10
2c:????15500080 ????ldrbne????r0, [r0, #-128]
30:????02022f2f ????andeq????r2, r2, #188????; 0xbc
34:????Address 0x00000034 is out of bounds.
?
Disassembly of section .ARM.attributes:
?
00000000 <.ARM.attributes>:
0:????00001741 ????andeq????r1, r0, r1, asr #14
4:????61656100 ????cmnvs????r5, r0, lsl #2
8:????01006962 ????tsteq????r0, r2, ror #18
c:????0000000d ????andeq????r0, r0, sp
10:????00543405 ????subseq????r3, r4, r5, lsl #8
14:????01080206 ????tsteq????r8, r6, lsl #4
从上面的反汇编代码知道,程序的入口是:图1-3:
图1-3
与在Makefile中指定的起始地址相同:图1-4:
图1-4
?
可以看到汇编代码中,的最右边是程序里的汇编代码,图1-5.
图1-5
上面的反汇编代码中,最右边可以看到是汇编文件的汇编代码。多了分号,这是系统加的,表示后面是注释。注释里是16进制数,跟立即数是对应的。最左边,可以看到内存地址,指定程序在50008000开始运行,由于ARM核默认四字节对齐运行方式,所以下一条指令在50008004地址开始。可以看到中间还有一串数字。这就是机器码。
有关机器码的知识,接下来打开ARM Architecture Reference Manual.pdf文档。打开之后找到The ARM Instruction Set
这一章。可以找到有关机器码的知识。如下图1-6:
图1-6
可以看到ARM机器码是32位整数,32的ARM机器码被分成了多个段,每个段有每个段的含义。接下来以一种机器码为例进行分析,以MOV指令为例。
在start.S汇编文件里有有两条指令:
mov r1,r2
moveq r2,#0xee
对应的机器码:
e1a01002
03a020ee
如下图1-7:
图1-7
?
下面是mov机器码的格式:图1-8:
图1-8
接下来转化为2进制的:
汇编指令:
mov r1,r2
机器码:
e1a01002= 11100001101000000001000000000010
?
汇编指令:
moveq r2,#0xee
机器码
03a020ee= 00000011101000000010000011101110
分析机器码:
汇编指令:
mov r1,r2
机器码:
e1a01002= 1110 00 0 1101 0 0000 0001 000000000010
由下面的condition表格知道,mov是没有条件的,没条件执行condition,[31:28]=1110,是对的。[27:26]这两位是保留位为00,也是对的。[25]对应的是I位,对应的是[11:0]的数,如果这12的操作数是立即数,I位1,如果这12位为寄存器,则I位为0。可以看到I位为0,后面的[11:0]是一个寄存器r2,也是正确的。[24:21]对应的是opcode,指明指令的类型。不同的指令,这里的值不同,这也操作系统识别不同指令的地方。[20]位是S位,是指明该指令的运行是否影响CPSR寄存器,mov指令的运行不会影响CPSR的任意位。所以是0。在mov指令中,没有使用Rn[19:16],所以四位都是0000。[15:12]四位是目的寄存器,这里目的寄存器我使用了r1,所以[15:12]=0001。后面[11:0]12位是源操作数,使用的寄存器是r2=000000000010。与前面的S位对应。
?
?
汇编指令:
moveq r2,#0xee
机器码
03a020ee= 0000 00 1 1101 0 0000 0010 000011101110
由下面的condition表格知道,moveq是有条件的,条件是eq,对应的[31:28]=0000,是对的。[27:26]这两位是保留位为00,也是对的。[25]对应的是I位,对应的是[11:0]的数,如果这12的操作数是立即数,I位1,如果这12位为寄存器,则I位为0。可以看到I位为1,后面的[11:0]是立即数0xee,也是正确的。[24:21]对应的是opcode,指明指令的类型。不同的指令,这里的值不同,这也操作系统识别不同指令的地方。这里用到的都是mov指令所以值是一样的。[20]位是S位,是指明该指令的运行是否影响CPSR寄存器,moveq指令的运行不会影响CPSR的任意位。所以是0。在moveq指令中,没有使用Rn[19:16],所以四位都是0000。[15:12]四位是目的寄存器,这里目的寄存器我使用了r2,所以[15:12]=0010。后面[11:0]12位是源操作数,使用的是立即数=000011101110。与前面的S位对应。
?
上面就是mov程序的汇编机器码位的解释。在这里,会注意到后面的12位可以通过S位来指明这12是立即数还是寄存器。后面还会讲到,这12中,只有末尾8位是用来表示立即数或者寄存器的,[11:8]是设置移位等信息的。所以表示的数最大0xff。当我们把他改为0x1ff的时候会报错:图1-9:
图1-9
?
?
所以会发现,ARM汇编的立即数的范围是很有限的。所以这就是后面要写的伪指令的知识要解决的问题。
?
下面是上面的图解:1-10:
图1-10
?
?
?
?
在指令中,前4位,即是[31:28],表示condition条件位:图1-11:
图1-11
Opcode指令类型的信息:21~14位:图1-12:
图1-12
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
二、定义类伪指令:
图2-1
上图2-1里就是要学习的伪指令。
伪指令:所谓的伪指令,可以从拆开伪和指令,之所以叫指令,就是它的执行看似起到一定效果,看似跟真实的指令一样。所以叫它指令,但是,它有一个伪字修饰。这是为什么呢?这是因为它的执行不会产生机器码,我们知道,指令只有转换成机器码才能被机器执行。它起到两种作用:
- 伪指令只是在编译的使用起到作用,就像C语言里的宏定义。
- 伪指令在执行的时候转化成其他的指令执行。
各个伪指令:
Global:定义一个全局的符号。
Data:定义数据段。数据存到数据段。
Ascii:定义字符串
Byte:定义字节
Word:字
Equ:相当于宏定义
Align:设置对齐。
第一个是.global,指明一个全局的标识,在下面的_start就是。
至于.data、.ascii、.byte、.word的操作如下:
Start.S:图2-2:
:
图2-2
对上面的工程的elf文件进行查看:
arm-linux-readelf -a gboot.elf
-a是全部输出的意思:输出的信息如下:
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2‘s complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0x50008000
Start of program headers: 52 (bytes into file)
Start of section headers: 33116 (bytes into file)
Flags: 0x5000002, has entry point, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
Number of section headers: 11
Section header string table index: 8
?
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 50008000 008000 00000c 00 AX 0 0 4
[ 2] .data PROGBITS 5001000c 00800c 00000f 00 WA 0 0 1
[ 3] .debug_aranges PROGBITS 00000000 008020 000020 00 0 0 8
[ 4] .debug_info PROGBITS 00000000 008040 000049 00 0 0 1
[ 5] .debug_abbrev PROGBITS 00000000 008089 000014 00 0 0 1
[ 6] .debug_line PROGBITS 00000000 00809d 000039 00 0 0 1
[ 7] .ARM.attributes ARM_ATTRIBUTES 00000000 0080d6 000018 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 0080ee 00006c 00 0 0 1
[ 9] .symtab SYMTAB 00000000 008314 000180 10 10 13 4
[10] .strtab STRTAB 00000000 008494 000087 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
?
There are no section groups in this file.
?
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x008000 0x50008000 0x50008000 0x0000c 0x0000c R E 0x8000
LOAD 0x00800c 0x5001000c 0x5001000c 0x0000f 0x0000f RW 0x8000
?
Section to Segment mapping:
Segment Sections...
00 .text
01 .data
?
There is no dynamic section in this file.
?
There are no relocations in this file.
?
There are no unwind sections in this file.
?
Symbol table ‘.symtab‘ contains 24 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 50008000 0 SECTION LOCAL DEFAULT 1
2: 5001000c 0 SECTION LOCAL DEFAULT 2
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 4
5: 00000000 0 SECTION LOCAL DEFAULT 5
6: 00000000 0 SECTION LOCAL DEFAULT 6
7: 00000000 0 SECTION LOCAL DEFAULT 7
8: 5001000c 0 NOTYPE LOCAL DEFAULT 2 hello
9: 50010016 0 NOTYPE LOCAL DEFAULT 2 bh
10: 50010016 0 NOTYPE LOCAL DEFAULT 2 $d
11: 50010017 0 NOTYPE LOCAL DEFAULT 2 say
12: 50008000 0 NOTYPE LOCAL DEFAULT 1 $a
13: 5000800c 0 NOTYPE GLOBAL DEFAULT ABS __exidx_end
14: 5001001b 0 NOTYPE GLOBAL DEFAULT ABS _bss_end__
15: 5001001b 0 NOTYPE GLOBAL DEFAULT ABS __bss_start__
16: 5000800c 0 NOTYPE GLOBAL DEFAULT ABS __exidx_start
17: 5001001b 0 NOTYPE GLOBAL DEFAULT ABS __bss_end__
18: 50008000 0 NOTYPE GLOBAL DEFAULT 1 _start
19: 5001001b 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
20: 5001001c 0 NOTYPE GLOBAL DEFAULT ABS __end__
21: 5001001b 0 NOTYPE GLOBAL DEFAULT ABS _edata
22: 5001001c 0 NOTYPE GLOBAL DEFAULT ABS _end
23: 5001000c 0 NOTYPE GLOBAL DEFAULT 2 __data_start
?
No version information found in this file.
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "4T"
Tag_CPU_arch: v4T
Tag_ARM_ISA_use: Yes
?
数据段的起始地址:图2-3:
?
图2-3
可以看到数据段的起始地址是5001000c,定义的Ascii:定义字符串,Byte:定义字节,Word:字,都在这数据段里面。
?
Equ伪指令:
定义一个宏的指令,运行如下图:2-4:
图2-4
上面可以看到r0是0x89,宏定义成功。
?
最后是align伪指令的操作:
在命令行执行:
arm-linux-readelf -a gboot.elf
可以看到say出的物理地址是50010017:图2-5:
图2-5
?
现在来say出的标识之上加上align对齐,进行四字节对齐,再次编译的结果是:图2-6:
图2-6
可以看到say处的地址变成了50010020,是4字节对齐了。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
操作类伪指令:
????1.nop:是空操作。意义,进行延时,编写驱动,返回硬件的时候,由于时序的要求,需要延时,就执行该指令。反汇编看看它实际执行的代码:在下图看到,nop实际上执行的代码是mov r0,,r0。就是一个死循环的操作。起到延时的作用。反汇编代码:图3-1-0:
图3-1-0
?
????2.ldr:前面的mov指令格式分析知道,12位的源操作数,只有8位表示数字大写,[11:8]是移位信息的。所以源操作数不能大于0x1ff,如果大于则会出错。图3-1-1:
图3-1-1
所以要操作一个大于8位的操作数,必须使用ldr伪指令。注意的是ldr伪指令的立即数是等号=开头,再加立即数。操作成功。图3-2:
图3-2
?
?
接下来看ldr伪指令是转化成哪些指令执行的。
对代码进行反汇编:
arm-linux-objdump -D -S gboot.elf
找到ldr的汇编代码:
????ldr r0,=0xeff//这里的ldr是伪指令
50008000:????e59f0000 ????ldr????r0, [pc, #0]????; 50008008 <_start+0x8>//这里的ldr是存储器//访问指令。Ldr存储器指令把0xeff放到内存地址为50008008的地方去了。
mov r3,#0x1
50008004:????e3a03001 ????mov????r3, #1????; 0x1
50008008:????00000eff ????.word????0x00000eff //在此地址定义了一个字的数据。反汇编代码:图3-3:
图3-3
?
?
下面看下面几个例子,来分析一下ldr伪指令的工作工程。
?
当start.S里只有下面这一行汇编代码的:图3-4:
ldr r0,=0xeff
对工程进行编译后反汇编:arm-linux-objdump -D -S gboot.elf
图3-4
从ldr r0,=0xeff的反汇编代码看到,ldr伪指令是在编译的时候转换成ldr存储器访问指令执行的。首先ldr伪指令把0xeff,存到了50008004地址处,同时定义了一个字的数据0x00000eff,这个数据大小跟我定义的数据大小一致的。然后通过内存访问指令,ldr r0,[pc,#-4],把这个数据取出来。我们知道pc指针总是指向正在执行指令的后第二条指令的地址。在这里伪指令ldr正在运行的地址是50008000,此时pc指针的地址是后第二条指令的地址,就是50008000+2*4=50008008。看到ldr存储器访问指令访问的地址是:
[pc,#-4]=pc-4=50008008-4=50008004,可以看到,这就是我定义一个字数据的地方,就是我定义存储数据的地方。这样,cpu就实现了存储大于8位数据。
下面是增加了汇编代码的反汇编截图,可以分析到跟这种情况是一致的。
?
增加一条汇编代码:图3-5:
Start.S里的汇编代码:
Ldr r0,=0xeff
Mov r1,#0x22
图3-5
增加一条汇编代码:图3-6:
Start.S里的汇编代码:
Ldr r0,=0xeff
Mov r1,#0x22
Mov r2,#0x32
图3-6
在ldr前加一行:图3-7:
Start.S里的汇编代码:
mov r3,#0x12
Ldr r0,=0xeff
Mov r1,#0x22
Mov r2,#0x32
?
图3-7
?
在第一行增加一行nop空操作,最后增加一行mov操作:图3-8:
nop
????mov r3,#0x12
????ldr r0,=0xeff
????mov r1,#0x22
????mov r2,#0x32
????mov r3,#0x42
图3-8
为了温习前面的知识,这里对这个工程的反汇编代码再来分析一次,看看上面汇编代码的执行过程。首先是nop空操作,反汇编代码看到他是做mov r0,r0的无限循环操作,不会产生什么影响,只是延时作用。然后mov r3,#0x12,操作的内存地址是50008004,它是把0x12存到该地址处。转换后的机器码是e3a03012,可以对应上面对这机器码进行分析,看看对不对。
从ldr r0,=0xeff的反汇编代码看到,ldr伪指令是在编译的时候转换成ldr存储器访问指令执行的。首先ldr伪指令把0xeff,存到了50008018地址处,同时定义了一个字的数据0x00000eff,这个数据大小跟我定义的数据大小一致的。然后通过内存访问指令,ldr r0,[pc,#8],把这个数据取出来。我们知道pc指针总是指向正在执行指令的后第二条指令的地址。在这里伪指令ldr正在运行的地址是50008008,此时pc指针的地址是后第二条指令的地址,就是50008008+2*4=50008010。看到ldr存储器访问指令访问的地址是:
[pc,#8]=pc+8=500080010+8=50008018,可以看到,这就是我定义一个字数据的地方,就是我定义存储数据的地方。这样,cpu就实现了存储大于8位数据。
数据完全正确,说明前面的操作与分析都是正确的。呼呼……………
?
以上是关于12.ARM伪指令操作的主要内容,如果未能解决你的问题,请参考以下文章