43

Is there a set of command-line options that will convince gcc to produce a flat binary file from a self-contained source file? For example, suppose the contents of foo.c are

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

No external references, nothing to export to the linker. I'd like to get a small file with just the machine instructions for this function, without any other decoration. Sort of like a (DOS) .COM file except 32-bit protected mode.

3
  • 3
    Two fine answers submitted at the same time. I don't know which one to give the check mark. Commented Oct 30, 2009 at 5:57
  • 1
    You can consider accepting a more recent answer which directly answers your question (pure compiler/linker answer). Commented Oct 18, 2016 at 20:41
  • A static function with no callers in its translation unit will optimize away. So remove the static before using the answers. Commented Apr 23, 2020 at 9:02

4 Answers 4

51

Try this out:

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

You can make sure it's correct with 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 

And compare it with the binary file:

$ 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 
Sign up to request clarification or add additional context in comments.

4 Comments

I had to include -j .text as a parameter to objcopy to get the correct result.
Why? What happened when you didn't?
It copied the wrong (not .text) section from the .o file. Version: GNU objcopy version 2.22.52.0.1-10.fc17 20120131
Be careful with passing the result of cc -c directly to objcopy without an intermediate ld. If cc decides to emit any relocations, ld won't be around to process them, and the binary will be wrong. (The binary will have null bytes where there should be addresses or offsets.) cc might emit relocations even if the source file appears, as the question mentions, "self-contained."
34

You can pass options to the linker directly with -Wl,<linker option>

The relevant documentation is copied below from the man gcc

-Wl,option
Pass option as an option to the linker. If option contains commas, it is split into multiple options at the commas. You can use this syntax to pass an argument to the option. For example, -Wl,-Map,output.map passes -Map output.map to the linker. When using the GNU linker, you can also get the same effect with -Wl,-Map=output.map.

So when compiling with gcc if you pass -Wl,--oformat=binary you will generate a binary file instead of the elf format. Where --oformat=binary tells ld to generate a binary file.

This removes the need to objcopy separately.

Note that --oformat=binary can be expressed as OUTPUT_FORMAT("binary") from within a linker script. If you want to deal with flat binaries, there's a big chance that you would benefit from high level of control that linker scripts provide.

3 Comments

Actually this answer is the only real valid answer here... I'd say that others are acceptable workarounds.
For future reference: the --oformat=binary solution seems to emit incorrect machine code in some cases or I'm not aware of something, but if you by any chance encounter some weird behavior of external symbols, then try objcopy solution.
I ran into issues of the output binary being way too big (padded with zeros) when using this method.
20

You can use objcopy to pull the text segment out of the .o file or the a.out file.

$ 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 

1 Comment

You seem to have lost the link.
10

The other answers are definitely the way to go. However, I had to specify additional command line arguments to objcopy in order for my output to be as expected. Note that I am developing 32-bit code on a 64-bit machine, hence the -m32 argument. Also, I like intel assembly syntax better, so you'll see that in the arguments as well.

$ 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 

Ok, here's where I had to specify that I specifically only wanted the .text section:

$ 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 

It took me about 2 hours of reading and trying different options before I figured this out. Hopefully this saves someone else that time.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.