2012-05-08 24 views
6

¿Qué puede causar una falla de segmentación al solo ingresar una función?SIGSEGV al ingresar a una función

La función introducir el siguiente aspecto:

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

donde Matrix es una struct. Cuando se ejecuta con el BGF traza inversa produce:

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

BGF no dice lo que es iQ. El ... está literalmente allí. ¿Qué podría causar esto?

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

Programa construido con -O3 -g

La persona que llama dice así:

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

tiene nada de especial

Volví a ejecutar el programa con valgrind:

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

Salida:

==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. 

Parece que es una pila dañado.

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) 

Bien, para que quede claro: los datos de Matrix viven en el montón. Básicamente tiene un puntero a los datos. La estructura es pequeña, 32 bytes. (Acabo de comprobar)

Ahora, reconstruí el programa con diferentes opciones de optimización:

-O0: el error no aparece.

-O1: el error no se muestra.

-O3: el error no se muestra.

--update

-O3 -fno-inline -fno-inline-functions: el error no aparece.

Eso lo explica. Demasiadas líneas en la función llevaron a un uso excesivo de la pila.

El problema se debía a un desbordamiento de pila

+2

Para ver sus variables, etc. no optimice. Compilar con '-O0 -g' – RageD

+2

¿Corrupción de pila? – Benj

+0

Bien, se reconstruirá con '-O0 -g'. Toma un tiempo – ritter

Respuesta

13

¿Qué puede causar un error de segmentación al solo ingresar una función?

La causa más frecuente es el agotamiento de la pila. Haz (gdb) disas en el punto de choque. Si la instrucción que se colgó es la primera lectura o escritura en una ubicación de la pila después de que se haya decrementado el %rsp, entonces el agotamiento de la pila es casi definitivamente la causa.

La solución generalmente implica crear subprocesos con pilas más grandes, mover algunas variables grandes de una pila a otra, o ambas.

Otra posible causa: si Matrix contiene arsenal muy grande, no se puede poner en la pila: el núcleo no se extienden más allá de pila actual en más de 128 K (más o menos, no recuerdo valor exacto) . Si Matrix es mayor que ese límite, no puede ponerlo en la pila.

Actualización:

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

Este desmontaje confirma el diagnóstico.

Además, está reservando 0x99b000 bytes en la pila (eso es casi 10MB). Debe haber algunos objetos descomunales que está tratando de localizar en la pila en la rutina eesu3. No hagas eso.

¿Qué quiere decir con "el núcleo no se extenderá más allá de pila actual en más de"

Cuando se extienden pila (decremento %rsp) por ejemplo, 1MB, y luego intente tocar la ubicación de la pila, la memoria no será accesible (el kernel crece a pedido). Esto generará una trampa de hardware y transferirá el control al kernel. Cuando el núcleo decide qué hacer, se ve en

  1. actual %rsp
  2. Meemory ubicación que la aplicación ha intentado acceder a
  3. límite de la pila para el subproceso actual

Si fallas dirección está por debajo actual %rsp, pero dentro de 128K (o alguna otra constante de magnitud similar), el kernel simplemente extiende la pila (siempre que dicha extensión no supere el límite de la pila).

Si la dirección de falla es más de 128K por debajo de la actual %rsp (como parece ser el caso aquí), obtiene SIGSEGV.

Todo esto funciona bien para la mayoría de los programas: incluso si usan mucha pila en un procedimiento recursivo, generalmente extienden la pila en trozos pequeños. Pero un programa equivalente que intentó reservar toda esa pila en una sola rutina se habría bloqueado.

De todos modos, haga (gdb) info locals en el punto de falla y vea qué locales podrían necesitar 10MB de pila. Luego muévelos a montón.

Actualización 2:

No hay gente

Ah, el programa probablemente no ha hecho lo suficiente en eesu3 para que haya gente local.

cuando se construye con -O0, el error desaparece. ¿Error de GCC?

Podría ser un error GCC, pero lo más probable es sólo que se GCC procesos en línea una gran cantidad de otras rutinas en eesu3, y cada una de las rutinas inline necesita sus propias bases de conocimiento N de pila. ¿Desaparece el problema si construyes la fuente que contiene eesu3 con -fno-inline?

Desafortunadamente, la clasificación de dicho comportamiento y la determinación de soluciones apropiadas, o la fijación de GCC, requiere experiencia del compilador. Puede comenzar compilando con -fdump-tree-all y mirando los archivos <source>.*t.* generados. Estos contienen vuelcos textuales de la representación interna de GCC en varias etapas del proceso de compilación. Usted puede ser capaz de entender lo suficiente como para seguir progresando.

+0

La carga útil de Matrix ya está en el montón. Realmente solo posee básicamente un puntero a los datos. No es del tamaño de struct Matrix (que tiene 8 o 16 bytes). Vea la salida anterior de 'disas' – ritter

+0

@Employed Russian, ¿Qué quiere decir con" el kernel no extenderá la pila más allá de la actual por más de "podría escribir más verbose por favor? – azat

+0

'info locals': No lugareños. Eso es extraño. Hay 30 vars locales definidos, cada 32bytes. ¿Donde han ido? Posible efecto de '-O3'? Al igual que en la pregunta actualizada al construir con '-O0', el error desaparece. ¿Error de GCC? – ritter

0

Si es una matriz, compruebe los índices que están tratando de acceder. ¿Tal vez está accediendo a elementos que van más allá de las dimensiones del objeto Matrix?

4

Es un desbordamiento de pila.

eesu3 intenta asignar algo muy grande en la pila, que puede verse en su código de montaje:

sub $0x99b000,%rsp 

Esto significa más de 10 MB de espacio de pila se consumen.

El problema puede estar en eesu3 o en una función que llama y el compilador elige alinear.

Mi conjetura es que el problema está en una llamada de función eesu3, pero no en el caso de que la prueba (una función de depuración?)
supongo que esto ya no sucede sin optimización - con la optimización, la función está incluido en eesu3, por lo que eesu3 usa mucha pila. Sin él, la función no está en línea, por lo que tendrá un problema solo cuando se llame.

+0

Sí, es probable que sea un desbordamiento de pila. Espero que esté bien para ti si @Emploed Russian entiende el punto. Él fue el primero en señalarlo – ritter

+0

El punto es que no tiene sentido señalar el punto de nuevo. – hochl

0

es probable que tenga algunas variables inicializadas en función

void eesu3(Matrix & iQ) 

Acabase el depurador podría pasar a través de declaraciones de variables, es probable que estén inicializado con el inicio del campo de aplicación (que es su función). Si declararía un búfer muy grande como ese:

char * buffer[268435456]; 

Podría obtener un desbordamiento de pila. Podría ser mejor asignar memoria como

void * pvBuffer = malloc(268435456); 

¿Has declarado un gran buffer? ¿Cuál es demasiado grande para poner en la pila? Podría significar que diferentes arquitecturas dan como resultado diferentes tamaños máximos posibles para búferes (sistemas operativos de 64 bits y 32 bits). ¿Diferentes núcleos? Como dijiste, el programa funciona bien en una máquina pero no en la otra.

Cuestiones relacionadas