Linux 上的交叉编译

1. C/C++ 编译的四个步骤

从源代码到可执行文件,通常分为:预处理 → 编译 → 汇编 → 链接。

1.1 预处理(Preprocessing)

处理以#开头的预处理指令,例如展开#include引入的头文件、替换宏、处理条件编译,并去掉注释。

gcc-Emain.c-omain.i

-E表示只进行预处理。C 的预处理结果通常以.i结尾,C++ 通常以.ii结尾。

1.2 编译(Compilation)

对预处理后的代码进行语法、语义检查和优化,生成目标架构的汇编代码。

gcc-Smain.i-omain.s

-S表示执行到生成汇编代码为止。这里的“编译”指四个步骤中的这一步;平时也会用“编译”泛指整个构建过程。

1.3 汇编(Assembly)

把汇编代码转换成机器指令,生成目标文件。

gcc-cmain.s-omain.o

-c表示执行到生成目标文件为止,不进行链接。.o文件通常包含机器代码、符号表、重定位信息等,还不能直接作为程序运行。

1.4 链接(Linking)

把目标文件与其他目标文件、所需库以及启动代码组合起来,解析函数和变量等符号的引用,完成必要的重定位,生成可执行文件。

以test.c和v.c两个源文件为例,先分别生成目标文件,再将它们链接为一个程序:

# 分别生成目标文件(完成预处理、编译和汇编,不进行链接)gcc-ctest.c-otest.o gcc-cv.c-ov.o# 将两个目标文件链接成可执行文件 testgcc test.o v.o-otest# 运行程序(要求架构与当前系统兼容)./test

其中,test.o和v.o是链接的输入,-o test指定生成的可执行文件名。

例如,test.c中使用了在v.c中定义的全局变量或函数,需要在test.c中提供相应声明。分别生成目标文件后,对这些外部符号的引用通常仍待解析;链接器会在v.o中找到对应定义,把两个目标文件组合成完整程序。如果漏掉v.o,且其他输入也没有提供这些定义,通常会报undefined reference(未定义的引用)。

代码中调用printf()等库函数时,链接还需要处理对相应库的引用。动态链接的程序在运行时还需要相应的动态库。

1.5 一条命令完成整个过程

# C 程序gcc main.c-omain# C++ 程序g++ main.cpp-omain# 运行生成的程序(要求架构与当前系统兼容)./main

上面的单条构建命令会完成四个步骤,默认不会保留中间的.i、.s、.o文件。C++ 通常使用g++,它会自动链接常用的 C++ 标准库。

记忆:预处理展开代码,编译生成汇编,汇编生成目标文件,链接生成可执行文件。

2. 交叉编译工具链(以 i.MX6ULL 为例)

2.1 什么是交叉编译

交叉编译是指:在一种平台上编译,生成供另一种平台运行的程序。

例如,在 x86_64 架构的 Linux 电脑上编译代码,生成能在 i.MX6ULL 开发板上运行的 ARM 程序。电脑是开发主机,开发板是目标平台。

i.MX6ULL 是 NXP 的处理器,采用32 位 ARM Cortex-A7 内核。因此,为它编译程序时,需要使用适合目标 ARM 平台的工具链。参考:NXP i.MX6ULL 产品资料

2.2 工具链一般从哪里获取

一般优先使用芯片厂商或开发板厂商随 BSP/SDK 提供、推荐的交叉编译工具链。

  • BSP(板级支持包):包含与开发板相关的启动、内核、驱动及构建配置等内容。
  • SDK(软件开发套件):通常包含交叉编译工具链、目标系统的头文件和库,以及环境配置脚本。

以 i.MX6ULL 为例,先查看 NXP 或所用开发板厂商的配套资料,使用与板上系统版本相匹配的工具链和 SDK。工具链不仅要匹配 CPU 架构,还要考虑 ABI(如浮点调用约定)、C 库及目标系统的库版本;仅看到名称里有arm,还不足以确认兼容。

根据本次提供的百问网开发包目录,实际使用的工具链前缀是arm-buildroot-linux-gnueabihf-,后续示例采用这一前缀。

2.3 为什么叫“工具链”

它是一组配合完成编译、汇编、链接等工作的工具。以当前开发包的arm-buildroot-linux-gnueabihf-前缀为例:

工具名称主要用途
arm-buildroot-linux-gnueabihf-gccC 编译驱动,也可调度汇编、链接等步骤
arm-buildroot-linux-gnueabihf-g++C++ 编译驱动
arm-buildroot-linux-gnueabihf-as汇编器
arm-buildroot-linux-gnueabihf-ld链接器
arm-buildroot-linux-gnueabihf-ar创建静态库
arm-buildroot-linux-gnueabihf-objcopy复制、提取或转换目标文件内容

生成的test应复制到匹配的 i.MX6ULL Linux 开发板上运行;在普通 x86_64 主机上通常不能直接运行。

记忆:在电脑上编译,在开发板上运行;工具链优先选厂商配套版本。

3. ELF 文件格式

3.1 什么是 ELF

ELF(Executable and Linkable Format,可执行与可链接格式)是 Linux 平台上广泛使用的二进制文件格式。前面生成的.o目标文件、最终的可执行程序,以及.so共享库,都可以采用 ELF 格式。

ELF 文件不仅保存机器指令和数据,还保存文件头、加载信息、符号表、调试信息等,供链接器、系统加载器和调试器使用。Linux 执行 ELF 程序时,需要根据其中的加载信息建立程序的内存映像。

3.2 ELF 文件结构示意图

下图按课堂照片右侧的内容重绘,保留各层名称和分组:

重点记住:

  • 文件头与头表:记录文件基本信息、各部分的位置及加载方式。
  • .text:存放机器指令。
  • .rodata:存放只读数据,如字符串常量。
  • .data与.bss:存放全局变量和静态变量;初值非零的通常放在.data,未显式初始化或初值为零的通常放在.bss。
  • 符号与调试信息:.symtab记录函数、变量等符号,图中的.stab保存调试信息。

记忆:ELF 不只是代码和数据,还包含供链接、加载和调试使用的信息。

4. file 和 readelf 命令

4.1 用两种编译器编译同一个 test.c

在 Linux 开发主机上,进入test.c所在目录,分别使用普通gcc和开发板配套的交叉编译器编译同一份源码。以下假设交叉编译工具链的bin已加入PATH。

# 普通 gcc:生成供当前 Linux 主机运行的程序gcc test.c-otest_pc# i.MX6ULL 配套交叉编译器:生成供开发板运行的程序arm-buildroot-linux-gnueabihf-gcc test.c-otest_arm

两个命令使用同一个test.c,分别输出test_pc和test_arm。

4.2 分别使用 file 查看两个文件

成功生成两个文件后,在同一目录执行:

filetest_pcfiletest_arm

file用于识别文件类型。重点比较输出中的位数和目标架构:

对比项test_pctest_arm
位数64-bit,64 位32-bit,32 位
目标架构x86-64,面向当前电脑ARM,面向 i.MX6ULL 开发板
类型描述shared objectexecutable
动态加载器/lib64/ld-linux-x86-64.so.2/lib/ld-linux-armhf.so.3

两者都是 ELF 文件,LSB表示小端字节序,dynamically linked表示动态链接。

结论:同一个test.c,普通gcc生成 x86-64 程序,交叉编译器生成 ARM 程序;两者面向不同平台,通常不能直接互换运行。

4.3 使用 readelf -h 查看文件头

readelf -h用于查看ELF 文件头,比file提供更详细的信息。分别查看两个文件:

readelf-htest_pc readelf-htest_arm

这里的-h表示查看文件头。也可使用工具链配套的arm-buildroot-linux-gnueabihf-readelf -h test_arm查看 ARM 程序。

test_pc的实际输出:

test_arm的实际输出:

作用:确认程序的位数、目标架构、文件类型和入口等信息,帮助检查是否编译成了目标平台需要的程序。

字段test_pctest_arm
Class(位数)ELF64,64 位ELF32,32 位
Machine(架构)x86-64ARM
Type(类型)DYN,结合前面的编译方式,可能为 PIE 程序EXEC,可执行文件
Flags(架构相关标志)0x0EABI5、hard-float ABI(硬浮点调用约定)

Entry point address是程序入口,通常对应启动代码,不能直接当作main()的地址。

5. BIN 文件与 ELF 的区别

这里的BIN 指原始二进制镜像(Raw Binary),通常保存程序的机器指令和数据,没有 ELF 文件头、程序头表、节头表等结构信息,常用于裸机程序或固件烧录。

对比ELFBIN(原始二进制镜像)
内容代码、数据,以及加载、链接等结构信息,可含符号和调试信息主要是代码和数据的原始字节
加载方式加载器可以从文件头和程序头等获取加载位置与入口加载位置和入口通常由启动流程或外部约定指定
常见用途Linux 应用程序、目标文件、共享库及调试裸机程序、固件镜像及烧录
信息查看可用readelf查看内部结构没有 ELF 结构,不能直接用readelf解析

两者都可以包含 CPU 执行的机器指令。BIN 也需要正确加载和启动;把普通 Linux 应用的 ELF 转成 BIN,并不会自动变成能独立运行的裸机程序。GNUobjcopy的原始二进制输出说明可参考 官方文档。

记忆:ELF 带有结构和描述信息,BIN 是按约定使用的原始字节镜像。.bin只是常见扩展名,不能仅凭后缀确定实际格式。

6. 交叉编译工具链中常用的工具

6.1 arm-buildroot-linux-gnueabihf-size 工具

作用:统计目标文件或可执行文件中代码、数据等节的大小,帮助了解程序的空间占用。

arm-buildroot-linux-gnueabihf-size test_arm

实际输出:

大小的单位都是字节。

列名本次值含义
text865程序指令和只读内容(如字符串)占多大
data288静态区,全局变量
bss4程序启动时要清零的数据占多大,如没写初值的全局变量
dec1157前面三项加起来,用十进制表示
hex485同一个总数,换成十六进制表示
filenametest_arm查看的是哪个文件

6.2 arm-buildroot-linux-gnueabihf-nm 工具

作用:查看目标文件或可执行文件的符号表,了解函数、变量的名称、类型和地址等信息。

arm-buildroot-linux-gnueabihf-nm test_arm

可以把符号表理解成程序的**“索引表”,主要记录函数、变量等的名字、位置和类别**,方便链接器、调试器查找。

例如图中的0001040c T main:

  • main:函数的名字。
  • 0001040c:这个函数在该程序中的地址,用十六进制表示。
  • T:表示符号位于代码区。

图中的U puts@GLIBC_2.4表示:程序使用了puts,但它的定义由外部库提供,当前文件中没有定义。

普通符号表通常不影响已链接程序的正常运行。链接完成后,CPU 按机器指令执行,调用函数不需要再查找main等名字。因此,去掉普通符号表(.symtab)通常仍能运行,只是查看函数名、调试和定位问题会更困难。动态链接所需的动态符号表(.dynsym)仍需保留。

6.3 arm-buildroot-linux-gnueabihf-strip 工具

作用:去掉程序中运行时不需要的符号、调试信息等,减小文件体积。

# 查看处理前的文件信息filetest_arm# 使用交叉工具链的 strip,直接修改 test_armarm-buildroot-linux-gnueabihf-strip test_arm# 再次查看文件信息filetest_arm

  • 处理前not stripped:尚未剥离普通符号表等信息。
  • 处理后stripped:已剥离这些信息,文件通常会变小。

图中处理前后仍是32 位 ARM ELF 可执行文件,动态链接方式也没有改变。去掉的是运行时不需要的辅助信息,通常不影响程序正常运行。

6.4 arm-buildroot-linux-gnueabihf-objdump 工具

作用:反汇编,把程序中的机器码显示成汇编指令,方便查看程序实际执行的指令。

arm-buildroot-linux-gnueabihf-objdump-dtest_arm

-d表示反汇编,查看test_arm中的机器码所对应的汇编指令。

6.5 arm-buildroot-linux-gnueabihf-objcopy 工具

作用:复制、提取或转换目标文件中的内容,例如把 ELF 文件转换成 BIN 文件。

arm-buildroot-linux-gnueabihf-objcopy-Obinary test_arm test_arm.bin

-O binary指定输出为原始二进制格式,test_arm是输入文件,test_arm.bin是生成的文件。格式转换不会让这个 Linux 应用自动变成能独立运行的裸机程序。

为什么转成 BIN 后,仍不能直接脱离 Linux 运行?

因为objcopy只转换文件格式,不改变程序原来的依赖。当前的test_arm是 Linux 动态链接程序,依赖 Linux 的加载器、库和系统服务,转成 BIN 后这些需求仍然存在。

转换有什么意义?

BIN 方便交给按地址写入原始字节的烧录工具或加载程序使用。裸机开发中,程序会事先配好启动代码、链接地址和必要的硬件初始化,先生成 ELF 供调试,再转换成 BIN 供烧录或加载:

按裸机要求编写的程序 → ELF(供链接、调试)→ BIN(供烧录、加载)

因此,本次把 Linux 应用转成 BIN,主要是练习格式转换;它不会因此获得裸机运行能力。能否脱离操作系统,取决于程序怎么编写和构建,而不是文件后缀。

相关新闻

彻底禁用Chrome和Edge自动更新的三步实操指南

彻底禁用Chrome和Edge自动更新的三步实操指南

最近好几个朋友跑来问我,说系统里的浏览器老是自己偷偷升级,明明是个小版本更新,结果重启之后一堆插件不能用、界面换了、甚至还出现过打不开网页的情况。网上一搜“禁用更新”能找到一堆教程,照着改完服务、关掉任务计划&#xf…

2026/10/20 2:24:28 阅读更多 →
REA 逆向工程三步法完全指南:从看懂别人的 App 功能到自己复刻

REA 逆向工程三步法完全指南:从看懂别人的 App 功能到自己复刻

REA 逆向工程三步法完全指南:从看懂别人的 App 功能到自己复刻 【免费下载链接】rea Reverse engineer anything with agents, from app behavior down to native binaries. 项目地址: https://gitcode.com/GitHub_Trending/rea2/rea REA(Reverse…

2026/10/20 2:24:28 阅读更多 →
2026论文王炸AI智能降重工具大曝光:一键把AIGC率降至安全线!

2026论文王炸AI智能降重工具大曝光:一键把AIGC率降至安全线!

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 2:24:28 阅读更多 →

最新新闻

鸿蒙ArkTS生鲜超市实战项目解析与避坑指南

鸿蒙ArkTS生鲜超市实战项目解析与避坑指南

简介:本资源是一套基于华为鸿蒙操作系统(HarmonyOS)开发的生鲜超市行业应用实战项目,面向Android开发者转型鸿蒙生态、高校学生学习分布式应用开发及企业级HarmonyOS项目实践者。项目聚焦线上线下融合购物场景,完整呈现…

2026/10/20 20:58:45 阅读更多 →
Chrome链接总是覆盖当前页?教你设置自动在新标签页打开

Chrome链接总是覆盖当前页?教你设置自动在新标签页打开

"每次点链接都把当前页覆盖掉,后来越来越烦"——这个需求我收到过太多次了。Chrome 默认点击链接就在当前标签页打开,想在新标签页打开,要么按着 Ctrl 点,要么右键菜单选"在新标签页中打开链接"。一次两次还行…

2026/10/20 20:58:45 阅读更多 →
LMDeploy实战:开源大模型量化、推理与OpenAI兼容服务部署

LMDeploy实战:开源大模型量化、推理与OpenAI兼容服务部署

这段时间一直在折腾开源大模型的落地部署,试了不少推理框架,最后在业务环境里稳定跑下来的是 LMDeploy。如果你也在做类似的事情——想把 Qwen、Llama 这类开源模型接到自己的系统里,或者想在项目里做一个带 API 的聊天服务,那么这…

2026/10/20 20:58:45 阅读更多 →
Windows cmd进入指定目录全攻略:cd命令、跨盘符与批处理技巧

Windows cmd进入指定目录全攻略:cd命令、跨盘符与批处理技巧

Windows 下在 cmd 里进指定目录,这事看起来简单到不值一提,但实际操作中我见过太多人在这一步卡住——输入cd D:\某文件夹没反应,路径一长就报错,带空格的目录怎么都进不去,还有一个很经典的cd后提示「不是内部或外部命…

2026/10/20 20:58:45 阅读更多 →
LMDeploy 大模型部署实战:从模型量化到API服务全流程指南

LMDeploy 大模型部署实战:从模型量化到API服务全流程指南

之前帮某开发者的“模拟项目X”做大模型部署时,我在 GPU 服务器上对比过好几套推理方案,踩完一圈坑之后,现在手里的长期方案基本固定成了 LMDeploy。如果你手里已经有一个开源对话模型,正打算放进业务里做 API 服务,或…

2026/10/20 20:58:45 阅读更多 →
TensorFlow鞋类识别实战:Fashion MNIST图像分类全流程

TensorFlow鞋类识别实战:Fashion MNIST图像分类全流程

前段时间重新整理之前的深度学习实验笔记,翻到鞋类识别(Fashion MNIST)这个项目时,觉得特别值得拿出来单独写一篇。原因很简单:很多人入门深度学习时,都会被建议先跑通手写数字识别(MNIST&#…

2026/10/20 20:57:37 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 1:01:30 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →