/* * This file is based on a patch submitted by Mark Wielaard * to ltrace project: * https://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=dfefa9f057857735a073ea655f5cb34351032c8e * * It was re-licensed for strace by the original author: * https://lists.strace.io/pipermail/strace-devel/2018-March/008063.html * * Copyright (c) 2014-2018 Mark Wielaard * Copyright (c) 2018 Masatake YAMATO * Copyright (c) 2018 The strace developers. * All rights reserved. * * 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_notify.h" #include struct ctx { Dwfl *dwfl; unsigned int last_proc_updating; }; static unsigned int mapping_generation; static void update_mapping_generation(struct tcb *tcp, void *unused) { mapping_generation++; } static void init(void) { mmap_notify_register_client(update_mapping_generation, NULL); } static void * tcb_init(struct tcb *tcp) { static const Dwfl_Callbacks proc_callbacks = { .find_elf = dwfl_linux_proc_find_elf, .find_debuginfo = dwfl_standard_find_debuginfo }; Dwfl *dwfl = dwfl_begin(&proc_callbacks); if (dwfl == NULL) { error_msg("dwfl_begin: %s", dwfl_errmsg(-1)); return NULL; } int r = dwfl_linux_proc_attach(dwfl, tcp->pid, true); if (r) { const char *msg = NULL; if (r < 0) msg = dwfl_errmsg(-1); else if (r > 0) msg = strerror(r); error_msg("dwfl_linux_proc_attach returned an error" " for process %d: %s", tcp->pid, msg); dwfl_end(dwfl); return NULL; } struct ctx *ctx = xmalloc(sizeof(*ctx)); ctx->dwfl = dwfl; ctx->last_proc_updating = 0; return ctx; } static void tcb_fin(struct tcb *tcp) { struct ctx *ctx = tcp->unwind_ctx; if (ctx) { dwfl_end(ctx->dwfl); free(ctx); } } static void flush_cache_maybe(struct tcb *tcp) { struct ctx *ctx = tcp->unwind_ctx; if (!ctx) return; if (ctx->last_proc_updating == mapping_generation) return; int r = dwfl_linux_proc_report(ctx->dwfl, tcp->pid); if (r < 0) error_msg("dwfl_linux_proc_report returned an error" " for pid %d: %s", tcp->pid, dwfl_errmsg(-1)); else if (r > 0) error_msg("dwfl_linux_proc_report returned an error" " for pid %d", tcp->pid); else if (dwfl_report_end(ctx->dwfl, NULL, NULL) != 0) error_msg("dwfl_report_end returned an error" " for pid %d: %s", tcp->pid, dwfl_errmsg(-1)); ctx->last_proc_updating = mapping_generation; } struct frame_user_data { unwind_call_action_fn call_action; unwind_error_action_fn error_action; void *data; int stack_depth; }; static int frame_callback(Dwfl_Frame *state, void *arg) { struct frame_user_data *user_data = arg; Dwarf_Addr pc; bool isactivation; if (!dwfl_frame_pc(state, &pc, &isactivation)) { /* Propagate the error to the caller. */ return -1; } if (!isactivation) pc--; Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state)); Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc); GElf_Off off = 0; if (mod != NULL) { const char *modname = NULL; const char *symname = NULL; GElf_Sym sym; Dwarf_Addr true_offset = pc; modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL, NULL, NULL, NULL); symname = dwfl_module_addrinfo(mod, pc, &off, &sym, NULL, NULL, NULL); dwfl_module_relocate_address(mod, &true_offset); user_data->call_action(user_data->data, modname, symname, off, true_offset); } /* Max number of frames to print reached? */ if (user_data->stack_depth-- == 0) return DWARF_CB_ABORT; return DWARF_CB_OK; } static void tcb_walk(struct tcb *tcp, unwind_call_action_fn call_action, unwind_error_action_fn error_action, void *data) { struct ctx *ctx = tcp->unwind_ctx; if (!ctx) return; struct frame_user_data user_data = { .call_action = call_action, .error_action = error_action, .data = data, .stack_depth = 256, }; flush_cache_maybe(tcp); int r = dwfl_getthread_frames(ctx->dwfl, tcp->pid, frame_callback, &user_data); if (r) error_action(data, r < 0 ? dwfl_errmsg(-1) : "too many stack frames", 0); } const struct unwind_unwinder_t unwinder = { .name = "libdw", .init = init, .tcb_init = tcb_init, .tcb_fin = tcb_fin, .tcb_walk = tcb_walk, };