Imported Upstream version 0.153
[platform/upstream/elfutils.git] / libdwfl / argp-std.c
1 /* Standard argp argument parsers for tools using libdwfl.
2    Copyright (C) 2005-2010 Red Hat, Inc.
3    This file is part of Red Hat elfutils.
4
5    Red Hat elfutils is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by the
7    Free Software Foundation; version 2 of the License.
8
9    Red Hat elfutils is distributed in the hope that it will be useful, but
10    WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13
14    You should have received a copy of the GNU General Public License along
15    with Red Hat elfutils; if not, write to the Free Software Foundation,
16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
17
18    In addition, as a special exception, Red Hat, Inc. gives You the
19    additional right to link the code of Red Hat elfutils with code licensed
20    under any Open Source Initiative certified open source license
21    (http://www.opensource.org/licenses/index.php) which requires the
22    distribution of source code with any binary distribution and to
23    distribute linked combinations of the two.  Non-GPL Code permitted under
24    this exception must only link to the code of Red Hat elfutils through
25    those well defined interfaces identified in the file named EXCEPTION
26    found in the source code files (the "Approved Interfaces").  The files
27    of Non-GPL Code may instantiate templates or use macros or inline
28    functions from the Approved Interfaces without causing the resulting
29    work to be covered by the GNU General Public License.  Only Red Hat,
30    Inc. may make changes or additions to the list of Approved Interfaces.
31    Red Hat's grant of this exception is conditioned upon your not adding
32    any new exceptions.  If you wish to add a new Approved Interface or
33    exception, please contact Red Hat.  You must obey the GNU General Public
34    License in all respects for all of the Red Hat elfutils code and other
35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
36    covered by this exception.  If you modify this file, you may extend this
37    exception to your version of the file, but you are not obligated to do
38    so.  If you do not wish to provide this exception without modification,
39    you must delete this exception statement from your version and license
40    this file solely under the GPL without exception.
41
42    Red Hat elfutils is an included package of the Open Invention Network.
43    An included package of the Open Invention Network is a package for which
44    Open Invention Network licensees cross-license their patents.  No patent
45    license is granted, either expressly or impliedly, by designation as an
46    included package.  Should you wish to participate in the Open Invention
47    Network licensing program, please visit www.openinventionnetwork.com
48    <http://www.openinventionnetwork.com>.  */
49
50 #include "libdwflP.h"
51 #include <argp.h>
52 #include <stdlib.h>
53 #include <assert.h>
54 #include <libintl.h>
55 #include <fcntl.h>
56 #include <unistd.h>
57
58 /* gettext helper macros.  */
59 #define _(Str) dgettext ("elfutils", Str)
60
61
62 #define OPT_DEBUGINFO   0x100
63 #define OPT_COREFILE    0x101
64
65 static const struct argp_option options[] =
66 {
67   { NULL, 0, NULL, 0, N_("Input selection options:"), 0 },
68   { "executable", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 },
69   { "core", OPT_COREFILE, "COREFILE", 0,
70     N_("Find addresses from signatures found in COREFILE"), 0 },
71   { "pid", 'p', "PID", 0,
72     N_("Find addresses in files mapped into process PID"), 0 },
73   { "linux-process-map", 'M', "FILE", 0,
74     N_("Find addresses in files mapped as read from FILE"
75        " in Linux /proc/PID/maps format"), 0 },
76   { "kernel", 'k', NULL, 0, N_("Find addresses in the running kernel"), 0 },
77   { "offline-kernel", 'K', "RELEASE", OPTION_ARG_OPTIONAL,
78     N_("Kernel with all modules"), 0 },
79   { "debuginfo-path", OPT_DEBUGINFO, "PATH", 0,
80     N_("Search path for separate debuginfo files"), 0 },
81   { NULL, 0, NULL, 0, NULL, 0 }
82 };
83
84 static char *debuginfo_path;
85
86 static const Dwfl_Callbacks offline_callbacks =
87   {
88     .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
89     .debuginfo_path = &debuginfo_path,
90
91     .section_address = INTUSE(dwfl_offline_section_address),
92
93     /* We use this table for core files too.  */
94     .find_elf = INTUSE(dwfl_build_id_find_elf),
95   };
96
97 static const Dwfl_Callbacks proc_callbacks =
98   {
99     .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
100     .debuginfo_path = &debuginfo_path,
101
102     .find_elf = INTUSE(dwfl_linux_proc_find_elf),
103   };
104
105 static const Dwfl_Callbacks kernel_callbacks =
106   {
107     .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
108     .debuginfo_path = &debuginfo_path,
109
110     .find_elf = INTUSE(dwfl_linux_kernel_find_elf),
111     .section_address = INTUSE(dwfl_linux_kernel_module_section_address),
112   };
113
114 static error_t
115 parse_opt (int key, char *arg, struct argp_state *state)
116 {
117   inline void failure (Dwfl *dwfl, int errnum, const char *msg)
118     {
119       if (dwfl != NULL)
120         dwfl_end (dwfl);
121       if (errnum == -1)
122         argp_failure (state, EXIT_FAILURE, 0, "%s: %s",
123                       msg, INTUSE(dwfl_errmsg) (-1));
124       else
125         argp_failure (state, EXIT_FAILURE, errnum, "%s", msg);
126     }
127   inline error_t fail (Dwfl *dwfl, int errnum, const char *msg)
128     {
129       failure (dwfl, errnum, msg);
130       return errnum == -1 ? EIO : errnum;
131     }
132
133   switch (key)
134     {
135     case OPT_DEBUGINFO:
136       debuginfo_path = arg;
137       break;
138
139     case 'e':
140       {
141         Dwfl *dwfl = state->hook;
142         if (dwfl == NULL)
143           {
144             dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
145             if (dwfl == NULL)
146               return fail (dwfl, -1, arg);
147             state->hook = 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             if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL)
156               return fail (dwfl, -1, arg);
157             state->hook = dwfl;
158           }
159         else
160           {
161           toomany:
162             argp_error (state, "%s",
163                         _("only one of -e, -p, -k, -K, or --core allowed"));
164             return EINVAL;
165           }
166       }
167       break;
168
169     case 'p':
170       if (state->hook == NULL)
171         {
172           Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
173           int result = INTUSE(dwfl_linux_proc_report) (dwfl, atoi (arg));
174           if (result != 0)
175             return fail (dwfl, result, arg);
176           state->hook = dwfl;
177         }
178       else
179         goto toomany;
180       break;
181
182     case 'M':
183       if (state->hook == NULL)
184         {
185           FILE *f = fopen (arg, "r");
186           if (f == NULL)
187           nofile:
188             {
189               int code = errno;
190               argp_failure (state, EXIT_FAILURE, code,
191                             "cannot open '%s'", arg);
192               return code;
193             }
194           Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
195           int result = INTUSE(dwfl_linux_proc_maps_report) (dwfl, f);
196           fclose (f);
197           if (result != 0)
198             return fail (dwfl, result, arg);
199           state->hook = dwfl;
200         }
201       else
202         goto toomany;
203       break;
204
205     case OPT_COREFILE:
206       {
207         Dwfl *dwfl = state->hook;
208         if (dwfl == NULL)
209           state->hook = dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
210         /* Permit -e and --core together.  */
211         else if (dwfl->callbacks != &offline_callbacks)
212           goto toomany;
213
214         int fd = open64 (arg, O_RDONLY);
215         if (fd < 0)
216           goto nofile;
217
218         Elf *core;
219         Dwfl_Error error = __libdw_open_file (&fd, &core, true, false);
220         if (error != DWFL_E_NOERROR)
221           {
222             argp_failure (state, EXIT_FAILURE, 0,
223                           _("cannot read ELF core file: %s"),
224                           INTUSE(dwfl_errmsg) (error));
225             return error == DWFL_E_ERRNO ? errno : EIO;
226           }
227
228         int result = INTUSE(dwfl_core_file_report) (dwfl, core);
229         if (result < 0)
230           {
231             elf_end (core);
232             close (fd);
233             return fail (dwfl, result, arg);
234           }
235
236         /* From now we leak FD and CORE.  */
237
238         if (result == 0)
239           {
240             argp_failure (state, EXIT_FAILURE, 0,
241                           _("No modules recognized in core file"));
242             return ENOENT;
243           }
244       }
245       break;
246
247     case 'k':
248       if (state->hook == NULL)
249         {
250           Dwfl *dwfl = INTUSE(dwfl_begin) (&kernel_callbacks);
251           int result = INTUSE(dwfl_linux_kernel_report_kernel) (dwfl);
252           if (result != 0)
253             return fail (dwfl, result, _("cannot load kernel symbols"));
254           result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl);
255           if (result != 0)
256             /* Non-fatal to have no modules since we do have the kernel.  */
257             failure (dwfl, result, _("cannot find kernel modules"));
258           state->hook = dwfl;
259         }
260       else
261         goto toomany;
262       break;
263
264     case 'K':
265       if (state->hook == NULL)
266         {
267           Dwfl *dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
268           int result = INTUSE(dwfl_linux_kernel_report_offline) (dwfl, arg,
269                                                                  NULL);
270           if (result != 0)
271             return fail (dwfl, result, _("cannot find kernel or modules"));
272           state->hook = dwfl;
273         }
274       else
275         goto toomany;
276       break;
277
278     case ARGP_KEY_SUCCESS:
279       {
280         Dwfl *dwfl = state->hook;
281
282         if (dwfl == NULL)
283           {
284             /* Default if no -e, -p, or -k, is "-e a.out".  */
285             arg = "a.out";
286             dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
287             if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL)
288               return fail (dwfl, -1, arg);
289             state->hook = dwfl;
290           }
291
292         /* One of the three flavors has done dwfl_begin and some reporting
293            if we got here.  Tie up the Dwfl and return it to the caller of
294            argp_parse.  */
295
296         int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL);
297         assert (result == 0);
298       }
299       break;
300
301     case ARGP_KEY_ERROR:
302       dwfl_end (state->hook);
303       state->hook = NULL;
304       break;
305
306     default:
307       return ARGP_ERR_UNKNOWN;
308     }
309
310   /* Update the input all along, so a parent parser can see it.  */
311   *(Dwfl **) state->input = state->hook;
312   return 0;
313 }
314
315 static const struct argp libdwfl_argp =
316   { .options = options, .parser = parse_opt };
317
318 const struct argp *
319 dwfl_standard_argp (void)
320 {
321   return &libdwfl_argp;
322 }
323
324 #ifdef _MUDFLAP
325 /* In the absence of a mudflap wrapper for argp_parse, or a libc compiled
326    with -fmudflap, we'll see spurious errors for using the struct argp_state
327    on argp_parse's stack.  */
328
329 void __attribute__ ((constructor))
330 __libdwfl_argp_mudflap_options (void)
331 {
332   __mf_set_options ("-heur-stack-bound");
333 }
334 #endif