12.ARM伪指令操作

12.ARM伪指令操作

首先ARM伪指令包括:

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

 

ARM机器码:

其实任何一种处理器可以运行的叫机器码,机器码是从汇编程序通过汇编器转换来的。接下来看看机器码的信息。流程:图1-1.

12.ARM伪指令操作_第1张图片

图1-1

在上一节里,建立好了一个简单的汇编工程,在start.S只有三行代码:图1-2:

12.ARM伪指令操作_第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.

12.ARM伪指令操作_第3张图片

图1-5

上面的反汇编代码中,最右边可以看到是汇编文件的汇编代码。多了分号,这是系统加的,表示后面是注释。注释里是16进制数,跟立即数是对应的。最左边,可以看到内存地址,指定程序在50008000开始运行,由于ARM核默认四字节对齐运行方式,所以下一条指令在50008004地址开始。可以看到中间还有一串数字。这就是机器码。

有关机器码的知识,接下来打开ARM Architecture Reference Manual.pdf文档。打开之后找到The ARM Instruction Set

这一章。可以找到有关机器码的知识。如下图1-6:

12.ARM伪指令操作_第4张图片

12.ARM伪指令操作_第5张图片

图1-6

可以看到ARM机器码是32位整数,32的ARM机器码被分成了多个段,每个段有每个段的含义。接下来以一种机器码为例进行分析,以MOV指令为例。

在start.S汇编文件里有有两条指令:

mov r1,r2

moveq r2,#0xee

对应的机器码:

e1a01002

03a020ee

如下图1-7:

12.ARM伪指令操作_第6张图片

图1-7

 

下面是mov机器码的格式:图1-8:

12.ARM伪指令操作_第7张图片

图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:

12.ARM伪指令操作_第8张图片

图1-10

 

 

 

 

在指令中,前4位,即是[31:28],表示condition条件位:图1-11:

 

图1-11

Opcode指令类型的信息:21~14位:图1-12:

12.ARM伪指令操作_第9张图片

12.ARM伪指令操作_第10张图片

图1-12

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

二、定义类伪指令:

12.ARM伪指令操作_第11张图片

图2-1

上图2-1里就是要学习的伪指令。

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

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

各个伪指令:

Global:定义一个全局的符号。

Data:定义数据段。数据存到数据段。

Ascii:定义字符串

Byte:定义字节

Word:字

Equ:相当于宏定义

Align:设置对齐。

第一个是.global,指明一个全局的标识,在下面的_start就是。

至于.data、.ascii、.byte、.word的操作如下:

Start.S:图2-2:

:12.ARM伪指令操作_第12张图片

图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:

12.ARM伪指令操作_第13张图片

图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:

12.ARM伪指令操作_第14张图片

12.ARM伪指令操作_第15张图片

图3-1-1

所以要操作一个大于8位的操作数,必须使用ldr伪指令。注意的是ldr伪指令的立即数是等号=开头,再加立即数。操作成功。图3-2:

12.ARM伪指令操作_第16张图片

图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:

12.ARM伪指令操作_第17张图片

图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

12.ARM伪指令操作_第18张图片

图3-5

增加一条汇编代码:图3-6:

Start.S里的汇编代码:

Ldr r0,=0xeff

Mov r1,#0x22

Mov r2,#0x32

12.ARM伪指令操作_第19张图片

图3-6

在ldr前加一行:图3-7:

Start.S里的汇编代码:

mov r3,#0x12

Ldr r0,=0xeff

Mov r1,#0x22

Mov r2,#0x32

 

12.ARM伪指令操作_第20张图片

图3-7

 

在第一行增加一行nop空操作,最后增加一行mov操作:图3-8:

nop

    mov r3,#0x12

    ldr r0,=0xeff

    mov r1,#0x22

    mov r2,#0x32

    mov r3,#0x42

12.ARM伪指令操作_第21张图片

12.ARM伪指令操作_第22张图片

图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伪指令操作)