mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 14:25:32 +08:00
GT-3645 - Gnu Demangler - update parser to handle the 'unnamed type'
This commit is contained in:
+58
-65
@@ -206,6 +206,17 @@ public class GnuDemanglerParser {
|
|||||||
private static final Pattern LAMBDA_PATTERN =
|
private static final Pattern LAMBDA_PATTERN =
|
||||||
Pattern.compile(".*(\\{" + LAMBDA + "\\((.*)\\)(#\\d+)\\})");
|
Pattern.compile(".*(\\{" + LAMBDA + "\\((.*)\\)(#\\d+)\\})");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sample: {unnamed type#1}
|
||||||
|
*
|
||||||
|
* Pattern: [optional text] brace unnamed type#digits brace
|
||||||
|
*
|
||||||
|
* Parts:
|
||||||
|
* -full text without leading characters (capture group 1)
|
||||||
|
*/
|
||||||
|
private static final Pattern UNNAMED_TYPE_PATTERN =
|
||||||
|
Pattern.compile("(\\{unnamed type#\\d+})");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sample: covariant return thunk to Foo::Bar::copy(Foo::CoolStructure*) const
|
* Sample: covariant return thunk to Foo::Bar::copy(Foo::CoolStructure*) const
|
||||||
*
|
*
|
||||||
@@ -390,7 +401,7 @@ public class GnuDemanglerParser {
|
|||||||
LambdaName lambdaName = getLambdaName(demangled);
|
LambdaName lambdaName = getLambdaName(demangled);
|
||||||
if (lambdaName != null) {
|
if (lambdaName != null) {
|
||||||
String uniqueName = lambdaName.getFullText();
|
String uniqueName = lambdaName.getFullText();
|
||||||
String escapedLambda = removeInternalSpaces(uniqueName);
|
String escapedLambda = removeBadSpaces(uniqueName);
|
||||||
simpleName = simpleName.replace("{lambda", escapedLambda);
|
simpleName = simpleName.replace("{lambda", escapedLambda);
|
||||||
function = new DemangledLambda(mangledSource, demangled, null);
|
function = new DemangledLambda(mangledSource, demangled, null);
|
||||||
function.setSignature(lambdaName.getFullText());
|
function.setSignature(lambdaName.getFullText());
|
||||||
@@ -484,12 +495,16 @@ public class GnuDemanglerParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes spaces inside of templates and parentheses by either dropping the spaces or
|
* Removes spaces from unwanted places. For example, all spaces internal to templates and
|
||||||
* replacing them with an underscore when they are surrounded by word characters
|
* parameter lists will be removed. Also, other special cases may be handled, such as when
|
||||||
|
* the 'unnamed type' construct is found.
|
||||||
|
*
|
||||||
|
* @param text the text to fix
|
||||||
|
* @return the fixed text
|
||||||
*/
|
*/
|
||||||
private String removeInternalSpaces(String name) {
|
private String removeBadSpaces(String text) {
|
||||||
CondensedString cs = new CondensedString(name);
|
CondensedString condensedString = new CondensedString(text);
|
||||||
return cs.getCondensedString();
|
return condensedString.getCondensedText();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -898,7 +913,6 @@ public class GnuDemanglerParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setNameAndNamespace(DemangledObject object, String name) {
|
private void setNameAndNamespace(DemangledObject object, String name) {
|
||||||
|
|
||||||
SymbolPath path = new SymbolPath(name);
|
SymbolPath path = new SymbolPath(name);
|
||||||
List<String> names = path.asList();
|
List<String> names = path.asList();
|
||||||
|
|
||||||
@@ -1011,7 +1025,7 @@ public class GnuDemanglerParser {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
String nameString = removeInternalSpaces(demangled).trim();
|
String nameString = removeBadSpaces(demangled).trim();
|
||||||
DemangledVariable variable =
|
DemangledVariable variable =
|
||||||
new DemangledVariable(mangledSource, demangledSource, (String) null);
|
new DemangledVariable(mangledSource, demangledSource, (String) null);
|
||||||
setNameAndNamespace(variable, nameString);
|
setNameAndNamespace(variable, nameString);
|
||||||
@@ -1025,7 +1039,7 @@ public class GnuDemanglerParser {
|
|||||||
* and Namespace{C} will be returned.
|
* and Namespace{C} will be returned.
|
||||||
*
|
*
|
||||||
* <p>This method will also escape spaces separators inside of templates
|
* <p>This method will also escape spaces separators inside of templates
|
||||||
* (see {@link #removeInternalSpaces(String)}).
|
* (see {@link #removeBadSpaces(String)}).
|
||||||
*
|
*
|
||||||
* @param names the names to convert
|
* @param names the names to convert
|
||||||
* @return the newly created type
|
* @return the newly created type
|
||||||
@@ -1036,13 +1050,13 @@ public class GnuDemanglerParser {
|
|||||||
}
|
}
|
||||||
int index = names.size() - 1;
|
int index = names.size() - 1;
|
||||||
String rawName = names.get(index);
|
String rawName = names.get(index);
|
||||||
String escapedName = removeInternalSpaces(rawName);
|
String escapedName = removeBadSpaces(rawName);
|
||||||
DemangledType myNamespace = new DemangledType(mangledSource, demangledSource, escapedName);
|
DemangledType myNamespace = new DemangledType(mangledSource, demangledSource, escapedName);
|
||||||
|
|
||||||
DemangledType namespace = myNamespace;
|
DemangledType namespace = myNamespace;
|
||||||
while (--index >= 0) {
|
while (--index >= 0) {
|
||||||
rawName = names.get(index);
|
rawName = names.get(index);
|
||||||
escapedName = removeInternalSpaces(rawName);
|
escapedName = removeBadSpaces(rawName);
|
||||||
DemangledType parentNamespace =
|
DemangledType parentNamespace =
|
||||||
new DemangledType(mangledSource, demangledSource, escapedName);
|
new DemangledType(mangledSource, demangledSource, escapedName);
|
||||||
namespace.setNamespace(parentNamespace);
|
namespace.setNamespace(parentNamespace);
|
||||||
@@ -1175,7 +1189,7 @@ public class GnuDemanglerParser {
|
|||||||
new DemangledString(mangledSource, demangledSource, "typeinfo-name", type,
|
new DemangledString(mangledSource, demangledSource, "typeinfo-name", type,
|
||||||
-1/*unknown length*/, false);
|
-1/*unknown length*/, false);
|
||||||
demangledString.setSpecialPrefix("typeinfo name for ");
|
demangledString.setSpecialPrefix("typeinfo name for ");
|
||||||
String namespaceString = removeInternalSpaces(type);
|
String namespaceString = removeBadSpaces(type);
|
||||||
setNamespace(demangledString, namespaceString);
|
setNamespace(demangledString, namespaceString);
|
||||||
return demangledString;
|
return demangledString;
|
||||||
}
|
}
|
||||||
@@ -1269,8 +1283,8 @@ public class GnuDemanglerParser {
|
|||||||
function.setName(originalPrefix);
|
function.setName(originalPrefix);
|
||||||
|
|
||||||
if (!StringUtils.isBlank(templates)) {
|
if (!StringUtils.isBlank(templates)) {
|
||||||
String escapedPrefix = removeInternalSpaces(originalPrefix);
|
String escapedPrefix = removeBadSpaces(originalPrefix);
|
||||||
String escapedTemplates = removeInternalSpaces(templates);
|
String escapedTemplates = removeBadSpaces(templates);
|
||||||
int templateIndex = escapedPrefix.indexOf(escapedTemplates);
|
int templateIndex = escapedPrefix.indexOf(escapedTemplates);
|
||||||
String operatorName = escapedPrefix.substring(0, templateIndex);
|
String operatorName = escapedPrefix.substring(0, templateIndex);
|
||||||
DemangledTemplate demangledTemplate = parseTemplate(escapedTemplates);
|
DemangledTemplate demangledTemplate = parseTemplate(escapedTemplates);
|
||||||
@@ -1516,10 +1530,7 @@ public class GnuDemanglerParser {
|
|||||||
|
|
||||||
private boolean isFunction;
|
private boolean isFunction;
|
||||||
|
|
||||||
private String rawReturnType;
|
|
||||||
private String returnType;
|
private String returnType;
|
||||||
|
|
||||||
private String rawName;
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
private List<DemangledDataType> parameters;
|
private List<DemangledDataType> parameters;
|
||||||
@@ -1543,16 +1554,13 @@ public class GnuDemanglerParser {
|
|||||||
String rawPrefix = signatureString.substring(0, prefixEndPos).trim();
|
String rawPrefix = signatureString.substring(0, prefixEndPos).trim();
|
||||||
|
|
||||||
CondensedString prefixString = new CondensedString(rawPrefix);
|
CondensedString prefixString = new CondensedString(rawPrefix);
|
||||||
String prefix = prefixString.getCondensedString();
|
String prefix = prefixString.getCondensedText();
|
||||||
int nameStart = Math.max(0, prefix.lastIndexOf(' '));
|
int nameStart = Math.max(0, prefix.lastIndexOf(' '));
|
||||||
|
name = prefix.substring(nameStart, prefix.length()).trim();
|
||||||
rawName = prefixString.substringOriginal(nameStart, prefix.length());
|
|
||||||
name = removeInternalSpaces(rawName);
|
|
||||||
|
|
||||||
// check for return type
|
// check for return type
|
||||||
if (nameStart > 0) {
|
if (nameStart > 0) {
|
||||||
rawReturnType = prefixString.substringOriginal(0, nameStart);
|
returnType = prefix.substring(0, nameStart);
|
||||||
returnType = removeInternalSpaces(rawReturnType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1579,21 +1587,27 @@ public class GnuDemanglerParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object that will change the input string (such as to remove spaces) while maintaining
|
* A class to handle whitespace manipulation within demangled strings. This class will
|
||||||
* the ability to translate from the new string back to the old string
|
* remove bad spaces, which is all whitespace that is not needed to separate distinct objects
|
||||||
|
* inside of a demangled string.
|
||||||
|
*
|
||||||
|
* <p>Generally, this class removes spaces within templates and parameter lists. It will
|
||||||
|
* remove some spaces, while converting some to underscores.
|
||||||
*/
|
*/
|
||||||
private class CondensedString {
|
private class CondensedString {
|
||||||
|
|
||||||
private String originalString;
|
@SuppressWarnings("unused") // used by toString()
|
||||||
private String condensedString;
|
private String sourceText;
|
||||||
|
private String condensedText;
|
||||||
private List<Part> parts = new ArrayList<>();
|
private List<Part> parts = new ArrayList<>();
|
||||||
|
|
||||||
CondensedString(String input) {
|
CondensedString(String input) {
|
||||||
this.originalString = input;
|
String fixed = fixupUnnamedTypes(input);
|
||||||
this.condensedString = createFixedString(input);
|
this.sourceText = fixed;
|
||||||
|
this.condensedText = convertTemplateAndParameterSpaces(fixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createFixedString(String name) {
|
private String convertTemplateAndParameterSpaces(String name) {
|
||||||
|
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
char last = NULL_CHAR;
|
char last = NULL_CHAR;
|
||||||
@@ -1638,46 +1652,25 @@ public class GnuDemanglerParser {
|
|||||||
return Character.isLetterOrDigit(last) && Character.isLetterOrDigit(next);
|
return Character.isLetterOrDigit(last) && Character.isLetterOrDigit(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String fixupUnnamedTypes(String demangled) {
|
||||||
|
String fixed = demangled;
|
||||||
|
Matcher matcher = UNNAMED_TYPE_PATTERN.matcher(demangled);
|
||||||
|
while (matcher.find()) {
|
||||||
|
String text = matcher.group(1);
|
||||||
|
String noSpace = text.replaceFirst("\\s", "_");
|
||||||
|
fixed = fixed.replace(text, noSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fixed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the original string value that has been 'condensed', which means to remove
|
* Returns the original string value that has been 'condensed', which means to remove
|
||||||
* internal spaces
|
* internal spaces
|
||||||
* @return the condensed string
|
* @return the condensed string
|
||||||
*/
|
*/
|
||||||
String getCondensedString() {
|
String getCondensedText() {
|
||||||
return condensedString;
|
return condensedText;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the given start and end <b>from the 'condensed' string</b> to create a substring of
|
|
||||||
* the original string
|
|
||||||
* @param condensedStart the start in the condensed string
|
|
||||||
* @param condensedEnd the end in the condensed string
|
|
||||||
* @return the substring from the original string
|
|
||||||
*/
|
|
||||||
String substringOriginal(int condensedStart, int condensedEnd) {
|
|
||||||
|
|
||||||
int start = condensedToOriginal(condensedStart);
|
|
||||||
int end = condensedToOriginal(condensedEnd);
|
|
||||||
return originalString.substring(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int condensedToOriginal(int index) {
|
|
||||||
|
|
||||||
int next = index + 1;
|
|
||||||
int n = 0;
|
|
||||||
for (int i = 0; i < parts.size(); i++) {
|
|
||||||
Part p = parts.get(i);
|
|
||||||
n += p.condensed.length();
|
|
||||||
if (n == next) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n == index) {
|
|
||||||
return parts.size(); // user asked for length of the 'condensed' string
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IndexOutOfBoundsException("Index not in condensed string: " + index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+19
@@ -1420,6 +1420,25 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
|||||||
signature);
|
signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFunctionWithLamba_WithUnnamedType() throws Exception {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mangled: _ZN13SoloGimbalEKFUt_C2Ev
|
||||||
|
//
|
||||||
|
// Demangled: SoloGimbalEKF::{unnamed type#1}::SoloGimbalEKF()
|
||||||
|
//
|
||||||
|
String mangled = "_ZN13SoloGimbalEKFUt_C2Ev";
|
||||||
|
String demangled = process.demangle(mangled);
|
||||||
|
|
||||||
|
DemangledObject object = parser.parse(mangled, demangled);
|
||||||
|
assertNotNull(object);
|
||||||
|
assertType(object, DemangledFunction.class);
|
||||||
|
|
||||||
|
String signature = object.getSignature(false);
|
||||||
|
assertEquals("undefined SoloGimbalEKF::{unnamed_type#1}::SoloGimbalEKF(void)", signature);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFunctionWithLambda_WrappingAnotherFunctionCall() throws Exception {
|
public void testFunctionWithLambda_WrappingAnotherFunctionCall() throws Exception {
|
||||||
|
|
||||||
|
|||||||
+25
@@ -76,6 +76,31 @@ public class GnuDemanglerIntegrationTest extends AbstractGhidraHeadlessIntegrati
|
|||||||
assertNotNull(cmd.getDemangledObject());
|
assertNotNull(cmd.getDemangledObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParsingReturnType_UnnamedType() throws Exception {
|
||||||
|
|
||||||
|
String mangled = "_ZN13SoloGimbalEKFUt_C2Ev";
|
||||||
|
|
||||||
|
GnuDemangler demangler = new GnuDemangler();
|
||||||
|
demangler.canDemangle(program);// this performs initialization
|
||||||
|
|
||||||
|
GnuDemanglerOptions options = new GnuDemanglerOptions();
|
||||||
|
options.setDemangleOnlyKnownPatterns(false);
|
||||||
|
options = options.withDeprecatedDemangler();
|
||||||
|
DemangledObject result = demangler.demangle(mangled, options);
|
||||||
|
assertNotNull(result);
|
||||||
|
assertEquals("undefined SoloGimbalEKF::{unnamed_type#1}::SoloGimbalEKF(void)",
|
||||||
|
result.getSignature(false));
|
||||||
|
|
||||||
|
DemanglerCmd cmd = new DemanglerCmd(addr("01001000"), mangled, options);
|
||||||
|
|
||||||
|
// this used to trigger an exception
|
||||||
|
boolean success = applyCmd(program, cmd);
|
||||||
|
assertTrue("Demangler command failed: " + cmd.getStatusMsg(), success);
|
||||||
|
|
||||||
|
assertNotNull(cmd.getDemangledObject());
|
||||||
|
}
|
||||||
|
|
||||||
private Address addr(String address) {
|
private Address addr(String address) {
|
||||||
return program.getAddressFactory().getAddress(address);
|
return program.getAddressFactory().getAddress(address);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user