【问题标题】:Is there a way to get gcc to output raw binary?有没有办法让 gcc 输出原始二进制文件?
【发布时间】:2023-03-08 08:52:02
【问题描述】:

是否有一组命令行选项可以说服 gcc 从自包含源文件生成平面二进制文件?例如,假设 foo.c 的内容是

static int f(int x)
{
  int y = x*x;
  return y+2;
}

没有外部引用,没有任何东西可以导出到链接器。我想得到一个只有这个功能的机器指令的小文件,没有任何其他装饰。除了 32 位保护模式外,有点像 (DOS) .COM 文件。

【问题讨论】:

  • 两个很好的答案同时提交。我不知道该打勾。
  • 您可以考虑接受直接回答您问题的more recent answer(纯编译器/链接器答案)。
  • 一个在其翻译单元中没有调用者的static 函数将被优化掉。所以在使用答案之前删除static

标签: linux gcc command-line linker x86


【解决方案1】:

试试这个:

$ gcc -c test.c     
$ objcopy -O binary -j .text test.o binfile

您可以使用objdump 确保它是正确的:

$ objdump -d test.o 
test.o:     file format pe-i386


Disassembly of section .text:

00000000 <_f>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 04                sub    $0x4,%esp
   6:   8b 45 08                mov    0x8(%ebp),%eax
   9:   0f af 45 08             imul   0x8(%ebp),%eax
   d:   89 45 fc                mov    %eax,-0x4(%ebp)
  10:   8b 45 fc                mov    -0x4(%ebp),%eax
  13:   83 c0 02                add    $0x2,%eax
  16:   c9                      leave  
  17:   c3                      ret  

并将其与二进制文件进行比较:

$ hexdump -C binfile 
00000000  55 89 e5 83 ec 04 8b 45  08 0f af 45 08 89 45 fc  |U......E...E..E.|
00000010  8b 45 fc 83 c0 02 c9 c3                           |.E......|
00000018

【讨论】:

  • 我必须将 -j .text 作为参数添加到 objcopy 才能获得正确的结果。
  • 为什么?当你不这样做时发生了什么?
  • 它从 .o 文件中复制了错误的(不是 .text)部分。版本:GNU objcopy version 2.22.52.0.1-10.fc17 20120131
  • 小心将cc -c 的结果直接传递给objcopy 而没有中间ld。如果cc 决定发出任何重定位,ld 将不会处理它们,并且二进制文件将是错误的。 (二进制文件在应该有地址或偏移量的地方会有空字节。)cc 可能会发出重定位,即使源文件出现,如问题所述,“自包含”。
【解决方案2】:

您可以使用-Wl,&lt;linker option&gt; 直接将选项传递给链接器

下面是从man gcc复制的相关文档

-Wl,选项
将选项作为选项传递给链接器。如果 option 包含逗号,则在逗号处将其拆分为多个选项。您可以使用 此语法将参数传递给选项。例如, -Wl,-Map,output.map 将 -Map output.map 传递给链接器。使用 GNU 链接器时,您也可以使用 -Wl,-Map=output.map.

所以当使用 gcc 编译时,如果你传递了-Wl,--oformat=binary,你将生成一个二进制文件而不是 elf 格式。其中--oformat=binary 告诉ld 生成二进制文件。

这消除了单独objcopy 的需要。

请注意,--oformat=binary 可以在 linker script 中表示为 OUTPUT_FORMAT("binary")。如果您想处理平面二进制文件,您很有可能会受益于链接描述文件提供的高级控制。

【讨论】:

  • 实际上这个答案是这里唯一真正有效的答案......我会说其他人是可以接受的解决方法。
  • 供将来参考:--oformat=binary 解决方案似乎在某些情况下会发出不正确的机器代码我不知道某些事情,但如果你有机会遇到一些外部符号的奇怪行为,然后尝试objcopy 解决方案。
  • 我在使用此方法时遇到了issues 输出二进制文件太大(用零填充)。
【解决方案3】:

您可以使用objcopy 将文本段拉出.o 文件或a.out 文件。

$ cat q.c
f() {}
$ cc -S -O q.c
$ cat q.s
        .file   "q.c"
        .text
.globl f
        .type   f, @function
f:
        pushl   %ebp
        movl    %esp, %ebp
        popl    %ebp
        ret
        .size   f, .-f
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits
$ cc -c -O q.c
$ objcopy -O binary q.o q.bin
$ od -X q.bin
0000000 5de58955 000000c3
0000005
$ objdump -d q.o
q.o:     file format elf32-i386
Disassembly of section .text:
00000000 <f>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   5d                      pop    %ebp
   4:   c3                      ret    

【讨论】:

  • 您似乎丢失了链接。
【解决方案4】:

其他答案绝对是要走的路。但是,我必须为 objcopy 指定额外的命令行参数,以使我的输出符合预期。请注意,我正在 64 位机器上开发 32 位代码,因此使用了 -m32 参数。另外,我更喜欢 intel 汇编语法,所以你也会在参数中看到这一点。

$ cat test.c
int main() { return 0; }
$ gcc -nostdinc -m32 -masm=intel -Wall -c test.c -o test.o
$ objdump --disassemble --disassembler-options intel test.o

test.o:     file format elf32-i386


Disassembly of section .text:

00000000 <main>:
   0:   55                      push   ebp
   1:   89 e5                   mov    ebp,esp
   3:   b8 00 00 00 00          mov    eax,0x0
   8:   5d                      pop    ebp
   9:   c3                      ret    

好的,在这里我必须指定我只需要 .text 部分:

$ objcopy --only-section=.text --output-target binary test.o test.bin
$ hexdump -C test.bin
00000000  55 89 e5 b8 00 00 00 00  5d c3   |U.......].|
0000000a

我花了大约 2 小时的阅读和尝试不同的选项才弄明白这一点。希望这可以节省其他人的时间。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-11
    • 2018-08-25
    • 1970-01-01
    • 1970-01-01
    • 2014-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多