Eu tenho o seguinte rastreamento de pilha. É possível extrair algo útil disso para depuração?
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)
Por onde começar a olhar para o código quando obtivermos um Segmentation fault
, e o rastreamento de pilha não for tão útil?
NOTA: Se eu postar o código, os especialistas em SO me darão a resposta. Quero seguir a orientação do SO e encontrar a resposta sozinho, então não estou postando o código aqui. Desculpas.
-fno-omit-frame-pointer
? Além disso, para corrupção de memória,valgrind
pode ser uma ferramenta mais apropriada, se for uma opção para você.Respostas:
Esses endereços falsos (0x00000002 e semelhantes) são, na verdade, valores de PC, não valores de SP. Agora, quando você obtém esse tipo de SEGV, com um endereço de PC falso (muito pequeno), 99% das vezes é devido à chamada por meio de um ponteiro de função falso. Observe que as chamadas virtuais em C ++ são implementadas por meio de ponteiros de função, portanto, qualquer problema com uma chamada virtual pode se manifestar da mesma maneira.
Uma instrução chamada indireta apenas empurra o PC após a chamada para a pilha e, em seguida, define o PC ao valor-alvo (falsa, neste caso), então se isso é o que aconteceu, você pode facilmente desfazê-lo por avançar manualmente o PC da pilha . No código x86 de 32 bits, você apenas faz:
Com o código x86 de 64 bits, você precisa
Então, você deve ser capaz de fazer um
bt
e descobrir onde o código é realmente.No outro 1% das vezes, o erro será devido à substituição da pilha, geralmente pelo estouro de uma matriz armazenada na pilha. Nesse caso, você pode obter mais clareza sobre a situação usando uma ferramenta como o valgrind
fonte
gdb executable corefile
irá abrir o gdb com o executável e o arquivo principal, momento em que você pode fazerbt
(ou os comandos acima seguidos debt
) ...sp
, nãoesp
oursp
, e sua instrução de chamada armazena o endereço de retorno nolr
registrador, não na pilha. Portanto, para ARM, tudo o que você realmente precisa para desfazer a chamada éset $pc = $lr
. Se$lr
for inválido, você terá um problema muito mais difícil de resolver.Se a situação for bastante simples, a resposta de Chris Dodd é a melhor. Parece que saltou um ponteiro NULL.
No entanto, é possível que o programa tenha atingido o pé, o joelho, o pescoço e o olho antes de cair - sobrescreveu a pilha, bagunçou o ponteiro do quadro e outros males. Nesse caso, desfazer o haxixe provavelmente não mostrará batatas e carne.
A solução mais eficiente será executar o programa no depurador e passar por cima das funções até que o programa trave. Assim que uma função de travamento for identificada, comece novamente e entre nessa função e determine qual função ela chama que causa o travamento. Repita até encontrar a única linha de código ofensiva. 75% das vezes, a correção será óbvia.
Nos outros 25% das situações, a chamada linha de código ofensiva é uma pista falsa. Ele estará reagindo a condições (inválidas) configuradas muitas linhas antes - talvez milhares de linhas antes. Se for esse o caso, o melhor curso escolhido depende de muitos fatores: principalmente sua compreensão do código e experiência com ele:
printf
em variáveis críticas leve ao A ha! Necessário !Boa sorte!
fonte
Supondo que o ponteiro da pilha seja válido ...
Pode ser impossível saber exatamente onde o SEGV ocorre a partir do backtrace - acho que os dois primeiros frames da pilha foram completamente substituídos. 0xbffff284 parece ser um endereço válido, mas os próximos dois não são. Para uma análise mais detalhada da pilha, você pode tentar o seguinte:
gdb $ x / 32ga $ rsp
ou uma variante (substitua o 32 por outro número). Isso imprimirá algum número de palavras (32) a partir do ponteiro da pilha de tamanho gigante (g), formatado como endereços (a). Digite 'help x' para obter mais informações sobre o formato.
Instrumentar seu código com alguns 'printf' de sentinela pode não ser uma má ideia, neste caso.
fonte
info symbol
como fazer isso no gdb.x/256wa $sp
=)Observe alguns de seus outros registradores para ver se um deles tem o ponteiro da pilha armazenado em cache. A partir daí, você pode recuperar uma pilha. Além disso, se estiver embutido, muitas vezes a pilha é definida em um endereço muito particular. Usando isso, às vezes você também pode obter uma pilha decente. Tudo isso pressupõe que quando você saltou para o hiperespaço, seu programa não vomitou toda a memória ao longo do caminho ...
fonte
Se for uma substituição de pilha, os valores podem muito bem corresponder a algo reconhecível do programa.
Por exemplo, acabei de olhar para a pilha
e
0x342d
é 13357, que acabou sendo um id de nó quando eu executei o grep nos logs do aplicativo para ele. Isso ajudou imediatamente a restringir os sites candidatos onde a substituição da pilha poderia ter ocorrido.fonte