makefile使用.lds链接脚本以及 $@ ,$^, $,< 解析

先来分析一个简单的.lds链接脚本

例1,假如现在有head.c init.c nand.c main.c这4个文件:

1.1 首先创建链接脚本nand.lds:

1 SECTIONS { 
2   firtst      0x00000000 : { head.o init.o nand.o}
3   second     0x30000000 : AT(4096) { main.o }
4 } 

SECTIONS { ... }                  用来描述输出文件的内存布局。

这个脚本里规定了两个段,firtst和cecond

0x00000000   0x30000000   

表示链接地址或运行地址,指程序在SRAM、SDRAM实际运行的地址,也就是使PC等于这个地址。

这里指head.o init.o nand.o的加载地址为0,运行地址在0x00000000,main.o运行地址在0x30000000

AT(4096)    

表示加载地址或存储地址,指程序编译后存放的地址,一般存在ROM、FLASH中,也就是运行这个指令时,会先将4096地址~(4096+2048)地址处的内容复制到0x30000000处运行(因为已经初始化了SDRAM以及Nand Flash)。

这里指main.o的加载地址为Nand Flash里的地址4096,运行地址在SDRAM里的地址  0x30000000。

1.2 制作Makefile

objs := head.o init.o nand.o main.o

nand.bin : $(objs)  
    arm-linux-ld -Tnand.lds    -o nand_elf $^
    arm-linux-objcopy -O binary -S nand_elf $@
    arm-linux-objdump -D -m arm  nand_elf > nand.dis

%.o:%.c
    arm-linux-gcc -Wall -c -O2 -o $@ $<

%.o:%.S
    arm-linux-gcc -Wall -c -O2 -o $@ $<

clean:
    rm -f  nand.dis nand.bin nand_elf *.o

 其中 objs 是代表的一个变量,表示obj文件,也可以是objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,后面就可以使用$(objs)来使用这个变量了。

$@               目标文件

$^                 所有的依赖文件

$<                 第一个依赖文件

例如: arm-linux-ld -Tnand.lds -o nand_elf $^          <<——  等价于  ——>>    arm-linux-ld  -o nand_elf head.o init.o nand.o main.o 

%.o:%.c                  表示所有的.o文件,依赖于对应的.c文件

%.o:%.S                  表示所有的.o文件,依赖于对应的.S文件

当有多个.o文件时,这时候.lds链接脚本 又该如何安排它们在可执行文件中的顺序?

这里就需要将多个目标文件的.text、.data和.bss等段链接在一起而链接脚本文件是告诉链接器从什么地址开始放置这些段

  • .text:代码段,存放程序执行代码的一块内存
  • .data:读/写数据段,存放已初始的全局变量或静态变量的一块内存
  • .rodata:只读数据段,存放只读数据段,比如全局const变量和#define定义的变量
  • .bss:存放未初始化的全局变量或静态变量,这里的变量存放只是用来预留位置,并不占用空间

常用命令:

ENTRY(SYMBOL);将SYMBOL的值设置成入口地址。一般设置为_start。

OUTPUT(FILENAME); 定义输出文件的名字。可以用它来指定默认的输出文件名称。当然我们一般都用手动-o进行指定,如果我们没有进行手动指定的话,输出文件名称就以这个FILENAME为输出文件名。

STARTUP(filename);指定filename为第一个输入文件。

OUTPUT_FORMAT(default, big, little);定义3种输出文件的格式。若有命令行选项-EB(大端),则使用第二个输出格式,有命令行指定-EL(小端),则使用第三个格式。否则使用默认的default输出格式。

OUT_ARCH(arch);设置输出文件的体系架构

 SECTIONS :最重要的,最基本的,也是最主要的命令,它告诉链接器如何把输入文件的各个section输出到目标文件中的各个section中去。

例2:分析 board/100ask24x0/u-boot.lds链接脚本

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段的结束位置
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Laoqi's Linux运维专列

python3–内置模块

3466
来自专栏Flutter入门到实战

开发工具总结(6)之Android Studio模板配置详解(提高开发效率必备技能)

版权声明:本文为博主原创文章(部分引用他人博文,已加上引用说明),未经博主允许不得转载。https://www.jianshu.com/p/1fe87050c1...

1092
来自专栏斑斓

PySpark分析二进制文件

客户需求 客户希望通过spark来分析二进制文件中0和1的数量以及占比。如果要分析的是目录,则针对目录下的每个文件单独进行分析。分析后的结果保存与被分析文件同名...

3384
来自专栏Golang语言社区

GO语言文件的创建与打开实例分析

文件操作是个很重要的话题,使用也非常频繁,熟悉如何操作文件是必不可少的。Golang 对文件的支持是在 os package 里,具体操作都封装在 type F...

2874
来自专栏从零开始学自动化测试

python笔记26-命令行传参sys.argv

平常我们在用别人写好的python包的时候,在cmd输入xx -h就能查看到帮助信息,输入xx -p 8080就能把参数传入程序里,看起来非常酷。 本篇就来讲下...

1034
来自专栏LIN_ZONE

谷歌断点调试(转载)

简单地说,断点调试是指自己在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,...

884
来自专栏用户2442861的专栏

windows下mongodb安装与使用整理

3.创建文件夹d:\mongodb\data\db、d:\mongodb\data\log,分别用来安装db和日志文件,在log文件夹下创建一个日志文件Mong...

642
来自专栏游戏杂谈

jscript调用bat注意事项

开发的游戏项目,需要一个工具,对指定的资源进行复制、加密,然后打包。之前打包时都手工操作,复制与加密这二步分别写了几个工具(lua加密与图片资源加密是分开的),...

693
来自专栏LinXunFeng的专栏

解决Instruments检测内存泄漏时真机无法定位的问题

1043
来自专栏安富莱嵌入式技术分享

【RL-TCPnet网络教程】第40章 RL-TCPnet之TFTP客户端(精简版)

本章节为大家讲解RL-TCPnet的TFTP客户端应用,学习本章节前,务必要优先学习第38章的TFTP基础知识。有了这些基础知识之后,再搞本章节会有事半功倍的效...

411

扫码关注云+社区