近期在翻看嵌入式应用开发完全手册中MMU一章中,有一段Makefile代码中使用了lds文件,其Makefile和lds脚本代码分别如下:
objs := head.o init.o leds.o
mmu_bin:$(objs)
arm-linux-ld -Tmmu.lds -o mmu_elf $^
arm-linux-objcopy -O binary -S mmu_elf $@
arm-linux-objdump -D -m arm mmu_elf > mmu_dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f mmu_bin mmu_elf mmu_dis *.o
SECTIONS{
first 0x0000000 : {head.o init.o}
second 0xB0004000 : AT(2048) { leds.o }
}
其中Makefile的语法主要是几个自动变量,$@表示规则的目标文件,$^ 表示所有依赖的名字,名字之间用空格隔开,$< 表示第一个依赖的文件名,% 是通配符,可以和一个字符串中任意个数的字符相匹配。Makefile的语法准备总结下写个博文方便下次复习。
lds脚本的分析有篇博文跟我这个很相似,我就改改拿来用了。
SECTIONS { ... } 用来描述输出文件的内存布局。
这个脚本里规定了两个段,firtst和cecond
0x00000000 0xB0004000
表示链接地址或运行地址,指程序在SRAM、SDRAM实际运行的地址,也就是使PC等于这个地址。
这里指head.o init.o 的加载地址为0,运行地址在0x00000000,leds.o运行地址在0xb0004000
AT(2048)
表示加载地址或存储地址,指程序编译后存放的地址,一般存在ROM、FLASH中,也就是运行这个指令时,会先将2048地址~(2048+2048)地址处的内容复制到0xb0004000处运行(因为已经初始化了SDRAM以及Nand Flash)。
这里指main.o的加载地址为Nand Flash里的地址2048,运行地址在SDRAM里的地址0xb0004000。
知其然也要知其所以然,所以总结下lds脚本的语法还是很有必要的。
有不少博客都有其详解,以下是我觉得不错的:
https://www.cnblogs.com/li-hao/p/4107964.html
http://blog.chinaunix.net/uid-28732854-id-3899215.html
第一篇博文太过详细,简直无法下手,第二篇很简略,说出本质
一个可执行程序通常是由:代码段,数据段,bss段构成的。同样,在用于链接这个程序的链接器脚本中,就会反应出这几个段的信息。
1>创建链接器脚本-段信息
2>设置起始链接地址
3>对齐设置
4>使用变量
5>设置代码段首文件
下面两段代码是从上面两篇博文中摘取下来的,帮助理解:
SECTIONS {
. = 0x50008000 //设置起始链接地址
. = ALIGN(4); //字节对齐设置
.text :
{
start.o (.text) /*设置代码段的首文件*/
*(.text) //所有文件的代码段
}
. = ALIGN(4); //字节对齐设置
.data :
{
*(.data) //所有文件的数据段
}
. = ALIGN(4); //字节对齐设置
bss_start = . ;//变量的使用
.bss :
{
*(.bss) //所有文件bss段
}
bss_end = .; //变量的使用
}
OUTPUT_ARCH(arm) //设置输出文件的体系架构。
ENTRY(_start) //将_start这个全局符号设置成入口地址。
SECTIONS //输出文件内容布局
{
. = 0x00000000; //指定地址0x00000000
. = ALIGN(4); //代码以4字节对齐
.text : //指定.text section段(位于0x00000000)
{
cpu/arm920t/start.o (.text) //添加第一个目标文件: cpu/arm920t/start.o里面的.text代码段
board/100ask24x0/boot_init.o (.text) //添加第二个目标文件: board/100ask24x0/boot_init.o里面的.text代码段
*(.text) // *(.text) 表示添加剩下的全部文件的.text代码段
}
. = ALIGN(4);
.rodata : { *(.rodata) } //指定.rodata section段(位于0x00000000+.text section),将所有的.rodata只读数据段合并成一个.rodata只读数据段
. = ALIGN(4);
.data : { *(.data) } //指定读写数据段, *(data):添加所有文件的数据段
. = ALIGN(4);
.got : { *(.got) } //指定got段,got段是uboot自定义的一个段
. = .;
__u_boot_cmd_start = .; //把__u_boot_cmd_start赋值为当前位置, 即起始位置
.u_boot_cmd : { *(.u_boot_cmd) } // u_boot_cmd段,所有的u-boot命令相关的定义都放在这个位置
__u_boot_cmd_end = .; // u_boot_cmd段结束位置
. = ALIGN(4);
__bss_start = .; //把__bss_start赋值为当前位置,即bss段的开始位置
.bss : { *(.bss) } //指定bss段,这里NOLOAD的意思是这段不需装载,仅在执行域中才会有这段
_end = .; //把_end赋值为当前位置,即bss段的结束位置
}