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???
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