El autor tiene como objetivo el desmontaje del resumen del método de depuración del kernel de Linux en Yuyang.

Método de depuración de desmontaje de Linux

Los módulos o aplicaciones del kernel de Linux a menudo se bloquean debido a varias razones. En general, la información de la pila de llamadas a funciones se imprime. En este caso, ¿cómo localizamos el problema? Este documento presenta un método de desmontaje para ayudar a localizar tales problemas.

El ejemplo de código es el siguiente:

#include <señal.h>

#include <stdio.h>

#include <stdlib.h>

#include <execinfo.h>

#include <fcntl.h>

#include <string.h>

#include <unistd.h>

#include <sys / types.h>

#include <sys / stat.h>

 

#define PRINT_DEBUG

#define MAX_BACKTRACE_LEVEL 10

#define BACKTRACE_LOG_NAME "backtrace.log"

 

static void show_reason (int sig, siginfo_t * info, void * secret)

{

    vacío * matriz [MAX_BACKTRACE_LEVEL];

    size_t tamaño;

#ifdef PRINT_DEBUG

    char ** cadenas;

    size_t i;

 

    tamaño = seguimiento (matriz, MAX_BACKTRACE_LEVEL);

    cadenas = backtrace_symbols (matriz, tamaño);

    printf ("Obtener marcos de pila% zd. \ n", tamaño);

    para (i = 0; i <tamaño; i ++)

      printf ("% s \ n", cadenas [i]);

gratis (cuerdas);

#demás

    int fd = open (BACKSTRACE_LOG_NAME, O_CREAT | O_WRONLY);

    tamaño = seguimiento (matriz, MAX_BACKTRACE_LEVEL);

    backtrace_symbols_fd (matriz, tamaño, fd);

    cerrar (fd);

#terminara si

    salir (0);

}

 

muere vacío () {

    char * str1;

    char * str2;

    char * str3;

char * str4 = NULL;

 

    strcpy (str4, "ab");

 

}

 

void let_it_die () {

    la();

}

 

int main (int argc, char ** argv) {

    acto de estructura sigaction;

    act.sa_sigaction = show_reason;

    sigemptyset (& act.sa_mask);

    act.sa_flags = SA_RESTART | SA_SIGINFO;

    sigaction (SIGSEGV, & act, NULL);

    sigaction (SIGUSR1, & act, NULL);

    sigaction (SIGFPE, & act, NULL);

    sigaction (SIGILL, & act, NULL);

    sigaction (SIGBUS, & act, NULL);

    sigaction (SIGABRT, & act, NULL);

    sigaction (SIGSYS, & act, NULL);

 

dejarlo morir();

 

   return 0;

 

}

En este ejemplo, usamos una función de procesamiento de señal personalizada para obtener e imprimir la información de la pila de llamadas de la función llamando a backtrace () y backtrace_symbols () cuando el programa es anormal. A continuación, compilamos y ejecutamos el programa.

                       

Al compilar, se agregan las opciones -g y -rdynamic, principalmente para agregar información de depuración. Se puede ver que se produjo una excepción durante el tiempo de ejecución, se imprimió la pila de llamadas a la función y el marco de la pila tenía 7 capas. En la información del marco de la pila, el kernel invierte capa por capa cuando obtiene la información de la pila de llamadas a la función, por lo que el orden de las llamadas a la función se invierte, es decir, main-> let_it_die () -> die ().

El formato que se muestra en cada línea de la pila de llamadas de función es: el archivo ejecutable donde se encuentra el código infractor (símbolo + desplazamiento relativo) [dirección de carga]

Tome la línea de información de la pila de llamadas "./backtrace(main+0xf7) [0x80488cd]" como ejemplo para mostrar que el archivo ejecutable actual es backtrace, y la línea de código está desplazada desde la dirección del símbolo principal a la línea 0xf7. Normalmente, el ejecutable en el código ensamblador que se desmonta del archivo, la dirección del símbolo principal más el desplazamiento relativo es igual a la dirección de carga posterior. A veces, tal vez debido a la actualización de la versión, después de que recompilamos el código para generar el archivo ejecutable, y luego desensamblamos y analizamos el problema, porque el código se actualiza en comparación con el momento en que ocurre el problema, entonces main + 0xf7 puede no ser igual a la llamada impresa cuando ocurre el problema La dirección de carga de la pila. Al calcular el valor del signo más el desplazamiento relativo y luego compararlo con la dirección de carga, puede confirmar si el código es consistente con el problema.

A continuación, desensamblamos el archivo ejecutable.

justin @ ubuntu: ~ / espacio de trabajo / backtrace $ objdump -dS backtrace> backtrace.asm

A continuación, localizaremos el problema analizando el código ensamblador desensamblado y la información de la pila de llamadas cuando ocurra el problema. Primero, comience desde la pila de llamadas inferior, es decir ./backtrace () [0x804873d], y busque el símbolo de dirección 0x804873d en el código de ensamblaje, de la siguiente manera:

 

Puede ver la línea de código correspondiente size = backtrace (array, MAX_BACKTRACE_LEVEL); Al mirar el código, puede saber que esta función se llama en la función show_reason. Intente encontrar la dirección del símbolo show_reason en el código ensamblador:

 

Al analizar el código, descubrí que la función show_reason es una función de manejo de excepciones personalizada cuando ocurre una excepción. Intenté encontrar la dirección 0xb7707410 impresa por la información de la pila de llamadas de la capa superior. Se encontró que no estaba en el código ensamblador , indicando que era una dirección externa, porque show_reason es una excepción interna en el programa. Se llama cuando el programa es anormal, por lo que el código interno debe ser la causa de la excepción del programa, por lo que a continuación analizamos la siguiente línea de llamada información de pila, es decir, ./backtrace(die+0x18) [0x80487c0]. Primero, busque el símbolo del dado en el código ensamblador, su dirección es 0x80487a8.

 

Utilice esta dirección para cargar el desplazamiento relativo 0x18 igual a 0x80487c0 y la dirección de carga que se muestra en la pila de llamadas de función es la misma. Busque la línea de la dirección en el código de ensamblaje:

 

Se puede ver que el problema está en strcpy (str4, "ab"); en esta línea de código, puede ver claramente que el str4 previamente definido es un puntero nulo. Copiar la cadena en el área apuntada por el puntero nulo provocar una excepción de puntero nulo.

Por supuesto, en más casos, no podremos desensamblar la información de desensamblaje del código fuente y el código ensamblador porque no podemos encontrar el código correspondiente cuando ocurre el problema, o el archivo ejecutable exportado se compila sin opciones de depuración. Para el primer caso, la dirección de carga no tiene significado de referencia, solo podemos encontrar el símbolo en la información de desmontaje, encontrar la línea de error a través del desplazamiento relativo y comparar y analizar el problema con el código existente. Para este último, solo podemos ubicar el código ensamblador de la línea de error y luego leer los fragmentos de código ensamblador para analizar la causa del problema.

 

Por lo general, usamos el desmontaje de objdump para analizar el problema, pero también usamos otros dos comandos particularmente útiles, a saber, nm y addr2line.

 

nm se utiliza para exportar la tabla de símbolos del archivo ejecutable, y su función es similar a readelf –s u objdump –T (t)

 

La dirección del símbolo die y el símbolo let_it_die buscados por el comando nm es la misma que la dirección desensamblada.

 

El comando addr2line puede imprimir símbolos, archivos ejecutables y líneas de códigos de error de archivos ejecutables especificando la dirección:

 

Aquí intenté encontrar la dirección de carga en ./backtrace(die+0x18) [0x80487c0], y encontré la función die llamada cuando ocurrió un error. La línea de error es la línea 80:

 

Se puede ver claramente que addr2line ubica la línea donde se llama a strcpy en la función die.

Supongo que te gusta

Origin blog.csdn.net/daocaokafei/article/details/114968003
Recomendado
Clasificación