咨询x86平台下直接与间接call指令的内存布局
Hey there! Comparing direct vs indirect call instructions on x86 is a fantastic way to dig into how x86 handles control flow at the machine code level. Let's break down their memory layouts and behavior step by step:
Direct call (Relative Call)
The direct call instruction uses a relative offset to calculate the target address. Here's its memory layout in 32-bit x86:
- 1-byte opcode:
0xE8 - 4-byte signed relative offset: This value is added to the address of the next instruction (right after the
callopcode + offset) to get the target function's address.
Example
Suppose we have this assembly:
call my_function ; Next instruction here: 0x08048400
If my_function is at 0x080483F0, the relative offset would be 0x080483F0 - 0x08048400 = -0x10 (which is 0xFFFFFFF0 in two's complement for 32 bits). The memory layout of the call instruction would be:
Address: 0x080483FC 0x080483FD 0x080483FE 0x080483FF 0x08048400 Bytes: 0xE8 0xF0 0xFF 0xFF 0xFF
The CPU computes the target by taking 0x08048400 (next instruction address) + 0xFFFFFFF0 (signed offset) = 0x080483F0, which is the address of my_function.
Indirect call (Absolute/Register/Memory Call)
Indirect call instructions don't use a relative offset—instead, they fetch the target address from a register or a memory location. There are a few common variants in 32-bit x86:
1. Register Indirect call
This uses a general-purpose register to hold the target address. The layout is:
- 2-byte opcode sequence:
0xFFfollowed by a ModR/M byte that specifies the target register.
Example
Assembly:
call eax
The machine code is 0xFF 0xD0 (the ModR/M byte 0xD0 encodes "call eax"). The memory layout is just two bytes:
Address: 0x08048400 0x08048401 Bytes: 0xFF 0xD0
The CPU jumps directly to the address stored in eax.
2. Memory Indirect call
This fetches the target address from a specified memory location. The layout is:
- 2-byte opcode sequence:
0xFFfollowed by a ModR/M byte that specifies the memory operand. - 4-byte memory address (if using a direct absolute memory operand, not a register-based addressing mode).
Example
Assembly:
call [0x08049000]
The machine code is 0xFF 0x15 0x00 0x90 0x04 0x08 (the ModR/M byte 0x15 encodes "call [absolute address]"). The memory layout is six bytes:
Address: 0x08048400 0x08048401 0x08048402 0x08048403 0x08048404 0x08048405 Bytes: 0xFF 0x15 0x00 0x90 0x04 0x08
The CPU reads the 4-byte address stored at 0x08049000 and jumps to that location.
Key Differences in Layout & Behavior
- Direct
call: Compact (5 bytes in 32-bit), uses relative offset, which makes it position-independent (ideal for shared libraries or PIC code). The target is fixed at assembly time unless you patch the offset. - Indirect
call: Variable length (2 bytes for register targets, 6+ bytes for direct memory targets), uses an absolute address sourced from a register or memory. This enables dynamic target selection—perfect for function pointers, virtual method calls, or any scenario where the target address isn't known until runtime.
内容的提问来源于stack exchange,提问作者JiaHao Xu

