Imported Upstream version 0.160
[platform/upstream/elfutils.git] / libdwfl / argp-std.c
1 /* Standard argp argument parsers for tools using libdwfl.
2    Copyright (C) 2005-2010, 2012 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
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 = open64 (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             /* From now we leak FD and CORE.  */
307
308             if (result == 0)
309               {
310                 argp_failure (state, EXIT_FAILURE, 0,
311                               _("No modules recognized in core file"));
312                 return ENOENT;
313               }
314           }
315         else if (opt->e)
316           {
317             if (INTUSE(dwfl_report_offline) (dwfl, "", opt->e, -1) == NULL)
318               return fail (dwfl, -1, opt->e);
319           }
320
321         /* One of the three flavors has done dwfl_begin and some reporting
322            if we got here.  Tie up the Dwfl and return it to the caller of
323            argp_parse.  */
324
325         int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL);
326         assert (result == 0);
327
328         /* Update the input all along, so a parent parser can see it.
329            As we free OPT the update below will be no longer active.  */
330         *(Dwfl **) state->input = dwfl;
331         free (opt);
332         state->hook = NULL;
333       }
334       break;
335
336     case ARGP_KEY_ERROR:
337       {
338         struct parse_opt *opt = state->hook;
339         dwfl_end (opt->dwfl);
340         free (opt);
341         state->hook = NULL;
342       }
343       break;
344
345     default:
346       return ARGP_ERR_UNKNOWN;
347     }
348
349   /* Update the input all along, so a parent parser can see it.  */
350   struct parse_opt *opt = state->hook;
351   if (opt)
352     *(Dwfl **) state->input = opt->dwfl;
353
354   return 0;
355 }
356
357 static const struct argp libdwfl_argp =
358   { .options = options, .parser = parse_opt };
359
360 const struct argp *
361 dwfl_standard_argp (void)
362 {
363   return &libdwfl_argp;
364 }