Jak analizować plik zrzutu pamięci programu za pomocą GDB, gdy ma on parametry wiersza poleceń?
Mój program działa tak:
exe -p param1 -i param2 -o param3
Rozbił się i wygenerował plik zrzutu pamięci, core.pid
.
Chcę przeanalizować plik zrzutu pamięci przez
gdb ./exe -p param1 -i param2 -o param3 core.pid
Ale GDB rozpoznaje parametry pliku EXE jako wejście GDB.
Jak przeanalizować plik zrzutu pamięci w takiej sytuacji?
9 answers
Możesz używać rdzenia z GDB na wiele sposobów, ale przekazywanie parametrów, które mają być przekazane do pliku wykonywalnego do GDB, nie jest sposobem użycia pliku core. To może być również powodem tego błędu. Możesz użyć pliku core w następujący sposób:
gdb <executable> <core-file>
lub gdb <executable> -c <core-file>
lub
gdb <executable>
...
(gdb) core <core-file>
Używając pliku core nie musisz podawać argumentów. Scenariusz awarii jest pokazany w GDB(sprawdzony z GDB w wersji 7.1 na Ubuntu).
Na przykład:
$ ./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)
Jeśli chcesz przekazuje parametry do pliku wykonywalnego do debugowania w GDB, używając --args
.
Na przykład:
$ 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)
Strony podręcznika będą pomocne, aby zobaczyć inne opcje GDB.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-03 00:24:15
Proste użycie GDB do debugowania plików coredump:
gdb <executable_path> <coredump_file_path>
Plik zrzutu rdzeniowego dla "procesu" jest tworzony jako "rdzeń".PID " plik.
Po wejściu do monitu GDB (po wykonaniu powyższego polecenia), wpisz:
...
(gdb) where
Dzięki temu uzyskasz informacje o stosie, w którym możesz przeanalizować przyczynę awarii/usterki. inne polecenie, do tych samych celów to:
...
(gdb) bt full
To jest to samo, co powyżej. Według konwencji, wymienia cały stos informacji (co ostatecznie prowadzi do miejsca katastrofy).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-03 00:29:25
Po prostu pomiń parametry. GDB ich nie potrzebuje:
gdb ./exe core.pid
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-03 00:25:06
objdump
+ gdb
minimal runnable przykład
TL; DR:
- GDB może być użyty do znalezienia błędnej linii, o której wcześniej wspomniano w: Jak analizować plik zrzutu pamięci programu za pomocą GDB, gdy ma on parametry wiersza poleceń?
- plik core zawiera argumenty CLI, nie trzeba ich przekazywać ponownie
- Może być używany do zrzutu pamięci masowo]}
Teraz pełny test edukacyjny konfiguracja:
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);
}
W tym celu należy wykonać następujące czynności:]}
gcc -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
ulimit -c unlimited
rm -f core
./main.out
Wyjście:
text addr: 0x4007d4
data addr: 0x7ffec6739220
mmap addr: 0x1612010
Segmentation fault (core dumped)
GDB wskazuje nam dokładną linię, w której wystąpił błąd segmentacji, czego chce większość użytkowników podczas debugowania:]}
gdb -q -nh main.out core
Potem:
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
Który wskazuje nas bezpośrednio na linię 7.
Argumenty CLI są przechowywane w pliku core i nie muszą być przekazywane ponownie
Aby odpowiedzieć na konkretne pytania argumentacji CLI, widzimy że jeśli zmienimy argumenty cli np. za pomocą:
rm -f core
./main.out 1 2
Wtedy to zostanie odzwierciedlone w poprzedniej bactrace bez żadnych zmian w naszych poleceniach:
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
Więc zauważ, jak teraz argc=3
. Dlatego musi to oznaczać, że plik core przechowuje te informacje. Domyślam się, że przechowuje je jako argumenty main
, tak jak przechowuje argumenty innych funkcji.
Ma to sens, jeśli weźmiemy pod uwagę, że zrzut pamięci musi przechowywać całą pamięć i rejestrować stan programu, a więc posiada wszystkie informacje potrzebne do określenia wartości argumentów funkcji na bieżącym stosie.
Mniej oczywiste jest, jak sprawdzić zmienne środowiskowe: jak pobrać zmienną środowiskową z zrzutu pamięci zmienne środowiskowe są również obecne w pamięci więc objdump zawiera te informacje, ale nie jestem pewien, jak wyświetlić wszystkie z nich za jednym razem wygodnie, jeden po drugim w następujący sposób działało na moich testach chociaż:
p __environ[0]
Analiza Binutils
Używając narzędzi binutils, takich jak readelf
i objdump
, możemy zbiorczo zrzucać informacje zawarte w pliku core
, takie jak stan pamięci.
Większość / wszystko musi być również widoczne przez GDB, ale te narzędzia binutils oferują bardziej masowe podejście, które jest wygodne dla niektórych przypadków użycia, podczas gdy GDB jest wygodniejsze dla bardziej interaktywnych eksploracji.
Pierwszy:
file core
Mówi nam, że plik core
jest aktualnie plik ELF:
core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './main.out'
Dlatego jesteśmy w stanie sprawdzić go bardziej bezpośrednio za pomocą zwykłych narzędzi binutils.
Szybkie spojrzenie na standard ELF pokazuje, że w rzeczywistości istnieje dedykowany do niego Typ ELF:
Elf32_Ehd.e_type == ET_CORE
Dalsze informacje o formacie można znaleźć na stronie:
man 5 core
Potem:
readelf -Wa core
Daje kilka wskazówek na temat struktury plików. Pamięć wydaje się być zawarta w zwykłych nagłówkach programu:
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
I jest jeszcze metadane obecne w obszarze notatek, w szczególności prstatus
zawiera komputer:
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
można łatwo zrzucić całą pamięć za pomocą:
objdump -s core
Który zawiera:
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..........
Który pasuje dokładnie do wartości stdout w naszym biegu.
To było testowane na Ubuntu 16.04 amd64, GCC 6.4.0 i binutils 2.26.1.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-02-03 09:24:16
From RMS ' s GDB debugger tutorial :
prompt > myprogram
Segmentation fault (core dumped)
prompt > gdb myprogram
...
(gdb) core core.pid
...
Upewnij się, że Twój plik naprawdę jest obrazem core
-- sprawdź go używając file
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-03 00:27:00
Nieco inne podejście pozwoli Ci całkowicie pominąć GDB. Jeśli potrzebujesz tylko backtrace, specyficzne dla Linuksa Narzędzie 'catchsegv' złapie SIGSEGV i wyświetli backtrace.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-03 00:31:03
Nie ma znaczenia, czy plik wykonywalny ma argumenty, czy nie. Aby uruchomić GDB na dowolnym pliku binarnym z wygenerowanym plikiem core, składnia jest poniżej.
Syntax:
gdb <binary name> <generated core file>
Eg:
gdb l3_entity 6290-corefile
Pozwolę sobie wziąć poniższy przykład, aby uzyskać więcej zrozumienia.
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)
Z powyższego wyjścia można się domyślić, czy jest to null access, SIGABORT, itp..
Te liczby od #0 do #10 są ramkami stosu GDB. Te ramki stosu nie są binarne. W powyższych klatkach 0 - 10 jeśli podejrzewasz coś złego select that frame
(gdb) frame 8
Teraz, aby zobaczyć więcej szczegółów na ten temat:
(gdb) list +
Aby dokładniej zbadać problem, możesz wydrukować podejrzane wartości zmiennych tutaj w tym momencie.
(gdb) print thread_name
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-03 00:34:54
Po prostu wpisz polecenie:
$ gdb <Binary> <codeDump>
Lub
$ gdb <binary>
$ gdb) core <coreDump>
Nie ma potrzeby podawania argumentów wiersza poleceń. Zrzut kodu wygenerowany w wyniku wcześniejszego ćwiczenia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-03 00:32:22
Plik zrzutu pamięci można analizować za pomocą polecenia "gdb".
gdb - The GNU Debugger
syntax:
# gdb executable-file core-file
example: # gdb out.txt core.xxx
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-03 00:27:33