How an Executable File Operates
1. Assembly
- The machine language understood by the CPU is binary code. Assembly expresses machine language in human-readable mnemonic instructions.
- Therefore, the programming language most similar to machine code is Assembly.
- To understand how machine code is executed, we will examine a simple Assembly program.
; Ubuntu 24.04 x86-64 (NASM, ELF64)
section .data
msg db "result: "
msg_len equ $ - msg
newline db 0x0a
section .bss
buf resb 1
section .text
global _start
_start:
mov al, 2
add al, 3
mov bl, al
mov rax, 1
mov rdi, 1
mov rsi, msg
mov rdx, msg_len
syscall
mov al, bl
add al, '0'
mov [buf], al
mov rax, 1
mov rdi, 1
mov rsi, buf
mov rdx, 1
syscall
mov rax, 1
mov rdi, 1
mov rsi, newline
mov rdx, 1
syscall
mov rax, 60
xor rdi, rdi
syscall
add.asm
; ============================================================
; Ubuntu 24.04 x86-64 (NASM, ELF64)
; ============================================================
section .data ; Initialized data
msg db "result: "
msg_len equ $ - msg
newline db 0x0a
section .bss ; Uninitialized data
buf resb 1 ; buffer for converting a single-digit number to ASCII
section .text ; Executable code section
global _start
_start:
;----------------------------------------------------------
; [1] Calculate 2 + 3 (use 8-bit AL)
;----------------------------------------------------------
mov al, 2 ; al = 2
add al, 3 ; al = 2 + 3 = 5
mov bl, al ; bl = 5 (preserve in BL because syscalls may overwrite RAX)
;----------------------------------------------------------
; [2] Print "result: " string (sys_write)
;----------------------------------------------------------
mov rax, 1 ; syscall: sys_write (rax, rcx, r11 may be clobbered)
mov rdi, 1 ; fd: stdout
mov rsi, msg
mov rdx, msg_len
syscall
;----------------------------------------------------------
; [3] Convert result to ASCII and print
;----------------------------------------------------------
mov al, bl ; al = 5 (restore result from BL)
add al, '0' ; 5 + 48 = 53 '5'
mov [buf], al ; store ASCII character in buf
mov rax, 1 ; syscall: sys_write
mov rdi, 1
mov rsi, buf
mov rdx, 1
syscall
;----------------------------------------------------------
; [4] Print newline
;----------------------------------------------------------
mov rax, 1
mov rdi, 1
mov rsi, newline
mov rdx, 1
syscall
;----------------------------------------------------------
; [5] Exit program (sys_exit, code=0)
;----------------------------------------------------------
mov rax, 60 ; syscall: sys_exit
xor rdi, rdi
syscall
1.1. Build
- To create an executable file from this Assembly program, the following process is required.
$ nasm -f elf64 -o add.o add.asm
$ ld -o add add.o
$ ./add
result: 5
1.2. Executable File
- Executable files running in a Linux (Ubuntu 24.04) environment use the ELF64 (Executable and Linkable Format 64-bit) format.
$ objdump -h add
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000006c 0000000000401000 0000000000401000 00001000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000009 0000000000402000 0000000000402000 00002000 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000004 000000000040200c 000000000040200c 00002009 2**2
ALLOC
$ objdump -s -j .text add
Contents of section .text:
401000 b0020403 88c3b801 000000bf 01000000 ................
401010 48be0020 40000000 0000ba08 0000000f H.. @...........
401020 0588d804 30880425 0c204000 b8010000 ....0..%. @.....
401030 00bf0100 000048be 0c204000 00000000 ......H.. @.....
401040 ba010000 000f05b8 01000000 bf010000 ................
401050 0048be08 20400000 000000ba 01000000 .H.. @..........
401060 0f05b83c 00000048 31ff0f05 ...<...H1...
$ objdump -s -j .data add
Contents of section .data:
402000 72657375 6c743a20 0a result: .
$ objdump -s -j .bss add
Contents of section .bss:
40200c 00000000
1.3. Executable Code
- The code section (.text) is typically placed at 0x401000, which is the 4KB page boundary (0x1000 = 4096) following the header.
- This is the default layout in x86-64 Linux. The address may vary depending on compiler options or security features such as ASLR.
$ objdump -d -M intel add
Disassembly of section .text:
0000000000401000 <_start>:
401000: b0 02 mov al,0x2
401002: 04 03 add al,0x3
401004: 88 c3 mov bl,al
401006: b8 01 00 00 00 mov eax,0x1
40100b: bf 01 00 00 00 mov edi,0x1
401010: 48 be 00 20 40 00 00 movabs rsi,0x402000
401017: 00 00 00
40101a: ba 08 00 00 00 mov edx,0x8
40101f: 0f 05 syscall
401021: 88 d8 mov al,bl
401023: 04 30 add al,0x30
401025: 88 04 25 0c 20 40 00 mov BYTE PTR ds:0x40200c,al
40102c: b8 01 00 00 00 mov eax,0x1
401031: bf 01 00 00 00 mov edi,0x1
401036: 48 be 0c 20 40 00 00 movabs rsi,0x40200c
40103d: 00 00 00
401040: ba 01 00 00 00 mov edx,0x1
401045: 0f 05 syscall
401047: b8 01 00 00 00 mov eax,0x1
40104c: bf 01 00 00 00 mov edi,0x1
401051: 48 be 08 20 40 00 00 movabs rsi,0x402008
401058: 00 00 00
40105b: ba 01 00 00 00 mov edx,0x1
401060: 0f 05 syscall
401062: b8 3c 00 00 00 mov eax,0x3c
401067: 48 31 ff xor rdi,rdi
40106a: 0f 05 syscall
The binary code at this address b0020403 88C3... is interpreted as follows.
b0 02 mov al,0x2
04 03 add al,0x3
88 C3 mov bl,al
...
This shows the result of interpreting the instruction code executed by the CPU into Assembly.
2. Execute
2.1. CPU Registers
- CPU registers are ultra-fast storage spaces directly used by the CPU to perform operations, and all core program execution processes are based on registers.
2.2. OP Code to CPU Resisters
- The CPU fetches instructions from memory (Fetch → Decode) and interprets them into μOps.
- After performing the operation (Execute → Memory), the result is written back to registers (WriteBack).
- Then RIP is incremented to the next instruction, and this process repeats.
- RIP (Register Instruction Pointer)
- A register in x86-64 CPUs that points to the address of the next instruction to execute
2.3. Execution Process
- During the build stage, source code is converted into machine code, creating an ELF executable file.
- The OS kernel loads the ELF via execve, places segments into virtual memory, and sets RIP to the entry point to begin execution.
- The CPU then repeatedly executes instructions through the IF → ID → EX → MEM → WB pipeline.