【发布时间】:2021-01-15 14:52:03
【问题描述】:
我正在尝试使用在 Ubuntu 上运行的交叉编译器来编译一些 Raspberry Pi 代码。我已经测试了可执行文件,它工作正常,但是将该可执行文件与本机编译器(Raspberry Pi 上的 gcc)生成的可执行文件进行比较,我发现二进制文件之间存在一些差异。
设置
本机编译器是gcc (Raspbian 8.3.0-6+rpi1) 8.3.0,在 Raspbian GNU/Linux 10 (buster) 上运行。交叉编译器是在 Ubuntu 18.04.4 (Bionic Beaver) 上运行的 arm-linux-gnueabihf-gcc-8 (Ubuntu/Linaro 8.4.0-1ubuntu1~18.04) 8.4.0。
我试图确保交叉编译器使用的编译标志与 Pi 上默认使用的编译标志相同:
-
marm生成在 ARM 状态下运行的代码(而不是 Thumb) march=armv6mfpu=vfp-
O0并且我关闭了优化以确保没有发生“有趣”的事情
我尝试过的
我写了我能想到的最简单的 C 代码:
int main() {}
然后使用统一汇编语言语法将其编译成汇编。两个编译器都生成了完全相同的程序集(.ident 行除外,但这并不重要):
.arch armv6
.eabi_attribute 28, 1
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 2
.eabi_attribute 30, 6
.eabi_attribute 34, 1
.eabi_attribute 18, 4
.file "main.c"
.text
.align 2
.global main
.arch armv6
.syntax unified
.arm
.fpu vfp
.type main, %function
main:
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 1, uses_anonymous_args = 0
@ link register save eliminated.
str fp, [sp, #-4]!
add fp, sp, #0
mov r3, #0
mov r0, r3
add sp, fp, #0
@ sp needed
ldr fp, [sp], #4
bx lr
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 8.4.0-1ubuntu1~18.04) 8.4.0"
.section .note.GNU-stack,"",%progbits
然后我尝试了其他方法:我只是在将目标文件链接到任何共享库之前构建了目标文件,但即便如此,本机文件和交叉编译文件之间也会存在差异。因此,这不是 Raspbian 和 Ubuntu 安装的库不同的问题。
问题
如果我使用本机编译器和交叉编译器编译此汇编代码,即使它们的功能相同,我也会得到不同的可执行文件。
问题
尽管这没什么大不了的,因为两个可执行文件都按预期工作(它们什么都不做并返回 0)我想知道:
- 为什么会这样?
- 是使用稍微不同版本的 gcc(8.3.0 与 8.4.0)的问题吗?
- 或者这些差异是运行编译器的操作系统所固有的?
- 是否可以使用交叉编译器来构建与本地编译器完全相同的可执行文件?即使代码非常庞大和复杂?
【问题讨论】:
-
鉴于它是同一个程序集,它绝对不是你的编译器。可能是您的链接器或传递给链接器的选项,或更可能是库版本。你没有说有什么区别。
-
您可以使用
objdump查看实际差异是什么(尝试--disassemble和--full-contents)。一旦你看到了实际的不同,原因就很明显了。 -
@NateEldredge,谢谢!使用
--full-contents我看到main.o文件仅在.comment部分的内容上有所不同;他们指定了运行 gcc 的操作系统,即 Raspbian 或 Ubuntu。但是在链接到其他库之后比较可执行文件,我发现除了.comment部分之外还有很多差异。 -
您是否链接到相同的 libc 版本?如果没有,也许发生了不同的符号版本? glibc 使用了一些符号版本控制技巧。
-
我们今天刚刚讨论了这个话题。没有理由期望任何两个编译器,即使是使用可能不同的选项单独构建的相同版本,也会生成相同的代码。然后是 C 库的主题,它不一定是工具链的一部分,即使两者都是 gnu 但可能是不同的版本,这里也不会匹配。链接器脚本通常是 C 库的一部分,但不是真正的编译器。基本上没有理由期望这些二进制文件匹配。显然他们没有这样的问题。