저자는 Yuyang에서 Linux 커널 디버깅 방법의 요약을 분해하는 것을 목표로합니다.

Linux 디스 어셈블리 디버깅 방법

Linux 커널 모듈이나 응용 프로그램은 여러 가지 이유로 충돌하는 경우가 많으며 일반적으로 함수 호출 스택 정보가 인쇄되는데이 경우 문제를 어떻게 찾을 수 있습니까? 이 문서에서는 이러한 문제를 찾는 데 도움이되는 분해 방법을 소개합니다.

코드 예는 다음과 같습니다.

#include <signal.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)

{

    무효 * 배열 [MAX_BACKTRACE_LEVEL];

    size_t 크기;

#ifdef PRINT_DEBUG

    char ** 문자열;

    size_t i;

 

    크기 = 역 추적 (배열, MAX_BACKTRACE_LEVEL);

    strings = backtrace_symbols (배열, 크기);

    printf ( "% zd 스택 프레임을 얻습니다. \ n", 크기);

    for (i = 0; i <크기; i ++)

      printf ( "% s \ n", 문자열 [i]);

무료 (문자열);

#그밖에

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

    크기 = 역 추적 (배열, MAX_BACKTRACE_LEVEL);

    backtrace_symbols_fd (배열, 크기, fd);

    닫기 (fd);

#endif

    exit (0);

}

 

void die () {

    char * str1;

    char * str2;

    char * str3;

char * str4 = NULL;

 

    strcpy (str4, "ab");

 

}

 

void let_it_die () {

    그만큼();

}

 

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

    struct sigaction act;

    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);

 

let_it_die ();

 

   반환 0;

 

}

이 예제에서는 사용자 정의 신호 처리 함수를 사용하여 프로그램이 비정상 일 때 backtrace () 및 backtrace_symbols ()를 호출하여 함수 호출 스택 정보를 얻고 인쇄합니다. 다음으로 프로그램을 컴파일하고 실행합니다.

                       

컴파일 할 때 주로 디버깅 정보를 추가하기 위해 -g 및 -rdynamic 옵션이 추가됩니다. 런타임 중에 예외가 발생하고 함수 호출 스택이 인쇄되었으며 스택 프레임이 7 개 계층임을 알 수 있습니다. 스택 프레임 정보에서 커널은 함수 호출 스택 정보를 획득 할 때 계층별로 반전하므로 함수 호출 순서가 반대로됩니다. 즉, main-> let_it_die ()-> die ()입니다.

함수 호출 스택의 각 줄에 표시되는 형식은 다음 같습니다. 문제가되는 코드가있는 실행 파일 (기호 + 상대 변위) [로드 주소]

호출 스택 정보 라인 "./backtrace(main+0xf7) [0x80488cd]"를 예로 들어 현재 실행 파일이 백 트레이스이고 코드 라인이 기본 심볼의 주소에서 0xf7 라인으로 오프셋되어 있음을 보여줍니다. 일반적으로 실행 파일에서 분해 된 어셈블리 코드에서 주 심볼의 주소에 상대 변위를 더한 값은 후속로드 주소와 같습니다. 때로는 버전 업데이트로 인해 코드를 다시 컴파일하여 실행 파일을 생성 한 다음 문제를 분해하고 분석 한 후 문제가 발생한 시간과 비교하여 코드가 업데이트되므로 main + 0xf7이 같지 않을 수 있습니다. 문제가 발생했을 때 인쇄 된 호출에 스택의로드 주소. 부호 값에 상대 변위를 더한 값을 계산 한 다음이를 부하 주소와 비교하여 코드가 문제와 일치하는지 확인할 수 있습니다.

다음으로 실행 파일을 분해합니다.

justin @ ubuntu : ~ / workspace / backtrace $ objdump -dS backtrace> backtrace.asm

다음으로 디스 어셈블 된 어셈블리 코드와 문제 발생시 호출 스택 정보를 분석하여 문제를 찾습니다. 먼저 맨 아래 호출 스택, 즉 ./backtrace () [0x804873d]에서 시작하여 0x804873d 주소 기호를 검색합니다. 다음과 같이 어셈블리 코드 :

 

해당 코드 라인 크기 = backtrace (array, MAX_BACKTRACE_LEVEL); 코드를 보면이 함수가 show_reason 함수에서 호출됨을 알 수 있습니다. 어셈블리 코드에서 show_reason 기호의 주소를 찾으십시오.

 

코드를 분석 한 결과, show_reason 함수는 예외 발생시 커스텀 예외 처리 함수라는 것을 알았습니다. 상위 계층의 호출 스택 정보에 인쇄 된 주소 0xb7707410을 찾아 보았습니다. 어셈블리 코드에없는 것으로 나타났습니다. , show_reason은 프로그램의 내부 예외이므로 외부 주소임을 나타냅니다. 프로그램이 비정상 일 때 호출되므로 내부 코드가 프로그램 예외의 원인이되어야하므로 다음 호출의 다음 줄을 분석합니다. 스택 정보, 즉 ./backtrace(die+0x18) [0x80487c0]. 먼저 어셈블리 코드에서 다이 기호를 찾으십시오. 주소는 0x80487a8입니다.

 

이 주소를 사용하여 0x80487c0과 같은 상대 변위 0x18을로드하고 함수 호출 스택에 표시된로드 주소는 동일합니다. 어셈블리 코드에서 주소 행을 찾으십시오.

 

문제가 strcpy (str4, "ab")에 있음을 알 수 있습니다.이 코드 줄에서는 이전에 정의 된 str4가 널 포인터라는 것을 분명히 알 수 있습니다. 널 포인터가 가리키는 영역에 문자열을 복사하면 널 포인터 예외가 발생합니다.

물론 문제가 발생했을 때 해당 코드를 찾을 수 없거나 내 보낸 실행 파일이 디버깅 옵션없이 컴파일되어 소스 코드와 어셈블리 코드의 디스 어셈블리 정보를 분해 할 수없는 경우도 많습니다. 전자의 경우 하중 번지는 참조 의미가없고 분해 정보에서 기호 만 찾아 상대 변위를 통해 오차 선을 찾아서 기존 코드와 문제를 비교 분석 할 수있다. 후자의 경우 오류 라인의 어셈블리 코드 만 찾은 다음 어셈블리 코드 스 니펫을 읽어 문제의 원인을 분석 할 수 있습니다.

 

일반적으로 objdump 디스 어셈블리를 사용하여 문제를 분석하지만 특히 유용한 두 가지 명령, 즉 nm 및 addr2line도 사용합니다.

 

nm는 실행 파일에서 기호 테이블을 내보내는 데 사용되며 그 기능은 readelf –s 또는 objdump –T (t)와 유사합니다.

 

nm 명령으로 검색된 die symbol 및 let_it_die symbol의 주소는 디스 어셈블 된 주소와 동일합니다.

 

addr2line 명령은 주소를 지정하여 실행 파일에서 기호, 실행 파일 및 오류 코드 줄을 인쇄 할 수 있습니다.

 

여기에서 ./backtrace(die+0x18) [0x80487c0]에서로드 주소를 찾으려고했는데 오류가 발생했을 때 호출되는 die 함수를 찾았습니다. 오류 라인은 80 행입니다.

 

addr2line이 die 함수에서 strcpy가 호출되는 줄을 찾는 것을 분명히 알 수 있습니다.

추천

출처blog.csdn.net/daocaokafei/article/details/114968003