【问题标题】:Generate the same object file with a native compiler and a cross compiler使用本机编译器和交叉编译器生成相同的目标文件
【发布时间】: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=armv6
  • mfpu=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 库的一部分,但不是真正的编译器。基本上没有理由期望这些二进制文件匹配。显然他们没有这样的问题。

标签: c linux assembly gcc arm


【解决方案1】:

链接器可能会在二进制文件中添加一个“build id”。制造虚假的“差异”。

尝试使用 -Wl,--build-id=none 编译它们。

还有.comment 部分,其中包含有关编译器版本及其运行平台的信息。之后您可以使用 strip -R .comment your_binary 将其剥离。

我不知道有什么方法可以让 gcc 不创建 .comment 部分,除了使用临时的 linker script(这可能不实用):

SECTIONS { /DISCARD/ : { *(.comment) } } INSERT BEFORE .bss

愚蠢的例子:

cc -Wall -o no_comment \
  -include stdio.h -xc - <<<'int main(){printf("test\n");}' \
  -xnone -Wl,-T /dev/fd/7 /dev/fd/7 7<<<'SECTIONS { /DISCARD/ : { *(.comment) } } INSERT BEFORE .bss' \
  -Wl,--build-id=none

【讨论】:

  • 谢谢您,我尝试了您的建议,但我仍然看到文件之间存在许多差异。正如@PeterCordes 评论的那样,我认为问题在于安装在 Ubuntu 上的库版本。
  • 不过,我会留下答案以供参考。
  • 我尝试了您最后发布的示例,但出现此错误:/usr/lib/gcc-cross/arm-linux-gnueabihf/8/../../../../arm-linux-gnueabihf/bin/ld: .ldata not found for insertcollect2: error: ld returned 1 exit status
  • @David 查看默认链接器脚本(使用ld --verbose)并找到另一个地方插入您的额外命令。 .bss 应该工作而不是 .ldata。我添加了一个指向 GNU ld 手册的链接——阅读它;-)
  • 感谢您的参考!是的! .bss 有效,它是我默认链接器脚本的一部分。
【解决方案2】:

是的,@user414777 是对的。

还有

.ident 行,但那一条无关紧要

... 实际上以二进制结尾: 试试readelf -p .comment a.out

【讨论】:

  • 谢谢@kisch,你说得对,我刚刚按照你的建议用readelf 验证了它,并且注释是可执行文件的一部分。很高兴知道这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-02-05
  • 1970-01-01
  • 2016-09-21
  • 2017-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多