Pregunta

Ok so I am writting an assembler for an OS I am developing. It is coming along great I have about all mov instructions and now I want to implement instructions like call and jmp. I really do not have good documentation so I am looking at machine code generated by NASM to find out opcodes and such. I wanted to see what the opcode for call was so I compiled some code that started with a label at the begging. I expected the address after the call opcode to be 00 00 00 00 but it was FB FF FF FF. I thought it had to do with the symbols so I compiled code with call 0x000000 to see what happened and the address was exactly the same (0xFBFFFFFF). Can someone explain this to me I am confused.

¿Fue útil?

Solución

Showing the actual code you are disassembling would be useful. Most likely that number is a little-endian negative offset. 0xFFFFFFFB = -5 in 2s complement. Did you write:

Label: call Label

If call is a 1-byte opcode with a 4-byte relative offset that would make sense.

Otros consejos

The most common form of CALL in 32-bit user mode x86 code is CALL rel32, which "calls" into a point at the operand plus the address of the next instruction. This is a near relative call.

For reference, it would be possible to use an absolute call, but the encoding is 6 bytes instead of 5; the encoding would be FF 14 XX XX XX XX. The extra byte (14) tells the instruction to read an immediate displacement which will be used as an immediate. However, this would need to be rewritten depending on the module base at which your program is loaded; a relative call does not need attention when relocating.

To visualize how this works, as the instruction is executed, this happens:

  • EIP (the instruction pointer) is incremented to point to the next instruction,
  • That address is pushed onto the stack (to provide a return address),
  • The immediate (eg. the rel32 value) is added to EIP, and
  • The next instruction is read from (the new) [EIP] as normal.

When this instruction is encoded, it looks like this: E8 XX XX XX XX. From this, you can see that the length of the instruction is 5 bytes.

Because EIP is incremented by the length of the instruction, the call will be relative to a point 5 bytes after the start of the instruction. So, if it happens that the relative address of your CALL 0x00000000 instruction is 0x00000000, it will be necessary to subtract 5 from EIP; your assembler has converted the absolute address to a relative one.

The offset can be negative. Also, recall that x86 addresses are little-endian. Thus, the instruction is E8 FB FF FF FF.

The consequence of this particular instruction, interestingly, is that EIP+5 will continually be pushed onto the stack until an exception (#SS(0)) is generated.

call often comes in various forms, e.g. one that uses an absolute address to jump to, another that jumps relative to the current one. This could be a relative one, although the four bytes probably are not the offset directly.

If in doubt, especially when implementing an assembler, you may want to take a look at the manual or datasheet.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top