내 프로그램은 다음과 같이 작동합니다.
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 :
- GDB는 앞에서 언급 한 실패한 줄을 찾는 데 사용될 수 있습니다. 명령 줄 매개 변수가있을 때 GDB로 프로그램의 코어 덤프 파일을 어떻게 분석합니까?
- 코어 파일에는 CLI 인수가 포함되어 있으며 다시 전달할 필요가 없습니다.
objdump -s core
대량으로 메모리를 덤프하는 데 사용할 수 있습니다
이제 전체 교육 시험 설정을 위해 :
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 분석
readelf
and 같은 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
노트 영역에 더 많은 메타 데이터가 있으며 특히 prstatus
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
다음을 사용하여 모든 메모리를 쉽게 덤프 할 수 있습니다.
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