ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ARM架构与交叉编译:从指令集原理到嵌入式工程实践

ARM架构与交叉编译:从指令集原理到嵌入式工程实践 1. 先搞懂ARM架构到底在讲什么我经常在带新人的时候被问一个问题学嵌入式为什么绕不开ARM架构后来自己做过几个量产项目慢慢意识到与其把“ARM架构”当成一门需要背的课程不如把它当成一把钥匙用来理解芯片、编译链、系统移植和调试器之间到底是怎么配合的。如果你接下来要做Linux内核移植、Qt应用开发、驱动编写或者只是单纯想把一个在PC上写好的C程序放到开发板上跑起来那ARM架构和交叉编译就是绕不开的地基。DAY17这个节点我建议你停一下别急着往下刷板子先把体系结构、指令集、编译工具链连接起来后面所有板级调试都会顺畅很多。后端多跑几次你会发现硬件平台一旦换了编译方式、库的路径、运行环境全部都要跟着换。所以下面我会把ARM架构的核心脉络和交叉编译的完整链路拆开讲顺便把那些文档里不会写、只有踩过坑才记得住的细节一起放出来。1.1 ARM不是“芯片厂”而是一套指令集架构ARM本质上不是某颗芯片而是一套指令集架构ISA加IP核授权体系。ARM公司做的事情是把CPU怎么取指令、怎么执行运算、寄存器怎么编排、异常怎么处理这些底层规则设计好然后授权给高通、苹果、三星、瑞萨、ST、TI这些芯片厂商让他们基于同一套架构去设计各自的SoC。所以我们看到的STM32是ARM Cortex-M内核i.MX系列是Cortex-A内核树莓派也是Cortex-A内核但它们外设、内存控制器、电源管理完全不同。从指令集演进来看ARMv4/ARMv5时代是入门版本用的最多的是指令集A32到了ARMv7Cortex-A/R/M三条产品线正式分开A系列跑应用、R系列做实时、M系列做单片机。ARMv8是分水岭引入AArch64也就是64位模式原生支持超过4GB内存寻址指令集也从A32变成了A64并且提供了AArch32运行模式来兼容老系统。ARMv9在2021年前后发布加入了更细粒度的安全控制、更丰富的矩阵运算能力但核心的编程模型和交叉编译方式没有发生断崖式变化。对初学者最容易混淆的是“架构版本”和“内核型号”。Cortex-A53是ARMv8-A架构Cortex-M4是ARMv7E-M架构Cortex-R52是ARMv8-R架构。你用aarch64工具链去编Cortex-M4的裸机代码一定跑不起来用arm-none-eabi工具链去编Cortex-A53的Linux内核同样也不行。所以第一步不是急着下载编译器而是先搞清楚你手上芯片的架构级别。1.2 ARM和x86到底差在哪里x86是CISC复杂指令集一条指令能顶好几条事指令长度不固定从1字节到十几字节都有CPU内部有微码去解释和调度。ARM是RISC精简指令集指令固定长度早期32位Thumb-2部分是16/32位混用大多数运算操作是“读寄存器、算、写寄存器”寻址模式相对简单也因此功耗更低、解码更简单。这套差异带来的直接结果就是两种平台的二进制文件完全不通用。你在x86 Linux上编译出来的ELF复制到ARM板上直接执行系统会报Exec format error。根本原因不是“不够智能”而是指令编码、寄存器约定、系统调用号、ABI参数传递规则都不一样。理解了这个后面遇到各种“在PC上好好的上板就崩”的问题第一反应就能想到是不是架构匹配出错了。另外还有一个底层差异是字节序和内存对齐策略。ARM默认小端运行但也可以配置成大端x86是小端。在实际嵌入式开发里如果你要跨平台共享一个二进制数据文件或者裸结构体字节序必须显式处理否则解析出来全是错位数字。这也是很多老工程师坚持所有跨平台协议都要用位流定义、禁止直接读结构体的原因。1.3 Cortex-A、Cortex-R、Cortex-M怎么选ARM的Cortex产品线可以看作一个“分工表”。Cortex-A系列跑的是应用处理器带MMU能跑完整的Linux、Android作用是支撑复杂操作系统和上层应用。树莓派、RK3568/RK3576、i.MX8、高通骁龙、苹果M系列内部都是Cortex-A核心。Cortex-R系列更看重实时性中断响应延迟可以做到纳秒级常见于汽车底盘控制、存储控制器、基带处理这类“延迟绝对不能飙高”的场景。特点是通常不带完整MMU而是带紧耦合内存TCM程序跑在确定性比较强的内存里。Cortex-M系列是最普及的微控制器核心不带MMU资源少、功耗低跑裸机或轻量RTOS例如FreeRTOS、RT-Thread、Zephyr。STM32大部分型号就是Cortex-M3/M4/M7国产GD32、AT32也走的是类似路线。实际选型时判断标准就三条要不要跑完整操作系统、实时要求有多高、功耗和成本阈值是多少。如果只是做传感器采集Cortex-M0足够了要跑Linux和视觉处理优先看Cortex-A带GPU/NPU的SoC如果做电机控制且延迟要求极端Cortex-R才是正解。ARM架构不是越贵越好而是匹配场景。2. 交叉编译为什么非要“跨”着来ARM架构搞明白了接下来就是怎么把代码变成能跑的二进制。很多人第一次接触交叉编译都觉得很怪我在PC上明明装得好好的GCC为什么要单独再搞一套工具链直接在板子上编不行吗当然可以但现实很骨感。开发板性能参差不齐入门级ARM板上编译一个Qt要数小时而PC上交叉编译可能只需要几分钟嵌入式发行版的软件源往往不全装个依赖能把人逼疯再有就是CI流水线和量产固件构建不可能每台板子手动make。于是在性能强的PC上生成目标平台二进制、再拷贝到板子上运行就成为嵌入式开发的标准动作。这就是交叉编译宿主是x86目标是ARM。2.1 编译器和目标架构必须“口径一致”交叉编译的本质是让运行在宿主机上的GCC生成目标机指令集的机器码。它并非魔法只是GCC在设计上就支持了多后端同一套GCC源码可以同时编译出x86后端、ARM后端、RISC-V后端最终前缀不同的那些编译器只是配置了不同后端和不同头文件路径的GCC而已。这里要分清三个概念build构建工具链时所在平台、host工具链运行时所在平台、target工具链生成的程序所运行平台。我们平时常见的x86_64-linux-gnu-gccbuild/host/target都是x86叫本机编译器arm-linux-gnueabihf-gccbuild和host是x86target是ARM叫交叉编译器如果做两阶段编译比如GCC自己也是程序build/host/target还会继续错位那就是自制工具链的进阶话题了。明白了这个逻辑就不会再问“我能不能在Windows上用arm-gcc编译Linux程序”这种问题。理论上可以但要额外处理sysroot、路径、编译目标工程复杂度会高很多所以主流Linux交叉编译都在Linux宿主下完成。2.2 交叉编译工具链的核心组成一套完整的交叉编译工具链至少包括四个部分binutils汇编器as、链接器ld、文件格式工具objcopy、反汇编工具objdump、GCCC/C编译器、C标准库glibc或musl、以及目标平台的头文件和符号链接。这些组件组合起来才是你用来构建可执行文件的“完整车间”。撸代码时最常用的是arm-linux-gnueabihf-gcc但遇到链接地址不对、ELF结构异常、动态链接器缺失时要用到arm-linux-gnueabihf-readelf、arm-linux-gnueabihf-objdump、arm-linux-gnueabihf-objcopy这类看起来不常用的小工具。一次完整的固件发布经常是编译出一堆-o文件再用objcopy把ELF转成裸的bin最后交给产线烧录。新手如果只认识gcc遇到链接脚本问题会非常吃力。工具链包里通常还会带一份sysroot它是目标板根文件系统的“最小样本”里面有lib/ld-linux-armhf.so.3、usr/lib/libc.so以及一堆头文件。交叉编译时编译器会从sysroot中去找目标板上的库和头文件而不是去宿主机/usr/include里找。这个隔离机制很容易被人忽略一旦配置错最常见的结果就是编译出来的程序带着一堆x86的库依赖上板就崩。2.3 工具链命名规则与版本选择工具链的名字看前缀就知道生态这一点值得用一张表记清楚工具链前缀目标平台典型用途arm-none-eabi-ARM 32位裸机/RTOSCortex-M固件开发arm-linux-gnueabihf-ARM 32位Linux硬浮点嵌入式Linux用户态、内核arm-linux-gnueabi-ARM 32位Linux软浮点低端平台/无VFP硬件aarch64-linux-gnu-ARM 64位LinuxAArch64用户态、内核aarch64-none-elf-ARM 64位裸机UEFI、固件、bootloader前缀里的gnueabihf值得多说一句hf就是hard float规定浮点参数通过VFP/NEON寄存器传递效率高软浮点就是通过通用寄存器模拟或者调用软浮点库。选择硬浮点工具链之前必须确认目标CPU有VFP或NEON单元否则运行时会触发未定义指令异常。版本选择上Linaro工具链曾经是嵌入式Linux的事实标准现在一般推荐用Arm官方GNU Toolchain或者发行版自带的gcc-arm-linux-gnueabihf包。32位老平台推荐GCC 10/1164位推荐GCC 12以上。特别要注意glibc版本工具链自带glibc如果比目标板的libc新运行时会报GLIBC_2.XX not found反过来老工具链编译新内核又可能缺少某些宏定义。稳妥做法是拿到板子先跑一遍ldd --version和cat /proc/cpuinfo再决定工具链版本。有意思的是搜索里经常出现的arm compiler 5.06 update 7和这里的Linux交叉工具链并不是一回事。armcc是ARM自家Keil MDK里的商业编译器主要针对Cortex-M裸机固件编译产物没有操作系统加载头不能直接代替arm-linux-gnueabihf-gcc去编译Linux用户态程序。不少从单片机转Linux的新人会把这两个搞混结果下载了armcc却编不出能在开发板上跑的ELF这个坑我先帮你排掉。3. 从零搭建交叉编译环境并跑通第一个程序工具链选好之后最重要的就是真刀真枪跑起来。这部分我按一条完整的实操路径走下载工具链、配置环境变量、编写并编译Hello World、确认ELF格式、部署到板子上运行。每一步都会解释为什么这么做避免你只copy命令跑通、换一台机器又懵掉。我建议你用一颗Cortex-A的板子比如RK3568、i.MX6ULL、ARM3399这类32位和64位都行来跟做。下面命令以32位硬浮点为例如果你的是Cortex-A53这类64位CPU把前缀换成aarch64-linux-gnu-即可。3.1 准备工具链并确认目标环境先把工具链放到一个统一目录。以Arm官方预编译工具链为例mkdir -p ~/toolchains cd ~/toolchains wget https://developer.arm.com/-/media/Files/downloads/gnu/11.2-2022.02/binrel/gcc-arm-11.2-2022.02-x86_64-arm-linux-gnueabihf.tar.xz sudo tar -xJf gcc-arm-11.2-2022.02-x86_64-arm-linux-gnueabihf.tar.xz -C /opt export PATH/opt/gcc-arm-11.2-2022.02-x86_64-arm-linux-gnueabihf/bin:$PATH最好把这个export写进~/.bashrc不然每次开终端都要重新导一次。检查是否生效arm-linux-gnueabihf-gcc -v正常会输出gcc version 11.2.1和target: arm-linux-gnueabihf。此时再查一下目标板信息uname -a cat /proc/cpuinfo | grep model name只有明确目标板的架构、CPU型号、内核版本后你后面加的-march、-mcpu参数才不是瞎猜。3.2 写Hello World完成首次编译整个过程和本机编译没有太大区别多的是工具链前缀#include stdio.h int main(void) { printf(hello arm\n); return 0; }保存为hello.c执行arm-linux-gnueabihf-gcc -O2 -marcharmv7-a -mfpuneon -mfloat-abihard \ -o hello hello.c编译成功后立即用file命令检查产物file hello你会看到类似输出ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3。这一行信息很多但只需要抓两个重点ARM说明这是ARM指令集dynamically linked说明它依赖目标板上的动态库。如果你本机是x86环境直接执行./hello会报Exec format error这是正常的因为它本来就是给ARM板跑的程序。3.3 动态链接与静态链接怎么选上面编译出来的是动态链接程序依赖目标板上的glibc动态库。如果目标板上库很全这种方式体积最小、升级方便。但如果目标板是一套精简rootfs里面连ld-linux-armhf.so.3都没有运行时会报No such file or directory尽管文件明明就在那里。这时候可以用静态链接arm-linux-gnueabihf-gcc -O2 -static -o hello_static hello.c静态编译会把C库直接编进可执行文件体积可能增加几百KB但移植起来省心。很多设备量产时选择静态编译用户态程序就是为了避免生产环境里libc版本不一致导致连锁故障。不过要注意静态编译也不能完全摆脱内核依赖比如系统调用接口变了二进制照样跑不了只是比动态库问题少一些。出货量大的产品我建议库还是正常动态管理用一套和自己的工具链同源的rootfs来配原型验证、临时调试用静态编译省时间。3.4 部署到板子上并验证运行确认产物没问题就把它传到板子上scp hello userboard_ip:/tmp/登录板子执行chmod x /tmp/hello /tmp/hello如果输出hello arm说明整条交叉编译链路已经通了。如果卡在权限或者解释器缺失回头检查readelf -l hello里interpreter那行路径是不是和板子/lib下的动态链接器一致。这一步的成功对新手的意义很大因为后面再复杂的工程本质上都是“工具链正确 头文件路径正确 库路径正确 运行环境匹配”这套组合拳。把这条最小链路刻进肌肉记忆再折腾Qt、OpenSSL才不会像无头苍蝇。4. 实战进阶第三方库与Qt工程交叉编译单个C文件交叉编译通了之后实战会遇到的大多是“依赖麻烦”。一个程序里调用OpenSSL、libcurl、SQLiteQt程序还有一堆Qt库要同时迁过去。这章我挑几个典型场景展开覆盖目前群里问得最多的Redis移植、OpenSSL交叉编译、CMake工程管理、Qt 5.12.10交叉编译环境。4.1 给Redis做一次ARM移植Redis是C写的开源项目官方Makefile基本是为x86默认优化过的但交叉移植并不难。核心是把CC、AR、MALLOC都指到交叉工具链上cd redis-7.x make distclean make CCarm-linux-gnueabihf-gcc \ ARarm-linux-gnueabihf-ar \ MALLOClibc \ CFLAGS-marcharmv7-a -mfpuneon -mfloat-abihard \ LDFLAGS-marcharmv7-aMALLOClibc是我强烈建议带上的参数。Redis默认使用jemalloc它在交叉环境下要做很多平台判断经常出现configure失败。换成libc分配器就是让Redis使用glibc自带的malloc实现性能在绝大多数嵌入式场景下足够用而且省去大量编译期的坑。如果你是64位ARM平台工具链前缀换成aarch64-march参数也换成armv8-a。编译成功后make PREFIX/opt/redis_arm install把redis-server和redis.conf拷贝到板子启动时用./redis-server /path/redis.conf注意在交叉编译Redis时如果遇到atomic相关报错大部分情况是工具链默认CPU架构太老不支持LDREX/STREX指令加上对应的-march就能解决别急着换编译器。4.2 交叉编译OpenSSL并让程序找到库OpenSSL是Crypto、HTTPS、SSH等一堆组件的底层依赖交叉编译时最常见的坑是它默认按照宿主环境生成一堆测试程序然后用来跑本机测试结果跑到ARM版上直接Exec format error。解决办法是configure阶段就把平台和编译器指明确./Configure linux-armv4 \ --prefix$PWD/arm_out \ --cross-compile-prefixarm-linux-gnueabihf- \ no-tests no-shared make -j4 make install_swno-tests可以让它跳过大量测试程序编译省时间也避开交叉环境跑测试的坑no-shared生成静态库方便后续把libcrypto.a直接编进业务程序省得部署时带一堆.so。OpenSSL 3.x之后configure参数略有调整但--cross-compile-prefix和--prefix依旧在。64位ARM平台用linux-aarch64。编完之后业务程序编译时通过-L$PWD/arm_out/lib -lcrypto引入。如果程序依赖头文件记得加-I$PWD/arm_out/include否则编译器会在宿主机/usr/include里找找到的自然还是x86版本的头文件运气差的话链接出一堆莫名奇妙的符号错。4.3 用CMake工具链文件管理复杂工程项目变复杂之后直接敲gcc命令不现实CMake是现在主流选择。交叉编译时只需要准备一个工具链文件比如arm-toolchain.cmakeset(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g) set(CMAKE_FIND_ROOT_PATH /opt/arm-rootfs) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)这里最关键的是三行FIND_ROOT_PATH它们告诉CMake找程序时用宿主机的工具但找库和头文件时只允许去ARM的sysroot里找。而CMAKE_FIND_ROOT_PATH一般指向你从板子上同步出来的根文件系统目录比如板子执行tar -cf sysroot.tar /usr /lib拉到PC解压后再放入这个变量。使用方式mkdir build cd build cmake -DCMAKE_TOOLCHAIN_FILE../arm-toolchain.cmake .. make -j8只要工具链文件写好整个工程在开发机上的构建体验和原生编译几乎一样。但要注意CMake里用find_package找库时那些库也必须是ARM版本否则它会静默地找到宿主机库然后给你编出一个链接混乱的产物——这种情况下file检查不出问题但运行时一定崩。4.4 Qt 5.12.10交叉编译环境的完整配置Qt交叉编译是嵌入式里最繁琐但也最常见的工作。以Qt 5.12.10为例核心目标是在一台x86 Linux PC上用arm-linux-gnueabihf-gcc编出一套跑在ARM板上的Qt库再用这套库的qmake去构建应用。准备工作至少包括三部分目标板rootfs可从板上打包或直接用工具链自带的sysroot、PC上的Qt源码包qt-everywhere-src-5.12.10.tar.xz、以及前面确认无误的交叉工具链。先解压源码并进入qtbase的mkspecs目录tar -xf qt-everywhere-src-5.12.10.tar.xz cd qtbase/mkspecs/linux-arm-gnueabi-g打开qmake.conf把编译器指到你的交叉工具链QMAKE_CC arm-linux-gnueabihf-gcc QMAKE_CXX arm-linux-gnueabihf-g QMAKE_LINK arm-linux-gnueabihf-g QMAKE_LINK_SHLIB arm-linux-gnueabihf-g然后回到源码根目录configure./configure -prefix /opt/Qt5.12.10-arm \ -opensource -confirm-license \ -xplatform linux-arm-gnueabi-g \ -no-opengl \ -nomake examples -nomake tests \ -no-xcb -no-gtk make -j8 make install-no-opengl和-no-xcb是我针对无GPU/无X11开发板减负的常用参数。如果板子有Mali等GPU并能提供OpenGL ES支持需要提前配好eglfs支持那又是另一个深度方向。默认裁剪后的Qt库主要支撑Widgets和linuxfb平台已经能覆盖工业平板、HMI、数据展示类的需求。应用工程编译时使用刚安装的交叉版qmake/opt/Qt5.12.10-arm/bin/qmake your_project.pro make到这一步产物是ARM版的ELF。部署Qt应用不能只拷程序还要把Qt插件和依赖库拷到板子上。最少包括libQt5Core.so.5、libQt5Gui.so.5、libQt5Widgets.so.5以及platforms/libqlinuxfb.so插件。运行前设置export LD_LIBRARY_PATH/opt/qt/lib:$LD_LIBRARY_PATH export QT_QPA_PLATFORMlinuxfb ./your_app我用这套流程给RK3576之类的新板子做过适配只要芯片和rootfs匹配基本就是重复“选编译器、配qmake、裁剪模块、跑linuxfb”这条路线。遇到编译期找不到xcb相关头文件多半是系统里装了xcb开发包但交叉sysroot里没有要么把对应开发包也交叉编一遍要么干脆disable。不要跟它硬刚。5. 常见问题排查与快速定位表交叉编译最痛苦的不是编译本身而是编译过了、运行不起来或者链接阶段报一堆不明所以的错误。这里把我实际项目里遇到过的问题和排查思路整理成一套速查体系关键是你要养成一个习惯看到任何ELF问题先file再readelf最后ldd这三板斧能解决80%的困惑。5.1 架构错误Exec format error怎么查现象是程序上传到板子上执行时报Exec format error或者反过来在PC上试图执行ARM程序报同样的错。这是最直接的“架构不匹配”信号。排查路径file hello # 看是ARM还是x86 readelf -h hello # 看Machine字段 uname -m # 看当前系统架构如果Machine是ARM但你在x86 PC上跑那当然不行如果Machine显示的是x86说明工具链配置错误用的根本不是交叉编译器。如果文件显示是ARM且在ARM板上还报错继续看下一层编译器生成的是ARMv7指令但目标CPU是ARMv8的兼容模式或者一款只支持ARMv5的古老芯片也会执行异常。5.2 链接阶段找不到库、找不到头文件fatal error: openssl/aes.h: No such file or directory头文件缺失undefined reference to SSL_new库缺失或顺序不对cannot find -lcrypto链接器没找到ARM版库文件。对策是三步走第一确认你找的头文件和库来自交叉工具链的sysroot而不是宿主机的/usr/include第二编译参数加上-I指向ARM版头文件目录链接参数加上-L指向ARM版库目录第三调整库顺序把被依赖的库放在右边比如arm-linux-gnueabihf-gcc main.c -L./arm_openssl/lib -lcrypto -o app实际项目里我见过太多人把x86的.so硬塞给ARM工具链然后链接器报“skipping incompatible”警告。这句话看见了请立刻停下来检查它的意思就是这个库不是这个架构能用的。5.3 运行时动态库缺失与GLIBC版本冲突程序在板子上跑系统提示/lib/ld-linux-armhf.so.3: No such file or directory但文件明明在第一反应就是动态链接器和库的路径没对上。可以检查readelf -l hello | grep interpreter看它找的链接器是哪个再对比板子上实际存在的路径。如果是GLIBC_2.34 not found说明你工具链的glibc比板子的旧编译出来的程序引用了更高版本的符号。解决办法优先选择换一套和目标板系统glibc版本匹配的工具链其次考虑静态编译。千万不要直接到板子上去升级glibc容易把整个rootfs搞到开机都开不了。运行时找不到库的另一个常见优先级是编译时用了-rpath指向某些路径但板子上没有对应目录或者你用scp拷了程序却忘了拷依赖的.so。临时调试可以用export LD_LIBRARY_PATH/path/to/lib但发布产品时建议用相对路径配合快速链接器或者直接把依赖库放到系统标准目录。5.4 浮点ABI不匹配与指令集不匹配编译时出现selected processor does not support vfpv3或者链接时报failed to merge target specific data of file ....o大概率是软浮点和硬浮点的对象文件混在一起了。GNUEABIHF工具链编出来的.o带硬浮点标签GNUEABI工具链编出来的.o带软浮点标签二者不能互相链接。检查方法arm-linux-gnueabihf-readelf -A hello输出的Tag_ABI_VFP_args一栏如果是VFP registers说明是硬浮点如果显示Standard就是软浮点。确保你的所有目标文件、静态库、动态库、最终链接器都使用同一套浮点ABI这个问题就消失了。指令集不匹配则表现为编译时没有加-march导致默认生成的指令在目标CPU上无法解码。解决办法是显式指定-marcharmv7-a -mfpuneon -mfloat-abihard64位平台对应-marcharmv8-a下面这个表是我在实际调试中的速查参考现象常见原因快速解法Exec format error架构不匹配/文件损坏file检查Machine字段执行时No such file or directory动态链接器缺失readelf查看interpreterGLIBC_2.XX not found工具链glibc比板子新换匹配工具链或静态编译cannot find -lcryptoARM库路径未指定加-L指向交叉编译的库目录skipping incompatible误用了宿主机/异架构库检查库为readelf文件类型selected processor does not support缺少march/mfpu参数显式指定CPU和浮点选项undefined reference库顺序错/库没链接调整顺序并补充-l选项最后一点个人体会做ARM平台交叉编译这几年我最大的感受是这个方向不是拼谁记住的命令多而是拼谁愿意在出错时多看一眼目标文件。被Exec format error逼到头大的时候用file、readelf这些工具多问自己几句“这个东西到底给谁跑的”往往比盲目换工具链更有效。每次拿到新板子我的习惯是先确认架构和glibc版本再决定工具链最后才动手写代码。这一步省下的时间远远超过提前那几分钟。如果你现在正卡在某个识别不清的编译错误上不妨先把环境停一下跑一遍file和readelf -h大概率谜底就在那几行输出里。
返回列表