GCC忽略-mcpu=arm7tdmi参数生成armv4t不支持指令问题
问题描述
- 运行环境:Debian系统,搭载GCC版本为10.2.1(对应Debian软件包版本10.2.1-6)
- 编译目标:为arm7tdmi核心(即armv4t架构)编译小型测试程序
- 使用的编译命令:
arm-linux-gnueabi-gcc -march=armv4t -mcpu=arm7tdmi -nostartfiles -static test.c -o test
编译完成后出现异常:生成的二进制包含clz等armv4t架构不支持的指令(clz为ARMv5架构新增的前导零计数指令),并未按照指定的CPU、架构参数生成兼容代码。
通过objdump工具反汇编二进制文件,可见如下异常指令片段:
104f4: 0a000071 beq 106c0 <.divsi3_skip_div0_test+0x1f0> 104f8: e16f2f13 clz r2, r3 104fc: e16f0f11 clz r0, r1 10500: e0402002 sub r2, r0, r2 10504: e272201f rsbs r2, r2, #31
通过readelf工具检查二进制文件属性,发现其标记的实际目标架构为armv5te,对应属性输出如下:
File Attributes Tag_CPU_name: "5TE" Tag_CPU_arch: v5TE Tag_ARM_ISA_use: Yes Tag_THUMB_ISA_use: Thumb-1
本次编译使用的test.c源码内容如下:
#include <stdint.h> #include <stddef.h> void _start() { const size_t len = 8; uint8_t arr[8] = {10, 12, 8, 5, 0, 2, 3, 55}; uint8_t *data = arr; uint16_t a = 1, b = 0, m = 7; for(int i=0; i<len; i++) { a = (a + data[i]) % m; b = (a + b) % m; } uint32_t res = (uint32_t)b << 16 | (uint32_t)a; }
问题原因
GCC并没有忽略你传入的-march和-mcpu参数,你手写的业务代码部分确实是按照armv4t架构生成指令的,问题出在静态链接的libgcc运行库上:
- 你的代码中使用了整数取模
%运算,armv4t架构没有硬件除法指令,GCC会自动调用libgcc库中预编译的整数除法辅助函数(反汇编中出现的.divsi3_skip_div0_test就是这类函数)实现运算逻辑。 - Debian源提供的10.2.1版本
arm-linux-gnueabi-gcc交叉工具链,默认没有内置适配armv4t架构的libgcc静态库变体,随包自带的libgcc静态库是按armv5te架构预编译的,本身就包含clz这类armv5及以上架构才支持的指令。 - 静态链接阶段,工具链找不到匹配armv4t架构的libgcc库,只能将系统自带的armv5te版本libgcc代码链接进最终二进制文件,不仅会引入armv4t不兼容的指令,ELF文件头的架构标记也会被链接进来的高版本目标文件拉高为armv5te。
解决方法
- 最稳妥的方案是换用专门适配armv4t/arm7tdmi目标的交叉工具链,不要直接使用Debian源默认的arm-linux-gnueabi工具链。可以自行编译GCC交叉工具链,编译时通过
--with-arch=armv4t --with-cpu=arm7tdmi配置参数生成对应架构的libgcc库,也可以直接使用Arm官方发布的、覆盖armv4t目标的GNU工具链。 - 如果只是做极简功能测试,也可以手动改写代码中会触发libgcc调用的运算逻辑,比如把取模运算替换为不依赖除法库函数的实现,避免链接到预编译的高版本libgcc代码,但这种方案不适合正式开发场景。
内容的提问来源于stack exchange,提问作者unturned3
相关产品推荐
相关产品推荐

