Tengo el siguiente rastro de pila. ¿Es posible distinguir algo útil de esto para depurar?
Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0 0x00000002 in ?? ()
#1 0x00000001 in ?? ()
#2 0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb)
¿Dónde empezar a mirar el código cuando obtenemos un Segmentation fault
, y el seguimiento de la pila no es tan útil?
NOTA: Si publico el código, los expertos de SO me darán la respuesta. Quiero seguir la guía de SO y encontrar la respuesta yo mismo, así que no voy a publicar el código aquí. Disculpas
-fno-omit-frame-pointer
? Además, para la corrupción de la memoria,valgrind
podría ser una herramienta más apropiada, si es una opción para usted.Respuestas:
Esas direcciones falsas (0x00000002 y similares) son en realidad valores de PC, no valores de SP. Ahora, cuando obtiene este tipo de SEGV, con una dirección de PC falsa (muy pequeña), el 99% de las veces se debe a una llamada a través de un puntero de función falso. Tenga en cuenta que las llamadas virtuales en C ++ se implementan mediante punteros de función, por lo que cualquier problema con una llamada virtual puede manifestarse de la misma manera.
Una instrucción de llamada indirecta simplemente empuja la PC después de la llamada a la pila y luego establece la PC en el valor objetivo (falso en este caso), por lo que si esto es lo que sucedió, puede deshacerlo fácilmente sacando manualmente la PC de la pila. . En código x86 de 32 bits, simplemente haga lo siguiente:
Con código x86 de 64 bits que necesita
Entonces, debería poder hacer una
bt
y averiguar dónde está realmente el código.El otro 1% de las veces, el error se debe a que se sobrescribe la pila, generalmente al desbordar una matriz almacenada en la pila. En este caso, es posible que pueda obtener más claridad sobre la situación utilizando una herramienta como valgrind
fuente
gdb executable corefile
abrirá gdb con el archivo ejecutable y principal, en cuyo punto puede hacerbt
(o los comandos anteriores seguidos debt
) ...sp
, noesp
orsp
, y su instrucción de llamada almacena la dirección de retorno en ellr
registro, no en la pila. Entonces, para ARM, todo lo que realmente necesita para deshacer la llamada esset $pc = $lr
. Si$lr
no es válido, tiene un problema mucho más difícil de resolver.Si la situación es bastante simple, la respuesta de Chris Dodd es la mejor. Parece que saltó a través de un puntero NULL.
Sin embargo, es posible que el programa se haya disparado en el pie, la rodilla, el cuello y el ojo antes de estrellarse: sobrescribió la pila, estropeó el puntero del cuadro y otros males. Si es así, no es probable que desenredar el hachís le muestre patatas y carne.
La solución más eficiente será ejecutar el programa bajo el depurador y pasar por alto las funciones hasta que el programa se bloquee. Una vez que se identifica una función que falla, comience de nuevo, ingrese a esa función y determine qué función llama la causa. Repita hasta que encuentre la única línea de código infractora. El 75% de las veces, la solución será obvia.
En el otro 25% de las situaciones, la línea de código infractora es una pista falsa. Estará reaccionando a condiciones (inválidas) configuradas muchas líneas antes, tal vez miles de líneas antes. Si ese es el caso, el mejor curso elegido depende de muchos factores: principalmente su comprensión del código y su experiencia con él:
printf
en variables críticas conduzca a la necesaria A ha!¡Buena suerte!
fuente
Suponiendo que el puntero de la pila es válido ...
Puede ser imposible saber exactamente dónde ocurre la SEGV a partir del backtrace; creo que los dos primeros marcos de pila se sobrescriben por completo. 0xbffff284 parece una dirección válida, pero las dos siguientes no lo son. Para ver más de cerca la pila, puede intentar lo siguiente:
gdb $ x / 32ga $ rsp
o una variante (reemplace el 32 con otro número). Eso imprimirá un número de palabras (32) a partir del puntero de pila de tamaño gigante (g), formateado como direcciones (a). Escriba 'ayuda x' para obtener más información sobre el formato.
Instrumentar su código con algunos 'printf' centinelas puede no ser una mala idea, en este caso.
fuente
info symbol
cómo hacerlo en gdb.x/256wa $sp
=)Mire algunos de sus otros registros para ver si uno de ellos tiene el puntero de pila almacenado en caché. A partir de ahí, es posible que pueda recuperar una pila. Además, si está incrustado, con frecuencia la pila se define en una dirección muy particular. Usando eso, a veces también puedes obtener una pila decente. Todo esto supone que cuando saltó al hiperespacio, su programa no vomitó toda la memoria en el camino ...
fuente
Si se trata de una sobrescritura de pila, los valores pueden corresponder a algo reconocible del programa.
Por ejemplo, me encontré mirando la pila
y
0x342d
es 13357, que resultó ser un ID de nodo cuando hice grep en los registros de la aplicación. Eso ayudó inmediatamente a reducir los sitios candidatos donde podría haberse producido la sobrescritura de la pila.fuente