• ARM汇编(gun-complier)


    首先,在前面已经将ARM架构体系和计算机系统的简单讲解,如果到时候忘了直接先浏览一下前面写好的《从计算机系统组成到ARM体系架构》。

    处理器执行状态

    最常用的执行状态ARM状态,其次是Thumb状态,除此之外还有ThumbEE状态,和Jazelle状态(不同状态有各自不同的指令集);关于这四个状态,在前面CPSR寄存器部分有详细描述

    从ARM切换到Thumb状态

    下面是一个从ARM切换到Thumb状态,再从Thumb状态切换到ARM状态的例子:

    	.text
    mov r3,#6			@PC=0x00000000		这句不是必须的
    adr r0,thumb		@pc=0x00000004		伪指令真正的汇编指令是:add r0,pc,0x4,这里的PC+0x4=Thumb的地址即=0x00000010,这里的PC是一个特别的值
    add r0,r0,#1		@pc=0x00000008		想要切换到Thumb状态需要让指定转跳的地址最后一位为1 
    bx r0				@pc=0x0000000c	 	转跳到thumb,即切换为Thumb状态,bx和bl是有区别的,bx可以切换状态,CPSR的状态会改变
    .code 16			@ 					即指定步长为0x216位地址每次偏移2个字节
    thumb:
    	mov r3,#4		@pc=0x00000010		这句不是必须的
    	ldr r3,=ARM		@pc=0x00000012		真正的汇编指令:LDR r3,[pc,#0xc] 即ARM的地址0x00000018
    	bx r3 			@pc=0x00000014		转跳到ARM,即切换为ARM状态,因为转跳地址最后一位不是1;同时CPSR寄存器的值会改变
    .code 32			@					即指定步长为0x432位地址每次偏移4字节
    ARM:
    	mov r3,#2		@pc=0x00000018		这句不是必须的
    	mov r3,#4	   	@pc=0x0000001c		这句不是必须的
    	.end			
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ARM执行状态和Thumb执行状态对比

    ARMTumb
    32位的代码密度,代码量更大16位的代码密度,代码量更小
    执行效率更高执行效率更低
    完整的体系结构,可以执行异常不是完整的体系结构,想要执行异常需要切换到ARM状态,只支持通用功能
    ARM指令集Thumb指令集,这两个指令集有区别,具体差异可以百度

    ARM常见指令集

    mov数据传输指令

    mov 寄存器,立即数或寄存器
    mov r1,#3 相当于r1=3;格式#?中?是立即数,立即数合法的条件(1)最高位1和最低位1之间二进制数的个数小于等于8合法;(2)如果最高位和最低位1之间二进制数的个数大于8,那么循环右移再判断,如果仍然大于8,那么不合法;

    mvn 取反传送指令

    mvn 寄存器1,立即数或者寄存器2
    mvn r1,#3 相当于r1=0xFFFFFFFC

    LDR数据传输(伪指令)

    LDR 寄存器,=数字
    LDR r1,=0x124242 相当于r1=124242,使用LDR不用考虑数字是否合法的问题,这个指令非常,非常好用且常用

    add 加法指令

    add 寄存器1,寄存器1,立即数或者寄存器2
    ldr r1,=3
    add r1,r1,#4 结果r1=3,r1=r1+4;最后r1=7;
    这里其实暴露了一个问题:还是立即数的问题;我认为最好的解决办法是这样写
    ldr r1,=3
    ldr r2,=4
    add r1,r1,r2

    adds能改变CPSR寄存器C的加法运算,adc带进位的加法运算

    如果指令后面带S,那么这个指令具备改变CPSR的能力
    adds 寄存器1,寄存器1,寄存器或立即数2
    adc 寄存器1,寄存器1,寄存器2或立即数
    下面是使用add和adc的例子
    .text
    ldr r1,=0xFFFFFFFC	@r1=0xFFFFFFFC
    adds r3,r1,#0x8		@r3=0x4,cpsr寄存器的C位置1
    ldr r3,=5			@r3=3
    adc r4,r3,#3		@r4=r3+3+(寄存器CPSR的)c,即r4=3+5+1
    .end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    sub减法指令,subs能改变CPSR寄存器N,C的寄存器的减法,sbc带进位的减法

    sub 寄存器1,寄存器1,寄存器2或立即数
    aubs 寄存器1,寄存器1,寄存器2或立即数
    sbc 寄存器1,寄存器1,寄存器2或立即数
    sub太简单就不举例了,下面举例subs和sbc的使用
    	.text
    ldr r1,=0X1		@r1=0x1
    ldr r2,=0XFF	@r2=0xFF
    ldr r4,=0x2		@r4=0x2
    ldr r5,=0x3		@r5=0x3
    subs r3,r2,r1	@r3=r2-r1,CPSR的中的C=1
    sbc  r6,r4,r5	@r6=r4-r5-!c 即r6=2-3-0=0xFFFFFFFF
    subs r3,r1,r2	@r3=r1-r2,CPSR中N=1,C=0;
    sbc  r6,r4,r5	@r6=r4-r5-!c 即r6=2-3-1=0xFFFFFFFE
    	.end	
    		
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    前面已经说过了一般来说指令后面加s后缀会改变CPSR

    mul乘法指令(无符号),muls有符号的乘法指令(即能改变CPSR的N位),mla加乘指令

    mul 寄存器1,寄存器1,寄存器2 即寄存器1中存放的值=寄存器中存放的值1*寄存器中存放的值
    muls 寄存器1,寄存器1,寄存器2 和mul一样,区别在于会改变CPSR的N位

    mla 寄存器1,寄存器1,寄存器2,寄存器3 即寄存器1中存放的值=(寄存器1中存放的值*寄存器2中存放的值)+寄存器3中存放的值

    .text
    	ldr r1,=0x4			@ r1=0x4
    	ldr r2,=0x20000002 	@ r2=0x20000002
    	ldr r5,=0x2			@r5=0x2
    	ldr r6,=0x2			@r6=0x2
    	ldr r7,=0x3			@r7=0x2
    	mul r3,r1,r2		@r3=r1+r2=0x80000008	CPSR寄存器中N不会置为1
    	muls r4,r1,r2		@r4=r1+r2=0x80000008	CPSR寄存器中的N位置1
    	mla r8,r5,r6,r7		@r8(=r5*r6)+r7		
    .end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    STR,和LDR指令(这里演示的不是伪指令),注意汇编和C不一样,并不区分大小写

    LDR在这里的用法是将寄存器中的值放入内存地址中
    STR和LDR相反,是将指定寄存器的值写入内存中,但是STR有个非常坑的地方,也就是权限问题
    讲到权限问题就不得不提到计算机中的MMU这个设备,虽然和接下来说的问题关联不太大,但是可以提一下;
    MMU内存管理单元:百度给我的解释是:MMU是Memory Management Unit的缩写,中文名是内存管理单元,有时称作分页内存管理单元(英语:paged memory management unit,缩写为PMMU)。它是一种负责处理中央处理器(CPU)的内存访问请求的计算机硬件。它的功能包括虚拟地址到物理地址的转换(即虚拟内存管理)、内存保护、中央处理器高速缓存的控制,在较为简单的计算机体系结构中,负责总线的仲裁以及存储体切换(bank switching,尤其是在8位的系统上;
    简单来说就是:关了MMU这个设备我们直接访问的就是物理地址,开启这个设备它会给我们弄一些虚拟地址,而这些虚拟地址通过这个设备的转换,最后会映射到物理地址;ARM一般来说有7种模式,而cortex-A系列增加了一个monitor模式,除了User都是特权模式(即实模式,可以直接访问物理地址),User是非特权模式(即保护模式,不能直接访问物理地址),
    虚拟地址和物理地址的的概念不说了,接下来言归正传,STR,LDR在使用的时候会碰到NO “write” permission 或者NO “read” permission,出现这个问题的原因是对于访问的内存空间没有填充,(简单来讲就是空间不属于这段程序,这段程序没有操作该内存空间的权限)下面是相关的例子:
    .text				@代码段
    @msr cpsr,0xD0		@这句没啥用,之前我以为是模式问题		
    ldr r1,=0x87654321	@r1=0x87654321
    mov r2,#0x40		@r2=0x40
    STR r1,[r2]			@*(0x40)=0x87654321,将寄存器r1种的值传给寄存器r2存放的值代表的地址中
    LDR r4,[r2]			@r4=*(0x40)将寄存器r2存放的值代表的地址中的值传给r4
    	.data			@数据段
    buf:				@标签
    	.space  4		@分配4个字节的空间
    .end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    当我们使用STR或者LDR我们应该定义一段数据段,数据段的大小应该要能存放下数据。同时注意STR和LDR的第二个参数代表的地址应该小于等于在数据段包含的的地址,否则结果可能不能达到我们的预期;

    stmfd压栈和ldmfd出栈指令

    关于stmxx和ldmxx
    满递减堆栈(Full decending)-FD :堆栈首部是高地址,堆栈向低地址增长。栈指针总是指向最后一个元素。注意,最后一个元素是最后压入的数据。
    空递减堆栈(Empty descending)—ED:堆栈首部是高地址,堆栈向低地址增长。栈指针总是指向下一个将要放入数据的空位置。
    满递增堆栈(Full ascending)—FA 堆栈首部是低地址,堆栈向高地址增长。栈指针总是指向堆栈最后一个元素。
    空递增堆栈(Empty ascending)-EA 堆栈首部指向低地址,堆栈向高地址增长。栈指针总是指向下一个将要放入数据的空位置
    stmfd和ldmfd是ATPCS标准使用的堆栈,下面是一个关于stmfd,和ldmfd的使用例子
    .text						@代码段声明
    	mov r1,#1				@r1=1
    	mov r2,#6				@r2=6
    	ldr sp, =stack_base		@为堆栈开辟空间,sp是堆栈指针,指向栈顶元素,sp=B0 
    	stmfd sp!, {r0-r12}	 	@入栈顺序是从后向前;将B0-(1*4)=r12,B0-(2*4)=r11,B0-(3*4)=r10,B0-(4*4)=r9,.....B0-(13*4)=r0;	同时sp=sp-0*4,sp=sp-1*4,sp=sp-2*4
    	mov r1,#2				@r1=2
    	ldmfd sp!, {r0-r12}^ 	@出栈顺序是从前往后,r0=B0-(13*4),r1=B0-(12*4),.......r12=B0-(1*4)
    	mov r1,#3			 	@r1=3
    	mov r2,#7			 	@r2=7
    .data						@数据段声明
    buf:						@标签
    	.space 48				@分配空间48字节
    stack_base:					@栈基地址,也就是SP最初指的地址
    .end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    ldr sp, =stack_base		 
    stmfd sp!, {r0-r12}	
    @这两句实际执行的过程如下图,stmfd压栈的过程并未完全列出,但实际上原理可以类推;
    @由压栈过程可以反推出栈过程,这里就不画图了;
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述


    常见ARM指令集表

    指令示例功能
    addadd r1,r2,r3r1=r2+r3
    adcadc r1,r2,r3r1=r2+r3+c,这里的C是CPSR的进位标志位
    subsub r1,r2,r3r1=r2-r3
    rsbrsb r1,r2,r3r1=r3-r2
    sbcsbc r1,r2,r3r1=r2-r3-!C,这里的C是CPSR的借位标志
    rscrsc r1,r2,r3r1=r3-r2-!C.这里的V是CPSR的借位标志
    andand r0,r0,#0x0Fr0=r0&0x0F; 实际上r0等于r0中的值和0F按位与
    orrorr r0,r0,#0x0F实际上r0=r0按位或0x0F
    eoreor r0,r0,#0xF实际上r0=r0按位异或0x0F
    bicbic r0,r0,0x1F实际上就是清除r0的[4:0]位,0x1F=B0001 1111,也就是后5位
    cmpcmp r1,#10就是比较r1和10是否相等;cpsr=r1-10(只会影响N,Z,C);(1)如果r1>10,cpsr的C位置1;(2)如果r1==10,cpsr的Z位和C位置1;(3)如果r1<10,cpsr的N位置1;cmp是一个有点特别的指令,可以纤细看cpsr描述
    cmncmn r1,#10就是比较r1和10是否不相等,cpsrt=r1+10;(只会影响N,Z,C)
    tsttst r1,#3cpsr= r1&3
    teqteq r1,r2cpsr=r1^r2
    movmov r1,#3r1=3
    mvnmvn r1,#3r1=~3
    lsrlsr r1,#2r1=r1/4;等于将r1右移2位
    lsllsr,r1,#2r1=r1*4;等于将r1左移2位
    asrasr r1,#2逻辑右移,右移,空出来的位置由之前的符号位补齐
    rorror r1,#2将r1循环右移2位

    分支指令

    指令功能
    B 跳转指令B[条件] 目的地址; 直接跳转到目的地址开始执行
    BL 带返回的跳转指令BL[条件] 目的地址;跳转到目的地址开始执行,但是跳转前会先将PC的保存到LR
    BX 带状态切换的跳转指令BX [条件] 目的地址;跳转到目的地址开始执行,目标地址的指令可以是ARM指令也可以是Thumb指令,这个指令能够改变CPSR
    BXL 带返回和状态切换的跳转指令BXL[条件] 目的地址;这个指令就是BL和BX的融合版,参照上面的特性即可

    数据定义,伪操作

    在这里插入图片描述


    条件码

    在之前的分支指令提到过条件,实际上其他指令也可以加条件,下面是条件码表,及其后面有2个简单的使用例子;

    在这里插入图片描述

    例子:用汇编实现C语言代码:if(a==0)b=1;else b=2;假设a和b都是无符号数,其中a=0
    .text
    mov   r1,#0				@r1=0
    cmp   r1,#0				@r1和0比较
    moveq r2,#1				@如果r1和0相等,那么r2=1
    movne r2,#2				@如果r1和0不相等,那么r2=2
    .end											   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    例子:用汇编实现if(a>=2||b>1)c=3;else c=4;假设a,b都是无符号数;其中a=0,b=2
    .text
    mov   r1,#0			@r1=0
    mov	  r2,#2			@r2=2
    cmp   r1,#2			@r1和2比较
    movcs r3,#3			@r1如果大于等于2,那么r3=3
    cmpcc r2,#1			@r1如果小于2,那么比较r2和1
    movhi r3,#4			@r2如果大于1,那么r3=4
    .end  
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    下面这个网址讲述了协处理器各种用途
    ARM体系支持16个协处理器

    https://blog.csdn.net/silent123go/article/details/53169783?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166150679616781683951306%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=166150679616781683951306&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allbaidu_landing_v2~default-1-53169783-null-null.142v42pc_rank_34_ecpm25,185v2control&utm_term=ARM%E5%8D%8F%E5%A4%84%E7%90%86%E5%99%A8%E6%9C%89%E5%93%AA%E4%BA%9B&spm=1018.2226.3001.4187


    mrs,msr指令

    msr把其他寄存器或立即数的值传递给cpsr寄存器;使用如:msr cpsr,r1
    mrs能够将cpsr寄存器的状态保存到其他寄存器;使用如: mrs r1,spsr

    swap 寄存器和存储器器之间交换(原子操作,不能由gcc编译器产生,只能由汇编器(as))

    使用方式如:swap[条件] r1,[r2] 代表将r1中的值,和r2值代表的存储器地址空间中的值做交换,[条件],可以省略,或者参照前面的条件表

    异常的优先级

    异常的优先级(级别号越低优先级越高)优先级级别号异常原因
    Rest0上电,复位
    Data Abort1访问非法内存(内存不存在或没有访问权限)
    FIQ2快中断
    IRQ3普通中断
    Prefetch Abort4地址预取指令执行时发现地址种的数据无法取出或无法访问,一般时B(分支指令跳转)触发
    swi5软件中断
    Undefine instruction6当流水线中的非法指令达到执行状态时执行

    ARM处理数据机器码的格式

    在这里插入图片描述


    软件中断(SWI)

    会产生一个异常陷阱,转跳到SWI硬件向量
    SWI处理程序可以检测SWI号,从而决定采取何种操作
    通过SWI机制,运行在用户模式下的应用程序,可以请求操作系统执行一系列特权操作。
    SWI<条件> 数字
    下面是一一个实例:创建一个异常中断向量表,同时指定一些SWI ,虽然部分异常功能并没有补全
    .text
    	ldr pc,_main		   @0x00	Rest
    	ldr pc,_undef_oder	   @0x04	Undefine Instruction
    	ldr pc,_swi_handler    @0x08	Supervisor call(svc)
    	ldr pc,_pref_abort	   @0x0c	Prefetch Abort
    	ldr pc,_data_abort	   @0x10	Data Abort
    	NOP					   @0x14	Not used
    	ldr pc,_irq			   @0x18	IRQ(interrupt)
    	ldr pc,_fiq			   @0x1c	FIQ(fast interrupt)
    _fiq:
    _main:
     	.word main		@//定义一个标签main,放在_main:标签下,并且main这个标签占4个字节 ,即_main对应代表4个字节的一个标签
    _undef_oder:
    _swi_handler:
    	.word swi_handler
    _pref_abort:
    _data_abort:
    _irq:
    
    swi_handler:
    	stmfd sp!,{r0-r12,lr}  @//保护现场,入栈
    	sub r0,lr,#4		   @ r0=lr-4  //得到swi指令的地址 
    	ldr r0,[r0]			   @ r0=*(r0) //得到swi指令地址中保存的机器码
    	bic r0,#0xFF000000	   @ //将高8位置零,就会得到低24位的数据,也就是中断号
    
    	cmp r0,#0			   @//判断终端号是否为0
    	bleq write			   @//终端号为0,则实现open函数功能
    
    	cmp r0,#1  			   @//判断中断号是否为1
    	bleq read			   @//中断号为1,实现read函数功能;在执行地址转跳的时候lr寄存器会自自动赋值为pc+0x4即lr=pc+0x4
    
    	ldmfd sp!,{r0-r12,pc}^ @//恢复现场,出栈
    
    write:
    	mov r0,#0x9			   @//r0=0x9
    	mov r1,#0x20		   @//r1=0x20
    	str r0,[r1]			   @//*(0x20)=0x9		将0x=9写入内存地址
    	mov pc,lr			   @//返回之前的位置并偏移0x4; 
    
    read:
    	mov r1,#0x20		   @//r1=0x20
    	ldr r0,[r1]			   @r0=*(0x20)		从内存地址获取数据到r0
    	mov pc,lr		 	   @//返回之前的位置并偏移0x4;
    
    main:
    
    	mov r0,#0x6				@r0=0x6
    	mov r1,#0x8				@r1=0x8
    
    	ldr sp,=stack_base		@//指定栈指针指向栈底部,也就是sp寄存器保存栈地地址	,注意这一步需要在切换到User模式之前
    	msr cpsr,#0xD0			@//从管理模式切换到User模式
    
    	swi 0					@//触发软件中断,且中断号为0
    
    	NOP						@//空指令
    	swi 1					@//触发软件中断,且中断号为0
    	NOP
    	
    .data
    buf:						@//切换到数据段
    	.space 100				@//开辟100字节空间
    stack_base:					@//栈底位置
    
    .end  
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    在产生异常的时候spsr会自动保存当前cpsr中的值,当异常结束再将spsr中的值自动赋值给cpsr


    中断处理

    ARM有两级外部中断FIQ,IRQ,但是大多数基于ARM的系统有超过2个中断源,因此需要一个中断控制器,(通常是地址映射的)来控制中断是怎么传递给ARM的;在许多系统中,一些中断的优先级比其他中断的优先级高,他们要抢先任何正在处理的优先级中断(也就是说中断可以被打断);
    FIQ的什么特点使得它处理得比IRQ更快?
    原因
    1.FIQ的处理优先级比IRQ更高,甚至可以打断正在执行的IRQ;
    2.FIQ模式有自己独有的寄存器,而IRQ需要和其他模式共用寄存器,在中断处理的保护/恢复现场会更快;
    3.在异常向量表中,FIQ处在最末尾。在异常向量表中IRQ只能保存中断处理程序的首地址,在发生IRQ时需要一次跳转;而FIQ处在最末尾,所以可以直接将FIQ模式下的中断处理程序紧接着存放,这样在处理FIQ时就少一次跳转。

    注意:(1)在之前的章节已经讲过FIQ模式有5个额外的私有寄存器(r8-r12);
    (2)在处理中断的时候应该先保护现场,即保护非私有的寄存器;

    如何禁止中断?

    cpsr 寄存器中I=1,F=1即可

    复位

    在这里插入图片描述

    为什么异常只能在arm状态下返回

    因为ARM状态完整的体系结构,可以执行异常;其他如Thumb状态不是完整的体系结构,只支持通用功能;

    杂项伪操作

    伪操作符作用
    .arm定义一下代码使用ARM指令集编译
    .thumb定义一下代码使用Thumb指令编译
    .section定义一个段,使用方式如.section expr,expr可以是.text .data .bss
    .text将定义符开始的代码编译到代码段
    .data定制符开始代码编译到数据段,初始化数据段
    .bss将数据变量放到.bss段,未初始化数据段
    .rodata只读数据段,或者称为常量区,用于存放如字符串,全局const变量(并不所有常量都在.rodata段如立即数是保存在.text段的;重复字符串会合并,程序中只保留一份;某些系统)中的.rodata段会被多个进程共享,同于提高空间利用率;另外嵌入式系统中,rodata不会加载到ARM中,而是直接在ROM中读取
    .align通过用零或指定的数据进行填充来使当前位置和指定边界对齐
    .global用来声明一个全局变量
    .org指定从当前位置加指定偏移量的地址开始存放代码,这之间的内存单元由指定的数据填充
    _start汇编程序的缺省入口是_start标志用户也可以用ENTRY标志指明其它入口
    .end文件结束

    .align的使用

    .对齐伪指令ALIGN
    对齐伪指令格局:

    ALIGN Num

    其间:Num有必要是2的幂,如:2、4、8和16等。

    伪指令的作用是:告知汇编程序,本伪指令下面的内存变量有必要从下一个能被Num整除的地址开端分配。

    假如下一个地址正好能被Num整除,那么,该伪指令不起作用,不然,汇编程序将空出若干个字节,直到下一个地址能被Num整除停止。

    .global _start
    _start:
    .text
    mov r0,#1			
    mov r1,#2
    mov r2,#3
    mov r3,#4
    b main
    buf:
    	.byte 0x88	@在这个位置占用一个字节的空间,这个空间中保存的数据是0x88
    .align(4)		@假如没有这一句,main处地址是0x11,明显不是4的倍数,无法执行后续命令,会直接报错;如果加了.aglign(4)表示会自动补充对齐,即让main处地址为0x14;
    main:
     	mov r1,#3
    	mov r1,#3
    .end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    .org的使用

    .global _start
    _start:
    .org 0x14,0x11	@指定从当前地址加偏移地址0x14开始存放代码,并且当前地址到当前地址加偏移地址0x12之间内存单元即,每个字节用0x11填充
    .text
    mov r0,#1
    mov r1,#2
    mov r2,#3
    mov r3,#4
    b main
    buf:
    	.byte 0x88
    .align(4)
    main:
     	mov r1,#3
    	mov r1,#3
    .end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    之前的map.lds内容:要使用_start就清除这个即可

    
    OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
    /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
    OUTPUT_ARCH(arm)
    ENTRY(_start)
    SECTIONS
    {
    	. = 0;
    	. = ALIGN(4);
    	.text      :
    	{
    		./arm.o(.text)
    		*(.text)
    	}
    	. = ALIGN(4);
        .rodata : 
    	{ *(.rodata) }
        . = ALIGN(4);
        .data : 
    	{ *(.data) }
        . = ALIGN(4);
        .bss :
         { *(.bss) }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    如果要使用下面的代码需要将mpa.lds的内容清空即可

    .global _start
    _start:
    .text
    	mov r0,#0
    	mov r1,#1
    	mov r2,#2
    .end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    切换到Thumb模式

    global _start
    start:
    .text
    mov r0,#0x1
    ldr r2,=thumb	   @伪指令r2=thumb
    add r1,r0,r2
    bx r1
    thumb:
    .Thumb				@等效于.code 16
    mov r0,#1
    mov r1,#2
    mov r2,#3
    .end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    或者

    global _start
    start:
    .text
    mov r0,#0x1
    ldr r2,_thumb		@不是伪指令,r2=*(_thumb)
    add r1,r0,r2
    bx r1
    _thumb:
    	.word thumb
    thumb:
    .Thumb		@等效于.code 16
    mov r0,#1
    mov r1,#2
    mov r2,#3
    .end
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    杂项伪操作

    .include

    .include “filename” 这和c语言#include"xxx"类似,在filename中可以定义一些汇编常量

    .equ

    .equ a,10 等价于C语言中的#define a 10

    .marco .endm

    在C语言中我们对于宏的操作都不会陌生 好处呢这里也就不多讲了
    提一点:有些函数 实体部分其实只有一行 这里可以进行宏代替 这样就可以减少函数的损耗
    函数的消耗对比于普通的宏来讲 主要在于进入函数的压栈 跳转 出函数的出栈 跳转
    宏没有这个过程 就等于较少了消耗

    这里的arm汇编中给出 arm汇编和gnu arm 汇编 对于宏的操作

    arm汇编  
    
        MACRO                                                                   ;宏开始
    $label HH $base,$ap,$d,$c,$b                                ;必须顶格写   声明宏名:HH   后面是他的四个参数
    
    $label                                                                          ;宏的标签
    
        DCD (\base << 20) | (\ap << 10) | \                     ;宏的实体
               (\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)
        MEND                                                                       ;宏结束的标志
    
    
    gnu  arm汇编的宏
    
    .macro HH base,ap,d,c,b                                          ;宏开始   宏名   参数
        .word (\base << 20) | (\ap << 10) | \                      ;宏实体部分
              (\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)
    .endm                                                                              ;宏结束
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    值得一提的是C中#undef 可以取消宏
    如#define M 2
    #undef M 那么M的宏定义就被取消了,而汇编中没有这个功能

    控制伪操作.if ,.else,.endif 这里的if,else用法和C差不多,而endif即是结束标志

  • 相关阅读:
    单链表(2)
    一起探秘,不可不知双向链表底层原理
    会声会影软件2023破解版最新激活序列号
    Python学习之——str/unicode/bytes
    警惕U盘、FTP等传统文件摆渡方式的7大弊端
    快速排序算法(思路分析) [数据结构][Java]
    3分钟从零解读Transformer的Encoder
    Part4_场景_第60章 阿利亚加&第61章 基于MATSim的新住户效用函数检验:以保定市为例
    掌握 Android 自动化测试框架 UiAutomator & UiAutomator2
    小程序开发的部分功能
  • 原文地址:https://blog.csdn.net/hpx12345678/article/details/126498434