2

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

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Francesco
  • 523
  • 4
  • 25
  • 3
    This is the modr/m byte for 16 bit mode. You need to use a 32 bit modr/m byte for 32 bit mode. Alternatively, use the `A1` opcode to avoid a modr/m byte. – fuz Nov 03 '21 at 12:14
  • @fuz thanks, but the problem is still there. The 32 bit table indicates 0x05 for Mod 00 and R/M 101 and reports [DI]. Why [DI]? – Francesco Nov 03 '21 at 12:19
  • 1
    Look at disassembly for NASM `mov eax, [foo]` / `ret`. (Or actually GAS .intel_syntax noprefix will assembler that as well). (except as fuz noted, assemblers will optimize that to the no-modrm encoding with an EAX destination). Your code should be using the same modrm encoding as that, with a 32-bit (4 byte) absolute address as the `disp32`. As fuz said, you're looking at the 16-bit table. It's totally irrelevant for you, although IIRC the 16-bit mode encoding for `[disp16]` (shown as *direct* in that table) is the same as the 32-bit encoding for `[disp32]`. – Peter Cordes Nov 03 '21 at 12:19
  • The 32-bit table doesn't include any entry for `[di]`, that's a 16-bit register so can't be part of a 32-bit addressing mode. Sounds like you forgot to change your code back to R/M = 6 instead of 5 since you don't want to use a register at all. – Peter Cordes Nov 03 '21 at 12:20
  • @PeterCordes thanks. Could you please send me the table? Some link and/or page? Maybe I'm reading the wrong table – Francesco Nov 03 '21 at 12:22
  • 1
    @Francesco The fields are the same in a 32 bit modr/m byte, but they have different meanings. You can find a table [on this site](http://ref.x86asm.net/geek32.html#modrm_byte_32). – fuz Nov 03 '21 at 12:23
  • 1
    There are links in https://stackoverflow.com/tags/x86/info to Intel's official manuals. Or https://wiki.osdev.org/X86-64_Instruction_Encoding#32.2F64-bit_addressing has a table. – Peter Cordes Nov 03 '21 at 12:23
  • Many thanks to both, very dumb error – Francesco Nov 03 '21 at 12:28

2 Answers2

3

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.

fuz
  • 88,405
  • 25
  • 200
  • 352
0

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

enter image description here

Francesco
  • 523
  • 4
  • 25