2011-03-22 3 views
12

내 질문은 오류 주소에 대해 질문 한 다른 질문과 약간 다릅니다. 나는 시그널 핸들러에서 신호가 저장된 명령 포인터에서 코드를 검사하고 호스트 아키텍처에 대한 가능한 syscall 입력 명령과 비교하여 신호가 syscall 또는 일반 사용자 코드를 인터럽트했는지 여부를 판별하기 위해 끔찍한 해킹을 구현하려고 시도하고있다. 계속 달리고있어.신호 처리기에서 저장된 명령어 포인터 주소 받기

How are POSIX cancellation points supposed to behave?

이 방법은 신뢰할 수없는하거나 잘못되면, 나는 또한 싶습니다이 내 오래된 질문에 설명 된 경쟁 조건 및 자원 누출로부터 고통을하지 않는 올바른 POSIX 쓰레드 취소를 구현의 일부입니다 이유를 들어라.

+0

참조 http://stackoverflow.com/q/4832743/371250 – ninjalj

+0

그리고 여기에있는 의견은 http://lxr.free-electrons.com/source/arch/x86/kernel/signal.c?v=2.6입니다. .37 # L320 – ninjalj

+0

신호 처리기에 있는지 확인할 필요가 없습니다. 이 코드는 신호 처리기에서만 실행됩니다. 결정해야 할 것은 신호 처리기가 반환 할 때 실행될 다음 명령어의 주소입니다. 나는 추한 해킹에 동의하지만 syscall에 의해 할당 된 리소스가 누출 될 수있는 경합 조건 없이는 시스템 콜에서 취소를 수행 할 수있는 다른 방법을 찾을 수 없습니다. –

답변

14
/* sigsegv.c */ 
/** 
* This source file is used to print out a stack-trace when your program 
* segfaults. It is relatively reliable and spot-on accurate. 
* 
* This code is in the public domain. Use it as you see fit, some credit 
* would be appreciated, but is not a prerequisite for usage. Feedback 
* on it's use would encourage further development and maintenance. 
* 
* Due to a bug in gcc-4.x.x you currently have to compile as C++ if you want 
* demangling to work. 
* 
* Please note that it's been ported into my ULS library, thus the check for 
* HAS_ULSLIB and the use of the sigsegv_outp macro based on that define. 
* 
* Author: Jaco Kroon <[email protected]> 
* 
* Copyright (C) 2005 - 2010 Jaco Kroon 
*/ 
#ifndef _GNU_SOURCE 
#define _GNU_SOURCE 
#endif 

/* Bug in gcc prevents from using CPP_DEMANGLE in pure "C" */ 
#if !defined(__cplusplus) && !defined(NO_CPP_DEMANGLE) 
#define NO_CPP_DEMANGLE 
#endif 

#include <memory.h> 
#include <stdlib.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <signal.h> 
#include <ucontext.h> 
#include <dlfcn.h> 
#ifndef NO_CPP_DEMANGLE 
#include <cxxabi.h> 
#ifdef __cplusplus 
using __cxxabiv1::__cxa_demangle; 
#endif 
#endif 

#ifdef HAS_ULSLIB 
#include "uls/logger.h" 
#define sigsegv_outp(x)   sigsegv_outp(,gx) 
#else 
#define sigsegv_outp(x, ...) fprintf(stderr, x "\n", ##__VA_ARGS__) 
#endif 

#if defined(REG_RIP) 
# define SIGSEGV_STACK_IA64 
# define REGFORMAT "%016lx" 
#elif defined(REG_EIP) 
# define SIGSEGV_STACK_X86 
# define REGFORMAT "%08x" 
#else 
# define SIGSEGV_STACK_GENERIC 
# define REGFORMAT "%x" 
#endif 

static void signal_segv(int signum, siginfo_t* info, void*ptr) { 
    static const char *si_codes[3] = {"", "SEGV_MAPERR", "SEGV_ACCERR"}; 

    int i, f = 0; 
    ucontext_t *ucontext = (ucontext_t*)ptr; 
    Dl_info dlinfo; 
    void **bp = 0; 
    void *ip = 0; 

    sigsegv_outp("Segmentation Fault!"); 
    sigsegv_outp("info.si_signo = %d", signum); 
    sigsegv_outp("info.si_errno = %d", info->si_errno); 
    sigsegv_outp("info.si_code = %d (%s)", info->si_code, si_codes[info->si_code]); 
    sigsegv_outp("info.si_addr = %p", info->si_addr); 
    for(i = 0; i < NGREG; i++) 
     sigsegv_outp("reg[%02d]  = 0x" REGFORMAT, i, ucontext->uc_mcontext.gregs[i]); 

#ifndef SIGSEGV_NOSTACK 
#if defined(SIGSEGV_STACK_IA64) || defined(SIGSEGV_STACK_X86) 
#if defined(SIGSEGV_STACK_IA64) 
    ip = (void*)ucontext->uc_mcontext.gregs[REG_RIP]; 
    bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP]; 
#elif defined(SIGSEGV_STACK_X86) 
    ip = (void*)ucontext->uc_mcontext.gregs[REG_EIP]; 
    bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP]; 
#endif 

    sigsegv_outp("Stack trace:"); 
    while(bp && ip) { 
     if(!dladdr(ip, &dlinfo)) 
      break; 

     const char *symname = dlinfo.dli_sname; 

#ifndef NO_CPP_DEMANGLE 
     int status; 
     char * tmp = __cxa_demangle(symname, NULL, 0, &status); 

     if (status == 0 && tmp) 
      symname = tmp; 
#endif 

     sigsegv_outp("% 2d: %p <%s+%lu> (%s)", 
       ++f, 
       ip, 
       symname, 
       (unsigned long)ip - (unsigned long)dlinfo.dli_saddr, 
       dlinfo.dli_fname); 

#ifndef NO_CPP_DEMANGLE 
     if (tmp) 
      free(tmp); 
#endif 

     if(dlinfo.dli_sname && !strcmp(dlinfo.dli_sname, "main")) 
      break; 

     ip = bp[1]; 
     bp = (void**)bp[0]; 
    } 
#else 
    sigsegv_outp("Stack trace (non-dedicated):"); 
    sz = backtrace(bt, 20); 
    strings = backtrace_symbols(bt, sz); 
    for(i = 0; i < sz; ++i) 
     sigsegv_outp("%s", strings[i]); 
#endif 
    sigsegv_outp("End of stack trace."); 
#else 
    sigsegv_outp("Not printing stack strace."); 
#endif 
    _exit (-1); 
} 

static void __attribute__((constructor)) setup_sigsegv() { 
    struct sigaction action; 
    memset(&action, 0, sizeof(action)); 
    action.sa_sigaction = signal_segv; 
    action.sa_flags = SA_SIGINFO; 
    if(sigaction(SIGSEGV, &action, NULL) < 0) 
     perror("sigaction"); 
} 

$ g++ -fPIC -shared -o libsigsegv.so -ldl sigsegv 

$ export LD_PRELOAD=/path/to/libsigsegv.so 

이 코드는 LUG에서 발견되었습니다. 여기에 URL을 가리 키도록 페이지에 도달 할 수 없으므로 전체 코드를 붙여 넣습니다. 이 코드는 SIGSEGV가 발생할 때 작은 스택 추적을 인쇄합니다. ucontext_t를 사용하지 않는 다른 방법이 있는지 확실하지 않습니다.

+1

+1 그리고 받아들입니다. 이 코드는 필요한 정보를 얻기 위해 SA_SIGINFO 신호 처리기에 세 번째 인수를 사용하는 방법을 보여줍니다. –