Why does `call dword 0x12345678` compile to [66,E8,72,56,34,12]?

assembly, nasm, x86

Solution

You are calling an absolute address with this instruction, although it may not seem so obvious at the first glance. Let's go through the assembly process step by step.

Firstly, you can see that your instruction has been assembled to `66 E8`. `66` is the operand size prefix - I'll explain why it got there later. `E8` points to the `CALL` instruction, which is expected, and the following bytes - `72563412` - are the little-endian representation of the 32-bit displacement relative to the instruction after the `CALL` instruction. This is actually how all relative jumps and calls are encoded in x86 : because the CPU always knows the current value of the instruction pointer, it can just add the value specified by the instruction to that value and execute a call/jump.

Secondly, there is a question why the assembler chose to assemble the instruction this way. If you are actually running `nasm` without any flags, then it defaults to the `bin` output mode, which just outputs raw opcodes into the file you specify. Additionally, it defaults to assembling instructions in 16-bit mode in that output mode. This is why the `66` prefix is there : it was possible to execute instructions with 32-bit operands in 16-bit mode, provided that this prefix was present in front of the opcode. Also, `nasm` in `bin` output mode assumes that the resulting binary begins at address `0` : this is why the "offset" of your call is `72563412` - if the IP starts at 0, then it will be 6 after processing the `CALL`, and `0x6 + 0x12345672` gives you `0x12345678`, which is the address you wanted to call. You can change the offset at which the program is loaded via the `ORG` (origin) directive.

If you don't want to use jumps/calls with relative addressing, then you will have to put the address of the code which you want to jump to/call in a register, and then execute the instruction with the register as an operand. Something like this :

mov eax, somecode
jmp eax

somecode:
    int 3

Assembles to the following :

00000000  66B809000000      mov eax,0x9
00000006  66FFE0            jmp eax
00000009  CD03              int 0x3

You can see that the absolute address of the jump is moved directly into the register. However, if you're simply executing jumps/calls to labels, I can't think of any reasons why you wouldn't use relative addressing. The CPU always knows its current IP - and your assembler should, too.

Problem

``` $ cat call.s call dword 0x12345678 $ nasm call.s $ ndisasm call 00000000 66E872563412 call dword 0x12345678 ``` Why is the result [66,E8,72,56,34,12] not [66,E8,78,56,34,12]? Why did `0x78` change into `0x72`?

Original source