Cela peut être une question très simple, j'essaie de déboguer une application qui génère l'erreur de segmentation suivante dans le kern.log
kernel: myapp[15514]: segfault at 794ef0 ip 080513b sp 794ef0 error 6 in myapp[8048000+24000]
Voici mes questions:
Existe-t-il une documentation sur les numéros d'erreur de diff sur segfault, dans ce cas, c'est l'erreur 6, mais j'ai vu l'erreur 4, 5
Quelle est la signification des informations at bf794ef0 ip 0805130b sp bf794ef0 and myapp[8048000+24000]
?
Jusqu'à présent, j'ai pu compiler avec des symboles, et quand je fais un x 0x8048000+24000
il renvoie un symbole, est-ce la bonne façon de le faire? Jusqu'à présent, mes hypothèses sont les suivantes:
Courir addr2line -e myapp 080513b
(et répétez pour les autres valeurs de pointeur d'instruction données) pour voir où l'erreur se produit. Mieux, obtenez une version instrumentée par débogage et reproduisez le problème sous un débogueur tel que gdb.
Dans le libfoo.so[NNNNNN+YYYY]
partie, le NNNNNN
est l'endroit où la bibliothèque a été chargée. Soustrayez cela du pointeur d'instruction (ip
) et vous obtiendrez l'offset dans le .so
de l'instruction incriminée. Ensuite, vous pouvez utiliser objdump -DCgl libfoo.so
et recherchez l'instruction à ce décalage. Vous devriez facilement être en mesure de déterminer de quelle fonction il s'agit à partir des étiquettes asm. Si la .so
n'a pas d'optimisations, vous pouvez également essayer d'utiliser addr2line -e libfoo.so <offset>
.
Voici la répartition des champs:
address
- l'emplacement en mémoire auquel le code tente d'accéder (il est probable que 10
et 11
sont des décalages à partir d'un pointeur que nous prévoyons de définir sur une valeur valide mais qui pointe à la place sur 0
)ip
- pointeur d'instruction, ie. où vit le code qui essaie de faire celasp
- pointeur de pileerror
- Indicateurs spécifiques à l'architecture; voir Arch/*/mm/fault.c
pour votre plateforme.Sur la base de mes connaissances limitées, vos hypothèses sont correctes.
sp
= pointeur de pileip
= pointeur d'instructionmyapp[8048000+24000]
= adresseSi je déboguais le problème, je modifierais le code pour produire un vidage de mémoire ou enregistrerais pile backtrace sur le crash. Vous pouvez également exécuter le programme sous (ou attacher) GDB.
Le code d'erreur est juste le code d'erreur architectural pour les défauts de page et semble être spécifique à l'architecture. Ils sont souvent documentés dans Arch/*/mm/fault.c
dans la source du noyau. Ma copie de Linux/Arch/i386/mm/fault.c
a la définition suivante pour error_code:
Ma copie de Linux/Arch/x86_64/mm/fault.c
ajoute ce qui suit:
S'il s'agit d'une bibliothèque partagée
Malheureusement, vous êtes arrosé; il n'est pas possible de savoir où les bibliothèques ont été placées en mémoire par l'éditeur de liens dynamique après coup.
Eh bien, il y a toujours une possibilité de récupérer les informations, non pas du binaire, mais de l'objet. Mais vous avez besoin de l'adresse de base de l'objet. Et ces informations se trouvent toujours dans le coredump, dans la structure link_map.
Vous devez donc d'abord importer la structure link_map dans GDB. Permet donc de compiler un programme avec le symbole de débogage et de l'ajouter à la 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
il vous imprimera tout le contenu de link_map, dans un ensemble de commandes GDB.
En soi, cela peut sembler anodin, mais avec la base_addr de l'objet partagé dont nous parlons, vous pouvez obtenir plus d'informations d'une adresse en déboguant directement l'objet partagé impliqué dans une autre instance GDB. Gardez le premier gdb pour avoir une idée du symbole.
REMARQUE: le script est plutôt incomplet, je pense que vous pouvez ajouter au deuxième paramètre du fichier add-symbol-file imprimé la somme avec cette valeur:
readelf -S $SO_PATH|grep -E '\.text[ \t]'|awk '{print $5}'
où $ SO_PATH est le premier argument du fichier add-symbol
J'espère que ça aide