您如何阅读 segfault 内核日志消息

Posted

技术标签:

【中文标题】您如何阅读 segfault 内核日志消息【英文标题】:How do you read a segfault kernel log message 【发布时间】:2011-01-11 20:56:03 【问题描述】:

这可能是一个非常简单的问题,我正在尝试调试在kern.log 中生成以下段错误的应用程序

kernel: myapp[15514]: segfault at 794ef0 ip 080513b sp 794ef0 error 6 in myapp[8048000+24000]

这是我的问题:

    是否有任何文档说明 segfault 上的 diff 错误编号是多少,在这种情况下是错误 6,但我看到了错误 4、5

    信息at bf794ef0 ip 0805130b sp bf794ef0 and myapp[8048000+24000]是什么意思?

到目前为止,我能够使用符号进行编译,当我执行x 0x8048000+24000 时,它会返回一个符号,这是正确的做法吗?到目前为止,我的假设如下:

sp = 堆栈指针? ip = 指令指针 在 = ???? myapp[8048000+24000] = 符号地址?

【问题讨论】:

添加了 [unix] [segmentation-fault] [syslog] 标签。 您好,您找到任何有用的信息了吗?我被卡住了。 【参考方案1】:

当报告指向一个程序,而不是一个共享库时

运行addr2line -e myapp 080513b(并重复给定的其他指令指针值)以查看错误发生的位置。更好的是,获取带有调试工具的构建,然后在 gdb 等调试器下重现问题。

如果是共享库

libfoo.so[NNNNNN+YYYY] 部分,NNNNNN 是加载库的位置。从指令指针 (ip) 中减去它,您将获得指向违规指令的 .so 的偏移量。然后您可以使用objdump -DCgl libfoo.so 并在该偏移处搜索指令。您应该能够很容易地从 asm 标签中找出它是哪个函数。如果.so 没有优化,您也可以尝试使用addr2line -e libfoo.so <offset>

错误是什么意思

以下是字段的细分:

address - 代码试图访问的内存位置(1011 可能是我们希望设置为有效值但指向 0 的指针的偏移量) ip - 指令指针,即。尝试执行此操作的代码所在的位置 sp - 堆栈指针 error - 特定于架构的标志;请参阅 arch/*/mm/fault.c 了解您的平台。

【讨论】:

共享库的事件,“[8048000+24000]”部分应该提示库的崩溃段映射到内存中的位置。 “readelf --segments mylib.so”列出了这些段,然后您可以计算崩溃段的 EIP 偏移量并将其提供给 addr2line(或在“objdump -dgS”中查看)。 我相信 0x8048000 是(可能)文本段映射的地址,因此您需要将-j .text 传递给objdump 命令。 (至少,这是我刚才诊断其中之一时所需要的。) @Charles Duffy 如果我见到你,我会拥抱你,就像我从未拥抱过一个活生生的灵魂一样。 在 64 位 Debian 系统上,我有 trap divide error ip:55fc4735e6d8 sp:7fa4d77fcd80 error:0 in myapp[55fc472d4000+ed000],但 addr2line -e myapp 55fc4735e6d8 只返回 ??:0。该应用程序是未启用优化的调试版本。有什么建议吗? 注意:我也试过addr2line -e myapp 8a6d8(因为 55fc4735e6d8 - 55fc472d4000 = 8a6d8),它确实找到了源代码行,但显然是不正确的,因为那里根本没有发生任何分裂。 (我遇到了陷阱分割错误。)【参考方案2】:

根据我有限的知识,你的假设是正确的。

sp = 堆栈指针 ip = 指令指针 myapp[8048000+24000] = 地址

如果我正在调试问题,我会修改代码以生成核心转储或在崩溃时记录stack backtrace。您也可以在(或附加)GDB 下运行该程序。

错误代码只是页面错误的架构错误代码,似乎是特定于架构的。它们通常记录在内核源代码的arch/*/mm/fault.c 中。我的Linux/arch/i386/mm/fault.c 副本对error_code 有以下定义:

bit 0 == 0 表示没有找到页面,1 表示保护错误 bit 1 == 0 表示读,1 表示写 bit 2 == 0 表示内核,1 表示用户模式

我的Linux/arch/x86_64/mm/fault.c 副本添加了以下内容:

位 3 == 1 表示错误是指令提取

【讨论】:

我遇到的问题是:1)应用程序在生产环境中出现段错误,其中符号被剥离,我所拥有的只是日志 2)我试图在开发环境,所以至少我可以看到它在哪里崩溃。 如果您有预剥离的二进制文件,请尝试通过 nm 或 objdump 运行它。 nm 很有帮助,至少我知道崩溃发生在哪里。最后一件事,什么是错误 6? ...外面有桌子吗? segfault at 794ef0 ... sp 794ef0 - 堆栈明显损坏。 谢谢,这很有帮助【参考方案3】:

如果是共享库

不幸的是,你被水洗了;不可能知道在哪里 动态链接器事后将库放置在内存中

好吧,仍然有可能检索信息,而不是从二进制文件中,而是从对象中。但是你需要对象的基地址。而且这些信息仍然在 coredump 中,在 link_map 结构中。

所以首先你想把 struct link_map 导入 GDB。因此,让我们用它编译一个带有调试符号的程序并将其添加到 GDB。

link.c

#include <link.h>
toto()struct link_map * s = 0x400;

get_baseaddr_from_coredump.sh

#!/bin/bash

BINARY=$(which myapplication)

IsBinPIE ()

    readelf -h $1|grep 'Type' |grep "EXEC">/dev/null || return 0
    return 1


Hex2Decimal ()

    export number="`echo "$1" | sed -e 's:^0[xX]::' | tr '[a-f]' '[A-F]'`"
    export number=`echo "ibase=16; $number" | bc`


GetBinaryLength ()

    if [ $# != 1 ]; then
    echo "Error, no argument provided"
    fi
    IsBinPIE $1 || (echo "ET_EXEC file, need a base_address"; exit 0)
    export totalsize=0
    # Get PT_LOAD's size segment out of Program Header Table (ELF format)
    export sizes="$(readelf -l $1 |grep LOAD |awk 'print $6'|tr '\n' ' ')"
    for size in $sizes
    do Hex2Decimal "$size"; export totalsize=$(expr $number + $totalsize); export totalsize=$(expr $number + $totalsize)
    done
    return $totalsize


if [ $# = 1 ]; then
    echo "Using binary $1"
    IsBinPIE $1 && (echo "NOT ET_EXEC, need a base_address..."; exit 0)
    BINARY=$1
fi

gcc -g3 -fPIC -shared link.c -o link.so

GOTADDR=$(readelf -S $BINARY|grep -E '\.got.plt[ \t]'|awk 'print $4')

echo "First do the following command :"
echo file $BINARY
echo add-symbol-file ./link.so 0x0
read
echo "Now copy/paste the following into your gdb session with attached coredump"
cat <<EOF
set \$linkmapaddr = *(0x$GOTADDR + 4)
set \$mylinkmap = (struct link_map *) \$linkmapaddr
while (\$mylinkmap != 0)
if (\$mylinkmap->l_addr)
printf "add-symbol-file .%s %#.08x\n", \$mylinkmap->l_name, \$mylinkmap->l_addr
end
set \$mylinkmap = \$mylinkmap->l_next
end

它将在一组 GDB 命令中打印出整个 link_map 内容。

它本身可能看起来很不合理,但是使用我们所讨论的共享对象的 base_addr,您可以通过在另一个 GDB 实例中直接调试所涉及的共享对象来从地址中获取更多信息。 保持第一个 gdb 有符号的 idee。

注意:脚本相当不完整,我怀疑你可能 add 到 add-symbol-file 的第二个参数打印了这个值的总和:

readelf -S $SO_PATH|grep -E '\.text[ \t]'|awk 'print $5'

其中 $SO_PATH 是添加符号文件的 第一个参数

希望对你有帮助

【讨论】:

如何编译link.c?

以上是关于您如何阅读 segfault 内核日志消息的主要内容,如果未能解决你的问题,请参考以下文章

NGINX segfault问题定位

分段错误 - 如何调试? [关闭]

深入理解Linux文件------日志文件

浅析Linux文件日志文件详解

如何在日志中获取推送通知收到消息

程序退出时的 Segfault (-11)