How to Disassemble Compiled C-Programs for x86 and RISC-V Architectures
To disassemble compiled programs/executables there are several great open source tools available. Executables are created for a specific combination of processor architecture and operating system. This piece provides an overview on how to analyze executables targeted at the x86 and RISC-V architectures on Linux utilizing gcc
, objdump
and readelf
. These tools should come preinstalled on most Linux distributions. It is assumed that the readers host system uses the x86 (AMD, Intel) architecture.
Compilation Process overview
During the compilation process can be distinguished in multiple steps, after each a temporary file is created.
These are Preprocessing, Compilation, Assemble and Linking, see image:

In the first step, the preprocessor removes comments, expands macro definitions and inserts headers as defined via #include
.
During compilation the compiler implements the operations defined in the source code using assembly instructions. Assembly instructions are defined in the instruction set (x86, RISC-V, ARM). It outputs the assembly file.
The assembler translates the assembly instructions into machine code, i.e. their binary representation and outputs an object file. Each source code file results in an object file.
Finally the linker connects all necessary objects files together into a single executable file.
On Linux object files and executable use the .elf
-Format, which stands for executable and linkable file.
General switches for gcc
-o
name of output file
-g
insert debugging information, to be used for example by GNU debugger gdb
-O
applied optimizations during compilation (default: O0
no optimizaitons, O1
to O3
increase the number of optimizations performed, further explanations see Gentoo Wiki )
x86
Your PC proably contains a processor from Intel or AMD using the x86 ISA. By default gcc will produce an executable using the same processor architecture, i.e. x86.
x86 assembly comes in two syntaxes: att
and intel
. Most Linux tools use by default the att
syntax.
I prefer the intel
syntax as it follows the same assembly operand conventions as ARM or RISC-V:
<instr> <destination> <src1> <src2>
Also it uses less special symbols.
My examples will include switches to change the output to intel syntax.
Simply produce executable using gcc
Preprocessing -> Compilation -> Assemble -> Linking = Executable
gcc -o example example.c
The executable can be inspected with readelf
. Show all information with -a
. View other options by entering just readelf
.
readelf -a example
Compile and save temporary files .i
, .s
,.o
gcc -g --save-temps -masm=intel -o example example.c
--save-temps
Save intermediate files (preprocessed .i
, assembled .s
, compiled.o
to disk):
Create List File .lst
A list file interleaves source and the corresponding assembly making it easy to compare the two.
gcc -g -Wa,-adhln -masm=intel example.c > example.lst
Disassemble object files .o
/ machine code
Create object file from source. Preprocessing -> Compilation -> Assemble = Object Files (contain machine code)
gcc -c example.c -o example.o
Create Listing file (interleave source code and corresponding assembly). Object file needs to contain debug symbols.
objdump -S --disassembler-options=intel test.o
Store disassembly (Interpret Machine Code as Assembly) in file example.asm
:
objdump -d -M intel test.o > example.asm
Works also on final executable program.
RISC-V
The RISC-V architecture defines a different instruction set and memory model that is incompatible with x86. For this reason a separate version of these tool was created. It can be downloaded and build from https://github.com/riscv-collab/riscv-gnu-toolchain.
This version of gcc creates executables targeting processors implementing the RISC-V architecture. They won't run on a x86 (AMD or Intel) computer. But we can inspect them never the less.
The functionality provided is similar:
Compile:
riscv64-unknown-linux-gnu-gcc -o example example.c
Compile, save intermediates for debugging:
riscv64-unknown-linux-gnu-gcc --save-temps -g -o example example.c
Create Listing File:
riscv64-unknown-linux-gnu-objdump -S example.o > example.lst
Disassemble:
riscv64-unknown-linux-gnu-objdump -d example.o > example.asm
Show gcc Include Paths
gcc -v -E -x c -
Src: https://stackoverflow.com/questions/65421161/visual-studio-code-cannot-open-source-file-iostream