2009-09-04 14 views
6

Il seguente codice riassume il problema che ho al momento. Il mio attuale flusso di esecuzione è il seguente e un I è in esecuzione in GCC 4.3.SetJmp/LongJmp: Perché viene generato un segfault?

jmp_buf a_buf; 
jmp_buf b_buf; 

void b_helper() 
{ 
    printf("entering b_helper"); 
    if(setjmp(b_buf) == 0) 
    { 
     printf("longjmping to a_buf"); 
     longjmp(a_buf, 1); 
    } 
    printf("returning from b_helper"); 
    return; //segfaults right here 
} 
void b() 
{ 
    b_helper(); 
} 
void a() 
{ 
    printf("setjmping a_buf"); 
    if(setjmp(a_buf) == 0) 
    { 
     printf("calling b"); 
     b(); 
    } 
    printf("longjmping to b_buf"); 
    longjmp(b_buf, 1); 
} 
int main() 
{ 
    a(); 
} 

Il flusso di esecuzione sopra riportato crea un segfault subito dopo il ritorno in b_helper. È quasi come se solo il frame stack b_helper fosse valido e gli stack sottostanti venissero cancellati.

Qualcuno può spiegare perché questo sta accadendo? Immagino che sia un'ottimizzazione GCC che cancella frame stack inutilizzati o qualcosa del genere.

Grazie.

risposta

12

È possibile solo longjmp() indietro su lo stack di chiamate. La chiamata a longjmp(b_buf, 1) è dove le cose iniziano a andare storte, perché la cornice dello stack referenziata da b_buf non esiste più dopo lo longjmp(a_buf).

Dalla documentazione per longjmp:

Il longjmp() routine non possono essere chiamati dopo la routine che chiama setjmp() routine di ritorni.

Ciò include "il ritorno" tramite un longjmp() fuori della funzione.

+0

C'è un modo per longjmp down the stack frame? È possibile copiare lo stack da b a b_helper nell'heap ed eseguirlo da lì? Inoltre, perché lo stack frame a cui fa riferimento b_buf non è più valido dopo aver saltato lo stack delle chiamate? – jameszhao00

+2

Una volta che una parte della pila viene rilasciata, è completamente non valida (altre chiamate di funzione, interruzioni o qualsiasi altra cosa potrebbe sovrascrivere la memoria). –

+4

Si può pensare a un 'longjmp()' come a un "ritorno esteso". Un riuscito 'longjmp()' funziona come una serie di ritorni successivi, svolgendo lo stack di chiamate fino a raggiungere il corrispondente 'setjmp()'. Una volta che i telegrammi di stack delle chiamate vengono svolti, non sono più validi. Ciò è in contrasto con le implementazioni di coroutine (ad esempio Modula-2) o continuazioni (ad esempio Scheme) in cui lo stack di chiamate rimane valido dopo il salto da qualche altra parte.C e C++ supportano solo un singolo stack di chiamate lineari, * a meno che * non utilizzi thread in cui crei più stack di chiamate indipendenti. –

5

La norma dice questo circa longjmp() (7.13.2.1 La funzione longjmp):

La funzione longjmp ripristina l'ambiente salvato dalla più recente invocazione della macro setjmp nella stessa invocazione del programma con l'argomento jmp_buf corrispondente. Se non v'è stata alcuna chiamata, o se la funzione di contenimento l'invocazione della macro setjmp è terminato esecuzione nel frattempo

con una nota che chiarisce questo un po ':

Ad esempio, eseguendo un'istruzione return o perché un'altra chiamata longjmp ha causato un trasferimento a una chiamata setjmp in una funzione precedente nell'insieme di chiamate nidificate.

Così si può non longjmp() indietro & indietro attraverso nidificate setjmp/longjmp set.

+0

Se esistono più ambiti nidificati all'interno di una funzione e setjmp() viene eseguito all'interno di un ambito nidificato, può essere legittimamente longjmp() di nuovo lì se uno ha lasciato l'ambito con il setjmp() ma è rimasto all'interno della stessa funzione? Ciò implicherebbe che le variabili volatili in uno scope nidificato sono obbligate a mantenere i loro valori se l'esecuzione lascia e rientra in tale ambito (ma rimane all'interno della funzione)? – supercat

Problemi correlati