【发布时间】:2018-03-02 13:41:52
【问题描述】:
TL;DR:0x400921f4 是 3.1415801 的 IEEE 754 表示形式吗?这是某种软浮动吗?我错过了什么?
我目前正在为 Raspberry Pi 3 和 Raspberry Pi Zero W 构建自己的交叉编译器时了解 crosstool-NG。(*1)
在阅读了分布在他们的论坛和 wiki 上的文档后,我了解到 RPi3 和 RPi0 编译器需要分别针对不同的 ARM 架构,ARMv7 和 ARMv6。但是,两个 SoC 都有一个浮点单元,所以我想使用硬浮点来匹配其余的 Raspberry Pi 库。
我针对RaspberryPi Tool repository 提供的 arm-linux-gnueabihf-gcc 4.9.3 测试了我新建的 armv6-rpi-linux-gnueabi-gcc 6.4.0。我阅读了如何确定可执行文件是否使用硬 fp here。果然,当我编译以下最小测试用例时。
我使用gcc -O0 -o main main.c 与两个编译器一起编译。在以下内容中:main.rpi 用于我的工具链,main.hf 用于预构建的工具链。
#include <stdio.h>
int main(int argc, char** argv) {
printf("Hello, world %0.7f", 3.14158f);
return 0;
}
readelf -A main.rpi 在使用我新建的 armv6-rpi-linux-gnueabi-gcc 时不会返回 Tag_ABI_VFP_args: VFP registers,但在使用预构建的 arm-linux-gnueabihf-gcc 时会返回 readelf -A main.hf。但是,这两个可执行文件在默认的 raspbian 拉伸图像上运行得很好,据说该图像仅支持硬 fp,输出如下:
Hello, world 3.1415801
奇怪的是,RPi 本身上的ldd main.rpi 只返回not a dynamic executable,而它列出了ldd main.hf 的依赖关系。同样,两者都运行并给出预期的输出。 objdump -R(在 RPi 上运行)对两个可执行文件返回相同的结果:
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0002101c R_ARM_GLOB_DAT __gmon_start__
0002100c R_ARM_JUMP_SLOT printf@GLIBC_2.4
00021010 R_ARM_JUMP_SLOT __libc_start_main@GLIBC_2.4
00021014 R_ARM_JUMP_SLOT __gmon_start__
00021018 R_ARM_JUMP_SLOT abort@GLIBC_2.4
这让我担心我使用了soft 或softfp 而不是hard 浮点数。因此,我使用objdump -d(在 RPi 上运行)查看了两个可执行文件的反汇编列表:
main.rpi:
---------
0001045c <main>:
1045c: e92d4800 push {fp, lr}
10460: e28db004 add fp, sp, #4
10464: e24dd008 sub sp, sp, #8
10468: e50b0008 str r0, [fp, #-8]
1046c: e50b100c str r1, [fp, #-12]
10470: e3a02103 mov r2, #-1073741824 ; 0xc0000000
10474: e59f3014 ldr r3, [pc, #20] ; 10490 <main+0x34>
10478: e59f0014 ldr r0, [pc, #20] ; 10494 <main+0x38>
1047c: ebffffa1 bl 10308 <printf@plt>
10480: e3a03000 mov r3, #0
10484: e1a00003 mov r0, r3
10488: e24bd004 sub sp, fp, #4
1048c: e8bd8800 pop {fp, pc}
10490: 400921f4 .word 0x400921f4 <--- float 3.1415801 here
10494: 00010508 .word 0x00010508 <--- pointer to format string
.
main.hf:
--------
000103f8 <main>:
103f8: e92d4800 push {fp, lr}
103fc: e28db004 add fp, sp, #4
10400: e24dd008 sub sp, sp, #8
10404: e50b0008 str r0, [fp, #-8]
10408: e50b100c str r1, [fp, #-12]
1040c: e59f0018 ldr r0, [pc, #24] ; 1042c <main+0x34>
10410: e3a02103 mov r2, #-1073741824 ; 0xc0000000
10414: e59f3014 ldr r3, [pc, #20] ; 10430 <main+0x38>
10418: ebffffa1 bl 102a4 <printf@plt>
1041c: e3a03000 mov r3, #0
10420: e1a00003 mov r0, r3
10424: e24bd004 sub sp, fp, #4
10428: e8bd8800 pop {fp, pc}
1042c: 000104a4 .word 0x000104a4 <--- pointer to format string
10430: 400921f4 .word 0x400921f4 <--- float 3.1415801 here
令我大吃一惊的是,两个编译器都为 main 生成了相同的结果,差异很小。 (*2) 在我看来,两者都像 hardfp。
我检查了是否有一些诡计,编译器将浮点数放在实际的字符串中,但没有发现。然后我随便确保 0x400921f4 确实是我打印的浮点值 3.1415801。我使用两个在线工具进行了检查,here 和 here。那是我对浮点数据的理解明显崩溃的时候。
所有转换器都告诉我 3.1415801 应该是 0x40490fa6 并且两个可执行文件中的文字 0x400921f4 产生 ~2.1426973。两个编译器是否都使用特殊的(软件?)浮点格式,即使它们表明它们使用Tag_ABI_FP_number_model: IEEE 754?我一直认为软件浮点只是常规的 IEEE 754,但在软件中计算而不是不同的格式。
如果我成功使用 hardfp 并且我的推理不正确,或者当我什至在反汇编中看不到正确的浮点常量时如何解释明显正确的行为,我现在真的很困惑。
(*1) 因为这是我第一次使用 crosstool-NG,所以我使用了他们的示例配置 armv6-rpi-linux-gnueabi,但将 gcc 降级到 6.4.0 和 gdb 降级到 7.12.1 以大致匹配可用的交叉编译器工具链对于 Windows。我还针对内核 4.4.83 及更高版本而不是 3.12.74。
(*2) 我检查了 printf@plt 中是否隐藏了软浮点操作,但事实并非如此。两者的程序集是(具有不同的地址):
00010308 <printf@plt>:
10308: e28fc600 add ip, pc, #0, 12
1030c: e28cca10 add ip, ip, #16, 20 ; 0x10000
10310: e5bcfcfc ldr pc, [ip, #3324]! ; 0xcfc
【问题讨论】:
-
虽然没有必要这样做,但我还没有看到编译器在硬浮点和软浮点之间来回切换时使用不同的格式。但也许那些编译器就在那里。
-
嗯...尽管有答案,但 ISTM 认为
0x400921F4是形成 8 字节双精度3.1415801的字节的十六进制表示的部分(完整的十六进制值:0x400921F4BF5A1D83)。3.1415801f的 4 字节浮点值是0x40490FA6。
标签: floating-point raspberry-pi arm