What is the difference between the encodings for the call instruction in x86 asm?
Asked Answered
L

1

0

For reference: an HTML extract of Intel's documentation for the call instruction.


I am aware that section 3.1.1.3 explains this but I am having trouble understanding the manual, probably because it is too technical.

  1. What is the /2 and /3 inside FF /2 and FF /3?

  2. What is the difference between r/m32 and m16:32? r/m32 covers both 32-bit registers and memory operands so wouldn't that make m16:32 redundant?

  3. According to the manual call ptr16:32 is a "Call far, absolute, address given in operand". If I understood correctly, this will allow me to call a function at a given absolute 32-bit address. How can I call the function at the absolute address 0x717A60 (I'm using the inline assembler in MSVC)? call 0x717A60 gives me an error and call ds:0x717A60 is assembled to call [0x717A60].

  4. Which opcode does the following machine code correspond to? FF 15 90 98 76 70

Lueluebke answered 7/8, 2016 at 2:50 Comment(5)
@rcgldr: It doesn't use a segment register, it's a far call that loads CS. You don't want that.Ebby
How is C++ involved in this?Oversexed
@DavidHoelzer: There's a question about generating a call to an absolute address using msvc inline asm buried as part of item 3. I didn't notice that until I was mostly done writing an answer to the obvious parts of the question. This is a great example of why SO has a one question per Question-post guideline.Ebby
Near duplicate of x86 function call types, but I'm not sure which if either should be closed as a duplicate of the other.Ebby
the first part is a duplicate of What does the /4 mean in FF /4?Tierratiersten
E
3
  1. The /2 vs. /3: See Section 3.1.1.1O Opcode Column in the Instruction Summary Table:

/digit — A digit between 0 and 7 indicates that the ModR/M byte of the instruction uses only the r/m (register or memory) operand. The reg field contains the digit that provides an extension to the instruction's opcode.

Using the /r field of the mod/rm byte of one-operand instructions is the same here as for instructions like inc r/m32 (FF /0).

x86 overloads a few opcode bytes with multiple one-operand instructions this way. The 3-bit /r field becomes another 3 opcode bits instead of an operand. More detail about that:


r/m32 vs. m16:32 vs. ptr16:32

See also x86 function call types

A "far" call loads the CS segment register as well as IP/EIP/RIP. A normal "near" call only needs a 32-bit (or 64-bit) address, and doesn't modify CS.

far call is never used in "normal" 32 or 64-bit user-space code on normal OSes, because they all use a flat memory model.

ptr16:32 is an immediate, with the 16-bit segment value and the 32-bit absolute address encoded into the instruction (little-endian, with the 32-bit offset first then the new CS value). This is a far call. See this Q&A. Assemblers will generate this instruction encoding for far call some_symbol, taking the segment value from the segment some_symbol is defined in. Again, you're really unlikely to ever use this outside of 16-bit code. But if you do, see this Q&A for how to get MASM to emit it.

m16:32 is a 6-byte memory operand, loaded from the effective address encoded by the ModR/M byte. This is another far call. So call far [eax] does a 48-bit load from the address in eax. Only memory addressing modes are legal for the ModR/M byte, because the instruction needs more data than the width of a register. (i.e. call far eax isn't legal)

r/m32 is a memory or register operand for a near call. You get this from call eax or call [eax]. Of course any addressing mode is legal, e.g. call FS:[edi + esi*4 + some_table]. The FS segment-override prefix applies to the location the function pointer is loaded from, not the segment that it jumps to. (i.e. it doesn't change CS because it's still a near call.)


How can I call the function at the absolute address 0x717A60

See Call an absolute pointer in x86 machine code.

If your code doesn't have to be position-independent, by far the best choice is something that assembles to call rel32 with the right relative displacement to reach that address. In NASM and AT&T syntax, you can simply write call 0x717A60 and the assembler + linker take care of it. I'm not sure how to write it in MASM; MSVC inline asm doesn't accept call 123456h.

There is no absolute direct near call encoding, so if you do need PIC then you should do something like mov eax, 0x717A60 / call eax.

You don't want to use a call far absolute direct call, because it's probably much slower, and pushes CS:EIP instead of just EIP as the return address. You'd also have to know what to put in the segment field.


FF 15 90 98 76 70

That disassembles to call DWORD PTR ds:0x70769890 (in GNU objdump -Mintel syntax), which is a call r/m32, where the operand is a [disp32] absolute addressing mode.

(Or in 64-bit code, it's a RIP-relative addressing mode with that rel32, but still a memory-indirect call that loads a function-pointer from a static location.)


See also the tag wiki for more links to docs.

Ebby answered 7/8, 2016 at 3:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.