ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Jetson Orin交叉编译ld-linux-aarch64.so.1缺失的根因与修复

Jetson Orin交叉编译ld-linux-aarch64.so.1缺失的根因与修复 说实话第一次在Jetson Orin上做交叉编译时我遇到的问题并不是CUDA工具链不兼容也不是OpenCV的依赖地狱而是一个看起来人畜无害、报错时却能把整个构建流程卡死的动态链接器缺失问题ld-linux-aarch64.so.1: No such file or directory。这个报错出现的位置很不固定有时在链接阶段直接抛出来有时在编译通过后把产物拷到Orin上一运行才冒出来。更头疼的是网上关于这个报错的说法五花八门有的让你重装工具链有的让你做软链接但都没解释清楚背后的机制。这篇内容我会从三个真实场景讲起把这个报错的根源、修复步骤、以及一套配置文件级的标准方案完整整理出来。无论你是在Ubuntu x86_64宿主机上给Jetson Orin、Orin Nano还是Orin NX交叉编译应用只要遇到类似的问题按这篇文章里的链路排查基本都能解决。1. 这个报错到底长什么样先还原三个典型现场很多人在看到ld-linux-aarch64.so.1缺失时报错时第一反应是去/lib下找这个文件但问题往往比这复杂得多。先看三个我实际遇到过的报错场景你对照自己当前所处阶段就能快速定位是哪一个环节出了问题。1.1 场景A工具链已安装但链接阶段报“无法找到-lc”这是最典型的一种。宿主机上已经执行过apt install gcc-aarch64-linux-gnu编译单个.c文件也正常但一旦进入真正的链接步骤比如用g链接一个包含了多个目标文件的程序或者链接一个需要libc的静态库时终端就会输出aarch64-linux-gnu-ld: cannot find -lc aarch64-linux-gnu-ld: cannot find -lstdc aarch64-linux-gnu-ld: cannot find -lm collect2: error: ld returned 1 exit status不要小看这三行cannot find它其实是在告诉你链接器知道你要链接libc、libstdc、libm这些库但它没有在搜索路径里找到适用于aarch64架构的版本。正常情况下如果你安装了完整的交叉编译环境libc.so这类库文件会被放在/usr/aarch64-linux-gnu/lib/或工具链自带的sysroot目录下。缺失时这个目录不是空的、就是工具链根本没有往这个目录里搜索。1.2 场景B编译链接都通过但Jetson上运行时提示找不到ld-linux-aarch64.so.1这个场景最迷惑。你在宿主机上交叉编译生成了可执行文件用file命令看也是ELF 64-bit LSB executable, ARM aarch64感觉一切正常。结果把文件拷贝到Jetson Orin开发板上执行时却直接报-bash: ./my_app: No such file or directory注意这个报错经常会被误判为“文件不存在”但它很可能是动态链接器缺失。你可以用ldd命令验证一下$ ldd ./my_app linux-vdso.so.1 (0x0000ffff8f7f0000) libstdc.so.6 not found libm.so.6 not found libgcc_s.so.1 not found libc.so.6 not found ld-linux-aarch64.so.1 not found看到ld-linux-aarch64.so.1 not found说明系统里没有这个动态链接器。这个文件其实是glibc的一部分负责在程序启动时解析并加载其他共享库相当于程序的“开幕主持人”。如果连主持人都没到场后面的节目自然没法开始。1.3 场景CCMake交叉编译时sysroot路径指向错误第三种常见场景出现在使用CMake工具链文件时。很多人会参照网上的模板写一个toolchain.cmake里面大致长这样set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc) set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g)但没有显式指定CMAKE_SYSROOT。CMake在内部做编译器检查时会执行一个测试程序来验证工具链能否正常工作。如果它生成的临时可执行文件在运行或链接阶段报告CMake Error: try_compile() failed The C compiler /usr/bin/aarch64-linux-gnu-gcc is not able to compile a simple test program.后台的CMakeError.log里往往就藏着ld-linux-aarch64.so.1: No such file or directory。原因多半是编译器检查阶段用到了host平台的搜索路径而宿主机的/lib目录下没有aarch64的动态链接器。2. 根因拆解动态链接器、sysroot和交叉编译工具链究竟什么关系在修复之前一定要把这三样东西的关系搞清楚。很多人修了半天就是因为不理解ld-linux-aarch64.so.1到底属于谁。2.1 动态链接器不是编译器的一部分它属于目标系统的glibc当你运行任何动态链接的Linux程序时内核首先加载的不是你写的代码而是一个叫“程序解释器”的小模块。在x86_64平台上它叫ld-linux-x86-64.so.2在ARM64平台上就是你看到的ld-linux-aarch64.so.1。这个解释器的工作流程是读取ELF文件头部中的PT_INTERP段找到自己的路径。把程序本身的依赖库比如libc.so.6、libstdc.so.6逐个加载进内存。做符号重定位把所有外部函数调用绑定到实际地址。把控制权交给你程序的_start入口。交叉编译工具链里的aarch64-linux-gnu-gcc只是一个编译器驱动它负责调用预处理器、编译器、汇编器和链接器。真正执行动态加载这个动作的是目标设备根文件系统里的glibc。所以在宿主机上编译出来的程序拿到Jetson上运行必须由Jetson系统里/lib/aarch64-linux-gnu/ld-linux-aarch64.so.1来加载。一旦这个文件缺失程序就没法启动。2.2 sysroot交叉编译时的“目标系统替身”交叉编译的本质是“在A平台上编译出B平台能运行的程序”。因为宿主机和目标的CPU架构、库文件布局都不一样编译器需要一个“目标系统的影子目录”里面存放目标架构的头文件和库文件这个东西就叫sysroot。正常情况下你使用sdkmanager刷写Jetson Orin后得到的BSP包解压开就是一个完整的Ubuntu根文件系统内部结构大概是Linux_for_Tegra/rootfs/ ├── lib/ │ ├── aarch64-linux-gnu/ │ │ ├── ld-linux-aarch64.so.1 │ │ ├── libc.so.6 │ │ ├── libm.so.6 │ │ └── ... ├── usr/ │ ├── include/ │ ├── lib/ │ └── ...如果你把这个目录通过--sysroot/path/to/rootfs交给编译器它就会自动去里面找头文件和库不再乱翻宿主机的目录。2.3 用readelf看清程序真正依赖的解释器这里教大家一个排查绝活用readelf直接查看ELF文件里注册的解释器路径readelf -l my_app | grep interpreter正常输出应该是interpreter /lib/ld-linux-aarch64.so.1如果这个路径在你的Jetson根文件系统里不存在或者被某个编译参数改到了一个无效路径程序就必然跑不起来。理解了这条链路再回头看第一个场景里的cannot find -lc本质上是编译器在链接时没有找到对应架构的libc库文件而场景B里ldd报ld-linux-aarch64.so.1 not found是程序运行时在目标系统上找不到动态链接器。两者虽然报错不同但修复思路高度一致让编译器在正确的位置找到目标架构的系统库与动态链接器。3. 快速修复在Ubuntu宿主机上补齐aarch64架构的系统库如果你的目的只是尽快把程序编译出来不追求源码级sysroot管理那么最快的路径就是安装Ubuntu官方提供的aarch64交叉工具链与arm64运行库。3.1 启用arm64架构并安装交叉编译包在Ubuntu 20.04或22.04宿主机上按顺序执行dpkg --print-foreign-architectures如果输出里没有arm64先添加sudo dpkg --add-architecture arm64 sudo apt update然后安装交叉编译工具链。这里我推荐直接装crossbuild-essential-arm64它会一次性拉齐gcc、g、libc6-dev和binutils等核心组件sudo apt install -y crossbuild-essential-arm64装完检查一下关键文件aarch64-linux-gnu-gcc --version ls -l /usr/aarch64-linux-gnu/lib/ld-linux-aarch64.so.1正常情况下/usr/aarch64-linux-gnu/lib/目录下会有ld-linux-aarch64.so.1以及一堆.so软链接。这就是Debian系交叉工具链默认使用的“准sysroot”目录。3.2 手工添加libc6:arm64的时机与风险在某些较精简的Ubuntu环境里即使装了crossbuild-essential-arm64/usr/aarch64-linux-gnu下的库可能仍然不完整。此时可以补装arm64版的libc和libstdcsudo apt install -y libc6:arm64 libstdc6:arm64这个操作会把arm64版本的动态库安装到/lib/aarch64-linux-gnu/。装上之后部分工具链会自动把该目录纳入搜索路径。但我要提醒一点不要为了凑路径就盲目在宿主机上添加arm64架构库。如果宿主机上的qt、gstreamer等原生x86软件也依赖libc多架构共存偶尔会引发apt包冲突。更稳妥的做法是检查工具链的默认搜索路径直接确认目标目录是否存在aarch64-linux-gnu-gcc -print-sysroot aarch64-linux-gnu-gcc -print-search-dirs3.3 验证编译产物是否正常库装好后写一个最简单的Hello World做端到端验证。#include stdio.h int main() { printf(hello jetson orin\n); return 0; }用交叉编译器编译aarch64-linux-gnu-gcc hello.c -o hello然后依次检查三道工序每道都不能少file hello # 期望: ELF 64-bit LSB executable, ARM aarch64 readelf -l hello | grep interpreter # 期望: /lib/ld-linux-aarch64.so.1 aarch64-linux-gnu-readelf -d hello | grep NEEDED # 期望: libc.so.6如果这三步输出都符合预期说明你的宿主机交叉编译环境已经可以正常工作了。此时把hello拷贝到Jetson上执行通常不会再出现ld-linux-aarch64.so.1报错。4. 专业做法利用Jetson Linux BSP的rootfs作为真正sysroot快速修复只适用于简单项目。一旦你的程序依赖了多个第三方库比如OpenCV、Boost、TensorRT仅仅靠宿主机上那几个arm64 dev包就会力不从心。这时候最专业的做法是把JetPack SDK里自带的根文件系统直接当作sysroot交给交叉编译工具链。4.1 两种获取Jetson Orin根文件系统的方式第一种方式是使用NVIDIA官方提供的Jetson Linux Driver Package。下载对应Orin型号的BSP包解压后你会得到一个完整的Linux_for_Tegra目录其中rootfs子目录就是标准Ubuntu根文件系统。第二种方式是从已经刷好系统的Jetson Orin上直接打包同步。这个方法适合你手头已经有正在运行的设备# 在Jetson上执行 sudo rsync -avxP / rootfs_sync/然后在宿主机上通过scp或rsync把rootfs_sync目录拉回来。这种方式的好处是目标设备上已经装好了你需要的所有运行时库编译时能精确匹配设备环境缺点是文件数量大如果网络带宽有限会花不少时间。4.2 用--sysroot告诉编译器“去这个目录里找”假设rootfs解压到了/opt/jetson_orin_rootfs那么一行简单的编译命令会长这样aarch64-linux-gnu-gcc \ --sysroot/opt/jetson_orin_rootfs \ -I/opt/jetson_orin_rootfs/usr/include \ -L/opt/jetson_orin_rootfs/usr/lib/aarch64-linux-gnu \ hello.c -o hello注意-L后面的路径/usr/lib/aarch64-linux-gnu在Ubuntu 22.04上很常见如果你用的是20.04可能是/usr/lib/aarch64-linux-gnu的软链接或者直接被优化成/usr/lib。建议先到rootfs里用ls看一眼实际结构再写路径。这里有个容易被忽略的点--sysroot不只是影响库文件搜索路径还会改变头文件搜索路径。如果你没有把-I指到rootfs的usr/include编译器可能会回退到宿主机的/usr/include然后拿x86架构的头文件来编译aarch64代码最后出现各种莫名其妙的“类型冲突”报错。4.3 CMake工具链文件里的sysroot标准写法实际项目中我用CMake的频率远高于手写gcc命令。下面是我在Jetson Orin交叉编译项目中使用过的toolchain.cmake可以直接参考set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(SYSROOT_PATH /opt/jetson_orin_rootfs) set(CROSS_COMPILE aarch64-linux-gnu-) set(CMAKE_SYSROOT ${SYSROOT_PATH}) set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc) set(CMAKE_CXX_COMPILER ${CROSS_COMPILE}g) set(CMAKE_FIND_ROOT_PATH ${SYSROOT_PATH}) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)这里最核心的是CMAKE_FIND_ROOT_PATH_MODE_*三兄弟。它们决定了find_package、find_library、find_path在搜索依赖时要不要被限制在sysroot范围内。把PROGRAM设为NEVER是为了让CMake仍然使用宿主机的工具程序比如cmake本身把LIBRARY、INCLUDE、PACKAGE设为ONLY是为了防止它跑到宿主机/usr/lib里找到x86版本的OpenCV等库。配置好后按常规流程操作mkdir build cd build cmake .. -DCMAKE_TOOLCHAIN_FILE../toolchain.cmake -DCMAKE_BUILD_TYPERelease make -j$(nproc)如果rootfs里的库路径和CMake预期不一致你还可以通过-DCMAKE_LIBRARY_PATH和-DCMAKE_INCLUDE_PATH两个变量补充指定。4.4 动态链接器与RPATH拷贝到Jetson上可能踩的第二个坑使用rootfs作为sysroot后链接阶段通常不会再报ld-linux-aarch64.so.1缺失但运行时还有一类典型问题你把编译产物拷到Jetson上执行时它提示找不到libMySDK.so或者某个第三方库。这通常不是因为动态链接器缺失而是因为库的查找路径不对。交叉编译时链接器通过-L找到了库文件但运行时系统动态链接器不会自动去rootfs的/usr/lib/aarch64-linux-gnu里找。解决办法是在链接时设置RPATHaarch64-linux-gnu-gcc ... -Wl,-rpath,/path/to/lib/on/jetson -o my_app或者用CMake方式set(CMAKE_INSTALL_RPATH /usr/local/lib/myapp) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)我在实际项目里倾向于不依赖RPATH而是直接把需要运行的第三方库拷贝到Jetson的/usr/lib/aarch64-linux-gnu下因为这样更符合嵌入式设备的部署习惯升级时不容易产生路径漂移。5. 深入排查交叉编译常见的四种变种报错与快速判断即使掌握了标准修复流程实际开发中还是可能遇到各种“看似不同、实则同根”的变种。这里列一个速查表方便你按图索骥。报错内容本质原因排查方向ld-linux-aarch64.so.1: No such file or directory动态链接器缺失rootfs或交叉工具链里缺glibc库文件cannot find -lclibc库文件搜索路径不对确认/usr/aarch64-linux-gnu/lib里有没有libc.so或libc.acrt1.o: No such file or directory软件启动文件缺失确认libc6-dev-arm64-cross或rootfs是否完整error while loading shared libraries: libxxx.so运行时库缺失或RPATH不对用ldd在Jetson上查看依赖5.1 cannot find -lc优先检查工具链搜索路径如果遇到cannot find -lc不要急着重装库先用-v参数观察链接器到底搜索了哪些目录aarch64-linux-gnu-gcc -v hello.c -o hello 21 | grep -E LIBRARY_PATH|COLLECT_GCC_OPTIONS看到实际搜索路径后再对照检查这些目录下是否真的有libc.so软链接。有些精简版的libc6-dev-arm64-cross安装后不会自动创建软链接你需要手动补cd /usr/aarch64-linux-gnu/lib sudo ln -sf ../../lib/aarch64-linux-gnu/libc.so.6 libc.so但这类软链接操作要非常谨慎最好先确认源路径存在否则会越修越乱。5.2 crt1.o缺失多半是sysroot不完整crt1.o是C运行时启动文件通常位于/usr/lib/aarch64-linux-gnu/crt1.o。当它缺失时链接器会直接报错。这个头绪不多就是系统库没装全。在宿主机上安装sudo apt install -y libc6-dev-arm64-cross如果你使用rootfs方式检查rootfs里/usr/lib/aarch64-linux-gnu/crt1.o和crti.o是否存在。实在缺的话最简单的办法就是从Jetson板子上直接拷贝scp jetson:/usr/lib/aarch64-linux-gnu/crt1.o /opt/jetson_orin_rootfs/usr/lib/aarch64-linux-gnu/5.3 在宿主机上模拟运行目标程序有时候你没有Jetson板子或者想快速验证交叉编译产物是否能正确加载。可以通过qemu-aarch64静态模拟器在宿主机上直接运行sudo apt install -y qemu-user-static ./helloqemu-user-static会拦截aarch64的系统调用并在用户态将指令翻译为x86执行。你完全可以借助它来验证程序能否跑通然后再部署到Jetson上省去来回拷贝的麻烦。不过要注意qemu模拟的是用户态并不完全等价于真实硬件涉及GPU、CUDA等设备的程序不能这么验。6. 从实际项目中总结的经验与建议修过几次这个报错之后我最大的感受是交叉编译的坑不在“编译”本身而在于你对“目标系统环境”的把控。这里分享几条我踩过坑之后的长期习惯希望对你有参考价值。6.1 维护一套固定版本的rootfs如果你的团队同时维护多个Jetson设备建议把rootfs的版本管理起来而不是今天从板子上同步一份、明天又从BSP包解压一份。版本混乱时宿主机编译环境和目标运行环境很容易出现“A库和B库不匹配”的隐蔽问题。我通常会把整理好的rootfs做成tar.zst存档并标注JetPack版本号sudo tar -C /opt/jetson_orin_rootfs -czf jetpack6_orin_rootfs.tar.gz .每次做交叉编译前都用同一份rootfs能显著减少环境的变量。6.2 交叉编译性能的几个提升技巧Jetson Orin的CPU核心数多AMAX之类的设备多交叉编译本身也支持分布式加速。但在本地单机场景下三个参数对编译速度影响最直接用ccache缓存交叉编译产物尤其是重复编译OpenCV这种大项目时能省很多时间。用make -j$(nproc)或ninja并行编译对于内存8GB以上的宿主机-j数可以稍微超出核数但不能无限拉高否则会因内存不足导致OOM。把产出目录放在SSD上避免机械硬盘的随机读写成为瓶颈。我在一台16核32GB内存的Ubuntu机器上给Orin交叉编译Qt 5.15.10时开启ccache后第二次全量编译速度大约快了40%左右效果相当可观。6.3 设备上保留一份“编译探针”脚本最后安利一个小工具在Jetson设备上放一个check脚本内容就是查看关键动态库和链接器是否存在。每次部署交叉编译产物前先跑一遍能快速确认设备环境是否符合预期。#!/bin/bash echo dynamic linker ls -l /lib/aarch64-linux-gnu/ld-linux-aarch64.so.1 echo key libraries ls -l /lib/aarch64-linux-gnu/libc.so.6 ls -l /usr/lib/aarch64-linux-gnu/libstdc.so.6 echo app interpreter readelf -l /path/to/your/app | grep interpreter把这个脚本放在设备上每次部署失败时的第一步排查就变得非常明确。硬件的坑、系统的坑、环境的坑都是这么一步步磨平的。交叉编译的报错不会因为多等一会儿就消失早一点摸清报错背后的机制就早一点从“编译五分钟、排错两小时”的困境里走出来。
返回列表