您如何阅读段错内核日志消息

Sul*_*enx 61 c c++ unix syslog segmentation-fault

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

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

这是我的问题:

  1. 有没有关于segfault上的差异错误号的文档,在这个例子中它是错误6,但我看到错误4,5

  2. 这些信息的含义是at bf794ef0 ip 0805130b sp bf794ef0 and myapp[8048000+24000]什么?

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

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

Cha*_*ffy 40

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

运行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你的平台.

  • 就我而言,将 addr2line 与可执行文件一起使用仅返回 `??:0` (即使在使用调试信息重新编译可执行文件之后)您能否澄清正确的输出需要什么? (2认同)

jsc*_*ier 36

根据我的有限知识,您的假设是正确的.

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

如果我正在调试问题,我会修改代码以生成核心转储或在崩溃时记录堆栈回溯.您也可以在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添加了以下内容:

  • bit 3 == 1表示故障是取指令

  • 段错误在794ef0 ... sp 794ef0 - 堆栈显然已损坏. (3认同)

小智 6

如果它是共享库

不幸的是,你已经被软管了; 事后,动态链接器无法知道库在内存中的位置.

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

首先,您要将struct link_map导入GDB.因此,让我们使用调试符号编译程序并将其添加到GDB.

link.c

#include <link.h>
toto(){struct link_map * s = 0x400;}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

它本身似乎是unnesseray,但是使用我们所关注的共享对象的base_addr,您可以通过直接在另一个GDB实例中调用所涉及的共享对象来从地址中获取更多信息.保持第一个gdb具有符号的idee.

注意:脚本相当不完整我怀疑你可能会添加 add-symbol-file的第二个参数打印带有此值的总和:

readelf -S $SO_PATH|grep -E '\.text[ \t]'|awk '{print $5}'
Run Code Online (Sandbox Code Playgroud)

其中$ SO_PATH是add-symbol-file 的第一个参数

希望能帮助到你