尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

ARM架构与交叉编译实战:嵌入式ARM开发17天总结

ARM架构与交叉编译实战:嵌入式ARM开发17天总结 系统学习嵌入式ARM开发到今天正好第17天准备把这个阶段的东西完整梳理一遍标题就叫DAY17-ARM 架构与交叉编译。说实话ARM架构这个词我第一次听的时候觉得挺玄乎实际上把它拆开成指令集架构、微架构、生态三个层面之后就好理解多了而交叉编译则是嵌入式开发最绕不开的一道坎你的开发机通常是x86架构目标板却是ARM架构怎么让程序在目标板上跑起来靠的就是一套专门的工具链。这篇文章我不会讲太深的理论更多是把我实际搭建环境、编译程序、踩坑排查的过程拿出来分享希望能给同样在学ARM、在做Linux移植、或者刚拿到一块开发板不知道从哪下手的读者一点参考。1. ARM架构到底在讲什么指令集、微架构与生态选型1.1 指令集架构与微架构能跑什么和怎么跑是两回事很多东西一说到ARM架构就容易混为一谈。实际上ARM架构严格来说指的是指令集架构Instruction Set ArchitectureISA它决定了一款CPU认识哪些指令、寄存器怎么编排、内存怎么访问、异常怎么处理。而微架构Microarchitecture指的是具体怎么实现这些指令比如流水线几级、缓存多大、乱序执行还是顺序执行。换句话说ISA是接口规范微架构是内部实现二者是两码事。同一套ARM指令集可以有完全不同的微架构实现。Cortex-A72、Cortex-A76、Cortex-X1这些都是ARMv8-A架构下的不同微架构但它们都能跑同一份编译好的ARMv8-A指令的程序。反过来同样叫ARM处理器如果一个是ARMv7-A、一个是ARMv8-A那它们能跑的指令就不是同一套二进制程序通常不能直接互换。这也是为什么arm架构这个词在选型时不够精确——你得说清楚具体是哪个架构版本比如ARMv7、ARMv8、ARMv9以及在这个架构版本下用的是哪一类核心。1.2 Cortex-A/R/M怎么选先想清楚你写的是Linux还是裸机ARM官方把处理器核心按应用场景分成三大类Cortex-AApplication应用处理器、Cortex-RReal-time实时处理器、Cortex-MMicrocontroller微控制器。Cortex-A系列跑的是复杂应用最典型的就是Linux、Android这类带MMU的操作系统。你需要跑Linux、跑Qt界面、跑复杂的协议栈就选Cortex-A比如Cortex-A53、A72、A78。Cortex-M系列面向裸机或RTOS比如FreeRTOS、RT-Thread特点是低功耗、中断响应快、没有MMU或者说MMU能力非常有限常见的有Cortex-M0/M3/M4/M7。Cortex-R系列介于两者之间强调确定性和实时性常用于汽车、工业控制领域比如TC387这类芯片里就集成了多核的Cortex-R。很多初学者拿到一块开发板上来就想用Linux结果发现自己手里的板子是Cortex-M内核这就非常尴尬。所以选型的第一步不是看芯片牌子而是先明确自己的软件形态跑不跑操作系统跑什么操作系统需要多大的内存和算力。这决定了你后面所有工具链的选型方向。1.3 ARM的授权模式对开发者的真实影响ARM和x86很大的不同在于它的商业模式ARM本身不生产芯片它把架构和核心设计授权给芯片厂商比如高通、联发科、华为、NXP、ST等厂商拿到授权后可以集成自己的外设和定制逻辑然后流片生产。这个模式对普通开发者有什么影响第一个影响是生态碎片化。同样是ARMv8-A架构不同厂的芯片外设寄存器完全不同UART控制器、GPIO控制器、中断控制器比如GIC、时钟树各写各的。你在A厂商的芯片上写的驱动拿去B厂商的芯片上基本没法直接编译。第二个影响是工具链适配虽然GCC对ARM的支持很成熟但如果你要用到芯片厂商的特殊启动流程、固件打包或者调试工具还得用原厂那一套。第三个影响是部分芯片还会提供自定义指令扩展不过这类东西在通用工具链里经常不支持需要专门打补丁实际项目里我一般不碰因为一旦用了后续升级工具链就是灾难。2. 交叉编译的本质为什么x86主机非要另搞一套工具链2.1 一次编译到处跑的谎言目标架构才是编译的边界很多从应用开发转过来的朋友一开始会问这样一个问题我写的C代码不都是编译成二进制吗凭什么x86上编出来的程序不能在ARM上跑原因其实很简单x86的CPU和ARM的CPU能识别的机器指令不是同一种语言。x86程序编译出来的是x86指令码ARM处理器根本不认识反过来也一样。所谓交叉编译就是在一种架构的主机上编译出目标架构上可运行的二进制程序。这里交叉两个字指的是编译工具运行的环境和生成程序运行的环境不一致。如果两者一致叫本地编译native compile。日常我们在x86 Linux上用gcc编译x86程序就是本地编译。嵌入式开发里目标板通常性能有限、存储有限不太可能在板上直接跑编译器所以几乎都是交叉编译。要把这件事真正理解透得知道一个关键概念编译器本身是一个运行在宿主机的程序但它生成的目标代码必须遵循目标平台的指令集和ABI规则。ABIApplication Binary Interface比指令集还要细它规定了函数调用时参数怎么传寄存器还是栈、寄存器哪些是调用者保存、结构体怎么布局、浮点数怎么传、动态链接器怎么找库。不同编译器、不同架构的ABI可能完全不同这也是为什么交叉编译时工具链必须和目标系统精确匹配。2.2 交叉编译工具链四元组看懂前缀就懂了一半用GCC的时候你会看到工具链名字带一个前缀比如arm-linux-gnueabihf-gcc。这个前缀不是随便写的它其实是一个四元组的缩写完整形式是arch-vendor-kernel-systemarch目标架构这里是armvendor厂商或工具链来源常见的有none无厂商、unknown、buildroot、linaro等kernel目标系统运行的操作系统或ABI类型常见的是linuxLinux系统也有eabi嵌入式ABI常用于裸机systemC库和浮点ABI常见的是gnueabi软浮点、gnueabihf硬浮点、musleabi等看一个具体前缀arm-linux-gnueabihf可以读出三层含义目标是ARM架构跑的是Linux系统用的是glibc硬浮点ABI。至于前面的arm具体指ARMv7还是ARMv8单纯从前缀还看不出来需要用arm-linux-gnueabihf-gcc -v或者-dumpmachine查看工具链默认的配置。理解这个前缀非常实用。你在网上搜arm交叉编译工具链会看到各种名字arm-none-eabi-gcc、arm-none-linux-gnueabi-gcc、arm-linux-gnueabihf-gcc、aarch64-linux-gnu-gcc。它们分别适用不同场景工具链前缀适用场景典型目标arm-none-eabi-裸机、RTOS无操作系统Cortex-M系列arm-none-linux-gnueabi-ARM Linux软浮点ARMv5/ARMv7老平台、ARM926arm-linux-gnueabihf-ARM Linux硬浮点ARMv7Cortex-A7/A8/A9aarch64-linux-gnu-64位ARM LinuxCortex-A53/A72等我自己第一次看到这么多前缀的时候头都是大的后来用了一招就记住了先明确我的目标板是Cortex-A系列跑Linux要浮点性能那就锁定arm-linux-gnueabihf这一条线。别贪多先解决一个场景。2.3 静态链接与动态链接开发板上能不能跑起来的关键交叉编译还有一个经常被忽略的问题程序在目标板上跑不起来很多情况下不是编译器的问题而是动态链接器找不到依赖库。动态链接的意思是程序本身只记录了我需要libc.so.6这个动态库真正运行的时候由系统里的动态链接器ld-linux-armhf.so.3这一类去加载。在x86主机上动态库默认在/lib/x86_64-linux-gnu、/usr/lib/x86_64-linux-gnu等路径而ARM的库路径完全不同而且CPU架构也不一样所以你编出来的ARM程序不能在x86主机上直接运行也不能直接去加载x86的库。解决这个问题有几个常用方法。最省事的是编译时加-static做静态链接这样所有依赖都打进了可执行文件不依赖目标板上的任何动态库。代价是体积变大而且静态链接的glibc在某些环境里会有DNS解析、NSS名称服务之类的问题需要留个心眼。更好的做法是准备好目标板的sysroot也就是目标板的根文件系统的拷贝编译时用--sysroot指向它这样编译器、链接器能找到ARM版本的动态库和头文件。发行给客户时把程序和相应的动态库一起部署再用rpath或者启动脚本设置库路径。3. 工具链选型与安装gcc-arm、ARM Compiler 5.06 怎么选3.1 三大工具链家族GCC、ARM Compiler、Clang 各自的脾气现在主流的ARM交叉编译工具链有三条线GCC线是最常见的开源、免费、文档多社区支持强。Linaro出品的gcc-arm工具链、Buildroot生成的工具链、各种芯片厂商定制过的工具链本质上都是GCC的衍生版本。对绝大多数应用场景跑Linux、写裸机程序首选GCC就对了。ARM Compiler线是ARM官方提供的商业编译器最新版本有ARM Compiler 6基于Clang/LLVM老版本是ARM Compiler 5armcc基于原来的ARM自家编译器。在KEIL MDK、ARM Development StudioADS这些IDE里会用到。网上经常搜到的ARM Compiler 5.06u7指的是ARM Compiler 5.06 Update 7build 960是ARMv7时代非常经典的一个版本很多老项目还在用它。要注意的是ARM Compiler 5是单独的许可证软件和GCC不是一回事有些朋友下载后不知道怎么装其实它会作为Keil的插件或者独立工具链安装但默认的armcc是在Windows下用的居多Linux下用得少。Clang/LLVM线是后起之秀Clang对ARM的支持已经很成熟尤其在代码生成的性能上经常有惊喜。但它背后的库依赖比较多普通嵌入式项目里如果不是脑壳疼的编译优化问题没必要专门切到Clang。选型的原则就一句话能用GCC就用GCC除非你的IDE强制要求ARM Compiler或者项目里已经有历史包袱比如那份老固件必须用armcc 5.06编译。我用过的项目里90%以上都是GCC一条路走到底。3.2 Ubuntu 20.04 下安装 gcc-arm 工具链实录这里以我自己在Ubuntu 20.04上搭建环境的完整过程为例。方法一直接用apt安装。Ubuntu的软件源里带了一套工具链不过版本不一定新sudo apt update sudo apt install gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf装完后验证arm-linux-gnueabihf-gcc --version这个方案的好处是快缺点是工具链版本由Ubuntu源决定通常比较老。我这边Ubuntu 20.04源里的gcc-arm-linux-gnueabihf是9.x对一般项目够用了但如果你的目标板是新的Cortex-A55/A76这类核心老工具链的默认调优参数可能不太理想最好确认一下它默认的arch和cpu。方法二用ARM官方提供的最新工具链。ARM在官网上发布预编译的gcc-arm工具链现在叫Arm GNU Toolchain是一个独立的tar包解压就能用。比如我下载过gcc-arm-10.3-2021.07-x86_64-arm-none-linux-gnueabihf解压后直接添加bin目录到PATH即可wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/10.3-2021.07/gcc-arm-none-eabi-10.3-2021.07-x86_64-linux.tar.bz2 tar xjf gcc-arm-10.3-2021.07-x86_64-linux.tar.bz2 export PATH$PWD/gcc-arm-none-eabi-10.3-2021.07/bin:$PATH上面这个例子是arm-none-eabi的包适合裸机。如果你要Linux的包就去Arm GNU Toolchain下载页面找带linux-gnueabihf名字的版本。下载的时候记得看清名字里是aarch6464位ARM还是arm32位ARM别下错了。我刚开始就下错过一次拿着64位的工具链去编译32位目标结果链接阶段各种cannot find crt1.o还以为是环境问题查了半天才发现是工具链架构选错了。方法三用Buildroot自己生成工具链。Buildroot可以精确控制工具链的glibc版本、GCC版本、内核头文件版本一致性最好但生成过程需要下载源码并编译一般要半小时以上。适合做产品、需要长期维护的场景学习阶段没必要上这么重的方案。3.3 环境变量配置与工具链自检装完工具链之后我不会急着写代码而是先做一轮自检确认工具链真能用、目标选项真正确。常用三条命令arm-linux-gnueabihf-gcc -v arm-linux-gnueabihf-gcc -dumpmachine arm-linux-gnueabihf-gcc -print-sysroot第一条会打印完整配置信息包括Target:那一行以及Thread model: posix表示多线程支持。第二条直接输出工具链的目标机器名看到arm-linux-gnueabihf就对了。第三条打印sysroot路径如果工具链是在主机上交叉编译的会显示一个实际存在的目录如果没有sysroot或者显示空那后面做动态链接时就要特别注意说明这个工具链可能没有配套的根文件系统你链接的目标库需要自己准备。另外建议把工具链的bin目录写入~/.bashrc或~/.zshrc免得每次开终端都要重新export。但要注意如果系统里同时装了多个工具链把两条都写进PATH同名命令会互相覆盖实际生效的很可能是后写的那条。我自己的习惯是在每个项目目录下放一个env.sh按项目来切换export PATH/opt/arm-gnu-toolchain/bin:$PATH export CROSS_COMPILEarm-linux-gnueabihf-这样切换项目的时候source env.sh一下就不会出现唉我这个命令怎么是x86的gcc在跑这种诡异问题。4. 第一个ARM程序与实用调试手段4.1 从hello.c到ARM可执行文件的完整过程工具链装好后最直接的事情就是编译一个程序扔到目标板或者模拟器里跑。过程其实和本地编译一模一样只是把gcc换成交叉版本的gcccat hello.c EOF #include stdio.h int main(void) { printf(hello arm, day 17\n); return 0; } EOF arm-linux-gnueabihf-gcc -o hello_arm hello.c就这么一条命令文件出来了。但二进制是不是ARM格式的还不能光靠文件扩展名和文件大小判断得用file命令看file hello_arm正常输出应该是类似hello_arm: 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, not stripped看到ARM, EABI5和interpreter /lib/ld-linux-armhf.so.3就说明它是一个32位ARM Linux动态链接的可执行文件。如果你是64位目标用aarch64-linux-gnu-gcc编译输出里应该是ELF 64-bit LSB executable, ARM aarch64interpreter会是/lib/ld-linux-aarch64.so.1。如果要静态链接arm-linux-gnueabihf-gcc -static -o hello_arm_static hello.c静态版本体积会大很多一般都有几百KB甚至1MB以上动态版本只有几KB。这也是判断一个程序静态还是动态最快的方法。4.2 file和readelf验证架构的黄金组合光看file还不够我一般还会用readelf深入看ELF文件的头部信息和依赖arm-linux-gnueabihf-readelf -h hello_arm arm-linux-gnueabihf-readelf -d hello_arm | grep NEEDED第一条会输出ELF头重点看Machine字段ARM平台显示ARMAArch64平台显示AArch64。第二条显示动态依赖库能看到libc.so.6就说明它依赖C库。这个习惯非常重要。因为很多时候你拿到一个第三方编译好的.so文件或可执行文件不知道它是x86还是ARM用file和readelf一照立刻现原形。网上经常有人问.so从x86迁移arm文件其实不叫迁移正确的做法是拿到ARM平台下重新编译一份。如果你拿到一个二进制文件想确认它能不能在你的ARM板子上用第一个动作就是file这是最直接的判断方式。4.3 用QEMU在x86上先跑起来手头没有开发板的时候怎么验证交叉编译的程序能跑最方便的是QEMU的用户态模拟。Ubuntu下安装sudo apt install qemu-user qemu-user-static装了qemu-user之后就可以直接在x86主机上运行ARM的二进制qemu-arm -L /usr/arm-linux-gnueabihf ./hello_arm这里的-L参数指定一个模拟的根文件系统路径用来加载动态链接器和依赖库。如果你用的是apt安装的gcc-arm-linux-gnueabihf工具链在Ubuntu里通常已经配好了/usr/arm-linux-gnueabihf这个sysroot直接能用。如果报找不到ld-linux-armhf.so.3说明-L路径不对或者工具链没有配套的库目录。QEMU用户态模拟跑单个程序非常快但有一个限制它不是完整的系统模拟不能直接跑多进程依赖很重的服务比如复杂的守护进程也可能遇到一些系统调用不支持。这时候就得用qemu-system-arm完整模拟一台ARM机器或者干脆上开发板真机。我在学习阶段的习惯是小工具、单文件程序用qemu-arm跑涉及设备访问、网络栈、多进程的就放到板子上真跑。5. Qt交叉编译环境搭建实战含OpenSSL5.1 Qt交叉编译的核心参数很多做嵌入式界面开发的朋友都会卡在Qt交叉编译这一步。网上搜qt5.12.10交叉编译、ubuntu-20.04 安装 qt 交叉编译环境答案很多但大部分是照着抄的没有解释参数的含义遇到编译失败就不知道怎么调。这里讲一遍我的理解。交叉编译Qt最核心的事情有两个一是让Qt的configure脚本认出你的目标平台二是指定一个安装前缀prefix让编译产物安装到你希望的目录。以Qt 5.12.10为例我的configure命令大概是./configure \ -prefix /opt/qt5.12.10-arm \ -opensource \ -confirm-license \ -release \ -xplatform linux-arm-gnueabihf-g \ -nomake examples \ -nomake tests \ -no-opengl \ -no-icu \ -qt-libpng \ -qt-libjpeg \ -qt-zlib \ -skip qtwayland \ -skip qtwebengine \ -skip qtscript \ -skip qt3d解释几个关键参数-xplatform linux-arm-gnueabihf-g这个最核心它告诉Qt用哪个mkspec。Qt在qtbase/mkspecs目录下预置了很多平台的规格文件。如果你的目标平台不在里面需要自己复制一个类似的再改。比如复制linux-arm-gnueabi-g改成linux-arm-gnueabihf-g然后修改里面的qmake.conf指定交叉编译器前缀和合适的qmake配置。-no-opengl如果你的目标板GPU或者开发环境没配好OpenGL ES先关掉免得编译一大堆用不到的模块。等后面确实需要再开。-no-icuICU库在嵌入式环境里比较重而且交叉编译ICU特别麻烦。界面程序如果不需要复杂文本排版比如中文显示Qt自身处理就够了可以关掉。-qt-libpng -qt-libjpeg -qt-zlib让Qt使用自身附带的图片库和压缩库而不是依赖系统里的版本。这样目标板上可以少装好多依赖。Qt configure完之后会生成一个qtbase目录和一堆模块目录然后make -j$(nproc)最后make install。整个编译过程在性能尚可的机器上大概要二十到四十分钟取决于你配置的模块数量。编译期间占用磁盘空间比较大我一般预留10GB以上空间特别是debug版会更大。5.2 OpenSSL与依赖库的交叉编译如果Qt程序里要用到HTTPS访问wave socket加密那就躲不开OpenSSL。这里有个经典大坑直接用系统里的OpenSSL库链接是不行的因为x86平台的openssl库ARM平台用不了但你也不能只交叉编译openssl还需要让Qt能找到这个交叉编译版的openssl头文件和库。交叉编译OpenSSL 1.1.1的典型步骤wget https://www.openssl.org/source/openssl-1.1.1w.tar.gz tar xzf openssl-1.1.1w.tar.gz cd openssl-1.1.1w ./Configure \ linux-armv4 \ shared \ --prefix/opt/openssl-arm \ --cross-compile-prefixarm-linux-gnueabihf- make -j$(nproc) make install关键在linux-armv4这个target它告诉OpenSSL编译成ARMv4及以上版本的指令集可以覆盖ARMv7、ARMv8的32位模式。--cross-compile-prefix则是交叉编译器的前缀。编译完成后动态库会安装到/opt/openssl-arm/lib头文件在/opt/openssl-arm/include。然后回到Qt配置加上-openssl-linked \ OPENSSL_LIBS-L/opt/openssl-arm/lib -lssl -lcrypto \ OPENSSL_INCDIR/opt/openssl-arm/include这里要注意一点如果openssl编译时带shared出来的动态库是libssl.so.1.1、libcrypto.so.1.1这样的版本化文件部署到目标板后程序运行时通常还需要在板子的/etc/ld.so.conf里加上库路径或者把库放到标准路径下再执行ldconfig。否则就会在板子上报libssl.so.1.1 not found。我最初就是漏了这一步Qt界面在开发机上看着好好的拷到板子上直接启动失败日志显示找不到libssl。5.3 把程序部署到开发板的注意事项交叉编译完Qt程序之后还不能直接把可执行文件和Qt的so库一股脑全拷到板子上。一个能跑起来的Qt程序至少需要三样东西第一是Qt运行库。Qt安装到/opt之后/opt/qt5.12.10-arm/lib下会有几十个动态库但程序不一定每个都需要。最稳妥的做法是用readelf -d 你的程序 | grep NEEDED查看程序直接依赖的Qt库然后再逐个检查这些Qt库又依赖什么。也可以用ldd的一个ARM版本来查但注意x86的ldd无法直接分析ARM程序需要用arm工具链提供的arm-linux-gnueabihf-ldd或者用readelf手动追。第二是平台插件。Qt程序启动的时候需要加载platforms/libqlinuxfb.so或者libqeglfs.so这些插件。很多人交叉编译成功、程序也拷到板子了结果一跑起来报错could not find or load the Qt platform plugin linuxfb就是因为没把qtbase/plugins/platforms目录拷过去或者没有设置环境变量QT_QPA_PLATFORM_PLUGIN_PATH。我在板子上一般是这样启动程序的export QT_QPA_PLATFORMlinuxfb export QT_QPA_PLATFORM_PLUGIN_PATH/opt/app/plugins/platforms export LD_LIBRARY_PATH/opt/app/lib:$LD_LIBRARY_PATH ./myapp第三是字库。Qt界面要显示中文板上得有一个中文字体文件常见的有wqy-zenhei.ttc或者DroidSansFallback.ttf放到程序能找到的字体路径否则界面上就是方块乱码。6. 常见问题与排查技巧实录6.1 经典报错速查表我把自己这段时间反复踩过的坑整理成一张表基本都是开发中最高频的报错现象原因解决方法cannot find -lc工具链sysroot里没有对应的C库检查工具链是否有配套sysroot或安装libc6-dev-armhf-crosscannot find crt1.o缺少目标平台的启动文件多出现在用了错误架构工具链时检查前缀是否正确relocation truncated to fit程序太大地址放不下或内存布局问题检查链接脚本/内存配置必要时加-Wl,--no-relax或调整地址undefined reference to \__aeabi_ddiv浮点或除法辅助函数缺失链接时加-lm或-lgcc或修改fpu/fpabi参数error: unrecognized command line option -mfloat-abihard编译器太老或目标架构不支持硬浮点检查目标是否支持硬浮点换新版本工具链或去掉改选项板子上运行报No such file or directory动态链接器路径不对或文件格式不匹配file检查格式readelf查interpreter确保代码是ARM板子上运行报libxxx.so.1: cannot open shared object file依赖的某个动态库没找到readelf查NEEDED把对应的库放到LD_LIBRARY_PATH路径下Qt程序报could not load the Qt platform pluginplatforms插件缺失或环境变量没设置拷贝插件目录设置QT_QPA_PLATFORM_PLUGIN_PATH编译很慢交叉编译工具链不支持多核或文件系统IO慢make加-j$(nproc)用SSD设CCACHE加速这里特别想说一下浮点ABI的坑。在32位ARM Linux里浮点参数传递有两种ABI软浮点softfp或者纯soft和硬浮点hard。软浮点用通用寄存器传浮点参数硬浮点用专用的FPU寄存器传。如果编译时用的选项和目标库/工具链用的选项不一致链接阶段就会出现一堆奇怪的浮点相关报错。最典型的就是前面列的__aeabi_ddiv找不到这通常是因为你的代码用了double除法但浮点支持函数没链接进来或者softfp与hard混用了。解决办法是确保整个工具链、所有库、所有编译参数里的浮点ABI选项保持一致。判断一个ELF文件用的是哪种浮点ABI可以用readelf -A看Tag_ABI_VFP_args的值1表示硬浮点0表示软浮点。6.2 从x86迁移ARM的.so处理套路实际项目里经常会遇到一个情况供应商提供了一个预编译的.so但它是x86的需要在ARM的板子上用。我踩过几次坑之后总结出一个标准处理流程第一步先判明架构。执行file libxxx.so看清楚到底是x86、x86_64还是ARM、AArch64。如果是x86系列很遗憾一般不能直接在ARM上用只能找供应商要ARM版或者拿源码交叉编译。如果供应商说这个库就是通用的那基本可以认定对方在糊弄人。第二步查看依赖。执行readelf -d libxxx.so | grep NEEDED看看它依赖哪些外部库。有些.so看起来是ARM的但依赖了一个系统里不存在的库版本一样跑不起来。第三步安装到目标板并测试。把.so放到板子的库目录执行ldconfig然后用ldd板子上的本地ldd或者用交叉工具链的arm-linux-gnueabihf-ldd检查是否所有依赖都满足。注意在x86开发机上执行x86版本的ldd看ARM库是没意义的会提示not a dynamic executable或者干脆解析不了。第四步如果供应商只给了x86版又没有源码那也不是完全没有解可以考虑反编译转写或者改用其他方案但成本都很高。所以我的原则是所有依赖库从项目第一天就必须要求供应商提供ARM版本写在需求文档里。这件事越早发现越省事千万别等到联调阶段才暴露。6.3 一个老工具链的提醒ARM Compiler 5.06未安装的迷惑现场最后专门聊聊很多人会遇到的ARM Compiler 5.06问题。这个编译器在MDK、老的开发环境里用得很多网上有人求下载有人装了又报该版本未安装之类的问题。实际上这是个非常典型的工具链版本识别问题。ARM Compiler 5.06 Update 7build 960是一个比较老的版本它和MDK的关系非常紧密。如果你在Keil MDK的安装界面里看到ARM Compiler 5.06u7选项勾选并安装之后编译器的位置一般在Keil的安装目录下的ARM/ARMCC/bin。如果你在命令行里输入armcc显示找不到命令多半是因为没有把ARMCC/bin目录加入PATH或者你下载的版本是一个需要单独安装的update包但并没有正确挂载到MDK里。建议如果你真的是在MDK下做STM32这类开发那ARM Compiler 5.06确实有必要但如果你是在Ubuntu下做ARM Linux开发那直接把自己装ARM Compiler的时间省下来老老实实用GCC。别在一开始就被arm compiler 5.06u7 download这种热搜词带跑偏选型之前先想清楚你的目标平台和开发模式比什么都重要。我个人在踩过一轮工具链的坑之后的体会是交叉编译这件事工具链选型占三成理解目标架构占三成剩下四成全在排错和依赖管理上。不要急着把代码写完编译先花点时间把file、readelf、ldd这些基础命令用熟把工具链前缀的含义搞清楚遇到问题就从架构对不对、ABI对不对、依赖全不全三个角度去排查你会发现很多看起来玄乎的问题其实都是可以一步步定位到根因的。这个“DAY17”的阶段总结就先写到这。接下来我打算把几个外设驱动的交叉编译流程也走一遍特别是GPIO、SPI、I2C这类子系统怎么写驱动、怎么配合Qt应用做数据采集到时候再开一篇继续分享。
返回列表