2009-03-01 11 views

risposta

27

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

Per ottenere EIP o IP in codice indipendente dalla posizione:

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

Ma questo sbilancia il predittore stack di chiamate/ritorno, in modo preferiscono chiamare una funzione che in realtà tornare, per evitare mispredicts filiali, il 15 o giù di lì future ret istruzioni nelle funzioni genitore. (A meno che non hai intenzione di tornare, o almeno così di rado che non importa.)

get_retaddr: 
    mov eax, [esp] 
    ret    ; keeps the return-address predictor stack balanced 

In modalità x86-64, RIP possono essere letti direttamente tramite un RIP-relativa lea.

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

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

MASM: lea rax, [rip]

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

+0

Ah, amo quel trucco. In ARM, ci sono problemi di pipeline durante la lettura del PC. Questo problema è presente anche nelle CPU Intel? – strager

+0

No, non è presente su x86. (A proposito, il problema del pipel arm ARM è pazzesco) –

+0

In x86, si ha il costo di un ramo, che è molto peggiore per la pipeline rispetto alla sola lettura del PC. PIC su ARM è molto più economico di x86, anche con le stranezze della pipeline. –

26

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 ciò potrebbe fare la cosa sbagliata e leggere una parola da [thisone], ma di solito c'è qualche sintassi f o fare in modo che l'assemblatore faccia la cosa giusta.)

Se il codice viene caricato staticamente su un indirizzo specifico, l'assemblatore sa già (se gli hai detto l'indirizzo di partenza corretto) gli indirizzi assoluti di tutte le istruzioni. Il codice caricato dinamicamente, come parte di un'applicazione su qualsiasi sistema operativo moderno, otterrà il giusto indirizzo grazie al riposizionamento degli indirizzi eseguito dal linker dinamico (a condizione che l'assemblatore sia abbastanza intelligente da generare le tabelle di rilocazione, che di solito sono).

+0

Grazie per le informazioni, buona idea in effetti.:) –

+0

Ho provato questo su x86-64 con l'assembly gcc inline, ma ho avuto un errore nel backend: l'indirizzamento assoluto a 32 bit non è supportato in modalità a 64 bit usando llvm 6.1.0. È il problema di LLVM, o non è possibile in modalità 64-bit? – csl

+1

@csl: o sei su OS X (dove non c'è soluzione alternativa all'utilizzo di 'lea rax, [thisone]'), o sei su Linux che crea un oggetto condiviso e quindi anche questo non può funzionare. ('mov'-immediate richiede un indirizzo costante di collegamento, quindi funziona solo in codice dipendente dalla posizione).Ma se stai realizzando un eseguibile Linux, [il tuo compilatore potrebbe impostare automaticamente un eseguibile indipendente dalla posizione e '-no-pie -fno-pie' rendere un eseguibile dipendente dalla posizione dove puoi usare l'indirizzamento assoluto] (https: //stackoverflow.com/questions/43367427/32-bit-absolute-addresses-no-longer-allowed-in-x86-64-linux). –

7

Non c'è alcuna istruzione per leggere direttamente il puntatore di istruzioni (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 lo snippet sopra riportato in una chiamata di funzione, si otterrà lo stesso indirizzo (all'interno di quella 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 soffiare lo stack di indirizzo di ritorno (RAS). Lo stack di indirizzo di ritorno è uno stack separato di indirizzi di ritorno utilizzati internamente dal processore per facilitare branch target prediction per istruzioni RET.

Ogniqualvolta si dispone di un'istruzione CALL, l'EIP corrente viene inserito nel RAS e ogni volta che si esegue un'istruzione RET, il RAS viene interrotto e il valore superiore viene utilizzato come predizione del target di diramazione per tale istruzione.Se si incasina il RAS (ad esempio non corrispondendo ad ogni CALL con un RET, come in Cody's solution), si otterrà un sacco di errate previsioni di rami inutili, rallentando il programma. Questo metodo non soffiare il RAS, poiché ha una coppia di istruzioni CALL e RET corrispondenti.

+0

Grazie, ragazzi rock :) –

+0

Mille grazie per le informazioni, non sapevo che c'erano due stack .. :) –

+1

Il RAS è uno stack interno utilizzato dal processore; non è accessibile al codice in alcun modo. Viene utilizzato solo per la previsione del target di ramo. Senza di esso, il codice funzionerebbe ancora correttamente, solo più lentamente. –

15

Su x86-64 si può fare ad esempio:

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

Grazie! Qual è il significato dei numeri? –

+0

questa è la codifica dell'istruzione - c'è un offset implicito a 32 bit che è 0, non sono sicuro se c'è una codifica più breve – matja

+1

'lea rax, [rip]' non ha funzionato in NASM 2.10. Sembra che RIP possa essere usato solo indirettamente con 'rel' come in' lea rax, [rel _start] '? –

0

Si può anche leggere questo da/proc/stat. Controlla le manpage proc.

+0

Puoi indicare dove? Non riuscivo a trovarlo facilmente. –

+0

In/proc/stat puoi trovare il puntatore di istruzioni (EIP) –

+0

Penso che tu intenda '/ proc/self/stat', sarebbe anche bello citare la manpage. –

3

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

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

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

Questo non è esattamente il montaggio :-) – hirschhornsalz

+0

dovrebbe essere '&& current_address_label', non' $$ ' –

+0

Non si tratta di un indirizzo relativo (non IP)? –

Problemi correlati