mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 17:59:46 +02:00
GP-4627 - MDMangUtils methods to get SymbolPaths
This commit is contained in:
parent
127f5679d1
commit
5f3de98c79
7 changed files with 271 additions and 17 deletions
|
@ -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() {
|
public String getMangled() {
|
||||||
return mangled;
|
return mangled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MDObjectCPP getNestedObject() {
|
||||||
|
return objectCPP;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -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
|
@Override
|
||||||
public Iterator<MDQualifier> iterator() {
|
public Iterator<MDQualifier> iterator() {
|
||||||
return quals.iterator();
|
return quals.iterator();
|
||||||
|
|
|
@ -44,12 +44,16 @@ public class MDQualifier extends MDParsableItem {
|
||||||
return (nameNested != null);
|
return (nameNested != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAnon() {
|
||||||
|
return (nameAnonymous != null);
|
||||||
|
}
|
||||||
|
|
||||||
public MDNestedName getNested() {
|
public MDNestedName getNested() {
|
||||||
return nameNested;
|
return nameNested;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MDReusableName getAnonymousName() {
|
public String getAnonymousName() {
|
||||||
return nameAnonymous;
|
return nameAnonymous.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class MDObjectCPP extends MDObject {
|
||||||
*/
|
*/
|
||||||
public String getName() {
|
public String getName() {
|
||||||
if (hashedObjectFlag) {
|
if (hashedObjectFlag) {
|
||||||
return hashedObject.toString();
|
return hashedObject.getName();
|
||||||
}
|
}
|
||||||
return getQualifiedName().getBasicName().toString();
|
return getQualifiedName().getBasicName().toString();
|
||||||
}
|
}
|
||||||
|
@ -267,10 +267,19 @@ public class MDObjectCPP extends MDObject {
|
||||||
hashString = builder.toString();
|
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
|
@Override
|
||||||
public void insert(StringBuilder builder) {
|
public void insert(StringBuilder builder) {
|
||||||
// We have made up the output format. Nothing is sacrosanct about this output.
|
builder.append(getName());
|
||||||
builder.append("`" + hashString + "'");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,8 +149,11 @@ public class MDTypeInfoParser {
|
||||||
dmang.increment();
|
dmang.increment();
|
||||||
typeInfo = new MDMemberFunctionInfo(dmang);
|
typeInfo = new MDMemberFunctionInfo(dmang);
|
||||||
typeInfo.setPrivate();
|
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(
|
typeInfo.setPointerFormat(
|
||||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
case 'D':
|
case 'D':
|
||||||
|
@ -172,7 +175,7 @@ public class MDTypeInfoParser {
|
||||||
typeInfo = new MDVFAdjustor(dmang);
|
typeInfo = new MDVFAdjustor(dmang);
|
||||||
typeInfo.setPrivate();
|
typeInfo.setPrivate();
|
||||||
typeInfo.setPointerFormat(
|
typeInfo.setPointerFormat(
|
||||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||||
break;
|
break;
|
||||||
case 'I': // A, B, I, J, Q, R: These might be "this adjustment" with no adjustment
|
case 'I': // A, B, I, J, Q, R: These might be "this adjustment" with no adjustment
|
||||||
case 'J':
|
case 'J':
|
||||||
|
@ -180,7 +183,7 @@ public class MDTypeInfoParser {
|
||||||
typeInfo = new MDMemberFunctionInfo(dmang);
|
typeInfo = new MDMemberFunctionInfo(dmang);
|
||||||
typeInfo.setProtected();
|
typeInfo.setProtected();
|
||||||
typeInfo.setPointerFormat(
|
typeInfo.setPointerFormat(
|
||||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||||
break;
|
break;
|
||||||
case 'K':
|
case 'K':
|
||||||
case 'L':
|
case 'L':
|
||||||
|
@ -202,7 +205,7 @@ public class MDTypeInfoParser {
|
||||||
typeInfo = new MDVFAdjustor(dmang);
|
typeInfo = new MDVFAdjustor(dmang);
|
||||||
typeInfo.setProtected();
|
typeInfo.setProtected();
|
||||||
typeInfo.setPointerFormat(
|
typeInfo.setPointerFormat(
|
||||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||||
break;
|
break;
|
||||||
case 'Q': // A, B, I, J, Q, R: These might be "this adjustment" with no adjustment
|
case 'Q': // A, B, I, J, Q, R: These might be "this adjustment" with no adjustment
|
||||||
case 'R':
|
case 'R':
|
||||||
|
@ -210,7 +213,7 @@ public class MDTypeInfoParser {
|
||||||
typeInfo = new MDMemberFunctionInfo(dmang);
|
typeInfo = new MDMemberFunctionInfo(dmang);
|
||||||
typeInfo.setPublic();
|
typeInfo.setPublic();
|
||||||
typeInfo.setPointerFormat(
|
typeInfo.setPointerFormat(
|
||||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
case 'T':
|
case 'T':
|
||||||
|
@ -232,7 +235,7 @@ public class MDTypeInfoParser {
|
||||||
typeInfo = new MDVFAdjustor(dmang);
|
typeInfo = new MDVFAdjustor(dmang);
|
||||||
typeInfo.setPublic();
|
typeInfo.setPublic();
|
||||||
typeInfo.setPointerFormat(
|
typeInfo.setPointerFormat(
|
||||||
(code % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
((code - 'A') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||||
break;
|
break;
|
||||||
case 'Y':
|
case 'Y':
|
||||||
case 'Z':
|
case 'Z':
|
||||||
|
@ -277,27 +280,30 @@ public class MDTypeInfoParser {
|
||||||
char ch = dmang.getAndIncrement();
|
char ch = dmang.getAndIncrement();
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
// UINFO: (0-5) isFunction, isMember, isvtordisp (0-5)
|
// 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 '0':
|
||||||
case '1':
|
case '1':
|
||||||
typeInfo = new MDVtordisp(dmang);
|
typeInfo = new MDVtordisp(dmang);
|
||||||
typeInfo.setPrivate();
|
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(
|
typeInfo.setPointerFormat(
|
||||||
(ch % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
((ch - '0') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||||
break;
|
break;
|
||||||
case '2':
|
case '2':
|
||||||
case '3':
|
case '3':
|
||||||
typeInfo = new MDVtordisp(dmang);
|
typeInfo = new MDVtordisp(dmang);
|
||||||
typeInfo.setProtected();
|
typeInfo.setProtected();
|
||||||
typeInfo.setPointerFormat(
|
typeInfo.setPointerFormat(
|
||||||
(ch % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
((ch - '0') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||||
break;
|
break;
|
||||||
case '4':
|
case '4':
|
||||||
case '5':
|
case '5':
|
||||||
typeInfo = new MDVtordisp(dmang);
|
typeInfo = new MDVtordisp(dmang);
|
||||||
typeInfo.setPublic();
|
typeInfo.setPublic();
|
||||||
typeInfo.setPointerFormat(
|
typeInfo.setPointerFormat(
|
||||||
(ch % 2 == 0) ? PointerFormat._NEAR : PointerFormat._NEAR);
|
((ch - '0') % 2 == 0) ? PointerFormat._NEAR : PointerFormat._FAR);
|
||||||
break;
|
break;
|
||||||
case '$':
|
case '$':
|
||||||
char ch2 = dmang.getAndIncrement();
|
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…
Add table
Add a link
Reference in a new issue