Use the -S option to gcc
(or g++
), optionally with -fverbose-asm which works well at the default -O0 to attach C names to asm operands as comments. It works less well at any optimization level, which you normally want to use to get asm worth looking at.
gcc -S helloworld.c
This will run the preprocessor (cpp) over helloworld.c, perform the initial compilation and then stop before the assembler is run. For useful compiler options to use in that case, see How to remove "noise" from GCC/clang assembly output? (or just look at your code on Matt Godbolt's online Compiler Explorer which filters out directives and stuff, and has highlighting to match up source lines with asm using debug information.)
By default, this will output the file helloworld.s
. The output file can be still be set by using the -o option, including -o -
to write to standard output for pipe into less.
gcc -S -o my_asm_output.s helloworld.c
Of course, this only works if you have the original source.
An alternative if you only have the resultant object file is to use objdump, by setting the --disassemble
option (or -d
for the abbreviated form).
objdump -S --disassemble helloworld > helloworld.dump
-S
interleaves source lines with normal disassembly output, so this option works best if debugging option is enabled for the object file (-g at compilation time) and the file hasn't been stripped.
Running file helloworld
will give you some indication as to the level of detail that you will get by using objdump.
Other useful objdump
options include -rwC
(to show symbol relocations, disable line-wrapping of long machine code, and demangle C++ names). And if you don't like AT&T syntax for x86, -Mintel
. See the man page.
So for example, objdump -drwC -Mintel -S foo.o | less
.
-r
is very important with a .o
that only has 00 00 00 00
placeholders for symbol references, as opposed to a linked executable.
If you compile with debug symbols (add -g
to your GCC command line, even if you're also using -O3
1),
you can use objdump -S
to produce a more readable disassembly interleaved with C source.
>objdump --help
[...]
-S, --source Intermix source code with disassembly
-l, --line-numbers Include line numbers and filenames in output
objdump -drwC -Mintel
is nice:
-r
shows symbol names on relocations (so you'd see puts
in the call
instruction below)
-R
shows dynamic-linking relocations / symbol names (useful on shared libraries)
-C
demangles C++ symbol names
-w
is "wide" mode: it doesn't line-wrap the machine-code bytes
-Mintel
: use GAS/binutils MASM-like .intel_syntax noprefix
syntax instead of AT&T
-S
: interleave source lines with disassembly.
You could put something like alias disas="objdump -drwCS -Mintel"
in your ~/.bashrc
. If not on x86, or if you like AT&T syntax, omit -Mintel
.
Example:
> gcc -g -c test.c
> objdump -d -M intel -S test.o
test.o: file format elf32-i386
Disassembly of section .text:
00000000 <main>:
#include <stdio.h>
int main(void)
{
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 83 e4 f0 and esp,0xfffffff0
6: 83 ec 10 sub esp,0x10
puts("test");
9: c7 04 24 00 00 00 00 mov DWORD PTR [esp],0x0
10: e8 fc ff ff ff call 11 <main+0x11>
return 0;
15: b8 00 00 00 00 mov eax,0x0
}
1a: c9 leave
1b: c3 ret
Note that this isn't using -r
so the call rel32=-4
isn't annotated with the puts
symbol name. And looks like a broken call
that jumps into the middle of the call instruction in main. Remember that the rel32
displacement in the call encoding is just a placeholder until the linker fills in a real offset (to a PLT stub in this case, unless you statically link libc).
Footnote 1: Interleaving source can be messy and not very helpful in optimized builds; for that, consider https://godbolt.org/ or other ways of visualizing which instructions go with which source lines. In optimized code there's not always a single source line that accounts for an instruction but the debug info will pick one source line for each asm instruction.
Best Answer
You should use GCC's
-fverbose-asm
option. It makes the compiler output additional information (in the form of comments) that make it easier to understand the assembly code's relationship to the original C/C++ code.