Home | History | Annotate | Download | only in strace
      1 /*
      2  * Copyright (c) 2013 Luca Clementi <luca.clementi (at) gmail.com>
      3  * Copyright (c) 2013-2018 The strace developers.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. The name of the author may not be used to endorse or promote products
     14  *    derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "defs.h"
     29 #include "unwind.h"
     30 
     31 #include "mmap_cache.h"
     32 #include <libunwind-ptrace.h>
     33 
     34 static unw_addr_space_t libunwind_as;
     35 
     36 static void
     37 init(void)
     38 {
     39 	mmap_cache_enable();
     40 
     41 	libunwind_as = unw_create_addr_space(&_UPT_accessors, 0);
     42 	if (!libunwind_as)
     43 		error_msg_and_die("failed to create address space"
     44 				  " for stack tracing");
     45 	unw_set_caching_policy(libunwind_as, UNW_CACHE_GLOBAL);
     46 }
     47 
     48 static void *
     49 tcb_init(struct tcb *tcp)
     50 {
     51 	void *r = _UPT_create(tcp->pid);
     52 
     53 	if (!r)
     54 		perror_msg_and_die("_UPT_create");
     55 	return r;
     56 }
     57 
     58 static void
     59 tcb_fin(struct tcb *tcp)
     60 {
     61 	_UPT_destroy(tcp->unwind_ctx);
     62 }
     63 
     64 static void
     65 get_symbol_name(unw_cursor_t *cursor, char **name,
     66 		size_t *size, unw_word_t *offset)
     67 {
     68 	for (;;) {
     69 		int rc = unw_get_proc_name(cursor, *name, *size, offset);
     70 
     71 		if (rc == 0)
     72 			break;
     73 		if (rc != -UNW_ENOMEM) {
     74 			**name = '\0';
     75 			*offset = 0;
     76 			break;
     77 		}
     78 		*name = xgrowarray(*name, size, 1);
     79 	}
     80 }
     81 
     82 static int
     83 print_stack_frame(struct tcb *tcp,
     84 		  unwind_call_action_fn call_action,
     85 		  unwind_error_action_fn error_action,
     86 		  void *data,
     87 		  unw_cursor_t *cursor,
     88 		  char **symbol_name,
     89 		  size_t *symbol_name_size)
     90 {
     91 	unw_word_t ip;
     92 
     93 	if (unw_get_reg(cursor, UNW_REG_IP, &ip) < 0) {
     94 		perror_msg("cannot walk the stack of process %d", tcp->pid);
     95 		return -1;
     96 	}
     97 
     98 	struct mmap_cache_entry_t *entry = mmap_cache_search(tcp, ip);
     99 
    100 	if (entry
    101 	    /* ignore mappings that have no PROT_EXEC bit set */
    102 	    && (entry->protections & MMAP_CACHE_PROT_EXECUTABLE)) {
    103 		unw_word_t function_offset;
    104 
    105 		get_symbol_name(cursor, symbol_name, symbol_name_size,
    106 				&function_offset);
    107 		unsigned long true_offset =
    108 			ip - entry->start_addr + entry->mmap_offset;
    109 		call_action(data,
    110 			    entry->binary_filename,
    111 			    *symbol_name,
    112 			    function_offset,
    113 			    true_offset);
    114 
    115 		return 0;
    116 	}
    117 
    118 	/*
    119 	 * there is a bug in libunwind >= 1.0
    120 	 * after a set_tid_address syscall
    121 	 * unw_get_reg returns IP == 0
    122 	 */
    123 	if (ip)
    124 		error_action(data, "unexpected_backtracing_error", ip);
    125 	return -1;
    126 }
    127 
    128 static void
    129 walk(struct tcb *tcp,
    130      unwind_call_action_fn call_action,
    131      unwind_error_action_fn error_action,
    132      void *data)
    133 {
    134 	char *symbol_name;
    135 	size_t symbol_name_size = 40;
    136 	unw_cursor_t cursor;
    137 	int stack_depth;
    138 
    139 	if (!tcp->mmap_cache)
    140 		error_func_msg_and_die("mmap_cache is NULL");
    141 
    142 	symbol_name = xmalloc(symbol_name_size);
    143 
    144 	if (unw_init_remote(&cursor, libunwind_as, tcp->unwind_ctx) < 0)
    145 		perror_func_msg_and_die("cannot initialize libunwind");
    146 
    147 	for (stack_depth = 0; stack_depth < 256; ++stack_depth) {
    148 		if (print_stack_frame(tcp, call_action, error_action, data,
    149 				&cursor, &symbol_name, &symbol_name_size) < 0)
    150 			break;
    151 		if (unw_step(&cursor) <= 0)
    152 			break;
    153 	}
    154 	if (stack_depth >= 256)
    155 		error_action(data, "too many stack frames", 0);
    156 
    157 	free(symbol_name);
    158 }
    159 
    160 static void
    161 tcb_walk(struct tcb *tcp,
    162 	 unwind_call_action_fn call_action,
    163 	 unwind_error_action_fn error_action,
    164 	 void *data)
    165 {
    166 	switch (mmap_cache_rebuild_if_invalid(tcp, __func__)) {
    167 		case MMAP_CACHE_REBUILD_RENEWED:
    168 			/*
    169 			 * Rebuild the unwinder internal cache.
    170 			 * Called when mmap cache subsystem detects a
    171 			 * change of tracee memory mapping.
    172 			 */
    173 			unw_flush_cache(libunwind_as, 0, 0);
    174 			ATTRIBUTE_FALLTHROUGH;
    175 		case MMAP_CACHE_REBUILD_READY:
    176 			walk(tcp, call_action, error_action, data);
    177 			break;
    178 		default:
    179 			/* Do nothing */
    180 			;
    181 	}
    182 }
    183 
    184 const struct unwind_unwinder_t unwinder = {
    185 	.name = "libunwind",
    186 	.init = init,
    187 	.tcb_init = tcb_init,
    188 	.tcb_fin = tcb_fin,
    189 	.tcb_walk = tcb_walk,
    190 };
    191