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