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