当前位置: 首页 > news >正文

Ubuntu 22.04 编译内核踩坑记:手把手教你用gcc-9解决 `yylloc` 重定义错误

Ubuntu 22.04 内核编译实战:gcc版本冲突与yylloc重定义问题的深度解析

第一次在Ubuntu 22.04上尝试编译Linux内核时,那种兴奋感很快被一盆冷水浇灭——屏幕上赫然显示着"multiple definition of `yylloc'"的错误提示。作为一个长期使用Linux但从未深入内核编译的开发者,我意识到自己正站在新手与进阶用户的分水岭上。这个问题看似简单,却涉及编译器版本管理、内核构建系统工作原理等核心知识。本文将带你完整经历从错误分析到解决方案的全过程,不仅解决眼前的问题,更理解背后的技术原理。

1. 问题现象与初步诊断

当你在Ubuntu 22.04上执行make命令开始内核编译时,可能会在构建dtc(设备树编译器)组件时遇到这样的错误:

/usr/bin/ld: scripts/dtc/dtc-parser.tab.o:(.bss+0x10): multiple definition of `yylloc'; scripts/dtc/dtc-lexer.lex.o:(.bss+0x0): first defined here collect2: error: ld returned 1 exit status

这个错误的核心是链接器(ld)发现了符号yylloc的多次定义。具体表现为:

  • dtc-parser.tab.odtc-lexer.lex.o两个目标文件都定义了yylloc变量
  • 链接器无法确定该使用哪个定义,因此报错并终止编译过程

提示:yylloc是Flex/Bison词法分析工具生成的代码中用于记录位置信息的全局变量,正常情况下应该只被定义一次。

通过gcc --version检查当前编译器版本,你会发现Ubuntu 22.04默认安装的是gcc 11.2.0。这正是问题的根源——较新的gcc版本在处理某些代码生成规则时与内核构建系统中的老代码存在兼容性问题。

2. 技术背景:为什么gcc 11会导致这个问题

要彻底理解这个问题,我们需要深入几个技术层面:

2.1 Flex/Bison工具链的工作原理

Linux内核构建系统使用Flex和Bison来生成词法分析器和语法分析器。这些工具会自动生成包含yylloc变量的代码:

  • dtc-lexer.lex.c由Flex生成,包含词法分析代码
  • dtc-parser.tab.c由Bison生成,包含语法分析代码
  • 两者都需要跟踪当前解析位置,因此都声明了yylloc变量

在传统C编程实践中,这种全局变量应该在一个源文件中定义,在其他文件中声明为extern。但自动生成的代码没有遵循这个约定。

2.2 gcc版本差异的影响

gcc 11相较于gcc 9在链接器行为上有一个重要变化:

gcc版本对多重定义的处理方式
gcc 9及以下容忍某些特定情况下的多重定义
gcc 10及以上严格执行标准,拒绝所有多重定义

这种变化是为了更好地遵循C语言标准,但却破坏了那些依赖旧行为的代码,包括Linux内核构建系统中的某些部分。

3. 解决方案:安装并切换至gcc-9

解决这个问题的直接方法是回退到gcc 9.x版本。以下是详细步骤:

3.1 安装gcc-9和g++-9

首先添加必要的软件源并安装旧版本编译器:

sudo apt update sudo apt install gcc-9 g++-9

验证安装是否成功:

gcc-9 --version # 应显示9.x.x版本 g++-9 --version # 应显示9.x.x版本

3.2 配置系统默认编译器

Ubuntu提供了update-alternatives工具来管理多个版本的软件:

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 100 sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 100

这里的参数含义是:

  • /usr/bin/gcc:符号链接路径
  • gcc:备选方案组的名称
  • /usr/bin/gcc-9:实际可执行文件路径
  • 100:优先级(数字越大优先级越高)

3.3 验证编译器切换

检查当前默认编译器版本:

gcc --version # 现在应该显示9.x.x g++ --version # 现在应该显示9.x.x

如果需要切换回gcc 11,可以使用:

sudo update-alternatives --config gcc sudo update-alternatives --config g++

然后从交互式菜单中选择gcc-11对应的编号。

4. 深入解决方案:替代方法与原理分析

除了简单的版本降级,我们还可以考虑其他解决方案,并深入理解其工作原理。

4.1 修改内核构建系统

对于希望保持gcc 11的用户,可以尝试修改内核源码:

  1. 编辑scripts/dtc/Makefile,找到dtc的构建规则
  2. 添加-fcommon编译选项:
dtc: $(DTC_OBJS) $(CC) $(CFLAGS) -fcommon -o $@ $(DTC_OBJS)

-fcommon选项告诉gcc使用传统的common symbol行为,即允许某些形式的多重定义。

注意:这种方法需要一定的Makefile知识,且可能影响其他部分的编译。

4.2 为什么gcc-9能解决问题

gcc 9及以下版本默认采用"common symbol"处理方式,这意味着:

  • 多个目标文件可以包含同名的未初始化全局变量
  • 链接时这些定义会被合并为一个
  • 这实际上是违反了C标准的传统行为,但被广泛使用

gcc 10及以后版本改为默认-fno-common,更严格地遵循标准,因此会报错。

5. 编译内核的完整流程与验证

现在我们已经解决了编译器问题,让我们回顾一下完整的内核编译流程:

5.1 准备编译环境

安装必要的依赖:

sudo apt install build-essential libncurses-dev bison flex libssl-dev libelf-dev

5.2 获取内核源码

可以从官方仓库克隆,或下载特定版本:

git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git cd linux git checkout v5.15 # 选择一个稳定版本

5.3 配置内核选项

make menuconfig # 基于ncurses的交互式配置 # 或使用当前系统配置作为基础 cp /boot/config-$(uname -r) .config make oldconfig

5.4 开始编译

make -j$(nproc) # 使用所有CPU核心加速编译

编译成功后安装内核模块:

sudo make modules_install sudo make install

5.5 验证新内核

重启系统并在GRUB菜单中选择新编译的内核:

uname -r # 检查当前运行的内核版本

6. 进阶技巧与注意事项

在长期的内核编译实践中,我总结了一些有用的技巧:

6.1 多版本编译器管理

使用update-alternatives可以轻松管理多个gcc版本:

sudo update-alternatives --config gcc

这会列出所有已注册的gcc版本,让你交互式选择。

6.2 内核配置建议

对于初次编译内核的用户,建议:

  • 从现有配置开始(/boot/config-$(uname -r)
  • 只修改必要的选项
  • 保持大多数选项为默认值

6.3 常见问题排查

如果遇到其他编译错误,可以:

  1. 检查make输出的完整错误信息
  2. 搜索内核邮件列表和论坛
  3. 尝试更旧或更新的内核版本
  4. 确保所有依赖项已正确安装

7. 理解更深层次:内核构建系统解析

为了从根本上避免类似问题,我们需要理解内核构建系统的工作原理:

7.1 Kbuild系统概述

Linux内核使用独特的Kbuild系统,主要特点包括:

  • 递归式Makefile结构
  • 自动生成依赖关系
  • 支持多种架构和配置
  • 复杂的预处理步骤

7.2 dtc在内核中的作用

设备树编译器(dtc)负责:

  • 将设备树源文件(.dts)编译为二进制格式(.dtb)
  • 支持设备树覆盖机制
  • 提供设备树反编译和验证功能

7.3 内核构建的各个阶段

  1. 配置阶段:生成.config文件和头文件
  2. 内核代码编译:生成vmlinux和内置模块
  3. 架构特定处理:如x86的bzImage生成
  4. 模块编译:生成可加载的内核模块
  5. 安装阶段:将文件复制到正确位置

在Ubuntu 22.04上编译内核遇到的yylloc重定义问题,表面上是编译器版本兼容性问题,实际上反映了开源生态中版本迭代带来的挑战。通过这次调试经历,我不仅解决了眼前的问题,更深入理解了Linux内核构建系统的运作机制。记住,在开源世界里,遇到问题时的最佳策略是:阅读错误信息、理解底层原理、寻找针对性解决方案。

http://www.jsqmd.com/news/567053/

相关文章:

  • 如何解析B站视频:零门槛的bilibili-parse使用指南
  • 北海穷游必吃的美食攻略
  • 51单片机实战:用快马平台生成智能农业监测系统,从需求到完整代码
  • AI 牛马项圈公司新估值 20 亿美元,亚秒级实时监控;ProactiveVideoQA:首个视频多模态模型主动交互基准丨日报
  • 野火STM32F429与LVGL实战:从CubeMX配置到GUI移植全解析
  • AI智能体在测试自动化中的作用
  • 告别CNN!用Vision Transformer(ViT)和CellViT搞定病理切片细胞分割,附完整代码与避坑指南
  • 【超详细教程】手把手教你部署OpenClaw,两步解锁龙虾AI助理!
  • sqlmap工具超详细使用教程:从零基础到实战攻防(附避坑指南)
  • QMCDecode:突破QQ音乐格式枷锁,高效实现音乐自由播放
  • 什么是BGP协议
  • 内网UOS服务器装不了软件?手把手教你用ISO搭建本地YUM源,离线也能秒装包!
  • 别再用HAL_Delay了!STM32F407用CubeMX配置GPIO实现精准LED闪烁(附源码)
  • 智能手表与手机数据打架?用HHAR数据集实战多设备传感器融合与校准
  • 巴法云MQTT实战:避开ESP8266连接与App Inventor开发的5个常见坑
  • 虚拟人格入殓师:为废弃AI写墓志铭
  • 终极AI编程助手OpenCode:如何5分钟告别传统编码困境
  • SD2026 一轮省集
  • Altium Designer实战:多层PCB布线中的信号完整性与热焊盘设计
  • Whisper-WebUI语音转写工具从部署到优化全指南:解决环境配置与功能实现难题
  • 智能商品标题生成:EcomGPT-7B+Transformer实战
  • 从直流潮流到PTDF:一个电力‘老司机’的MATPOWER避坑指南与效率技巧
  • Graphormer科研效率提升方案:替代传统DFT计算的轻量级AI代理模型
  • 【企业级MCP服务模板首发】:内置JWT鉴权+OpenTelemetry追踪+动态插件热加载——仅限首批200位开发者获取的v3.2.0私有分支
  • 玩过逆变器的的朋友都知道,T型三电平这货天生自带谐波克星属性。咱们今天重点聊聊怎么在仿真里搞出五电平线电压波形,特别是当负载突然不平衡时怎么稳住场子
  • Penpot Docker实战部署:从零到生产环境的完全指南
  • 告别相位差烦恼:手把手教你用FPGA实现AD9371多片同步(附IQ旋转测量实战)
  • 梦之形修改器
  • DDR5内存自刷新模式详解:如何正确配置2N模式下的self refresh operation
  • Arduino移位寄存器引脚扩展库MorePins详解