Domanda

Può il contatore di programma sulla CPU Intel possono essere letti direttamente (cioè senza 'trucchi') in modalità kernel o qualche altro modo?

È stato utile?

Soluzione

No, EIP / IP non è possibile accedere direttamente, ma nel codice dipendente dalla posizione è una costante in modo da poter utilizzare un vicino (o lontano) simbolo come un immediato collegamento in tempo.

   mov eax, nearby_label    ; in position-dependent code
nearby_label:

Per ottenere EIP o IP nel codice a 32 bit indipendente dalla posizione:

        call _here
_here:  pop eax
; eax now holds the PC.

Nelle CPU più recenti rispetto Pentium Pro (o PIII probabilmente), call rel32 con rel32 = 0 è speciale carter di non influenzare il ritorno indirizzo pila predittore . Quindi questo è efficiente e compatta su x86 moderna, ed è ciò che clang utilizza per i 32 bit codice indipendente dalla posizione.

Su vecchi a 32 bit Pentium Pro CPU, questo sarebbe sbilanciare il predittore stack di chiamate / ritorno, in modo preferiscono chiamare una funzione che in realtà tornare, per evitare mispredicts filiali su un massimo di 15 o giù di lì istruzioni ret futuro nelle tue funzioni principali. (A meno che non hai intenzione di tornare, o almeno così di rado che non importa.) La pila predittori di ritorno-di indirizzo si riprenderà, però.

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.

In modalità x86-64, RIP può essere letto direttamente utilizzando un lea RIP-relativa .

default rel           ; NASM directive: use RIP-relative by default

lea  rax, [_here]     ; RIP + 0
_here:

MASM o GNU .intel_syntax: lea rax, [rip]

AT & T sintassi: lea 0(%rip), %rax

Altri suggerimenti

Se è necessario l'indirizzo di un'istruzione specifica, di solito qualcosa come questo fa il trucco:

thisone: 
   mov (e)ax,thisone

(Nota:. Su alcuni assemblatori questo potrebbe fare la cosa sbagliata e leggere una parola dalla [thisone], ma di solito c'è una certa sintassi per ottenere l'assemblatore di fare la cosa giusta)

Se il codice viene caricato in modo statico ad un indirizzo specifico, l'assemblatore sa già (se hai detto che il giusto indirizzo di partenza) gli indirizzi assoluti di tutte le istruzioni. Codice caricata dinamicamente, dire come parte di un'applicazione su qualsiasi sistema operativo moderno, otterrà l'indirizzo giusto, grazie ad affrontare delocalizzazione fatto dal linker dinamico (fornito l'assemblatore è abbastanza intelligente per generare le tabelle di trasferimento, che di solito sono).

In x86-64 si può fare ad esempio:

lea rax,[rip] (48 8d 05 00 00 00 00)

Non v'è alcuna istruzione per leggere direttamente il puntatore all'istruzione (EIP) su x86. È possibile ottenere l'indirizzo dell'istruzione corrente in fase di montaggio, con un po 'di assembly inline:

// GCC inline assembler; for MSVC, syntax is different
uint32_t eip;
__asm__ __volatile__("movl $., %0", : "=r"(eip));

La direttiva . assembler viene sostituito con l'indirizzo dell'istruzione corrente dall'assembler. Si noti che se si avvolge il frammento di codice in una chiamata di funzione, devi semplicemente ottenere lo stesso indirizzo (all'interno di tale funzione) ogni volta. Se si desidera una funzione C più utilizzabile, è possibile invece utilizzare alcuni di montaggio non in linea:

// 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

Ciò significa che ogni volta che si desidera ottenere il puntatore all'istruzione, è un po 'meno efficiente in quanto è necessario una chiamata di funzione in più. Si noti che facendo in questo modo non soffia la pila indirizzo di ritorno (RAS). Lo stack indirizzo di ritorno è una pila separata di indirizzi di ritorno utilizzato internamente dal processore per facilitare predizione bersaglio ramo per le istruzioni RET.

Ogni volta che si dispone di un'istruzione CALL, la corrente EIP viene spinto sul RAS, e ogni volta che si dispone di un'istruzione RET, RAS è spuntato, e il valore superiore è usato come la previsione di destinazione diramazione per tale istruzione. Se sbagli il RAS (ad esempio per non corrispondenti ciascuna chiamata con un RET, come in soluzione di Cody), si sta andando ad ottenere tutta una serie di previsioni sbagliate avvengono filiali inutili, rallentando il programma verso il basso. Questo metodo non saltare i RAS, dal momento che ha una coppia di istruzioni CALL e RET.

È un'architettura modo indipendente (ma gcc dipendente) di accedere al indirizzo che viene eseguito utilizzando etichette come valori:

http://gcc.gnu.org/onlinedocs/gcc/ Le etichette-as-Values.html

void foo()
{
  void *current_address = $$current_address_label;
  current_address_label:
      ....
}

È inoltre possibile leggere questo da / proc / stat. Controllare le pagine di manuale proc.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top