|  | /* addr2line.c -- convert addresses to line number and function name | 
|  | Copyright (C) 1997-2021 Free Software Foundation, Inc. | 
|  | Contributed by Ulrich Lauther <Ulrich.Lauther@mchp.siemens.de> | 
|  |  | 
|  | This file is part of GNU Binutils. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3, or (at your option) | 
|  | any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program; if not, write to the Free Software | 
|  | Foundation, 51 Franklin Street - Fifth Floor, Boston, | 
|  | MA 02110-1301, USA.  */ | 
|  |  | 
|  |  | 
|  | /* Derived from objdump.c and nm.c by Ulrich.Lauther@mchp.siemens.de | 
|  |  | 
|  | Usage: | 
|  | addr2line [options] addr addr ... | 
|  | or | 
|  | addr2line [options] | 
|  |  | 
|  | both forms write results to stdout, the second form reads addresses | 
|  | to be converted from stdin.  */ | 
|  |  | 
|  | #include "sysdep.h" | 
|  | #include "bfd.h" | 
|  | #include "getopt.h" | 
|  | #include "libiberty.h" | 
|  | #include "demangle.h" | 
|  | #include "bucomm.h" | 
|  | #include "elf-bfd.h" | 
|  |  | 
|  | static bool unwind_inlines;	/* -i, unwind inlined functions. */ | 
|  | static bool with_addresses;	/* -a, show addresses.  */ | 
|  | static bool with_functions;	/* -f, show function names.  */ | 
|  | static bool do_demangle;	/* -C, demangle names.  */ | 
|  | static bool pretty_print;	/* -p, print on one line.  */ | 
|  | static bool base_names;		/* -s, strip directory names.  */ | 
|  |  | 
|  | /* Flags passed to the name demangler.  */ | 
|  | static int demangle_flags = DMGL_PARAMS | DMGL_ANSI; | 
|  |  | 
|  | static int naddr;		/* Number of addresses to process.  */ | 
|  | static char **addr;		/* Hex addresses to process.  */ | 
|  |  | 
|  | static asymbol **syms;		/* Symbol table.  */ | 
|  |  | 
|  | static struct option long_options[] = | 
|  | { | 
|  | {"addresses", no_argument, NULL, 'a'}, | 
|  | {"basenames", no_argument, NULL, 's'}, | 
|  | {"demangle", optional_argument, NULL, 'C'}, | 
|  | {"exe", required_argument, NULL, 'e'}, | 
|  | {"functions", no_argument, NULL, 'f'}, | 
|  | {"inlines", no_argument, NULL, 'i'}, | 
|  | {"pretty-print", no_argument, NULL, 'p'}, | 
|  | {"recurse-limit", no_argument, NULL, 'R'}, | 
|  | {"recursion-limit", no_argument, NULL, 'R'}, | 
|  | {"no-recurse-limit", no_argument, NULL, 'r'}, | 
|  | {"no-recursion-limit", no_argument, NULL, 'r'}, | 
|  | {"section", required_argument, NULL, 'j'}, | 
|  | {"target", required_argument, NULL, 'b'}, | 
|  | {"help", no_argument, NULL, 'H'}, | 
|  | {"version", no_argument, NULL, 'V'}, | 
|  | {0, no_argument, 0, 0} | 
|  | }; | 
|  |  | 
|  | static void usage (FILE *, int); | 
|  | static void slurp_symtab (bfd *); | 
|  | static void find_address_in_section (bfd *, asection *, void *); | 
|  | static void find_offset_in_section (bfd *, asection *); | 
|  | static void translate_addresses (bfd *, asection *); | 
|  |  | 
|  | /* Print a usage message to STREAM and exit with STATUS.  */ | 
|  |  | 
|  | static void | 
|  | usage (FILE *stream, int status) | 
|  | { | 
|  | fprintf (stream, _("Usage: %s [option(s)] [addr(s)]\n"), program_name); | 
|  | fprintf (stream, _(" Convert addresses into line number/file name pairs.\n")); | 
|  | fprintf (stream, _(" If no addresses are specified on the command line, they will be read from stdin\n")); | 
|  | fprintf (stream, _(" The options are:\n\ | 
|  | @<file>                Read options from <file>\n\ | 
|  | -a --addresses         Show addresses\n\ | 
|  | -b --target=<bfdname>  Set the binary file format\n\ | 
|  | -e --exe=<executable>  Set the input file name (default is a.out)\n\ | 
|  | -i --inlines           Unwind inlined functions\n\ | 
|  | -j --section=<name>    Read section-relative offsets instead of addresses\n\ | 
|  | -p --pretty-print      Make the output easier to read for humans\n\ | 
|  | -s --basenames         Strip directory names\n\ | 
|  | -f --functions         Show function names\n\ | 
|  | -C --demangle[=style]  Demangle function names\n\ | 
|  | -R --recurse-limit     Enable a limit on recursion whilst demangling.  [Default]\n\ | 
|  | -r --no-recurse-limit  Disable a limit on recursion whilst demangling\n\ | 
|  | -h --help              Display this information\n\ | 
|  | -v --version           Display the program's version\n\ | 
|  | \n")); | 
|  |  | 
|  | list_supported_targets (program_name, stream); | 
|  | if (REPORT_BUGS_TO[0] && status == 0) | 
|  | fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); | 
|  | exit (status); | 
|  | } | 
|  |  | 
|  | /* Read in the symbol table.  */ | 
|  |  | 
|  | static void | 
|  | slurp_symtab (bfd *abfd) | 
|  | { | 
|  | long storage; | 
|  | long symcount; | 
|  | bool dynamic = false; | 
|  |  | 
|  | if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0) | 
|  | return; | 
|  |  | 
|  | storage = bfd_get_symtab_upper_bound (abfd); | 
|  | if (storage == 0) | 
|  | { | 
|  | storage = bfd_get_dynamic_symtab_upper_bound (abfd); | 
|  | dynamic = true; | 
|  | } | 
|  | if (storage < 0) | 
|  | bfd_fatal (bfd_get_filename (abfd)); | 
|  |  | 
|  | syms = (asymbol **) xmalloc (storage); | 
|  | if (dynamic) | 
|  | symcount = bfd_canonicalize_dynamic_symtab (abfd, syms); | 
|  | else | 
|  | symcount = bfd_canonicalize_symtab (abfd, syms); | 
|  | if (symcount < 0) | 
|  | bfd_fatal (bfd_get_filename (abfd)); | 
|  |  | 
|  | /* If there are no symbols left after canonicalization and | 
|  | we have not tried the dynamic symbols then give them a go.  */ | 
|  | if (symcount == 0 | 
|  | && ! dynamic | 
|  | && (storage = bfd_get_dynamic_symtab_upper_bound (abfd)) > 0) | 
|  | { | 
|  | free (syms); | 
|  | syms = xmalloc (storage); | 
|  | symcount = bfd_canonicalize_dynamic_symtab (abfd, syms); | 
|  | } | 
|  |  | 
|  | /* PR 17512: file: 2a1d3b5b. | 
|  | Do not pretend that we have some symbols when we don't.  */ | 
|  | if (symcount <= 0) | 
|  | { | 
|  | free (syms); | 
|  | syms = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* These global variables are used to pass information between | 
|  | translate_addresses and find_address_in_section.  */ | 
|  |  | 
|  | static bfd_vma pc; | 
|  | static const char *filename; | 
|  | static const char *functionname; | 
|  | static unsigned int line; | 
|  | static unsigned int discriminator; | 
|  | static bool found; | 
|  |  | 
|  | /* Look for an address in a section.  This is called via | 
|  | bfd_map_over_sections.  */ | 
|  |  | 
|  | static void | 
|  | find_address_in_section (bfd *abfd, asection *section, | 
|  | void *data ATTRIBUTE_UNUSED) | 
|  | { | 
|  | bfd_vma vma; | 
|  | bfd_size_type size; | 
|  |  | 
|  | if (found) | 
|  | return; | 
|  |  | 
|  | if ((bfd_section_flags (section) & SEC_ALLOC) == 0) | 
|  | return; | 
|  |  | 
|  | vma = bfd_section_vma (section); | 
|  | if (pc < vma) | 
|  | return; | 
|  |  | 
|  | size = bfd_section_size (section); | 
|  | if (pc >= vma + size) | 
|  | return; | 
|  |  | 
|  | found = bfd_find_nearest_line_discriminator (abfd, section, syms, pc - vma, | 
|  | &filename, &functionname, | 
|  | &line, &discriminator); | 
|  | } | 
|  |  | 
|  | /* Look for an offset in a section.  This is directly called.  */ | 
|  |  | 
|  | static void | 
|  | find_offset_in_section (bfd *abfd, asection *section) | 
|  | { | 
|  | bfd_size_type size; | 
|  |  | 
|  | if (found) | 
|  | return; | 
|  |  | 
|  | if ((bfd_section_flags (section) & SEC_ALLOC) == 0) | 
|  | return; | 
|  |  | 
|  | size = bfd_section_size (section); | 
|  | if (pc >= size) | 
|  | return; | 
|  |  | 
|  | found = bfd_find_nearest_line_discriminator (abfd, section, syms, pc, | 
|  | &filename, &functionname, | 
|  | &line, &discriminator); | 
|  | } | 
|  |  | 
|  | /* Read hexadecimal addresses from stdin, translate into | 
|  | file_name:line_number and optionally function name.  */ | 
|  |  | 
|  | static void | 
|  | translate_addresses (bfd *abfd, asection *section) | 
|  | { | 
|  | int read_stdin = (naddr == 0); | 
|  |  | 
|  | for (;;) | 
|  | { | 
|  | if (read_stdin) | 
|  | { | 
|  | char addr_hex[100]; | 
|  |  | 
|  | if (fgets (addr_hex, sizeof addr_hex, stdin) == NULL) | 
|  | break; | 
|  | pc = bfd_scan_vma (addr_hex, NULL, 16); | 
|  | } | 
|  | else | 
|  | { | 
|  | if (naddr <= 0) | 
|  | break; | 
|  | --naddr; | 
|  | pc = bfd_scan_vma (*addr++, NULL, 16); | 
|  | } | 
|  |  | 
|  | if (bfd_get_flavour (abfd) == bfd_target_elf_flavour) | 
|  | { | 
|  | const struct elf_backend_data *bed = get_elf_backend_data (abfd); | 
|  | bfd_vma sign = (bfd_vma) 1 << (bed->s->arch_size - 1); | 
|  |  | 
|  | pc &= (sign << 1) - 1; | 
|  | if (bed->sign_extend_vma) | 
|  | pc = (pc ^ sign) - sign; | 
|  | } | 
|  |  | 
|  | if (with_addresses) | 
|  | { | 
|  | printf ("0x"); | 
|  | bfd_printf_vma (abfd, pc); | 
|  |  | 
|  | if (pretty_print) | 
|  | printf (": "); | 
|  | else | 
|  | printf ("\n"); | 
|  | } | 
|  |  | 
|  | found = false; | 
|  | if (section) | 
|  | find_offset_in_section (abfd, section); | 
|  | else | 
|  | bfd_map_over_sections (abfd, find_address_in_section, NULL); | 
|  |  | 
|  | if (! found) | 
|  | { | 
|  | if (with_functions) | 
|  | { | 
|  | if (pretty_print) | 
|  | printf ("?? "); | 
|  | else | 
|  | printf ("??\n"); | 
|  | } | 
|  | printf ("??:0\n"); | 
|  | } | 
|  | else | 
|  | { | 
|  | while (1) | 
|  | { | 
|  | if (with_functions) | 
|  | { | 
|  | const char *name; | 
|  | char *alloc = NULL; | 
|  |  | 
|  | name = functionname; | 
|  | if (name == NULL || *name == '\0') | 
|  | name = "??"; | 
|  | else if (do_demangle) | 
|  | { | 
|  | alloc = bfd_demangle (abfd, name, demangle_flags); | 
|  | if (alloc != NULL) | 
|  | name = alloc; | 
|  | } | 
|  |  | 
|  | printf ("%s", name); | 
|  | if (pretty_print) | 
|  | /* Note for translators:  This printf is used to join the | 
|  | function name just printed above to the line number/ | 
|  | file name pair that is about to be printed below.  Eg: | 
|  |  | 
|  | foo at 123:bar.c  */ | 
|  | printf (_(" at ")); | 
|  | else | 
|  | printf ("\n"); | 
|  |  | 
|  | free (alloc); | 
|  | } | 
|  |  | 
|  | if (base_names && filename != NULL) | 
|  | { | 
|  | char *h; | 
|  |  | 
|  | h = strrchr (filename, '/'); | 
|  | if (h != NULL) | 
|  | filename = h + 1; | 
|  | } | 
|  |  | 
|  | printf ("%s:", filename ? filename : "??"); | 
|  | if (line != 0) | 
|  | { | 
|  | if (discriminator != 0) | 
|  | printf ("%u (discriminator %u)\n", line, discriminator); | 
|  | else | 
|  | printf ("%u\n", line); | 
|  | } | 
|  | else | 
|  | printf ("?\n"); | 
|  | if (!unwind_inlines) | 
|  | found = false; | 
|  | else | 
|  | found = bfd_find_inliner_info (abfd, &filename, &functionname, | 
|  | &line); | 
|  | if (! found) | 
|  | break; | 
|  | if (pretty_print) | 
|  | /* Note for translators: This printf is used to join the | 
|  | line number/file name pair that has just been printed with | 
|  | the line number/file name pair that is going to be printed | 
|  | by the next iteration of the while loop.  Eg: | 
|  |  | 
|  | 123:bar.c (inlined by) 456:main.c  */ | 
|  | printf (_(" (inlined by) ")); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* fflush() is essential for using this command as a server | 
|  | child process that reads addresses from a pipe and responds | 
|  | with line number information, processing one address at a | 
|  | time.  */ | 
|  | fflush (stdout); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Process a file.  Returns an exit value for main().  */ | 
|  |  | 
|  | static int | 
|  | process_file (const char *file_name, const char *section_name, | 
|  | const char *target) | 
|  | { | 
|  | bfd *abfd; | 
|  | asection *section; | 
|  | char **matching; | 
|  |  | 
|  | if (get_file_size (file_name) < 1) | 
|  | return 1; | 
|  |  | 
|  | abfd = bfd_openr (file_name, target); | 
|  | if (abfd == NULL) | 
|  | bfd_fatal (file_name); | 
|  |  | 
|  | /* Decompress sections.  */ | 
|  | abfd->flags |= BFD_DECOMPRESS; | 
|  |  | 
|  | if (bfd_check_format (abfd, bfd_archive)) | 
|  | fatal (_("%s: cannot get addresses from archive"), file_name); | 
|  |  | 
|  | if (! bfd_check_format_matches (abfd, bfd_object, &matching)) | 
|  | { | 
|  | bfd_nonfatal (bfd_get_filename (abfd)); | 
|  | if (bfd_get_error () == bfd_error_file_ambiguously_recognized) | 
|  | { | 
|  | list_matching_formats (matching); | 
|  | free (matching); | 
|  | } | 
|  | xexit (1); | 
|  | } | 
|  |  | 
|  | if (section_name != NULL) | 
|  | { | 
|  | section = bfd_get_section_by_name (abfd, section_name); | 
|  | if (section == NULL) | 
|  | fatal (_("%s: cannot find section %s"), file_name, section_name); | 
|  | } | 
|  | else | 
|  | section = NULL; | 
|  |  | 
|  | slurp_symtab (abfd); | 
|  |  | 
|  | translate_addresses (abfd, section); | 
|  |  | 
|  | free (syms); | 
|  | syms = NULL; | 
|  |  | 
|  | bfd_close (abfd); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | main (int argc, char **argv) | 
|  | { | 
|  | const char *file_name; | 
|  | const char *section_name; | 
|  | char *target; | 
|  | int c; | 
|  |  | 
|  | #ifdef HAVE_LC_MESSAGES | 
|  | setlocale (LC_MESSAGES, ""); | 
|  | #endif | 
|  | setlocale (LC_CTYPE, ""); | 
|  | bindtextdomain (PACKAGE, LOCALEDIR); | 
|  | textdomain (PACKAGE); | 
|  |  | 
|  | program_name = *argv; | 
|  | xmalloc_set_program_name (program_name); | 
|  | bfd_set_error_program_name (program_name); | 
|  |  | 
|  | expandargv (&argc, &argv); | 
|  |  | 
|  | if (bfd_init () != BFD_INIT_MAGIC) | 
|  | fatal (_("fatal error: libbfd ABI mismatch")); | 
|  | set_default_bfd_target (); | 
|  |  | 
|  | file_name = NULL; | 
|  | section_name = NULL; | 
|  | target = NULL; | 
|  | while ((c = getopt_long (argc, argv, "ab:Ce:rRsfHhij:pVv", long_options, (int *) 0)) | 
|  | != EOF) | 
|  | { | 
|  | switch (c) | 
|  | { | 
|  | case 0: | 
|  | break;		/* We've been given a long option.  */ | 
|  | case 'a': | 
|  | with_addresses = true; | 
|  | break; | 
|  | case 'b': | 
|  | target = optarg; | 
|  | break; | 
|  | case 'C': | 
|  | do_demangle = true; | 
|  | if (optarg != NULL) | 
|  | { | 
|  | enum demangling_styles style; | 
|  |  | 
|  | style = cplus_demangle_name_to_style (optarg); | 
|  | if (style == unknown_demangling) | 
|  | fatal (_("unknown demangling style `%s'"), | 
|  | optarg); | 
|  |  | 
|  | cplus_demangle_set_style (style); | 
|  | } | 
|  | break; | 
|  | case 'r': | 
|  | demangle_flags |= DMGL_NO_RECURSE_LIMIT; | 
|  | break; | 
|  | case 'R': | 
|  | demangle_flags &= ~ DMGL_NO_RECURSE_LIMIT; | 
|  | break; | 
|  | case 'e': | 
|  | file_name = optarg; | 
|  | break; | 
|  | case 's': | 
|  | base_names = true; | 
|  | break; | 
|  | case 'f': | 
|  | with_functions = true; | 
|  | break; | 
|  | case 'p': | 
|  | pretty_print = true; | 
|  | break; | 
|  | case 'v': | 
|  | case 'V': | 
|  | print_version ("addr2line"); | 
|  | break; | 
|  | case 'h': | 
|  | case 'H': | 
|  | usage (stdout, 0); | 
|  | break; | 
|  | case 'i': | 
|  | unwind_inlines = true; | 
|  | break; | 
|  | case 'j': | 
|  | section_name = optarg; | 
|  | break; | 
|  | default: | 
|  | usage (stderr, 1); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (file_name == NULL) | 
|  | file_name = "a.out"; | 
|  |  | 
|  | addr = argv + optind; | 
|  | naddr = argc - optind; | 
|  |  | 
|  | return process_file (file_name, section_name, target); | 
|  | } |