// ThePond is an x86-64 machine (gdb) [sergey@thepond cs59]$ cat hello-loop.c #include int main() { int i = 10; while( i >= 0 ){ i = i - 1; puts( "Hello" ); } return 42; } [sergey@thepond cs59]$ gcc -o hello hello-loop.c [sergey@thepond cs59]$ gdb ./hello GNU gdb (GDB) 17.2 Copyright (C) 2025 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later [skipped] (No debugging symbols found in ./hello) (gdb) disas main Dump of assembler code for function main: 0x0000000000001139 <+0>: push %rbp 0x000000000000113a <+1>: mov %rsp,%rbp 0x000000000000113d <+4>: sub $0x10,%rsp 0x0000000000001141 <+8>: movl $0xa,-0x4(%rbp) 0x0000000000001148 <+15>: jmp 0x115d 0x000000000000114a <+17>: subl $0x1,-0x4(%rbp) 0x000000000000114e <+21>: lea 0xeaf(%rip),%rax # 0x2004 0x0000000000001155 <+28>: mov %rax,%rdi 0x0000000000001158 <+31>: call 0x1030 0x000000000000115d <+36>: cmpl $0x0,-0x4(%rbp) 0x0000000000001161 <+40>: jns 0x114a 0x0000000000001163 <+42>: mov $0x2a,%eax 0x0000000000001168 <+47>: leave 0x0000000000001169 <+48>: ret End of assembler dump. // Let's see the actual machine code: (gdb) disas /r main Dump of assembler code for function main: 0x0000000000001139 <+0>: 55 push %rbp 0x000000000000113a <+1>: 48 89 e5 mov %rsp,%rbp 0x000000000000113d <+4>: 48 83 ec 10 sub $0x10,%rsp 0x0000000000001141 <+8>: c7 45 fc 0a 00 00 00 movl $0xa,-0x4(%rbp) // <<---- 0x0000000000001148 <+15>: eb 13 jmp 0x115d 0x000000000000114a <+17>: 83 6d fc 01 subl $0x1,-0x4(%rbp) 0x000000000000114e <+21>: 48 8d 05 af 0e 00 00 lea 0xeaf(%rip),%rax # 0x2004 0x0000000000001155 <+28>: 48 89 c7 mov %rax,%rdi 0x0000000000001158 <+31>: e8 d3 fe ff ff call 0x1030 0x000000000000115d <+36>: 83 7d fc 00 cmpl $0x0,-0x4(%rbp) 0x0000000000001161 <+40>: 79 e7 jns 0x114a 0x0000000000001163 <+42>: b8 2a 00 00 00 mov $0x2a,%eax 0x0000000000001168 <+47>: c9 leave 0x0000000000001169 <+48>: c3 ret End of assembler dump. // Note that the instructions are of different lengths, some one byte, others include // 4-byte values inside them (called an "immediate"), e.g., movl at offset main+8 // You can easily disassemble machine code with AI chat bots: ---- begin Claude quote ---- disassemble x86-64 instruction "c7 45 fc 0a 00 00 00" ## Decoding: `C7 45 FC 0A 00 00 00` ### Byte-by-byte breakdown | Byte(s) | Value | Meaning | |---|---|---| | `C7` | opcode | **MOV r/m32, imm32** (move immediate 32-bit value to register/memory) | | `45` | ModRM | see breakdown below | | `FC` | disp8 | displacement byte = **-4** (0xFC as signed byte) | | `0A 00 00 00` | imm32 | immediate value, little-endian = **0x0000000A = 10** | ### ModRM byte breakdown — `0x45` = `01 000 101` | Field | Bits | Value | Meaning | |---|---|---|---| | `mod` | 01 | `01` | 8-bit displacement follows | | `reg` | 000 | `000` | not a register select here — opcode `C7` uses `/0` as an extension field, so this must be `000` | | `rm` | 101 | `101` | with `mod=01`, this selects **[RBP + disp8]** addressing | ### Full decoded instruction ```asm MOV DWORD PTR [rbp-0x4], 0xa ``` or equivalently: ```asm MOV DWORD PTR [rbp-4], 10 ``` ### What it does ``` *(int32_t*)(RBP - 4) = 10 ``` Stores the 32-bit immediate value **10** into the memory location at **RBP − 4**. ### Why this pattern is so common This is the classic compiler-generated instruction for something like: ```c int local_var = 10; ``` where `local_var` lives at `[rbp-4]` in the stack frame — a very typical local-variable initialization in unoptimized (`-O0`) x86-64 code using the traditional RBP-based frame layout. ### Immediate locations marked ``` C7 45 FC 0A 00 00 00 │ │ │ └──────────── imm32 (bytes 4-7): the value 10 │ │ └────────────────────── disp8 (byte 3): displacement -4 │ └─────────────────────────── ModRM (byte 2): addressing mode └──────────────────────────────── opcode (byte 1): MOV r/m32, imm32 ``` ---- end Claude quote ---- // The above assembly style is called AT&T assembly. The alternative is Intel. // Note that the target of the memory operation is different between these styles! // (look at mov at main+8 again). // We can change the style: (gdb) set disassembly-flavor intel (gdb) disas /r main Dump of assembler code for function main: 0x0000000000001139 <+0>: 55 push rbp 0x000000000000113a <+1>: 48 89 e5 mov rbp,rsp 0x000000000000113d <+4>: 48 83 ec 10 sub rsp,0x10 0x0000000000001141 <+8>: c7 45 fc 0a 00 00 00 mov DWORD PTR [rbp-0x4],0xa // <<-- 0x0000000000001148 <+15>: eb 13 jmp 0x115d 0x000000000000114a <+17>: 83 6d fc 01 sub DWORD PTR [rbp-0x4],0x1 0x000000000000114e <+21>: 48 8d 05 af 0e 00 00 lea rax,[rip+0xeaf] # 0x2004 0x0000000000001155 <+28>: 48 89 c7 mov rdi,rax 0x0000000000001158 <+31>: e8 d3 fe ff ff call 0x1030 0x000000000000115d <+36>: 83 7d fc 00 cmp DWORD PTR [rbp-0x4],0x0 0x0000000000001161 <+40>: 79 e7 jns 0x114a 0x0000000000001163 <+42>: b8 2a 00 00 00 mov eax,0x2a 0x0000000000001168 <+47>: c9 leave 0x0000000000001169 <+48>: c3 ret End of assembler dump.