资讯动态

交叉编译报错 cannot find crt1.o 的原因与彻底解决方案

发布时间:2026/10/1 13:39:36 来源:尧图企业网站定制
干过交叉编译的人十有八九都撞见过这个报错arm-linux-gnueabihf-gcc main.c -o main /usr/lib/gcc-cross/arm-linux-gnueabihf/9/../../../../arm-linux-gnueabihf/bin/ld: cannot find crt1.o: No such file or directory collect2: error: ld returned 1 exit status我第一次碰到时第一反应是工具链是不是装坏了重装了三遍 gcc-arm-linux-gnueabihf问题照旧。后来才弄明白这压根不是工具链损坏而是链接器在找 C 运行时启动文件时搜错了目录。这问题在 Ubuntu 上交叉编译时尤其常见Qt 交叉编译、Boost 库交叉编译、裸机 ARM 开发里都能碰到。这篇文章就围绕这个报错把 crt1.o 是什么、为什么会找不到、怎么彻底解决讲清楚顺便把排查思路也整理成一套流程以后再遇到类似cannot find xxx的问题可以直接套用。1. 问题本质先搞懂 crt1.o 是什么为什么交叉编译会找不到它1.1 crt1.o 到底是个什么文件crt 是 C Runtime 的缩写crt1.o 是 C 程序运行时启动文件由 glibc 提供。它的核心职责是定义程序的入口点_start负责在 main 函数被调用之前完成一系列的初始化工作比如设置栈指针、解析命令行参数、初始化全局变量、准备环境变量等。你可以把它理解成程序出生后的第一口呼吸——操作系统加载可执行文件后首先进入的就是 crt1.o 里编译出来的_start代码而不是 main 函数。除了 crt1.oC 运行时还有几个兄弟文件crti.o提供函数 prologue函数序言负责为初始化函数表__init_array_start和__init_array_end做准备工作。crtn.o与 crti.o 配对提供函数 epilogue函数尾声。crtbegin.o / crtend.o由 GCC 编译器提供负责 C 全局构造和析构相关的注册逻辑。crt0.o / crt0S.o常见于裸机开发或没有操作系统支持的场景由芯片厂商或独立工具链提供。在链接一个普通可执行程序时gcc 会自动把 crt1.o、crti.o、crtbegin.o 等文件作为链接输入的一部分传给 ld。所以当你看到cannot find crt1.o时本质是链接器按照默认搜索路径找不到启动文件。它不是报我读不懂这个文件而是报我压根没找到这个文件。1.2 交叉编译的链路与本地编译有什么不同本地编译时gcc 编译和链接都是用宿主机的头文件和库路径天然匹配。比如在 x86_64 的 Ubuntu 上编译 x86_64 程序gcc 默认去/usr/lib/x86_64-linux-gnu/找 crt1.o、libc.so 这些文件一找一个准。交叉编译就不同了。编译器的目标平台是 ARM或者其他架构用到的头文件、启动文件、库文件都必须是对应目标架构的版本。问题是gcc 交叉编译器在寻找这些文件时如果没做特殊配置会沿用一整套基于宿主机的默认搜索路径。你可以用下面两条命令看得很清楚arm-linux-gnueabihf-gcc -print-search-dirs arm-linux-gnueabihf-gcc -print-sysroot在我第一次出问题的机器上-print-sysroot的输出是空的等于说 sysroot 没设置gcc 只能去宿主机那一堆路径里翻找 ARM 版本的 crt1.o结果自然是找不到。这就是整个问题的直接原因。2. 排查过程从报错到定位问题的完整思路2.1 先别急着重装工具链用三招看清真相第一招看链接器的详细输出。在编译命令末尾加上-Wl,--verboseld 会把完整的搜库过程打印出来。arm-linux-gnueabihf-gcc main.c -o main -Wl,--verbose 21 | grep crt1.o输出里可以看到 ld 逐个尝试了哪些目录去查找 crt1.o比如/usr/lib/gcc-cross/...、/usr/lib/...等。如果这些路径里都没有 ARM 架构的 crt1.o那基本就是路径配置的问题。第二招确认工具链的 sysroot 到底在哪。sysroot 是交叉编译器的逻辑根目录gcc 在找启动文件和库文件时会在 sysroot 指定的前缀下搜索。你可以用-print-sysroot查看如果输出为空就需要手动指定。第三招直接在文件系统上定位 crt1.o 的真实位置然后和链接器搜索的目录对比看哪里没接上find /usr -name crt1.o 2/dev/null在我机器上输出是/usr/arm-linux-gnueabihf/lib/crt1.o /usr/arm-linux-gnueabihf/usr/lib/crt1.o对照前面的搜索结果链接器搜索列表里并没有包含/usr/arm-linux-gnueabihf这个路径问题定位就清晰了工具链装好了文件也在就是 gcc/ld 的搜索路径没指过去。这和工具链损坏是两码事。2.2 其他容易混淆的找不到场景cannot find crt1.o并不是唯一一种与路径相关的问题。在实际项目里往往还伴随其他类似的报错它们的定位思路是相通的cannot find -lc找不到 C 库libc.so通常是 libc6-dev 或对应交叉版本没安装或者 -L 参数没指对。cannot find -lstdc交叉工具链缺少 libstdc 开发包常见于 C 交叉编译。cannot find crti.o/cannot find crtn.o启动文件不完整同样和 sysroot 路径有关。这类问题有一个统一的口诀先确认文件在不在再确认链接器有没有搜到那个路径。如果文件在但链接器没搜到就是路径配置问题如果文件根本不存在就是工具链组件缺失或没安装对应架构的开发库。3. 解决方案三种有效做法与验证流程3.1 方案一手动指定 --sysroot最直接如果你用的工具链提供了完整的 sysroot 目录最干净的方式就是在编译链接时加上--sysrootarm-linux-gnueabihf-gcc main.c -o main --sysroot/usr/arm-linux-gnueabihf这个参数会告诉 gcc 和底层的 ld所有默认搜索路径的根都在/usr/arm-linux-gnueabihf下。于是 gcc 搜索 crt1.o 时实际去的就是/usr/arm-linux-gnueabihf/usr/lib/crt1.o或/usr/arm-linux-gnueabihf/lib/crt1.o正好能命中。如果你的工具链是通过 apt 安装的比如 Ubuntu 上的 gcc-arm-linux-gnueabihf它的 sysroot 通常就在/usr/arm-linux-gnueabihf下。你可以先验证一下这个目录结构是否完整ls /usr/arm-linux-gnueabihf正常情况下里面有lib、usr/lib、usr/include等目录。Qt 交叉编译时修改 qmake.conf 里的QMAKE_LFLAGS --sysroot/usr/arm-linux-gnueabihf也是这个原理。3.2 方案二用 -L 和 -B 补充搜索路径临时应急有时 sysroot 因为各种原因不能直接用比如工具链的 sysroot 不完整或者你把 crt1.o 单独拷贝到了自定义目录。这时可以用-B参数告诉链接器去哪里找 crt1.o 这类启动文件arm-linux-gnueabihf-gcc main.c -o main -B /usr/arm-linux-gnueabihf/lib/同时也建议把库搜索路径加上arm-linux-gnueabihf-gcc main.c -o main -L /usr/arm-linux-gnueabihf/lib -B /usr/arm-linux-gnueabihf/lib/需要强调的是-B和-L的作用并不相同-B告诉 gcc 去哪里找编译器内部组件包括启动文件 crt*.o-L告诉链接器去哪里找-l指定的库比如-lm对应的 libm.so。两者一起用才稳妥只加-L往往治不了crt1.o找不到的问题。这种方法适合快速验证是不是路径问题但不建议长期使用因为每一条编译命令都要多加参数很容易漏。更好的做法是把参数写进 Makefile 或 CMake 的交叉编译工具链文件里。3.3 方案三检查工具链组件并重新安装如果find /usr -name crt1.o找不到任何文件说明工具链本身就不完整。在 Ubuntu 上交叉编译 ARM 需要安装一组配套包sudo apt install gcc-arm-linux-gnueabihf sudo apt install libc6-dev-armhf-cross sudo apt install g-arm-linux-gnueabihflibc6-dev-armhf-cross这个包是重点它提供了 ARM 架构的 C 库开发文件包括 crt1.o、libc.so、libc.a 等。只装 gcc 不装这个包就会出现编译器在但启动文件不在的尴尬局面。如果你用的是自己下载的独立工具链比如 ARM 官方或芯片厂商提供的安装解压后一定要先设置环境变量export PATH/opt/arm-gcc/bin:$PATH export CROSS_COMPILEarm-none-linux-gnueabihf-然后执行arm-none-linux-gnueabihf-gcc -v确认版本正常再用-print-sysroot确认识别到了内部的 sysroot。很多独立工具链自带的 sysroot 就在工具链目录内不需要手动指定--sysroot但前提是 PATH 指向正确。3.4 验证如何确认交叉编译产物真的正常解决问题后用一段最简单的代码验证整个工具链是否可用#include stdio.h int main(void) { printf(hello arm!\n); return 0; }编译命令arm-linux-gnueabihf-gcc hello.c -o hello生成 hello 后用 file 命令查看格式file hello如果输出是hello: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, ...说明架构正确交叉编译链路已经打通。进一步验证运行行为如果条件允许可以拷贝到 ARM 板子上执行或者安装 qemu-user 在宿主机上模拟运行sudo apt install qemu-user ./helloqemu-user 会直接运行 ARM 可执行文件输出hello arm!就说明程序不仅链接成功运行时依赖的动态库路径也正常。4. 工具链搜索路径背后的原理以及 Qt/CMake 场景中的常见坑4.1 ld 的搜索路径是怎么组织起来的链接器 ld 搜索 crt1.o 和各类库文件时有一套固定的路径优先级。大致从高到低是命令行中-L指定的路径。--sysroot指定的根目录下的默认路径比如/usr/lib、/lib。链接器编译时内置的默认搜索路径。环境变量LIBRARY_PATH中指定的路径。gcc 在调用 ld 之前也会根据自身配置拼接一串路径比如/usr/lib/gcc-cross/arm-linux-gnueabihf/9/...、/usr/lib/x86_64-linux-gnu/...。如果交叉编译时 sysroot 为空gcc 会把宿主机x86_64的路径也传给 ldld 在那些路径下找不到 ARM 版本的 crt1.o最终报错。可以从两个方向解决一是给 ld 传递正确的搜索路径-L/-B/--sysroot二是让 gcc 在构造路径时带上正确的前缀配置好 sysroot。4.2 CMake 交叉编译时如何配置 sysroot项目里用 CMake 做交叉编译时推荐用工具链文件toolchain.cmake来集中管理配置。一个典型的 ARM 交叉编译工具链文件长这样set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc) set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g) set(CMAKE_SYSROOT /usr/arm-linux-gnueabihf) set(CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)使用方式mkdir build-arm cd build-arm cmake .. -DCMAKE_TOOLCHAIN_FILE../toolchain.cmake make注意CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER的含义是查找程序比如编译器、链接器等时不依赖 sysroot因为程序是宿主机上的可执行文件而头文件和库文件必须在 sysroot 内查找这样可以避免误用宿主机的头文件。4.3 Qt 交叉编译中的同款问题Qt 交叉编译时qmake 的 qmake.conf 中如果少写了 sysroot 或库路径会出现同样的问题。以 Qt 5.12.10 交叉编译 ARM 为例常见的补充项包括QMAKE_LFLAGS --sysroot/usr/arm-linux-gnueabihf QMAKE_INCDIR /usr/arm-linux-gnueabihf/usr/include QMAKE_LIBDIR /usr/arm-linux-gnueabihf/usr/lib另外如果报错信息中还出现了/bin/bash^M: bad interpreter这样的内容一般是脚本文件在 Windows 下编辑后保存的换行符问题和交叉编译本身无关但经常在一个部署流程中同时出现。用sed -i s/\r$// xxx.sh清理换行符就能解决。5. 常见问题与排查技巧实录5.1 典型报错速查表为了让你以后遇到类似问题有个快速索引我把交叉编译中常见的找不到类报错整理成一张速查表报错信息可能原因排查方向cannot find crt1.osysroot 未设置或工具链缺组件确认-print-sysroot输出检查/usr/arm-linux-gnueabihf/lib/crt1.o是否存在cannot find -lclibc 开发包未安装安装libc6-dev-armhf-cross或检查-L路径cannot find -lstdcC 运行时库缺失安装g-arm-linux-gnueabihf确认工具链完整cannot find crti.o/crtn.o启动文件缺失检查 glibc 开发包是否安装完整/bin/bash^M: bad interpreter脚本换行符为 CRLF用sed -i s/\r$//转换fatal error: xxx.h: No such file or directory头文件搜索路径未指定检查-I参数、CMAKE_C_FLAGS或CFLAGS实际排查时可以按列出的顺序逐项检查10 分钟内基本能定位到问题。5.2 一次真实的排查过程记录我最近一次碰到这个报错是在给一个 Rust 项目做 ARM 交叉编译时出现的。Rust 项目本身用了 cc crate 来编译一段 C 代码报错信息是ld: cannot find crt1.o: No such file or directory。我第一时间先确认宿主机架构uname -m输出aarch64。然后我又查了一下 sysrootgcc -print-sysroot输出为空。因为是在 aarch64 宿主上交叉编译 ARMv7 目标sysroot 必须手动指定。我在项目里通过环境变量给 cc crate 传了参数export CFLAGS--sysroot/usr/arm-linux-gnueabihf export LDFLAGS--sysroot/usr/arm-linux-gnueabihf重新构建后问题消失。这说明在我的场景中编译器是从 CFLAGS/LDFLAGS 环境变量读取参数的配置好 sysroot 后链接器立刻就能找到对应的启动文件。5.3 几个值得养成的交叉编译习惯踩坑多了以后我自己总结出来几个习惯可以大幅减少这类问题的出现频率第一拿到一个新的交叉工具链不要直接编译大项目先编译一个 hello world 验证基础链路这样能把工具链是否有问题和项目配置是否有问题分开来。如果 hello world 都过不了先解决环境问题再谈项目。第二优先使用系统包管理器安装交叉工具链必要时再用独立工具链。系统包管理器的好处是依赖会自动安装好比如libc6-dev-armhf-cross这类配套包不会被漏掉。独立工具链虽然灵活但目录结构、sysroot 位置全要自己管理出问题的概率高很多。自己解压的工具链一定要手动检查是否包含完整的 sysroot。第三交叉编译参数尽可能集中管理。CMake 用 toolchain.cmakeQt 用 qmake.confMakefile 用顶层的变量定义不要把--sysroot、-L这类参数散落在各种地方。集中管理的好处是出问题时有统一的排查入口。第四遇到No such file or directory先冷静不要条件反射重装整个环境。八成问题出在路径配置上先按文件在不在 → 链接器搜没搜到该路径的顺序排查往往比重装更快。6. 写在最后的实操心得根据我的个人经验交叉编译报错百分之七八十都是路径问题cannot find crt1.o只是这类问题的一个经典代表。解决它的核心思路就是两条让链接器知道文件在哪或者让文件出现在链接器搜索的路径里。--sysroot是正统做法它一次解决了启动文件、库文件、头文件三者的根目录问题比一个个加-L要从容得多。如果你的交叉编译任务经常要做建议把 sysroot 的配置固化到构建脚本或工具链文件里而不是每次都靠命令行手动敲。最后再分享一个小技巧如果你用的是自定义工具链git 仓库里一定要放一份build_env.sh把 PATH、CROSS_COMPILE、CFLAGS、LDFLAGS 都写清楚。交叉编译环境这东西三个月不管自己都会忘环境脚本是成本最低的防呆手段。等你再一次在另一个项目里遇到cannot find crt1.o时打开这个脚本跑一遍 source大多数问题就都消停了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑