]> sourceware.org Git - systemtap.git/blob - setupdwfl.cxx
PR10678 module reloc refers to symbol in dwarf refer to kernel symbols.
[systemtap.git] / setupdwfl.cxx
1 // Setup routines for creating fully populated DWFLs. Used in pass 2 and 3.
2 // Copyright (C) 2009 Red Hat, Inc.
3 //
4 // This file is part of systemtap, and is free software. You can
5 // redistribute it and/or modify it under the terms of the GNU General
6 // Public License (GPL); either version 2, or (at your option) any
7 // later version.
8
9 #include "config.h"
10 #include "setupdwfl.h"
11
12 #include "dwarf_wrappers.h"
13 #include "dwflpp.h"
14 #include "session.h"
15
16 #include <set>
17 #include <string>
18
19 extern "C" {
20 #include <fnmatch.h>
21 #include <stdlib.h>
22 }
23
24 static const char *debuginfo_path_arr = "+:.debug:/usr/lib/debug:build";
25 static const char *debuginfo_env_arr = getenv("SYSTEMTAP_DEBUGINFO_PATH");
26 static const char *debuginfo_path = (debuginfo_env_arr ?: debuginfo_path_arr);
27
28 // NB: kernel_build_tree doesn't enter into this, as it's for
29 // kernel-side modules only.
30 static const char *debuginfo_usr_path_arr = "+:.debug:/usr/lib/debug";
31 static const char *debuginfo_usr_path = (debuginfo_env_arr
32 ?: debuginfo_usr_path_arr);
33
34 static const Dwfl_Callbacks kernel_callbacks =
35 {
36 dwfl_linux_kernel_find_elf,
37 dwfl_standard_find_debuginfo,
38 dwfl_offline_section_address,
39 (char **) & debuginfo_path
40 };
41
42 static const Dwfl_Callbacks user_callbacks =
43 {
44 NULL,
45 dwfl_standard_find_debuginfo,
46 NULL, /* ET_REL not supported for user space, only ET_EXEC and ET_DYN.
47 dwfl_offline_section_address, */
48 (char **) & debuginfo_usr_path
49 };
50
51 using namespace std;
52
53 // Store last kernel and user Dwfl for reuse since they are often
54 // re-requested (in phase 2 and then in phase 3).
55 static DwflPtr kernel_dwfl;
56 static DwflPtr user_dwfl;
57
58 // Setup in setup_dwfl_kernel(), for use in setup_dwfl_report_kernel_p().
59 // Either offline_search_modname or offline_search_names is
60 // used. When offline_search_modname is not NULL then
61 // offline_search_names is ignored.
62 static const char *offline_search_modname;
63 static set<string> offline_search_names;
64 static unsigned offline_modules_found;
65
66 // Whether or not we are done reporting kernel modules in
67 // set_dwfl_report_kernel_p().
68 static bool setup_dwfl_done;
69
70 // Kept for user_dwfl cache, user modules don't allow wildcards, so
71 // just keep the set of module strings.
72 static set<string> user_modset;
73
74 // Determines whether or not we will make setup_dwfl_report_kernel_p
75 // report true for all module dependencies. This is necessary for
76 // correctly resolving some dwarf constructs that relocate against
77 // symbols in vmlinux and/or other modules they depend on. See PR10678.
78 static const bool setup_all_deps = true;
79
80 // Set up our offline search for kernel modules. We don't want the
81 // offline search iteration to do a complete search of the kernel
82 // build tree, since that's wasteful, so create a predicate that
83 // filters and stops reporting as soon as we got everything.
84 static int
85 setup_dwfl_report_kernel_p(const char* modname, const char* filename)
86 {
87 if (pending_interrupts || setup_dwfl_done)
88 return -1;
89
90 // elfutils sends us NULL filenames sometimes if it can't find dwarf
91 if (filename == NULL)
92 return 0;
93
94 // Check kernel first since it is often the only thing needed,
95 // then we never have to parse and setup the module deps map.
96 // It will be reported as the very first thing.
97 if (setup_all_deps && ! strcmp (modname, "kernel"))
98 {
99 if ((offline_search_modname != NULL
100 && ! strcmp (offline_search_modname, "kernel"))
101 || (offline_search_names.size() == 1
102 && *offline_search_names.begin() == "kernel"))
103 setup_dwfl_done = true;
104
105 offline_modules_found++;
106 return 1;
107 }
108
109 // If offline_search_modname is setup use it (either as regexp or
110 // explicit module/kernel name) and ignore offline_search_names.
111 // Otherwise use offline_search_names exclusively.
112 if (offline_search_modname != NULL)
113 {
114 if (dwflpp::name_has_wildcard (offline_search_modname))
115 {
116 int match_p = !fnmatch(offline_search_modname, modname, 0);
117 // In the wildcard case, we don't short-circuit (return -1)
118 // analogously to dwflpp::module_name_final_match().
119 if (match_p)
120 offline_modules_found++;
121 return match_p;
122 }
123 else
124 { /* non-wildcard mode, reject mismatching module names */
125 if (strcmp(modname, offline_search_modname))
126 return 0;
127 else
128 {
129 // Done, only one name needed and found it.
130 offline_modules_found++;
131 setup_dwfl_done = true;
132 return 1;
133 }
134 }
135 }
136 else
137 { /* find all in set mode, reject mismatching module names */
138 if (offline_search_names.find(modname) == offline_search_names.end())
139 return 0;
140 else
141 {
142 offline_modules_found++;
143 if (offline_search_names.size() == offline_modules_found)
144 setup_dwfl_done = true;
145 return 1;
146 }
147 }
148 }
149
150 static DwflPtr
151 setup_dwfl_kernel (unsigned *modules_found, systemtap_session &s)
152 {
153 Dwfl *dwfl = dwfl_begin (&kernel_callbacks);
154 dwfl_assert ("dwfl_begin", dwfl);
155 dwfl_report_begin (dwfl);
156
157 // We have a problem with -r REVISION vs -r BUILDDIR here. If
158 // we're running against a fedora/rhel style kernel-debuginfo
159 // tree, s.kernel_build_tree is not the place where the unstripped
160 // vmlinux will be installed. Rather, it's over yonder at
161 // /usr/lib/debug/lib/modules/$REVISION/. It seems that there is
162 // no way to set the dwfl_callback.debuginfo_path and always
163 // passs the plain kernel_release here. So instead we have to
164 // hard-code this magic here.
165 string elfutils_kernel_path;
166 if (s.kernel_build_tree == string("/lib/modules/"
167 + s.kernel_release
168 + "/build"))
169 elfutils_kernel_path = s.kernel_release;
170 else
171 elfutils_kernel_path = s.kernel_build_tree;
172
173 offline_modules_found = 0;
174
175 // First try to report full path modules.
176 set<string>::iterator it = offline_search_names.begin();
177 while (it != offline_search_names.end())
178 {
179 if ((*it)[0] == '/')
180 {
181 const char *cname = (*it).c_str();
182 Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
183 if (mod)
184 offline_modules_found++;
185 }
186 it++;
187 }
188
189 // We always need this, even when offline_search_modname is NULL
190 // and offline_search_names is empty because we still might want
191 // the kernel vmlinux reported.
192 setup_dwfl_done = false;
193 int rc = dwfl_linux_kernel_report_offline (dwfl,
194 elfutils_kernel_path.c_str(),
195 &setup_dwfl_report_kernel_p);
196
197 (void) rc; /* Ignore since the predicate probably returned -1 at some point,
198 And libdwfl interprets that as "whole query failed" rather than
199 "found it already, stop looking". */
200
201 // NB: the result of an _offline call is the assignment of
202 // virtualized addresses to relocatable objects such as
203 // modules. These have to be converted to real addresses at
204 // run time. See the dwarf_derived_probe ctor and its caller.
205
206 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
207 *modules_found = offline_modules_found;
208
209 StapDwfl *stap_dwfl = new StapDwfl(dwfl);
210 kernel_dwfl = DwflPtr(stap_dwfl);
211
212 return kernel_dwfl;
213 }
214
215 DwflPtr
216 setup_dwfl_kernel(const std::string &name,
217 unsigned *found,
218 systemtap_session &s)
219 {
220 const char *modname = name.c_str();
221 set<string> names; // Default to empty
222
223 /* Support full path kernel modules, these cannot be regular
224 expressions, so just put them in the search set. */
225 if (name[0] == '/' || ! dwflpp::name_has_wildcard (modname))
226 {
227 names.insert(name);
228 modname = NULL;
229 }
230
231 if (kernel_dwfl != NULL
232 && offline_search_modname == modname
233 && offline_search_names == names)
234 {
235 *found = offline_modules_found;
236 return kernel_dwfl;
237 }
238
239 offline_search_modname = modname;
240 offline_search_names = names;
241
242 return setup_dwfl_kernel(found, s);
243 }
244
245 DwflPtr
246 setup_dwfl_kernel(const std::set<std::string> &names,
247 unsigned *found,
248 systemtap_session &s)
249 {
250 if (kernel_dwfl != NULL
251 && offline_search_modname == NULL
252 && offline_search_names == names)
253 {
254 *found = offline_modules_found;
255 return kernel_dwfl;
256 }
257
258 offline_search_modname = NULL;
259 offline_search_names = names;
260 return setup_dwfl_kernel(found, s);
261 }
262
263 DwflPtr
264 setup_dwfl_user(const std::string &name)
265 {
266 if (user_dwfl != NULL
267 && user_modset.size() == 1
268 && (*user_modset.begin()) == name)
269 return user_dwfl;
270
271 user_modset.clear();
272 user_modset.insert(name);
273
274 Dwfl *dwfl = dwfl_begin (&user_callbacks);
275 dwfl_assert("dwfl_begin", dwfl);
276 dwfl_report_begin (dwfl);
277
278 // XXX: should support buildid-based naming
279 const char *cname = name.c_str();
280 Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
281 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
282 if (! mod)
283 {
284 dwfl_end(dwfl);
285 dwfl = NULL;
286 }
287
288 StapDwfl *stap_dwfl = new StapDwfl(dwfl);
289 user_dwfl = DwflPtr(stap_dwfl);
290
291 return user_dwfl;
292 }
293
294 DwflPtr
295 setup_dwfl_user(std::vector<std::string>::const_iterator &begin,
296 const std::vector<std::string>::const_iterator &end,
297 bool all_needed)
298 {
299 // See if we have this dwfl already cached
300 set<string> modset(begin, end);
301 if (user_dwfl != NULL && modset == user_modset)
302 return user_dwfl;
303
304 user_modset = modset;
305
306 Dwfl *dwfl = dwfl_begin (&user_callbacks);
307 dwfl_assert("dwfl_begin", dwfl);
308 dwfl_report_begin (dwfl);
309
310 // XXX: should support buildid-based naming
311 while (begin != end && dwfl != NULL)
312 {
313 const char *cname = (*begin).c_str();
314 Dwfl_Module *mod = dwfl_report_offline (dwfl, cname, cname, -1);
315 if (! mod && all_needed)
316 {
317 dwfl_end(dwfl);
318 dwfl = NULL;
319 }
320 begin++;
321 }
322 if (dwfl)
323 dwfl_assert ("dwfl_report_end", dwfl_report_end(dwfl, NULL, NULL));
324
325 StapDwfl *stap_dwfl = new StapDwfl(dwfl);
326 user_dwfl = DwflPtr(stap_dwfl);
327
328 return user_dwfl;
329 }
330
331 bool
332 is_user_module(const std::string &m)
333 {
334 return m[0] == '/' && m.rfind(".ko", m.length() - 1) != m.length() - 3;
335 }
This page took 0.103664 seconds and 6 git commands to generate.