How do you get assembler output from C/C++ source in gcc?
Solution 1
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. 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 info.)
By default this will output a file helloworld.s
. The output file can be still be set by using the -o
option, including -o -
to write to stdout 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.
Solution 2
This will generate assembly code with the C code + line numbers interweaved, to more easily see which lines generate what code:
# create assembler code:
g++ -S -fverbose-asm -g -O2 test.cc -o test.s
# create asm interlaced with source lines:
as -alhnd test.s > test.lst
Found in Algorithms for programmers, page 3 (which is the overall 15th page of the PDF).
Solution 3
The following command line is from Christian Garbin's blog
g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
I ran G++ from a DOS window on Win-XP, against a routine that contains an implicit cast
c:\gpp_code>g++ -g -O -Wa,-aslh horton_ex2_05.cpp >list.txt
horton_ex2_05.cpp: In function `int main()':
horton_ex2_05.cpp:92: warning: assignment to `int' from `double'
The output is asssembled generated code iterspersed with the original C++ code (the C++ code is shown as comments in the generated asm stream)
16:horton_ex2_05.cpp **** using std::setw;
17:horton_ex2_05.cpp ****
18:horton_ex2_05.cpp **** void disp_Time_Line (void);
19:horton_ex2_05.cpp ****
20:horton_ex2_05.cpp **** int main(void)
21:horton_ex2_05.cpp **** {
164 %ebp
165 subl $128,%esp
?GAS LISTING C:\DOCUME~1\CRAIGM~1\LOCALS~1\Temp\ccx52rCc.s
166 0128 55 call ___main
167 0129 89E5 .stabn 68,0,21,LM2-_main
168 012b 81EC8000 LM2:
168 0000
169 0131 E8000000 LBB2:
169 00
170 .stabn 68,0,25,LM3-_main
171 LM3:
172 movl $0,-16(%ebp)
Solution 4
Use the -S switch
g++ -S main.cpp
or also with gcc
gcc -S main.c
Also see this
Solution 5
-save-temps
This was mentioned at https://stackoverflow.com/a/17083009/895245 but let me further exemplify it.
The big advantage of this option over -S
is that it is very easy to add it to any build script, without interfering much in the build itself.
When you do:
gcc -save-temps -c -o main.o main.c
main.c
#define INC 1
int myfunc(int i) {
return i + INC;
}
and now, besides the normal output main.o
, the current working directory also contains the following files:
-
main.i
is a bonus and contains the preprossessed file:# 1 "main.c" # 1 "<built-in>" # 1 "<command-line>" # 31 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 32 "<command-line>" 2 # 1 "main.c" int myfunc(int i) { return i + 1; }
-
main.s
contains the desired generated assembly:.file "main.c" .text .globl myfunc .type myfunc, @function myfunc: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -4(%rbp) movl -4(%rbp), %eax addl $1, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size myfunc, .-myfunc .ident "GCC: (Ubuntu 8.3.0-6ubuntu1) 8.3.0" .section .note.GNU-stack,"",@progbits
If you want to do it for a large number of files, consider using instead:
-save-temps=obj
which saves the intermediate files to the same directory as the -o
object output instead of the current working directory, thus avoiding potential basename conflicts.
Another cool thing about this option is if you add -v
:
gcc -save-temps -c -o main.o -v main.c
it actually shows the explicit files being used instead of ugly temporaries under /tmp
, so it is easy to know exactly what is going on, which includes the preprocessing / compilation / assembly steps:
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -E -quiet -v -imultiarch x86_64-linux-gnu main.c -mtune=generic -march=x86-64 -fpch-preprocess -fstack-protector-strong -Wformat -Wformat-security -o main.i
/usr/lib/gcc/x86_64-linux-gnu/8/cc1 -fpreprocessed main.i -quiet -dumpbase main.c -mtune=generic -march=x86-64 -auxbase-strip main.o -version -fstack-protector-strong -Wformat -Wformat-security -o main.s
as -v --64 -o main.o main.s
Tested in Ubuntu 19.04 amd64, GCC 8.3.0.
CMake predefined targets
CMake automatically provides a targets for the preprocessed file:
make help
shows us that we can do:
make main.s
and that target runs:
Compiling C source to assembly CMakeFiles/main.dir/main.c.s
/usr/bin/cc -S /home/ciro/hello/main.c -o CMakeFiles/main.dir/main.c.s
so the file can be seen at CMakeFiles/main.dir/main.c.s
Tested on cmake 3.16.1.
Doug T.
I work at Shopify in search and discovery to help every business be able to connect their products and ideas with customers. Blog @softwaredoug LinkedIn
Updated on October 12, 2021Comments
-
Doug T. over 2 years
How does one do this?
If I want to analyze how something is getting compiled, how would I get the emitted assembly code?
-
Grumdrig about 11 yearsSadly,
as
on OS X doesn't know these flags. If it did, though, you could probably one-line this using-Wa
to pass options toas
. -
legends2k about 11 years
g++ -g -O0 -c -fverbose-asm -Wa,-adhln test.cpp > test.lst
would be the short hand version of this. -
phuclv about 10 yearsYou can also use either
gcc -c -g -Wa,-ahl=test.s test.c
orgcc -c -g -Wa,-a,-ad test.c > test.txt
-
touchStone over 9 yearsan addition use : objdump -M intel -S --disassemble helloworld > helloworld.dump to get the object dump in intel syntax compatible with nasm on linux.
-
Jesse Chisholm almost 9 years@Paladin - Not necessarily. The OP was about getting the assembler output equivalent of the C/C++ source code, this gets the Listing, which I agree is more useful for understanding what the compiler and optimizer is doing. But it would cause the assembler itself to barf, as it is not expecting the line numbers, and compiled bytes off tot he left of the assembly instructions.
-
fiorentinoing over 8 yearsIf you have a single function to optimize/check, then you can give a try to online Interactive C++ Compilers i.e. godbolt
-
Peter Cordes almost 8 yearsA blog post explaining this in more detail, including the one-command version like legends and Lu'u posted. But why
-O0
? That's full of loads/stores that make it hard to track a value, and doesn't tell you anything about how efficient the optimized code will be. -
Peter Cordes almost 7 years@touchStone: GAS
.intel_syntax
is not compatible with NASM. It's more like MASM (e.g.mov eax, symbol
is a load, unlike in NASM where it's amov r32, imm32
of the address), but not totally compatible with MASM either. I do highly recommend it as a nice format to read, especially if you like to write in NASM syntax though.objdump -drwC -Mintel | less
orgcc foo.c -O1 -fverbose-asm -masm=intel -S -o- | less
are useful. (See also How to remove “noise” from GCC/clang assembly output?).-masm=intel
works with clang, too. -
Basile Starynkevitch almost 7 yearsBetter use
gcc -O -fverbose-asm -S
-
Ruslan over 6 yearsStill, this script is a dirty hack which doesn't fully convert the syntax. E.g.
mov eax,ds:0x804b794
is not very NASMish. Also, sometimes it just strips useful information:movzx eax,[edx+0x1]
leaves the reader to guess whether the memory operand wasbyte
orword
. -
Peter Cordes over 6 yearsTo disassemble in NASM syntax in the first place, use Agner Fog's
objconv
. You can get it to disassemble to stdout with output file =/dev/stdout
, so you can pipe intoless
for viewing. There's alsondisasm
, but it only disassembles flat binaries, and doesn't know about object files (ELF / PE). -
Peter Cordes over 6 yearsUse at least
-O2
, or whatever optimization options you actually use when building your project, if you want to see how gcc optimizes your code. (Or if you use LTO, like you should, then you have to disassemble the linker output to see what you really get.) -
Peter Cordes over 6 yearsTo display in the terminal, use
gcc foo.c -masm=intel -fverbose-asm -O3 -S -o- |less
.-S
on its own createsfoo.s
. -
Peter Cordes over 2 yearsYou can also use
gcc -O2 -save-temps foo.c
to compile+assemble+link, but save the intermediate.s
and.o
files, instead of separately running a build that only compiles to asm. (But also a.i
preprocessed C file). So it's fewer steps, but produces files you don't want. -
yugr over 2 years@PeterCordes there is an easier way, see this question