关于c ++:您如何阅读段内核日志消息

How do you read a segfault kernel log message

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

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

以下是我的问题:

  • 是否有关于segfault上的差异错误号的文档,在本例中是错误6,但我看到了错误4、5

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

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

    • sp=堆栈指针?
    • IP=指令指针
    • 在=?????
    • 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

    错误意味着什么

    以下是字段的细分:

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


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

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

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

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

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

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

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


    If it's a shared library

    You're hosed, unfortunately; it's not possible to know where the
    libraries were placed in memory by the dynamic linker after-the-fact.

    嗯,仍然有可能检索信息,不是从二进制文件,而是从对象。但您需要对象的基地址。这些信息仍然在coredump中,在link-map结构中。

    因此,首先要将结构链接图导入gdb。因此,让我们用调试符号编译一个程序,并将其添加到gdb中。

    链接C

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

    从u coredump.sh获取u baseaddru

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    #!/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 '
    ' ' ')"

        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
    "
    , \$mylinkmap->l_name, \$mylinkmap->l_addr
    end
    set \$mylinkmap = \$mylinkmap->l_next
    end

    它将在一组gdb命令中打印整个链接映射内容。

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

    注意:脚本相当不完整,我怀疑您可能会在添加符号文件的第二个参数中添加用此值打印的和:

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

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

    希望它有帮助