I like objdump for this, but the most useful options are not obvious - especially if you use it in an object file that contains repositions and not the final binary.
objdump -d some_binary does a reasonable job.
objdump -d some_object.o less useful because external function calls do not get exposed:
... 00000005 <foo>: 5: 55 push %ebp 6: 89 e5 mov %esp,%ebp 8: 53 push %ebx ... 29: c7 04 24 00 00 00 00 movl $0x0,(%esp) 30: e8 fc ff ff ff call 31 <foo+0x2c> 35: 89 d8 mov %ebx,%eax ...
call is actually printf() ... adding the -r flag helps with this; he marks the move. objdump -dr some_object.o gives:
... 29: c7 04 24 00 00 00 00 movl $0x0,(%esp) 2c: R_386_32 .rodata.str1.1 30: e8 fc ff ff ff call 31 <foo+0x2c> 31: R_386_PC32 printf ...
Then it will be useful for me to see each line annotated as <symbol+offset> . objdump has a convenient option for this, but it has the annoying side effect of disabling dumping the actual bytes - objdump --prefix-addresses -dr some_object.o gives:
... 00000005 <foo> push %ebp 00000006 <foo+0x1> mov %esp,%ebp 00000008 <foo+0x3> push %ebx ...
But it turns out that you can undo this by providing another obscure option, finally arriving at my favorite objdump spell:
objdump --prefix-addresses --show-raw-insn -dr file.o
which gives a conclusion as follows:
... 00000005 <foo> 55 push %ebp 00000006 <foo+0x1> 89 e5 mov %esp,%ebp 00000008 <foo+0x3> 53 push %ebx ... 00000029 <foo+0x24> c7 04 24 00 00 00 00 movl $0x0,(%esp) 2c: R_386_32 .rodata.str1.1 00000030 <foo+0x2b> e8 fc ff ff ff call 00000031 <foo+0x2c> 31: R_386_PC32 printf 00000035 <foo+0x30> 89 d8 mov %ebx,%eax ...
And if you created using debugging symbols (i.e. compiled with -g ) and you replace -dr with -Srl , it will try to annotate the output with the corresponding source lines.