制作U-Boot烧写镜像到SD卡的过程(中篇:LDS文件)
csdn 2023-12-08

上一篇文章,讲述了制作U-Boot烧写镜像到SD卡的过程,其中运用make的方式来进行将.s文件编译成.bin文件,那make是什么意思?它主要实现了什么?

先讲一下,如果不采用make的方式该怎样实现这个过程。

准备工作

先准备两个.s文件,myboot.s和mylowlevel_init.s。为了使用讲解一下链接过程,本文故意将gpio_out和led2_on两个过程写在两个文件中。

myboot.s

b reset //8种异常的处理,都是跳转到reset b reset
b reset
b reset
b reset
b reset
b reset

reset: bl gpio_out
	bl led2_on
	mov r0, r1 //5句无用代码 mov r1, r2
	mov r2, r3
	mov r3, r4
	mov r4, r5 1: //标号 b 1b //死循环 gpio_out: ldr r11, =0xE0200280 //获得寄存器地址 ldr r12, =0x00001111 //配置成输出状态 str r12, [r11] //将r12寄存器的值放回r11 ldr r11, =0xE0200284 ldr r12, =0xF str r12, [r11] mov pc, lr

mylowlevel_init.s

.globl led2_on
led1_on: ldr r11, =0xE0200284 ldr r12, [r11] bic r12, r12, #(1<<1) //将r12的第二位清零,回写到r12 str r12, [r11] mov pc, lr

下面就先将.s文件汇编成.o文件,利用arm-linux-gcc命令。

arm-linux-gcc -c mystart.s
arm-linux-gcc -c mylowlevel_init.s

那么两个.o文件怎么链接成一个可执行文件呢?

.o链接成可执行文件

LDS文件

当对一个.c文件进行处理,经过预编译、编译、汇编后,最终会生成.o文件。最后一步,将所有的.o文件进行链接,从而生成最终的可执行文件。

那么如何进行链接呢?

汇编后的.o文件会由好多段(section)组成,其中最基本的段就是:bss段,data段和text段。这些段的含义如下:

  • bss段:bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,属于静态内存分配
  • data段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域,属于静态内存分配
  • text段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读(某些架构也允许代码段为可写,即允许修改程序)。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

在链接的过程中,当然不会喜欢将这些段直接拼接,而是希望它们按种类进行分别拼接,也就是说:

对于标准C程序而言,链接是固定的。它是ld调用一个缺省的链接脚本来完成的。因此对于一般的应用开发者,几乎感觉不到ld以及链接脚本的存在。

但是如果在一些特殊情况下,主要是底层非操作系统程序。里面很多代码,特别是汇编代码。必须要链接到指定的位置。而且这个时候的程序入口不一定也不是main了。这种情况在bootloader、Linux内核以及裸机程序下比较普遍,这时就要手工编写lds文件了。

总而言之,创建.o可执行文件的最后一步就是链接。它是由ld或者是用gcc间接调用ld来完成的。它主要任务和把外部库和应用程序目标代码的各个段放到正确位置。

那么,本文就先创建一个简单的LDS文件(myboot.lds)来进行链接:

SECTIONS { . = 0xD0020010 //链接完的可执行文件开始运行的地址(即定位器位置) .text : { mystart.o //mystart.o的.text最前面 * (.text) //剩余文件的.text都放在后面,先后顺序不管 } .data : { * (.data) //所有文件的.data放在一起 } .bss_start = .; //.bss_start在当前位置 .bss : { //可能在其他的文件中用到 * (.bss) } .bss_end = .; //.bss_end在当前位置 } 

想要了解更多的内容,可以参考博文:Linux下的lds链接脚本详解。

链接

链接部分使用arm-linux-ld指令:

arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o

查看文件

查看一下mystart.o文件:

arm-linux-objdump -S mystart.o

运行结果如图所示:

可以看到,由于未链接所以地址从0开始。前面8句都是跳转到地址20,地址20就是reset语句。但是,可以看到所有的汇编代码相同,但是机器指令却是不同的。

根据机器指令,如何计算出汇编代码呢?

根据ARM的手册,可以查看这些机器指令的含义,这里就不展开了。ea000006就表示向后跳转6条指令;而pc指针等于当前地址+8,即指向后两条指令处。总共是向后跳转8条指令,也就是reset处。这样就可以理解了。

另外,还看到led2_on的汇编代码,由于还未进行链接不知道跳转到哪里去,所以显示的地址为0。

除此之外,还看到gpio_out的代码有所变化,r11变成了fp,r12变成了ip,这其实就是ARM内核将r11、r12寄存器增加了新的功能,但是在一般情形下,也可以当做普通寄存器,不需要管。

还有,由于ARM的每条指令为4个字节,但是立即数0xE0200280直接就已经是4个字节了。因此,ARM就将这一条指令变成了两条指令,一条指令将0xE0200280放在一个新的地址上,另一条指令通过相对指针偏转指令来进行读取

40: e59fb014    ldr    fp, [pc, #20] 5c: e0200280 .word 0xe0200280 

顺便也查看一下mylowlevel_init.o文件

arm-linux-objdump -S mylowlevel_init.o

运行结果如图所示:

就不再进行分析了。

最后看一下u-boot可执行文件:

arm-linux-objdump -S myboot

运行结果如图所示:

可以看到,起始地址和led2_on的地址都没有问题。

后续工作

得到了可执行文件,但是由于myboot的文件较大,需要生成纯二进制.bin文件。使用objcopy命令:

arm-linux-objcopy -O binary myboot myboot.bin

然后,添加HeaderInfo信息:

./mkv210 u-boot.bin u-boot.16k

最后烧写到SD卡中:

sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1 

总结起来:

arm-linux-gcc -c mystart.s
arm-linux-gcc -c mylowlevel_init.s //.s文件生成.o文件 arm-linux-ld -T myboot.lds -o myboot mystart.o mylowlevel_init.o //.o文件生成可执行文件 arm-linux-objcopy -O binary myboot myboot.bin //只保留二进制文件 ./mkv210 u-boot.bin u-boot.16k //添加HeaderInfo sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1 //烧写到SD卡 

可以看到,如果讲需求改成点亮LED3的话,只需要修改mylowlevel_init.s即可。但是这些命令都还要再次输入一遍,这是很麻烦的事情!

有没有什么简单的办法呢?这就是make的功能了,请看下一篇博文。

声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 【7.24 深圳】2025国际AI+IoT生态发展大会/2025全球 MCU及嵌入式技术论坛


  • 相关技术文库
  • 单片机
  • 嵌入式
  • MCU
  • STM
  • 基于C51单片机实现汽车座椅自动控制系统的软硬件设计

    引言 随着人们生活水平的提高,对汽车座椅的舒适性要求也越来越高,要求对汽车座椅地调节能够更加简单、方便、快捷。目前,汽车座椅位置的调节多采用基于手动调节方式的机械和电动控制两种方式。汽车座椅位置的调节...

    07-02
  • MCS51单片机程序设计时堆栈的计算方法解析

    用C语言进行MCS51系列单片机程序设计是单片机开发和应用的必然趋势。Keil公司的C51编译器支持经典8051和8051派生产品的版本,通称为Cx51。应该说,Cx51是C语言在MCS51单片机上的扩展,既有C语言的共性,又有它自己...

    07-02
  • 51单片机定时器工作原理及用法

    TMOD : 控制定时器的工作方式。8个bit,高四位 bit 控制 T1,、低四位 bit 控制 T0。因为定时器有4种工作方式;TMOD = 0x00(工作方式0),TMOD = 0x01(工作方式0),TMOD = 0x02(工作方式2),TMOD = 0x03(工作方式3)。...

    07-02
  • 51单片机学习单片机之路总结

    学习单片机有一学期了,现在也由51转到STM32了。一直想对51的学习做一个总结。也希望对别人有一些启发。也给后学者提供一些建议。当然本文是我对自己学习过程的总结,若有不对的地方,还请高手指出。 我想,再看本...

    07-02
  • hot51增强型单片机开发板原理图

    功能要求: 一):绿灯25s倒计时,绿灯过度红灯有5s黄灯时间,红灯25s后直接跳绿灯。 二):按键按下模拟闯红灯输入,产生5s蜂鸣器鸣叫。 开发环境: 软件:Keil uVision4 硬件:HOT51增强型单片机开发板 程序代码:...

    07-01
  • 51单片机的延时子程序

    延时程序在单片机编程中使用非常广泛,但一些读者在学习中不知道延时程序怎么编程,不知道机器周期和指令周期的区别,不知道延时程序指令的用法, ,本文就此问题从延时程序的基本概念、机器周期和指令周期的区别和联系...

    07-01
  • 什么是Flash盘?Flash盘的结构是什么样的?

    Flash是大家常使用的存储之一,对于Flash,大家或多或少有所了解。上篇文章中,小编对Flash闪存的类型有所介绍。为继续增进大家对Flash的认识,本文将对Flash盘、Flash盘结构以及Flash读写操作予以介绍。如果你对本...

    07-01
  • 深谈嵌入式系统,嵌入式系统是如何组成的?

    嵌入式系统在生活中有诸多应用,大家对于嵌入式系统或多或少有所耳闻。在前两篇文章中,小编对嵌入式系统进行过详细介绍。为继续增进大家对嵌入式系统的认识,本文将对嵌入式系统的组成加以说明。如果你对嵌入式系...

    06-27
  • 嵌入式系统秘籍共享,最全嵌入式系统解析

    嵌入式系统的应用十分广泛,因此越来越多的人学习嵌入式系统。由此,在学习嵌入式系统之前,我们应当对嵌入式系统具备一些认识。所以在本文余下部分,小编将对嵌入式系统进行全面解析。如果你对嵌入式系统具有兴趣...

    06-27
  • 51单片机超声波测距程序详解

    51单片机超声波测距程序详解 超声波四通道测距:超声波测距实现分为三大块: 其一是12864带字库的液晶驱动程序: 代码如下: /////////////////12864驱动程序/////////////////////////// //1写数据 void WriteDat...

    06-25
  • 51系列单片机的引脚图

    51系列单片机的引脚图 端子介绍 l P0.0~P0.7 P0口8位双向口线(在引脚的39~32号端子)。 l P1.0~P1.7 P1口8位双向口线(在引脚的1~8号端子)。 l P2.0~P2.7 P2口8位双向口线(在引脚的21~28号端子)。 l P3.0~P3.7 P2口8...

    06-25
  • 51单片机串口通信需要加超时中断吗?

    接收数据时,超过一定时间就算出错. 这个超时的时间是单片机自己算出的吗?超时的时间是由编程序的人定的,他定多长就多长从一段程序开始 实现电脑向 单片机发送一些数据,单片机返回Iget +数据 #include #define u...

    06-25
下载排行榜
更多
评测报告
更多
广告