这可能是一个非常简单的问题,我正在尝试调试在kern.log
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
时,它返回一个符号,这是正确的方法吗?到目前为止,我的假设如下:
答案 0 :(得分: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
- 代码尝试访问的内存中的位置(10
和11
可能是指向我们希望设置为有效值的指针的偏移量,但是而是指向0
)ip
- 指令指针,即。尝试执行此操作的代码所在的位置sp
- 堆栈指针error
- 特定于架构的标志;请参阅arch/*/mm/fault.c
了解您的平台。答案 1 :(得分:36)
根据我的有限知识,您的假设是正确的。
sp
=堆栈指针ip
=指令指针myapp[8048000+24000]
=地址如果我正在调试问题,我会修改代码以生成核心转储或在崩溃时记录stack backtrace。您也可以在(或附加)GDB下运行该程序。
错误代码只是页面错误的架构错误代码,似乎是特定于体系结构的。它们通常记录在内核源代码中的arch/*/mm/fault.c
中。我的Linux/arch/i386/mm/fault.c
副本对error_code有以下定义:
我的Linux/arch/x86_64/mm/fault.c
副本添加了以下内容:
答案 2 :(得分:6)
如果是共享库
不幸的是,你被软管了;它不可能知道在哪里 在事后,动态链接器将库放在内存中。
嗯,仍有可能检索信息,而不是从二进制文件中检索信息,而是从对象中检索信息。但是你需要对象的基地址。此信息仍在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内容。
它本身似乎是unnesseray但是使用我们所关注的共享对象的base_addr,你可以通过直接在另一个GDB实例中调用所涉及的共享对象来从地址中获取更多信息。 保持第一个gdb具有符号的idee。
注意:脚本相当不完整我怀疑你可以添加到add-symbol-file的第二个参数,用这个值打印总和:
readelf -S $SO_PATH|grep -E '\.text[ \t]'|awk '{print $5}'
其中$ SO_PATH是add-symbol-file的第一个参数
希望有所帮助