69

这可能是一个非常简单的问题,我正在尝试调试在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 = 指令指针
  • 在 = ???
  • myapp[8048000+24000] = 符号地址?
4

3 回答 3

52

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

运行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你的平台。
于 2010-03-31T00:21:11.093 回答
37

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

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

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

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

  • 位 0 == 0 表示未找到页面,1 表示保护故障
  • 位 1 == 0 表示读取,1 表示写入
  • bit 2 == 0 表示内核,1 表示用户模式

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

  • 位 3 == 1 表示错误是指令获取
于 2010-02-01T19:33:23.960 回答
7

如果是共享库

不幸的是,你被水洗了;事后动态链接器无法知道库在内存中的位置

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

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

链接.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 具有符号的概念。

注意:脚本相当不完整,我怀疑您可能会添加到 add-symbol-file 的第二个参数中,并使用此值打印总和:

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

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

希望能帮助到你

于 2012-06-06T17:14:26.600 回答