ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

龙芯平台Linux 4.19内核编译报错排查与交叉编译实践指南

龙芯平台Linux 4.19内核编译报错排查与交叉编译实践指南 前几天朋友发来三段编译日志说在龙芯2K3000的板子上编Linux 4.19内核编一半就报错来回折腾一天没解决。我看完日志第一反应不是去猜哪个函数写错了而是反问他三个问题源码从哪拉下来的交叉编译器用的哪套make ARCH设成了什么结果他一个都答不上来。这个场景我见得太多了——龙芯平台上报Linux 4.19编译出错十个里有八个不是代码问题而是整条编译链路从源头就错了。这篇文章就把我在龙芯平台上编4.19内核踩过的坑、排查过的错误捋一遍。4.19至今还是很多国产化工控项目的锚定版本AFC这类场景选它很常见但生态不像x86那么顺滑。下面按“为什么容易错—动手前确认什么—逐条对着日志排错—编译完怎么保证能启动”的顺序讲尽量让新人和被板子折磨过的老手都能对号入座。1. 为什么Linux 4.19在龙芯上编译报错会比x86平台多一个量级1.1 官方主线4.19里根本找不到LoongArch目录Linux 4.19是2018年底发布的LTS内核。LoongArch是龙芯后来才公开的指令集架构并不存在于4.19时代的官方主线中。这意味着你跑到kernel.org拉一个4.19.xxx的原版压缩包解压之后进arch目录翻到底也只能看到mips下的loongson64绝对翻不出loongarch目录。这种情况下无论怎么调整编译命令Makefile都会直接告诉你arch/loongarch/Makefile: No such file or directory make[1]: *** No rule to make target arch/loongarch/Kconfig. Stop.这条报错基本等于直说源码不对不是编译姿势不对。所以排错第一件事永远先看arch目录下有没有对应架构有LoongArch目录后面的讨论才有意义。内核来源架构目录结果kernel.org 4.19 原版arch/mips/loongson64仅MIPS龙芯编LoongArch必失败Loongnix/龙芯社区4.19分支arch/loongarch可编3A5000/2K1000/2K3000板卡厂商BSP 4.19视厂商而定通常可用但要做二次确认1.2 龙芯自己就有两套架构选错defconfig等于白做这个问题比源代码分支更隐蔽。龙芯早期处理器用MIPS指令集3A3000、3B4000都是后来新处理器切到LoongArch指令集3A5000、3A6000、2K1000、2K2000、2K3000这些都是。同样是“4.19内核”MIPS龙芯走arch/mips/loongson64LoongArch龙芯走arch/loongarch两条线代码不同defconfig也不同。LoongArch板子常见的是loongson2k_defconfig或loongson64_defconfigMIPS龙芯常见的是loongson3_defconfig。不同维护分支可能改名务必先ls一下arch/loongarch/configs或arch/mips/configs以仓库里实际文件名为准。我见过有人拿3A3000的MIPS配置硬套到2K3000板子上编译器一会儿报这一会儿报那就是.config里全是MIPS选项CPU型号、串口地址、中断控制器全对不上。这种错误靠看日志很难定位因为报错点分散在各个驱动里其实源头就是一个。1.3 工具链错位导致的报错最隐蔽编译x86内核时大家习惯用发行版自带gcc就行。到了龙芯4.19发行版自带gcc是给本机架构编的交叉编译器又经常版本不对用x86的gcc直接编链接阶段疯狂报错或机器码不对。用mips64el工具链去编loongarch汇编器不认LoongArch指令。用太老的gccgcc 8之前对LoongArch支持很差某些-march参数根本不存在头文件里也能报语法错。这类错看起来像代码错实际是工具链错。内核源码里的汇编文件对binutils版本尤其敏感老版本binutils不识别LoongArch伪指令会在head.S这类文件上报一堆莫名其妙的错。工具链问题后面单独说这里先记结论在龙芯上编4.19工具链的可信度比源码还值得怀疑。2. 翻车前先确认三件事源码分支、目标架构、工具链版本2.1 先摸清板子上的CPU型号拿到板子后不要急着解压源码先上板子确认CPU。最简单的方法是uname -m和cat /proc/cpuinfo。在板子有串口或SSH的情况下一条命令就能看到processor信息。有设备树的再cat /proc/device-tree/model通常会带loongson字样。uname -m输出是loongarch64说明是LoongArch处理器。如果是mips64说明是MIPS龙芯。这一步不搞清楚后面所有defconfig都选不对报错也会千奇百怪。2.2 找到匹配的4.19源码正确源码从哪来我的经验优先级板卡厂商BSP里带的源码包他们调过板级配置最贴近硬件。Loongnix仓库里的kernel-4.19分支。龙芯开源社区维护的linux-4.19分支。下载后立刻检查arch目录判断标准是ls arch | grep loongarch能出结果且make kernelversion输出是4.19开头。我之前遇到过有人拿了一个5.10的内核然后硬套4.19的defconfig来编报错后到处查最后发现版本号压根不对白折腾半天。2.3 交叉编译器怎么准备方案A直接用龙芯官方发布的交叉编译器一般命名类似loongarch64-linux-gnu-解压后放进/opt或某个工具目录加入PATH。方案B用crosstool-ng自建。版本选择上gcc建议9以上binutils建议2.32以上否则很容易触发汇编错误。我实测下来gcc 10.x加binutils 2.38的组合比较稳。如果是MIPS龙芯就配mips64el-linux-gnu-的工具链别搞混。工具链前缀在不同版本里可能有差异比如loongarch64-unknown-linux-gnu-验证方法很简单loongarch64-linux-gnu-gcc -dumpmachine输出为loongarch64-linux-gnu之类就基本对。再写一个hello.c编一下看能不能过能过说明工具链基础可用。2.4 x86主机上交叉编译需要装的系统依赖在x86的Ubuntu/Debian主机上交叉编译确保有这些包apt install build-essential flex bison libncurses-dev libssl-dev bc kmod cpio缺libncurses-dev会卡在make menuconfig缺libssl-dev会在签名阶段报错缺bc会在scripts/kconfig里报错。这些错在报错信息里都算明显但第一次遇到容易愣住明明在编内核为什么去找ncurses库因为配置界面要用curses库画菜单。这类“报错点和原因隔着一条街”的问题在龙芯平台上特别多所以心态上要先接受一个事实日志里的信息是准确的但它不会告诉你该去换源码还是换工具链。3. 从编译日志逐行倒推的排错实战这一章是核心。我按错误类型分别列一下每类都给出“现象—排查—解决”的完整链路你对着日志找自己那条就行。3.1 缺失架构目录的错误现象make ARCHloongarch loongarch_defconfig *** Cant find default configuration arch/loongarch/configs/loongarch_defconfig!或者更直接Makefile: No rule to make target arch/loongarch/Kconfig. Stop.排查思路不要急着修改配置文件先看arch目录里到底有什么。如果arch下没有loongarch目录说明源码不是龙芯维护的4.19分支后面的一切操作都没意义。解决方法是换源码而不是新建一个空目录。3.2 汇编错误几乎都是工具链版本问题一个很常见的场景head.S里的伪指令不被识别arch/loongarch/kernel/head.S: Assembler messages: arch/loongarch/kernel/head.S: Error: unknown pseudo-op: .dword.dword是64位下的常见伪指令绝大多数LoongArch工具链都能支持。如果这里报unknown pseudo-op基本可以断定binutils过老或者根本就是用错了工具链比如拿mips工具链来编loongarch。解决方法是换编译器不要尝试改源码绕过去。排查时先看工具链版本loongarch64-linux-gnu-as --version | head -1如果binutils版本在2.32以下直接换新版本。还有一类隐藏情况是PATH里同时存在多个工具链调用的as不是你以为的那个。用which确认一下实际路径或者用绝对路径调用工具链。3.3 menuconfig阶段的ncurses报错现象Unable to find the ncurses libraries.或者make[1]: *** [scripts/kconfig/conf] Error 1然后提示缺少curses.h。解决就是装libncurses-dev。这个比较基础但需要提醒的是有些精简系统连gcc都不一定装全别光盯着内核报错先看系统依赖。在龙芯板子本机编译时如果用的是Loongnix或统信这类国产系统软件包管理器里装依赖的命令可能不叫apt遇到报错先看当前系统是什么发行版。3.4 链接时的undefined reference现象类似drivers/misc/foo.o: undefined reference to __udivdi3__udivdi3是编译器在目标平台没有硬件除法指令或ABI限制时对64位除法生成的libgcc辅助函数调用。普通内核链接时一般不会出现出现了多半说明用的是软浮点或32位ABI配置和当前架构不匹配。解决方向检查.config里的CPU类型、ABI选项比如CONFIG_64BIT是否打开以及工具链是否支持硬件除法。还有一种常见情况是外部模块单独编译时编进模块的配置选项和当前内核不一致触发modpost的undefined!。我之前在2K1000上调一个GPIO驱动模块就遇到过这种ERROR: modpost: gpiochip_get_data [drivers/gpio/foo.ko] undefined!内核里函数确实存在但编译模块时用的头文件或配置符号和内核本体不一致。这种情况重新用内核源码的make modules_prepare再编模块通常能解决。如果还不行就去查模块对应的Kconfig有没有被打开。3.5 一次完整的报错到修复过程我把前面几个问题连成一个浓缩案例。假设你拿到一块2K3000板子、一份官方4.19原版源码、一套x86默认gcc。第一步执行make ARCHloongarch CROSS_COMPILEloongarch64-linux-gnu- loongarch_defconfig报错找不到arch/loongarch/...。此时不怪编译器怪源码。换龙芯4.19维护分支。第二步配置通过开始编译报head.S汇编错。查工具链版本发现gcc 8.3.0太老。换gcc 10加binutils 2.38的组合。第三步再编译报缺少libssl-dev导致的sign-file编译失败。装依赖。第四步终于编出vmlinux。但U-Boot引导时卡死没有串口输出。排查发现设备树没编进镜像U-Boot里fdt地址没有对应二进制。编译对应的dtb后重新打包启动正常。这个案例里每一步都不是深奥的内核知识但顺序错、链路不完整时就会误以为是代码问题。我始终强调看日志倒推就是因为报错点和真正根因经常隔了两三层。4. 内核编译成功不等于能启动镜像、设备树和启动参数4.1 先弄清楚板子的引导方式各板子的U-Boot/GRUB加载格式不一样。有人只make出vmlinux就烧进去U-Boot不认报bad magic number然后以为内核没编对。本质上vmlinux是ELF格式U-Boot若要求uImage就得用mkimage包一层。命令大概是make uImage LOADADDR0x9000000000004000LOADADDR要按板子内存布局填不是随手抄的。不同的龙芯板子DDR映射起始地址可能不相同2K3000和3A5000的地址空间也不一样这个值必须看BSP文档或U-Boot里环境变量。如果用GRUB引导直接放vmlinuz即可。这块不能省全凭板卡资料。4.2 设备树要不要单独编LoongArch下很多设备都靠设备树描述。4.19的龙芯分支会在arch/loongarch/boot/dts下放各板级dts比如2K3000的板子可能叫loongson2k3000.dts具体以仓库实际文件名为准。编译时如果没编dtb刷机后在U-Boot里依然显示加载内核成功可一旦跳转后没有任何串口输出大概率是内核根本不知道串口在哪、内存在哪。要注意的是不同维护分支的dts路径不同先在dts目录下ls看看有没有对应板型没有就对照同系列dts改一版。AFC这类嵌入式工控场景GPIO控制、看门狗、RTC这些外设全挂在设备树下面。缺了某个节点虽然不会让编译报错但运行时候模块加载不出来表现就是“驱动明明编了却没生效”这个坑非常隐蔽。4.3 rootfs和cmdline的坑内核起来了rootfs起不来也是常见现象。常见表现串口无输出console没设对比如板子默认ttyS0cmdline里写ttyAMA0自然黑屏。VFS: Unable to mount root fs检查root指向的设备以及内核里对应的文件系统和磁盘驱动是否内建。如果rootfs在sda但sata/ahci驱动被编成模块且没有initramfsmount阶段就会失败。所以与启动相关的驱动尽量以内建方式编入不要图省事用模块。这是嵌入式场景和服务器场景一个很重要的差别。启动参数示例consolettyS0,115200 root/dev/sda2 rw具体串口号和波特率看板子原理图或BSP文档。4.4 工控稳定性的编译期准备AFC项目往往要7x24小时运行。编译内核时需要考虑开启CONFIG_WATCHDOG并选对看门狗驱动配合应用层喂狗。RTC驱动内建保证断电后时间基准还在。串口、GPIO、CAN等工业接口按实际需求编入。不需要的驱动别乱开。很多报错来自把desktop板子的config直接拿来编编出来驱动和板子对不上启动时IRQ冲突、驱动崩溃全来了。我见过一个案例有人用3A5000的配置文件编2K3000编译全过启动到一半卡在USB控制器初始化。查了半天是config里开了SATA相关的一个新驱动而2K3000的SATA控制器和3A5000不完全一样导致探测逻辑跑偏。后来回到板卡基线config再逐一裁剪问题消失。5. 一张避坑检查表和我平时构建内核的习惯5.1 检查表检查项正确做法错误后果源码来源4.19龙芯维护分支编译直接找不到loongarchCPU型号uname/cpuinfo确认defconfig全乱工具链loongarch64交叉编译器gcc9binutils2.32汇编符号、链接错误系统依赖libncurses-dev libssl-dev bc flex bison等各阶段命令失败ARCH/CROSS_COMPILE每次make都要带或写环境变量编到x86去了dtb编译对应板级dtb并打包U-Boot跳转后无输出cmdlineconsole对应板子串口root指定正确黑屏或挂不上rootfs启动驱动关键驱动内建initramfs缺失时挂载失败5.2 我平时构建内核的几个习惯不直接改源码目录下的.config用make Obuild_dir把构建目录单独拆出去。这样同一份源码可以同时维护多个配置出问题也不会污染源码。我经常同时维护debug版和release版两个构建目录切来切去很方便。保存日志。编译命令带上teemake ARCHloongarch CROSS_COMPILEloongarch64-linux-gnu- -j$(nproc) 21 | tee build.log排查时grep -i error比在终端翻页强多了。make -j$(nproc)要注意内存特别是2K3000板子内存不大时本机编译容易OOM。交叉编译时-j16没问题但编dtb或模块时偶尔有依赖顺序问题先串行编一次保底。每次改配置后执行make olddefconfig把新配置项合并进来。4.19这种老内核上如果直接硬改.config很容易出现配置项被忽略的情况。编完内核后模块使用的内核版本要一致。很多“编译成功但insmod报错”的问题都是内核和模块版本错位。5.3 给新手的一个小验证脚本把下面这个脚本放在内核源码根目录编译前先跑一遍能在第一步拦截掉一半问题#!/bin/bash CROSSloongarch64-linux-gnu- if [ ! -d arch/loongarch ]; then echo [ERROR] arch/loongarch not found, check kernel source branch exit 1 fi if ! command -v ${CROSS}gcc /dev/null 21; then echo [ERROR] ${CROSS}gcc not found, check toolchain path exit 1 fi ${CROSS}gcc -dumpmachine ${CROSS}gcc --version | head -1 ${CROSS}as --version | head -1 make ARCHloongarch kernelversion这个脚本不复杂但每次开始新的板子项目都能帮我省不少时间。踩过几次坑之后我现在拿到任何一块龙芯板子第一件事就是写板卡信息备忘CPU型号、源码分支、工具链版本、defconfig、dtb文件、启动参数。全部记下来之后才去动编译。这个习惯不只在4.19上有用在之后升级内核版本、换BSP时对照这份备忘就能知道哪些变量变了、哪些报错是因为哪个环节变了。龙芯平台这几年用得越来越多AFC、电力、交通这些对长期稳定要求很高的场景都在往国产平台迁移工具链和内核版本更新也不会停。把编译链路里的每个环节都当成独立变量去验证比事后对着报错猜根因要靠谱得多。
返回列表