1 /* Standard argp argument parsers for tools using libdwfl.
2    Copyright (C) 2005-2010, 2012 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 = open64 (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 	    /* From now we leak FD and CORE.  */
307 
308 	    if (result == 0)
309 	      {
310 		argp_failure (state, EXIT_FAILURE, 0,
311 			      _("No modules recognized in core file"));
312 		return ENOENT;
313 	      }
314 	  }
315 	else if (opt->e)
316 	  {
317 	    if (INTUSE(dwfl_report_offline) (dwfl, "", opt->e, -1) == NULL)
318 	      return fail (dwfl, -1, opt->e);
319 	  }
320 
321 	/* One of the three flavors has done dwfl_begin and some reporting
322 	   if we got here.  Tie up the Dwfl and return it to the caller of
323 	   argp_parse.  */
324 
325 	int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL);
326 	assert (result == 0);
327 
328 	/* Update the input all along, so a parent parser can see it.
329 	   As we free OPT the update below will be no longer active.  */
330 	*(Dwfl **) state->input = dwfl;
331 	free (opt);
332 	state->hook = NULL;
333       }
334       break;
335 
336     case ARGP_KEY_ERROR:
337       {
338 	struct parse_opt *opt = state->hook;
339 	dwfl_end (opt->dwfl);
340 	free (opt);
341 	state->hook = NULL;
342       }
343       break;
344 
345     default:
346       return ARGP_ERR_UNKNOWN;
347     }
348 
349   /* Update the input all along, so a parent parser can see it.  */
350   struct parse_opt *opt = state->hook;
351   if (opt)
352     *(Dwfl **) state->input = opt->dwfl;
353 
354   return 0;
355 }
356 
357 static const struct argp libdwfl_argp =
358   { .options = options, .parser = parse_opt };
359 
360 const struct argp *
dwfl_standard_argp(void)361 dwfl_standard_argp (void)
362 {
363   return &libdwfl_argp;
364 }
365