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