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

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

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 =指令指针
  • at = ????
  • myapp [8048000 + 24000] =符号地址?

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

    运行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于您的平台的arch/*/mm/fault.c

  • 基于我有限的知识,你的假设是正确的。

  • sp =堆栈指针
  • ip =指令指针
  • myapp[8048000+24000] =地址
  • 如果我正在调试这个问题,我会修改代码以产生核心转储或在崩溃时记录栈回溯。 您也可以在GDB下运行该程序(或附加)。

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

  • 位0 == 0表示未找到页面,1表示保护错误
  • 位1 == 0表示读取,1表示写入
  • 位2 == 0表示内核,1表示用户模式
  • 我的Linux/arch/x86_64/mm/fault.c增加了以下内容:

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

  • 如果它是共享库

    不幸的是, 事后不可能知道动态链接器将库放置在内存中的什么位置

    那么,仍然有可能检索信息,而不是从二进制文件中,而是从对象中检索。 但是你需要对象的基地址。 这些信息仍然位于链接地图结构中的核心转储内。

    所以首先你要将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 %#.08xn", $mylinkmap->l_name, $mylinkmap->l_addr
    end
    set $mylinkmap = $mylinkmap->l_next
    end
    

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

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

    注:脚本是相当不完整的,我怀疑你可能会添加到第二个参数添加符号文件打印与此值的总和:

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

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

    希望能帮助到你

    链接地址: http://www.djcxy.com/p/86047.html

    上一篇: How do you read a segfault kernel log message

    下一篇: Line number of segmentation fault