[linux] 명령 줄 매개 변수가있을 때 GDB로 프로그램의 코어 덤프 파일을 분석하려면 어떻게합니까?

내 프로그램은 다음과 같이 작동합니다.

exe -p param1 -i param2 -o param3

충돌하여 코어 덤프 파일을 생성했습니다 core.pid.

코어 덤프 파일을 분석하고 싶습니다

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

그러나 GDB는 EXE 파일의 매개 변수를 GDB의 입력으로 인식합니다.

이 상황에서 코어 덤프 파일을 어떻게 분석합니까?



답변

여러 가지 방법으로 GDB와 함께 코어를 사용할 수 있지만 실행 파일로 전달 될 매개 변수를 GDB에 전달하는 것은 코어 파일을 사용하는 방법이 아닙니다. 이것은 또한 그 오류가 발생한 이유 일 수 있습니다. 다음과 같은 방법으로 코어 파일을 사용할 수 있습니다.

gdb <executable> <core-file>또는 gdb <executable> -c <core-file>또는

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

핵심 파일을 사용할 때 인수를 전달할 필요가 없습니다. 충돌 시나리오는 GDB에 표시됩니다 (Ubuntu의 GDB 버전 7.1로 확인).

예를 들면 다음과 같습니다.

$ ./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)

GDB에서 디버깅 할 실행 파일에 매개 변수를 전달하려면을 사용하십시오 --args.

예를 들면 다음과 같습니다.

$ 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)

매뉴얼 페이지는 다른 GDB 옵션을 보는 데 도움이 될 것입니다.


답변

코어 덤프 파일을 디버그하기위한 GDB의 간단한 사용법 :

gdb <executable_path> <coredump_file_path>

“프로세스”에 대한 코어 덤프 파일은 “core.pid”파일로 작성됩니다.

위 명령을 실행할 때 GDB 프롬프트에 들어간 후 다음을 입력하십시오.

...
(gdb) where

그러면 충돌 / 결함의 원인을 분석 할 수있는 스택 정보가 표시됩니다.
동일한 목적을위한 다른 명령 은 다음과 같습니다.

...
(gdb) bt full

위와 동일합니다. 일반적으로 전체 스택 정보를 나열합니다 (결과적으로 충돌 위치로 이어짐).


답변

매개 변수를 건너 뛰십시오. GDB는 필요하지 않습니다 :

gdb ./exe core.pid


답변

objdump+ gdb최소한의 실행 가능한 예

TL; DR :

이제 전체 교육 시험 설정을 위해 :

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);
}

컴파일하고 실행하여 코어를 생성하십시오.

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

산출:

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

GDB는 세그멘테이션 오류가 발생한 정확한 행을 가리키며, 이는 디버깅하는 동안 대부분의 사용자가 원하는 것입니다.

gdb -q -nh main.out core

그때:

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

버기 라인 7로 바로 연결됩니다.

CLI 인수는 코어 파일에 저장되며 다시 전달할 필요가 없습니다.

특정 CLI 인수 질문에 대답하기 위해 cli 인수를 변경하면 다음과 같이 나타납니다.

rm -f core
./main.out 1 2

그러면 명령 변경없이 이전 bactrace에 반영됩니다.

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

이제 어떻게하는지 주목하십시오 argc=3. 따라서 이는 핵심 파일이 해당 정보를 저장함을 의미해야합니다. 나는 그것이 단지의 인수로 저장 같은데요 main은 다른 함수의 인수를 저장처럼.

코어 덤프가 전체 메모리를 저장하고 프로그램의 레지스터 상태를 저장해야하므로 현재 스택에서 함수 인수의 값을 결정하는 데 필요한 모든 정보가 있어야합니다.

덜 분명한 것은 환경 변수를 검사하는 방법입니다. 코어 덤프에서 환경 변수를 가져 오는 방법 환경 변수 도 메모리에 존재 하므로 objdump는 해당 정보를 포함하지만 모든 변수 를 한 번에 나열하는 방법을 잘 모르겠습니다. 다음과 같이 하나씩 테스트를 수행했습니다.

p __environ[0]

Binutils 분석

readelfand 같은 binutils 도구를 사용 하여 메모리 상태와 같은 파일에 objdump포함 된 정보를 대량 덤프 할 수 있습니다 core.

GDB는 대부분 / 모두 GDB를 통해 볼 수 있어야하지만 이러한 binutils 도구는 특정 사용 사례에 편리한보다 대량 접근 방식을 제공하는 반면 GDB는보다 대화식 탐색에 더 편리합니다.

먼저:

file core

core파일이 실제로 ELF 파일 임을 알려줍니다 .

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

그렇기 때문에 일반적인 binutils 도구를 사용하여 더 직접 검사 할 수 있습니다.

ELF 표준을 간단히 살펴보면 실제로 ELF 유형이 전용이라는 것을 알 수 있습니다.

Elf32_Ehd.e_type == ET_CORE

추가 형식 정보는 다음에서 찾을 수 있습니다.

man 5 core

그때:

readelf -Wa core

파일 구조에 대한 힌트를 제공합니다. 메모리는 일반 프로그램 헤더에 포함 된 것으로 보입니다.

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

노트 영역에 더 많은 메타 데이터가 있으며 특히 prstatusPC를 포함합니다 .

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 다음을 사용하여 모든 메모리를 쉽게 덤프 할 수 있습니다.

objdump -s core

포함하는:

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

런에서 stdout 값과 정확히 일치합니다.

이것은 Ubuntu 16.04 amd64, GCC 6.4.0 및 binutils 2.26.1에서 테스트되었습니다.


답변

에서 RMS의 GDB 디버거 자습서 :

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

파일이 실제로 core이미지 인지 확인하십시오 file.를 사용하여 확인하십시오 .


답변

약간 다른 접근 방식을 사용하면 GDB를 완전히 건너 뛸 수 있습니다. 원하는 모든 것이 역추 적인 경우 Linux 특정 유틸리티 ‘catchsegv’ 는 SIGSEGV를 포착하여 역 추적을 표시합니다.


답변

실행 파일에 인수가 있는지 여부는 중요하지 않습니다. 생성 된 코어 파일이있는 바이너리에서 GDB를 실행하려면 구문은 다음과 같습니다.

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

더 이해하기 위해 아래 예를 들어 보겠습니다.

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)

위의 출력에서 ​​NULL 액세스, SIGABORT 등 코어에 대해 추측 할 수 있습니다.

이 숫자 # 0 ~ # 10은 GDB의 스택 프레임입니다. 이 스택 프레임은 바이너리가 아닙니다. 위의 0-10 프레임에서 잘못된 것이 의심되면 해당 프레임을 선택하십시오.

(gdb) frame 8

이제 그것에 대한 자세한 내용을 보려면 :

(gdb) list +

문제를 자세히 조사하기 위해이 시점에서 의심되는 변수 값을 여기에 인쇄 할 수 있습니다.

(gdb) print thread_name