register program pointer instruction address x86 cpu-registers program-counter

x86 - program - ¿Por qué no puedes establecer el puntero de instrucción directamente?



program counter pic (4)

Creo que significaron que no se puede acceder directamente al registro IP de la misma manera que se accede a los otros registros. Los programadores definitivamente pueden escribir en IP, por ejemplo, emitiendo una instrucción de salto.

El artículo de Wikipedia sobre el ensamblaje de x86 dice que "el programador no puede acceder directamente al registro de IP".

Directamente significa con instrucciones como mover y agregar.

Por qué no? Cuál es la razón detrás de esto? ¿Cuáles son las restricciones técnicas?


Ese habría sido un posible diseño para x86. ARM expone su contador de programa para lectura / escritura como R15 . Eso es inusual, sin embargo.

Permite una función muy compacta de prólogo / epílogo, junto con la capacidad de empujar o abrir múltiples registros con una sola instrucción: push {r5, lr} en la entrada, y pop {r5, pc} para regresar. (Aparece el valor guardado del registro de enlace en el contador del programa).

Sin embargo, hace que las implementaciones de ARM de alto rendimiento / fuera de orden sean menos convenientes, y se eliminó para AArch64.

Así que es posible, pero usa uno de los registros . ARM de 32 bits tiene 16 registros enteros (incluido el PC), por lo que un número de registro requiere 4 bits para codificarse en el código de máquina ARM. Otro registro casi siempre está vinculado como puntero de pila, por lo que ARM tiene 14 registros enteros de propósito general. (LR se puede guardar en la pila, por lo que puede ser y se utiliza como un registro de propósito general dentro de los cuerpos de funciones).

La mayor parte del x86 moderno se hereda de 8086. Fue diseñado con una codificación de instrucciones de longitud variable bastante compacta, y solo 8 registros, que requieren solo 3 bits para cada registro src y dst en el código de la máquina.

En el 8086 original, no tenían un propósito muy general, y el direccionamiento relativo a SP no es posible en el modo de 16 bits, por lo que esencialmente 2 registros (SP y BP) están vinculados para cosas de pila. Esto deja solo 6 registros de propósito algo general, y tener uno de ellos como PC en lugar de propósito general sería una gran reducción en los registros disponibles, lo que aumenta considerablemente la cantidad de derrames / recargas en el código típico.

AMD64 agregó r8-r15, y el modo de direccionamiento relativo a RIP. lea rsi, [rip+whatever] y los modos de direccionamiento relativos a RIP para el acceso directo a datos estáticos y constantes, es todo lo que necesita para un código independiente de la posición eficiente. Las instrucciones JMP indirectas son totalmente suficientes para escribir en RIP.

En realidad, no se puede obtener nada al permitir que se usen instrucciones arbitrarias para leer o escribir en la PC, ya que siempre se puede hacer lo mismo con un registro entero y un salto indirecto. Sería casi una desventaja pura que el R15 de x86-64 sea lo mismo que RIP, especialmente para el rendimiento de la arquitectura como un objetivo del compilador. (Las cosas extrañas de Asm escritas a mano ya eran un nicho poco común en 2000, cuando se diseñó AMD64).

Entonces, AMD64 es realmente la primera vez que x86 plausiblemente ha ganado un contador de programa completamente expuesto como ARM, pero hubo muchas buenas razones para no hacerlo.


No se puede acceder directamente porque no hay un caso de uso legítimo. Tener un cambio de instrucción arbitrario eip haría que la predicción de la rama fuera muy difícil, y probablemente abriría toda una serie de problemas de seguridad.

Puedes editar eip usando jmp , call o ret . Simplemente no puede leer o escribir eip a eip usando operaciones normales

Establecer eip en un registro es tan simple como jmp eax . También puedes push eax; ret push eax; ret , que empuja el valor de eax a la pila y luego regresa (es decir, salta y salta). La tercera opción es call eax que realiza una llamada a la dirección en eax.

La lectura se puede hacer así:

call get_eip get_eip: pop eax ; eax now contains the address of this instruction


jmp establecerá el registro EIP .

este código establecerá eip en 00401000:

mov eax, 00401000 jmp eax ;set Eip to 00401000

y para obtener EIP

call GetEIP . . GetEIP: mov eax, [esp] ret