| /* |
| * 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) |
| { |
| mmap_cache_enable(); |
| |
| 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; |
| } |
| |
| struct mmap_cache_entry_t *entry = mmap_cache_search(tcp, ip); |
| |
| if (entry |
| /* ignore mappings that have no PROT_EXEC bit set */ |
| && (entry->protections & MMAP_CACHE_PROT_EXECUTABLE)) { |
| unw_word_t function_offset; |
| |
| get_symbol_name(cursor, symbol_name, symbol_name_size, |
| &function_offset); |
| unsigned long true_offset = |
| ip - entry->start_addr + entry->mmap_offset; |
| call_action(data, |
| entry->binary_filename, |
| *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 |
| walk(struct tcb *tcp, |
| unwind_call_action_fn call_action, |
| unwind_error_action_fn error_action, |
| void *data) |
| { |
| 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 |
| tcb_walk(struct tcb *tcp, |
| unwind_call_action_fn call_action, |
| unwind_error_action_fn error_action, |
| void *data) |
| { |
| 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 */ |
| ; |
| } |
| } |
| |
| const struct unwind_unwinder_t unwinder = { |
| .name = "libunwind", |
| .init = init, |
| .tcb_init = tcb_init, |
| .tcb_fin = tcb_fin, |
| .tcb_walk = tcb_walk, |
| }; |