How to make lldb ignore EXC_BAD_ACCESS exception?

前端 未结 4 1420
粉色の甜心
粉色の甜心 2021-02-04 15:41

I am writing a program on Mac OSX depending on the sigaction/sa_handler mechanism. Run a code snippet from user and get ready to catch signals/exceptions at any time. The progra

相关标签:
4条回答
  • 2021-02-04 16:23

    I needed this in a recent project, so I just built my own LLDB. I patched a line in tools/debugserver/source/MacOSX/MachTask.mm from

    err = ::task_set_exception_ports (task, m_exc_port_info.mask, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
    

    to

    err = ::task_set_exception_ports (task, m_exc_port_info.mask & ~EXC_MASK_BAD_ACCESS, m_exception_port, EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES, THREAD_STATE_NONE);
    

    which causes the debugserver to be unable to catch EXC_BAD_ACCESS exceptions. Now, my custom LLDB works just fine: it still catches SIGSEGV and SIGBUS but no longer enters a silly infinite loop when faced with EXC_BAD_ACCESS. Setting process handle options on the previously-fatal signals works fine too, and I can now debug SEGV handlers with impunity.

    Apple really ought to make this an option in LLDB...seems like a really easy fix for them.

    0 讨论(0)
  • 2021-02-04 16:27

    This is a long-standing bug in the debugger interface in Mac OS X (gdb had the same problem...) If you have a developer account, please file a bug with http://bugreport.apple.com. So few people actually use SIGSEGV handlers that the problem never gets any attention from the kernel folks, so more bugs is good...

    0 讨论(0)
  • 2021-02-04 16:28

    A little bit of example code can make a question like this a lot easier to answer ... I've never used the sigaction API before but I threw this together -

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    
    void segv_handler (int in)
    {
        puts ("in segv_handler()");
    }
    
    void sigbus_handler (int in)
    {
        puts ("in sigbus_handler()");
    }
    
    int main ()
    {
        struct sigaction action;
        action.sa_mask = 0;
        action.sa_flags = 0;
    
    
        action.sa_handler = segv_handler;
        sigaction (SIGSEGV, &action, NULL);
        action.sa_handler = sigbus_handler;
        sigaction (SIGBUS, &action, NULL);
    
        puts ("about to send SIGSEGV signal from main()");
        kill (getpid(), SIGSEGV);
    
        puts ("about to send SIGBUS signal from main()");
        kill (getpid(), SIGBUS);
    
        puts ("exiting main()");
    
    }
    
    
    % lldb a.out
    (lldb) br s -n main
    (lldb) r
    (lldb) pr h -p true -s false SIGSEGV SIGBUS
    (lldb) c
    Process 54743 resuming
    about to send SIGSEGV signal from main()
    Process 54743 stopped and restarted: thread 1 received signal: SIGSEGV
    in segv_handler()
    about to send SIGBUS signal from main()
    Process 54743 stopped and restarted: thread 1 received signal: SIGBUS
    in sigbus_handler()
    exiting main()
    Process 54743 exited with status = 0 (0x00000000) 
    (lldb) 
    

    Everything looks like it's working correctly here. If I'd added -n false to the process handle arguments, lldb wouldn't have printed the lines about Process .. stopped and restarted.

    Note that these signal settings do not persist across process executions. So if you're starting your debug session over (r once you've already started the process once), you'll need to re-set these. You may want to create a command alias shortcut and put it in your ~/.lldbinit file so you can set the process handling the way you prefer with a short cmd.

    0 讨论(0)
  • 2021-02-04 16:39

    We can do it easily. Just add this code.

    #include <mach/task.h>
    #include <mach/mach_init.h>
    #include <mach/mach_port.h>
    
    int ret = task_set_exception_ports(
                                       mach_task_self(),
                                       EXC_MASK_BAD_ACCESS,
                                       MACH_PORT_NULL,//m_exception_port,
                                       EXCEPTION_DEFAULT,
                                       0);
    

    Don't forget to do this

    proc hand -p true -s false SIGSEGV 
    proc hand -p true -s false SIGBUS
    

    Full code:

    #include <stdio.h>
    #include <string.h>
    #include <signal.h>
    #include <pthread.h>
    #include <unistd.h>
    
    #include <mach/task.h>
    #include <mach/mach_init.h>
    #include <mach/mach_port.h>
    
    static void handler(int signo, siginfo_t *sigaction, void *context)
    {
        printf("in handler.\n");
        signal(signo, SIG_DFL);
    }
    
    static void gen_exception()
    {
        printf("gen_exception in.\n");
        *(int *)0 = 0;
        printf("gen_exception out.\n");
    }
    
    void *gen_exception_thread(void *parg)
    {
        gen_exception();
        return 0;
    }
    
    int main()
    {
        task_set_exception_ports(
                                 mach_task_self(),
                                 EXC_MASK_BAD_ACCESS,
                                 MACH_PORT_NULL,//m_exception_port,
                                 EXCEPTION_DEFAULT,
                                 0);
        
        
        struct sigaction sa;
        sa.sa_sigaction = handler;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_SIGINFO;
    
        if(sigaction(/*SIGBUS*/SIGSEGV, &sa, NULL) == -1) {
            printf("sigaction fails.\n");
            return 0;
        }
    
        pthread_t id;
        pthread_create(&id, NULL, gen_exception_thread, NULL);
        pthread_join(id, NULL);
    
        return 0;
    }
    

    Refer to (Chinese article): https://zhuanlan.zhihu.com/p/33542591

    0 讨论(0)
提交回复
热议问题