Author Topic: call/jump with relative offset  (Read 23175 times)

nobody

  • Guest
call/jump with relative offset
« on: October 09, 2008, 12:34:50 PM »
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.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: call/jump with relative offset
« Reply #1 on: October 09, 2008, 05:46:54 PM »
Sorry you didn't get a response from nasm-devel. Sometimes my messages don't get through, either. Worth every penny we pay for it! :)

AFAIK, Nasm's behaviour *is* intentional - and correct. Re-think your expectations with the idea in mind that a label *is* an immediate (as opposed to "call [label]", "call reg", or "call [reg]"), and I think you'll agree that Nasm's doing it right.

Best,
Frank

nobody

  • Guest
Re: call/jump with relative offset
« Reply #2 on: October 13, 2008, 01:07:59 PM »
Thanks for the response.

Yeah, intuitively I see what you're getting at, if you think of a label or an immediate value as an absolute offset from the beginning of the section.  I'm not sure I would call it 'right' (or wrong), but certainly more useful from the assembly programmer's perspective :)

Just want to understand though, it's still not clear to me how I would know to implement things this way by reading the (AMD64/Intel) architecture manuals.  The language I see (for example, for the Jcc instructions), strictly refers to the operand as 'a signed offset relative to the current value of the instruction pointer in the EIP register' (Intel vol 2A pg 3-541). What am I missing?

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: call/jump with relative offset
« Reply #3 on: October 13, 2008, 02:48:36 PM »
I'm not sure you're actually "missing" anything. It's a confusing issue! The "operand" in the machine code is not the same as the "operand" we write in the source code (or see in the disassembly). AFAIK, all(?) assemblers do it that way. More convenient for the programmer for the assembler to do this calculation "behind our back" than to force the user to calculate (and recalculate when the code changes) the "distance to target". We don't really need to know this, unless we're wrting an assembler/compiler/other tool... Damned if I  know where you're supposed to read this!

Best,
Frank