assembly - programa - program counter que es
Lectura del contador del programa directamente (6)
En x86-64 puede hacer, por ejemplo:
lea rax,[rip] (48 8d 05 00 00 00 00)
¿Puede el contador del programa en las CPU Intel leerse directamente (es decir, sin "trucos") en el modo kernel o en algún otro modo?
Existe una arquitectura independiente (pero dependiente de gcc) para acceder a la dirección que se está ejecutando utilizando etiquetas como valores:
http://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
void foo()
{
void *current_address = $$current_address_label;
current_address_label:
....
}
No hay instrucciones para leer directamente el puntero de instrucción (EIP) en x86. Puede obtener la dirección de la instrucción actual ensamblada con un pequeño ensamblaje en línea:
// GCC inline assembler; for MSVC, syntax is different
uint32_t eip;
__asm__ __volatile__("movl $., %0", : "=r"(eip));
El .
la directiva ensamblador se reemplaza con la dirección de la instrucción actual por el ensamblador. Tenga en cuenta que si ajusta el fragmento de arriba en una llamada de función, obtendrá la misma dirección (dentro de esa función) cada vez. Si desea una función C más utilizable, puede utilizar un ensamblaje no en línea:
// In a C header file:
uint32_t get_eip(void);
// In a separate assembly (.S) file:
.globl _get_eip
_get_eip:
mov 0(%esp), %eax
ret
Esto significa que cada vez que desea obtener el puntero de instrucción, es un poco menos eficiente ya que necesita una llamada de función adicional. Tenga en cuenta que hacerlo de esta manera no arruina la pila de direcciones de retorno (RAS). La pila de direcciones de retorno es una pila separada de direcciones de retorno utilizadas internamente por el procesador para facilitar la predicción del objetivo de bifurcación para las instrucciones RET.
Cada vez que tiene una instrucción CALL, el EIP actual se inserta en el RAS, y cada vez que tiene una instrucción RET, se abre el RAS, y el valor superior se usa como predicción de sucursal objetivo para esa instrucción. Si arruinas el RAS (por ejemplo, al no hacer coincidir cada LLAMADA con un RET, como en la solución de Cody ), vas a tener un montón de errores innecesarios en las ramificaciones, ralentizando tu programa. Este método no arruina el RAS, ya que tiene un par coincidente de instrucciones CALL y RET.
No, no se puede acceder a EIP / IP directamente, pero en el código de posición dependiente es una constante de tiempo de enlace para que pueda usar un símbolo cercano (o distante) como inmediato.
mov eax, nearby_label ; in position-dependent code
nearby_label:
Para obtener EIP o IP en código de 32 bits independiente de la posición:
call _here
_here: pop eax
; eax now holds the PC.
En las CPU más nuevas que Pentium Pro (o PIII probablemente), la call rel32
con rel32 = 0 tiene un diseño especial para no afectar la pila del predictor de la dirección de retorno . De modo que es eficiente y compacto en el x86 moderno, y es lo que utiliza clang para el código independiente de posición de 32 bits.
En las antiguas CPUs Pentium Pro de 32 bits, esto desequilibraría la pila del predictor de llamada / retorno, por lo que preferiría llamar a una función que efectivamente retorna, para evitar errores de derivación en hasta 15 o más futuras instrucciones ret
en las funciones principales. (A menos que no regrese, o tan raramente que no importa). Sin embargo, la pila de predictores de dirección de retorno se recuperará.
get_retaddr_ppro:
mov eax, [esp]
ret ; keeps the return-address predictor stack balanced
; even on CPUs where call +0 isn''t a no-op.
En el modo x86-64, RIP se puede leer directamente usando un lea
relativo al RIP .
default rel ; NASM directive: use RIP-relative by default
lea rax, [_here] ; RIP + 0
_here:
MASM o GNU .intel_syntax
: lea rax, [rip]
Sintaxis de AT & T: lea 0(%rip), %rax
Si necesita la dirección de una instrucción específica, generalmente algo como esto funciona bien:
thisone:
mov (e)ax,thisone
(Nota: en algunos ensambladores esto podría hacer algo incorrecto y leer una palabra de [esto], pero generalmente hay alguna sintaxis para que el ensamblador haga lo correcto).
Si su código está cargado estáticamente en una dirección específica, el ensamblador ya sabe (si le indicó la dirección de inicio correcta) las direcciones absolutas de todas las instrucciones. El código cargado dinámicamente, digamos como parte de una aplicación en cualquier sistema operativo moderno, obtendrá la dirección correcta gracias a la reubicación de direcciones realizada por el enlazador dinámico (siempre que el ensamblador sea lo suficientemente inteligente como para generar las tablas de reubicación, que generalmente son).
También puede leer esto de / proc / stat. Verifique las páginas de manual de proc.