diff --git a/binutils/NEWS b/binutils/NEWS index a3ee86ef7f..ce236e5a31 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -1,5 +1,10 @@ -*- text -*- +* The addr2line, c++filt, nm and objdump tools now have a limit on the maximum amount of recursion + that is allowed whilst demangling strings. The default value for this + limit is 1024. The --recurse-limit=N option can be used to change the + limit, or remove it entirely if N is zero. + * Objdump's --disassemble option can now take a parameter, specifying the starting symbol for disassembly. Disassembly will continue from this symbol up to the next symbol. diff --git a/binutils/addr2line.c b/binutils/addr2line.c index 008e62026e..822f990bad 100644 --- a/binutils/addr2line.c +++ b/binutils/addr2line.c @@ -45,6 +45,9 @@ static bfd_boolean do_demangle; /* -C, demangle names. */ static bfd_boolean pretty_print; /* -p, print on one line. */ static bfd_boolean base_names; /* -s, strip directory names. */ +/* Flags passed to the name demangler. */ +static int demangle_flags = DMGL_PARAMS | DMGL_ANSI | DMGL_RECURSE_LIMIT; + static int naddr; /* Number of addresses to process. */ static char **addr; /* Hex addresses to process. */ @@ -59,6 +62,8 @@ static struct option long_options[] = {"functions", no_argument, NULL, 'f'}, {"inlines", no_argument, NULL, 'i'}, {"pretty-print", no_argument, NULL, 'p'}, + {"recurse-limit", required_argument, NULL, 'r'}, + {"recursion-limit", required_argument, NULL, 'r'}, {"section", required_argument, NULL, 'j'}, {"target", required_argument, NULL, 'b'}, {"help", no_argument, NULL, 'H'}, @@ -91,6 +96,7 @@ usage (FILE *stream, int status) -s --basenames Strip directory names\n\ -f --functions Show function names\n\ -C --demangle[=style] Demangle function names\n\ + -r --recurse-limit= Set the recursion limit for name demangling\n\ -h --help Display this information\n\ -v --version Display the program's version\n\ \n")); @@ -289,7 +295,7 @@ translate_addresses (bfd *abfd, asection *section) name = "??"; else if (do_demangle) { - alloc = bfd_demangle (abfd, name, DMGL_ANSI | DMGL_PARAMS); + alloc = bfd_demangle (abfd, name, demangle_flags); if (alloc != NULL) name = alloc; } @@ -442,7 +448,7 @@ main (int argc, char **argv) file_name = NULL; section_name = NULL; target = NULL; - while ((c = getopt_long (argc, argv, "ab:Ce:sfHhij:pVv", long_options, (int *) 0)) + while ((c = getopt_long (argc, argv, "ab:Ce:r:sfHhij:pVv", long_options, (int *) 0)) != EOF) { switch (c) @@ -469,6 +475,18 @@ main (int argc, char **argv) cplus_demangle_set_style (style); } break; + case 'r': + { + unsigned long limit = strtoul (optarg, NULL, 0); + if (limit > 1) + { + cplus_demangle_set_recursion_limit (limit); + demangle_flags |= DMGL_RECURSE_LIMIT; + } + else + demangle_flags &= ~ DMGL_RECURSE_LIMIT; + } + break; case 'e': file_name = optarg; break; diff --git a/binutils/cxxfilt.c b/binutils/cxxfilt.c index e7272445c9..f5a23a3cc7 100644 --- a/binutils/cxxfilt.c +++ b/binutils/cxxfilt.c @@ -29,7 +29,7 @@ #include "safe-ctype.h" #include "bucomm.h" -static int flags = DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE; +static int flags = DMGL_PARAMS | DMGL_ANSI | DMGL_VERBOSE | DMGL_RECURSE_LIMIT; static int strip_underscore = TARGET_PREPENDS_UNDERSCORE; static const struct option long_options[] = @@ -42,6 +42,8 @@ static const struct option long_options[] = {"no-verbose", no_argument, NULL, 'i'}, {"types", no_argument, NULL, 't'}, {"version", no_argument, NULL, 'v'}, + {"recurse-limit", required_argument, NULL, 'r'}, + {"recursion-limit", required_argument, NULL, 'r'}, {NULL, no_argument, NULL, 0} }; @@ -102,6 +104,7 @@ Options are:\n\ fprintf (stream, "\ [-p|--no-params] Do not display function arguments\n\ [-i|--no-verbose] Do not show implementation details (if any)\n\ + [-r|--recurse-limit ] Set recursion limit to . [Default 1024]\n\ [-t|--types] Also attempt to demangle type encodings\n\ [-s|--format "); print_demangler_list (stream); @@ -180,7 +183,7 @@ main (int argc, char **argv) expandargv (&argc, &argv); - while ((c = getopt_long (argc, argv, "_hinps:tv", long_options, (int *) 0)) != EOF) + while ((c = getopt_long (argc, argv, "_hinpr:s:tv", long_options, (int *) 0)) != EOF) { switch (c) { @@ -195,6 +198,18 @@ main (int argc, char **argv) case 'p': flags &= ~ DMGL_PARAMS; break; + case 'r': + { + unsigned long limit = strtoul (optarg, NULL, 0); + if (limit > 1) + { + cplus_demangle_set_recursion_limit (limit); + flags |= DMGL_RECURSE_LIMIT; + } + else + flags &= ~ DMGL_RECURSE_LIMIT; + } + break; case 't': flags |= DMGL_TYPES; break; diff --git a/binutils/dlltool.c b/binutils/dlltool.c index 2c751241f1..963d541b8e 100644 --- a/binutils/dlltool.c +++ b/binutils/dlltool.c @@ -1802,7 +1802,7 @@ gen_def_file (void) for (i = 0, exp = d_exports; exp; i++, exp = exp->next) { char *quote = strchr (exp->name, '.') ? "\"" : ""; - char *res = cplus_demangle (exp->internal_name, DMGL_ANSI | DMGL_PARAMS); + char *res = cplus_demangle (exp->internal_name, DMGL_ANSI | DMGL_PARAMS | DMGL_RECURSE_LIMIT); if (res) { diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi index 9954adf484..ac10d582ce 100644 --- a/binutils/doc/binutils.texi +++ b/binutils/doc/binutils.texi @@ -769,7 +769,8 @@ nm [@option{-A}|@option{-o}|@option{--print-file-name}] [@option{-a}|@option{--d [@option{-s}|@option{--print-armap}] [@option{-t} @var{radix}|@option{--radix=}@var{radix}] [@option{-u}|@option{--undefined-only}] [@option{-V}|@option{--version}] [@option{-X 32_64}] [@option{--defined-only}] [@option{--no-demangle}] - [@option{--plugin} @var{name}] [@option{--size-sort}] [@option{--special-syms}] + [@option{--plugin} @var{name}] [@option{--recurse-limit=}@var{N}] + [@option{--size-sort}] [@option{--special-syms}] [@option{--synthetic}] [@option{--with-symbol-versions}] [@option{--target=}@var{bfdname}] [@var{objfile}@dots{}] @c man end @@ -939,6 +940,17 @@ for more information on demangling. @item --no-demangle Do not demangle low-level symbol names. This is the default. +@item --recurse-limit=@var{N} +@itemx --recursion-limit @var{N} +Sets a limit on the amount of recursion performed whilst demangling +strings. Since the name mangling formats allow for an inifinite +level of recursion it is possible to create strings whose decoding +will exhaust the amount of stack space available on the host machine, +triggering a memory fault. The limit tries to prevent this from +happening by restricting recursion to a maximum amount. The default +value for this limit is 1024. To disable the limit set its value to +0. + @item -D @itemx --dynamic @cindex dynamic symbols @@ -2098,6 +2110,7 @@ objdump [@option{-a}|@option{--archive-headers}] [@option{--adjust-vma=}@var{offset}] [@option{--dwarf-depth=@var{n}}] [@option{--dwarf-start=@var{n}}] + [@option{--recurse-limit=@var{n}}] [@option{--special-syms}] [@option{--prefix=}@var{prefix}] [@option{--prefix-strip=}@var{level}] @@ -2174,6 +2187,17 @@ mangling styles. The optional demangling style argument can be used to choose an appropriate demangling style for your compiler. @xref{c++filt}, for more information on demangling. +@item --recurse-limit=@var{N} +@itemx --recursion-limit @var{N} +Sets a limit on the amount of recursion performed whilst demangling +strings. Since the name mangling formats allow for an inifinite +level of recursion it is possible to create strings whose decoding +will exhaust the amount of stack space available on the host machine, +triggering a memory fault. The limit tries to prevent this from +happening by restricting recursion to a maximum amount. The default +value for this limit is 1024. To disable the limit set its value to +0. + @item -g @itemx --debugging Display debugging information. This attempts to parse STABS @@ -3403,6 +3427,7 @@ c++filt [@option{-_}|@option{--strip-underscore}] [@option{-p}|@option{--no-params}] [@option{-t}|@option{--types}] [@option{-i}|@option{--no-verbose}] + [@option{-r} @var{N}|@option{--recurse-limit=}@var{N}] [@option{-s} @var{format}|@option{--format=}@var{format}] [@option{--help}] [@option{--version}] [@var{symbol}@dots{}] @c man end @@ -3507,6 +3532,18 @@ demangled to ``signed char''. Do not include implementation details (if any) in the demangled output. +@item -r @var{N} +@itemx --recurse-limit=@var{N} +@itemx --recursion-limit @var{N} +Sets a limit on the amount of recursion performed whilst demangling +strings. Since the name mangling formats allow for an inifinite +level of recursion it is possible to create strings whose decoding +will exhaust the amount of stack space available on the host machine, +triggering a memory fault. The limit tries to prevent this from +happening by restricting recursion to a maximum amount. The default +value for this limit is 1024. To disable the limit set its value to +0. + @item -s @var{format} @itemx --format=@var{format} @command{c++filt} can decode various methods of mangling, used by @@ -3580,6 +3617,7 @@ c++filt @var{option} @var{symbol} addr2line [@option{-a}|@option{--addresses}] [@option{-b} @var{bfdname}|@option{--target=}@var{bfdname}] [@option{-C}|@option{--demangle}[=@var{style}]] + [@option{-r} @var{N}|@option{--recurse-limit=}@var{N}] [@option{-e} @var{filename}|@option{--exe=}@var{filename}] [@option{-f}|@option{--functions}] [@option{-s}|@option{--basename}] [@option{-i}|@option{--inlines}] @@ -3705,6 +3743,22 @@ Read offsets relative to the specified section instead of absolute addresses. Make the output more human friendly: each location are printed on one line. If option @option{-i} is specified, lines for all enclosing scopes are prefixed with @samp{(inlined by)}. + +@item -r @var{N} +@itemx --recurse-limit=@var{N} +@itemx --recursion-limit @var{N} +Sets a limit on the amount of recursion performed whilst demangling +strings. Since the name mangling formats allow for an inifinite +level of recursion it is possible to create strings whose decoding +will exhaust the amount of stack space available on the host machine, +triggering a memory fault. The limit tries to prevent this from +happening by restricting recursion to a maximum amount. The default +value for this limit is 1024. To disable the limit set its value to +0. + +Note this option is only effective if the @option{-C} or +@option{--demangle} option has been enabled. + @end table @c man end diff --git a/binutils/nm.c b/binutils/nm.c index bc4fccb5fc..e26a88afc5 100644 --- a/binutils/nm.c +++ b/binutils/nm.c @@ -162,6 +162,8 @@ static int line_numbers = 0; /* Print line numbers for symbols. */ static int allow_special_symbols = 0; /* Allow special symbols. */ static int with_symbol_versions = 0; /* Include symbol version information in the output. */ +static int demangle_flags = DMGL_ANSI | DMGL_PARAMS | DMGL_RECURSE_LIMIT; + /* When to print the names of files. Not mutually exclusive in SYSV format. */ static int filename_per_file = 0; /* Once per file, on its own line. */ static int filename_per_symbol = 0; /* Once per symbol, at start of line. */ @@ -194,9 +196,13 @@ static const char *plugin_target = NULL; static bfd *lineno_cache_bfd; static bfd *lineno_cache_rel_bfd; -#define OPTION_TARGET 200 -#define OPTION_PLUGIN (OPTION_TARGET + 1) -#define OPTION_SIZE_SORT (OPTION_PLUGIN + 1) +enum long_option_values +{ + OPTION_TARGET = 200, + OPTION_PLUGIN, + OPTION_SIZE_SORT, + OPTION_RECURSE_LIMIT +}; static struct option long_options[] = { @@ -217,6 +223,8 @@ static struct option long_options[] = {"print-file-name", no_argument, 0, 'o'}, {"print-size", no_argument, 0, 'S'}, {"radix", required_argument, 0, 't'}, + {"recurse-limit", required_argument, NULL, OPTION_RECURSE_LIMIT}, + {"recursion-limit", required_argument, NULL, OPTION_RECURSE_LIMIT}, {"reverse-sort", no_argument, &reverse_sort, 1}, {"size-sort", no_argument, 0, OPTION_SIZE_SORT}, {"special-syms", no_argument, &allow_special_symbols, 1}, @@ -245,6 +253,7 @@ usage (FILE *stream, int status) `gnu', `lucid', `arm', `hp', `edg', `gnu-v3', `java'\n\ or `gnat'\n\ --no-demangle Do not demangle low-level symbol names\n\ + --recurse-limit=N Set demangling recursion limit to N. [Default 1024]\n\ -D, --dynamic Display dynamic symbols instead of normal symbols\n\ --defined-only Display only defined symbols\n\ -e (ignored)\n\ @@ -407,7 +416,7 @@ print_symname (const char *form, const char *name, bfd *abfd) { if (do_demangle && *name) { - char *res = bfd_demangle (abfd, name, DMGL_ANSI | DMGL_PARAMS); + char *res = bfd_demangle (abfd, name, demangle_flags); if (res != NULL) { @@ -1687,6 +1696,18 @@ main (int argc, char **argv) cplus_demangle_set_style (style); } break; + case OPTION_RECURSE_LIMIT: + { + unsigned long limit = strtoul (optarg, NULL, 0); + if (limit > 1) + { + cplus_demangle_set_recursion_limit (limit); + demangle_flags |= DMGL_RECURSE_LIMIT; + } + else + demangle_flags &= ~ DMGL_RECURSE_LIMIT; + } + break; case 'D': dynamic = 1; break; diff --git a/binutils/objdump.c b/binutils/objdump.c index 21f1284319..e275f6f06d 100644 --- a/binutils/objdump.c +++ b/binutils/objdump.c @@ -120,6 +120,8 @@ static size_t prefix_length; static bfd_boolean unwind_inlines; /* --inlines. */ static const char * disasm_sym; /* Disassembly start symbol. */ +static int demangle_flags = DMGL_ANSI | DMGL_PARAMS | DMGL_RECURSE_LIMIT; + /* A structure to record the sections mentioned in -j switches. */ struct only { @@ -252,6 +254,7 @@ usage (FILE *stream, int status) The STYLE, if specified, can be `auto', `gnu',\n\ `lucid', `arm', `hp', `edg', `gnu-v3', `java'\n\ or `gnat'\n\ + --recurse-limit=N Set demangling recursion limit to N. [Default 1024]\n\ -w, --wide Format output for more than 80 columns\n\ -z, --disassemble-zeroes Do not skip blocks of zeroes when disassembling\n\ --start-address=ADDR Only process data whose address is >= ADDR\n\ @@ -302,6 +305,7 @@ enum option_values OPTION_DWARF_DEPTH, OPTION_DWARF_CHECK, OPTION_DWARF_START, + OPTION_RECURSE_LIMIT, OPTION_INLINES }; @@ -333,6 +337,8 @@ static struct option long_options[]= {"line-numbers", no_argument, NULL, 'l'}, {"no-show-raw-insn", no_argument, &show_raw_insn, -1}, {"prefix-addresses", no_argument, &prefix_addresses, 1}, + {"recurse-limit", required_argument, NULL, OPTION_RECURSE_LIMIT}, + {"recursion-limit", required_argument, NULL, OPTION_RECURSE_LIMIT}, {"reloc", no_argument, NULL, 'r'}, {"section", required_argument, NULL, 'j'}, {"section-headers", no_argument, NULL, 'h'}, @@ -884,7 +890,7 @@ objdump_print_symname (bfd *abfd, struct disassemble_info *inf, if (do_demangle && name[0] != '\0') { /* Demangle the name. */ - alloc = bfd_demangle (abfd, name, DMGL_ANSI | DMGL_PARAMS); + alloc = bfd_demangle (abfd, name, demangle_flags); if (alloc != NULL) name = alloc; } @@ -2290,7 +2296,7 @@ disassemble_section (bfd *abfd, asection *section, void *inf) if (do_demangle && name[0] != '\0') { /* Demangle the name. */ - alloc = bfd_demangle (abfd, name, DMGL_ANSI | DMGL_PARAMS); + alloc = bfd_demangle (abfd, name, demangle_flags); if (alloc != NULL) name = alloc; } @@ -3268,7 +3274,7 @@ dump_symbols (bfd *abfd ATTRIBUTE_UNUSED, bfd_boolean dynamic) /* If we want to demangle the name, we demangle it here, and temporarily clobber it while calling bfd_print_symbol. FIXME: This is a gross hack. */ - alloc = bfd_demangle (cur_bfd, name, DMGL_ANSI | DMGL_PARAMS); + alloc = bfd_demangle (cur_bfd, name, demangle_flags); if (alloc != NULL) (*current)->name = alloc; bfd_print_symbol (cur_bfd, stdout, *current, @@ -3927,6 +3933,18 @@ main (int argc, char **argv) cplus_demangle_set_style (style); } break; + case OPTION_RECURSE_LIMIT: + { + unsigned long limit = strtoul (optarg, NULL, 0); + if (limit > 1) + { + cplus_demangle_set_recursion_limit (limit); + demangle_flags |= DMGL_RECURSE_LIMIT; + } + else + demangle_flags &= ~ DMGL_RECURSE_LIMIT; + } + break; case 'w': do_wide = wide_output = TRUE; break; diff --git a/binutils/prdbg.c b/binutils/prdbg.c index 4b9fa06c26..c981555521 100644 --- a/binutils/prdbg.c +++ b/binutils/prdbg.c @@ -286,6 +286,8 @@ static const struct debug_write_fns tg_fns = pr_end_function, /* Same, does nothing. */ tg_lineno }; + +static int demangle_flags = DMGL_ANSI | DMGL_PARAMS | DMGL_RECURSE_LIMIT; /* Print out the generic debugging information recorded in dhandle. */ @@ -2600,7 +2602,7 @@ tg_variable (void *p, const char *name, enum debug_var_kind kind, dname = NULL; if (info->demangler) - dname = info->demangler (info->abfd, name, DMGL_ANSI | DMGL_PARAMS); + dname = info->demangler (info->abfd, name, demangle_flags); from_class = NULL; if (dname != NULL) @@ -2661,7 +2663,7 @@ tg_start_function (void *p, const char *name, bfd_boolean global) dname = NULL; if (info->demangler) - dname = info->demangler (info->abfd, name, DMGL_ANSI | DMGL_PARAMS); + dname = info->demangler (info->abfd, name, demangle_flags); if (! substitute_type (info, dname ? dname : name)) return FALSE; diff --git a/binutils/stabs.c b/binutils/stabs.c index bf53607560..dcd2aba57f 100644 --- a/binutils/stabs.c +++ b/binutils/stabs.c @@ -215,6 +215,8 @@ static debug_type stab_demangle_v3_arg (void *, struct stab_handle *, struct demangle_component *, debug_type, bfd_boolean *); +static int demangle_flags = DMGL_ANSI | DMGL_RECURSE_LIMIT; + /* Save a string in memory. */ static char * @@ -4517,7 +4519,7 @@ stab_demangle_template (struct stab_demangle_info *minfo, const char **pp, free (s1); - s3 = cplus_demangle (s2, DMGL_ANSI); + s3 = cplus_demangle (s2, demangle_flags); free (s2); @@ -5243,7 +5245,7 @@ stab_demangle_v3_argtypes (void *dhandle, struct stab_handle *info, void *mem; debug_type *pargs; - dc = cplus_demangle_v3_components (physname, DMGL_PARAMS | DMGL_ANSI, &mem); + dc = cplus_demangle_v3_components (physname, DMGL_PARAMS | demangle_flags, &mem); if (dc == NULL) { stab_bad_demangle (physname); @@ -5418,7 +5420,7 @@ stab_demangle_v3_arg (void *dhandle, struct stab_handle *info, /* We print this component to get a class name which we can use. FIXME: This probably won't work if the template uses template parameters which refer to an outer template. */ - p = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, dc, 20, &alc); + p = cplus_demangle_print (DMGL_PARAMS | demangle_flags, dc, 20, &alc); if (p == NULL) { fprintf (stderr, _("Failed to print demangled template\n")); @@ -5498,7 +5500,7 @@ stab_demangle_v3_arg (void *dhandle, struct stab_handle *info, /* We print this component in order to find out the type name. FIXME: Should we instead expose the demangle_builtin_type_info structure? */ - p = cplus_demangle_print (DMGL_PARAMS | DMGL_ANSI, dc, 20, &alc); + p = cplus_demangle_print (DMGL_PARAMS | demangle_flags, dc, 20, &alc); if (p == NULL) { fprintf (stderr, _("Couldn't get demangled builtin type\n")); diff --git a/binutils/testsuite/config/default.exp b/binutils/testsuite/config/default.exp index b34e45cd20..9ecfcf3e15 100644 --- a/binutils/testsuite/config/default.exp +++ b/binutils/testsuite/config/default.exp @@ -93,6 +93,12 @@ if ![info exists WINDRES] then { if ![info exists DLLTOOL] then { set DLLTOOL [findfile $base_dir/dlltool] } +if ![info exists CXXFILT] then { + set CXXFILT [findfile $base_dir/cxxfilt] +} +if ![info exists CXXFILTFLAGS] then { + set CXXFILTFLAGS "" +} if ![file isdirectory tmpdir] {catch "exec mkdir tmpdir" status} --- /dev/null 2018-11-29 08:15:54.667999248 +0000 +++ binutils/testsuite/binutils-all/cxxfilt.exp 2018-11-29 13:09:29.789147447 +0000 @@ -0,0 +1,44 @@ +# Copyright (C) 2018 Free Software Foundation, Inc. + +# 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 of the License, 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, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + +proc test_cxxfilt {options mangled_string demangled_string} { + global CXXFILT + global CXXFILTFLAGS + + set testname "cxxfilt: demangling $mangled_string" + set got [binutils_run $CXXFILT "$options $CXXFILTFLAGS $mangled_string"] + + if ![regexp $demangled_string $got] then { + fail "$testname" + verbose 0 "expected: $demangled_string" + return + } + + pass $testname +} + +# Mangled and demangled strings stolen from libiberty/testsuite/demangle-expected. +test_cxxfilt {} \ + "AddAlignment__9ivTSolverUiP12ivInteractorP7ivTGlue" \ + "ivTSolver::AddAlignment(unsigned int, ivInteractor ., ivTGlue .)*" + +test_cxxfilt {--format=lucid} \ + "__ct__12strstreambufFPFl_PvPFPv_v" \ + "strstreambuf..strstreambuf(void .(.)(long), void (.)(void .))*" + +test_cxxfilt {--recurse-limit=2} \ + "Z3fooiPiPS_PS0_PS1_PS2_PS3_PS4_PS5_PS6_PS7_PS8_PS9_PSA_PSB_PSC_" \ + "foo(int, int., int.., int..., int...., int....., int......, int......., int........, int........., int.........., int..........., int............, int............., int.............., int...............)*"