I sent this as an email to nasm-devel and got no response, so I thought I'd try here.
I've been sort-of building an x86_64 assembler as part of a runtime code generation project in python, and recently built up a test suite to compare output from my system against NASM.
I'm confused by the machine code NASM is generating for call and jump instructions that use immediate values as an offset (as opposed to a label or register). Consider this example I've been working with:
main:
xor rbx,rbx
call call_lbl
jmp exit_lbl
call_lbl:
mov rbx,15
ret
exit_lbl:
mov rax,1
int 0x80
As expected the code returns 15 if you run it. The call instruction, if you look at the machine code, has an offset of 5 (0xE805000000), no surprise there. Now my understanding then is that I could replace 'call call_lbl' with 'call 0x5' and the resulting machine code will be the same.
However this is not the case -- the machine code generated for 'call 0x5' is 0xE8FDFFFFFF, or an offset of -2. Which of course makes no sense. Apparently this value is the difference between absolute location 5 and the byte following the call instruction. In other words 'call 0x5' is interpreted to mean I want to call/jump to 5 bytes after the start of the section.
Is this intentional? Why is it done this way? From the AMD64 manual this is not what I would expect -- instead I'd expect to see the value 5 as the relative offset in the machine code.