X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=binutils%2Faddr2line.c;h=fd2ac80dacd2fd14b353a7acf55aeb6f8d826288;hb=4bb8b8e9381bce9734454470ebd1572534e7514e;hp=cd31b8191e7627660906d1a286ce9d77b63c6c7d;hpb=32866df75ece22ec1fd88e99e3c5effe9238e072;p=binutils-gdb.git diff --git a/binutils/addr2line.c b/binutils/addr2line.c index cd31b8191e7..fd2ac80dacd 100644 --- a/binutils/addr2line.c +++ b/binutils/addr2line.c @@ -1,6 +1,5 @@ /* addr2line.c -- convert addresses to line number and function name - Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2006, 2007 - Free Software Foundation, Inc. + Copyright (C) 1997-2022 Free Software Foundation, Inc. Contributed by Ulrich Lauther This file is part of GNU Binutils. @@ -37,24 +36,38 @@ #include "libiberty.h" #include "demangle.h" #include "bucomm.h" +#include "elf-bfd.h" +#include "safe-ctype.h" -static bfd_boolean unwind_inlines; /* -i, unwind inlined functions. */ -static bfd_boolean with_functions; /* -f, show function names. */ -static bfd_boolean do_demangle; /* -C, demangle names. */ -static bfd_boolean base_names; /* -s, strip directory names. */ +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 long symcount; 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'}, @@ -78,13 +91,17 @@ usage (FILE *stream, int status) fprintf (stream, _(" If no addresses are specified on the command line, they will be read from stdin\n")); fprintf (stream, _(" The options are:\n\ @ Read options from \n\ + -a --addresses Show addresses\n\ -b --target= Set the binary file format\n\ -e --exe= Set the input file name (default is a.out)\n\ -i --inlines Unwind inlined functions\n\ -j --section= 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")); @@ -100,18 +117,47 @@ usage (FILE *stream, int status) static void slurp_symtab (bfd *abfd) { - long symcount; - unsigned int size; + long storage; + bool dynamic = false; if ((bfd_get_file_flags (abfd) & HAS_SYMS) == 0) return; - symcount = bfd_read_minisymbols (abfd, FALSE, (void *) &syms, &size); - if (symcount == 0) - symcount = bfd_read_minisymbols (abfd, TRUE /* dynamic */, (void *) &syms, &size); + 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 @@ -121,7 +167,8 @@ static bfd_vma pc; static const char *filename; static const char *functionname; static unsigned int line; -static bfd_boolean found; +static unsigned int discriminator; +static bool found; /* Look for an address in a section. This is called via bfd_map_over_sections. */ @@ -136,19 +183,20 @@ find_address_in_section (bfd *abfd, asection *section, if (found) return; - if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0) + if ((bfd_section_flags (section) & SEC_ALLOC) == 0) return; - vma = bfd_get_section_vma (abfd, section); + vma = bfd_section_vma (section); if (pc < vma) return; - size = bfd_get_section_size (section); + size = bfd_section_size (section); if (pc >= vma + size) return; - found = bfd_find_nearest_line (abfd, section, syms, pc - vma, - &filename, &functionname, &line); + 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. */ @@ -161,44 +209,128 @@ find_offset_in_section (bfd *abfd, asection *section) if (found) return; - if ((bfd_get_section_flags (abfd, section) & SEC_ALLOC) == 0) + if ((bfd_section_flags (section) & SEC_ALLOC) == 0) return; - size = bfd_get_section_size (section); + size = bfd_section_size (section); if (pc >= size) return; - found = bfd_find_nearest_line (abfd, section, syms, pc, - &filename, &functionname, &line); + found = bfd_find_nearest_line_discriminator (abfd, section, syms, pc, + &filename, &functionname, + &line, &discriminator); } -/* Read hexadecimal addresses from stdin, translate into +/* Lookup a symbol with offset in symbol table. */ + +static bfd_vma +lookup_symbol (bfd *abfd, char *sym, size_t offset) +{ + long i; + + for (i = 0; i < symcount; i++) + { + if (!strcmp (syms[i]->name, sym)) + return syms[i]->value + offset + bfd_asymbol_section (syms[i])->vma; + } + /* Try again mangled */ + for (i = 0; i < symcount; i++) + { + char *d = bfd_demangle (abfd, syms[i]->name, demangle_flags); + bool match = d && !strcmp (d, sym); + free (d); + + if (match) + return syms[i]->value + offset + bfd_asymbol_section (syms[i])->vma; + } + return 0; +} + +/* Split an symbol+offset expression. adr is modified. */ + +static bool +is_symbol (char *adr, char **symp, size_t *offset) +{ + char *end; + + while (ISSPACE (*adr)) + adr++; + if (ISDIGIT (*adr) || *adr == 0) + return false; + /* Could be either symbol or hex number. Check if it has +. */ + if (TOUPPER(*adr) >= 'A' && TOUPPER(*adr) <= 'F' && !strchr (adr, '+')) + return false; + + *symp = adr; + while (*adr && !ISSPACE (*adr) && *adr != '+') + adr++; + end = adr; + while (ISSPACE (*adr)) + adr++; + *offset = 0; + if (*adr == '+') + { + adr++; + *offset = strtoul(adr, NULL, 0); + } + *end = 0; + return true; +} + +/* Read hexadecimal or symbolic with offset 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); + char *adr; + char addr_hex[100]; + char *symp; + size_t offset; 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); + adr = addr_hex; } else { if (naddr <= 0) break; --naddr; - pc = bfd_scan_vma (*addr++, NULL, 16); + adr = *addr++; + } + + if (is_symbol (adr, &symp, &offset)) + pc = lookup_symbol (abfd, symp, offset); + else + pc = bfd_scan_vma (adr, 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; } - found = FALSE; + 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 @@ -207,49 +339,82 @@ translate_addresses (bfd *abfd, asection *section) if (! found) { if (with_functions) - printf ("??\n"); + { + if (pretty_print) + printf ("?? "); + else + printf ("??\n"); + } printf ("??:0\n"); } else { - do { - 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, DMGL_ANSI | DMGL_PARAMS); - if (alloc != NULL) - name = alloc; - } - - printf ("%s\n", name); - - if (alloc != NULL) - free (alloc); - } - - if (base_names && filename != NULL) - { - char *h; - - h = strrchr (filename, '/'); - if (h != NULL) - filename = h + 1; - } - - printf ("%s:%u\n", filename ? filename : "??", line); - if (!unwind_inlines) - found = FALSE; - else - found = bfd_find_inliner_info (abfd, &filename, &functionname, &line); - } while (found); + 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 @@ -277,6 +442,9 @@ process_file (const char *file_name, const char *section_name, 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); @@ -304,11 +472,8 @@ process_file (const char *file_name, const char *section_name, translate_addresses (abfd, section); - if (syms != NULL) - { - free (syms); - syms = NULL; - } + free (syms); + syms = NULL; bfd_close (abfd); @@ -323,38 +488,41 @@ main (int argc, char **argv) char *target; int c; -#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) +#ifdef HAVE_LC_MESSAGES setlocale (LC_MESSAGES, ""); #endif -#if defined (HAVE_SETLOCALE) setlocale (LC_CTYPE, ""); -#endif bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); program_name = *argv; xmalloc_set_program_name (program_name); + bfd_set_error_program_name (program_name); expandargv (&argc, &argv); - bfd_init (); + 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, "b:Ce:sfHhij:Vv", long_options, (int *) 0)) + 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; + do_demangle = true; if (optarg != NULL) { enum demangling_styles style; @@ -367,15 +535,24 @@ main (int argc, char **argv) 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; + base_names = true; break; case 'f': - with_functions = TRUE; + with_functions = true; break; + case 'p': + pretty_print = true; + break; case 'v': case 'V': print_version ("addr2line"); @@ -385,7 +562,7 @@ main (int argc, char **argv) usage (stdout, 0); break; case 'i': - unwind_inlines = TRUE; + unwind_inlines = true; break; case 'j': section_name = optarg;