Blame libdwfl/argp-std.c

Packit Service 97d2fb
/* Standard argp argument parsers for tools using libdwfl.
Packit Service 97d2fb
   Copyright (C) 2005-2010, 2012, 2015 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of either
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU Lesser General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 3 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 2 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or both in parallel, as here.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 97d2fb
   General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received copies of the GNU General Public License and
Packit Service 97d2fb
   the GNU Lesser General Public License along with this program.  If
Packit Service 97d2fb
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include "libdwflP.h"
Packit Service 97d2fb
#include <argp.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include <assert.h>
Packit Service 97d2fb
#include <libintl.h>
Packit Service 97d2fb
#include <fcntl.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
Packit Service 97d2fb
/* gettext helper macros.  */
Packit Service 97d2fb
#define _(Str) dgettext ("elfutils", Str)
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
#define OPT_DEBUGINFO	0x100
Packit Service 97d2fb
#define OPT_COREFILE	0x101
Packit Service 97d2fb
Packit Service 97d2fb
static const struct argp_option options[] =
Packit Service 97d2fb
{
Packit Service 97d2fb
  { NULL, 0, NULL, 0, N_("Input selection options:"), 0 },
Packit Service 97d2fb
  { "executable", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 },
Packit Service 97d2fb
  { "core", OPT_COREFILE, "COREFILE", 0,
Packit Service 97d2fb
    N_("Find addresses from signatures found in COREFILE"), 0 },
Packit Service 97d2fb
  { "pid", 'p', "PID", 0,
Packit Service 97d2fb
    N_("Find addresses in files mapped into process PID"), 0 },
Packit Service 97d2fb
  { "linux-process-map", 'M', "FILE", 0,
Packit Service 97d2fb
    N_("Find addresses in files mapped as read from FILE"
Packit Service 97d2fb
       " in Linux /proc/PID/maps format"), 0 },
Packit Service 97d2fb
  { "kernel", 'k', NULL, 0, N_("Find addresses in the running kernel"), 0 },
Packit Service 97d2fb
  { "offline-kernel", 'K', "RELEASE", OPTION_ARG_OPTIONAL,
Packit Service 97d2fb
    N_("Kernel with all modules"), 0 },
Packit Service 97d2fb
  { "debuginfo-path", OPT_DEBUGINFO, "PATH", 0,
Packit Service 97d2fb
    N_("Search path for separate debuginfo files"), 0 },
Packit Service 97d2fb
  { NULL, 0, NULL, 0, NULL, 0 }
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
static char *debuginfo_path;
Packit Service 97d2fb
Packit Service 97d2fb
static const Dwfl_Callbacks offline_callbacks =
Packit Service 97d2fb
  {
Packit Service 97d2fb
    .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
Packit Service 97d2fb
    .debuginfo_path = &debuginfo_path,
Packit Service 97d2fb
Packit Service 97d2fb
    .section_address = INTUSE(dwfl_offline_section_address),
Packit Service 97d2fb
Packit Service 97d2fb
    /* We use this table for core files too.  */
Packit Service 97d2fb
    .find_elf = INTUSE(dwfl_build_id_find_elf),
Packit Service 97d2fb
  };
Packit Service 97d2fb
Packit Service 97d2fb
static const Dwfl_Callbacks proc_callbacks =
Packit Service 97d2fb
  {
Packit Service 97d2fb
    .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
Packit Service 97d2fb
    .debuginfo_path = &debuginfo_path,
Packit Service 97d2fb
Packit Service 97d2fb
    .find_elf = INTUSE(dwfl_linux_proc_find_elf),
Packit Service 97d2fb
  };
Packit Service 97d2fb
Packit Service 97d2fb
static const Dwfl_Callbacks kernel_callbacks =
Packit Service 97d2fb
  {
Packit Service 97d2fb
    .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
Packit Service 97d2fb
    .debuginfo_path = &debuginfo_path,
Packit Service 97d2fb
Packit Service 97d2fb
    .find_elf = INTUSE(dwfl_linux_kernel_find_elf),
Packit Service 97d2fb
    .section_address = INTUSE(dwfl_linux_kernel_module_section_address),
Packit Service 97d2fb
  };
Packit Service 97d2fb
Packit Service 97d2fb
/* Structure held at state->HOOK.  */
Packit Service 97d2fb
struct parse_opt
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwfl *dwfl;
Packit Service 97d2fb
  /* The -e|--executable parameter.  */
Packit Service 97d2fb
  const char *e;
Packit Service 97d2fb
  /* The --core parameter.  */
Packit Service 97d2fb
  const char *core;
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
static inline void
Packit Service 97d2fb
failure (Dwfl *dwfl, int errnum, const char *msg, struct argp_state *state)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (dwfl != NULL)
Packit Service 97d2fb
    dwfl_end (dwfl);
Packit Service 97d2fb
  if (errnum == -1)
Packit Service 97d2fb
    argp_failure (state, EXIT_FAILURE, 0, "%s: %s",
Packit Service 97d2fb
                  msg, INTUSE(dwfl_errmsg) (-1));
Packit Service 97d2fb
  else
Packit Service 97d2fb
    argp_failure (state, EXIT_FAILURE, errnum, "%s", msg);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static inline error_t
Packit Service 97d2fb
fail (Dwfl *dwfl, int errnum, const char *msg, struct argp_state *state)
Packit Service 97d2fb
{
Packit Service 97d2fb
  failure (dwfl, errnum, msg, state);
Packit Service 97d2fb
  return errnum == -1 ? EIO : errnum;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static error_t
Packit Service 97d2fb
parse_opt (int key, char *arg, struct argp_state *state)
Packit Service 97d2fb
{
Packit Service 97d2fb
  switch (key)
Packit Service 97d2fb
    {
Packit Service 97d2fb
    case ARGP_KEY_INIT:
Packit Service 97d2fb
      {
Packit Service 97d2fb
	assert (state->hook == NULL);
Packit Service 97d2fb
	struct parse_opt *opt = calloc (1, sizeof (*opt));
Packit Service 97d2fb
	if (opt == NULL)
Packit Service 97d2fb
	  failure (NULL, DWFL_E_ERRNO, "calloc", state);
Packit Service 97d2fb
	state->hook = opt;
Packit Service 97d2fb
      }
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case OPT_DEBUGINFO:
Packit Service 97d2fb
      debuginfo_path = arg;
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'e':
Packit Service 97d2fb
      {
Packit Service 97d2fb
	struct parse_opt *opt = state->hook;
Packit Service 97d2fb
	Dwfl *dwfl = opt->dwfl;
Packit Service 97d2fb
	if (dwfl == NULL)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
Packit Service 97d2fb
	    if (dwfl == NULL)
Packit Service 97d2fb
	      return fail (dwfl, -1, arg, state);
Packit Service 97d2fb
	    opt->dwfl = dwfl;
Packit Service 97d2fb
Packit Service 97d2fb
	    /* Start at zero so if there is just one -e foo.so,
Packit Service 97d2fb
	       the DSO is shown without address bias.  */
Packit Service 97d2fb
	    dwfl->offline_next_address = 0;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	if (dwfl->callbacks != &offline_callbacks)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	  toomany:
Packit Service 97d2fb
	    argp_error (state, "%s",
Packit Service 97d2fb
			_("only one of -e, -p, -k, -K, or --core allowed"));
Packit Service 97d2fb
	    return EINVAL;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	opt->e = arg;
Packit Service 97d2fb
      }
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'p':
Packit Service 97d2fb
      {
Packit Service 97d2fb
	struct parse_opt *opt = state->hook;
Packit Service 97d2fb
	if (opt->dwfl == NULL)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
Packit Service 97d2fb
	    int result = INTUSE(dwfl_linux_proc_report) (dwfl, atoi (arg));
Packit Service 97d2fb
	    if (result != 0)
Packit Service 97d2fb
	      return fail (dwfl, result, arg, state);
Packit Service 97d2fb
Packit Service 97d2fb
	    /* Non-fatal to not be able to attach to process, ignore error.  */
Packit Service 97d2fb
	    INTUSE(dwfl_linux_proc_attach) (dwfl, atoi (arg), false);
Packit Service 97d2fb
Packit Service 97d2fb
	    opt->dwfl = dwfl;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	else
Packit Service 97d2fb
	  goto toomany;
Packit Service 97d2fb
      }
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'M':
Packit Service 97d2fb
      {
Packit Service 97d2fb
	struct parse_opt *opt = state->hook;
Packit Service 97d2fb
	if (opt->dwfl == NULL)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    FILE *f = fopen (arg, "r");
Packit Service 97d2fb
	    if (f == NULL)
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		int code = errno;
Packit Service 97d2fb
		argp_failure (state, EXIT_FAILURE, code,
Packit Service 97d2fb
			      "cannot open '%s'", arg);
Packit Service 97d2fb
		return code;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	    Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
Packit Service 97d2fb
	    int result = INTUSE(dwfl_linux_proc_maps_report) (dwfl, f);
Packit Service 97d2fb
	    fclose (f);
Packit Service 97d2fb
	    if (result != 0)
Packit Service 97d2fb
	      return fail (dwfl, result, arg, state);
Packit Service 97d2fb
	    opt->dwfl = dwfl;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	else
Packit Service 97d2fb
	  goto toomany;
Packit Service 97d2fb
      }
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case OPT_COREFILE:
Packit Service 97d2fb
      {
Packit Service 97d2fb
	struct parse_opt *opt = state->hook;
Packit Service 97d2fb
	Dwfl *dwfl = opt->dwfl;
Packit Service 97d2fb
	if (dwfl == NULL)
Packit Service 97d2fb
	  opt->dwfl = dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
Packit Service 97d2fb
	/* Permit -e and --core together.  */
Packit Service 97d2fb
	else if (dwfl->callbacks != &offline_callbacks)
Packit Service 97d2fb
	  goto toomany;
Packit Service 97d2fb
	opt->core = arg;
Packit Service 97d2fb
      }
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'k':
Packit Service 97d2fb
      {
Packit Service 97d2fb
	struct parse_opt *opt = state->hook;
Packit Service 97d2fb
	if (opt->dwfl == NULL)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    Dwfl *dwfl = INTUSE(dwfl_begin) (&kernel_callbacks);
Packit Service 97d2fb
	    int result = INTUSE(dwfl_linux_kernel_report_kernel) (dwfl);
Packit Service 97d2fb
	    if (result != 0)
Packit Service 97d2fb
	      return fail (dwfl, result, _("cannot load kernel symbols"), state);
Packit Service 97d2fb
	    result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl);
Packit Service 97d2fb
	    if (result != 0)
Packit Service 97d2fb
	      /* Non-fatal to have no modules since we do have the kernel.  */
Packit Service 97d2fb
	      argp_failure (state, 0, result, _("cannot find kernel modules"));
Packit Service 97d2fb
	    opt->dwfl = dwfl;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	else
Packit Service 97d2fb
	  goto toomany;
Packit Service 97d2fb
      }
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case 'K':
Packit Service 97d2fb
      {
Packit Service 97d2fb
	struct parse_opt *opt = state->hook;
Packit Service 97d2fb
	if (opt->dwfl == NULL)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    Dwfl *dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
Packit Service 97d2fb
	    int result = INTUSE(dwfl_linux_kernel_report_offline) (dwfl, arg,
Packit Service 97d2fb
								   NULL);
Packit Service 97d2fb
	    if (result != 0)
Packit Service 97d2fb
	      return fail (dwfl, result, _("cannot find kernel or modules"), state);
Packit Service 97d2fb
	    opt->dwfl = dwfl;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	else
Packit Service 97d2fb
	  goto toomany;
Packit Service 97d2fb
      }
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case ARGP_KEY_SUCCESS:
Packit Service 97d2fb
      {
Packit Service 97d2fb
	struct parse_opt *opt = state->hook;
Packit Service 97d2fb
	Dwfl *dwfl = opt->dwfl;
Packit Service 97d2fb
Packit Service 97d2fb
	if (dwfl == NULL)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    /* Default if no -e, -p, or -k, is "-e a.out".  */
Packit Service 97d2fb
	    arg = "a.out";
Packit Service 97d2fb
	    dwfl = INTUSE(dwfl_begin) (&offline_callbacks);
Packit Service 97d2fb
	    if (INTUSE(dwfl_report_offline) (dwfl, "", arg, -1) == NULL)
Packit Service 97d2fb
	      return fail (dwfl, -1, arg, state);
Packit Service 97d2fb
	    opt->dwfl = dwfl;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
Packit Service 97d2fb
	if (opt->core)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    int fd = open (opt->core, O_RDONLY);
Packit Service 97d2fb
	    if (fd < 0)
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		int code = errno;
Packit Service 97d2fb
		argp_failure (state, EXIT_FAILURE, code,
Packit Service 97d2fb
			      "cannot open '%s'", opt->core);
Packit Service 97d2fb
		return code;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
Packit Service 97d2fb
	    Elf *core;
Packit Service 97d2fb
	    Dwfl_Error error = __libdw_open_file (&fd, &core, true, false);
Packit Service 97d2fb
	    if (error != DWFL_E_NOERROR)
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		argp_failure (state, EXIT_FAILURE, 0,
Packit Service 97d2fb
			      _("cannot read ELF core file: %s"),
Packit Service 97d2fb
			      INTUSE(dwfl_errmsg) (error));
Packit Service 97d2fb
		return error == DWFL_E_ERRNO ? errno : EIO;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
Packit Service 97d2fb
	    int result = INTUSE(dwfl_core_file_report) (dwfl, core, opt->e);
Packit Service 97d2fb
	    if (result < 0)
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		elf_end (core);
Packit Service 97d2fb
		close (fd);
Packit Service 97d2fb
		return fail (dwfl, result, opt->core, state);
Packit Service 97d2fb
	      }
Packit Service 97d2fb
Packit Service 97d2fb
	    /* Non-fatal to not be able to attach to core, ignore error.  */
Packit Service 97d2fb
	    INTUSE(dwfl_core_file_attach) (dwfl, core);
Packit Service 97d2fb
Packit Service 97d2fb
	    /* Store core Elf and fd in Dwfl to expose with dwfl_end.  */
Packit Service 97d2fb
	    if (dwfl->user_core == NULL)
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		dwfl->user_core = calloc (1, sizeof (struct Dwfl_User_Core));
Packit Service 97d2fb
		if (dwfl->user_core == NULL)
Packit Service 97d2fb
		  {
Packit Service 97d2fb
		    argp_failure (state, EXIT_FAILURE, 0,
Packit Service 97d2fb
				  _("Not enough memory"));
Packit Service 97d2fb
		    return ENOMEM;
Packit Service 97d2fb
		  }
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	    dwfl->user_core->core = core;
Packit Service 97d2fb
	    dwfl->user_core->fd = fd;
Packit Service 97d2fb
Packit Service 97d2fb
	    if (result == 0)
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		argp_failure (state, EXIT_FAILURE, 0,
Packit Service 97d2fb
			      _("No modules recognized in core file"));
Packit Service 97d2fb
		return ENOENT;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	else if (opt->e)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    if (INTUSE(dwfl_report_offline) (dwfl, "", opt->e, -1) == NULL)
Packit Service 97d2fb
	      return fail (dwfl, -1, opt->e, state);
Packit Service 97d2fb
	  }
Packit Service 97d2fb
Packit Service 97d2fb
	/* One of the three flavors has done dwfl_begin and some reporting
Packit Service 97d2fb
	   if we got here.  Tie up the Dwfl and return it to the caller of
Packit Service 97d2fb
	   argp_parse.  */
Packit Service 97d2fb
Packit Service 97d2fb
	int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL);
Packit Service 97d2fb
	if (result != 0)
Packit Service 97d2fb
	  return fail (dwfl, -1, arg, state);
Packit Service 97d2fb
Packit Service 97d2fb
	/* Update the input all along, so a parent parser can see it.
Packit Service 97d2fb
	   As we free OPT the update below will be no longer active.  */
Packit Service 97d2fb
	*(Dwfl **) state->input = dwfl;
Packit Service 97d2fb
	free (opt);
Packit Service 97d2fb
	state->hook = NULL;
Packit Service 97d2fb
      }
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    case ARGP_KEY_ERROR:
Packit Service 97d2fb
      {
Packit Service 97d2fb
	struct parse_opt *opt = state->hook;
Packit Service 97d2fb
	dwfl_end (opt->dwfl);
Packit Service 97d2fb
	free (opt);
Packit Service 97d2fb
	state->hook = NULL;
Packit Service 97d2fb
      }
Packit Service 97d2fb
      break;
Packit Service 97d2fb
Packit Service 97d2fb
    default:
Packit Service 97d2fb
      return ARGP_ERR_UNKNOWN;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Update the input all along, so a parent parser can see it.  */
Packit Service 97d2fb
  struct parse_opt *opt = state->hook;
Packit Service 97d2fb
  if (opt)
Packit Service 97d2fb
    *(Dwfl **) state->input = opt->dwfl;
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static const struct argp libdwfl_argp =
Packit Service 97d2fb
  { .options = options, .parser = parse_opt };
Packit Service 97d2fb
Packit Service 97d2fb
const struct argp *
Packit Service 97d2fb
dwfl_standard_argp (void)
Packit Service 97d2fb
{
Packit Service 97d2fb
  return &libdwfl_argp;
Packit Service 97d2fb
}