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