Home | History | Annotate | Download | only in libdwfl
      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
    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 *
    373 dwfl_standard_argp (void)
    374 {
    375   return &libdwfl_argp;
    376 }
    377