Home > Software engineering >  ModRM: Can't use direct address, assembly x86
ModRM: Can't use direct address, assembly x86

Time:11-04

I can't understand why I receive an Illegal Instruction with a piece of code implemented as assembly inside C program. I explain a bit.

I have a value at a specific address and I want to move that value into EAX x86 register, something like

MOV EAX, [address]

In the C code I have the pointer for that address. I have prepared the binary code of this instruction in this way:

MOV OpCode:

 7 6 5 4 3 2 1 0
|_|_|_|_|_|_|D|S|

D = 1 --> I want REG as destination
S = 1 --> 32 bit operand

                 D S
--> |1|0|0|0|1|0|1|1| --> 0x89


Mod R/M:

 7 6 5 4 3 2 1 0
|_|_|_|_|_|_|_|_|

--> MOD = |7|6|    bits
--> REG = |5|4|3|  bits
--> R/M = |2|1|0|  bits

MOD = Indirect  --> |0|0|
REG = EAX = 0   --> |0|0|0|

Address is not in a register and I want to move memory data to EAX register. I have the value of the memory location, so:

R/M = |1|1|0| --> direct

--> |0|0|0|0|0|1|1|0| --> 0x06

Address = [byte0] [byte1] [byte2] [byte3]

--> 0x8b 0x06 [byte3] [byte2] [byte1] [byte0] 0xc3

In this way I obtain an illegal instruction but I don't understand why. If I use R/M = 101 I obtain 0x8b 0x05 [byte3] [byte2] [byte1] [byte0] 0xc3 and all work fine.

So, where is the problem? Is there someone that could explain the MOD=00, R/M=110 usage from this table? direct???

enter image description here

MOD=00, R/M=101 works but in the table I see memory location calculated from DI but I have a direct memory location...

Note: Code compiled on Ubuntu 20.04 (64 bit) with -m32 gcc switch

CodePudding user response:

The key problem is that you are looking at a 16 bit modr/m table while assembling for 32 bit mode. Each operating mode has its own addressing mode and the modr/m and SIB bytes differ in how memory operands are encoded.

As for 32 bit mode, direct addressing is selected by mod = 00, r/m = 101. So the correct modr/m byte is:

 00 000 101 XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX
mod reg r/m ---------- displacement -----------

Where reg = 000 (encoding eax) and mod = 00, r/m = 101 encoding a direct address. This is the 05 modr/m byte you already observed to be working.

In 16 bit mode, direct addressing is instead selected by mod = 00, r/m = 110 with a 2 byte displacement, giving a modr/m byte of 06.

Note that in 16 and 32 bit operating modes, you can switch between a 16 and a 32 bit modr/m byte by supplying a 67 address size override prefix.

As an alternative option for your particular use case, you can also use the A1 opcode for mov eax, off32. This opcode directly takes a 32 bit address to load from with no modr/m byte, but both the addressing mode and the destination register are hard coded.

CodePudding user response:

Ok, I worked with the wrong table. I found the correct table from Intel website. Thanks for the rapid answers

enter image description here

  • Related