Skip to content

Commit

Permalink
GP-4717 - Add DemangledNamespaceNode and refine MDMangGhidra namespac…
Browse files Browse the repository at this point in the history
…e processing, including setting anonymous namespace names to their underlying name
  • Loading branch information
ghizard committed Jul 15, 2024
1 parent 947709e commit 08c95d2
Show file tree
Hide file tree
Showing 8 changed files with 455 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* ###
* 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;

import org.apache.commons.lang3.StringUtils;

import ghidra.program.model.symbol.Namespace;

/**
* Represents a plain namespace node that is not a type or method
*/
public class DemangledNamespaceNode implements Demangled {

// The intention is for this to be as refined a part of a larger mangled string as possible,
// but it is up to the user to know if they can pass that more refined string or if they
// just have to pass a bigger piece.
protected String mangled;
private String originalDemangled;
private String demangledName;
private String name; // 'safe' name

protected Demangled namespace;

/**
* Constructor
* @param mangled as a refined a piece of the (larger) original mangled stream as the user
* can provide, though many times the larger piece is all that the user can provide
* @param originalDemangled the original demangled string to match mangled string with the
* same caveats
* @param name the name of the namespace node
*/
public DemangledNamespaceNode(String mangled, String originalDemangled, String name) {
this.mangled = mangled;
this.originalDemangled = originalDemangled;
setName(name);
}

@Override
public void setName(String name) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Name cannot be blank");
}
demangledName = name;
this.name = DemanglerUtil.stripSuperfluousSignatureSpaces(name).replace(' ', '_');
}

@Override
public String getName() {
return name;
}

@Override
public String getMangledString() {
return mangled;
}

@Override
public String getOriginalDemangled() {
return originalDemangled;
}

@Override
public String getDemangledName() {
return demangledName;
}

@Override
public void setNamespace(Demangled ns) {
namespace = ns;
}

@Override
public Demangled getNamespace() {
return namespace;
}

@Override
public String getNamespaceString() {
return getName(true);
}

@Override
public String getNamespaceName() {
return name;
}

@Override
public String getSignature() {
return getNamespaceName();
}

private String getName(boolean includeNamespace) {
StringBuilder builder = new StringBuilder();
if (includeNamespace && namespace != null) {
builder.append(namespace.getNamespaceString());
builder.append(Namespace.DELIMITER);
}
builder.append(demangledName);
return builder.toString();
}

@Override
public String toString() {
return getNamespaceString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ public class DemangledType implements Demangled {
private boolean isConst;
private boolean isVolatile;

public DemangledType(String mangled, String originaDemangled, String name) {
public DemangledType(String mangled, String originalDemangled, String name) {
this.mangled = mangled;
this.originalDemangled = originaDemangled;
this.originalDemangled = originalDemangled;
setName(name);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,33 +94,79 @@ public MDDataType demangleType(String mangledArg, boolean errorOnRemainingChars)
return returnedType;
}

public DemangledType processNamespace(MDQualifiedName qualifiedName) {
public Demangled processNamespace(MDQualifiedName qualifiedName) {
return processNamespace(qualifiedName.getQualification());
}

private DemangledType processNamespace(MDQualification qualification) {
private Demangled processNamespace(MDQualification qualification) {
Iterator<MDQualifier> it = qualification.iterator();
if (!it.hasNext()) {
return null;
}

MDQualifier qual = it.next();
DemangledType type = new DemangledType(mangledSource, demangledSource, qual.toString());
DemangledType parentType = type;
Demangled type = getDemangled(qual);
Demangled current = type;
// Note that qualifiers come in reverse order, from most refined to root being the last
while (it.hasNext()) {
qual = it.next();
DemangledType newType;
if (qual.isNested()) {
String subMangled = qual.getNested().getMangled();
newType = new DemangledType(subMangled, demangledSource, qual.toString());
Demangled parent = getDemangled(qual);
current.setNamespace(parent);
current = parent;
}
return type;
}

private Demangled getDemangled(MDQualifier qual) {
Demangled demangled;
if (qual.isNested()) {
String subMangled = qual.getNested().getMangled();
MDObjectCPP obj = qual.getNested().getNestedObject();
MDTypeInfo typeInfo = obj.getTypeInfo();
MDType type = typeInfo.getMDType();
if (type instanceof MDDataType dt) {
demangled = new DemangledType(subMangled, qual.toString(), qual.toString());
}
else if (type instanceof MDFunctionType ft) {
// We currently cannot handle functions as part of a namespace, so we will just
// treat the demangled function namespace string as a plain namespace.
//demangled = new DemangledFunction(subMangled, qual.toString(), qual.toString());
demangled =
new DemangledNamespaceNode(subMangled, qual.toString(), qual.toString());
}
else {
newType = new DemangledType(mangledSource, demangledSource, qual.toString());
demangled =
new DemangledNamespaceNode(subMangled, qual.toString(), qual.toString());
}
parentType.setNamespace(newType);
parentType = newType;
}
return type;
else if (qual.isAnon()) {
// Instead of using the standard qual.toString() method, which returns
// "`anonymous namespace'" for anonymous qualifiers, we use qual.getAnonymousName()
// which will have the underlying anonymous name of the form "A0xfedcba98" to create
// a standardized anonymous name that is distinguishable from other anonymous names.
// The standardized name comes from createStandardAnonymousNamespaceNode(). This
// is especially important when there are sibling anonymous names.
String anon = MDMangUtils.createStandardAnonymousNamespaceNode(qual.getAnonymousName());
demangled = new DemangledNamespaceNode(mangledSource, qual.toString(), anon);
}
else if (qual.isInterface()) {
// TODO: need to do better; setting namespace for now
demangled = new DemangledNamespaceNode(mangledSource, qual.toString(), qual.toString());
}
else if (qual.isNameQ()) {
// TODO: need to do better; setting namespace for now, as it looks like interface
demangled = new DemangledNamespaceNode(mangledSource, qual.toString(), qual.toString());
}
else if (qual.isNameC()) {
// TODO: need to do better; setting type for now, but not processed yet and not sure
// what it is
demangled = new DemangledType(mangledSource, qual.toString(), qual.toString());
}
else {
// This takes care of plain and local namespaces
demangled = new DemangledNamespaceNode(mangledSource, qual.toString(), qual.toString());
}
return demangled;
}

private DemangledObject processItem() {
Expand Down
Loading

0 comments on commit 08c95d2

Please sign in to comment.