Why can't you set the instruction pointer directly?

前端 未结 4 1717
陌清茗
陌清茗 2020-11-27 06:09

The Wikipedia article about x86 assembly says that \"the IP register cannot be accessed by the programmer directly.\"

Directly means with instructions like mov and a

相关标签:
4条回答
  • 2020-11-27 06:22

    You can't access it directly because there's no legitimate use case. Having any arbitrary instruction change eip would make branch prediction very difficult, and would probably open up a whole host of security issues.

    You can edit eip using jmp, call or ret. You just can't directly read from or write to eip using normal operations

    Setting eip to a register is as simple as jmp eax. You can also do push eax; ret, which pushes the value of eax to the stack and then returns (i.e. pops and jumps). The third option is call eax which does a call to the address in eax.

    Reading can be done like this:

    call get_eip
      get_eip:
    pop eax ; eax now contains the address of this instruction
    
    0 讨论(0)
  • 2020-11-27 06:27

    I think they meant that the IP register cannot be accessed directly in the same way the other registers are accessed. Programmers can definitely write to IP, for example by issuing a jump instruction.

    0 讨论(0)
  • 2020-11-27 06:30

    That would have been a possible design for x86. ARM does expose its program counter for read/write as R15. That's unusual, though.

    It allows a very compact function prologue/epilogue, along with the ability to push or pop multiple registers with a single instruction: push {r5, lr} on entry, and pop {r5, pc} to return. (Popping the saved value of the link register into the program counter).

    However, it makes high-perf / out-of-order ARM implementations less convenient, and was dropped for AArch64.


    So it's possible, but uses up one of the registers. 32-bit ARM has 16 integer registers (including PC), so a register number takes 4 bits to encode in ARM machine code. Another register is almost always tied up as the stack pointer, so ARM has 14 general-purpose integer registers. (LR can be saved to the stack, so it can be and is used as a general-purpose register inside function bodies).

    Most of modern x86 is inherited from 8086. It was designed with fairly compact variable-length instruction encoding, and only 8 registers, requiring only 3 bits for each src and dst register in the machine code.

    In the original 8086, they were not very general-purpose, and SP-relative addressing isn't possible in 16-bit mode, so essentially 2 registers (SP and BP) are tied up for stack stuff. This leaves only 6 somewhat-general purpose registers, and having one of them be the PC instead of general-purpose would be a huge reduction in available registers, greatly increasing the amount of spill/reload in typical code.


    AMD64 added r8-r15, and the RIP-relative addressing mode. lea rsi, [rip+whatever], and RIP-relative addressing modes for direct access to static data and constants, is all you need for efficient position-independent code. Indirect JMP instructions are totally sufficient for writing to RIP.

    There isn't really anything to be gained by allowing arbitrary instructions to be used to read or write the PC, since you can always do the same thing with an integer register and an indirect jump. It would be almost pure downside for x86-64's R15 to be the same thing as RIP, especially for the architecture's performace as a compiler target. (Hand-written asm weird stuff was already very much an uncommon niche thing by 2000, when AMD64 was designed.)

    So AMD64 is really the first time that x86 could plausibly have gained a fully-exposed program counter like ARM, but there were many good reasons not to do that.

    0 讨论(0)
  • 2020-11-27 06:43

    jmp will set the EIP register.

    this code will set eip to 00401000:

    mov eax, 00401000
    jmp eax ;set Eip to 00401000
    

    and for getting EIP

    call GetEIP
    .
    .
    GetEIP:
    mov eax, [esp]
    ret
    
    0 讨论(0)
提交回复
热议问题