mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-10 14:11:59 +00:00
GP-4627 - MDMangUtils methods to get SymbolPaths
This commit is contained in:
parent
127f5679d1
commit
5f3de98c79
@ -0,0 +1,118 @@
|
||||
/* ###
|
||||
* 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 mdemangler;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import mdemangler.datatype.complex.MDComplexType;
|
||||
import mdemangler.datatype.modifier.MDModifierType;
|
||||
import mdemangler.naming.*;
|
||||
import mdemangler.object.MDObjectCPP;
|
||||
|
||||
/**
|
||||
* Utility class for MDMang users (and perhaps internal)
|
||||
*/
|
||||
public class MDMangUtils {
|
||||
|
||||
private MDMangUtils() {
|
||||
// purposefully empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns SymbolPath for the demangled item
|
||||
* @param parsableItem the demangled item
|
||||
* @return the symbol path
|
||||
*/
|
||||
public static SymbolPath getSymbolPath(MDParsableItem parsableItem) {
|
||||
return getSymbolPath(parsableItem, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a more simple SymbolPath for the demangled item. Any embedded object found at
|
||||
* the main namespace level will have its namespace components retrieved and inserted
|
||||
* appropriately in the main SymbolPath namespace. However, embedded objects that are more
|
||||
* deeply placed (such as when used for a template argument) don't and shouldn't take part
|
||||
* in this simplification
|
||||
* @param parsableItem the demangled item
|
||||
* @return the symbol path
|
||||
*/
|
||||
public static SymbolPath getSimpleSymbolPath(MDParsableItem parsableItem) {
|
||||
return getSymbolPath(parsableItem, true);
|
||||
}
|
||||
|
||||
private static SymbolPath getSymbolPath(MDParsableItem parsableItem, boolean simple) {
|
||||
List<String> parts = new ArrayList<>();
|
||||
// When simple is true, we need to recurse the nested hierarchy to pull the names
|
||||
// up to the main namespace level, so we set recurse = true
|
||||
recurseNamespace(parts, parsableItem, simple);
|
||||
SymbolPath sp = null;
|
||||
for (String part : parts) {
|
||||
sp = new SymbolPath(sp, part);
|
||||
}
|
||||
return sp;
|
||||
}
|
||||
|
||||
private static void recurseNamespace(List<String> parts, MDParsableItem item,
|
||||
boolean recurseNested) {
|
||||
item = getReferencedType(item);
|
||||
String name;
|
||||
MDQualification qualification;
|
||||
if (item instanceof MDComplexType complexType) {
|
||||
MDQualifiedName qualName = complexType.getNamespace();
|
||||
name = qualName.getName();
|
||||
qualification = qualName.getQualification();
|
||||
}
|
||||
else if (item instanceof MDObjectCPP objCpp) {
|
||||
MDObjectCPP embeddedObj = objCpp.getEmbeddedObject();
|
||||
name = embeddedObj.getName();
|
||||
qualification = embeddedObj.getQualification();
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> myParts = new ArrayList<>();
|
||||
// the qualification comes in reverse order... the last is nearest to namespace root
|
||||
for (MDQualifier qual : qualification) {
|
||||
if (qual.isNested() && recurseNested) {
|
||||
MDNestedName nestedName = qual.getNested();
|
||||
MDObjectCPP nestedObjCpp = nestedName.getNestedObject();
|
||||
List<String> nestedParts = new ArrayList<>();
|
||||
recurseNamespace(nestedParts, nestedObjCpp, recurseNested);
|
||||
myParts.addAll(0, nestedParts);
|
||||
}
|
||||
else if (qual.isAnon()) {
|
||||
myParts.add(0, qual.getAnonymousName());
|
||||
}
|
||||
else {
|
||||
myParts.add(0, qual.toString());
|
||||
}
|
||||
}
|
||||
myParts.add(name);
|
||||
parts.addAll(myParts);
|
||||
}
|
||||
|
||||
// This method recurses
|
||||
private static MDParsableItem getReferencedType(MDParsableItem item) {
|
||||
if (item instanceof MDModifierType m) {
|
||||
return getReferencedType(m.getReferencedType());
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
}
|
@ -55,6 +55,11 @@ public class MDNestedName extends MDParsableItem {
|
||||
public String getMangled() {
|
||||
return mangled;
|
||||
}
|
||||
|
||||
public MDObjectCPP getNestedObject() {
|
||||
return objectCPP;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -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.
|
||||
@ -138,6 +138,10 @@ public class MDQualification extends MDParsableItem implements Iterable<MDQualif
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides iterator of MDQualifiers, where the last iteration is the namespace root
|
||||
* @return the iterator
|
||||
*/
|
||||
@Override
|
||||
public Iterator<MDQualifier> iterator() {
|
||||
return quals.iterator();
|
||||
|
@ -44,12 +44,16 @@ public class MDQualifier extends MDParsableItem {
|
||||
return (nameNested != null);
|
||||
}
|
||||
|
||||
public boolean isAnon() {
|
||||
return (nameAnonymous != null);
|
||||
}
|
||||
|
||||
public MDNestedName getNested() {
|
||||
return nameNested;
|
||||
}
|
||||
|
||||
public MDReusableName getAnonymousName() {
|
||||
return nameAnonymous;
|
||||
public String getAnonymousName() {
|
||||
return nameAnonymous.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,7 +63,7 @@ public class MDObjectCPP extends MDObject {
|
||||
*/
|
||||
public String getName() {
|
||||
if (hashedObjectFlag) {
|
||||
return hashedObject.toString();
|
||||
return hashedObject.getName();
|
||||
}
|
||||
return getQualifiedName().getBasicName().toString();
|
||||
}
|
||||
@ -267,10 +267,19 @@ public class MDObjectCPP extends MDObject {
|
||||
hashString = builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name representation
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
// We have made up the name representation with the encompassing tick marks (similar
|
||||
// to other types). Nothing is sacrosanct about this output.
|
||||
return "`" + hashString + "'";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(StringBuilder builder) {
|
||||
// We have made up the output format. Nothing is sacrosanct about this output.
|
||||
builder.append("`" + hashString + "'");
|
||||
builder.append(getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,8 +149,11 @@ public class MDTypeInfoParser {
|
||||
dmang.increment();
|
||||
typeInfo = new MDMemberFunctionInfo(dmang);
|
||||
typeInfo.setPrivate();
|
||||
// When considering code 'A' to be at index 0, then for this and other processing
|
||||
// below, those that have an *even* index are *near* pointers and those with
|
||||
// an *odd* index are *far* pointers
|
||||
typeInfo.setPointerFormat(
|
||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
||||
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||
break;
|
||||
case 'C':
|
||||
case 'D':
|
||||
@ -172,7 +175,7 @@ public class MDTypeInfoParser {
|
||||
typeInfo = new MDVFAdjustor(dmang);
|
||||
typeInfo.setPrivate();
|
||||
typeInfo.setPointerFormat(
|
||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
||||
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||
break;
|
||||
case 'I': // A, B, I, J, Q, R: These might be "this adjustment" with no adjustment
|
||||
case 'J':
|
||||
@ -180,7 +183,7 @@ public class MDTypeInfoParser {
|
||||
typeInfo = new MDMemberFunctionInfo(dmang);
|
||||
typeInfo.setProtected();
|
||||
typeInfo.setPointerFormat(
|
||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
||||
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||
break;
|
||||
case 'K':
|
||||
case 'L':
|
||||
@ -202,7 +205,7 @@ public class MDTypeInfoParser {
|
||||
typeInfo = new MDVFAdjustor(dmang);
|
||||
typeInfo.setProtected();
|
||||
typeInfo.setPointerFormat(
|
||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
||||
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||
break;
|
||||
case 'Q': // A, B, I, J, Q, R: These might be "this adjustment" with no adjustment
|
||||
case 'R':
|
||||
@ -210,7 +213,7 @@ public class MDTypeInfoParser {
|
||||
typeInfo = new MDMemberFunctionInfo(dmang);
|
||||
typeInfo.setPublic();
|
||||
typeInfo.setPointerFormat(
|
||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
||||
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||
break;
|
||||
case 'S':
|
||||
case 'T':
|
||||
@ -232,7 +235,7 @@ public class MDTypeInfoParser {
|
||||
typeInfo = new MDVFAdjustor(dmang);
|
||||
typeInfo.setPublic();
|
||||
typeInfo.setPointerFormat(
|
||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
||||
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||
break;
|
||||
case 'Y':
|
||||
case 'Z':
|
||||
@ -277,27 +280,30 @@ public class MDTypeInfoParser {
|
||||
char ch = dmang.getAndIncrement();
|
||||
switch (ch) {
|
||||
// UINFO: (0-5) isFunction, isMember, isvtordisp (0-5)
|
||||
// UINFO: val%2==0: near; val%2==0: far;
|
||||
// UINFO: val%2==0: near; val%2==1: far;
|
||||
case '0':
|
||||
case '1':
|
||||
typeInfo = new MDVtordisp(dmang);
|
||||
typeInfo.setPrivate();
|
||||
// When considering code '0' to be at index 0, then for this and other processing
|
||||
// below, those that have an *even* index are *near* pointers and those with
|
||||
// an *odd* index are *far* pointers
|
||||
typeInfo.setPointerFormat(
|
||||
(ch % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
||||
((ch - '0') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||
break;
|
||||
case '2':
|
||||
case '3':
|
||||
typeInfo = new MDVtordisp(dmang);
|
||||
typeInfo.setProtected();
|
||||
typeInfo.setPointerFormat(
|
||||
(ch % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
||||
((ch - '0') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||
break;
|
||||
case '4':
|
||||
case '5':
|
||||
typeInfo = new MDVtordisp(dmang);
|
||||
typeInfo.setPublic();
|
||||
typeInfo.setPointerFormat(
|
||||
(ch % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
||||
((ch - '0') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||
break;
|
||||
case '$':
|
||||
char ch2 = dmang.getAndIncrement();
|
||||
|
@ -0,0 +1,108 @@
|
||||
/* ###
|
||||
* 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 mdemangler;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import mdemangler.datatype.MDDataType;
|
||||
|
||||
/**
|
||||
* This class performs testing of MDMangUtils methods
|
||||
*/
|
||||
public class MDMangUtilsTest extends AbstractGenericTest {
|
||||
|
||||
@Test
|
||||
public void testWithLambdaAndSimpleConversionApplies() throws Exception {
|
||||
// From record number 604770
|
||||
// We cared about the lambda because this is a situation where we need to deal
|
||||
// with nested types that were causing problems for PDB
|
||||
String mangled = ".?AV<lambda_0>@?0??name0@name1@@YA?AUname2@2@Uname3@2@Uname4@2@@Z@";
|
||||
String expected =
|
||||
"`struct name1::name2 __cdecl name1::name0(struct name1::name3,struct name1::name4)'::`1'::<lambda_0>";
|
||||
String simpleExpected = "name1::name0::`1'::<lambda_0>";
|
||||
String expectedDemangled =
|
||||
"class `struct name1::name2 __cdecl name1::name0(struct name1::name3,struct name1::name4)'::`1'::<lambda_0>";
|
||||
|
||||
MDMangGhidra demangler = new MDMangGhidra();
|
||||
MDDataType item = demangler.demangleType(mangled, true);
|
||||
|
||||
String demangled = item.toString();
|
||||
SymbolPath symbolPath = MDMangUtils.getSymbolPath(item);
|
||||
SymbolPath simpleSymbolPath = MDMangUtils.getSimpleSymbolPath(item);
|
||||
String result = symbolPath.getPath();
|
||||
String simpleResult = simpleSymbolPath.getPath();
|
||||
|
||||
assertEquals(expected, result);
|
||||
assertEquals(simpleExpected, simpleResult);
|
||||
assertEquals(expectedDemangled, demangled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeNamespaceSimpleConversionDoesNotApply1() throws Exception {
|
||||
String mangled =
|
||||
".?AU?$name0@$$QEAV<lambda_0>@?0??name1@name2@?Aname3@name4@@UEAAXVname5@4@HAEBVname6@4@@Z@@name7@name8@@";
|
||||
String expected =
|
||||
"name8::name7::name0<class `public: virtual void __cdecl name4::`anonymous namespace'::name2::name1(class Aname3::name5,int,class Aname3::name6 const & __ptr64) __ptr64'::`1'::<lambda_0> && __ptr64>";
|
||||
// See MDMangUtils.getSimpleSymbolPath(item) javadoc to understand why expected and
|
||||
// simpleExpected are the same
|
||||
String simpleExpected = expected;
|
||||
String expectedDemangled =
|
||||
"struct name8::name7::name0<class `public: virtual void __cdecl name4::`anonymous namespace'::name2::name1(class Aname3::name5,int,class Aname3::name6 const & __ptr64) __ptr64'::`1'::<lambda_0> && __ptr64>";
|
||||
|
||||
MDMangGhidra demangler = new MDMangGhidra();
|
||||
MDDataType item = demangler.demangleType(mangled, true);
|
||||
|
||||
String demangled = item.toString();
|
||||
SymbolPath symbolPath = MDMangUtils.getSymbolPath(item);
|
||||
SymbolPath simpleSymbolPath = MDMangUtils.getSimpleSymbolPath(item);
|
||||
String result = symbolPath.getPath();
|
||||
String simpleResult = simpleSymbolPath.getPath();
|
||||
|
||||
assertEquals(expected, result);
|
||||
assertEquals(simpleExpected, simpleResult);
|
||||
assertEquals(expectedDemangled, demangled);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTypeNamespaceSimpleConversionDoesNotApply2() throws Exception {
|
||||
String mangled = ".?AU?$name0@$$QEAV<lambda_0>@?0???1Aname1@name2@@UEAA@XZ@@name3@name4@@";
|
||||
String expected =
|
||||
"name4::name3::name0<class `public: virtual __cdecl name2::Aname1::~Aname1(void) __ptr64'::`1'::<lambda_0> && __ptr64>";
|
||||
// See MDMangUtils.getSimpleSymbolPath(item) javadoc to understand why expected and
|
||||
// simpleExpected are the same
|
||||
String simpleExpected = expected;
|
||||
String expectedDemangled =
|
||||
"struct name4::name3::name0<class `public: virtual __cdecl name2::Aname1::~Aname1(void) __ptr64'::`1'::<lambda_0> && __ptr64>";
|
||||
|
||||
MDMangGhidra demangler = new MDMangGhidra();
|
||||
MDDataType item = demangler.demangleType(mangled, true);
|
||||
|
||||
String demangled = item.toString();
|
||||
SymbolPath symbolPath = MDMangUtils.getSymbolPath(item);
|
||||
SymbolPath simpleSymbolPath = MDMangUtils.getSimpleSymbolPath(item);
|
||||
String result = symbolPath.getPath();
|
||||
String simpleResult = simpleSymbolPath.getPath();
|
||||
|
||||
assertEquals(expected, result);
|
||||
assertEquals(simpleExpected, simpleResult);
|
||||
assertEquals(expectedDemangled, demangled);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user