正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-8.2-链接脚本

前言:

本文是根据哔哩哔哩网站上"正点原子[第二期]Linux之ARM(MX6U)裸机篇"视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。

引用:

正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com

《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》第 8.1 章

《正点原子资料_A盘/02开发板原理图/IMX6ULL_MINI_V2.2(Mini底板原理图).pdf》

正点原子资料下载中心 --- 正点原子资料下载中心 1.0.0 文档

资料盘开发板资料链接: https://pan.baidu.com/s/1j5Jzbdx9i-g0cWIi3wf2XA提取码:ag1u

正文:

本文是 "正点原子[第二期]Linux之ARM(MX6U)裸机篇--第8.2讲" 的读书笔记。第8.2 介绍了编译器连接脚本 .lds 文件的语法。并给出了一个例子,如何使用链接脚本来实现我们上一节C语言 LED 驱动程序实验里指定 start.o main.o 的文件链接顺序。

0. 链接脚本简介

在上一节C语言LED灯驱动实验的 Makefile 里,我们链接代码时使用了如下语句

bash 复制代码
arm-linux-gnueabihf-ld -Ttext 0x87800000 -o led.elf $^

上面语句是通过 "-Ttext" 来指定连接地址是 0x87800000 的,这样的话所有文件都会链接到以0x87800000 为起始地址的区域。但是有时候我们很多文件需要连接到指定的区域,或者叫做段里面,比如在 Linux 里面初始化函数都会放到 .init 段里面。因此我们需要能够自定义一些段,这些段的起始地址我们可以自由指定,同样的我们也可以指定一个文件或者函数应该放在哪个段里面去。要完成这个功能我们就需要使用到链接脚本,看名字就知道链接脚本主要用于链接的,用于描述文件应该如何被链接在一起形成最终可执行文件。其主要目的是描述输入文件中的段如何被映射到输出文件中,并且控制输出文件中的内存排布。比如,我们编译生成的文件一般都包含 .txt 段,.data 段等等。

1. 链接脚本的语法

链接脚本的语法很简单,就是编写一系列的命令,这行命令组成了链接脚本,每个命令是一个带有参数的关键字或者一个对符号的赋值,可以使用分号分隔命令。像文件名之类的字符串可以直接键入,也可以使用通配符"*"。最简单的链接脚本可以值包含一个命令 "SECTIONS",我们可以在这一个 "SECTIONS" 里面描述输出文件的内存布局。我们一般编译出来的代码都包含 .text, .data, .bss, .rodata 这四个段,假设现在代码要被链接到 0x10000000 这个地址,数据要被链接到 0x30000000 这个地方,下面就是完成此功能的最简单的链接脚本:

bash 复制代码
SECTIONS {
    . = 0x10000000;
    .text : { *(.text) }
    . = 0x30000000;
    .data ALIGN(4) : { *(.data) }
    .bss ALIGN(4) : { *(.bss) }
}

第一行写了一个关键字 "SECTIONS" ,后面跟了一个大括号,这个大括号和第七行的大括号是一对,这是必须得。看起来就跟C语言里面的函数一样。

第二行对一个特殊的符号 "." 进行赋值,"." 在链接脚本里叫做定位计数器,默认的定位计数器为0。我们要求代码链接到 0x10000000 位起始位置的地方,因此这一行给 "." 赋值 0x10000000 ,表示以 0x10000000 开始,后面的文件或者段都会以 0x10000000 为起始地址开始链接。

第3行的 ".text" 是段名,后面的冒号是语法要求,冒号后面的大括号里面可以填上要链接到 ".text" 这个段里的所有文件,"*(.text)" 中的 "*" 是通配符,表示所有输入文件的 .text 段都放到 ".text" 中。

第四行,我们的要求是将数据放到 0x30000000 开始的地方,所以我们需要重新设置定位计数器,将其改为 0x30000000。如果不重新设置的话会怎样?假设".text"段的大小为 0x10000,那么接下来的 .data 段的起始几十就是 0x10000000 + 0x10000 = 0x10010000,者明显不符合我们的要求。所以我们必须调整定位计数器为 0x30000000。

第五行跟第三行一行,定义了一个名字位 ".data" 的段,然后所有文件的 ".data" 段都放到这里面。但是这一行多了一个 "ALIGN(4)" ,这是什么意思呢?这是用来对 .data 这个段的起始地址做字节对齐的,ALGN(4) 表示4字节对齐。也就是说段 ".data" 的起始地址要能被 4 整除,一般常见的都是 ALIGN(4) 或者 ALIGN(8),也就是4字节对齐或者8字节对齐。

第6行定义了一个 ".bss" 段,所有文件中的 ".bss" 数据都会北方这个里面,".ss" 数据就是哪些定义了但是没有被初始化的变量。

上面就是链接脚本最基本的语法格式,我们接下来就按照这个基本的语法格式来编写我们本次 "C语言LED灯驱动程序"实验的链接脚本,我们本次实验的链接脚本要求如下:

  1. 链接起始地址为 0x87800000。
  2. start.o 要被链接到最开始的地方,因为 start.o 里面包含了第一个要执行的命令。

根据要求,在Makefle 同目录下创建一个 "im6ul.lds"的文件,然后在文件里输入如下代码:

cpp 复制代码
SECTIONS {
    . = 0x87800000;
    .text : 
    {
        start.o
        main.o
        *(.text)
    }

    .rodata ALIGN(4) : { *(.rodata*) }
    .data   ALIGN(4) : { *(.data) }
    __bss_start = .;
    .bss    ALIGN(4) : { *(.bss) *(COMMON) }
    __bss_end = .;
}

上面的链接脚本,其第2行设置定位计数器为 0x87800000 ,因为我们的链接地址就是 0x87800000。第5行设置链接到开始为止的文件为 start.o ,因为 start.o 里面包含着第一个要执行的指令,所以一定要链接到最开始的地方。第6行时 main.o 这个文件,起始这里可以不用写出来,因为 main.o 的位置就无所谓了,可以有由编译器自行决定链接位置。第11,13 行有 "__bss_start" 和 "__bss_end" 这两个东西?这个是什么呢? "__bss_start" 和 "__bss_end" 是符号,第11, 13 这两行起始就是对这两个符号赋值,其值为定位符号 "." ,这两个符号用来保存 .bss 段的起始地址和结束地址。前面说了 .bss 段是定义了但没有被初始化的变量,我们需要手动对 .bss 段的变量清零的,因此我们需要知道 .bss 段的起始地址和结束地址,这样我们直接对这段内存赋 0 即可完成清零。通过第11,13 行代码,.bss 段的起始地址和结束地址就保存在了 "__bss_start" 和 "__bss_end" 中,我们就可以直接在汇编或者C文件里面使用这两个符号了。

3. 修改Makefile使用 "imx6u.lds" 链接脚本

在上一节我们已经编写好了链接脚本 imx6u.lds ,我们可定是要使用这个链接脚本文件的,将Makifile中的如下一行代码:

cpp 复制代码
arm-linux-gnueabihf-ld -Ttext 0x87800000 $^ -o ledc.elf

修改为:

bash 复制代码
arm-linux-gnueabihf-ld -Timx6ul.lds -o led.elf $^

其实就是将 "-T" 后面的 0x87800000 修改为 imx6ul.lds,表示使用 imx6ul.lds 这个链接脚本文件。修改完成以后使用新的 Makefile 和 链接脚本文件重新编译功能,编译成功之后就可以烧写到 SD 卡验证了。

链接脚本实验中,容易遇到的 .lds 文件的语法错误(我自己实验遇到的):

  • 在赋值语句的后面,缺失了分号 ";"
  • 在 ".bss" 段名语句中间,缺失了冒号 ":"

在编译的时候,编译器发现 .lds 链接脚本的语法错误就会输出错误提示信息,如下图,需要根据编译器提示的错误信息,修正 .lds 链接脚本文件里的语法错误。

(上面这张图就是我自己在第三行 '. = 0x87800000' 这一行尾缺失了分号";",编译其检查到 链接脚本语法错误,输出的错误提示信息。我们根据错误提示信息的行数修正第三行,加上分号。)

4. 下载验证

使用修改后的 Makefile,通过链接脚本控制文件的链接起始为止为 0x87800000,并且通过链接脚本指定文件的链接顺序,把 start.o 文件链接到最终文件的起始位置 0x87800000 处。

bash 复制代码
imxdownload ledc.bin /dev/sdb

烧录SD卡后,把SD卡查到正点原子 I.MX6ULL APHA/Mini 开发板上,开发板上电验证LED灯是否闪烁。

我验证的结果是使用链接脚本控制 led.elf 的链接位置和文件链接顺序后,使用正点原子提供的 imxdownlaod 烧写到SD卡中后,开发板LED灯正产闪烁。

相关推荐
Lovyk1 小时前
Linux 正则表达式
linux·运维
好望角雾眠1 小时前
第一阶段C#基础-10:集合(Arraylist,list,Dictionary等)
笔记·学习·c#
艾伦~耶格尔1 小时前
【集合框架LinkedList底层添加元素机制】
java·开发语言·学习·面试
星仔编程1 小时前
python学习DAY46打卡
学习
Fireworkitte2 小时前
Ubuntu、CentOS、AlmaLinux 9.5的 rc.local实现 开机启动
linux·ubuntu·centos
大霞上仙2 小时前
实现自学习系统,输入excel文件,能学习后进行相应回答
python·学习·excel
sword devil9002 小时前
ubuntu常见问题汇总
linux·ubuntu
ac.char3 小时前
在CentOS系统中查询已删除但仍占用磁盘空间的文件
linux·运维·centos
yatingliu20194 小时前
HiveQL | 个人学习笔记
hive·笔记·sql·学习
武当豆豆4 小时前
C++编程学习(第25天)
开发语言·c++·学习