如何在 Linux 中查看核心文件以进行调试?

如何在 Linux 中查看核心文件以进行调试?

我想在调试程序时查看核心文件的内容。如何查看核心文件的内容?

答案1

GDB 最小可运行示例

GDB 之前曾在以下网址提到过:https://unix.stackexchange.com/a/89934/32558考虑投票赞成该答案。

简单的c

int myfunc(int i) {
    *(int*)(0) = i;
    return i - 1;
}

int main(int argc, char **argv) {
    (void)argv;
    int i = argc * 2;
    int ret = myfunc(i);
    return ret;
}

编译、运行生成core:

gcc -ggdb3 -std=c99 -Wall -Wextra -pedantic -o simple.out simple.c

要生成核心文件,我们首先必须在当前终端中运行:

ulimit -c unlimited

这意味着“转储核心文件没有任何大小限制”。之所以存在这种情况,是因为核心文件包含崩溃进程的整个内存,因此它们可能非常大。

从 Ubuntu 16.04 开始测试,你必须删除预先存在的核心文件(TODO 强制?我忘了):

rm -f core

从 Ubuntu 22.04 开始测试,您需要对抗 apport 才能获取核心文件:https://askubuntu.com/questions/1349047/where-do-i-find-core-dump-files-and-how-do-i-view-and-analyze-the-backtrace-st/1442665#1442665例如:

echo 'core' | sudo tee /proc/sys/kernel/core_pattern

然后我们运行程序:

./simple.out

终端包含:

Segmentation fault (core dumped)

核心文件已经生成。在 Ubuntu 16.04 上,该文件的名称为:

core

在 Ubuntu 22.04 上,echo 'core' | sudo tee /proc/sys/kernel/core_pattern文件命名为:

core.<pid>

其中 PID 是进程 ID,一个数字,例如:

core.162152

我认为这是因为 Linux 内核更新开始添加.pid后缀。待办事项确认。

我们现在可以使用核心文件作为

gdb simple.out core
gdb simple.out core.162152

现在我们进入一个 GDB 会话,这与程序崩溃时的情况完全相同,当然我们不能“继续运行”,因为程序即将结束:

#0  0x0000557097e0813c in myfunc (i=2) at simple.c:2
2           *(int*)(0) = i; /* line 7 */
(gdb) bt
#0  0x0000557097e0813c in myfunc (i=2) at simple.c:2
#1  0x0000557097e0816b in main (argc=1, argv=0x7ffcffc4ba18) at simple.c:9
(gdb) up
#1  0x0000557097e0816b in main (argc=1, argv=0x7ffcffc4ba18) at simple.c:9
9           int ret = myfunc(i);
(gdb) p argc
$1 = 1

因此,运行后bt,我们立即了解代码崩溃时的位置,这有时足以解决错误。

从示例中可以看出,您现在可以在崩溃时检查程序内存以尝试确定故障原因,进程虚拟内存完全包含在核心文件中。

在 Ubuntu 16.04 和 22.04 amd64 中测试。

也可以直接通过GDB运行程序

如果问题很容易重现(即快速且确定性地崩溃),并且您可以轻松控制命令行(即不是由您不希望/无法修改的另一个程序调用的程序),那么最好的方法就是通过GDB运行程序:

gdb -ex run simple.out

当接收到信号时,GDB 默认会在信号原因处中断,我们将处于与使用核心文件时完全相同的情况。

直接 Binutils 分析

让我们尝试在没有 GDB 的情况下观察 core 文件的内容,以便更好地理解它。因为我们可以。

让我们创建一个程序来打印它自己的一些内存地址,以便我们可以将事物关联起来:

主程序

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

程序输出:

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

第一的:

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

它与我们运行中的标准输出值完全匹配。

在 Ubuntu 16.04 amd64、GCC 6.4.0、binutils 2.26.1 中测试。

Mozillarr逆向调试是终极“核心文件”

核心文件允许您检查中断时的堆栈。

但总的来说,您真正需要做的是及时返回以进一步确定根本故障原因。

令人惊叹的 Mozilla rr 允许您做到这一点,但代价是跟踪文件更大,并且性能略有下降。

示例位于:https://stackoverflow.com/questions/1470434/how-does-reverse-debugging-work/53063242#53063242

也可以看看

答案2

gdb 是 GNU 调试器,可用于检查核心文件。 BTW bt(backtrace)是一个有用的 gdb 命令,用于检查程序调用堆栈。

gdb binary-file core-file

答案3

如果更喜欢使用命令行工具,那么您可以使用数据库:

gdb <program> <core file>

或者

gdb <program> -c <core file>

如果你喜欢gui,那么安装滴滴,然后从那里打开要调试的程序和核心文件。

答案4

#-------------------------------------------------------------------------
#!/usr/bin/ksh
# -------------------------------------------------------------------------

_OUTFILE=XXXX-XXXX-Audit-`date +"%Y%m%d%H%M"`.log
>$_OUTFILE
MAILLIST=""
COREPATH=$PKMS/logs/cores
MARKER=$COREPATH/marker

function Parse
{
   while getopts :p:u:s:l: name
      do
    case $name in
        p) PKMS="$OPTARG" ;;       # $PKMS
        u) DBUSER="$OPTARG" ;;     # $DBUSER 
        s) DBPSWD="$OPTARG" ;;     # $DBPSWD
        l) DBLOCN="$OPTARG" ;;     # $DBLOC 
        *) Usage ;;                     # display usage and exit
       esac
      done
   if [[ -z "${PKMS}"  || -z "${DBUSER}" || -z "${DBPSWD}" || -z "${DBLOCN}" ]] 
   then
    echo $Usage
    exit -1
   fi
}


function getCoreDumps
{
   COREFILES=$COREPATH/newcores.txt
   STACKS=$COREPATH/stacks.txt
   DATE=$(date +%y%m%d%H%M%S)
   >$COREFILES
   >$STACKS
   umask 002

   find $COREPATH -type f -newer $MARKER -name "core" > $COREFILES
   find $COREPATH -type f -newer $MARKER -name "core.?" >> $COREFILES

   rm $STACKS 2>/dev/null

   for i in $(<$COREFILES)
   do
        mv $i $i.$DATE
        chmod g+r,g+w $i.$DATE
        #echo "Coredump recently found at" `date` '\n'>> $STACKS
        echo $i.$DATE >> $STACKS
    #echo >> $STACKS
   done

   NL=$(wc -l $COREFILES  | awk '{ print $1 }')
   if [ "$NL" -gt 0 ]
   then
    echo "New CORE files found:" >> $_OUTFILE
    echo "--- ---- ----- ------" >> $_OUTFILE
    cat $STACKS >> $_OUTFILE
   else
    echo "No new CORE files found" >> $_OUTFILE
    echo "-- --- ---- ----- -----" >> $_OUTFILE
   fi

}



#/usr/bin/clear

echo "\t\t\t\t---------------------------------\t" >> $_OUTFILE
echo "\t\t\t\t
echo "\t\t\t\t---------------------------------\t" >> $_OUTFILE

date "+                             %d/%m/%Y %H:%M:%S"  >> $_OUTFILE

echo "===================" >> $_OUTFILE
echo " APPICATION MACHINES" >> $_OUTFILE
echo "===================" >> $_OUTFILE
echo >> $_OUTFILE
echo >> $_OUTFILE



getCoreDumps
echo >> $_OUTFILE
echo >> $_OUTFILE



echo "===================" >> $_OUTFILE
echo "XXXX APP DataBase Info" >> $_OUTFILE
echo "===================" >> $_OUTFILE

echo >> $_OUTFILE
getAPPDBInfo
echo >> $_OUTFILE
echo >> $_OUTFILE

MAILDATE=$(date +%d/%m/%Y)


mailx -s "XXXX Monitor Log for $PKMS Environment - Dated $MAILDATE" $MAILLIST < $_OUTFILE

touch $MARKER
rm /tmp/XXXXtempOUTFILE
exit 0

相关内容