如何将核心文件与其关联的可执行文件捆绑在一起



如何将核心文件与其关联的可执行文件和共享库捆绑在一起?

当程序崩溃时,它会生成一个核心文件,我可以使用 gdb 对其进行调试。但是,如果有人出现在我身后并"有用地"重新编译程序并打开了额外的调试,或升级软件包,或者以任何方式弄乱了系统,那么该核心文件将变得毫无用处。

所以我想要的是一种将核心文件与它引用的所有其他二进制文件捆绑成一个大文件的方法。

然后,当然,我还需要一种方法在 gdb 中打开此文件。我不想将文件"提取"回其原始位置并覆盖升级或更改的二进制文件。我正在想象一个 shell 脚本,它将二进制文件提取到临时目录,然后告诉 gdb 查看那里。

gdb 已经有你想要的信息(info sharedlib):

$ gdb -ex 'set height 0' -ex 'set confirm off' 
  -ex 'file /path/to/exe' -ex 'core-file core.pid' 
  -ex 'info sharedlib' -ex quit 

所以很自然地,你可以让 gdb 给你这个列表,从那里你可以创建一个"gdb-bundle"压缩包,其中包含可执行文件和 gdb 报告的所有共享库。

我写了一个脚本来自动化此操作:

#!/bin/sh
me=$(basename $0)
usage() {
    echo "Usage:
  $me -p <pid>
  $me <executable> <core>
DESCRIPTION
  $me - Creates a tarball containing the executable, it's core dump and
        all the shared libraries that gdb said it loads.
OPTIONS
    -p <pid>  A running process id of a process to be bundled.
    -h        Show this help message"
}
pid=
while getopts hp: opt
do
    case "$opt" in
        p)
            pid="$OPTARG"
            ;;
        h)
            usage
            exit
            ;;
        ?)
            echo Unknown option
            exit
            ;;
    esac
done
shift $(($OPTIND -1))
executable=$1
corename=$2
if [ -n "$pid" ]; then
    test "$pid" -gt 0 || { echo "pid must be numeric"; exit 1; }
    proc=/proc/$pid/exe
    executable=`readlink -e $proc` ||
        { echo "Could not readlink $proc"; exit 1; }
    corename=${basename}.$pid.core
else
    test -z "$executable" && usage && exit 1;
    test -z "$corename" && usage && exit 1;
fi
basename=$(basename $executable)
if [ -n "$pid" ]; then
    sharedlibs=$(gdb -ex "attach $pid" -ex 'set height 0' 
        -ex 'set confirm off' -ex "generate-core-file $corename" 
        -ex 'info sharedlib' -ex quit|
        sed -n '/Shared Object Library/,/^(/p'|grep -E '(Yes|No)'|
        sed -e 's,[^/]+,,') || exit 1
    dir="gdb-${basename}.$pid.$(date +%F-%H%M%S)"
else
    sharedlibs=$(gdb -ex 'set height 0' -ex 'set confirm off' 
        -ex "file $executable" -ex "core-file $corename" 
        -ex 'info sharedlib' -ex quit|
        sed -n '/Shared Object Library/,/^(/p'|grep -E '(Yes|No)'|
        sed -e 's,[^/]+,,') || exit 1
    dir="gdb-${basename}.$(date +%F-%H%M%S)"
fi
mkdir "$dir" && cp "$corename" "$dir" &&
tar chf - $sharedlibs $executable|tar -C $dir -xf - &&
echo -e "gdb:ntgdb -ex 'set solib-absolute-prefix ./'" 
    "-ex 'file .$executable' -ex 'core-file ./$corename' " 
    > $dir/makefile &&
echo tar czf $dir.tar.gz $dir &&
tar czf $dir.tar.gz $dir

您可以捕获 SIGSEGV,然后编写一个子进程以将相关的进程文件(使用 lsof)复制到临时目录中。

首先找到与核心关联的二进制文件。您有以下几种选择:

  1. 将系统配置为在导致它们的二进制文件之后命名核心,并将二进制文件安装在脚本可以从中获取它们的标准位置。有关内核命名的说明,请参见 core(5)。

  2. 如果您无法配置系统,我想出的最好的方法是在内核上运行字符串,然后对于每个字符串,这是一个有效的路径,在其上运行"file"并 grepping 为"ELF"和"可执行文件"。这可能会给你错误的答案,特别是如果二进制文件是由另一个实用程序(如make)调用的,它们可能会出现在核心中,所以你可以添加额外的过滤器,比如排除/bin和/usr/bin中的路径,如果你知道你的二进制文件总是在/opt中,并选择具有最新mtime的二进制文件(较新的软件更有可能是核心的,更有可能是你的比系统附带的软件)。

  3. 一个补充技巧是在字符串输出中查找"_=/your/binary/here"——"_"是一个特殊的环境变量,由一些 shell/WMs/apps 设置为它们正在调用的二进制文件。它也可能具有误导性(例如,当调用您的二进制文件时,它将是/usr/bin/make)。

其次,收集二进制文件的依赖项:

  1. 再次 Grep 内核输出的字符串,用于 LD_PRELOAD 和 LD_LIBRARY_PATH 的值。这些可能会影响二进制文件实际链接的库。
  2. 在二进制文件上运行"ldd",将这些变量设置为相同的值,并在"=>"上拆分以查找每个库的文件名。

然后将其全部复制到一个目录中并在其上运行 tar。将日志文件复制到目录中通常也很有帮助。

最新更新