2009-06-03 13 views
10

Ho scritto un programma "pericoloso" in C++ che salta avanti e indietro da uno stack ad un altro. L'obiettivo è quello di passare dal livello più basso di uno stack di chiamate a un chiamante, fare qualcosa e poi tornare indietro di nuovo, ogni volta saltando tutte le chiamate tra di loro.Cosa fa il registro PIC (% ebx)?

Effettuare questa operazione modificando manualmente l'indirizzo di base dello stack (impostazione %ebp) e passando a un indirizzo di etichetta. Funziona completamente, con gcc e icc entrambi, senza alcuna corruzione dello stack. Il giorno in cui ha funzionato è stata una bella giornata.

Ora sto prendendo lo stesso programma e lo riscrivo in C, e non funziona. In particolare, non funziona con gcc v4.0.1 (Mac OS). Una volta saltato sul nuovo stack frame (con il puntatore di base dello stack impostato correttamente), vengono eseguite le seguenti istruzioni, appena prima di una chiamata a fprintf. L'ultima istruzione elencati qui si blocca, NULL dereferenziazione:

lea 0x18b8(%ebx), %eax 
mov (%eax), %eax 
mov (%eax), %eax 

Ho fatto qualche debug, e ho capito che impostando manualmente il registro %ebx quando passo stack frame (utilizzando un valore ho osservato prima di partire la funzione in primo luogo), risolvo il bug. Ho letto che questo registro si occupa di "codice indipendente dalla posizione" in gcc.

Che cos'è il codice indipendente dalla posizione? Come funziona il codice indipendente dalla posizione? A che cosa sta puntando questo registro?

+0

Si può prendere in considerazione setjmp/longjmp per ottenere questa funzionalità senza mucking con% ebx direttamente. –

+2

In generale, sì, hai ragione. In questo caso, devo essere in grado di passare a un chiamante, eseguire qualche altra funzione e poi tornare indietro al callee. Con setjmp/longjmp, la pila del callee verrebbe sovrascritta dall'altra funzione. –

risposta

6

Il PIC è un codice che viene spostato dinamicamente quando viene caricato. Il codice non PIC è saltato e gli indirizzi di chiamata sono impostati al momento del collegamento. PIC ha una tabella che fa riferimento a tutti i luoghi in cui tali valori esistono, proprio come un file .dll.

Quando l'immagine viene caricata, il caricatore aggiornerà questi valori in modo dinamico. Altri schemi fanno riferimento a un valore di dati che definisce una "base" e l'indirizzo di destinazione viene deciso eseguendo calcoli sulla base. La base viene di solito impostata nuovamente dal caricatore.

Infine, altri schemi utilizzano vari trampolini che chiamano a offset relativi noti. Gli offset relativi contengono codice e/o dati che vengono aggiornati da un caricatore.

Ci sono diversi motivi per cui vengono scelti diversi schemi. Alcuni sono veloci durante l'esecuzione, ma più lenti da caricare. Alcuni sono veloci da caricare, ma hanno minori prestazioni di runtime.

13

EBX punti alla tabella di offset globale. Vedi this reference about PIC on i386. Il collegamento spiega quale PIC è un modo in cui viene utilizzato EBX.

+0

GOT - Tabella offset globale. –

+0

Questa era una buona risorsa; assolutamente da leggere. –