Chinaunix首页 | 论坛 | 博客
  • 博客访问: 918266
  • 博文数量: 132
  • 博客积分: 9976
  • 博客等级: 中将
  • 技术积分: 1781
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-30 20:40
文章分类

全部博文(132)

文章存档

2013年(1)

2011年(1)

2010年(15)

2009年(77)

2008年(36)

2007年(2)

我的朋友

分类: C/C++

2008-02-27 11:17:22

Unix/Linux C 异常处理的问题

=============================================
来自网络的文章 --> 不断增加中, 如果文章作者不同意引用,请联系我: cj_9379@163.com
我会马上删除。
--------------------------------------------
19。

--------------------------------------------

18。

--------------------------------------------
17。

--------------------------------------------
16。

--------------------------------------------

15。

--------------------------------------------


14。

--------------------------------------------

13。

--------------------------------------------

12。

--------------------------------------------

11。

--------------------------------------------

10。

--------------------------------------------

9。

--------------------------------------------

8。

--------------------------------------------

7。

--------------------------------------------

6。

--------------------------------------------

5。

--------------------------------------------

4。

--------------------------------------------

3。

--------------------------------------------

2。 来源:


C/C++ Linux下C++异常处理技巧-实例讲解1

Linux下C++异常处理技巧-实例讲解

  保留异常来源信息

  在C++中,无论何时在处理程序内捕获一个异常,关于该异常来源的信息都是不为人知的。异常的具体来源可以提供许多更好地处理该异常的重要信息,或者提供一些可以附加到错误日志的信息,以便以后进行分析。

  为了解决这一问题,可以在抛出异常语句期间,在异常对象的构造函数中生成一个堆栈跟踪。ExceptionTracer是示范这种行为的一个类。

  清单 1. 在异常对象构造函数中生成一个堆栈跟踪

// Sample Program:
// Compiler: gcc 3.2.3 20030502
// Linux: Red Hat
#include
#include
#include
#include
using namespace std;
/////////////////////////////////////////////
class ExceptionTracer
{
public:
   ExceptionTracer()
   {
     void * array[25];
     int nSize = backtrace(array, 25);
     char ** symbols = backtrace_symbols(array, nSize);
     for (int i = 0; i < nSize; i++)
     {
       cout << symbols[i] << endl;
     }
     free(symbols);
   }
};

  管理信号

  每当进程执行一个令人讨厌的动作,以致于Linux?内核发出一个信号时,该信号都必须被处理。信号处理程序通常会释放一些重要资源并终止应用程序。在这种情况下,堆栈上的所有对象实例都处于未破坏状态。另一方面,如果这些信号被转换成 C++ 异常,那么您可以优雅地调用其构造函数,并安排多层 catch 块,以便更好地处理这些信号。

  清单2中定义的SignalExceptionClass,提供了表示内核可能发出信号的C++异常的抽象。SignalTranslator 是一个基于 SignalExceptionClass 的模板类,它通常用来实现到 C++异常的转换。在任何瞬间,只能有一个信号处理程序处理一个活动进程的一个信号。因此,SignalTranslator 采用了 singleton 设计模式。整体概念通过用于 SIGSEGV 的 SegmentationFault 类和用于 SIGFPE 的 FloatingPointException 类得到了展示。

  清单 2. 将信号转换成异常

template class SignalTranslator
{
private:
   class SingleTonTranslator
   {
   public:
     SingleTonTranslator()
     {
       signal(SignalExceptionClass::GetSignalNumber(), SignalHandler);
     }
     static void SignalHandler(int)
     {
       throw SignalExceptionClass();
     }
   };
public:
   SignalTranslator()
   {
     static SingleTonTranslator s_objTranslator;
   }
};
// An example for SIGSEGV
class SegmentationFault : public ExceptionTracer, public exception
{
public:
   static int GetSignalNumber() {return SIGSEGV;}
};
SignalTranslator g_objSegmentationFaultTranslator;
// An example for SIGFPE
class FloatingPointException : public ExceptionTracer, public exception
{
public:
   static int GetSignalNumber() {return SIGFPE;}
};
SignalTranslator g_objFloatingPointExceptionTranslator;

  管理构造函数和析构函数中的异常

: XML WebService完全实例详细解析

本文章共:3页 [1]

views:(0) | Comments:(0) | |

相关内容:



C/C++ Linux下C++异常处理技巧-实例讲解 2

Linux下C++异常处理技巧-实例讲解

  在全局(静态全局)变量的构造和析构期间,每个ANSI C++ 都捕获到异常是不可能的。因此,ANSI C++ 不建议在那些其实例可能被定义为全局实例(静态全局实例)的类的构造函数和析构函数中抛出异常。换一种说法就是永远都不要为那些其构造函数和析构函数可能抛出异常的类定义全局(静态全局)实例。不过,如果假定有一个特定编译器和一个特定系统,那么可能可以这样做,幸运的是,对于 Linux 上的 GCC,恰好是这种情况。

  使用ExceptionHandler类可以展示这一点,该类也采用了singleton设计模式。其构造函数注册了一个未捕获的处理程序。因为每次只能有一个未捕获的处理程序处理一个活动进程,构造函数应该只被调用一次,因此要采用singleton模式。应该在定义有问题的实际全局(静态全局)变量之前定义ExceptionHandler 的全局(静态全局)实例。

  清单 3. 处理构造函数中的异常

class ExceptionHandler
{
private:
class SingleTonHandler
{
public:
SingleTonHandler()
{
set_terminate(Handler);
}
static void Handler()
{
// Exception from construction/destruction of global variables
try
{
// re-throw
throw;
}
catch (SegmentationFault &)
{
cout << "SegmentationFault" << endl;
}
catch (FloatingPointException &)
{
cout << "FloatingPointException" << endl;
}
catch (...)
{
cout << "Unknown Exception" << endl;
}
//if this is a thread performing some core activity
abort();
// else if this is a thread used to service requests
// pthread_exit();
}
};
public:
ExceptionHandler()
{
static SingleTonHandler s_objHandler;
}
};
//////////////////////////////////////////////////////////////////////////
class A
{
public:
A()
{
//int i = 0, j = 1/i;
*(int *)0 = 0;
}
};
// Before defining any global variable, we define a dummy instance
// of ExceptionHandler object to make sure that
// ExceptionHandler::SingleTonHandler::SingleTonHandler() is invoked
ExceptionHandler g_objExceptionHandler;
A g_a;
//////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
return 0;
}

  处理多线程程序中的异常

: 网友原创:从N层到.NET详细剖析原理

本文章共:3页 [2]

views:(0) | Comments:(0) | |

相关内容:


C/C++ Linux下C++异常处理技巧-实例讲解 3

Linux下C++异常处理技巧-实例讲解

  有时一些异常没有被捕获,这将造成进程异常中止。不过很多时候,进程包含多个线程,其中少数线程执行核心应用程序逻辑,同时,其余线程为外部请求提供服务。如果服务线程因编程错误而没有处理某个异常,则会造成整个应用程序崩溃。这一点可能是不受人们欢迎的,因为它会通过向应用程序传送不合法的请求而助长拒绝服务攻击。为了避免这一点,未捕获处理程序可以决定是请求异常中止调用,还是请求线程退出调用。清单 3 中 ExceptionHandler::SingleTonHandler::Handler() 函数的末尾处展示了该处理程序。

  结束语

  我简单地讨论了少许 C++ 编程设计模式,以便更好地执行以下任务:

  在抛出异常的时候追踪异常的来源。

  将信号从内核程序转换成 C++ 异常。

  捕获构造和/或析构全局变量期间抛出的异常。

  多线程进程中的异常处理。

  我希望您能采用这些技巧中的一些来开发无忧代码。


--------------------------------------------

1。 来源: http://www.xfocus.net/releases/200308/a589.html

内核处理信号对应用层堆栈的影响


创建时间:2003-08-13
文章属性:原创
文章提交:alert7 (sztcww_at_sina.com)

内核处理信号对应用层堆栈的影响

by alert7 < alert7@xfocus.org >
主页: http://www.xfocus.org/  
时间:2003年8月1日

好久没有为组织做点贡献了,真有点过意不去:(

本文着重点在内核信号处理对应用层堆栈的影响上,其他的一些在处理信号细节上被忽略。
至于本文是否跟安全相关,那就是仁者见仁智者见智了。

1 发送信号过程:
发送信号的过程比接收信号的过程简单的多。当应用层用KILL命令向某个进程发送进程的时候,
内核只在进程task_struct的sigpending结构中安排一个信号位。

2 接收信号过程
信号处理的时机。
当某个进程有悬而未决的信号的时候,内核就会调用do_signal函数
do_signal做一些其他功能上的事情,真正递送一个信号是在handle_signal函数。于是在最后
do_signal函数调用了handle_signal真正递送一个信号。当然要想到达这一步需要一些条件。比如说
应用层已经声明要处理该信号,信号不是些不可捕获的信号等等...

重点中的重点,我们来看看handle_signal函数

/*
* OK, we're invoking a handler
*/    

static void
handle_signal(unsigned long sig, struct k_sigaction *ka,
          siginfo_t *info, sigset_t *oldset, struct pt_regs * regs)
{
....
    /* Set up the stack frame */
    if (ka->sa.sa_flags & SA_SIGINFO)
        setup_rt_frame(sig, ka, info, oldset, regs);
    else
        setup_frame(sig, ka, oldset, regs);

.....
}
去掉一些我们不想关心的东西,代码就剩下上面这些。
以上函数setup_rt_frame和setup_frame就是内核在应用层的堆栈上安排信号堆栈帧的过程,就是我们所
要关注的。setup_rt_frame和setup_frame雷同,我们就来分析下setup_frame函数。


static void setup_frame(int sig, struct k_sigaction *ka,
            sigset_t *set, struct pt_regs * regs)
{
    struct sigframe *frame;
    int err = 0;

    frame = get_sigframe(ka, regs, sizeof(*frame)); //决定要使用应用层堆栈的地址

    if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) //判断是否可写
        goto give_sigsegv;

    err |= __put_user((current->exec_domain
                   && current->exec_domain->signal_invmap
                   && sig < 32
                   ? current->exec_domain->signal_invmap[sig]
                   : sig),
                  &frame->sig);
    if (err)
        goto give_sigsegv;
    
    /*保存寄存器信号到&frame->sc和&frame->fpstate中*/
    err |= setup_sigcontext(&frame->sc, &frame->fpstate, regs, set->sig[0]);
    if (err)
        goto give_sigsegv;

    if (_NSIG_WORDS > 1) {
        err |= __copy_to_user(frame->extramask, &set->sig[1],
                      sizeof(frame->extramask));
    }
    if (err)
        goto give_sigsegv;

    /* Set up to return from userspace.  If provided, use a stub
       already in userspace.  */
    if (ka->sa.sa_flags & SA_RESTORER) {
        err |= __put_user(ka->sa.sa_restorer, &frame->pretcode);
    } else {
        /*把frame->retcod的地址放到&frame->pretcode中,这样当信号处理函数返回时候就会*/
        /*跳到frame->retcode地址去执行代码了*/
        err |= __put_user(frame->retcode, &frame->pretcode);
        /* This is popl %eax ; movl $,%eax ; int $0x80 */
        err |= __put_user(0xb858, (short *)(frame->retcode+0));
        err |= __put_user(__NR_sigreturn, (int *)(frame->retcode+2));
        err |= __put_user(0x80cd, (short *)(frame->retcode+6));
        /*以上在frame->retcode上安排了popl %eax ; movl $,%eax ; int $0x80指令*/
    }

    if (err)
        goto give_sigsegv;

    /* Set up registers for signal handler */
    regs->esp = (unsigned long) frame; //让应用层的esp指向frame;
    regs->eip = (unsigned long) ka->sa.sa_handler;//EIP为信号处理函数

    set_fs(USER_DS);
    regs->xds = __USER_DS;
    regs->xes = __USER_DS;
    regs->xss = __USER_DS;
    regs->xcs = __USER_CS;
    regs->eflags &= ~TF_MASK;

#if DEBUG_SIG
    printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n",
        current->comm, current->pid, frame, regs->eip, frame->pretcode);
#endif

    return;

give_sigsegv:
    if (sig == SIGSEGV)
        ka->sa.sa_handler = SIG_DFL;
    force_sig(SIGSEGV, current);
}

到此,内核在应用层的堆栈上就安排了一个帧,我们来看一下一个实际的例子。

[alert7@redhat73 sigal]$ cat test.c
test ()
        {
        printf("test");
return;
        }
int main(int argv,char **argc) {
         char buf[256];

        signal(10,test);
        while(1);
}
[alert7@redhat73]
(gdb) b main
Breakpoint 1 at 0x8048501
(gdb) r dd dd
Starting program: /home/alert7/sigal/test dd dd

Breakpoint 1, 0x08048501 in main ()
(gdb)
Breakpoint 2 at 0x42029098
(gdb) c
Continuing.

(gdb) x/5i 0x42029098
0x42029098 <__restore>: pop    %eax
0x42029099 <__restore+1>:       mov    $0x77,%eax
0x4202909e <__restore+6>:       int    $0x80
0x420290a0 <__restore+8>:       mov    (%esp,1),%ebx
0x420290a3 <__restore+11>:      ret
(gdb) i reg esp ebp eip
esp            0xbffff748       0xbffff748
ebp            0xbffffb38       0xbffffb38
eip            0x4202909e       0x4202909e
(gdb) x/50x $esp-8 //$esp-8就是内核构造的一个信号帧
0xbffff740:     0x42029098      0x0000000a      0x00000000      0x00000000
0xbffff750:     0x0000002b      0x0000002b      0xbffffba4      0x40013020
0xbffff760:     0xbffffb38      0xbffffa20      0x4213030c      0xbffffc00
0xbffff770:     0x08049752      0xbffffb2c      0x00000001      0x00000000
0xbffff780:     0x08048570      0x00000023      0x00000346      0xbffffa20
0xbffff790:     0x0000002b      0x00000000      0x00000000      0x00000000

0xbffff7a0:     0x4000083e      0x400005b8      0x40000218      0x400131e8
0xbffff7b0:     0x00000003      0x40013e48      0x00000003      0x42009e38
0xbffff7c0:     0x40013d68      0x0d1fc7ae      0x0d1fc7ae      0xbffff890
0xbffff7d0:     0x40013bc8      0x4200f624      0x00000000      0x00000000
0xbffff7e0:     0x42009e38      0x40013bc8      0x00000000      0x00000000
0xbffff7f0:     0x00000000      0x00000000      0x00000000      0x00000000
0xbffff800:     0x00000000      0x00000000


struct sigframe
{
    char *pretcode;            //这里为0x42029098,在该程序中,ka->sa.sa_flags 有 SA_RESTORER标志,
                    //所以没有在堆栈中安排指令,而是使用了一个现成的地址
    int sig;            //信号为10
    struct sigcontext sc;
    struct _fpstate fpstate;
    unsigned long extramask[_NSIG_WORDS-1];
    char retcode[8];
};
struct sigcontext {
    unsigned short gs, __gsh;//0,0
    unsigned short fs, __fsh;//0,0
    unsigned short es, __esh;//0x2b,0
    unsigned short ds, __dsh;//0x2b,0
    unsigned long edi;        //0xbffffba4
    unsigned long esi;        //0x40013020
    unsigned long ebp;        //0xbffffb38
    unsigned long esp;        //0xbffffa20
    unsigned long ebx;        //0x4213030c
    unsigned long edx;        //0xbffffc00
    unsigned long ecx;        //0x08049752
    unsigned long eax;        //0xbffffb2c
    unsigned long trapno;        //0x00000001
    unsigned long err;        //0x00000000
    unsigned long eip;        //0x08048570
    unsigned short cs, __csh;    //0x23,0
    unsigned long eflags;        //0x00000346
    unsigned long esp_at_signal;    //0xbffffa20
    unsigned short ss, __ssh;    //0x2b,0
    struct _fpstate * fpstate;    //0x00000000
    unsigned long oldmask;        //0x00000000
    unsigned long cr2;        //0x00000000
};

内核在应用层的堆栈上安了一个帧后,当一返回到应用态的时候就跳到信号处理函数test去执行了。
此时图一 ①,应用层的堆栈多了一个帧,如下:
**********************************************************************************
图一
        (内存高址)
        +--------------------------------------+
        | ...                                  |  
        +--------------------------------------+
        | char retcode[8]                      | 8个字节
        +--------------------------------------+
        | long extramask[_NSIG_WORDS-1];       |  
        +--------------------------------------+
        | struct _fpstate fpstate;           |  
        +--------------------------------------+
        |  struct sigcontext sc;           |  
        +--------------------------------------+  <---------esp指向这里 ③
        | int sig;                   |
        +--------------------------------------+  <---------esp指向这里 ②
        | char *pretcode;               |
        +--------------------------------------+  <---------esp指向这里 ①
        |  ...                       |  
        +--------------------------------------+
        (内存低址)

**********************************************************************************
由于内核是让应用程序跳到信号处理函数的,所以不象一般的调用会把当前的EIP压入堆栈,所以现在
esp指向的pretcode的值将来信号处理完就返回到那里去了。此时ESP情况如图一 ② 的情况

当test信号处理函数完成时候,将返回到frame->pretcode也就是0x42029098的地址去执行,在这里0x42029098地址代码如下:
0x42029098 <__restore>:        pop    %eax    //弹出frame->sig,这里为10
0x42029099 <__restore+1>:       mov    $0x77,%eax
0x4202909e <__restore+6>:       int    $0x80    //请求sys_sigreturn系统调用
当执行完以上三条指令的时候,应用层的堆栈就变成了 ③ 的情况了。

忽略切入内核的细节,sys_sigreturn系统调用被调用。下面是该函数的实现细节。
asmlinkage int sys_sigreturn(unsigned long __unused)
{
    struct pt_regs *regs = (struct pt_regs *) &__unused;
    struct sigframe *frame = (struct sigframe *)(regs->esp - 8);//取得frame地址,-8是为了补上ret和pop
                                                            //这两个指令分别弹出的pretcode和sig
                                    //看看上面的图会更清楚
    sigset_t set;
    int eax;

    if (verify_area(VERIFY_READ, frame, sizeof(*frame)))
        goto badframe;
    if (__get_user(set.sig[0], &frame->sc.oldmask)
        || (_NSIG_WORDS > 1
        && __copy_from_user(&set.sig[1], &frame->extramask,
                    sizeof(frame->extramask))))
        goto badframe;

    sigdelsetmask(&set, ~_BLOCKABLE);
    spin_lock_irq(¤t->sigmask_lock);
    current->blocked = set;
    recalc_sigpending(current);
    spin_unlock_irq(¤t->sigmask_lock);
    
    /*把frame保存的一些信息恢复出来,修改regs一些寄存器*/    
    if (restore_sigcontext(regs, &frame->sc, &eax))
        goto badframe;
    return eax;

badframe:
    force_sig(SIGSEGV, current);
    return 0;
}    
restore_sigcontext函数好象也没有什么好说的,等到sys_sigreturn函数返回,regs的一些寄存器又恢复到信号来之前的值了。
所以等到内核态在返回到应用态的时候,又恢复到原来的地址去执行了。

参考资料:
    linux 2.4.18 kernel src

-------the end--------

--------------------------------------------
0。 来源: http://blog.chinaunix.net/u1/44989/showart_355920.html

修改信号处理函数 -- sigaction



    程序在运行的时候经常会收到系统或其他进程发过来的信号,有时候我们希望改变对信号的缺省处理,这时候就需要使用信号处理程序了。

#include <signal.h>
   int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}



       man手册上说sa_handler和sa_sigaction在某些系统是作为union实现的,不能一起使用。
       不过对我们来说只需要其中一个,信号处理函数或者定义成sa_handler形式或者sa_sigaction形式,完全根据我们程序的需要来定。按照POSIX的说法,sa_sigaction的第三个参数可以被转换成类型为
ucontext_t并指向信号发送时线程执行的上下文。而中另外定义了对ucontext的处理函数getcontext(), setcontext(), makecontext() 以及 swapcontext().
      
       man手册上说sa_restorer属于一个被废弃的域。

       sa_handler可以接受SIG_DFL(缺省处理)或者SIG_IGN(忽略该信号)。

       sa_mask 定义了在此信号处理程序执行时,哪些信号可以被阻塞住。

       sa_flags指定了一系列信号处理程序的选项:
              SA_NOCLDSTOP
                     不处理信号SIGCHLD.

              SA_ONESHOT 或 SA_RESETHAND
                     信号处理程序只使用一次。

              SA_ONSTACK
                     只在特定的调用栈中使用此处理程序,该调用栈由sigaltstack()指定。

              SA_RESTART
                     为了兼容BSD信号的语义

              SA_NOMASK or SA_NODEFER
                     缺省情况下触发信号处理程序的信号在处理期间会被阻塞,如果在sa_flags定义了SA_NODEFER或者SA_NOMASK就不再阻塞。

              SA_SIGINFO
                     信号处理程序时使用3各参数,见sa_sigaction(after Linux 2.1.86).

       下面是结构siginfo_t的定义:

siginfo_t {
    int si_signo; /* for all signals. Signal number */
    int si_errno; /* for all signals. An errno value */
    nt si_code; /* for all signals. why this signal occur */
    pid_t si_pid; /* Sending process ID */
    uid_t si_uid; /* Real user ID of sending process */
    int si_status; /* Exit value or signal */
    lock_t si_utime; /* User time consumed */
    clock_t si_stime; /* System time consumed */
    sigval_t si_value; /* Signal value */
    int si_int; /* POSIX.1b signal */
    void * si_ptr; /* POSIX.1b signal */
    void * si_addr; /* Memory location which caused fault */
    int si_band; /* Band event */
    int si_fd; /* File descriptor */
}


             
下面是使用sigaction处理SIGUSR1并且打印当前调用栈的代码。要注意的就是如果要在你的程序里引用sigsegv.h、sigsegv.c得到堆栈信息的话记得加上-rdynamic -ldl参数

sigsegv.h

#ifndef __sigsegv_h__
#define __sigsegv_h__

#ifdef __cplusplus
extern "C" {
#endif

  int setup_sigsegv();

#ifdef __cplusplus
}
#endif

#endif /* __sigsegv_h__ */



sigsegv.c

#define _GNU_SOURCE
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <ucontext.h>
#include <dlfcn.h>
#include <execinfo.h>
#ifndef NO_CPP_DEMANGLE
#include <cxxabi.h>
#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"};

  size_t i;
  ucontext_t *ucontext = (ucontext_t*)ptr;

#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
  int f = 0;
  Dl_info dlinfo;
  void **bp = 0;
  void *ip = 0;
#else
  void *bt[20];
  char **strings;
  size_t sz;
#endif

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

#if defined(SIGSEGV_STACK_X86) || defined(SIGSEGV_STACK_IA64)
# 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

  fprintf(stderr, "Stack trace:\n");
  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

    fprintf(stderr, "% 2d: %p <%s+%u> (%s)\n",
            ++f,
            ip,
            symname,
            (unsigned)(ip - 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
  fprintf(stderr, "Stack trace (non-dedicated):\n");
  sz = backtrace(bt, 20);
  strings = backtrace_symbols(bt, sz);

  for(i = 0; i &lt; sz; ++i)
    fprintf(stderr, "%s\n", strings[i]);
#endif
  fprintf(stderr, "End of stack trace\n");
  exit (-1);
}

int 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) &lt; 0) {
    perror("sigaction");
    return 0;
  }

  return 1;
}

#ifndef SIGSEGV_NO_AUTO_INIT
static void __attribute((constructor)) init(void) {
  setup_sigsegv();
}
#endif



#include "sigsegv.h"
#include &lt;string.h&gt;

int die() {
  char *err = NULL;
  strcpy(err, "gonner");
  return 0;
}

int main() {
  return die();
}



其中,dladdr()是Glibc的扩展函数,在POSIX中并没有定义。


#define _GNU_SOURCE

#include <dlfcn.h>
int dladdr(void *addr, Dl_info *info);
void *dlvsym(void *handle, char *symbol, char *version);

typedef struct {
  const char *dli_fname;/* Filename of defining object */
  void *dli_fbase; /* Load address of that object */
  const char *dli_sname;/* Name of nearest lower symbol */
  void *dli_saddr; /* Exact value of nearest symbol */

} Dl_info;


reference:
http://blog.chinaunix.net/u/3425/showart_263408.html




阅读(1603) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~