2012-05-08 10 views
6

Cosa può causare un errore di segmentazione quando si entra semplicemente in una funzione?SIGSEGV quando si immette una funzione

La funzione è entrata assomiglia:

21: void eesu3(Matrix & iQ) 
22: { 

dove Matrix è un struct. Quando si esegue con GDB il backtrace produce:

(gdb) backtrace 
#0 eesu3 (iQ=...) at /home/.../eesu3.cc:22 
#1 ... 

GDB non dice che cosa è iQ. I ... sono letteralmente lì. Cosa potrebbe causare questo?

GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

programma costruito con -O3 -g

Il chiamante va come:

Matrix q; 
// do some stuff with q 
eesu3(q); 

Niente di speciale qui

Riesegui il programma con valgrind:

valgrind --tool=memcheck --leak-check=yes --show-reachable=yes --num-callers=20 --track-fds=yes <prgname> 

uscita:

==2240== Warning: client switching stacks? SP change: 0x7fef7ef68 --> 0x7fe5e3000 
==2240==   to suppress, use: --max-stackframe=10076008 or greater 
==2240== Invalid write of size 8 
==2240== at 0x14C765B: eesu3(Matrix &) (eesu3.cc:22) 
... 
==2240== Address 0x7fe5e3fd8 is on thread 1's stack 
==2240== 
==2240== Can't extend stack to 0x7fe5e2420 during signal delivery for thread 1: 
==2240== no stack segment 
==2240== 
==2240== Process terminating with default action of signal 11 (SIGSEGV) 
==2240== Access not within mapped region at address 0x7FE5E2420 
==2240== at 0x14C765B: eesu3(Matrix&) (eesu3.cc:22) 
==2240== If you believe this happened as a result of a stack 
==2240== overflow in your program's main thread (unlikely but 
==2240== possible), you can try to increase the size of the 
==2240== main thread stack using the --main-stacksize= flag. 
==2240== The main thread stack size used in this run was 8388608. 

Sembra proprio uno stack danneggiato.

Dump of assembler code for function eesu3(Matrix &): 
    0x00000000014c7640 <+0>: push %rbp 
    0x00000000014c7641 <+1>: mov %rsp,%rbp 
    0x00000000014c7644 <+4>: push %r15 
    0x00000000014c7646 <+6>: push %r14 
    0x00000000014c7648 <+8>: push %r13 
    0x00000000014c764a <+10>: push %r12 
    0x00000000014c764c <+12>: push %rbx 
    0x00000000014c764d <+13>: and $0xfffffffffffff000,%rsp 
    0x00000000014c7654 <+20>: sub $0x99b000,%rsp 
=> 0x00000000014c765b <+27>: mov %rdi,0xfd8(%rsp) 

Va bene, per essere chiari: i dati di Matrix sono disponibili. Praticamente contiene un puntatore ai dati. La struttura è piccola, 32 byte. (Appena controllato)

Ora, ho ricostruito il programma con diverse opzioni di ottimizzazione:

-O0: l'errore non mostra.

-O1: l'errore non viene visualizzato.

-O3: l'errore non viene visualizzato.

--update

-O3 -fno-inline -fno-inline-functions: l'errore non mostra.

Questo lo spiega. Troppi inline nella funzione hanno portato a un eccessivo utilizzo dello stack.

Il problema era dovuto a un overflow dello stack

+2

Per controllare le variabili, ecc. Non ottimizzare. Compilare con '-O0 -g' – RageD

+2

Corruzione dello stack? – Benj

+0

Ok, ricostruirà con '-O0 -g'. Ci vuole un po 'di tempo – ritter

risposta

13

Cosa può causare un errore di segmentazione quando si entra in una funzione?

La causa più frequente è l'esaurimento dello stack. Fare (gdb) disas al punto di arresto. Se l'istruzione che si è arrestata in modo anomalo è la prima lettura o scrittura in una posizione di stack dopo che %rsp è stata decrementata, l'esaurimento dello stack è quasi sicuramente la causa.

La soluzione di solito comporta la creazione di thread con stack più grandi, lo spostamento di grandi variabili dallo stack all'heap o entrambi.

Un'altra possibile causa: se Matrix contiene Very Large Array, non si può mettere sulla pila: il kernel non estendersi al di là dello stack corrente di oltre 128 K (o giù di lì, non mi ricordo valore esatto) . Se Matrix è più grande di quel limite, non puoi metterlo in pila.

Aggiornamento:

0x00000000014c7654 <+20>: sub $0x99b000,%rsp 
=> 0x00000000014c765b <+27>: mov %rdi,0xfd8(%rsp) 

Questa smontaggio conferma la diagnosi.

Inoltre, si riservano 0x99b000 byte nello stack (ovvero quasi 10 MB). Ci devono essere degli oggetti giganteschi che stai cercando di localizzare in pila nella routine eesu3. Non farlo.

Che cosa si intende per "il kernel non si estenderà oltre la pila attuale di più di"

Quando si estende pila (decremento %rsp) per esempio 1MB, quindi prova a toccare la posizione dello stack, la memoria non sarà accessibile (il kernel cresce stack su richiesta). Questo genererà un trap hardware e trasferirà il controllo al kernel. Quando il kernel decide cosa fare, esamina

  1. attuale %rsp
  2. Meemory posizione che l'applicazione ha tentato di accedere
  3. limite Stack per il thread corrente

Se fagliazione indirizzo è al di sotto corrente %rsp, ma entro 128K (o qualche altra costante di grandezza simile), il kernel estende semplicemente lo stack (purché tale estensione non superi il limite dello stack).

Se l'indirizzo di errore è superiore a 128 K sotto l'attuale %rsp (come sembra essere il caso qui), si ottiene SIGSEGV.

Tutto questo funziona bene per la maggior parte dei programmi: anche se usano un sacco di stack in una procedura ricorsiva, di solito estendono lo stack in piccoli blocchi. Ma un programma equivalente che ha cercato di riservare tutto lo stack in una singola routine si sarebbe schiantato.

In ogni caso, fare (gdb) info locals al punto di arresto, e vedere quali persone del posto potrebbero richiedere 10 MB di stack. Quindi spostali in heap.

Aggiornamento 2:

Nessuna locali

Ah, il programma ha probabilmente non fatto abbastanza in eesu3 che ci sia gente del posto.

quando si crea con -O0 l'errore scompare. Bug GCC?

Potrebbe essere un bug di GCC, ma più probabilmente è solo che GCC è inlining un sacco di altre routine in eesu3, e ciascuna delle routine inline ha bisogno dei suoi propri N KBs di stack. Il problema scompare se si crea la sorgente contenente eesu3 con -fno-inline?

Sfortunatamente, il triage di tale comportamento e la risoluzione di soluzioni alternative appropriate o la correzione di GCC richiedono l'esperienza del compilatore. Potresti iniziare compilando con -fdump-tree-all e guardando i file <source>.*t.* generati. Questi contengono dump testuali della rappresentazione interna GCC in varie fasi del processo di compilazione. Si maggio essere in grado di comprendere abbastanza di esso per fare ulteriori progressi.

+0

Il carico utile di Matrix è già presente nell'heap. In realtà possiede solo un puntatore ai dati. Non è la dimensione della struct Matrix (che è 8 o 16 byte). Vedi sopra l'output di 'disas' – ritter

+0

@Employed Russian, cosa intendi con" il kernel non estenderà lo stack oltre l'attuale di più di "potresti scrivere più dettagliato per favore? – azat

+0

'info locals': nessuna gente del posto. È strano. Ci sono 30 vars locali definiti, ciascuno 32 byte. Dove sono andati? Possibile effetto di '-O3'? Come nella domanda aggiornata quando si costruisce con '-O0' l'errore scompare. Bug GCC? – ritter

0

Se la sua una matrice, controllare gli indici che si sta tentando di accedere. Forse stai accedendo a elementi che vanno oltre le dimensioni dell'oggetto Matrix?

4

È un overflow dello stack.

eesu3 tenta di allocare qualcosa di molto grande in pila, che può essere visto nel suo codice assembly:

sub $0x99b000,%rsp 

Questo significa più di 10 MB di spazio di stack sono consumati.

Il problema può essere in eesu3 o una funzione che chiama e il compilatore sceglie di inline.

La mia ipotesi è che il problema è in una chiamata di funzione eesu3, ma non nel caso in cui si prova (una funzione di debug?)
Credo che questo perché non avviene senza ottimizzazione - con l'ottimizzazione, la funzione è in linea eesu3, quindi eesu3 utilizza un sacco di stack. Senza di esso, la funzione non è in linea, quindi avrai un problema solo quando viene effettivamente chiamato.

+0

Sì, è probabile che si verifichi un overflow dello stack. Spero che vada bene per te se @Emploed Russian ottiene il punto. È stato il primo a farlo notare – ritter

+0

Il punto è che non ha senso puntare il dito verso l'esterno. – hochl

0

Probabilmente avete alcune variabili inizializzate in funzione

void eesu3(Matrix & iQ) 

Allthough il debugger potrebbe passare da dichiarazioni di variabili, sono probabilmente inizializzato con l'inizio del campo di applicazione (cioè la funzione). Se si dichiara un buffer molto grande in questo modo:

char * buffer[268435456]; 

È possibile ottenere un overflow dello stack. Potrebbe essere meglio allocare memoria come

void * pvBuffer = malloc(268435456); 

Hai dichiarato un buffer di grandi dimensioni? Quale è troppo grande per essere messo in pila? Potrebbe significare che diverse architetture danno luogo a diverse dimensioni massime possibili per i buffer (sistemi operativi a 64 e 32 bit)? Kernel diversi? Come hai detto tu, il programma funziona bene su una macchina ma non sull'altra.

Problemi correlati