最近,工作重心要从裸机开发转移到嵌入式 Linux 系统开发,在之前的博文 Linux 之八 完整嵌入式 Linux 环境、(交叉)编译工具链、CPU 体系架构、嵌入式系统构建工具 中详细介绍了嵌入式 Linux 环境,接下来就是实际动手学习了。

  这篇博文我们仅仅关注构建过程本身,想要吃透 U-Boot,有太多东西需要学习!最开始我想放到一篇文章中,写着写着内容越来越多,最终超过了 CSDN 编辑器的限制。。。最终决定把内容拆分成多篇文章。你可能需要:

  1. U-Boot 之二 移植过程详解、 STM32F769I-EVAL 开发板适配
  2. U-Boot 之三 详解使用 eclipse + J-Link 进行编译及在线调试
  3. U-Boot 之四 构建过程(Kconfig 配置 + Kbuild 编译)详解
  4. U-Boot 之五 详解 U-Boot 及 SPL 的启动流程

  文中涉及的代码均放到了我个人的 Github 上:https://github.com/ZCShou/BOARD-STM32F769I-EVAL,大家可以直接拿来边学习边验证,避免眼高手低。

注意

  U-Boot 本身没有提供对于我使用的 STM32F769I-EVAL 板子的支持,我这里就是用它支持的 STM32F769-Disco 板子来进行编译。这里仅仅关注如何进行零基础编译,在博文 U-Boot 之二 移植过程详解、 STM32F769I-EVAL 开发板适配 中我详细介绍了如何将 U-Boot 移植到 STM32F769I-EVAL 板子。

  STM32F769-Disco 开发板与 STM32F769I-EVAL 评估板的 MCU 都使用的是 STM32F769NI,因此,我们直接编译后下载是可以运行的。但是两者的板载资源不同(例如,DRAM 大小),但是 U-Boot 识别的某些外设信息是不对的。因此,这里仅仅关注编译过程及编译后的使用方法!

编译环境

  Ubuntu 20.04.3 LTS 准备就绪之后,还有一些工具需要安装。第一个就是 GCC,GCC 就使用 Ubuntu 自带的 9.3.0 版即可。在编译 U-Boot 的过程中,我们还需要安装其他一些依赖工具,这个在后面用到的时候缺啥装啥就可以(出现各种错误的时候再安装相应工具即可)。

  1. u-boot 使用 openssl 1.x 版本,最新的 Ubuntu 22.04 LTS 已经将内置的 OpenSSL 升级到了 3.x 版本。如果在 Ubuntu 22.04 LTS 中编译会有提示一堆警告。
  2. 最新版 u-boot 已经屏蔽了警告,但貌似还没有完全切换到 OpenSSL 3.x 版本。

  这里再介绍一下安装依赖包的一些方法。正常我们应该是先使用 find 命令查找依赖是否存在。因为存在一种情况是,依赖文件本身存在但是没有在环境变量 LD_LIBRARY_PATH 里,如果不存在直接安装即可。

  1. 直接安装的情况。一般使用命令 sudo apt update -> apt-cache search xxxx -> sudo apt install xxxx,具体示例(仅仅是个示例,安装了没啥用哈)如下:

    这样我们可以查看响应的依赖包的具体说明,然后根据需要来安装。
  2. 没有在环境变量 LD_LIBRARY_PATH 里的情况,目前有两种方法:
    1. 直接把依赖的动态库拷贝到上面执行的可执行命令时显示被找到的库的目录下。
    2. 把依赖的动态库所在的目录添加到环境变量 LD_LIBRARY_PATH 里:export LD_LIBRARY_PATH=/path_to_lib/:/lib/x86_64-linux-gnu/:$LD_LIBRARY_PATH

  后来,我将开发环境 Ubuntu 20.04 LTS + Arm GNU Toolchain 10.3 -2021.10进行了升级,升级之后需要注意下面的各种问题,升级后的开发环境如下所示(本文后续内容同时对新 / 旧这两个环境进行了验证)。

  1. 由于 Ubuntu 20.04 LTS 默认是标配 OpenSSL 3.x,而旧版 U-Boot 使用的是 OpenSSL 1.x,因此,该环境编译旧版 U-Boot 将出现一堆警告。commit e927e21c07483337ffb63b828d4ddb5e0db342b2 添加了相关处理:
  2. Arm GNU Toolchain 10.2-2022.02 存在 BUG,导致编译 U-Boot 报错,不要使用这个版本!

编译过程

  在开始编译之前,我们先介绍两个命令:make clean 用于清空编译中间文件 和 make distclean 用于清除所有编译产生的文件。如果我们想要重新编译,可以使用以上两个命令清理环境(这两个命令在 U-Boot 根目录的 Makefile 中有详细的定义,在博文 U-Boot 之四 构建过程(Kconfig 配置 + Kbuild 编译)详解 中会有说明)。

  1. 第一步肯定是获取 U-Boot 的源代码,我这里直接使用了当前最新存档版:u-boot-2021.10.tar.bz2。这里需要重点注意,我最开始直接使用 Git 获取了最新的源代码,结果编译之后运行直接 HardFault,分析好久没找到原因,最后决定使用一个稳定发布版试试,结果没有问题。。。

      成功下载并解压源代码之后(注意我这里将解压后的文件名命名为了 u-boot),我们需要进入 u-boot 目录下,使用命令:cd u-boot。此后就在 u-boot 目录下进行各种操作。

  2. 第二步就是生成配置,直接使用命令:ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 stm32f769-disco_defconfig其中,参数 O=xx 用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:

    1. /bin/sh: 1: bison: not found

        这个错误是由于我们没有安装 bison 这个工具。Bison 是一个通用解析器生成器。GUN 软件之一,官网 https://www.gnu.org/software/bison/。Ubuntu 下直接使用命令:sudo apt install bison 即可(这个不是最新版,如果需要最新版需要自己从源码安装)。

    2. /bin/sh: 1: flex: not found

        这个错误是由于我们没有安装 flex 这个工具。flex 是一个词法分析器。用来将一个 .l 文件生成一个 .c 程序文件。源代码托管于 Github:https://github.com/westes/flex。Ubuntu 下直接使用命令:sudo apt install flex 即可。

    3. 正常完成如下所示:

  3. 第三步修改配置(裁剪)。直接使用命令:ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 menuconfig其中,参数 O=xx 用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:

    1. 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 将选择去掉,然后保存退出。

        其中还有很多项,这个就需要根据自己需要来具体进行裁剪了。当然有个前提是,修改了配置很大可能还需要配套修改对应的源代码,因为之所以修改肯定是为了适应自己开发板。
    2. Your display is too small to run Menuconfig!

      这个错误提示很明显,就是终端界面太小,把终端拖大一些就好了。
  4. 第四步就是真正的编译了,直接使用命令:ARCH=arm CROSS_COMPILE=arm-none-eabi- make O=build_stm32 -j$(nproc)其中,参数 O=xx 用于指定编译的输出路径,这样,所有编译输出都放到指定目录下,方便查看。 不出意外的话会出现以下错误:

    1. /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 官网下载压缩包的安装方法:

      1. 下载最新版的 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

      2. 将压缩包解压。我这里将其解压到了 /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 的各种可执行程序、库等文件了。

      3. 此时我们需要将 /opt/gcc-arm-none-eabi-10.3-2021.10 添加到系统环境变量这样才能正常在终端中使用各种命令。具体有两种方法(我采用了第二种):

        1. 第一种是创建符号连接

          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
          
        2. 第二种是在 .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"
          
      4. 可能需要重启我们之前已经打开的终端以上配置才会生效。

      5. 这里有一点需要注意,通过压缩包安装不能解决依赖关系,需要我们自己运行尝试,看看少啥安啥。目前已知的依赖是 ncures5(会报错:error while loading shared libraries: libncurses.so.5),其需要安装 sudo apt install libncurses5(貌似应该是需要 32 位的,我们上面安装的 libncurses-dev 应该是 64 位的)。
        至于需不需要其他的依赖,大家自行尝试,我这里是没有提示需要其他任何组件。

    2. fatal error: openssl/evp.h:

      这个错误主要是由于 U-Boot 代码使用了 openssl 中的相代码,而我们的环境中没有安装 openssl。Ubuntu 下直接使用命令:sudo apt install libssl-dev 即可。

  5. 正常编译完成之后。我们需要的文件就有了。我们真正需要的是根目录下的 u-boot.bin 和 spl 目录下的 u-boot-spl.bin

  6. 其他一些问题。我在试用 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: 编译的内存映射文件。
  • 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.binu-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.binu-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 的目的。

参考

  1. https://askubuntu.com/questions/1243252/how-to-install-arm-none-eabi-gdb-on-ubuntu-20-04-lts-focal-fossa
  2. https://james-hui.com/2021/07/02/building-a-small-uboot-linux-and-rootfs-for-arm-cortex-m7/
  3. opdenacker-understanding-u-boot-falcon-mode.pdf
  4. https://adrianalin.gitlab.io/popsblog.me/posts/build-linux-for-stm32f769i-disco-using-buildroot/

U-Boot 之一 零基础编译 U-Boot 过程详解、Image 镜像介绍及使用说明、DTB 文件使用说明相关推荐

  1. 零基础入门 Unity 之 UGUI 详解专栏 | 寻找C站宝藏

    零基础入门 Unity 之 UGUI 详解专栏 | 寻找C站宝藏 六大推荐理由 理由一:系统 理由二:详细 理由三:专业 理由四:图解 理由五:深度 理由六:实例 一键直达:<UGUI 控件详解 ...

  2. Keras深度学习实战(1)——神经网络基础与模型训练过程详解

    Keras深度学习实战(1)--神经网络基础与模型训练过程详解 0. 前言 1. 神经网络基础 1.1 简单神经网络的架构 1.2 神经网络的训练 1.3 神经网络的应用 2. 从零开始构建前向传播 ...

  3. 从零编写60%+胜率的短线择时信号!零基础python量化投资代码详解【邢不行】

    引言: 邢不行的系列帖子"量化小讲堂",通过实际案例教初学者使用python进行量化投资,了解行业研究方向,希望能对大家有帮助. ​ ​ 如果有人说你是百年难得一见的量化投资天才, ...

  4. 零基础3D游戏建模入门详解

    文章目录 一.3D建模工作岗位 二.我是零基础,我选3D手绘低模还是次世代高模呢? 三.3D角色和3D 场景之间,选哪一个好一点呢? 四.如何有效地学会建模? 一.3D建模工作岗位 要是想从事3D建模 ...

  5. C++ 编译,运行过程 详解。

    要更深入了解C++, 必须要知道一个程序从开始到结束都干了些什么, 怎么干的. 所以我从C++编译到运行过程,解析下程序是怎么跑的. 首先,初略的说一下之前C++的编译过程,C++编译过程包括预编译- ...

  6. C/C++编译和链接过程详解 概述 (重定向表,导出符号表,未解决符号表)

    详解link  有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错 ...

  7. 零基础学Python:作用域详解

    1.作用域介绍 python中的作用域分4种情况: L:local,局部作用域,即函数中定义的变量: E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局 ...

  8. 零基础入门Python3-列表list详解

    list是一个有序的列表,比如:某个班级里面总共有5个学生,这些学生名字清单依次是:zhangsan.lisi.wangwu.meimei.huanhuan.这个名字清单就是一个有序的列表,给他们的名 ...

  9. pythonlist详解_零基础入门Python3-列表list详解

    list是一个有序的列表,比如:某个班级里面总共有5个学生,这些学生名字清单依次是:zhangsan.lisi.wangwu.meimei.huanhuan.这个名字清单就是一个有序的列表,给他们的名 ...

  10. C语言编译的五大过程详解

    前言 说实话,很多人做了很久的C/C++,也用了很多IDE,但是对于可执行程序的底层生成一片茫然,这无疑是一种悲哀,可以想象到大公司面试正好被问到这样的问题,有多悲催不言而喻,这里正由于换工作的缘故, ...

最新文章

  1. 网络协议命令行处理c语言程序解析,网络协议工程SPIN实验报告剖析.doc
  2. [转]在Javascript中闭包(Closure)
  3. 第3章 Python 数字图像处理(DIP) - 灰度变换与空间滤波3 -幂律变换、伽马变换
  4. Educational Codeforces Round 107 (Rated for Div. 2) 题解
  5. C++笔记-QSslSocket::supportsSsl返回false(windows版的Qt不支持SSL)解决
  6. 聚焦LS-MIMO的四大层面,浅谈5G关键技术
  7. 一件事情没弄清楚,十有八九做不好
  8. c#窗口操作-句柄操控全解
  9. EasyRecovery深度扫描以恢复桌面遗失数据的方法
  10. web制作、开发人员需知的Web缓存知识
  11. VeraCrypt磁盘加密软件
  12. 自动刷新网页代码(可多个网页)
  13. 洪湖市计算机软件学校,湖北省教育厅关于公布“第十届湖北省中小学电脑制作作品评选”暨“第四届湖北省中小学信息技术创新与实践活动”获奖名单的通知...
  14. protobuf java代码生成及实例
  15. 服务端与客户端加密解密思路
  16. android 学习笔记③
  17. 2022 全球程序员薪酬报告:字节主管工程师年薪中位数 56.4 万美元,排世界第 7...
  18. HashSet里的元素是不能重复的,那用什么方法来区分重复与否呢?
  19. sublime text3 多窗口打开设置
  20. xml基础、DTD验证、Schema验证(备忘)

热门文章

  1. iPhone白苹果不能开机,怎么办?
  2. Linux重定向与管道符
  3. linux scandir函数,Linux的scandir函数
  4. SP SCHWARZER 7E53000
  5. Windows10 无法连接到局域网内打印机,并提示0x00000533错误
  6. 关于微信小程序,需要了解的有哪些?
  7. python面试笔试宝典pdf_Python面试宝典之基础篇-02
  8. HTML文档标题标记有哪几种,HTML基本结构与常用标记.doc
  9. 《少有人走的路》读书笔记
  10. uniapp解决首页点击返回(手机返回键)跳到登录页