]> granicus.if.org Git - strace/blob - unwind-libdw.c
clone: fix print_tls_arg on x86
[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  * SPDX-License-Identifier: LGPL-2.1-or-later
15  */
16
17 #include "defs.h"
18 #include "unwind.h"
19 #include "mmap_notify.h"
20 #include <elfutils/libdwfl.h>
21
22 struct ctx {
23         Dwfl *dwfl;
24         unsigned int last_proc_updating;
25 };
26
27 static unsigned int mapping_generation;
28
29 static void
30 update_mapping_generation(struct tcb *tcp, void *unused)
31 {
32         mapping_generation++;
33 }
34
35 static void
36 init(void)
37 {
38         mmap_notify_register_client(update_mapping_generation, NULL);
39 }
40
41 static void *
42 tcb_init(struct tcb *tcp)
43 {
44         static const Dwfl_Callbacks proc_callbacks = {
45                 .find_elf = dwfl_linux_proc_find_elf,
46                 .find_debuginfo = dwfl_standard_find_debuginfo
47         };
48
49         Dwfl *dwfl = dwfl_begin(&proc_callbacks);
50         if (dwfl == NULL) {
51                 error_msg("dwfl_begin: %s", dwfl_errmsg(-1));
52                 return NULL;
53         }
54
55         int r = dwfl_linux_proc_attach(dwfl, tcp->pid, true);
56         if (r) {
57                 const char *msg = NULL;
58
59                 if (r < 0)
60                         msg = dwfl_errmsg(-1);
61                 else if (r > 0)
62                         msg = strerror(r);
63
64                 error_msg("dwfl_linux_proc_attach returned an error"
65                           " for process %d: %s", tcp->pid, msg);
66                 dwfl_end(dwfl);
67                 return NULL;
68         }
69
70         struct ctx *ctx = xmalloc(sizeof(*ctx));
71         ctx->dwfl = dwfl;
72         ctx->last_proc_updating = 0;
73         return ctx;
74 }
75
76 static void
77 tcb_fin(struct tcb *tcp)
78 {
79         struct ctx *ctx = tcp->unwind_ctx;
80         if (ctx) {
81                 dwfl_end(ctx->dwfl);
82                 free(ctx);
83         }
84 }
85
86 static void
87 flush_cache_maybe(struct tcb *tcp)
88 {
89         struct ctx *ctx = tcp->unwind_ctx;
90         if (!ctx)
91                 return;
92
93         if (ctx->last_proc_updating == mapping_generation)
94                 return;
95
96         int r = dwfl_linux_proc_report(ctx->dwfl, tcp->pid);
97
98         if (r < 0)
99                 error_msg("dwfl_linux_proc_report returned an error"
100                           " for pid %d: %s", tcp->pid, dwfl_errmsg(-1));
101         else if (r > 0)
102                 error_msg("dwfl_linux_proc_report returned an error"
103                           " for pid %d", tcp->pid);
104         else if (dwfl_report_end(ctx->dwfl, NULL, NULL) != 0)
105                 error_msg("dwfl_report_end returned an error"
106                           " for pid %d: %s", tcp->pid, dwfl_errmsg(-1));
107
108         ctx->last_proc_updating = mapping_generation;
109 }
110
111 struct frame_user_data {
112         unwind_call_action_fn call_action;
113         unwind_error_action_fn error_action;
114         void *data;
115         int stack_depth;
116 };
117
118 static int
119 frame_callback(Dwfl_Frame *state, void *arg)
120 {
121         struct frame_user_data *user_data = arg;
122         Dwarf_Addr pc;
123         bool isactivation;
124
125         if (!dwfl_frame_pc(state, &pc, &isactivation)) {
126                 /* Propagate the error to the caller.  */
127                 return -1;
128         }
129
130         if (!isactivation)
131                 pc--;
132
133         Dwfl *dwfl = dwfl_thread_dwfl(dwfl_frame_thread(state));
134         Dwfl_Module *mod = dwfl_addrmodule(dwfl, pc);
135         GElf_Off off = 0;
136
137         if (mod != NULL) {
138                 const char *modname = NULL;
139                 const char *symname = NULL;
140                 GElf_Sym sym;
141                 Dwarf_Addr true_offset = pc;
142
143                 modname = dwfl_module_info(mod, NULL, NULL, NULL, NULL,
144                                            NULL, NULL, NULL);
145                 symname = dwfl_module_addrinfo(mod, pc, &off, &sym,
146                                                NULL, NULL, NULL);
147                 dwfl_module_relocate_address(mod, &true_offset);
148                 user_data->call_action(user_data->data, modname, symname,
149                                        off, true_offset);
150         }
151         /* Max number of frames to print reached? */
152         if (user_data->stack_depth-- == 0)
153                 return DWARF_CB_ABORT;
154
155         return DWARF_CB_OK;
156 }
157
158 static void
159 tcb_walk(struct tcb *tcp,
160          unwind_call_action_fn call_action,
161          unwind_error_action_fn error_action,
162          void *data)
163 {
164         struct ctx *ctx = tcp->unwind_ctx;
165         if (!ctx)
166                 return;
167
168         struct frame_user_data user_data = {
169                 .call_action = call_action,
170                 .error_action = error_action,
171                 .data = data,
172                 .stack_depth = 256,
173         };
174
175         flush_cache_maybe(tcp);
176
177         int r = dwfl_getthread_frames(ctx->dwfl, tcp->pid, frame_callback,
178                                       &user_data);
179         if (r)
180                 error_action(data,
181                              r < 0 ? dwfl_errmsg(-1) : "too many stack frames",
182                              0);
183 }
184
185 const struct unwind_unwinder_t unwinder = {
186         .name = "libdw",
187         .init = init,
188         .tcb_init = tcb_init,
189         .tcb_fin = tcb_fin,
190         .tcb_walk = tcb_walk,
191 };