]> granicus.if.org Git - strace/blob - unwind-libdw.c
Add support for /dev/[u]random ioctls
[strace] / unwind-libdw.c
1 /*
2  * This file is based on a patch submitted by Mark Wielaard <mjw@redhat.com>
3  * to ltrace project:
4  * https://anonscm.debian.org/cgit/collab-maint/ltrace.git/commit/?id=dfefa9f057857735a073ea655f5cb34351032c8e
5  *
6  * It was re-licensed for strace by the original author:
7  * https://lists.strace.io/pipermail/strace-devel/2018-March/008063.html
8  *
9  * Copyright (c) 2014-2018 Mark Wielaard <mjw@redhat.com>
10  * Copyright (c) 2018 Masatake YAMATO <yamato@redhat.com>
11  * Copyright (c) 2018 The strace developers.
12  * All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  * 3. The name of the author may not be used to endorse or promote products
23  *    derived from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36
37 #include "defs.h"
38 #include "unwind.h"
39 #include "mmap_notify.h"
40 #include <elfutils/libdwfl.h>
41
42 struct ctx {
43         Dwfl *dwfl;
44         unsigned int last_proc_updating;
45 };
46
47 static unsigned int mapping_generation;
48
49 static void
50 update_mapping_generation(struct tcb *tcp, void *unused)
51 {
52         mapping_generation++;
53 }
54
55 static void
56 init(void)
57 {
58         mmap_notify_register_client(update_mapping_generation, NULL);
59 }
60
61 static void *
62 tcb_init(struct tcb *tcp)
63 {
64         static const Dwfl_Callbacks proc_callbacks = {
65                 .find_elf = dwfl_linux_proc_find_elf,
66                 .find_debuginfo = dwfl_standard_find_debuginfo
67         };
68
69         Dwfl *dwfl = dwfl_begin(&proc_callbacks);
70         if (dwfl == NULL) {
71                 error_msg("dwfl_begin: %s", dwfl_errmsg(-1));
72                 return NULL;
73         }
74
75         int r = dwfl_linux_proc_attach(dwfl, tcp->pid, true);
76         if (r) {
77                 const char *msg = NULL;
78
79                 if (r < 0)
80                         msg = dwfl_errmsg(-1);
81                 else if (r > 0)
82                         msg = strerror(r);
83
84                 error_msg("dwfl_linux_proc_attach returned an error"
85                           " for process %d: %s", tcp->pid, msg);
86                 dwfl_end(dwfl);
87                 return NULL;
88         }
89
90         struct ctx *ctx = xmalloc(sizeof(*ctx));
91         ctx->dwfl = dwfl;
92         ctx->last_proc_updating = 0;
93         return ctx;
94 }
95
96 static void
97 tcb_fin(struct tcb *tcp)
98 {
99         struct ctx *ctx = tcp->unwind_ctx;
100         if (ctx) {
101                 dwfl_end(ctx->dwfl);
102                 free(ctx);
103         }
104 }
105
106 static void
107 flush_cache_maybe(struct tcb *tcp)
108 {
109         struct ctx *ctx = tcp->unwind_ctx;
110         if (!ctx)
111                 return;
112
113         if (ctx->last_proc_updating == mapping_generation)
114                 return;
115
116         int r = dwfl_linux_proc_report(ctx->dwfl, tcp->pid);
117
118         if (r < 0)
119                 error_msg("dwfl_linux_proc_report returned an error"
120                           " for pid %d: %s", tcp->pid, dwfl_errmsg(-1));
121         else if (r > 0)
122                 error_msg("dwfl_linux_proc_report returned an error"
123                           " for pid %d", tcp->pid);
124         else if (dwfl_report_end(ctx->dwfl, NULL, NULL) != 0)
125                 error_msg("dwfl_report_end returned an error"
126                           " for pid %d: %s", tcp->pid, dwfl_errmsg(-1));
127
128         ctx->last_proc_updating = mapping_generation;
129 }
130
131 struct frame_user_data {
132         unwind_call_action_fn call_action;
133         unwind_error_action_fn error_action;
134         void *data;
135         int stack_depth;
136 };
137
138 static int
139 frame_callback(Dwfl_Frame *state, void *arg)
140 {
141         struct frame_user_data *user_data = arg;
142         Dwarf_Addr pc;
143         bool isactivation;
144
145         if (!dwfl_frame_pc(state, &pc, &isactivation)) {
146                 /* Propagate the error to the caller.  */
147                 return -1;
148         }
149
150         if (!isactivation)
151                 pc--;
152
153         Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state));
154         Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc);
155         GElf_Off off = 0;
156
157         if (mod != NULL) {
158                 const char *modname = NULL;
159                 const char *symname = NULL;
160                 GElf_Sym sym;
161                 Dwarf_Addr true_offset = pc;
162
163                 modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL,
164                                            NULL, NULL, NULL);
165                 symname = dwfl_module_addrinfo(mod, pc, &off, &sym,
166                                                NULL, NULL, NULL);
167                 dwfl_module_relocate_address(mod, &true_offset);
168                 user_data->call_action(user_data->data, modname, symname,
169                                        off, true_offset);
170         }
171         /* Max number of frames to print reached? */
172         if (user_data->stack_depth-- == 0)
173                 return DWARF_CB_ABORT;
174
175         return DWARF_CB_OK;
176 }
177
178 static void
179 tcb_walk(struct tcb *tcp,
180          unwind_call_action_fn call_action,
181          unwind_error_action_fn error_action,
182          void *data)
183 {
184         struct ctx *ctx = tcp->unwind_ctx;
185         if (!ctx)
186                 return;
187
188         struct frame_user_data user_data = {
189                 .call_action = call_action,
190                 .error_action = error_action,
191                 .data = data,
192                 .stack_depth = 256,
193         };
194
195         flush_cache_maybe(tcp);
196
197         int r = dwfl_getthread_frames(ctx->dwfl, tcp->pid, frame_callback,
198                                       &user_data);
199         if (r)
200                 error_action(data,
201                              r < 0 ? dwfl_errmsg(-1) : "too many stack frames",
202                              0);
203 }
204
205 const struct unwind_unwinder_t unwinder = {
206         .name = "libdw",
207         .init = init,
208         .tcb_init = tcb_init,
209         .tcb_fin = tcb_fin,
210         .tcb_walk = tcb_walk,
211 };