web-dev-qa-db-fra.com

Comment lire un message de journal du noyau segfault

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:

  1. 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

  2. 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:

  • sp = pointeur de pile?
  • ip = pointeur d'instruction
  • à = ????
  • myapp [8048000 + 24000] = adresse du symbole?
61
Sullenx

Lorsque le rapport pointe vers un programme, pas une bibliothèque partagée

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.

S'il s'agit d'une bibliothèque partagée

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>.

Que signifie l'erreur

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 cela
  • sp - pointeur de pile
  • error - Indicateurs spécifiques à l'architecture; voir Arch/*/mm/fault.c pour votre plateforme.
40
Charles Duffy

Sur la base de mes connaissances limitées, vos hypothèses sont correctes.

  • sp = pointeur de pile
  • ip = pointeur d'instruction
  • myapp[8048000+24000] = adresse

Si 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:

  • bit 0 == 0 signifie aucune page trouvée, 1 signifie défaut de protection
  • bit 1 == 0 signifie lecture, 1 signifie écriture
  • bit 2 == 0 signifie noyau, 1 signifie mode utilisateur

Ma copie de Linux/Arch/x86_64/mm/fault.c ajoute ce qui suit:

  • bit 3 == 1 signifie que l'erreur était une extraction d'instruction
36
jschmier

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

6
scripthelps