1/* Standard argp argument parsers for tools using libdwfl.
2   Copyright (C) 2005, 2007, 2008 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
65static 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
84static char *debuginfo_path;
85
86static 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
97static 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
105static 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
114static error_t
115parse_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 = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
219	if (core == NULL)
220	  {
221	    close (fd);
222	    argp_failure (state, EXIT_FAILURE, 0,
223			  _("cannot read ELF core file: %s"),
224			  elf_errmsg (-1));
225	    return EIO;
226	  }
227
228	GElf_Ehdr ehdr;
229	int result = INTUSE(dwfl_core_file_report) (dwfl, core,
230						    gelf_getehdr (core, &ehdr));
231	if (result < 0)
232	  {
233	    elf_end (core);
234	    close (fd);
235	    return fail (dwfl, result, arg);
236	  }
237
238	/* From now we leak FD and CORE.  */
239
240	if (result == 0)
241	  {
242	    argp_failure (state, EXIT_FAILURE, 0,
243			  _("No modules recognized in core file"));
244	    return ENOENT;
245	  }
246      }
247      break;
248
249    case 'k':
250      if (state->hook == NULL)
251	{
252	  Dwfl *dwfl = INTUSE(dwfl_begin) (&kernel_callbacks);
253	  int result = INTUSE(dwfl_linux_kernel_report_kernel) (dwfl);
254	  if (result != 0)
255	    return fail (dwfl, result, _("cannot load kernel symbols"));
256	  result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl);
257	  if (result != 0)
258	    /* Non-fatal to have no modules since we do have the kernel.  */
259	    failure (dwfl, result, _("cannot find kernel modules"));
260	  state->hook = dwfl;
261	}
262      else
263	goto toomany;
264      break;
265
266    case 'K':
267      if (state->hook == NULL)
268	{
269	  Dwfl *dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
270	  int result = INTUSE(dwfl_linux_kernel_report_offline) (dwfl, arg,
271								 NULL);
272	  if (result != 0)
273	    return fail (dwfl, result, _("cannot find kernel or modules"));
274	  state->hook = dwfl;
275	}
276      else
277	goto toomany;
278      break;
279
280    case ARGP_KEY_SUCCESS:
281      {
282	Dwfl *dwfl = state->hook;
283
284	if (dwfl == NULL)
285	  {
286	    /* Default if no -e, -p, or -k, is "-e a.out".  */
287	    arg = "a.out";
288	    dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
289	    if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL)
290	      return fail (dwfl, -1, arg);
291	    state->hook = dwfl;
292	  }
293
294	/* One of the three flavors has done dwfl_begin and some reporting
295	   if we got here.  Tie up the Dwfl and return it to the caller of
296	   argp_parse.  */
297
298	int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL);
299	assert (result == 0);
300      }
301      break;
302
303    case ARGP_KEY_ERROR:
304      dwfl_end (state->hook);
305      state->hook = NULL;
306      break;
307
308    default:
309      return ARGP_ERR_UNKNOWN;
310    }
311
312  /* Update the input all along, so a parent parser can see it.  */
313  *(Dwfl **) state->input = state->hook;
314  return 0;
315}
316
317static const struct argp libdwfl_argp =
318  { .options = options, .parser = parse_opt };
319
320const struct argp *
321dwfl_standard_argp (void)
322{
323  return &libdwfl_argp;
324}
325
326#ifdef _MUDFLAP
327/* In the absence of a mudflap wrapper for argp_parse, or a libc compiled
328   with -fmudflap, we'll see spurious errors for using the struct argp_state
329   on argp_parse's stack.  */
330
331void __attribute__ ((constructor))
332__libdwfl_argp_mudflap_options (void)
333{
334  __mf_set_options ("-heur-stack-bound");
335}
336#endif
337