Gnu Demangler - Added support for demangler simplifications

This commit is contained in:
dragonmacher 2024-08-05 17:02:22 -04:00
parent 2760eebc92
commit 9018ece7c0
10 changed files with 489 additions and 48 deletions

View File

@ -428,6 +428,19 @@
Only demangle symbols that follow known compiler mangling patterns.
Leaving this option off may cause non-mangled symbols to get demangled.
</TD>
</TR><TR>
<TD>Use Standard Text Replacements
</TD>
<TD>
Use text simplifications in demangled output, for example to use standard c++
typedefs. Using this option will produce shorter demangled output in some cases.
<P>
The simplifications are defined in
<CODE>Ghidra/Features/GnuDemangler/data/default.gnu.demangler.replacements.txt</CODE>
This file can be edited. Also new files can be added with more simplifications.
Each new file must end with <CODE>.gnu.demangler.replacements.txt</CODE> and must
reside inside of a module's <CODE>data</CODE> directory.
</TD>
</TR>
</TABLE>
</P>

View File

@ -1,3 +1,4 @@
##VERSION: 2.0
Module.manifest||GHIDRA||||END|
data/default.gnu.demangler.replacements.txt||GHIDRA||||END|
src/test/resources/ghidra/app/util/demangler/gnu_mangled_names.txt||GHIDRA||reviewed||END|

View File

@ -0,0 +1,205 @@
//
// This file contains a list of search and replace text pairs.
// Each line is one specialaization name to full library definition.
//
// This list is based on content found in:
// https://en.cppreference.com/w/cpp/string
// https://en.cppreference.com/w/cpp/io
// https://en.cppreference.com/w/cpp/regex/basic_regex
//
// The list has been tested against a small selection of known samples, but since not all of these entries have been tested, they have been built based on observed patterns and are quite speculative.
//
// Users can edit this file or add content to new files in an installation.
// New files must end with '.gnu.demangler.replacements.txt' and must be placed inside of a module's 'data' directory.
// When each replacement file is loaded, the will be sorted by name.
// All replacements will be processed in the order the files are loaded, in the order listed in each file.
//
// The replacements will be executed in the order they are listed in this file.
//
// Each line must be of the format: replacement text | whitespace | search text
// The replacement text may be two literal double quote characters (""), which signals to replace the match with the empty string.
// Any line can be disabled by deleting or prepending a double forward slash.
//
//
// General removal statements
//
// current tests show this is a non-leading mangled namespace placeholder
"" ::__1
// current tests show this is a non-leading mangled namespace placeholder
"" ::__cxx11
// current tests show this is only in the leading mangled namespace position
"" __gnu_cxx::
// speculative: pool_allocator has been spotted in some binaries; replace first so the cases below will match
std::allocator<char> pool_allocator<char>
//
// Standard c++ library typedefs
//
std::string std::basic_string<char, std::char_traits<char>, std::allocator<char> >
std::wstring std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >
std::u8string std::basic_string<char8_t, std::char_traits<char8_t>, std::allocator<char8_t> >
std::u16string std::basic_string<char16_t, std::char_traits<char16_t>, std::allocator<char16_t> >
std::u32string std::basic_string<char32_t, std::char_traits<char32_t>, std::allocator<char32_t> >
std::string_view std::basic_string_view<char, std::char_traits<char>, std::allocator<char> >
std::wstring_view std::basic_string_view<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >
std::u8string_view std::basic_string<char8_t_view, std::char_traits<char8_t_view>, std::allocator<char8_t_view> >
std::u16string_view std::basic_string<char16_t_view, std::char_traits<char16_t_view>, std::allocator<char16_t_view> >
std::u32string_view std::basic_string<char32_t_view, std::char_traits<char32_t_view>, std::allocator<char32_t_view> >
std::stringstream std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >
std::wstringstream std::basic_stringstream<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >
std::istringstream std::basic_istringstream<char, std::char_traits<char>, std::allocator<char> >
std::wistringstream std::basic_istringstream<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >
std::ostringstream std::basic_ostringstream<char, std::char_traits<char>, std::allocator<char> >
std::wostringstream std::basic_ostringstream<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >
std::spanbuf std::basic_spanbuf<char, std::char_traits<char> >
std::wspanbuf std::basic_spanbuf<wchar_t, std::char_traits<wchar_t> >
std::ispanstream std::basic_ispanstream<char, std::char_traits<char> >
std::wispanstream std::basic_ispanstream<wchar_t, std::char_traits<wchar_t> >
std::spanstream std::basic_spanstream<char, std::char_traits<char> >
std::wspanstream std::basic_spanstream<wchar_t, std::char_traits<wchar_t> >
std::syncbuf std::basic_syncbuf<char, std::char_traits<char> >
std::wsyncbuf std::basic_syncbuf<wchar_t, std::char_traits<wchar_t> >
std::osyncstream std::basic_osyncstream<char, std::char_traits<char> >
std::wosyncstream std::basic_osyncstream<wchar_t, std::char_traits<wchar_t> >
std::stringbuf std::basic_stringbuf<char, std::char_traits<char>, std::allocator<char> >
std::wstringbuf std::basic_stringbuf<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >
std::ios std::basic_ios<char, std::char_traits<char> >
std::wios std::basic_ios<wchar_t, std::char_traits<wchar_t> >
std::istream std::basic_istream<char, std::char_traits<char> >
std::wistream std::basic_istream<wchar_t, std::char_traits<wchar_t> >
std::ostream std::basic_ostream<char, std::char_traits<char> >
std::wostream std::basic_ostream<wchar_t, std::char_traits<wchar_t> >
std::iostream std::basic_iostream<char, std::char_traits<char> >
std::wiostream std::basic_iostream<wchar_t, std::char_traits<wchar_t> >
std::fstream std::basic_fstream<char, std::char_traits<char> >
std::wfstream std::basic_fstream<wchar_t, std::char_traits<wchar_t> >
std::ifstream std::basic_ifstream<char, std::char_traits<char> >
std::wifstream std::basic_ifstream<wchar_t, std::char_traits<wchar_t> >
std::ofstream std::basic_ofstream<char, std::char_traits<char> >
std::wofstream std::basic_ofstream<wchar_t, std::char_traits<wchar_t> >
std::streambuf std::basic_streambuf<char, std::char_traits<char> >
std::wstreambuf std::basic_streambuf<wchar_t, std::char_traits<wchar_t> >
std::filebuf std::basic_filebuf<char, std::char_traits<char> >
std::wfilebuf std::basic_filebuf<wchar_t, std::char_traits<wchar_t> >
std::regex std::basic_regex<char, std::regex_traits<char> >
std::wregex std::basic_regex<wchar_t, std::regex_traits<wchar_t> >
//
// Secondary pass conversions
//
// These run after previous conversions so that we can perform further replacements
//
std::string::string std::string::basic_string
std::string::~string std::string::~basic_string
std::wstring::wstring std::wstring::basic_string
std::wstring::~wstring std::wstring::~basic_string
std::u8string::u8string std::u8string::basic_string
std::u8string::~u8string std::u8string::~basic_string
std::u16string::u16string std::u16string::basic_string
std::u16string::~u16string std::u16string::~basic_string
std::u32string::u32string std::u32string::basic_string
std::u32string::~u32string std::u32string::~basic_string
std::string_view::string_view std::string_view::basic_string_view
std::string_view::~string_view std::string_view::~basic_string_view
std::wstring_view::wstring_view std::wstring_view::basic_string_view
std::wstring_view::~wstring_view std::wstring_view::~basic_string_view
std::u8string_view::u8string_view std::u8string_view::basic_string
std::u8string_view::~u8string_view std::u8string_view::~basic_string
std::u16string_view::u16string_view std::u16string_view::basic_string
std::u16string_view::~u16string_view std::u16string_view::~basic_string
std::u32string_view::u32string_view std::u32string_view::basic_string
std::u32string_view::~u32string_view std::u32string_view::~basic_string
std::stringstream::stringstream std::stringstream::basic_stringstream
std::stringstream::~stringstream std::stringstream::~basic_stringstream
std::wstringstream::wstringstream std::wstringstream::basic_stringstream
std::wstringstream::~wstringstream std::wstringstream::~basic_stringstream
std::istringstream::istringstream std::istringstream::basic_istringstream
std::istringstream::~istringstream std::istringstream::~basic_istringstream
td::wistringstream::wistringstream std::wistringstream::basic_istringstream
std::wistringstream::~wistringstream std::wistringstream::~basic_istringstream
std::ostringstream::ostringstream std::ostringstream::basic_ostringstream
std::ostringstream::~ostringstream std::ostringstream::~basic_ostringstream
std::wostringstream::wostringstream std::wostringstream::basic_ostringstream
std::wostringstream::~wostringstream std::wostringstream::~basic_ostringstream
std::stringbuf::stringbuf std::stringbuf::basic_stringbuf
std::stringbuf::~stringbuf std::stringbuf::~basic_stringbuf
std::wstringbuf::wstringbuf std::wstringbuf::basic_stringbuf
std::wstringbuf::~wstringbuf std::wstringbuf::~basic_stringbuf
std::ios::ios std::ios::basic_ios
std::ios::~ios std::ios::~basic_ios
std::wios::wios std::wios::basic_ios
std::wios::~wios std::wios::~basic_ios
std::istream::istream std::istream::basic_istream
std::istream::~istream std::istream::~basic_istream
std::wistream::wistream std::wistream::basic_istream
std::wistream::~wistream std::wistream::~basic_istream
std::ostream::ostream std::ostream::basic_ostream
std::ostream::~ostream std::ostream::~basic_ostream
std::wostream::wostream std::wostream::basic_ostream
std::wostream::~wostream std::wostream::~basic_ostream
std::iostream::iostream std::iostream::basic_iostream
std::iostream::~iostream std::iostream::~basic_iostream
std::wiostream::wiostream std::wiostream::basic_iostream
std::wiostream::~wiostream std::wiostream::~basic_iostream
std::fstream::fstream std::fstream::basic_fstream
std::fstream::~fstream std::fstream::~basic_fstream
std::wfstream::wfstream std::wfstream::basic_fstream
std::wfstream::~wfstream std::wfstream::~basic_fstream
std::ifstream::ifstream std::ifstream::basic_ifstream
std::ifstream::~ifstream std::ifstream::~basic_ifstream
std::wifstream::wifstream std::wifstream::basic_ifstream
std::wifstream::~wifstream std::wifstream::~basic_ifstream
std::ofstream::ofstream std::ofstream::basic_ofstream
std::ofstream::~ofstream std::ofstream::~basic_ofstream
std::wofstream::wofstream std::wofstream::basic_ofstream
std::wofstream::~wofstream std::wofstream::~basic_ofstream
std::streambuf::streambuf std::streambuf::basic_streambuf
std::streambuf::~streambuf std::streambuf::~basic_streambuf
std::wstreambuf::wstreambuf std::wstreambuf::basic_streambuf
std::wstreambuf::~wstreambuf std::wstreambuf::~basic_streambuf
std::filebuf::filebuf std::filebuf::basic_filebuf
std::filebuf::~filebuf std::filebuf::~basic_filebuf
std::wfilebuf::wfilebuf std::wfilebuf::basic_filebuf
std::wfilebuf::~wfilebuf std::wfilebuf::~basic_filebuf
std::spanbuf::spanbuf std::spanbuf::basic_spanbuf
std::spanbuf::~spanbuf std::spanbuf::~basic_spanbuf
std::wspanbuf::wspanbuf std::wspanbuf::basic_spanbuf
std::wspanbuf::~wspanbuf std::wspanbuf::~basic_spanbuf
std::ispanstream::ispanstream std::ispanstream::basic_ispanstream
std::ispanstream::~ispanstream std::ispanstream::~basic_ispanstream
std::wispanstream::wispanstream std::wispanstream::basic_ispanstream
std::wispanstream::~wispanstream std::wispanstream::~basic_ispanstream
std::spanstream::spanstream std::spanstream::basic_spanstream
std::spanstream::~spanstream std::spanstream::~basic_spanstream
std::wspanstream::wspanstream std::wspanstream::basic_spanstream
std::wspanstream::~wspanstream std::wspanstream::~basic_spanstream
std::syncbuf::syncbuf std::syncbuf::basic_syncbuf
std::syncbuf::~syncbuf std::syncbuf::~basic_syncbuf
std::wsyncbuf::wsyncbuf std::wsyncbuf::basic_syncbuf
std::wsyncbuf::~wsyncbuf std::wsyncbuf::~basic_syncbuf
std::osyncstream::osyncstream std::osyncstream::basic_osyncstream
std::osyncstream::~osyncstream std::osyncstream::~basic_osyncstream
std::wosyncstream::wosyncstream std::wosyncstream::basic_osyncstream
std::wosyncstream::~wosyncstream std::wosyncstream::~basic_osyncstream
// speculative:
std::string std::basic_string<std::remove_const<char const>::type, std::char_traits<std::remove_const<char const>::type>, std::allocator<std::remove_const<char const>::type> >

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -43,6 +43,11 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
"Only demangle symbols that follow known compiler mangling patterns. " +
"Leaving this option off may cause non-mangled symbols to get demangled.";
private static final String OPTION_NAME_DEMANGLE_USE_STANDARD_REPLACEMENTS =
"Use Standard Text Replacements";
private static final String OPTION_DESCRIPTION_STANDARD_REPLACEMENTS =
"Use text simplifications in demangled output, for example to use standard c++ typedefs.";
private static final String OPTION_NAME_APPLY_SIGNATURE = "Apply Function Signatures";
private static final String OPTION_DESCRIPTION_APPLY_SIGNATURE =
"Apply any recovered function signature, in addition to the function name";
@ -64,6 +69,7 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
private boolean applyFunctionSignature = true;
private boolean applyCallingConvention = true;
private boolean demangleOnlyKnownPatterns = false;
private boolean useStandardReplacements = true;
private GnuDemanglerFormat demanglerFormat = GnuDemanglerFormat.AUTO;
private boolean useDeprecatedDemangler = false;
@ -92,6 +98,9 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
options.registerOption(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns,
help, OPTION_DESCRIPTION_USE_KNOWN_PATTERNS);
options.registerOption(OPTION_NAME_DEMANGLE_USE_STANDARD_REPLACEMENTS,
useStandardReplacements, help, OPTION_DESCRIPTION_STANDARD_REPLACEMENTS);
GnuOptionsEditor optionsEditor = new GnuOptionsEditor();
options.registerOption(OPTION_NAME_USE_DEPRECATED_DEMANGLER, OptionType.BOOLEAN_TYPE,
@ -112,6 +121,8 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
options.getBoolean(OPTION_NAME_APPLY_CALLING_CONVENTION, applyCallingConvention);
demangleOnlyKnownPatterns =
options.getBoolean(OPTION_NAME_DEMANGLE_USE_KNOWN_PATTERNS, demangleOnlyKnownPatterns);
useStandardReplacements =
options.getBoolean(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useStandardReplacements);
demanglerFormat = options.getEnum(OPTION_NAME_DEMANGLER_FORMAT, GnuDemanglerFormat.AUTO);
useDeprecatedDemangler =
options.getBoolean(OPTION_NAME_USE_DEPRECATED_DEMANGLER, useDeprecatedDemangler);
@ -125,6 +136,7 @@ public class GnuDemanglerAnalyzer extends AbstractDemanglerAnalyzer {
options.setApplySignature(applyFunctionSignature);
options.setApplyCallingConvention(applyCallingConvention);
options.setDemangleOnlyKnownPatterns(demangleOnlyKnownPatterns);
options.setUseStandardReplacements(useStandardReplacements);
return options;
}

View File

@ -115,9 +115,8 @@ public class GnuDemangler implements Demangler {
throw new DemangledException(true);
}
boolean onlyKnownPatterns = options.demangleOnlyKnownPatterns();
DemangledObject demangledObject =
parse(originalMangled, process, demangled, onlyKnownPatterns);
parse(originalMangled, process, demangled, options);
if (demangledObject == null) {
return demangledObject;
}
@ -226,14 +225,16 @@ public class GnuDemangler implements Demangler {
}
private DemangledObject parse(String mangled, GnuDemanglerNativeProcess process,
String demangled, boolean demangleOnlyKnownPatterns) {
String demangled, GnuDemanglerOptions options) {
if (demangleOnlyKnownPatterns && !isKnownMangledString(mangled, demangled)) {
boolean onlyKnownPatterns = options.demangleOnlyKnownPatterns();
if (onlyKnownPatterns && !isKnownMangledString(mangled, demangled)) {
return null;
}
boolean replaceStdTypedefs = options.shouldUseStandardReplacements();
GnuDemanglerParser parser = new GnuDemanglerParser();
DemangledObject demangledObject = parser.parse(mangled, demangled);
DemangledObject demangledObject = parser.parse(mangled, demangled, replaceStdTypedefs);
return demangledObject;
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -46,6 +46,7 @@ public class GnuDemanglerOptions extends DemanglerOptions {
private final GnuDemanglerFormat format;
private final boolean isDeprecated;
private boolean useStandardReplacements;
/**
* Default constructor to use the modern demangler with auto-detect for the format. This
@ -76,6 +77,7 @@ public class GnuDemanglerOptions extends DemanglerOptions {
public GnuDemanglerOptions(GnuDemanglerFormat format, boolean isDeprecated) {
this.format = format;
this.isDeprecated = isDeprecated;
this.useStandardReplacements = true;
if (!format.isAvailable(isDeprecated)) {
throw new IllegalArgumentException(
format.name() + " is not available in the " + getDemanglerName());
@ -98,13 +100,29 @@ public class GnuDemanglerOptions extends DemanglerOptions {
format = GnuDemanglerFormat.AUTO;
isDeprecated = false;
}
this.useStandardReplacements = true;
}
private GnuDemanglerOptions(GnuDemanglerOptions copy, GnuDemanglerFormat format,
boolean deprecated) {
this(copy, format, deprecated, true);
}
private GnuDemanglerOptions(GnuDemanglerOptions copy, GnuDemanglerFormat format,
boolean deprecated, boolean useStandardReplacements) {
super(copy);
this.format = format;
this.isDeprecated = deprecated;
this.useStandardReplacements = useStandardReplacements;
}
/**
* Changes this options value of {@link #shouldUseStandardReplacements()}
* @param replace true to replace
*/
public void setUseStandardReplacements(boolean replace) {
this.useStandardReplacements = replace;
}
/**
@ -158,12 +176,23 @@ public class GnuDemanglerOptions extends DemanglerOptions {
return format;
}
/**
* Returns whether the gnu demangler parser should replace demangler output with standard text
* replacements defined in {@code GnuDemangler/data/default.gnu.demangler.replacements.txt}.
*
* @return true to use replacements; false to not replace text
*/
public boolean shouldUseStandardReplacements() {
return useStandardReplacements;
}
@Override
public String toString() {
//@formatter:off
return "{\n" +
"\tdoDisassembly: " + doDisassembly() + ",\n" +
"\tapplySignature: " + applySignature() + ",\n" +
"\tuseStandardReplacements: " + useStandardReplacements + ",\n" +
"\tdemangleOnlyKnownPatterns: " + demangleOnlyKnownPatterns() + ",\n" +
"\tdemanglerName: " + getDemanglerName() + ",\n" +
"\tdemanglerApplicationArguments: " + getDemanglerApplicationArguments() + ",\n" +

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -15,6 +15,7 @@
*/
package ghidra.app.util.demangler.gnu;
import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -25,16 +26,33 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import generic.jar.ResourceFile;
import generic.json.Json;
import ghidra.app.util.SymbolPathParser;
import ghidra.app.util.demangler.*;
import ghidra.framework.Application;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.symbol.Namespace;
import ghidra.util.Msg;
import ghidra.util.StringUtilities;
import utilities.util.FileUtilities;
/**
* Parses output generated by the gnu demangler. This class will create a {@link DemangledObject}
* if it can successfully parse the given input. By default, this class will apply simplifications
* to the input, for example, to use simplified typedefs provided by the c++ language. To avoid
* simplifications, use {@link #parse(String, String, boolean)}.
* <p>
* The simplification process is based upon predefined text patterns that this class will search for
* and replace with alternate text. New simplifications can be added by providing a module data file
* that ends with {@value #REPLACEMENT_FILE_EXTENSION}. The file is parsed expected each line to
* contain a replacement string consisting of the replacement and the search value, separated by
* whitespace.
*/
public class GnuDemanglerParser {
private static final String REPLACEMENT_FILE_EXTENSION = ".gnu.demangler.replacements.txt";
private static final String CONSTRUCTION_VTABLE_FOR = "construction vtable for ";
private static final String VTT_FOR = "VTT for ";
private static final String VTABLE_FOR = "vtable for ";
@ -347,6 +365,8 @@ public class GnuDemanglerParser {
private static final Pattern RUST_LEGACY_SUFFIX_PATTERN =
Pattern.compile("(.*)::h[0-9a-f]{16}");
private static final List<GnuDemanglerReplacement> REPLACEMENTS = loadStandardReplacements();
private String mangledSource;
private String demangledSource;
@ -357,11 +377,32 @@ public class GnuDemanglerParser {
* @param demangled the demangled text
* @return the demangled object
* @throws DemanglerParseException if there is an unexpected error parsing
* @see #parse(String, String, boolean)
*/
public DemangledObject parse(String mangled, String demangled) throws DemanglerParseException {
return parse(mangled, demangled, true);
}
/**
* Parses the given demangled string and creates a {@link DemangledObject}
*
* @param mangled the original mangled text
* @param demangled the demangled text
* @param simplify true signals to simplify types where possible, such as using simple c++
* typedefs where possible.
* @return the demangled object
* @throws DemanglerParseException if there is an unexpected error parsing
* @see #parse(String, String, boolean)
*/
public DemangledObject parse(String mangled, String demangled, boolean simplify)
throws DemanglerParseException {
demangled = cleanupRustLegacySymbol(demangled);
if (simplify) {
demangled = replaceStdLibraryTypes(demangled);
}
this.mangledSource = mangled;
this.demangledSource = demangled;
@ -507,6 +548,14 @@ public class GnuDemanglerParser {
return demangled;
}
private String replaceStdLibraryTypes(String demangled) {
String d = demangled;
for (GnuDemanglerReplacement replacement : REPLACEMENTS) {
d = StringUtils.replace(d, replacement.find(), replacement.replace());
}
return d;
}
private void setReturnType(String demangled, DemangledFunction function, String returnType) {
String updatedReturnType = returnType;
@ -1462,6 +1511,66 @@ public class GnuDemanglerParser {
return myNamespace;
}
private static List<GnuDemanglerReplacement> loadStandardReplacements() {
List<GnuDemanglerReplacement> results = new ArrayList<>();
List<ResourceFile> files =
Application.findFilesByExtensionInApplication(REPLACEMENT_FILE_EXTENSION);
// sort files by name to provide a consistent way for prioritizing replacements
files.sort((r1, r2) -> r1.getName().compareTo(r2.getName()));
for (ResourceFile file : files) {
List<String> lines = getReplacementLines(file);
for (String line : lines) {
GnuDemanglerReplacement replacement = parseReplacement(line, file);
if (replacement != null) {
results.add(replacement);
}
}
}
return results;
}
private static GnuDemanglerReplacement parseReplacement(String line, ResourceFile file) {
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (Character.isWhitespace(c)) {
String replace = line.substring(0, i).trim();
// special case
if (replace.equals("\"\"")) {
replace = StringUtils.EMPTY;
}
String find = line.substring(i).trim();
return new GnuDemanglerReplacement(find, replace, file);
}
}
Msg.warn(GnuDemanglerParser.class,
"Malformed replacement line. No spaces found: " + line + ". In file: " + file);
return null;
}
private static List<String> getReplacementLines(ResourceFile file) {
try {
List<String> lines = FileUtilities.getLines(file);
return lines.stream()
.filter(l -> !l.startsWith("//"))
.filter(l -> !l.isBlank())
.map(String::trim)
.collect(Collectors.toList());
}
catch (IOException e) {
Msg.error(GnuDemanglerParser.class,
"Unable to load gnu demangler replacement file: " + file, e);
return Collections.emptyList();
}
}
//==================================================================================================
// Inner Classes
//==================================================================================================

View File

@ -0,0 +1,39 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.demangler.gnu;
import java.util.Objects;
import generic.jar.ResourceFile;
/**
* A simple object that is used to find and replace content within Gnu demangled strings.
* @param find the string to search for; cannot be null
* @param replace the replacement string; cannot be null
* @param source the file from whence the replacement came; may be null
*/
public record GnuDemanglerReplacement(String find, String replace, ResourceFile source) {
public GnuDemanglerReplacement {
Objects.requireNonNull(find, "'find' cannot be null");
Objects.requireNonNull(replace, "'replace' cannot be null");
}
@Override
public String toString() {
return replace + "\t\t" + find;
}
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -197,15 +197,15 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
List<DemangledParameter> parameters = function.getParameters();
assertEquals(
"__insertion_sort<__gnu_cxx::__normal_iterator<std::pair<unsigned_long,PcodeOp*>*,std::vector<std::pair<unsigned_long,PcodeOp*>,std::allocator<std::pair<unsigned_long,PcodeOp*>>>>,bool(*)(std::pair<unsigned_long,PcodeOp*>const&,std::pair<unsigned_long,PcodeOp*>const&)>",
"__insertion_sort<__normal_iterator<std::pair<unsigned_long,PcodeOp*>*,std::vector<std::pair<unsigned_long,PcodeOp*>,std::allocator<std::pair<unsigned_long,PcodeOp*>>>>,bool(*)(std::pair<unsigned_long,PcodeOp*>const&,std::pair<unsigned_long,PcodeOp*>const&)>",
function.getName());
assertEquals("std", function.getNamespace().getName());
assertEquals(
"__gnu_cxx::__normal_iterator<std::pair<unsigned long,PcodeOp *> *,std::vector<std::pair<unsigned long,PcodeOp *>,std::allocator<std::pair<unsigned long,PcodeOp *>>>>",
"__normal_iterator<std::pair<unsigned long,PcodeOp *> *,std::vector<std::pair<unsigned long,PcodeOp *>,std::allocator<std::pair<unsigned long,PcodeOp *>>>>",
parameters.get(0).toString());
assertEquals(
"__gnu_cxx::__normal_iterator<std::pair<unsigned long,PcodeOp *> *,std::vector<std::pair<unsigned long,PcodeOp *>,std::allocator<std::pair<unsigned long,PcodeOp *>>>>",
"__normal_iterator<std::pair<unsigned long,PcodeOp *> *,std::vector<std::pair<unsigned long,PcodeOp *>,std::allocator<std::pair<unsigned long,PcodeOp *>>>>",
parameters.get(1).toString());
assertEquals(
"bool (*)(std::pair<unsigned long,PcodeOp *> const &,std::pair<unsigned long,PcodeOp *> const &)",
@ -283,11 +283,10 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
DemangledObject object = parser.parse(mangled, demangled);
assertType(object, DemangledThunk.class);
assertName(object, "~basic_ostringstream", "std",
"basic_ostringstream<char,std::char_traits<char>,pool_allocator<char>>");
assertName(object, "~ostringstream", "std", "ostringstream");
assertEquals(
"virtual thunk to undefined __thiscall std::basic_ostringstream<char,std::char_traits<char>,pool_allocator<char>>::~basic_ostringstream(void)",
"virtual thunk to undefined __thiscall std::ostringstream::~ostringstream(void)",
object.getSignature(false));
}
@ -915,8 +914,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertName(object, "Image", "Magick", "Image");
assertEquals(
"undefined Magick::Image::Image(" +
"std::basic_string<char,std::char_traits<char>,std::allocator<char>> const &)",
"undefined Magick::Image::Image(std::string const &)",
object.getSignature(false));
}
@ -977,9 +975,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String name = object.getName();
assertEquals("operator>>", name);
assertEquals(
"std::basic_istream<char,std::char_traits<char>> & " +
"std::operator>><char,std::char_traits<char>>" +
"(std::basic_istream<char,std::char_traits<char>> &,char &)",
"std::istream & std::operator>><char,std::char_traits<char>>(std::istream &,char &)",
object.getSignature());
}
@ -989,9 +985,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String raw = "std::basic_ostream<char, std::char_traits<char> >& " +
"std::operator<< <std::char_traits<char> >" +
"(std::basic_ostream<char, std::char_traits<char> >&, char const*)";
String formatted = "std::basic_ostream<char,std::char_traits<char>> & " +
"std::operator<<<std::char_traits<char>>" +
"(std::basic_ostream<char,std::char_traits<char>> &,char const *)";
String formatted =
"std::ostream & std::operator<<<std::char_traits<char>>(std::ostream &,char const *)";
DemangledObject object =
parser.parse("_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc", raw);
String name = object.getName();
@ -1010,9 +1005,8 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
"(*)(std::basic_ostream<char, std::char_traits<char> >&))");
String name = object.getName();
assertEquals("operator<<", name);
assertName(object, "operator<<", "std", "basic_ostream<char,std::char_traits<char>>");
assertEquals("undefined std::basic_ostream<char,std::char_traits<char>>" + "::operator<<(" +
"std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))",
assertName(object, "operator<<", "std", "ostream");
assertEquals("undefined std::ostream::operator<<(std::ostream & (*)(std::ostream &))",
object.getSignature());
}
@ -1118,7 +1112,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String signature = object.getSignature(false);
assertEquals(
"std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>> std::_Bind<std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>(EduAppConfigs::*(EduAppConfigs_const*))()const>::operator()<missing_argument,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>>(void)",
"std::string std::_Bind<std::string(EduAppConfigs::*(EduAppConfigs_const*))()const>::operator()<missing_argument,std::string>(void)",
signature);
}
@ -1151,7 +1145,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String signature = object.getSignature(false);
assertEquals(
"std::__cxx11::basic_string<std::remove_const<char_const>::type,std::char_traits<std::remove_const<char_const>::type>,std::allocator<std::remove_const<char_const>::type>> gsl::to_string<char_const,-1l>(gsl::basic_string_span<char const,long>)",
"std::string gsl::to_string<char_const,-1l>(gsl::basic_string_span<char const,long>)",
signature);
}
@ -1289,10 +1283,10 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
DemangledObject object = parser.parse(mangled, demangled);
assertType(object, DemangledFunction.class);
assertName(object, "operator.cast.to.basic_string", "Magick", "Color");
assertName(object, "operator.cast.to.string", "Magick", "Color");
assertEquals("std::basic_string<char,std::char_traits<char>,std::allocator<char>> " +
"Magick::Color::operator.cast.to.basic_string(void)", object.getSignature(false));
assertEquals("std::string Magick::Color::operator.cast.to.string(void)",
object.getSignature(false));
}
@Test
@ -1636,7 +1630,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
"vector<boost::function<void()>,std::allocator<boost::function<void()>>>");
assertEquals(
"undefined std::vector<boost::function<void()>,std::allocator<boost::function<void()>>>::_M_insert_aux(__gnu_cxx::__normal_iterator<boost::function<void ()> *,std::vector<boost::function<void ()>,std::allocator<boost::function<void ()>>>>,boost::function<void ()> const &)",
"undefined std::vector<boost::function<void()>,std::allocator<boost::function<void()>>>::_M_insert_aux(__normal_iterator<boost::function<void ()> *,std::vector<boost::function<void ()>,std::allocator<boost::function<void ()>>>>,boost::function<void ()> const &)",
object.getSignature(false));
}
@ -1665,7 +1659,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String signature = object.getSignature(false);
assertEquals(
"undefined WebCore::ContentFilterUnblockHandler::ContentFilterUnblockHandler(WTF::String,std::__1::function<void (std::__1::function<void (bool)>)>)",
"undefined WebCore::ContentFilterUnblockHandler::ContentFilterUnblockHandler(WTF::String,std::function<void (std::function<void (bool)>)>)",
signature);
}
@ -1775,9 +1769,9 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertType(object, DemangledFunction.class);
String signature = object.getSignature(false);
assertEquals("undefined __gnu_cxx" + "::" +
assertEquals("undefined " +
"__stoa<long,int,char,int>(long(*)(char_const*,char**,int),char_const*,char_const*,unsigned_long*,int)" +
"::" + "_Save_errno::_Save_errno(void)", signature);
"::_Save_errno::_Save_errno(void)", signature);
}
@Test
@ -1814,12 +1808,12 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertType(object, DemangledFunction.class);
String name =
"for_each_args<WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::__1::integral_constant<long,0l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,1l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,2l>>>";
"for_each_args<WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::integral_constant<long,0l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std<long,1l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std<long,2l>>>";
assertName(object, name, "brigand");
String signature = object.getSignature(false);
assertEquals(
"WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1} brigand::for_each_args<WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::__1::integral_constant<long,0l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,1l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,2l>>>(WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::__1::integral_constant<long,long>> &&,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,long>> &&,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std::__1<long,long>> &&)",
"WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1} brigand::for_each_args<WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::integral_constant<long,0l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std<long,1l>>,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std<long,2l>>>(WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1},brigand::type_<std::integral_constant<long,long>> &&,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std<long,long>> &&,WebCore::JSConverter<WebCore::IDLUnion<WebCore::IDLNull,WebCore::IDLDOMString,WebCore::IDLUnrestrictedDouble>>::convert(JSC::ExecState&,WebCore::JSDOMGlobalObject&,WTF::Variant<decltype(nullptr),WTF::String,double>const&)::{lambda(auto:1&&)#1}<std<long,long>> &&)",
signature);
}
@ -1928,7 +1922,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
String signature = object.getSignature(false);
assertEquals(
"undefined WebCore::TextCodecICU::registerCodecs(void (*)(char const *,WTF::Function<std::__1::unique_ptr<WebCore::TextCodec,std::__1::default_delete<WebCore::TextCodec>> ()> &&))",
"undefined WebCore::TextCodecICU::registerCodecs(void (*)(char const *,WTF::Function<std::unique_ptr<WebCore::TextCodec,std::default_delete<WebCore::TextCodec>> ()> &&))",
signature);
}
@ -2215,13 +2209,12 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
assertType(object, DemangledFunction.class);
assertName(object,
"__allocate_at_least[abi:v160006]<std::__1::allocator<std::__1::unique_ptr<void,applesauce::raii::v1::detail::opaque_deletion_functor<void*,&VPTimeFreqConverter_Dispose>>>>",
"std",
"__1");
"__allocate_at_least[abi:v160006]<std::allocator<std::unique_ptr<void,applesauce::raii::v1::detail::opaque_deletion_functor<void*,&VPTimeFreqConverter_Dispose>>>>",
"std");
String signature = object.getSignature(false);
assertEquals(
"std::__1::__allocation_result<std::__1::allocator_traits<std::__1::allocator<std::__1::unique_ptr<void,applesauce::raii::v1::detail::opaque_deletion_functor<void*,&VPTimeFreqConverter_Dispose>>>>::pointer> std::__1::__allocate_at_least[abi:v160006]<std::__1::allocator<std::__1::unique_ptr<void,applesauce::raii::v1::detail::opaque_deletion_functor<void*,&VPTimeFreqConverter_Dispose>>>>(std::__1::allocator<std::__1::unique_ptr<void,applesauce::raii::v1::detail::opaque_deletion_functor<void *,&VPTimeFreqConverter_Dispose>>> &,unsigned long)",
"std::__allocation_result<std::allocator_traits<std::allocator<std::unique_ptr<void,applesauce::raii::v1::detail::opaque_deletion_functor<void*,&VPTimeFreqConverter_Dispose>>>>::pointer> std::__allocate_at_least[abi:v160006]<std::allocator<std::unique_ptr<void,applesauce::raii::v1::detail::opaque_deletion_functor<void*,&VPTimeFreqConverter_Dispose>>>>(std::allocator<std::unique_ptr<void,applesauce::raii::v1::detail::opaque_deletion_functor<void *,&VPTimeFreqConverter_Dispose>>> &,unsigned long)",
signature);
}

View File

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -86,6 +86,45 @@ public class GnuDemanglerTest extends AbstractGenericTest {
}
}
@Test
public void testUseStandardReplacements() throws Exception {
//
// Mangled: _ZTv0_n24_NSt19basic_ostringstreamIcSt11char_traitsIcE14pool_allocatorIcEED0Ev
//
// Demangled: virtual thunk to std::basic_ostringstream<char, std::char_traits<char>, pool_allocator<char> >::~basic_ostringstream()
//
// Replaced: virtual thunk to undefined __thiscall std::ostringstream::~ostringstream(void)
//
String mangled =
"_ZTv0_n24_NSt19basic_ostringstreamIcSt11char_traitsIcE14pool_allocatorIcEED0Ev";
GnuDemangler demangler = new GnuDemangler();
demangler.canDemangle(program);// this perform initialization
GnuDemanglerOptions options = new GnuDemanglerOptions();
options.setUseStandardReplacements(true);
DemangledObject dobj = demangler.demangle(mangled, options);
assertNotNull(dobj);
String signature = dobj.getSignature();
assertEquals(
"virtual thunk to undefined __thiscall std::ostringstream::~ostringstream(void)",
signature);
//
// Now disable demangled string replacement
//
options.setUseStandardReplacements(false);
dobj = demangler.demangle(mangled, options);
assertNotNull(dobj);
String fullSignature = dobj.getSignature();
assertEquals(
"virtual thunk to undefined __thiscall std::basic_ostringstream<char,std::char_traits<char>,pool_allocator<char>>::~basic_ostringstream(void)",
fullSignature);
}
@Test
public void testDemangleOnlyKnownPatterns_True() throws Exception {