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