一、MDK和/GNU伪指令差别
我们在学习培训汇编代码的情况下历经会见到下列二种设计风格的编码:
gnu编码开头是:
- .global _start
- _start: @选编通道
- ldr sp,=0x41000000
- .end @汇编程序完毕
MDK编码开头是:
- AREA Example,CODE,READONLY ;申明代码段Example
- ENTRY ;程序流程通道
- Start
- MOV R0,#0
- OVER
- END
这二种设计风格的编码是要应用不一样的c语言编译器,大家以前的案例编码全是MDK设计风格的。
那么多针对大家新手而言要学习培训哪样设计风格呢?回答是毫无疑问的,学习培训GNU设计风格的汇编代码,由于做Linux驱动开发务必把握的linux核心、uboot,而这两个手机软件便是GNU设计风格的。
为了更好地大伙儿不必把过少活力消耗在临时不起作用的专业知识上,下边大家只讲GNU设计风格选编。
二、GNU选编写法:
1. 编码行中的注释符号:
‘@’ 整行注释符号: ‘#’ 句子分离出来标记:
立即操作数作为前缀: ‘#’ 或 ‘$’
2. 全局性型号:
型号只有由a~z,A~Z,0~9,“.”,_等(由点、英文字母、数据、下横线等构成,除部分型号外,不可以以数据开始)标识符构成,型号的后边加“:”。
段内型号的详细地址值在选编时明确;段外型号的详细地址值在联接时明确。
3. 部分型号:
部分型号关键在部分范畴内应用并且部分型号能够反复出現。它由两台构成开头是一个0-99立即的数据部分型号 后边加“:”
- F:标示c语言编译器只向前搜索,编码个数提升的方位 / 编码的下一句
- B:标示c语言编译器只向后检索,编码个数减少的方位
留意部分型号的自动跳转,就远原则「举例说明:」
- 文档部位
- arch/arm/kernel/entry-armv.S
三、伪实际操作:
1. 标记界定伪指令
2. 数据信息界定(Data Definition)伪实际操作
数据信息界定伪实际操作一般用以为特殊的数据信息分派数据存储器,另外可进行已分派数据存储器的复位。普遍的数据信息界定伪实际操作有以下几类:
【举例说明】
.word
- val: .word 0x11223344
- mov r1,#val ;将值0x11223344设定到存储器r1中
.space
- label: .space size,expr ;expr能够是4字节之内的浮点数
- a: space 8, 0x1
.rept
- .rept cnt ;cnt是反复频次
- .endr
留意:
3. if挑选
句法结构
- .if logical-expressing
- ……
- .else
- ……
- .endif
相近c语言里的条件编译 。
【举例说明】
- .if val2==1
- mov r1,#val2
- .endif
4. macro宏定义.
macro,.endm 宏定义相近c语言里的宏涵数 。
macro伪实际操作能够将一段编码界定为一个总体,称之为宏命令。随后就可以在程序流程中根据宏命令数次启用此段编码。
英语的语法文件格式:
- .macro {$label} 姓名{$parameter{,$parameter}…}
- ……..code
- .endm
在其中,$型号在宏命令被进行时,型号会被更换为客户界定的标记。
宏实际操作能够应用一个或好几个主要参数,当宏实际操作被进行时,这种主要参数被相对的值更换。
「留意」:先界定后应用
举例说明:
「【例1】:沒有主要参数的宏完成子函数回到」
- .macro MOV_PC_LR
- MOV PC,LR
- .endm
- 启用方法以下:
- MOV_PC_LR
「【例2】:带主要参数宏完成子函数回到」
- .macro MOV_PC_LR ,param
- mov r1,\param
- MOV PC,LR
- .endm
启用方式以下:
- MOV_PC_LR #12
四、杂类伪实际操作
举例说明:.set
- .set start, 0x40
- mov r1, #start ;r1里边是0x40
举例说明 .equ
- .equ start, 0x40
- mov r1, #start ;r1里边是0x40
- #define PI 3.1415
等额的于
- .equ PI, 31415
五、GNU伪指令
关键环节:伪指令在编译程序的时候会转换为相匹配的ARM命令
1.ADR伪指令 :该命令把标识所属的详细地址载入到存储器中。ADR伪指令为小范畴详细地址载入伪指令,应用的相对性偏位范畴:当详细地址值是字节对齐 (8位) 时,取值范围为-255~255,当详细地址值是字两端对齐 (32位系统) 时,取值范围为-1020~1020。英语的语法文件格式:
- ADR{cond} register,label
- R R0, lable
2.ADRL伪指令:将中等水平范畴详细地址载入到存储器中
ADRL伪指令为中等水平范畴详细地址载入伪指令。应用相对性偏位范畴:当详细地址值是字节对齐时,取值范围为-64~64KB;当详细地址值是字两端对齐时,取值范围为-256~256KB
英语的语法文件格式:
- ADRL{cond} register,label
- ADRL R0,lable
3.LDR伪指令: LDR伪指令装车一个32位系统的参量和一个详细地址到存储器。英语的语法文件格式:
- LDR{cond} register,=[expr|label-expr]
- LDR R0,=0XFFFF0000 ;mov r1,#0x12 比照一下
留意:(1)ldr伪指令和ldr命令区别 下边是ldr伪指令:
- ldr r1,=val @ r1 = val 是伪指令,将val型号详细地址赋给r1
- 【与MDK不一样,MDK只适用ldr r1,=val】
下边是ldr命令:
- ldr r2,val @ r1 = *val 是arm命令,将型号val详细地址里的內容给r2
- val: .word 0x11223344
(2)怎样运用ldr伪指令完成长自动跳转
- ldr pc,=32位系统详细地址
(3)编号中处理非立即数的难题 用arm伪指令ldr
- ldr r0,=0x999 ;0x999 并不是立即数,
六、GNU选编的编译程序
1. 没有lds文档的编译程序
假定大家有下列编码,包含一个main.c文档,一个start.s文件:start.s
- .global _start
- _start: @选编通道
- ldr sp,=0x41000000
- b main
- .global mystrcopy
- .text
- mystrcopy: //主要参数dest->r0,src->r2
- LDRB r2, [r1], #1
- STRB r2, [r0], #1
- CMP r2, #0 //分辨是否字符串数组尾
- BNE mystrcopy
- MOV pc, lr
- stop:
- b stop @无限循环,避免 跑飞 等额的于while(1)
- .end @汇编程序完毕
main.c
- extern void mystrcopy(char *d,const char *s);
- int main(void)
- {
- const char *src ="yikoulinux";
- char dest[20]={};
- mystrcopy(dest,src);//启用选编完成的mystrcopy涵数
- while(1);
- return 0;
- }
Makefile撰写方式以下:
- 1. TARGET=start
- 2. TARGETC=main
- 3. all:
- 4. arm-none-linux-gnueabi-gcc -O0 -g -c -o $(TARGETC).o $(TARGETC).c
- 5. arm-none-linux-gnueabi-gcc -O0 -g -c -o $(TARGET).o $(TARGET).s
- 6. #arm-none-linux-gnueabi-gcc -O0 -g -S -o $(TARGETC).s $(TARGETC).c
- 7. arm-none-linux-gnueabi-ld $(TARGETC).o $(TARGET).o -Ttext 0x40008000 -o $(TARGET).elf
- 8. arm-none-linux-gnueabi-objcopy -O binary -S $(TARGET).elf $(TARGET).bin
- 9. clean:
- 10. rm -rf *.o *.elf *.dis *.bin
Makefile含意以下:
【填补】
2. 依靠lds文档编译程序
具体的工程文件,段复杂性远比大家这一要繁杂的多,特别是在Linux核心有上万个文档,段的遍布以及繁杂,因此 这就必须大家依靠lds文档来界定运行内存的遍布。
文档目录
main.c和start.s和上一节一致。
map.lds
- OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
- /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
- OUTPUT_ARCH(arm)
- ENTRY(_start)
- SECTIONS
- {
- . = 0x40008000;
- . = ALIGN(4);
- .text :
- {
- .start.o(.text)
- *(.text)
- }
- . = ALIGN(4);
- .rodata :
- { *(.rodata) }
- . = ALIGN(4);
- .data :
- { *(.data) }
- . = ALIGN(4);
- .bss :
- { *(.bss) }
- }
解释一下所述的事例:
射频连接器每念完一个section叙述后, 将定位仪标记的值提升该section的尺寸。
看来下,Makefile应当要怎么写:
- # CORTEX-A9 PERI DRIVER CODE
- # VERSION 1.0
- # ATHUOR 一口Linux
- # MODIFY DATE
- # 2020.11.17 Makefile
- #=================================================#
- CROSS_COMPILE = arm-none-linux-gnueabi-
- NAME =start
- CFLAGS=-mfloat-abi=softfp -mfpu=vfpv3 -mabi=apcs-gnu -fno-builtin -fno-builtin-function -g -O0 -c
- LD = $(CROSS_COMPILE)ld
- CC = $(CROSS_COMPILE)gcc
- OBJCOPY = $(CROSS_COMPILE)objcopy
- OBJDUMP = $(CROSS_COMPILE)objdump
- OBJS=start.o main.o
- #================================================#
- all: $(OBJS)
- $(LD) $(OBJS) -T map.lds -o $(NAME).elf
- $(OBJCOPY) -O binary $(NAME).elf $(NAME).bin
- $(OBJDUMP) -D $(NAME).elf > $(NAME).dis
- %.o: %.S
- $(CC) $(CFLAGS) -c -o $@ $<
- %.o: %.s
- $(CC) $(CFLAGS) -c -o $@ $<
- %.o: %.c
- $(CC) $(CFLAGS) -c -o $@ $<
- clean:
- rm -rf $(OBJS) *.elf *.bin *.dis *.o
编译程序結果以下:
编译程序結果
最后形成start.bin,改文档能够烧录到单片机开发板检测,由于本例沒有形象化状况,事后文章内容大家添加其他作用再检测。
【留意】
exynos4412 详细地址遍布
阅读者能够依据自身手上的单片机开发板相匹配的soc指南搜索该详细地址。
linux核心的出现异常空间向量表
linux核心的运行内存遍布也是依靠lds文档界定的,linux核心的编译程序大家暂不探讨,编译程序好以后会再下列部位形成相匹配的lds文档:
- arch/arm/kernel/vmlinux.lds
大家看下该文件的一部分內容:
vmlinux.lds
OUTPUT_ARCH(arm)制订相匹配的CPU;
ENTRY(stext)表明程序流程的通道是stext。
另外大家还可以见到linux运行内存的区划更为的繁杂,事后大家探讨linux核心,再再次剖析该文件。
3. elf文件和bin文档差别:
1) ELF
ELF格式文件是一个对外开放规范,各种各样UNIX系统软件的可执行程序都选用ELF文件格式,它有三种不一样的种类:
ELF文件格式出示了二种不一样的角度,连接器把ELF文档当做是Section的结合,而加载器把ELF文档当做是Segment的结合。
2) bin
BIN文件是立即的二进制文件,內部沒有详细地址标识。bin文档內部数据信息依照代码段或是数据信息段的物理学室内空间详细地址来排序。一般用开发板烧录时从00刚开始,而假如免费下载运作,则免费下载到编译程序时的详细地址就可以。
在Linux OS上,为了更好地运作可执行程序,她们是遵照ELF文件格式的,一般gcc -o test test.c,形成的test文档便是ELF文件格式的,那样就可以运作了,实行elf文件,则核心会应用加载器来分析elf文件并实行。
在Embedded中,假如通电运行,沒有OS系统软件,假如将ELF文件格式的文档烧写进去,包括一些ELF文档的符号表标识符表这类的section,运作遇到这种,便会造成 不成功,假如用objcopy形成纯碎的二进制文件,去祛除符号表这类的section,只将代码段数据信息段保存出来,程序流程就可以一步一步运作。
elf文件里边包括了符号表等。BIN文件是将elf文件中的代码段,数据信息段,也有一些自定的段提取出去制成的一个运行内存的镜像系统。
而且elf文件中代码段数据信息段的部位并并不是它具体的物理学部位。他具体物理学部位是在表格中标识出去的。