2018-03-13 20:28:10 +03:00
/*
* Copyright ( c ) 2013 Luca Clementi < luca . clementi @ gmail . com >
* Copyright ( c ) 2013 - 2018 The strace developers .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ` ` AS IS ' ' AND ANY EXPRESS OR
* IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include "defs.h"
# include "unwind.h"
# include "mmap_cache.h"
# include <libunwind-ptrace.h>
static unw_addr_space_t libunwind_as ;
static void
init ( void )
{
2018-04-30 00:45:40 +03:00
mmap_cache_enable ( ) ;
2018-03-13 20:28:10 +03:00
libunwind_as = unw_create_addr_space ( & _UPT_accessors , 0 ) ;
if ( ! libunwind_as )
error_msg_and_die ( " failed to create address space "
" for stack tracing " ) ;
unw_set_caching_policy ( libunwind_as , UNW_CACHE_GLOBAL ) ;
}
static void *
tcb_init ( struct tcb * tcp )
{
void * r = _UPT_create ( tcp - > pid ) ;
if ( ! r )
perror_msg_and_die ( " _UPT_create " ) ;
return r ;
}
static void
tcb_fin ( struct tcb * tcp )
{
_UPT_destroy ( tcp - > unwind_ctx ) ;
}
static void
get_symbol_name ( unw_cursor_t * cursor , char * * name ,
size_t * size , unw_word_t * offset )
{
for ( ; ; ) {
int rc = unw_get_proc_name ( cursor , * name , * size , offset ) ;
if ( rc = = 0 )
break ;
if ( rc ! = - UNW_ENOMEM ) {
* * name = ' \0 ' ;
* offset = 0 ;
break ;
}
* name = xgrowarray ( * name , size , 1 ) ;
}
}
static int
print_stack_frame ( struct tcb * tcp ,
unwind_call_action_fn call_action ,
unwind_error_action_fn error_action ,
void * data ,
unw_cursor_t * cursor ,
char * * symbol_name ,
size_t * symbol_name_size )
{
unw_word_t ip ;
if ( unw_get_reg ( cursor , UNW_REG_IP , & ip ) < 0 ) {
perror_msg ( " cannot walk the stack of process %d " , tcp - > pid ) ;
return - 1 ;
}
2018-05-04 17:45:44 +03:00
struct mmap_cache_entry_t * entry = mmap_cache_search ( tcp , ip ) ;
if ( entry
2018-03-13 20:28:10 +03:00
/* ignore mappings that have no PROT_EXEC bit set */
2018-05-04 17:45:44 +03:00
& & ( entry - > protections & MMAP_CACHE_PROT_EXECUTABLE ) ) {
2018-03-13 20:28:10 +03:00
unw_word_t function_offset ;
get_symbol_name ( cursor , symbol_name , symbol_name_size ,
& function_offset ) ;
2018-05-04 17:45:44 +03:00
unsigned long true_offset =
ip - entry - > start_addr + entry - > mmap_offset ;
2018-03-13 20:28:10 +03:00
call_action ( data ,
2018-05-04 17:45:44 +03:00
entry - > binary_filename ,
2018-03-13 20:28:10 +03:00
* symbol_name ,
function_offset ,
true_offset ) ;
return 0 ;
}
/*
* there is a bug in libunwind > = 1.0
* after a set_tid_address syscall
* unw_get_reg returns IP = = 0
*/
if ( ip )
error_action ( data , " unexpected_backtracing_error " , ip ) ;
return - 1 ;
}
static void
2018-04-30 00:45:40 +03:00
walk ( struct tcb * tcp ,
unwind_call_action_fn call_action ,
unwind_error_action_fn error_action ,
void * data )
2018-03-13 20:28:10 +03:00
{
char * symbol_name ;
size_t symbol_name_size = 40 ;
unw_cursor_t cursor ;
int stack_depth ;
if ( ! tcp - > mmap_cache )
error_func_msg_and_die ( " mmap_cache is NULL " ) ;
symbol_name = xmalloc ( symbol_name_size ) ;
if ( unw_init_remote ( & cursor , libunwind_as , tcp - > unwind_ctx ) < 0 )
perror_func_msg_and_die ( " cannot initialize libunwind " ) ;
for ( stack_depth = 0 ; stack_depth < 256 ; + + stack_depth ) {
if ( print_stack_frame ( tcp , call_action , error_action , data ,
& cursor , & symbol_name , & symbol_name_size ) < 0 )
break ;
if ( unw_step ( & cursor ) < = 0 )
break ;
}
if ( stack_depth > = 256 )
error_action ( data , " too many stack frames " , 0 ) ;
free ( symbol_name ) ;
}
static void
2018-04-30 00:45:40 +03:00
tcb_walk ( struct tcb * tcp ,
unwind_call_action_fn call_action ,
unwind_error_action_fn error_action ,
void * data )
2018-03-13 20:28:10 +03:00
{
2018-04-30 00:45:40 +03:00
switch ( mmap_cache_rebuild_if_invalid ( tcp , __func__ ) ) {
case MMAP_CACHE_REBUILD_RENEWED :
/*
* Rebuild the unwinder internal cache .
* Called when mmap cache subsystem detects a
* change of tracee memory mapping .
*/
unw_flush_cache ( libunwind_as , 0 , 0 ) ;
ATTRIBUTE_FALLTHROUGH ;
case MMAP_CACHE_REBUILD_READY :
walk ( tcp , call_action , error_action , data ) ;
break ;
default :
/* Do nothing */
;
}
2018-03-13 20:28:10 +03:00
}
const struct unwind_unwinder_t unwinder = {
. name = " libunwind " ,
. init = init ,
. tcb_init = tcb_init ,
. tcb_fin = tcb_fin ,
. tcb_walk = tcb_walk ,
} ;