文章目录
- 一、汇编版本
- 二、C语言版本
- (一)Makefile文件
- 1. `.elf`文件
- 2. `.map`文件
- 3. `wildcard`函数
- 4. `patsubst`函数
- (二)map.lds
- (三)start.S
一、汇编版本
# 工程名对应的变量
NAME=asm-led
# 交叉编译器的前缀的变量,不同的交叉编译器的前缀可能不同
CROSS_COMPILE = arm-linux-gnueabihf-
# arm-linux-gnueabihf-gcc : 编译器
CC = $(CROSS_COMPILE)gcc
# arm-linux-gnueabihf-ld : 链接器
LD = $(CROSS_COMPILE)ld
# arm-linux-gnueabihf-objcopy : 格式化拷贝命令
OBJCOPY = $(CROSS_COMPILE)objcopy
# arm-linux-gnueabihf-objdump : 反汇编命令
OBJDUMP = $(CROSS_COMPILE)objdump
# Makefile文件本质是由很多规格构成
# 规则包含:目标,依赖,命令
# 目标:依赖
# (tab键)命令
all:
@# 将.S文件编译生成.o文件
@# -O0 : 代码的优化等级,不优化
$(CC) -O0 -g -c $(NAME).S -o $(NAME).o
@# 将.o文件链接生成.elf格式文件
@# -Ttext:指定程序入口地址,参数为物理地址
$(LD) -Ttext=0xC0008000 $(NAME).o -o $(NAME).elf
@# 将.elf文件格式化拷贝生成.bin文件
$(OBJCOPY) -O binary $(NAME).elf $(NAME).bin
@# 将elf格式文件生成.dis的反汇编文件
$(OBJDUMP) -D $(NAME).elf > $(NAME).dis
clean:
rm -rf *.elf *.bin *.o *.dis
install:
sudo cp $(NAME).bin /mnt/hgfs/share/
@# 拷贝bin文件到共享文件夹中,需要修改为自己的共享文件夹的路径
二、C语言版本
(一)Makefile文件
#CROSS_COMPILE:定义了交叉编译工具链的前缀,
#这里是arm-linux-gnueabihf-,用于编译针对ARM架构的代码。
CROSS_COMPILE = arm-linux-gnueabihf-
#NAME:项目名称,可以在命令行使用NAME参数赋值进行指定
#如:make NAME=led; 注意此时下载时 make install NAME=led
NAME = interface
#=============================================================================#
#CFLAGS:定义了C编译器的标志(flags)
#添加gdb调试信息(-g)
#指定ARM架构(-marm)
#显示所有警告(-Wall)
#关闭优化(-O0)选值范围在-O0~-O2,-O2优化等级最高
#指定ABI(-mabi=apcs-gnu)
#启用NEON浮点支持(-mfpu=neon)、指定浮点ABI(-mfloat-abi=softfp)
#禁用内置函数(-fno-builtin)
#不使用标准C库(-nostdinc)
#指定头文件路径(-I)。
CFLAGS += -g -marm -Wall -O0 -mabi=apcs-gnu -mfpu=neon -mfloat-abi=softfp -fno-builtin \
-nostdinc -I./common/include -I./include
#链接命令
LD = $(CROSS_COMPILE)ld
#C编译器
CC = $(CROSS_COMPILE)gcc
#查看elf文件的符号表信息
NM = $(CROSS_COMPILE)nm
#二进制格式化拷贝
OBJCOPY = $(CROSS_COMPILE)objcopy
#反汇编工具
OBJDUMP = $(CROSS_COMPILE)objdump
#============================================================================#
#OBJSss:使用wildcard函数收集所有的.S(汇编源文件)和.c(C源文件)文件,这些文件来自不同的目录(start/、common/src/、src/以及当前目录)。
OBJSss := $(wildcard start/*.S) $(wildcard common/src/*.S) $(wildcard *.S)\
$(wildcard start/*.c) $(wildcard common/src/*.c) \
$(wildcard src/*.c) $(wildcard *.c)
#OBJSs:将.S文件(汇编源文件)通过patsubst函数转换成.o(目标文件)的形式。
#patsubst 函数被用来将.S 文件(汇编源文件)的列表转换成对应的 .o 文件(目标文件)的列表
OBJSs := $(patsubst %.S,%.o,$(OBJSss))
#OBJS:将OBJSs变量中所有的.c文件(C源文件)也转换成.o(目标文件)的形式。
OBJS := $(patsubst %.c,%.o,$(OBJSs))
#============================================================================#
#$@:所有的目标
#$<:第一个依赖文件
%.o: %.S
@echo " AS $@"
@$(CC) $(CFLAGS) -c -o $@ $<
%.o: %.c
@echo " CC $@"
@$(CC) $(CFLAGS) -c -o $@ $<
#目标all依赖于规则clean和$(OBJ)的文件,因此当执行make时,会先执行clean目标,然后编译所有源文件生成.o文件
all:clean $(OBJS)
#使用链接器$(LD)链接这些.o文件生成.elf文件
#-T map.lds 按照map.lds链接脚本文件进行链接
@echo " LD Linking $(NAME).elf"
@$(LD) $(OBJS) -T map.lds -o $(NAME).elf
#接着使用$(OBJCOPY)将.elf文件转换成.bin文件
@echo " OBJCOPY Objcopying $(NAME).bin"
@$(OBJCOPY) -O binary $(NAME).elf $(NAME).bin
#使用$(NM)生成符号表(.map文件)
@echo " MAP Generating $(NAME).map"
@$(NM) $(NAME).elf > $(NAME).map
#最后使用$(OBJDUMP)生成反汇编代码(.dis文件)
@echo " OBJDUMP Objdumping $(NAME).dis"
@$(OBJDUMP) -DS $(NAME).elf > $(NAME).dis
#distclean和clean:一个规则可以有多个目标,这两个目标有相同的依赖文件,
#它们都会删除所有的.o文件、.elf文件、.bin文件、.dis文件和.map文件,并打印“CLEAN complete.”消息。
distclean clean:
@rm -rf $(OBJS) *.elf *.bin *.dis *.map
@echo " CLEAN complete."
install:
sudo cp $(NAME).bin /mnt/hgfs/Desktop/
执行make时的输出:
1. .elf
文件
.elf文件,Executable and Linkable Format(可执行和可链接格式)的缩写,是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储等文件的格式。ELF文件主要用于Linux平台,是Linux下目标文件和可执行文件的标准格式。
objcopy 在将 .elf 文件转换成 .bin 文件时,主要执行的操作是提取 .elf 文件中的二进制代码段(即机器码),并将这些代码段直接写入到 .bin 文件中,而不包含 .elf 文件中的其他部分,如符号表、重定位信息、调试信息等。
2. .map
文件
.map文件提供关于编译后程序(如可执行文件或库)的符号表、内存布局、段分配等详细信息的视图,是开发者在编译和调试过程中非常有用的工具,它提供了关于程序内存布局和符号表的详细信息,有助于开发者理解程序的行为、查找问题并进行优化。
3. wildcard
函数
objects := $(wildcard *.o)
让 objects 的值是所有[.o]的文件名的集合
wildcard函数支持在不同路径下搜索文件,并将搜索到的所有符合条件的值的集合返回
4. patsubst
函数
$(patsubst <pattern>,<replacement>,<text>)
- 名称:模式字符串替换函数----patsubst。
- 功能:查找
<text>
中的单词(单词以“空格”、“Tab”或“回车”“换行”分隔)是否符
合模式<pattern>
,如果匹配的话,则以<replacement>
替换。这里,<pattern>
可以包括通配符“%
”,表示任意长度的字串。如果<replacement>
中也包含“%”,那么,<replacement>
中的这个“%
”将是<pattern>
中的那个“%”所代表的字串。 (可以用“\”来转义,以“\%
”来表示真实含义的“%
”字符) - 返回:函数返回被替换过后的字符串。
OBJS := $(patsubst %.c,%.o,$(OBJSs))
此处patsubst函数会将$(OBJSs)
中所有以.c为后缀的文件名的字符串替换为.o为后缀的文件名的字符串。另一句同理
执行完成后OBJS变量中存放的就是所有.c和.S文件更改为.o后缀的文件名的字符串
(二)map.lds
链接脚本(Linker Script)是用于指定如何将程序的各个部分(如代码、数据等)放置到内存中的。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/
/*OUTPUT_FORMAT:指定输出文件的格式。这里设置为elf32-littlearm,表示输出文件为32位的小端ARM ELF格式*/
OUTPUT_ARCH(arm)
/*OUTPUT_ARCH:指定输出文件的架构,这里是arm。*/
ENTRY(_start)
/*ENTRY:指定程序的入口点,这里是_start,这通常是C语言程序的第一个执行指令所在的位置。*/
SECTIONS /*SECTIONS部分定义了如何将程序的各个段(section)映射到内存中。*/
{
. = 0xc0008000; /*程序的入口地址*/
/*. = 0xc0008000;:设置当前位置(.)为0xc0008000,这通常是程序的加载地址,也是.text段的开始地址。*/
. = ALIGN(4); /*对齐*/
/*. = ALIGN(4);:确保接下来的段从4字节对齐的地址开始。这是为了确保符合ARM架构的对齐要求,提高访问效率。*/
.text : /*.text段包含了程序的可执行代码,代码段*/
{
start/start.o(.text)
/*第一个.o文件,start/start.o(.text):特别指定了start.o文件中的.text段应该首先被包含进来。这通常用于确保启动代码(如设置堆栈、初始化硬件等)首先被执行。*/
*(.text) /**(.text):随后包含所有其他.o文件中的.text段。具体位置,由编译器决定*/
}
. = ALIGN(4);
.rodata : /*只读数据端,.rodata段包含了只读数据,如字符串常量等。*/
{ *(.rodata) } /* *(.rodata):包含所有.o文件中的.rodata段。 */
. = ALIGN(4);
.data : /*初始化的全局变量,.data段包含了已初始化的全局变量。
*(.data):包含所有.o文件中的.data段。*/
{ *(.data) }
. = ALIGN(4);
__bss_start = .; /*__bss_start = . 定义了一个符号__bss_start,其值为.bss段的开始地址。这可以用于在程序运行时定位.bss段的起始位置。*/
.bss : /*.bss段用于存储未初始化的全局变量。在内存中,.bss段通常不占用实际的空间(即不存储具体的值),但在加载时,会为其分配空间并清零。*/
{ *(.bss) } /**(.bss):包含所有.o文件中的.bss段。*/
__bss_end__ = .; /*__bss_end__ = . 定义了另一个符号__bss_end__,其值为.bss段的结束地址。这可以用于计算.bss段的大小或在程序运行时进行其他操作。*/
}
(三)start.S
.text /*这是一个汇编指令,用于指示接下来的代码段应该被放置在程序的文本(即代码)段中。在链接过程中,.text段通常会被放置在可执行文件的开始部分。*/
.global _start /*这条指令声明了一个全局符号_start,这是程序的入口点。在链接时,链接器会查找这个符号,并将其地址设置为程序执行时的初始PC(程序计数器)值。*/
_start: /*这是程序的入口点标签。*/
@ 异常向量表
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction:
.word undefined_instruction
_software_interrupt:
.word software_interrupt
_prefetch_abort:
.word prefetch_abort
_data_abort:
.word data_abort
_not_used:
.word not_used
_irq:
.word irq
_fiq:
.word fiq
/*这些标签后面跟随的.word指令用于定义这些标签对应的内存地址中存储的值,即异常处理程序的地址。例如,_undefined_instruction: .word undefined_instruction表示在_undefined_instruction标签对应的内存地址中存储undefined_instruction(一个标签或地址,指向未定义指令异常的处理程序)的地址。*/
/* The actual reset code */
reset:
@ 重新映射异常向量表的入口地址
/* Set Vector Base Address Register */
mrc p15, 0, r0, c1, c0, 0
bic r0, #(1<<13)
mcr p15, 0, r0, c1, c0, 0
ldr r0,=0xc0008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register
/* Set the cpu to svc32 mode */
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr, r0
/* Enable NEON/VFP unit */
mrc p15, #0, r1, c1, c0, #2
orr r1, r1, #(0xf << 20)
mcr p15, #0, r1, c1, c0, #2
mov r1, #0
mcr p15, #0, r1, c7, c5, #4
mov r0, #0x40000000
fmxr fpexc, r0
/* Cache init */
mrc p15, 0, r0, c0, c0, 0
and r1, r0, #0x00f00000
and r2, r0, #0x0000000f
orr r2, r2, r1, lsr #20-4
cmp r2, #0x30
mrceq p15, 0, r0, c1, c0, 1
orreq r0, r0, #0x6
mcreq p15, 0, r0, c1, c0, 1
/* Invalidate L1 I/D */
mov r0, #0
mcr p15, 0, r0, c8, c7, 0
mcr p15, 0, r0, c7, c5, 0
/* Disable mmu stuff and caches */
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000
bic r0, r0, #0x00000007
orr r0, r0, #0x00001000
orr r0, r0, #0x00000002
orr r0, r0, #0x00000800
mcr p15, 0, r0, c1, c0, 0
/* Initialize stacks */
@ 初始化各种模式下的占空间
init_stack:
ldr r0, stacktop /*get stack top pointer*/
/********svc mode stack********/
mov sp, r0
sub r0, #128*4 /*512 byte for irq mode of stack*/
/********irq mode stack********/
msr cpsr, #0xd2
mov sp, r0
sub r0, #128*4 /*512 byte for fiq mode of stack*/
/********fiq mode stack********/
msr cpsr, #0xd1
mov sp, r0
sub r0, #0
/********abort mode stack******/
msr cpsr, #0xd7
mov sp, r0
sub r0, #0
/********undefine mode stack**/
msr cpsr, #0xdb
mov sp, r0
sub r0, #0
/***sys mode and usr mode stack***/
msr cpsr, #0x10
mov sp, r0 /*1024 byte for user mode of stack*/
/******clear bss section********/
@ 清除BSS段
ldr r0, =__bss_start /* this is auto-relocated! */
ldr r1, =__bss_end__ /* this is auto-relocated! */
mov r2, #0x00000000 /* prepare zero to clear BSS */
clbss_l:
cmp r0, r1 /* while not at end of BSS */
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l
/* Call _main */
ldr pc, =main @ 汇编调用C 跳转到main.c文件的main函数中
/*
* Exception handlers
*/
.align 5 // 2的5次方,=32bit 也就是4字节对其
undefined_instruction:
b .
.align 5
software_interrupt:
b .
.align 5
prefetch_abort:
b .
.align 5
data_abort:
b .
.align 5
not_used:
b .
.align 5
.global irq
irq:
sub lr, lr, #4
stmfd sp!, {r0-r12, lr}
bl do_irq
ldmfd sp!, {r0-r12, pc}^
.align 5
.global fiq
fiq:
b .
stacktop: .word stack + 4 * 512
.data
stack: .space 4 * 512