1 /* Standard argp argument parsers for tools using libdwfl.
2 Copyright (C) 2005-2010, 2012, 2015 Red Hat, Inc.
3 This file is part of elfutils.
4
5 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
7
8 * the GNU Lesser General Public License as published by the Free
9 Software Foundation; either version 3 of the License, or (at
10 your option) any later version
11
12 or
13
14 * the GNU General Public License as published by the Free
15 Software Foundation; either version 2 of the License, or (at
16 your option) any later version
17
18 or both in parallel, as here.
19
20 elfutils is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
24
25 You should have received copies of the GNU General Public License and
26 the GNU Lesser General Public License along with this program. If
27 not, see <http://www.gnu.org/licenses/>. */
28
29 #include "libdwflP.h"
30 #include <argp.h>
31 #include <stdlib.h>
32 #include <assert.h>
33 #include <libintl.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36
37 /* gettext helper macros. */
38 #define _(Str) dgettext ("elfutils", Str)
39
40
41 #define OPT_DEBUGINFO 0x100
42 #define OPT_COREFILE 0x101
43
44 static const struct argp_option options[] =
45 {
46 { NULL, 0, NULL, 0, N_("Input selection options:"), 0 },
47 { "executable", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 },
48 { "core", OPT_COREFILE, "COREFILE", 0,
49 N_("Find addresses from signatures found in COREFILE"), 0 },
50 { "pid", 'p', "PID", 0,
51 N_("Find addresses in files mapped into process PID"), 0 },
52 { "linux-process-map", 'M', "FILE", 0,
53 N_("Find addresses in files mapped as read from FILE"
54 " in Linux /proc/PID/maps format"), 0 },
55 { "kernel", 'k', NULL, 0, N_("Find addresses in the running kernel"), 0 },
56 { "offline-kernel", 'K', "RELEASE", OPTION_ARG_OPTIONAL,
57 N_("Kernel with all modules"), 0 },
58 { "debuginfo-path", OPT_DEBUGINFO, "PATH", 0,
59 N_("Search path for separate debuginfo files"), 0 },
60 { NULL, 0, NULL, 0, NULL, 0 }
61 };
62
63 static char *debuginfo_path;
64
65 static const Dwfl_Callbacks offline_callbacks =
66 {
67 .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
68 .debuginfo_path = &debuginfo_path,
69
70 .section_address = INTUSE(dwfl_offline_section_address),
71
72 /* We use this table for core files too. */
73 .find_elf = INTUSE(dwfl_build_id_find_elf),
74 };
75
76 static const Dwfl_Callbacks proc_callbacks =
77 {
78 .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
79 .debuginfo_path = &debuginfo_path,
80
81 .find_elf = INTUSE(dwfl_linux_proc_find_elf),
82 };
83
84 static const Dwfl_Callbacks kernel_callbacks =
85 {
86 .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
87 .debuginfo_path = &debuginfo_path,
88
89 .find_elf = INTUSE(dwfl_linux_kernel_find_elf),
90 .section_address = INTUSE(dwfl_linux_kernel_module_section_address),
91 };
92
93 /* Structure held at state->HOOK. */
94 struct parse_opt
95 {
96 Dwfl *dwfl;
97 /* The -e|--executable parameter. */
98 const char *e;
99 /* The --core parameter. */
100 const char *core;
101 };
102
103 static error_t
parse_opt(int key,char * arg,struct argp_state * state)104 parse_opt (int key, char *arg, struct argp_state *state)
105 {
106 inline void failure (Dwfl *dwfl, int errnum, const char *msg)
107 {
108 if (dwfl != NULL)
109 dwfl_end (dwfl);
110 if (errnum == -1)
111 argp_failure (state, EXIT_FAILURE, 0, "%s: %s",
112 msg, INTUSE(dwfl_errmsg) (-1));
113 else
114 argp_failure (state, EXIT_FAILURE, errnum, "%s", msg);
115 }
116 inline error_t fail (Dwfl *dwfl, int errnum, const char *msg)
117 {
118 failure (dwfl, errnum, msg);
119 return errnum == -1 ? EIO : errnum;
120 }
121
122 switch (key)
123 {
124 case ARGP_KEY_INIT:
125 {
126 assert (state->hook == NULL);
127 struct parse_opt *opt = calloc (1, sizeof (*opt));
128 if (opt == NULL)
129 failure (NULL, DWFL_E_ERRNO, "calloc");
130 state->hook = opt;
131 }
132 break;
133
134 case OPT_DEBUGINFO:
135 debuginfo_path = arg;
136 break;
137
138 case 'e':
139 {
140 struct parse_opt *opt = state->hook;
141 Dwfl *dwfl = opt->dwfl;
142 if (dwfl == NULL)
143 {
144 dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
145 if (dwfl == NULL)
146 return fail (dwfl, -1, arg);
147 opt->dwfl = dwfl;
148
149 /* Start at zero so if there is just one -e foo.so,
150 the DSO is shown without address bias. */
151 dwfl->offline_next_address = 0;
152 }
153 if (dwfl->callbacks != &offline_callbacks)
154 {
155 toomany:
156 argp_error (state, "%s",
157 _("only one of -e, -p, -k, -K, or --core allowed"));
158 return EINVAL;
159 }
160 opt->e = arg;
161 }
162 break;
163
164 case 'p':
165 {
166 struct parse_opt *opt = state->hook;
167 if (opt->dwfl == NULL)
168 {
169 Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
170 int result = INTUSE(dwfl_linux_proc_report) (dwfl, atoi (arg));
171 if (result != 0)
172 return fail (dwfl, result, arg);
173
174 /* Non-fatal to not be able to attach to process, ignore error. */
175 INTUSE(dwfl_linux_proc_attach) (dwfl, atoi (arg), false);
176
177 opt->dwfl = dwfl;
178 }
179 else
180 goto toomany;
181 }
182 break;
183
184 case 'M':
185 {
186 struct parse_opt *opt = state->hook;
187 if (opt->dwfl == NULL)
188 {
189 FILE *f = fopen (arg, "r");
190 if (f == NULL)
191 {
192 int code = errno;
193 argp_failure (state, EXIT_FAILURE, code,
194 "cannot open '%s'", arg);
195 return code;
196 }
197 Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
198 int result = INTUSE(dwfl_linux_proc_maps_report) (dwfl, f);
199 fclose (f);
200 if (result != 0)
201 return fail (dwfl, result, arg);
202 opt->dwfl = dwfl;
203 }
204 else
205 goto toomany;
206 }
207 break;
208
209 case OPT_COREFILE:
210 {
211 struct parse_opt *opt = state->hook;
212 Dwfl *dwfl = opt->dwfl;
213 if (dwfl == NULL)
214 opt->dwfl = dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
215 /* Permit -e and --core together. */
216 else if (dwfl->callbacks != &offline_callbacks)
217 goto toomany;
218 opt->core = arg;
219 }
220 break;
221
222 case 'k':
223 {
224 struct parse_opt *opt = state->hook;
225 if (opt->dwfl == NULL)
226 {
227 Dwfl *dwfl = INTUSE(dwfl_begin) (&kernel_callbacks);
228 int result = INTUSE(dwfl_linux_kernel_report_kernel) (dwfl);
229 if (result != 0)
230 return fail (dwfl, result, _("cannot load kernel symbols"));
231 result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl);
232 if (result != 0)
233 /* Non-fatal to have no modules since we do have the kernel. */
234 failure (dwfl, result, _("cannot find kernel modules"));
235 opt->dwfl = dwfl;
236 }
237 else
238 goto toomany;
239 }
240 break;
241
242 case 'K':
243 {
244 struct parse_opt *opt = state->hook;
245 if (opt->dwfl == NULL)
246 {
247 Dwfl *dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
248 int result = INTUSE(dwfl_linux_kernel_report_offline) (dwfl, arg,
249 NULL);
250 if (result != 0)
251 return fail (dwfl, result, _("cannot find kernel or modules"));
252 opt->dwfl = dwfl;
253 }
254 else
255 goto toomany;
256 }
257 break;
258
259 case ARGP_KEY_SUCCESS:
260 {
261 struct parse_opt *opt = state->hook;
262 Dwfl *dwfl = opt->dwfl;
263
264 if (dwfl == NULL)
265 {
266 /* Default if no -e, -p, or -k, is "-e a.out". */
267 arg = "a.out";
268 dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
269 if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL)
270 return fail (dwfl, -1, arg);
271 opt->dwfl = dwfl;
272 }
273
274 if (opt->core)
275 {
276 int fd = open (opt->core, O_RDONLY);
277 if (fd < 0)
278 {
279 int code = errno;
280 argp_failure (state, EXIT_FAILURE, code,
281 "cannot open '%s'", opt->core);
282 return code;
283 }
284
285 Elf *core;
286 Dwfl_Error error = __libdw_open_file (&fd, &core, true, false);
287 if (error != DWFL_E_NOERROR)
288 {
289 argp_failure (state, EXIT_FAILURE, 0,
290 _("cannot read ELF core file: %s"),
291 INTUSE(dwfl_errmsg) (error));
292 return error == DWFL_E_ERRNO ? errno : EIO;
293 }
294
295 int result = INTUSE(dwfl_core_file_report) (dwfl, core, opt->e);
296 if (result < 0)
297 {
298 elf_end (core);
299 close (fd);
300 return fail (dwfl, result, opt->core);
301 }
302
303 /* Non-fatal to not be able to attach to core, ignore error. */
304 INTUSE(dwfl_core_file_attach) (dwfl, core);
305
306 /* Store core Elf and fd in Dwfl to expose with dwfl_end. */
307 if (dwfl->user_core == NULL)
308 {
309 dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core));
310 if (dwfl->user_core == NULL)
311 {
312 argp_failure (state, EXIT_FAILURE, 0,
313 _("Not enough memory"));
314 return ENOMEM;
315 }
316 }
317 dwfl->user_core->core = core;
318 dwfl->user_core->fd = fd;
319
320 if (result == 0)
321 {
322 argp_failure (state, EXIT_FAILURE, 0,
323 _("No modules recognized in core file"));
324 return ENOENT;
325 }
326 }
327 else if (opt->e)
328 {
329 if (INTUSE(dwfl_report_offline) (dwfl, "", opt->e, -1) == NULL)
330 return fail (dwfl, -1, opt->e);
331 }
332
333 /* One of the three flavors has done dwfl_begin and some reporting
334 if we got here. Tie up the Dwfl and return it to the caller of
335 argp_parse. */
336
337 int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL);
338 assert (result == 0);
339
340 /* Update the input all along, so a parent parser can see it.
341 As we free OPT the update below will be no longer active. */
342 *(Dwfl **) state->input = dwfl;
343 free (opt);
344 state->hook = NULL;
345 }
346 break;
347
348 case ARGP_KEY_ERROR:
349 {
350 struct parse_opt *opt = state->hook;
351 dwfl_end (opt->dwfl);
352 free (opt);
353 state->hook = NULL;
354 }
355 break;
356
357 default:
358 return ARGP_ERR_UNKNOWN;
359 }
360
361 /* Update the input all along, so a parent parser can see it. */
362 struct parse_opt *opt = state->hook;
363 if (opt)
364 *(Dwfl **) state->input = opt->dwfl;
365
366 return 0;
367 }
368
369 static const struct argp libdwfl_argp =
370 { .options = options, .parser = parse_opt };
371
372 const struct argp *
dwfl_standard_argp(void)373 dwfl_standard_argp (void)
374 {
375 return &libdwfl_argp;
376 }
377