Home | History | Annotate | Download | only in src
      1 /* Locate source files and line information for given addresses
      2    Copyright (C) 2005, 2006, 2007, 2008 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4    Written by Ulrich Drepper <drepper (at) redhat.com>, 2005.
      5 
      6    Red Hat elfutils is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by the
      8    Free Software Foundation; version 2 of the License.
      9 
     10    Red Hat elfutils is distributed in the hope that it will be useful, but
     11    WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License along
     16    with Red Hat elfutils; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     18 
     19    Red Hat elfutils is an included package of the Open Invention Network.
     20    An included package of the Open Invention Network is a package for which
     21    Open Invention Network licensees cross-license their patents.  No patent
     22    license is granted, either expressly or impliedly, by designation as an
     23    included package.  Should you wish to participate in the Open Invention
     24    Network licensing program, please visit www.openinventionnetwork.com
     25    <http://www.openinventionnetwork.com>.  */
     26 
     27 #ifdef HAVE_CONFIG_H
     28 # include <config.h>
     29 #endif
     30 
     31 #include <argp.h>
     32 #include <assert.h>
     33 #include <errno.h>
     34 #include <error.h>
     35 #include <fcntl.h>
     36 #include <inttypes.h>
     37 #include <libdwfl.h>
     38 #include <dwarf.h>
     39 #include <libintl.h>
     40 #include <locale.h>
     41 #include <mcheck.h>
     42 #include <stdbool.h>
     43 #include <stdio.h>
     44 #include <stdio_ext.h>
     45 #include <stdlib.h>
     46 #include <string.h>
     47 #include <unistd.h>
     48 
     49 
     50 /* Name and version of program.  */
     51 static void print_version (FILE *stream, struct argp_state *state);
     52 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
     53 
     54 /* Bug report address.  */
     55 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
     56 
     57 
     58 /* Values for the parameters which have no short form.  */
     59 #define OPT_DEMANGLER 0x100
     60 
     61 /* Definitions of arguments for argp functions.  */
     62 static const struct argp_option options[] =
     63 {
     64   { NULL, 0, NULL, 0, N_("Output selection options:"), 2 },
     65   { "basenames", 's', NULL, 0, N_("Show only base names of source files"), 0 },
     66   { "absolute", 'A', NULL, 0,
     67     N_("Show absolute file names using compilation directory"), 0 },
     68   { "functions", 'f', NULL, 0, N_("Also show function names"), 0 },
     69   { "symbols", 'S', NULL, 0, N_("Also show symbol or section names"), 0 },
     70 
     71   { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
     72   /* Unsupported options.  */
     73   { "target", 'b', "ARG", OPTION_HIDDEN, NULL, 0 },
     74   { "demangle", 'C', "ARG", OPTION_HIDDEN | OPTION_ARG_OPTIONAL, NULL, 0 },
     75   { "demangler", OPT_DEMANGLER, "ARG", OPTION_HIDDEN, NULL, 0 },
     76   { NULL, 0, NULL, 0, NULL, 0 }
     77 };
     78 
     79 /* Short description of program.  */
     80 static const char doc[] = N_("\
     81 Locate source files and line information for ADDRs (in a.out by default).");
     82 
     83 /* Strings for arguments in help texts.  */
     84 static const char args_doc[] = N_("[ADDR...]");
     85 
     86 /* Prototype for option handler.  */
     87 static error_t parse_opt (int key, char *arg, struct argp_state *state);
     88 
     89 static struct argp_child argp_children[2]; /* [0] is set in main.  */
     90 
     91 /* Data structure to communicate with argp functions.  */
     92 static const struct argp argp =
     93 {
     94   options, parse_opt, args_doc, doc, argp_children, NULL, NULL
     95 };
     96 
     97 
     98 /* Handle ADDR.  */
     99 static int handle_address (const char *addr, Dwfl *dwfl);
    100 
    101 
    102 /* True if only base names of files should be shown.  */
    103 static bool only_basenames;
    104 
    105 /* True if absolute file names based on DW_AT_comp_dir should be shown.  */
    106 static bool use_comp_dir;
    107 
    108 /* True if function names should be shown.  */
    109 static bool show_functions;
    110 
    111 /* True if ELF symbol or section info should be shown.  */
    112 static bool show_symbols;
    113 
    114 
    115 int
    116 main (int argc, char *argv[])
    117 {
    118   int remaining;
    119   int result = 0;
    120 
    121   /* Make memory leak detection possible.  */
    122   mtrace ();
    123 
    124   /* We use no threads here which can interfere with handling a stream.  */
    125   (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
    126 
    127   /* Set locale.  */
    128   (void) setlocale (LC_ALL, "");
    129 
    130   /* Make sure the message catalog can be found.  */
    131   (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
    132 
    133   /* Initialize the message catalog.  */
    134   (void) textdomain (PACKAGE_TARNAME);
    135 
    136   /* Parse and process arguments.  This includes opening the modules.  */
    137   argp_children[0].argp = dwfl_standard_argp ();
    138   argp_children[0].group = 1;
    139   Dwfl *dwfl = NULL;
    140   (void) argp_parse (&argp, argc, argv, 0, &remaining, &dwfl);
    141   assert (dwfl != NULL);
    142 
    143   /* Now handle the addresses.  In case none are given on the command
    144      line, read from stdin.  */
    145   if (remaining == argc)
    146     {
    147       /* We use no threads here which can interfere with handling a stream.  */
    148       (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
    149 
    150       char *buf = NULL;
    151       size_t len = 0;
    152       while (!feof_unlocked (stdin))
    153 	{
    154 	  if (getline (&buf, &len, stdin) < 0)
    155 	    break;
    156 
    157 	  result = handle_address (buf, dwfl);
    158 	}
    159 
    160       free (buf);
    161     }
    162   else
    163     {
    164       do
    165 	result = handle_address (argv[remaining], dwfl);
    166       while (++remaining < argc);
    167     }
    168 
    169   return result;
    170 }
    171 
    172 
    173 /* Print the version information.  */
    174 static void
    175 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
    176 {
    177   fprintf (stream, "addr2line (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
    178   fprintf (stream, gettext ("\
    179 Copyright (C) %s Red Hat, Inc.\n\
    180 This is free software; see the source for copying conditions.  There is NO\n\
    181 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
    182 "), "2008");
    183   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
    184 }
    185 
    186 
    187 /* Handle program arguments.  */
    188 static error_t
    189 parse_opt (int key, char *arg __attribute__ ((unused)),
    190 	   struct argp_state *state)
    191 {
    192   switch (key)
    193     {
    194     case ARGP_KEY_INIT:
    195       state->child_inputs[0] = state->input;
    196       break;
    197 
    198     case 'b':
    199     case 'C':
    200     case OPT_DEMANGLER:
    201       /* Ignored for compatibility.  */
    202       break;
    203 
    204     case 's':
    205       only_basenames = true;
    206       break;
    207 
    208     case 'A':
    209       use_comp_dir = true;
    210       break;
    211 
    212     case 'f':
    213       show_functions = true;
    214       break;
    215 
    216     case 'S':
    217       show_symbols = true;
    218       break;
    219 
    220     default:
    221       return ARGP_ERR_UNKNOWN;
    222     }
    223   return 0;
    224 }
    225 
    226 
    227 static bool
    228 print_dwarf_function (Dwfl_Module *mod, Dwarf_Addr addr)
    229 {
    230   Dwarf_Addr bias = 0;
    231   Dwarf_Die *cudie = dwfl_module_addrdie (mod, addr, &bias);
    232 
    233   Dwarf_Die *scopes;
    234   int nscopes = dwarf_getscopes (cudie, addr - bias, &scopes);
    235   if (nscopes <= 0)
    236     return false;
    237 
    238   for (int i = 0; i < nscopes; ++i)
    239     switch (dwarf_tag (&scopes[i]))
    240       {
    241       case DW_TAG_subprogram:
    242 	{
    243 	  const char *name = dwarf_diename (&scopes[i]);
    244 	  if (name == NULL)
    245 	    return false;
    246 	  puts (name);
    247 	  return true;
    248 	}
    249 
    250       case DW_TAG_inlined_subroutine:
    251 	{
    252 	  const char *name = dwarf_diename (&scopes[i]);
    253 	  if (name == NULL)
    254 	    return false;
    255 	  printf ("%s inlined", name);
    256 
    257 	  Dwarf_Files *files;
    258 	  if (dwarf_getsrcfiles (cudie, &files, NULL) == 0)
    259 	    {
    260 	      Dwarf_Attribute attr_mem;
    261 	      Dwarf_Word val;
    262 	      if (dwarf_formudata (dwarf_attr (&scopes[i],
    263 					       DW_AT_call_file,
    264 					       &attr_mem), &val) == 0)
    265 		{
    266 		  const char *file = dwarf_filesrc (files, val, NULL, NULL);
    267 		  unsigned int lineno = 0;
    268 		  unsigned int colno = 0;
    269 		  if (dwarf_formudata (dwarf_attr (&scopes[i],
    270 						   DW_AT_call_line,
    271 						   &attr_mem), &val) == 0)
    272 		    lineno = val;
    273 		  if (dwarf_formudata (dwarf_attr (&scopes[i],
    274 						   DW_AT_call_column,
    275 						   &attr_mem), &val) == 0)
    276 		    colno = val;
    277 		  if (lineno == 0)
    278 		    {
    279 		      if (file != NULL)
    280 			printf (" from %s", file);
    281 		    }
    282 		  else if (colno == 0)
    283 		    printf (" at %s:%u", file, lineno);
    284 		  else
    285 		    printf (" at %s:%u:%u", file, lineno, colno);
    286 		}
    287 	    }
    288 	  printf (" in ");
    289 	  continue;
    290 	}
    291       }
    292 
    293   return false;
    294 }
    295 
    296 static void
    297 print_addrsym (Dwfl_Module *mod, GElf_Addr addr)
    298 {
    299   GElf_Sym s;
    300   GElf_Word shndx;
    301   const char *name = dwfl_module_addrsym (mod, addr, &s, &shndx);
    302   if (name == NULL)
    303     {
    304       /* No symbol name.  Get a section name instead.  */
    305       int i = dwfl_module_relocate_address (mod, &addr);
    306       if (i >= 0)
    307 	name = dwfl_module_relocation_info (mod, i, NULL);
    308       if (name == NULL)
    309 	puts ("??");
    310       else
    311 	printf ("(%s)+%#" PRIx64 "\n", name, addr);
    312     }
    313   else if (addr == s.st_value)
    314     puts (name);
    315   else
    316     printf ("%s+%#" PRIx64 "\n", name, addr - s.st_value);
    317 }
    318 
    319 static int
    320 see_one_module (Dwfl_Module *mod,
    321 		void **userdata __attribute__ ((unused)),
    322 		const char *name __attribute__ ((unused)),
    323 		Dwarf_Addr start __attribute__ ((unused)),
    324 		void *arg)
    325 {
    326   Dwfl_Module **result = arg;
    327   if (*result != NULL)
    328     return DWARF_CB_ABORT;
    329   *result = mod;
    330   return DWARF_CB_OK;
    331 }
    332 
    333 static int
    334 find_symbol (Dwfl_Module *mod,
    335 	     void **userdata __attribute__ ((unused)),
    336 	     const char *name __attribute__ ((unused)),
    337 	     Dwarf_Addr start __attribute__ ((unused)),
    338 	     void *arg)
    339 {
    340   const char *looking_for = ((void **) arg)[0];
    341   GElf_Sym *symbol = ((void **) arg)[1];
    342 
    343   int n = dwfl_module_getsymtab (mod);
    344   for (int i = 1; i < n; ++i)
    345     {
    346       const char *symbol_name = dwfl_module_getsym (mod, i, symbol, NULL);
    347       if (symbol_name == NULL)
    348 	continue;
    349       switch (GELF_ST_TYPE (symbol->st_info))
    350 	{
    351 	case STT_SECTION:
    352 	case STT_FILE:
    353 	case STT_TLS:
    354 	  break;
    355 	default:
    356 	  if (!strcmp (symbol_name, looking_for))
    357 	    {
    358 	      ((void **) arg)[0] = NULL;
    359 	      return DWARF_CB_ABORT;
    360 	    }
    361 	}
    362     }
    363 
    364   return DWARF_CB_OK;
    365 }
    366 
    367 static int
    368 handle_address (const char *string, Dwfl *dwfl)
    369 {
    370   char *endp;
    371   uintmax_t addr = strtoumax (string, &endp, 0);
    372   if (endp == string)
    373     {
    374       bool parsed = false;
    375       int n;
    376       char *name = NULL;
    377       if (sscanf (string, "(%m[^)])%" PRIiMAX "%n", &name, &addr, &n) == 2
    378 	  && string[n] == '\0')
    379 	{
    380 	  /* It was (section)+offset.  This makes sense if there is
    381 	     only one module to look in for a section.  */
    382 	  Dwfl_Module *mod = NULL;
    383 	  if (dwfl_getmodules (dwfl, &see_one_module, &mod, 0) != 0
    384 	      || mod == NULL)
    385 	    error (EXIT_FAILURE, 0, gettext ("Section syntax requires"
    386 					     " exactly one module"));
    387 
    388 	  int nscn = dwfl_module_relocations (mod);
    389 	  for (int i = 0; i < nscn; ++i)
    390 	    {
    391 	      GElf_Word shndx;
    392 	      const char *scn = dwfl_module_relocation_info (mod, i, &shndx);
    393 	      if (unlikely (scn == NULL))
    394 		break;
    395 	      if (!strcmp (scn, name))
    396 		{
    397 		  /* Found the section.  */
    398 		  GElf_Shdr shdr_mem;
    399 		  GElf_Addr shdr_bias;
    400 		  GElf_Shdr *shdr = gelf_getshdr
    401 		    (elf_getscn (dwfl_module_getelf (mod, &shdr_bias), shndx),
    402 		     &shdr_mem);
    403 		  if (unlikely (shdr == NULL))
    404 		    break;
    405 
    406 		  if (addr >= shdr->sh_size)
    407 		    error (0, 0,
    408 			   gettext ("offset %#" PRIxMAX " lies outside"
    409 				    " section '%s'"),
    410 			   addr, scn);
    411 
    412 		  addr += shdr->sh_addr + shdr_bias;
    413 		  parsed = true;
    414 		  break;
    415 		}
    416 	    }
    417 	}
    418       else if (sscanf (string, "%m[^-+]%" PRIiMAX "%n", &name, &addr, &n) == 2
    419 	       && string[n] == '\0')
    420 	{
    421 	  /* It was symbol+offset.  */
    422 	  GElf_Sym sym;
    423 	  void *arg[2] = { name, &sym };
    424 	  (void) dwfl_getmodules (dwfl, &find_symbol, arg, 0);
    425 	  if (arg[0] != NULL)
    426 	    error (0, 0, gettext ("cannot find symbol '%s'"), name);
    427 	  else
    428 	    {
    429 	      if (sym.st_size != 0 && addr >= sym.st_size)
    430 		error (0, 0,
    431 		       gettext ("offset %#" PRIxMAX " lies outside"
    432 				" contents of '%s'"),
    433 		       addr, name);
    434 	      addr += sym.st_value;
    435 	      parsed = true;
    436 	    }
    437 	}
    438 
    439       free (name);
    440       if (!parsed)
    441 	return 1;
    442     }
    443 
    444   Dwfl_Module *mod = dwfl_addrmodule (dwfl, addr);
    445 
    446   if (show_functions)
    447     {
    448       /* First determine the function name.  Use the DWARF information if
    449 	 possible.  */
    450       if (! print_dwarf_function (mod, addr) && !show_symbols)
    451 	puts (dwfl_module_addrname (mod, addr) ?: "??");
    452     }
    453 
    454   if (show_symbols)
    455     print_addrsym (mod, addr);
    456 
    457   Dwfl_Line *line = dwfl_module_getsrc (mod, addr);
    458 
    459   const char *src;
    460   int lineno, linecol;
    461   if (line != NULL && (src = dwfl_lineinfo (line, &addr, &lineno, &linecol,
    462 					    NULL, NULL)) != NULL)
    463     {
    464       const char *comp_dir = "";
    465       const char *comp_dir_sep = "";
    466 
    467       if (only_basenames)
    468 	src = basename (src);
    469       else if (use_comp_dir && src[0] != '/')
    470 	{
    471 	  comp_dir = dwfl_line_comp_dir (line);
    472 	  if (comp_dir != NULL)
    473 	    comp_dir_sep = "/";
    474 	}
    475 
    476       if (linecol != 0)
    477 	printf ("%s%s%s:%d:%d\n",
    478 		comp_dir, comp_dir_sep, src, lineno, linecol);
    479       else
    480 	printf ("%s%s%s:%d\n",
    481 		comp_dir, comp_dir_sep, src, lineno);
    482     }
    483   else
    484     puts ("??:0");
    485 
    486   return 0;
    487 }
    488 
    489 
    490 #include "debugpred.h"
    491