回答:
コアは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)
manページは、他の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:
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
その後、これはコマンドに変更を加えずに前のバクトレースに反映されます。
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
およびのようなbinutilsツールを使用することにより、メモリの状態などobjdump
のcore
ファイルに含まれる情報を一括ダンプできます。
ほとんど/すべてが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
exe
がfirefox
Linuxのようなシェルスクリプト(いくつかの変数を設定するなど)ではないことを確信していますか?