Como analiso o arquivo de despejo principal de um programa com o GDB quando ele possui parâmetros de linha de comando?

156

Meu programa funciona assim:

exe -p param1 -i param2 -o param3

Ele travou e gerou um arquivo de despejo principal core.pid,.

Eu quero analisar o arquivo de despejo principal

gdb ./exe -p param1 -i param2 -o param3 core.pid

Mas o GDB reconhece os parâmetros do arquivo EXE como entrada do GDB.

Como analiso um arquivo de despejo principal nessa situação?

Treper
fonte
1
Tem certeza de que exenão é um script de shell (para definir algumas variáveis, etc.) como, por exemplo, o firefoxLinux?
Basile Starynkevitch 29/11

Respostas:

182

Você pode usar o núcleo com o GDB de várias maneiras, mas passar parâmetros que devem ser passados ​​ao executável para o GDB não é a maneira de usar o arquivo principal. Esse também pode ser o motivo pelo qual você recebeu esse erro. Você pode usar o arquivo principal das seguintes maneiras:

gdb <executable> <core-file>ou gdb <executable> -c <core-file>ou

gdb <executable>
...
(gdb) core <core-file>

Ao usar o arquivo principal, você não precisa passar argumentos. O cenário de falha é mostrado no GDB (verificado com o GDB versão 7.1 no Ubuntu).

Por exemplo:

$ ./crash -p param1 -o param2
Segmentation fault (core dumped)
$ gdb ./crash core
GNU gdb (GDB) 7.1-ubuntu
...
Core was generated by `./crash -p param1 -o param2'. <<<<< See this line shows crash scenario
Program terminated with signal 11, Segmentation fault.
#0  __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
    in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

Se você deseja passar parâmetros para o executável a ser depurado no GDB, use --args.

Por exemplo:

$ gdb --args ./crash -p param1 -o param2
GNU gdb (GDB) 7.1-ubuntu
...
(gdb) r
Starting program: /home/@@@@/crash -p param1 -o param2

Program received signal SIGSEGV, Segmentation fault.
__strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
    in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

As páginas de manual serão úteis para ver outras opções do GDB.

another.anon.coward
fonte
38

Uso simples do GDB, para depurar arquivos coredump:

gdb <executable_path> <coredump_file_path>

Um arquivo coredump para um "processo" é criado como um arquivo "core.pid".

Depois de entrar no prompt do GDB (na execução do comando acima), digite:

...
(gdb) where

Isso fornecerá as informações da pilha, onde é possível analisar a causa da falha / falha. Outro comando, para os mesmos fins, é:

...
(gdb) bt full

É o mesmo que acima. Por convenção, ele lista todas as informações da pilha (o que leva ao local da falha).

parasita
fonte
22

Basta pular os parâmetros. O GDB não precisa deles:

gdb ./exe core.pid
Nialscorva
fonte
Mas isso não funciona. O aviso de saída gdb: o arquivo principal pode não corresponder ao arquivo executável especificado. Falha ao ler uma imagem válida do arquivo de objeto da memória.
Treper
6
"arquivo principal pode não corresponder ao executável especificado". Você modificou o exe depois que ele produziu o núcleo? Você talvez o reconstruiu com diferentes opções de linha de comando? É muito importante fornecer ao GDB exatamente o mesmo binário que produziu o núcleo. Caso contrário, você terá lixo.
Empregado russo
2
Verifique também se o binário que está sendo passado para o gdb não está removido. Você pode executar 'arquivo <nome binário>', que mostra que está despojado ou não.
Diwakar Sharma
12

objdump+ gdbexemplo mínimo executável

TL; DR:

Agora, para a configuração completa do teste educacional:

main.c

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int myfunc(int i) {
    *(int*)(NULL) = i; /* line 7 */
    return i - 1;
}

int main(int argc, char **argv) {
    /* Setup some memory. */
    char data_ptr[] = "string in data segment";
    char *mmap_ptr;
    char *text_ptr = "string in text segment";
    (void)argv;
    mmap_ptr = (char *)malloc(sizeof(data_ptr) + 1);
    strcpy(mmap_ptr, data_ptr);
    mmap_ptr[10] = 'm';
    mmap_ptr[11] = 'm';
    mmap_ptr[12] = 'a';
    mmap_ptr[13] = 'p';
    printf("text addr: %p\n", text_ptr);
    printf("data addr: %p\n", data_ptr);
    printf("mmap addr: %p\n", mmap_ptr);

    /* Call a function to prepare a stack trace. */
    return myfunc(argc);
}

Compile e execute para gerar o núcleo:

gcc -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
ulimit -c unlimited
rm -f core
./main.out

Resultado:

text addr: 0x4007d4
data addr: 0x7ffec6739220
mmap addr: 0x1612010
Segmentation fault (core dumped)

O GDB indica a linha exata em que ocorreu a falha de segmentação, que é o que a maioria dos usuários deseja durante a depuração:

gdb -q -nh main.out core

então:

Reading symbols from main.out...done.
[New LWP 27479]
Core was generated by `./main.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
7           *(int*)(NULL) = i;
(gdb) bt
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
#1  0x000000000040072b in main (argc=1, argv=0x7ffec6739328) at main.c:28

o que nos aponta diretamente para a linha de buggy 7.

Os argumentos da CLI são armazenados no arquivo principal e não precisam ser passados ​​novamente

Para responder às perguntas específicas dos argumentos da CLI, vemos que, se alterarmos os argumentos cli, por exemplo, com:

rm -f core
./main.out 1 2

então isso se reflete no bactrace anterior sem nenhuma alteração em nossos comandos:

Reading symbols from main.out...done.
[New LWP 21838]
Core was generated by `./main.out 1 2'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
7           *(int*)(NULL) = i; /* line 7 */
(gdb) bt
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
#1  0x0000564583cf2858 in main (argc=3, argv=0x7ffcca4effa8) at main.c:2

Então, observe como agora argc=3. Portanto, isso deve significar que o arquivo principal armazena essas informações. Suponho que apenas armazene como argumentos de main, assim como armazena os argumentos de quaisquer outras funções.

Isso faz sentido se você considerar que o dump principal deve armazenar toda a memória e o estado do registro do programa e, portanto, possui todas as informações necessárias para determinar o valor dos argumentos da função na pilha atual.

Menos óbvia é como inspecionar as variáveis ​​de ambiente: Como obter variáveis ​​de ambiente de um dump principal As variáveis ​​de ambiente também estão presentes na memória para que o objdump contenha essas informações, mas não tenho certeza de como listar todas elas de uma só vez. , um por um da seguinte forma funcionou nos meus testes:

p __environ[0]

Análise de Binutils

Usando ferramentas binutils como readelfe objdump, podemos despejar em massa as informações contidas no corearquivo, como o estado da memória.

A maioria / tudo isso também deve estar visível no GDB, mas essas ferramentas binutils oferecem uma abordagem mais em massa, conveniente para certos casos de uso, enquanto o GDB é mais conveniente para uma exploração mais interativa.

Primeiro:

file core

nos diz que o corearquivo é realmente um arquivo ELF :

core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './main.out'

é por isso que somos capazes de inspecioná-lo mais diretamente com as ferramentas binutils comuns.

Uma rápida olhada no padrão ELF mostra que existe realmente um tipo ELF dedicado a ele:

Elf32_Ehd.e_type == ET_CORE

Informações adicionais sobre o formato podem ser encontradas em:

man 5 core

Então:

readelf -Wa core

fornece algumas dicas sobre a estrutura do arquivo. A memória parece estar contida nos cabeçalhos regulares do programa:

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  NOTE           0x000468 0x0000000000000000 0x0000000000000000 0x000b9c 0x000000     0
  LOAD           0x002000 0x0000000000400000 0x0000000000000000 0x001000 0x001000 R E 0x1000
  LOAD           0x003000 0x0000000000600000 0x0000000000000000 0x001000 0x001000 R   0x1000
  LOAD           0x004000 0x0000000000601000 0x0000000000000000 0x001000 0x001000 RW  0x1000

e há mais alguns metadados presentes em uma área de anotações, principalmente prstatuso PC :

Displaying notes found at file offset 0x00000468 with length 0x00000b9c:
  Owner                 Data size       Description
  CORE                 0x00000150       NT_PRSTATUS (prstatus structure)
  CORE                 0x00000088       NT_PRPSINFO (prpsinfo structure)
  CORE                 0x00000080       NT_SIGINFO (siginfo_t data)
  CORE                 0x00000130       NT_AUXV (auxiliary vector)
  CORE                 0x00000246       NT_FILE (mapped files)
    Page size: 4096
                 Start                 End         Page Offset
    0x0000000000400000  0x0000000000401000  0x0000000000000000
        /home/ciro/test/main.out
    0x0000000000600000  0x0000000000601000  0x0000000000000000
        /home/ciro/test/main.out
    0x0000000000601000  0x0000000000602000  0x0000000000000001
        /home/ciro/test/main.out
    0x00007f8d939ee000  0x00007f8d93bae000  0x0000000000000000
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93bae000  0x00007f8d93dae000  0x00000000000001c0
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93dae000  0x00007f8d93db2000  0x00000000000001c0
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93db2000  0x00007f8d93db4000  0x00000000000001c4
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93db8000  0x00007f8d93dde000  0x0000000000000000
        /lib/x86_64-linux-gnu/ld-2.23.so
    0x00007f8d93fdd000  0x00007f8d93fde000  0x0000000000000025
        /lib/x86_64-linux-gnu/ld-2.23.so
    0x00007f8d93fde000  0x00007f8d93fdf000  0x0000000000000026
        /lib/x86_64-linux-gnu/ld-2.23.so
  CORE                 0x00000200       NT_FPREGSET (floating point registers)
  LINUX                0x00000340       NT_X86_XSTATE (x86 XSAVE extended state)

objdump pode facilmente despejar toda a memória com:

objdump -s core

que contém:

Contents of section load1:

 4007d0 01000200 73747269 6e672069 6e207465  ....string in te
 4007e0 78742073 65676d65 6e740074 65787420  xt segment.text 

Contents of section load15:

 7ffec6739220 73747269 6e672069 6e206461 74612073  string in data s
 7ffec6739230 65676d65 6e740000 00a8677b 9c6778cd  egment....g{.gx.

Contents of section load4:

 1612010 73747269 6e672069 6e206d6d 61702073  string in mmap s
 1612020 65676d65 6e740000 11040000 00000000  egment..........

que corresponde exatamente ao valor stdout em nossa execução.

Isso foi testado no Ubuntu 16.04 amd64, GCC 6.4.0 e binutils 2.26.1.

Ciro Santilli adicionou uma nova foto
fonte
10

No tutorial do depurador GDB do RMS :

prompt > myprogram
Segmentation fault (core dumped)
prompt > gdb myprogram
...
(gdb) core core.pid
...

Verifique se o arquivo é realmente uma coreimagem - verifique usando file.

Brian Cain
fonte
9

Uma abordagem ligeiramente diferente permitirá que você pule completamente o GDB. Se tudo o que você deseja é um backtrace, o utilitário específico do Linux 'catchsegv' captura o SIGSEGV e exibe um backtrace.

Rob Latham
fonte
3

Não importa se o executável tem argumentos ou não. Para executar o GDB em qualquer binário com um arquivo principal gerado, a sintaxe está abaixo.

Syntax:
gdb <binary name> <generated core file>
Eg:
gdb l3_entity 6290-corefile

Deixe-me dar o exemplo abaixo para mais compreensão.

bash-4.1$ **gdb l3_entity 6290-corefile**

**Core was generated** by `/dir1/dir2/dir3/l3_entity **Program terminated with signal SIGABRT, Aborted.**
#0
#1
#2
#3
#4
#5
#6
#7
#8
#9
#10
(gdb)

A partir da saída acima, você pode adivinhar algo sobre o núcleo, seja um acesso NULL, SIGABORT, etc.

Esses números de 0 a 10 são os quadros de pilha do GDB. Esses quadros de pilha não são do seu binário. Nos quadros 0 a 10 acima, se você suspeitar de algo errado, selecione esse quadro

(gdb) frame 8

Agora, para ver mais detalhes sobre isso:

(gdb) list +

Para investigar melhor o problema, você pode imprimir os valores de variáveis ​​suspeitos aqui neste momento.

(gdb) print thread_name
Venkatakrishna Kalepalli
fonte
0

Basta digitar o comando:

$ gdb <Binary> <codeDump>

Ou

$ gdb <binary>

$ gdb) core <coreDump>

Não há necessidade de fornecer nenhum argumento de linha de comando. O dump de código gerado devido a um exercício anterior.

KB Arora
fonte
-1

Você pode analisar o arquivo de despejo principal usando o comando "gdb".

 gdb - The GNU Debugger

 syntax:

 # gdb executable-file core-file

 example: # gdb out.txt core.xxx 
Ranjithkumar T
fonte
1
out.txt é um arquivo executável? Parece uma extensão de arquivo enganosa.
Alan