定制你的操作系统-buildroot

155 阅读10分钟

在物联网和嵌入式设备飞速发展的今天,如何快速、高效地构建一个精简、稳定且高度定制化的Linux操作系统,成为了开发者面临的关键挑战。Buildroot,作为一个简单、高效且功能强大的嵌入式Linux系统构建工具,正为此而生。

本内容旨在为您提供一份关于Buildroot的全面指南。我们不仅会手把手地带您完成一个完整嵌入式OS的配置、编译,更将深入其内部,揭开自动化构建过程的神秘面纱,理解其背后的工作原理。通过理论与实践的结合,您将获得从“使用者”到“掌控者”的蜕变,能够为您的特定硬件和应用场景,量身打造最合适的根文件系统。

一、 buildroot工具简介

1.1 工具简介

官网首页对buildroot的定义,和特点有非常明确的介绍。

1.     buildroot的宗旨为了让嵌入式linux开发更容易。

2.     方便管理,为嵌入式设备生成交叉编译工具链,文件系统,交叉编译bootloader和kernel image。

3.     简单易用,所有配置通过像linux一样的menuconfig,gconfig和xconfig。

4.     支持丰富的package资源,构建需要的rootfs。

buildroot原则上是一个嵌入式自动构建框架,可以编译内核、U-boot、根文件系统。但是 buildroot 下载的 linux 和 uboot官方源码,里面会缺少很多驱动文件,而且最新的 linux 内核和 uboot 会对编译器版本号有要求,可能导致编译失败。所以一般uboot和linux内核都是厂家提供, buildroot主要用来编译生成根文件系统。

buildroot 源码可以从 buildroot 官网下载,官网地址为 buildroot.org/

本文是基于buildroot-2025.08.2进行测试。

1.2 buildroot源码目录

buildroot的目录结构如下:

├── arch:   存放CPU架构相关的配置脚本,如arm/mips/x86,这些CPU相关的配置

├── board   存放了一些默认开发板的配置补丁之类的

├── boot   Bootloader 相关包和配置。

├── CHANGES

├── Config.in  系统级的配置菜单定义

├── Config.in.legacy

├── configs:  预置的配置文件仓库,放置已有开发板的配置文件.

├── COPYING

├── DEVELOPERS

├── dl:       存放下载的源代码及应用软件的压缩包.

├── docs:     存放相关的参考文档.

├── fs:       文件系统格式的生成器。

├── linux:    存放着Linux内核的构建规则。

├── Makefile  整个Buildroot系统的引擎和入口

├── Makefile.legacy

├── output: 构建产物目录.

│   ├── build: 所有组件(工具链、软件包、内核等)的构建目录。

│   ├── host: 为宿主机编译的工具和库,包括交叉编译工具链。

│   ├── images: 存放着编译好的uboot.bin, zImage, rootfs等镜像文件,可烧写到板子里

│   ├── staging  类似根文件系统的布局,包含所有已编译包的开发文件(头文件和库)

│   └── target: 用来制作rootfs文件系统,里面放着Linux系统基本的目录结构,以及编译好的应用库和bin可执行文件. (buildroot根据用户配置把.ko .so .bin文件安装到对应的目录下去,根据用户的配置安装指定位置)

├── package:下面放着软件包的配置文件, Config.in和soft_name.mk,soft_name.mk是构建脚本,用Makefile语法精确定义了如何下载、配置、编译和安装这个软件包。

├── README

├── support

├── system 根文件系统的全局配置。

└── toolchain 工具链的构建和集成。

这个结构清晰地展现了Buildroot作为一个构建系统的核心逻辑:以配置为输入,通过Makefile驱动,在package, boot, linux, toolchain等核心目录中完成编译,最终在output目录输出镜像。按照Buildroot本身提供构建流程的框架,开发者按照格式写脚本,提供必要的构建细节,配置整个系统,最后自动构建出嵌入式系统。

二、 编译流程

2.1 基本操作

       1.编译配置:make menuconfig

image.png

       2.根据芯片平台进行配置;

       3.make开始编译;

2.2 导入编译工具链

Buildroot为交叉编译工具链提供了两种解决方案:

image.png 内部工具链 (Internal toolchain backend),在配置界面中称为Buildroot工具链(Buildroot toolchain)。

外部工具链 (External toolchain backend),在配置界面中称为外部工具链(External toolchain)。

使用Toolchain菜单中的Toolchain Type选项可以在这两个解决方案之间进行选择。选择了一个解决方案,就会出现相应的后续配置选项。

选择Buildroot toolchain代表要从零开始用原材料软件包自动构造工具链,过程比较复杂,本文不做详细介绍。

一般使用的都是芯片厂家提供的编译工具链,选择External toolchain

外部工具链后端允许使用现有的预构建的交叉编译工具链。Buildroot支持许多著名的交叉编译工具链(从Linaro for ARM、Sourcery CodeBench for ARM、x86-64、PowerPC和MIPS),并且能够自动下载它们,或者它可以指向一个定制的工具链,可以下载或在本地安装。

目前,有三个解决方案来使用外部工具链:

1.使用预定义的外部工具链概要文件,并让Buildroot下载、提取和安装工具链。Buildroot已经知道一些CodeSourcery和Linaro工具链。只需在Toolchain中从可用的工具链概要文件中选择工具链概要文件。这绝对是最简单的解决方案。

2.使用一个预定义的外部工具链概要文件,但是不必让Buildroot下载和提取工具链,可以配置Buildroot工具链安装在位置。只需在Toolchain中选择工具链配置文件,取消选择自动下载工具链,并在Toolchain path文本条目中填写交叉编译工具链的路径。

3.使用完全自定义的外部工具链。这对于使用crosstool-NG或Buildroot本身生成的工具链特别有用。为此,在Toolchain列表中选择Custom toolchain解决方案。需要填充Toolchain path、Toolchain prefix和External toolchain C library选项。然后,需要配置Buildroot外部编译工具链支持什么。如果编译工具链使用glibc库,只配置是否支持c++,以及它是否有内置的RPC支持。如果编译工具链使用了uClibc库,需要配置是否支持RPC、宽字符、区域设置、程序调用、线程和c++。在执行make的开始,Buildroot 编译时会核对所选择的选项是否与工具链配置匹配。

以rk3588为例,以rk3588为例自动构建编译工具链配置:

image.png

使用本地编译工具链配置,已在本地部署的rk3588的编译工具链地址为: /opt/toolchains/rockchip_rk3588/bin/aarch64-buildroot-linux-gnu-gcc

配置如下:

image.png

image.png

image.png

    配置完编译工具链后就可以开始编译了。

2.3 系统&文件系统配置

在System configuration配置项下可以对根文件系统下部分初始化配置进行配置,例如默认的shell工具、系统工具路径、登陆是否需要密码等;

image.png

   在Filesystem images中可以对根文件系统的相关内容进行配置,比如说ext4等文件系统格式,根文件系统采用的文件系统类型等等。

image.png

2.4 Target packages配置

此选项用于配置要选择的第三方库或软件、比如 alsa-utils、ffmpeg、iperf、ftp、ssh等工具,可以按需选择。

image.png

busybox是构建根文件系统的瑞士军刀, 在构建根文件系统的时候也是要用到 busybox 的,但是buildroot的make menuconfig无法直接配置 busybox 。在 buildroot下打开 busybox 的配置界面输入如下命令:

make busybox-menuconfig

即可进入busybox配置界面。

image.png

2.5编译结果

    输入make指令后,接下来就是编译过程,编译完成后将会在output/target/下生成编译结果,所有的工具和动态库会按照根文件系统目录进行安装。

image.png

     根据文件系统配置打包后的根文件系统镜像输出到output/images/路径下。

2.6编译原理

每个软件包在package路径下都有一个对应的文件夹,里面存放了相应的config.in、xxx.mk、xxx.patch等文件;config.in是配置文件,通过make menuconfig进行配置使能,选择是否编译;而.mk文件就是编译文件,每个动态库的编译安装过程可以分为下载、解压、patch、配置、编译、安装等步骤,每个步骤完成后都会在output/build/xxx/文件夹下生成一个标志文件,比如说下载标志:.stamp_downloaded。每个步骤执行前都会检查标志文件,判断是否继续执行。

buildroot的编译流程是先从.mk文件中读取XXX_LIB_SITE这个库的下载路径,下载存放到dl/xxx/路径。

image.png

 然后从dl/xxx/xxx.tar下解压出源码到output/build/xxx,如果有patch文件则自动进行patch过程,然后利用本身的配置文件(如果有的话)覆盖output/build/xxx下的配置文件,在开始编译连接完成后安装到output/相应文件夹下。

Buildroot提供了函数框架和变量命令框架,采用它的框架编写的app_pkg.mk这种Makefile格式的自动构建脚本,将被package/pkg-generic.mk 这个核心脚本展开填充到buildroot主目录下的Makefile中去。最后make all执行Buildroot主目录下的Makefile,生成想要的image。

package/pkg-generic.mk中通过调用同目录下的pkg-download.mk、pkg-utils.mk文件,已经帮自动实现了下载、解压、依赖包下载编译等一系列机械化的流程。只要需要按照格式写Makefile脚app_pkg.mk,填充下载地址,链接依赖库的名字等一些特有的构建细节即可。

三、 多平台配置管理

实际使用中,buildroot肯定需要用于多个芯片平台的编译;buildroot支持以下配置管理指令:

image.png

在配置完成后可以将该平台的配置保存到configs文件夹,make menuconfig中配置保存路径的配置如下图所示,然后运行make  savedefconfig即可完成配置的保存,

然后通过 make test_rk3588_defconfig可以导入相应配置。

四、 问题记录

问题1 依赖问题

Makefile:543: *** libiconv is in the dependency chain of dosfstools that has added it to its _DEPENDENCIES variable without selecting it or depending on it from Config.in.  Stop.

问题原因:dosfstools依赖libiconv,libiconv库未使能;

解决方法: .config中的libiconv相关配置项要打开:

BR2_PACKAGE_LIBICONV=y

问题2 编译工具链版本问题

Incorrect selection of kernel headers: expected 4.4.x, got 4.6.x

原因是在交叉编译器路径下有一个内核版本代码

buildroot在执行check-kernel-headers.sh时会检测这这个内核版本代码

#define LINUX_VERSION_CODE 263680

/opt/trans-toolchains/aarch64-rk3308-linux/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/aarch64-linux-gnu/libc/usr/include/linux/version.h

#define LINUX_VERSION_CODE 263680

#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))

263680 = 0x40600(h)

所以移位得到4.6.x的内核版本

  解决办法1 修改version.h

假如当前版本为3.2.x

通过计算得知LINUX_VERSION_CODE 应该为197120

197120 = 30200(h)

移位后为3.2.x。

#define LINUX_VERSION_CODE 197120

解决方法2

Buildroot下make menuconfig中改内核版本为4.6和编译器版本对应即可

image.png

问题3: 全局动态库依赖问题 问题原因:增加自定义库时,定义了对-lssl的依赖:

TARGET_LDFLAGS += -L$(STAGING_DIR)/usr/lib64/ -lcrypto -lssl

会影响到所有库的依赖,导致Make clean后重新编译发现无法编译;

解决方法:

修改为:

image.png