12.ARM伪指令操作

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了12.ARM伪指令操作相关的知识,希望对你有一定的参考价值。

12.ARM伪指令操作

首先ARM伪指令包括:

  1. ARM机器码
  2. 定义类伪指令
  3. 操作类伪指令

?

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里就是要学习的伪指令。

伪指令:所谓的伪指令,可以从拆开伪和指令,之所以叫指令,就是它的执行看似起到一定效果,看似跟真实的指令一样。所以叫它指令,但是,它有一个伪字修饰。这是为什么呢?这是因为它的执行不会产生机器码,我们知道,指令只有转换成机器码才能被机器执行。它起到两种作用:

  1. 伪指令只是在编译的使用起到作用,就像C语言里的宏定义。
  2. 伪指令在执行的时候转化成其他的指令执行。

各个伪指令:

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伪指令操作的主要内容,如果未能解决你的问题,请参考以下文章

ARM伪指令,王明学learn

嵌入式:ARM符号定义伪操作详解

ARM的伪指令

嵌入式:ARM 汇编控制伪操作

ARM汇编程序中的伪指令

嵌入式:ARM符号定义伪操作详解