本文主要是介绍U-Boot 之二 零基础编译、详解 Image 镜像及 DTB 文件,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在上一篇博文 U-Boot 之一 源码文件、启动阶段(TPL、SPL)、FALCON、设备树 中我们已经对 U-Boot 的源码进行了整体的一个认识,本篇博文就来看看如何对 U-Boot 进行编译,不过这里仅仅关注构建过程本身,不涉及 U-Boot 中任何技术细节。
基本环境
我们需要一台 Linux PC(可以使用虚拟机) 及一个块可运行 Linux 的开发板。尽管部分源码支持在 Windows 下的 Linux 环境中编译(例如 WSL),但是强烈不推荐,因为难免出现一些奇怪问题。
开发环境
我这里使用的基本开发环境是全新安装的 Ubuntu 22.04.1 LTS,为了避免一开始就安装一堆不是道干啥的软件问题,我们采用在后续开发中缺少补啥的方式(这就是我所谓的零基础编译)。使用 sudo apt install lsb-core
安装并查看发行版基本信息如下:
- 由于 Ubuntu 22.04 LTS 默认是标配 OpenSSL 3.x,而旧版 U-Boot 使用的是 OpenSSL 1.x,所以,该环境编译旧版 U-Boot(从 commit e927e21c 开始添加了相关处理) 将出现一堆警告,因此,后续使用 u-boot-v2022.10 这个版本为主:
- Arm GNU Toolchain 10.2-2022.02 存在 BUG,导致编译 U-Boot 报错,不要使用这个版本!
- 新版(Arm GNU Toolchain 10.3 之后的版本)的 Arm GNU Toolchain 在 Linux 上 GDB 需要 Python3.8 支持。然而,Ubuntu 22.04 默认的 Python 是 3.10。直接运行
arm-none-eabi-gdb
报错如下:
解决方法就是直接手动安装 Python3.8 即可。 旧版的 Arm GNU Toolchain 10.3 -2021.10 不需要 Python 支持sudo add-apt-repository ppa:deadsnakes/ppa -y sudo apt install python3.8
运行环境
我使用的嵌入式 Linux 运行环境是 STM32F769I-EVAL 板子。STM32F769I-EVAL 板子使用的 STM32F769NI 这个 MCU,STM32F769NI 这款 MCU 采用的是 ARM Cortex-M7 的核心,指令集架构是 ARMv7m。此外,还需要注意,这个板子上的的串口的 RX 默认是断开,需要用短路帽连接起来。
U-Boot 本身没有提供对于 STM32F769I-EVAL 板子的支持,但是它支持的 STM32F769-Disco 板子。STM32F769-Disco 开发板与 STM32F769I-EVAL 评估板的 MCU 都使用的是 STM32F769NI,因此,本文我们就直接编译 STM32F769-Disco 板子的固件。如下是整个嵌入式 Linux 环境的内存布局。
不过两者的板载资源不同(例如,DRAM 大小),因此,直接下载到开发板运行则 U-Boot 识别的某些外设信息是不对的。本篇博文仅仅关注如何进行零基础编译,在博文 U-Boot 之三 移植过程详解、 STM32F769I-EVAL 开发板适配 中我详细介绍了如何将 U-Boot 移植到 STM32F769I-EVAL 板子。
示例代码
嵌入式 Linux 运行环境搭建系列博文涉及的所有源代码均放到了我个人的 Gitee 上:https://gitee.com/itexp/BOARD-STM32F769I-EVAL
。这个仓库中包含了的所有源代码会根据后续博文一步步进行各种适配,如果你是纯学习则可以直接 clone 该仓库来学习!
其中,为了在适配 Buildroot、Yocto 等工具时方便,该仓库采用了 git submodule
来进行基本的组织。本篇博文我们重点是学习 U-Boot,因此,也可以直接使用 https://gitee.com/itexp/u-boot-v2021.10 这个子仓库来学习。
编译过程
由于我这里采用的是全新安装的 Ubuntu 22.04 LTS,各种软件包都没有安装,因此,肯定会出现各种错误,我们就采用构建中缺啥补啥,解决其中出现的各种错误。
-
第一步肯定是获取 U-Boot 的源代码,我这里直接使用了当前最新存档版:
u-boot-2021.10.tar.bz2
。这里需要重点注意,我最开始直接使用 Git 获取了最新的源代码,结果编译之后运行直接 HardFault,分析好久没找到原因,最后决定使用一个稳定发布版试试,结果没有问题。。。
成功下载并解压源代码之后(注意我这里将解压后的文件名命名为了 u-boot),我们需要进入 u-boot 目录下,使用命令:cd u-boot
。此后就在 u-boot 目录下进行各种操作。 -
第二步就是生成配置,直接使用命令:
ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 stm32f769-disco_defconfig
。其中,参数O=xx
用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:-
/bin/sh: 1: bison: not found
这个错误是由于我们没有安装 bison 这个工具。Bison 是一个通用解析器生成器。GUN 软件之一,官网 https://www.gnu.org/software/bison/。Ubuntu 下直接使用命令:sudo apt install bison
即可(这个不是最新版,如果需要最新版需要自己从源码安装)。 -
/bin/sh: 1: flex: not found
这个错误是由于我们没有安装 flex 这个工具。flex 是一个词法分析器。用来将一个.l
文件生成一个.c
程序文件。源代码托管于 Github:https://github.com/westes/flex。Ubuntu 下直接使用命令:sudo apt install flex
即可。 -
正常完成如下所示:
-
-
第三步修改配置(裁剪)。直接使用命令:
ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 menuconfig
。其中,参数O=xx
用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:- Unable to find the ncurses package
这个错误是由于我们没有安装 ncurses 这个工具。ncurses(new curses)是一套编程库,它提供了一系列的函数以便使用者调用它们去生成基于文本的用户界面。GUN 软件之一,官网:https://invisible-island.net/ncurses/。Ubuntu 下直接使用命令:sudo apt install libncurses-dev
即可。安装成功之后,重新make menuconfig
,就会进入下面的界面:
我们需要做的就是选择其中的 SPL / TPL 菜单项,然后回车,在其中下翻页找到Activate Falcon Mode
项(SPL / TPL ---> Activate Falcon Mode
),按n
将选择去掉,然后保存退出。
其中还有很多项,这个就需要根据自己需要来具体进行裁剪了。当然有个前提是,修改了配置很大可能还需要配套修改对应的源代码,因为之所以修改肯定是为了适应自己开发板。 - Your display is too small to run Menuconfig!
这个错误提示很明显,就是终端界面太小,把终端拖大一些就好了。
- Unable to find the ncurses package
-
第四步就是真正的编译了,直接使用命令:
ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 -j$(nproc)
。其中,参数O=xx
用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:-
/bin/sh: 1: arm-none-eabi-gcc: not found
这个错误是由于我们没有安装 GCC for ARM 导致的。也就是没有编译 U-Boot 使用的编译器。解决方法也非常简单,就是安装 GCC for ARM 即可。这里有个需要注意的点,如果大家搜索 Ubuntu 下安装 GCC for ARM,很多文章都过推荐使用 apt 命令来安装,类似于:
sudo apt-get install gcc-arm-none-eabi
,这个版本并不是最新的(貌似使用这个旧版本也可以,我选择了使用最新版)。更重要的是,ARM 之前已经宣布不再更新 Launchpad 上的 GCC for ARM 了(具体见 Launchpad 上的说明)。 其只在 ARM 的官网提供编译好的压缩包及源代码的压缩包。这里简单来讲解一下直接从 ARM 官网下载压缩包的安装方法:
-
下载最新版的 Linux x86_64 Tarball。目前最新的是 gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 。GCC for ARM 下载地址:https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads
-
将压缩包解压。我这里将其解压到了
/opt/
目录下。直接使用命令:tar -xjvf gcc-arm-none-eabi-10.3-2021.10-x86_64-linux.tar.bz2 -C /opt
。等待解压完成,/opt/gcc-arm-none-eabi-10.3-2021.10
目录下就是最新的 GCC for ARM 的各种可执行程序、库等文件了。 -
此时我们需要将
/opt/gcc-arm-none-eabi-10.3-2021.10
添加到系统环境变量这样才能正常在终端中使用各种命令。具体有两种方法(我采用了第二种):- 第一种是创建符号连接
sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-gcc /usr/bin/arm-none-eabi-gcc sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-g++ /usr/bin/arm-none-eabi-g++ sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-gdb /usr/bin/arm-none-eabi-gdb sudo ln -s /opt/gcc-arm-none-eabi-your-version/bin/arm-none-eabi-size /usr/bin/arm-none-eabi-size
- 第二种是在
.bashrc
文件(在 Linux 系统普通用户目录(cd /home/xxx
)或 root 用户目录(cd /root
)下,用指令ls -al
可以看到该隐藏文件,一般使用用户目录下的即可)最后面增加以下内容:# GCC for ARM export PATH="$PATH:/opt/gcc-arm-none-eabi-10.3-2021.10/bin"
- 第一种是创建符号连接
-
可能需要重启我们之前已经打开的终端以上配置才会生效。
-
这里有一点需要注意,通过压缩包安装不能解决依赖关系,需要我们自己运行尝试,看看少啥安啥。目前已知的依赖是
ncures5
(会报错:error while loading shared libraries: libncurses.so.5)和libncursesw5
(报错:error while loading shared libraries: libncursesw.so.5: cannot open shared object file: No such file or directory),其需要安装sudo apt install libncurses5 libncursesw5
(貌似应该是需要 32 位的,我们上面安装的 libncurses-dev 应该是 64 位的)。
至于需不需要其他的依赖,大家自行尝试,我这里是没有提示需要其他任何组件。
-
-
fatal error: openssl/evp.h:
这个错误主要是由于 U-Boot 代码使用了 openssl 中的相代码,而我们的环境中没有安装 openssl。Ubuntu 下直接使用命令:sudo apt install libssl-dev
即可。
-
-
正常编译完成之后。我们需要的文件就有了。我们真正需要的是根目录下的
u-boot.bin
和 spl 目录下的u-boot-spl.bin
。
-
其他一些问题。我在试用
make CHANGELOG
命令时出现错误 unrecognized command line option ‘-mno-unaligned-access’,不知道为啥,也没找到如何解决。最新的 U-Boot-v2022.07 版本已经没有该错误了
Image 镜像
成功编译之后,就会在 U-Boot 源码的根目录下产生多个可执行二进制文件以及编译过程文件,这些文件都是 u-boot.xxx
的命名方式。这些文件由一些列名为 .xxx.cmd
的文件生成,.xxx.cmd
这些文件都是由编译系统产生的用于处理最终的可执行程序的。注意,下面部分文件可能没有与自己的 make menuconfig
中的配置有关系。
- u-boot: 这个文件是编译后产生的 ELF 格式的最原始的 U-Boot 镜像文件,后续的文件都是由它产生的!
.u-boot.cmd
这个命令脚本描述了如何产生。
- u-boot-nodtb.bin: 这文件是使用编译工具链的
objcopy
工具从u-boot
这个文件中提取来的,它只包含可执行的二进制代码。就是把u-boot
这个文件中对于执行不需要的节区删除后剩余的仅执行需要的部分。由.u-boot-nodtb.bin.cmd
这个命令脚本产生。
- u-boot-dtb.bin: 在
u-boot-nodtb.bin
尾部拼接上设备树后形成的文件。由.u-boot-dtb.bin.cmd
这个命令脚本产生。
- u-boot.bin: 就是把
u-boot-dtb.bin
重命名得到的。由.u-boot.bin.cmd
这个命令脚本产生。
- u-boot.img: 在
u-boot.bin
开头拼接一些信息后形成的文件。由.u-boot.img.cmd
这个命令脚本产生。 - u-boot-dtb.img: 在
u-boot.bin
开头拼接一些信息后形成的文件。由.u-boot-dtb.img.cmd
这个命令脚本产生。 - u-boot.srec: S-Record 格式的镜像文件。由
.u-boot.srec.cmd
这个命令脚本产生。 - u-boot.sym: 这个是从
u-boot
中导出的符号表文件。由.u-boot.sym.cmd
这个命令脚本产生。 - u-boot.lds: 编译使用的链接脚本文件。由
.u-boot.lds.cmd
这个命令脚本产生。 - u-boot.map: 编译的内存映射文件。该文件是有编译工具链的连接器输出的!
- System.map: 记录 U-Boot 中各个符号在内核中位置,但是这个文件是使用了
nm
和grep
工具来手动生成的
- u-boot.dtb: 这个是编译好的设备树二进制文件。就是
./dts/dt.dtb
重命名得到的。./dts/dt.dtb
来自于 arch/arm/dts/stm32f769-eval.dtb 重命名。
默认是开启了 SPL 的,因此,在编译 U-Boot 时会额外单独编译 SPL,编译产生的镜像文件就存放在 ./SPL
目录下。这下面的镜像生成方式与 U-Boot 基本是一模一样的。
- u-boot-spl: 这个文件是编译后产生的 ELF 格式的 SPL 镜像文件,后续的文件都是由它产生的!
.u-boot-spl.cmd
这个命令脚本描述了如何产生。 - u-boot-spl-nodtb.bin: 这文件是使用编译工具链的
objcopy
工具从u-boot-spl
这个文件中提取来的,它只包含可执行的二进制代码。就是把u-boot-spl
这个文件中对于执行不需要的节区删除后剩余的仅执行需要的部分。由.u-boot-spl-nodtb.bin.cmd
这个命令脚本产生。 - u-boot-spl-dtb.bin: 在
u-boot-nodtb.bin
尾部依次拼接上u-boot-spl-pad.bin
和u-boot-spl.dtb
后形成的文件。由.u-boot-spl-dtb.bin.cmd
这个命令脚本产生。 - u-boot-spl.bin: 就是把
u-boot-dtb.bin
重命名得到的。由.u-boot-spl.bin.cmd
这个命令脚本产生。 - u-boot-spl.sym: 这个是从
u-boot
中导出的符号表文件。由.u-boot-spl.sym.cmd
这个命令脚本产生。 - u-boot-spl.lds: 编译使用的链接脚本文件。由
.u-boot-spl.lds.cmd
这个命令脚本产生。 - u-boot-spl.map: 编译 SPL 的内存映射文件。
- u-boot-spl.dtb: 这个是编译好的设备树二进制文件。就是
./dts/dt.dtb
重命名得到的。./dts/dt.dtb
来自于 arch/arm/dts/stm32f769-eval.dtb 重命名。 - u-boot-spl-pad.bin: 应该是对齐使用的数据,具体为啥需要这个还没找到。
Image 使用
编译之后,源码的根目录下会生成一堆二进制的文件(.bin),其中,在默认的 U-Boot 配置下,我们实际需要的 U-Boot 实际包含两部分:spl/u-boot-spl.bin
和 u-boot.bin
。而且,默认情况下,编译的这俩文件是可以直接在 MCU 内部的 Nor Flash 中运行的。
因此,我们可以直接使用 J-link 等调试器将这俩文件烧写到 Flash 中。至于烧写地址,在 U-Boot 源码中(具体见于 ./config/stm32f769-disco_defconfig
和 ./include/configs/stm32f746-disco.h
)都是配置好了的。
设备树
设备树源文件被最终编译为二进制的 DTB 文件,原始的 DTB 文件位于 arch/arm/dts/xxx.dtb
下面,构建系统会复制到 ./dts/dt.dtb
,进一步重命名为 ./u-boot.dtb
。u-boot 支持两种形式将 dtb 编译到 u-boot 的镜像中:
-
dtb 和 u-boot 的 bin文件分离
- 需要打开
CONFIG_OF_SEPARATE
宏来使能。 - 在这种方式下,u-boot 的编译和 dtb 的编译是分开的,先生成 u-boot 的 bin 文件,然后再另外生成dtb 文件。
- dtb 最终会自动追加到 u-boot 的 bin 文件的最后面。因此,可以通过 u-boot 的结束地址符号,也就是 _end 符号来获取 dtb 的地址。
- 需要打开
-
dtb 集成到 u-boot 的 bin 文件内部
- 需要打开
CONFIG_OF_EMBED
宏来使能。 - 在这种方式下,在编译 u-boot 的过程中,也会编译 dtb。
- 最终 dtb 是包含到了u-boot 的 bin 文件内部的。dtb 会位于 u-boot 的 .dtb.init.rodata 段中,并且在代码中可以通过 __dtb_dt_begin 符号获取其符号。
官方不推荐这种方式,建议仅用于调试
- 需要打开
-
另外,也可以通过 fdtcontroladdr 环境变量来指定 dtb 的地址。可以通过直接把 dtb 加载到内存的某个位置,并在环境变量中设置 fdtcontroladdr 为这个地址,达到动态指定 dtb 的目的。
参考
- https://askubuntu.com/questions/1243252/how-to-install-arm-none-eabi-gdb-on-ubuntu-20-04-lts-focal-fossa
- https://james-hui.com/2021/07/02/building-a-small-uboot-linux-and-rootfs-for-arm-cortex-m7/
- opdenacker-understanding-u-boot-falcon-mode.pdf
- https://adrianalin.gitlab.io/popsblog.me/posts/build-linux-for-stm32f769i-disco-using-buildroot/
这篇关于U-Boot 之二 零基础编译、详解 Image 镜像及 DTB 文件的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!