GP-4340 Improved datatype conflict name handling

This commit is contained in:
ghidra1 2024-02-23 16:24:22 -05:00
parent 7f58541ba0
commit 5dc7347eee
22 changed files with 493 additions and 371 deletions

View file

@ -29,6 +29,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.stream.JsonWriter; import com.google.gson.stream.JsonWriter;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.database.data.ProgramDataTypeManager; import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFormatException; import ghidra.program.model.address.AddressFormatException;
@ -96,7 +97,8 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
* @param baseWriter the writer to use when writing data types * @param baseWriter the writer to use when writing data types
* @throws IOException if there is an exception writing the output * @throws IOException if there is an exception writing the output
*/ */
public IsfDataTypeWriter(DataTypeManager dtm, List<DataType> target, Writer baseWriter) throws IOException { public IsfDataTypeWriter(DataTypeManager dtm, List<DataType> target, Writer baseWriter)
throws IOException {
super(baseWriter); super(baseWriter);
this.baseWriter = baseWriter; this.baseWriter = baseWriter;
this.dtm = dtm; this.dtm = dtm;
@ -117,11 +119,12 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
STRICT = true; STRICT = true;
} }
@Override
public JsonObject getRootObject(TaskMonitor monitor) throws CancelledException, IOException { public JsonObject getRootObject(TaskMonitor monitor) throws CancelledException, IOException {
genRoot(monitor); genRoot(monitor);
return data; return data;
} }
@Override @Override
protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException { protected void genRoot(TaskMonitor monitor) throws CancelledException, IOException {
genMetadata(); genMetadata();
@ -160,7 +163,8 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
oskey = metaData.get("Compiler ID"); oskey = metaData.get("Compiler ID");
if (metaData.containsKey("PDB Loaded")) { if (metaData.containsKey("PDB Loaded")) {
os = gson.toJsonTree(new IsfWinOS(metaData)); os = gson.toJsonTree(new IsfWinOS(metaData));
} else if (metaData.containsKey("Executable Format")) { }
else if (metaData.containsKey("Executable Format")) {
if (metaData.get("Executable Format").contains("ELF")) { if (metaData.get("Executable Format").contains("ELF")) {
oskey = "linux"; oskey = "linux";
os = gson.toJsonTree(new IsfLinuxOS(gson, metaData)); os = gson.toJsonTree(new IsfLinuxOS(gson, metaData));
@ -201,15 +205,18 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
Symbol symbol = iterator.next(); Symbol symbol = iterator.next();
symbolToJson(imageBase, symbolTable, linkages, map, symbol); symbolToJson(imageBase, symbolTable, linkages, map, symbol);
} }
} else { }
else {
for (Address addr : requestedAddresses) { for (Address addr : requestedAddresses) {
Symbol[] symsFromAddr = symbolTable.getSymbols(addr.add(imageBase.getOffset())); Symbol[] symsFromAddr =
symbolTable.getSymbols(addr.add(imageBase.getOffset()));
for (Symbol symbol : symsFromAddr) { for (Symbol symbol : symsFromAddr) {
symbolToJson(imageBase, symbolTable, linkages, map, symbol); symbolToJson(imageBase, symbolTable, linkages, map, symbol);
} }
} }
} }
} else { }
else {
for (String key : requestedSymbols) { for (String key : requestedSymbols) {
SymbolIterator iter = symbolTable.getSymbols(key); SymbolIterator iter = symbolTable.getSymbols(key);
while (iter.hasNext()) { while (iter.hasNext()) {
@ -263,7 +270,7 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
monitor.setMaximum(keylist.size()); monitor.setMaximum(keylist.size());
for (String key : keylist) { for (String key : keylist) {
DataType dataType = map.get(key); DataType dataType = map.get(key);
if (key.contains(".conflict")) { if (DataTypeUtilities.isConflictDataType(dataType)) {
continue; continue;
} }
obj = getObjectForDataType(dataType, monitor); obj = getObjectForDataType(dataType, monitor);
@ -273,28 +280,34 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
if (dataType instanceof FunctionDefinition) { if (dataType instanceof FunctionDefinition) {
// Would be nice to support this in the future, but Volatility does not // Would be nice to support this in the future, but Volatility does not
add(functions, dataType.getPathName(), obj); add(functions, dataType.getPathName(), obj);
} else if (IsfUtilities.isBaseDataType(dataType)) { }
else if (IsfUtilities.isBaseDataType(dataType)) {
add(baseTypes, dataType.getPathName(), obj); add(baseTypes, dataType.getPathName(), obj);
} else if (dataType instanceof TypeDef) { }
else if (dataType instanceof TypeDef) {
DataType baseDataType = ((TypeDef) dataType).getBaseDataType(); DataType baseDataType = ((TypeDef) dataType).getBaseDataType();
if (IsfUtilities.isBaseDataType(baseDataType)) { if (IsfUtilities.isBaseDataType(baseDataType)) {
add(baseTypes, dataType.getPathName(), obj); add(baseTypes, dataType.getPathName(), obj);
} else if (baseDataType instanceof Enum) { }
else if (baseDataType instanceof Enum) {
add(enums, dataType.getPathName(), obj); add(enums, dataType.getPathName(), obj);
} else { }
else {
add(userTypes, dataType.getPathName(), obj); add(userTypes, dataType.getPathName(), obj);
} }
} else if (dataType instanceof Enum) { }
else if (dataType instanceof Enum) {
add(enums, dataType.getPathName(), obj); add(enums, dataType.getPathName(), obj);
} else if (dataType instanceof Composite) { }
else if (dataType instanceof Composite) {
add(userTypes, dataType.getPathName(), obj); add(userTypes, dataType.getPathName(), obj);
} }
monitor.increment(); monitor.increment();
} }
} }
private void symbolToJson(Address imageBase, SymbolTable symbolTable, Map<String, Symbol> linkages, private void symbolToJson(Address imageBase, SymbolTable symbolTable,
Map<String, JsonObject> map, Symbol symbol) { Map<String, Symbol> linkages, Map<String, JsonObject> map, Symbol symbol) {
String key = symbol.getName(); String key = symbol.getName();
Address address = symbol.getAddress(); Address address = symbol.getAddress();
JsonObject sym = map.containsKey(key) ? map.get(key) : new JsonObject(); JsonObject sym = map.containsKey(key) ? map.get(key) : new JsonObject();
@ -305,10 +318,12 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
sym.addProperty("linkage_name", linkage.getName()); sym.addProperty("linkage_name", linkage.getName());
sym.addProperty("address", linkage.getAddress().getOffset()); sym.addProperty("address", linkage.getAddress().getOffset());
} }
} else { }
else {
if (address.getAddressSpace().equals(imageBase.getAddressSpace())) { if (address.getAddressSpace().equals(imageBase.getAddressSpace())) {
sym.addProperty("address", address.subtract(imageBase)); sym.addProperty("address", address.subtract(imageBase));
} else { }
else {
sym.addProperty("address", address.getOffset()); sym.addProperty("address", address.getOffset());
} }
} }
@ -323,6 +338,7 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
} }
} }
@Override
public void write(JsonObject obj) { public void write(JsonObject obj) {
gson.toJson(obj, writer); gson.toJson(obj, writer);
} }
@ -332,7 +348,8 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
add(baseTypes, "undefined", getTree(newTypedefPointer(null))); add(baseTypes, "undefined", getTree(newTypedefPointer(null)));
} }
protected JsonObject getObjectForDataType(DataType dt, TaskMonitor monitor) throws IOException, CancelledException { protected JsonObject getObjectForDataType(DataType dt, TaskMonitor monitor)
throws IOException, CancelledException {
IsfObject isf = getIsfObject(dt, monitor); IsfObject isf = getIsfObject(dt, monitor);
if (isf != null) { if (isf != null) {
JsonObject jobj = (JsonObject) getTree(isf); JsonObject jobj = (JsonObject) getTree(isf);
@ -351,7 +368,8 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
* @param monitor the task monitor * @param monitor the task monitor
* @throws IOException if there is an exception writing the output * @throws IOException if there is an exception writing the output
*/ */
protected IsfObject getIsfObject(DataType dt, TaskMonitor monitor) throws IOException, CancelledException { protected IsfObject getIsfObject(DataType dt, TaskMonitor monitor)
throws IOException, CancelledException {
if (dt == null) { if (dt == null) {
throw new IOException("Null datatype passed to getIsfObject"); throw new IOException("Null datatype passed to getIsfObject");
} }
@ -377,21 +395,29 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
if (dt instanceof Dynamic dynamic) { if (dt instanceof Dynamic dynamic) {
DataType rep = dynamic.getReplacementBaseType(); DataType rep = dynamic.getReplacementBaseType();
return rep == null ? null : getIsfObject(rep, monitor); return rep == null ? null : getIsfObject(rep, monitor);
} else if (dt instanceof TypeDef typedef) { }
else if (dt instanceof TypeDef typedef) {
return getObjectTypeDef(typedef, monitor); return getObjectTypeDef(typedef, monitor);
} else if (dt instanceof Composite composite) { }
else if (dt instanceof Composite composite) {
return new IsfComposite(composite, this, monitor); return new IsfComposite(composite, this, monitor);
} else if (dt instanceof Enum enumm) { }
else if (dt instanceof Enum enumm) {
return new IsfEnum(enumm); return new IsfEnum(enumm);
} else if (dt instanceof BuiltInDataType builtin) { }
else if (dt instanceof BuiltInDataType builtin) {
return new IsfBuiltIn(builtin); return new IsfBuiltIn(builtin);
} else if (dt instanceof BitFieldDataType) { }
else if (dt instanceof BitFieldDataType) {
// skip - not hit // skip - not hit
} else if (dt instanceof FunctionDefinition) { /// FAIL }
else if (dt instanceof FunctionDefinition) { /// FAIL
// skip - not hit // skip - not hit
} else if (dt.equals(DataType.DEFAULT)) { }
else if (dt.equals(DataType.DEFAULT)) {
// skip - not hit // skip - not hit
} else { }
else {
Msg.warn(this, "Unable to write datatype. Type unrecognized: " + dt.getClass()); Msg.warn(this, "Unable to write datatype. Type unrecognized: " + dt.getClass());
} }
@ -417,8 +443,8 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
} }
} }
} }
Msg.warn(this, Msg.warn(this, "WARNING! conflicting data type names: " + dt.getPathName() + " - " +
"WARNING! conflicting data type names: " + dt.getPathName() + " - " + resolvedType.getPathName()); resolvedType.getPathName());
return resolved.get(dt); return resolved.get(dt);
} }
@ -465,8 +491,9 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
return newIsfDynamicComponent(dynamic, type, elementCnt); return newIsfDynamicComponent(dynamic, type, elementCnt);
} }
Msg.error(this, dynamic.getClass().getSimpleName() + " returned bad replacementBaseType: " Msg.error(this,
+ replacementBaseType.getClass().getSimpleName()); dynamic.getClass().getSimpleName() + " returned bad replacementBaseType: " +
replacementBaseType.getClass().getSimpleName());
} }
} }
return null; return null;
@ -500,7 +527,7 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
IsfObject baseObject = getObjectDataType(IsfUtilities.getBaseDataType(dataType)); IsfObject baseObject = getObjectDataType(IsfUtilities.getBaseDataType(dataType));
return new IsfDataTypeTypeDef(dataType, baseObject); return new IsfDataTypeTypeDef(dataType, baseObject);
} }
if (dataType.getPathName().contains(".conflict")) { if (DataTypeUtilities.isConflictDataType(dataType)) {
if (!deferredKeys.contains(dataType.getPathName())) { if (!deferredKeys.contains(dataType.getPathName())) {
deferredKeys.add(dataType.getPathName()); deferredKeys.add(dataType.getPathName());
} }
@ -513,7 +540,8 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
* *
* @throws CancelledException if the action is cancelled by the user * @throws CancelledException if the action is cancelled by the user
*/ */
protected IsfObject getObjectTypeDef(TypeDef typeDef, TaskMonitor monitor) throws CancelledException { protected IsfObject getObjectTypeDef(TypeDef typeDef, TaskMonitor monitor)
throws CancelledException {
DataType dataType = typeDef.getDataType(); DataType dataType = typeDef.getDataType();
String typedefName = typeDef.getPathName(); String typedefName = typeDef.getPathName();
@ -527,7 +555,8 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
return newTypedefUser(typeDef, isfObject); return newTypedefUser(typeDef, isfObject);
} }
return newTypedefPointer(typeDef); return newTypedefPointer(typeDef);
} catch (Exception e) { }
catch (Exception e) {
Msg.error(this, "TypeDef error: " + e); Msg.error(this, "TypeDef error: " + e);
} }
clearResolve(typedefName, baseType); clearResolve(typedefName, baseType);
@ -544,7 +573,8 @@ public class IsfDataTypeWriter extends AbstractIsfWriter {
return; return;
} }
requestedAddresses.add(address); requestedAddresses.add(address);
} catch (AddressFormatException e) { }
catch (AddressFormatException e) {
throw new IOException("Bad address format: " + key); throw new IOException("Bad address format: " + key);
} }
} }

View file

@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
// Search for the root cause of a datatype conflict based upon a selected datatype. // Search for the root cause of a datatype conflict based upon a selected datatype.
//@category Data Types //@category Data Types
import java.util.*; import java.util.*;
@ -55,8 +56,8 @@ public class FindDataTypeConflictCauseScript extends GhidraScript {
} }
DataType selectedDt = DataTypeUtilities.getBaseDataType(selectedDatatypes.get(0)); DataType selectedDt = DataTypeUtilities.getBaseDataType(selectedDatatypes.get(0));
if (selectedDt instanceof Pointer || selectedDt instanceof Array) { if (selectedDt == null) {
popup("Selected datatype must not be a Pointer or Array"); popup("Selected datatype must not be a default Pointer");
return; return;
} }

View file

@ -190,11 +190,13 @@ public class DataTypeSyncInfo {
if (sourceDt == null) { if (sourceDt == null) {
return true; return true;
} }
if (!DataTypeSynchronizer.namesAreEquivalent(sourceDt, refDt)) { if (!DataTypeSynchronizer.isPointerOrArray(refDt)) {
return true; if (!DataTypeSynchronizer.namesAreEquivalent(sourceDt, refDt)) {
} return true;
if (!StringUtils.equals(refDt.getDescription(), sourceDt.getDescription())) { }
return true; if (!StringUtils.equals(refDt.getDescription(), sourceDt.getDescription())) {
return true;
}
} }
DataType dt = sourceDt.clone(refDt.getDataTypeManager()); DataType dt = sourceDt.clone(refDt.getDataTypeManager());
return !dt.isEquivalent(refDt); return !dt.isEquivalent(refDt);

View file

@ -28,8 +28,7 @@ import ghidra.app.plugin.core.datamgr.archive.DataTypeManagerHandler;
import ghidra.app.util.ToolTipUtils; import ghidra.app.util.ToolTipUtils;
import ghidra.app.util.html.HTMLDataTypeRepresentation; import ghidra.app.util.html.HTMLDataTypeRepresentation;
import ghidra.app.util.html.MissingArchiveDataTypeHTMLRepresentation; import ghidra.app.util.html.MissingArchiveDataTypeHTMLRepresentation;
import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.database.data.*;
import ghidra.program.database.data.ProgramDataTypeManager;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.util.*; import ghidra.util.*;
import ghidra.util.exception.AssertException; import ghidra.util.exception.AssertException;
@ -127,11 +126,13 @@ public class DataTypeSynchronizer {
// not handled by resolve. // not handled by resolve.
long lastChangeTime = refDT.getLastChangeTime(); long lastChangeTime = refDT.getLastChangeTime();
DataType sourceDT = sourceDTM.resolve(refDT, DataTypeConflictHandler.REPLACE_HANDLER); DataType sourceDT = sourceDTM.resolve(refDT, DataTypeConflictHandler.REPLACE_HANDLER);
if (!namesAreEquivalent(refDT, sourceDT)) { if (!isPointerOrArray(refDT)) {
renameDataType(sourceDTM, sourceDT, refDT); if (!namesAreEquivalent(refDT, sourceDT)) {
} renameDataType(sourceDTM, sourceDT, refDT);
if (!StringUtils.equals(refDT.getDescription(), sourceDT.getDescription())) { }
sourceDT.setDescription(refDT.getDescription()); if (!StringUtils.equals(refDT.getDescription(), sourceDT.getDescription())) {
sourceDT.setDescription(refDT.getDescription());
}
} }
sourceDT.setLastChangeTime(lastChangeTime); sourceDT.setLastChangeTime(lastChangeTime);
refDT.setLastChangeTimeInSourceArchive(lastChangeTime); refDT.setLastChangeTimeInSourceArchive(lastChangeTime);
@ -140,11 +141,13 @@ public class DataTypeSynchronizer {
public static void updateAssumingTransactionsOpen(DataTypeManager refDTM, DataType sourceDT) { public static void updateAssumingTransactionsOpen(DataTypeManager refDTM, DataType sourceDT) {
long lastChangeTime = sourceDT.getLastChangeTime(); long lastChangeTime = sourceDT.getLastChangeTime();
DataType refDT = refDTM.resolve(sourceDT, DataTypeConflictHandler.REPLACE_HANDLER); DataType refDT = refDTM.resolve(sourceDT, DataTypeConflictHandler.REPLACE_HANDLER);
if (!namesAreEquivalent(refDT, sourceDT)) { if (!isPointerOrArray(sourceDT)) {
renameDataType(refDTM, refDT, sourceDT); if (!namesAreEquivalent(refDT, sourceDT)) {
} renameDataType(refDTM, refDT, sourceDT);
if (!StringUtils.equals(sourceDT.getDescription(), refDT.getDescription())) { }
refDT.setDescription(sourceDT.getDescription()); if (!StringUtils.equals(sourceDT.getDescription(), refDT.getDescription())) {
refDT.setDescription(sourceDT.getDescription());
}
} }
refDT.setLastChangeTimeInSourceArchive(lastChangeTime); refDT.setLastChangeTimeInSourceArchive(lastChangeTime);
refDT.setLastChangeTime(lastChangeTime); refDT.setLastChangeTime(lastChangeTime);
@ -252,14 +255,10 @@ public class DataTypeSynchronizer {
} }
} }
String name = dtToCopy.getName(); String name = dtToCopy.getName();
int index = name.indexOf(DataType.CONFLICT_SUFFIX);
if (index > 0) {
name = name.substring(0, index);
}
CategoryPath path = sourceDT.getCategoryPath(); CategoryPath path = sourceDT.getCategoryPath();
if (sourceDTM.getDataType(path, name) != null) { if (sourceDTM.getDataType(path, name) != null) {
name = ((DataTypeManagerDB) sourceDTM).getUnusedConflictName(sourceDT.getCategoryPath(), name = ((DataTypeManagerDB) sourceDTM).getUnusedConflictName(sourceDT.getCategoryPath(),
name); dtToCopy);
} }
try { try {
sourceDT.setName(name); sourceDT.setName(name);
@ -282,27 +281,19 @@ public class DataTypeSynchronizer {
return false; return false;
} }
public static boolean namesAreEquivalent(DataType dt1, DataType dt2) { static boolean isPointerOrArray(DataType dt) {
return (dt instanceof Pointer) || (dt instanceof Array);
}
static boolean namesAreEquivalent(DataType dt1, DataType dt2) {
if (isAutoNamedTypedef(dt1)) { if (isAutoNamedTypedef(dt1)) {
return isAutoNamedTypedef(dt2); return isAutoNamedTypedef(dt2);
} }
else if (isAutoNamedTypedef(dt2)) { else if (isAutoNamedTypedef(dt2)) {
return false; return false;
} }
String name1 = dt1.getName(); return DataTypeUtilities.getNameWithoutConflict(dt1)
String name2 = dt2.getName(); .equals(DataTypeUtilities.getNameWithoutConflict(dt2));
if (name1.equals(name2)) {
return true;
}
int index = name1.indexOf(DataType.CONFLICT_SUFFIX);
if (index > 0) {
name1 = name1.substring(0, index);
}
index = name2.indexOf(DataType.CONFLICT_SUFFIX);
if (index > 0) {
name2 = name2.substring(0, index);
}
return name1.equals(name2);
} }

View file

@ -15,6 +15,7 @@
*/ */
package ghidra.app.util; package ghidra.app.util;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.FunctionSignature; import ghidra.program.model.listing.FunctionSignature;
@ -53,9 +54,9 @@ public class DataTypeNamingUtil {
} }
sb.append("_"); sb.append("_");
sb.append(mangleDTName(returnType.getName())); sb.append(mangleDTName(returnType));
for (ParameterDefinition p : parameters) { for (ParameterDefinition p : parameters) {
sb.append("_").append(mangleDTName(p.getDataType().getName())); sb.append("_").append(mangleDTName(p.getDataType()));
} }
if (functionDefinition.hasVarArgs()) { if (functionDefinition.hasVarArgs()) {
@ -72,8 +73,9 @@ public class DataTypeNamingUtil {
return name; return name;
} }
private static String mangleDTName(String s) { private static String mangleDTName(DataType dt) {
return s.replaceAll(" ", "_").replaceAll("\\*", "ptr"); String name = DataTypeUtilities.getNameWithoutConflict(dt);
return name.replaceAll(" ", "_").replaceAll("\\*", "ptr");
} }
} }

View file

@ -25,14 +25,14 @@ class LibraryExportedSymbol {
private int memsize; private int memsize;
private int ordinal = -1; private int ordinal = -1;
private String symbolName; private String symbolName;
private String fowardLibraryName; private String forwardLibraryName;
private String fowardSymbolName; private String forwardSymbolName;
private int purge; private int purge;
private boolean noReturn; private boolean noReturn;
private String comment; private String comment;
LibraryExportedSymbol(String libName, int memsize, int ordinal, String symbolName, LibraryExportedSymbol(String libName, int memsize, int ordinal, String symbolName,
String fowardLibraryName, String fowardSymbolName, int purge, boolean noReturn, String forwardLibraryName, String forwardSymbolName, int purge, boolean noReturn,
String comment) { String comment) {
this.libName = libName; this.libName = libName;
this.memsize = memsize; this.memsize = memsize;
@ -41,8 +41,8 @@ class LibraryExportedSymbol {
this.purge = purge; this.purge = purge;
this.noReturn = noReturn; this.noReturn = noReturn;
this.comment = comment; this.comment = comment;
this.fowardLibraryName = fowardLibraryName; this.forwardLibraryName = forwardLibraryName;
this.fowardSymbolName = fowardSymbolName; this.forwardSymbolName = forwardSymbolName;
} }
/** /**
@ -125,12 +125,12 @@ class LibraryExportedSymbol {
private synchronized void processForwardedEntry() { private synchronized void processForwardedEntry() {
purge = -2; purge = -2;
LibrarySymbolTable lib = LibraryLookupTable.getSymbolTable(fowardLibraryName, memsize); LibrarySymbolTable lib = LibraryLookupTable.getSymbolTable(forwardLibraryName, memsize);
if (lib == null) { if (lib == null) {
return; return;
} }
LibraryExportedSymbol libSym = lib.getSymbol(fowardSymbolName); LibraryExportedSymbol libSym = lib.getSymbol(forwardSymbolName);
if (libSym == null) { if (libSym == null) {
return; return;
} }
@ -151,24 +151,24 @@ class LibraryExportedSymbol {
} }
/** /**
* @return true if this symbol is fowarded to another library * @return true if this symbol is forwarded to another library
*/ */
boolean isFowardEntry() { boolean isFowardEntry() {
return fowardLibraryName != null; return forwardLibraryName != null;
} }
/** /**
* @return the fowarded library name * @return the forwarded library name
*/ */
String getFowardLibraryName() { String getFowardLibraryName() {
return fowardLibraryName; return forwardLibraryName;
} }
/** /**
* @return the fowarded symbol name * @return the forwarded symbol name
*/ */
String getFowardSymbolName() { String getFowardSymbolName() {
return fowardSymbolName; return forwardSymbolName;
} }
void setName(String name) { void setName(String name) {

View file

@ -56,8 +56,8 @@ class LibrarySymbolTable {
private String date; private String date;
private String version; private String version;
private int tempPurge; private int tempPurge;
private String fowardLibrary = null; private String forwardLibrary = null;
private String fowardSymbol = null; private String forwardSymbol = null;
private HashMap<String, LibraryExportedSymbol> symMap = new HashMap<>(); private HashMap<String, LibraryExportedSymbol> symMap = new HashMap<>();
private ArrayList<LibraryExportedSymbol> exportList = new ArrayList<>(); private ArrayList<LibraryExportedSymbol> exportList = new ArrayList<>();
private HashMap<Integer, LibraryExportedSymbol> ordMap = new HashMap<>(); private HashMap<Integer, LibraryExportedSymbol> ordMap = new HashMap<>();
@ -140,8 +140,8 @@ class LibrarySymbolTable {
} }
} }
fowardLibrary = null; forwardLibrary = null;
fowardSymbol = null; forwardSymbol = null;
tempPurge = -1; tempPurge = -1;
String comment = ""; String comment = "";
@ -160,12 +160,12 @@ class LibrarySymbolTable {
Reference[] refs = library.getReferenceManager().getReferencesFrom(symAddr); Reference[] refs = library.getReferenceManager().getReferencesFrom(symAddr);
if (refs != null && refs.length > 0 && refs[0].isExternalReference()) { if (refs != null && refs.length > 0 && refs[0].isExternalReference()) {
ExternalReference exRef = (ExternalReference) refs[0]; ExternalReference exRef = (ExternalReference) refs[0];
fowardLibrary = exRef.getLibraryName(); forwardLibrary = exRef.getLibraryName();
fowardSymbol = exRef.getLabel(); forwardSymbol = exRef.getLabel();
} }
} }
if (fowardLibrary == null || fowardLibrary.length() <= 0) { if (forwardLibrary == null || forwardLibrary.length() <= 0) {
MemoryBlock block = library.getMemory().getBlock(symAddr); MemoryBlock block = library.getMemory().getBlock(symAddr);
if (block != null && block.isExecute()) { if (block != null && block.isExecute()) {
pseudoDisassemble(library, symAddr); pseudoDisassemble(library, symAddr);
@ -179,12 +179,12 @@ class LibrarySymbolTable {
noReturn = true; noReturn = true;
} }
if (fowardLibrary != null && fowardLibrary.length() > 0) { if (forwardLibrary != null && forwardLibrary.length() > 0) {
forwards.add(fowardLibrary); forwards.add(forwardLibrary);
} }
LibraryExportedSymbol expSym = new LibraryExportedSymbol(tableName, size, ordinal, LibraryExportedSymbol expSym = new LibraryExportedSymbol(tableName, size, ordinal,
realName, fowardLibrary, fowardSymbol, tempPurge, noReturn, comment); realName, forwardLibrary, forwardSymbol, tempPurge, noReturn, comment);
// add to export list in order // add to export list in order
exportList.add(expSym); exportList.add(expSym);
@ -225,8 +225,8 @@ class LibrarySymbolTable {
Scalar scalar = instr.getScalar(0); Scalar scalar = instr.getScalar(0);
if (scalar != null) { if (scalar != null) {
tempPurge = (int) scalar.getSignedValue(); tempPurge = (int) scalar.getSignedValue();
fowardLibrary = null; forwardLibrary = null;
fowardSymbol = null; forwardSymbol = null;
return false; return false;
} }
} }
@ -234,15 +234,17 @@ class LibrarySymbolTable {
if (ftype.isJump() && ftype.isComputed()) { if (ftype.isJump() && ftype.isComputed()) {
Reference[] refs = instr.getReferencesFrom(); Reference[] refs = instr.getReferencesFrom();
if (refs.length > 0) { if (refs.length > 0) {
Data data = instr.getProgram().getListing().getDefinedDataAt( Data data = instr.getProgram()
refs[0].getToAddress()); .getListing()
.getDefinedDataAt(refs[0].getToAddress());
if (data != null) { if (data != null) {
refs = instr.getProgram().getReferenceManager().getReferencesFrom( refs = instr.getProgram()
data.getMinAddress()); .getReferenceManager()
.getReferencesFrom(data.getMinAddress());
if (refs != null && refs.length > 0 && refs[0].isExternalReference()) { if (refs != null && refs.length > 0 && refs[0].isExternalReference()) {
ExternalReference exRef = (ExternalReference) refs[0]; ExternalReference exRef = (ExternalReference) refs[0];
fowardLibrary = exRef.getLibraryName(); forwardLibrary = exRef.getLibraryName();
fowardSymbol = exRef.getLabel(); forwardSymbol = exRef.getLabel();
} }
} }
} }
@ -425,26 +427,24 @@ class LibrarySymbolTable {
version = root.getAttributeValue("VERSION"); version = root.getAttributeValue("VERSION");
List<Element> children = CollectionUtils.asList(root.getChildren(), Element.class); List<Element> children = CollectionUtils.asList(root.getChildren(), Element.class);
Iterator<Element> iter = children.iterator(); for (Element export : children) {
while (iter.hasNext()) {
Element export = iter.next();
int ordinal = Integer.parseInt(export.getAttributeValue("ORDINAL")); int ordinal = Integer.parseInt(export.getAttributeValue("ORDINAL"));
String name = export.getAttributeValue("NAME"); String name = export.getAttributeValue("NAME");
int purge = Integer.parseInt(export.getAttributeValue("PURGE")); int purge = Integer.parseInt(export.getAttributeValue("PURGE"));
String comment = export.getAttributeValue("COMMENT"); String comment = export.getAttributeValue("COMMENT");
String fowardLibName = export.getAttributeValue("FOWARDLIBRARY"); String forwardLibName = export.getAttributeValue("FOWARDLIBRARY");
String fowardSymName = export.getAttributeValue("FOWARDSYMBOL"); String forwardSymName = export.getAttributeValue("FOWARDSYMBOL");
String noReturnStr = export.getAttributeValue("NO_RETURN"); String noReturnStr = export.getAttributeValue("NO_RETURN");
boolean noReturn = noReturnStr != null && "y".equals(noReturnStr); boolean noReturn = noReturnStr != null && "y".equals(noReturnStr);
if (fowardLibName != null && fowardLibName.length() > 0 && if (forwardLibName != null && forwardLibName.length() > 0 &&
!fowardLibName.equals(tableName)) { !forwardLibName.equals(tableName)) {
forwards.add(fowardLibName); forwards.add(forwardLibName);
} }
LibraryExportedSymbol sym = new LibraryExportedSymbol(tableName, size, ordinal, LibraryExportedSymbol sym = new LibraryExportedSymbol(tableName, size, ordinal,
name, fowardLibName, fowardSymName, purge, noReturn, comment); name, forwardLibName, forwardSymName, purge, noReturn, comment);
exportList.add(sym); exportList.add(sym);
symMap.put(name, sym); symMap.put(name, sym);
@ -494,10 +494,7 @@ class LibrarySymbolTable {
root.setAttribute("DATE", TIMESTAMP_FORMAT.format(new Date(lastModifiedSeconds))); root.setAttribute("DATE", TIMESTAMP_FORMAT.format(new Date(lastModifiedSeconds)));
root.setAttribute("VERSION", lversion); root.setAttribute("VERSION", lversion);
Iterator<LibraryExportedSymbol> iter = exportList.iterator(); for (LibraryExportedSymbol sym : exportList) {
while (iter.hasNext()) {
LibraryExportedSymbol sym = iter.next();
Element export = new Element("EXPORT"); Element export = new Element("EXPORT");
export.setAttribute("ORDINAL", sym.getOrdinal() + ""); export.setAttribute("ORDINAL", sym.getOrdinal() + "");

View file

@ -218,18 +218,6 @@ class CategoryDB extends DatabaseObject implements Category {
return map; return map;
} }
private String getBaseName(String dataTypeName) {
int indexOf = dataTypeName.indexOf(DataType.CONFLICT_SUFFIX);
if (indexOf <= 0) {
return dataTypeName;
}
return dataTypeName.substring(0, indexOf);
}
private boolean isConflictName(String dataTypeName) {
return dataTypeName.contains(DataType.CONFLICT_SUFFIX);
}
/** /**
* @see ghidra.program.model.data.Category#getCategories() * @see ghidra.program.model.data.Category#getCategories()
*/ */
@ -549,7 +537,7 @@ class CategoryDB extends DatabaseObject implements Category {
} }
else { // both dataTypes remain else { // both dataTypes remain
movedDataType.setNameAndCategory(path, movedDataType.setNameAndCategory(path,
mgr.getUnusedConflictName(path, movedDataType.getName())); mgr.getUnusedConflictName(path, movedDataType));
} }
} }
else { else {
@ -621,14 +609,14 @@ class CategoryDB extends DatabaseObject implements Category {
void dataTypeAdded(DataType dataType) { void dataTypeAdded(DataType dataType) {
String dtName = dataType.getName(); String dtName = dataType.getName();
dataTypeMap.put(dtName, dataType); dataTypeMap.put(dtName, dataType);
if (isConflictName(dtName)) { if (DataTypeUtilities.isConflictDataType(dataType)) {
conflictMap.addDataType(dataType); conflictMap.addDataType(dataType);
} }
} }
void dataTypeRemoved(String dataTypeName) { void dataTypeRemoved(String dataTypeName) {
dataTypeMap.remove(dataTypeName); dataTypeMap.remove(dataTypeName);
if (isConflictName(dataTypeName)) { if (DataTypeUtilities.isConflictDataTypeName(dataTypeName)) {
conflictMap.removeDataTypeName(dataTypeName); conflictMap.removeDataTypeName(dataTypeName);
} }
} }
@ -639,8 +627,9 @@ class CategoryDB extends DatabaseObject implements Category {
@Override @Override
public List<DataType> getDataTypesByBaseName(String dataTypeName) { public List<DataType> getDataTypesByBaseName(String dataTypeName) {
List<DataType> list = new ArrayList<>(); List<DataType> list = new ArrayList<>();
String baseName = getBaseName(dataTypeName); String baseName = DataTypeUtilities.getNameWithoutConflict(dataTypeName);
DataType baseType = dataTypeMap.get(baseName); DataType baseType = dataTypeMap.get(baseName);
if (baseType != null) { if (baseType != null) {
@ -681,8 +670,8 @@ class CategoryDB extends DatabaseObject implements Category {
Collection<DataType> values = dataTypeMap.values(); Collection<DataType> values = dataTypeMap.values();
for (DataType dataType : values) { for (DataType dataType : values) {
String dataTypeName = dataType.getName(); String dataTypeName = dataType.getName();
if (isConflictName(dataTypeName)) { if (DataTypeUtilities.isConflictDataType(dataType)) {
String baseName = getBaseName(dataTypeName); String baseName = DataTypeUtilities.getNameWithoutConflict(dataType);
Map<String, DataType> innerMap = Map<String, DataType> innerMap =
map.computeIfAbsent(baseName, b -> new HashMap<>()); map.computeIfAbsent(baseName, b -> new HashMap<>());
innerMap.put(dataTypeName, dataType); innerMap.put(dataTypeName, dataType);
@ -705,7 +694,7 @@ class CategoryDB extends DatabaseObject implements Category {
} }
String dataTypeName = dataType.getName(); String dataTypeName = dataType.getName();
String baseName = getBaseName(dataTypeName); String baseName = DataTypeUtilities.getNameWithoutConflict(dataType);
Map<String, DataType> innerMap = map.computeIfAbsent(baseName, b -> new HashMap<>()); Map<String, DataType> innerMap = map.computeIfAbsent(baseName, b -> new HashMap<>());
innerMap.put(dataTypeName, dataType); innerMap.put(dataTypeName, dataType);
} }
@ -721,7 +710,7 @@ class CategoryDB extends DatabaseObject implements Category {
if (map == null) { if (map == null) {
return; return;
} }
String baseName = getBaseName(dataTypeName); String baseName = DataTypeUtilities.getNameWithoutConflict(dataTypeName);
Map<String, DataType> innerMap = map.get(baseName); Map<String, DataType> innerMap = map.get(baseName);
if (innerMap == null) { if (innerMap == null) {
return; return;

View file

@ -392,7 +392,7 @@ abstract class DataTypeDB extends DatabaseObject implements DataType {
// generate a name that would not cause a duplicate in either the current path // generate a name that would not cause a duplicate in either the current path
// or // or
// the new path. Use the new name if possible. // the new path. Use the new name if possible.
String uniqueName = dataMgr.getUniqueName(path, getCategoryPath(), name); String uniqueName = dataMgr.getTemporaryUniqueName(path, getCategoryPath(), name);
doSetName(uniqueName); doSetName(uniqueName);
// set the path - this is guaranteed to work since we make a name that won't // set the path - this is guaranteed to work since we make a name that won't

View file

@ -1091,11 +1091,6 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
} }
} }
ConflictResult resolveConflict(DataTypeConflictHandler handler, DataType addedDataType,
DataType existingDataType) {
return handler.resolveConflict(addedDataType, existingDataType);
}
@Override @Override
public String getUniqueName(CategoryPath path, String baseName) { public String getUniqueName(CategoryPath path, String baseName) {
int pos = baseName.lastIndexOf('_'); int pos = baseName.lastIndexOf('_');
@ -1119,7 +1114,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
return name; return name;
} }
String getUniqueName(CategoryPath path1, CategoryPath path2, String baseName) { String getTemporaryUniqueName(CategoryPath newCategoryPath, CategoryPath currentCategoryPath,
String baseName) {
int pos = baseName.lastIndexOf('_'); int pos = baseName.lastIndexOf('_');
int oneUpNumber = 0; int oneUpNumber = 0;
String name = baseName; String name = baseName;
@ -1134,7 +1130,8 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
// the number will get updated below // the number will get updated below
} }
} }
while (getDataType(path1, name) != null || getDataType(path2, name) != null) { while (getDataType(newCategoryPath, name) != null ||
getDataType(currentCategoryPath, name) != null) {
++oneUpNumber; ++oneUpNumber;
name = baseName + "_" + oneUpNumber; name = baseName + "_" + oneUpNumber;
} }
@ -1428,47 +1425,47 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
/** /**
* This method gets a ".conflict" name that is not currently used by any data * This method gets a ".conflict" name that is not currently used by any data
* types in the indicated category of the data type manager. * types in the datatype's category within this data type manager. If the baseName without
* conflict suffix is not used that name will be returned.
* <br>
* NOTE: The original datatype name will be returned unchanged for pointers and arrays since
* they cannot be renamed.
*
* @param dt datatype who name is used to establish non-conflict base name * @param dt datatype who name is used to establish non-conflict base name
* @return the unused conflict name or original name for datatypes whose name is automatic * @return the unused conflict name or original name for datatypes whose name is automatic
*/ */
public String getUnusedConflictName(DataType dt) { public String getUnusedConflictName(DataType dt) {
return getUnusedConflictName(dt.getCategoryPath(), dt);
}
/**
* This method gets a ".conflict" name that is not currently used by any data
* types in the indicated category within this data type manager. If the baseName without
* conflict suffix is not used that name will be returned.
* <br>
* NOTE: The original datatype name will be returned unchanged for pointers and arrays since
* they cannot be renamed.
*
* @param path the category path of the category where the new data type live in
* the data type manager.
* @param dt datatype who name is used to establish non-conflict base name
* @return the unused conflict name
*/
public String getUnusedConflictName(CategoryPath path, DataType dt) {
String name = dt.getName(); String name = dt.getName();
if ((dt instanceof Array) || (dt instanceof Pointer) || (dt instanceof BuiltInDataType)) { if ((dt instanceof Array) || (dt instanceof Pointer) || (dt instanceof BuiltInDataType)) {
// name not used - anything will do // name not used - anything will do
return name; return name;
} }
return getUnusedConflictName(dt.getCategoryPath(), name);
}
/** String baseName = DataTypeUtilities.getNameWithoutConflict(dt);
* This method gets a ".conflict" name that is not currently used by any data String testName = baseName;
* types in the indicated category of the data type manager.
*
* @param path the category path of the category where the new data type live in
* the data type manager.
* @param name The name of the data type. This name may or may not contain
* ".conflict" as part of it. If the name contains ".conflict", only
* the part of the name that comes prior to the ".conflict" will be
* used to determine a new unused conflict name.
* @return the unused conflict name
*/
public String getUnusedConflictName(CategoryPath path, String name) {
int index = name.indexOf(DataType.CONFLICT_SUFFIX);
if (index > 0) {
name = name.substring(0, index);
}
// Name sequence: <baseName>, <baseName>.conflict, <basename>.conflict1, ...
String baseName = name + DataType.CONFLICT_SUFFIX;
String testName = name;
int count = 0; int count = 0;
while (getDataType(path, testName) != null) { while (getDataType(path, testName) != null) {
String countSuffix = ""; testName = baseName + DataType.CONFLICT_SUFFIX;
if (count != 0) { if (count > 0) {
countSuffix = Integer.toString(count); testName += Integer.toString(count);
} }
testName = baseName + countSuffix;
++count; ++count;
} }
return testName; return testName;
@ -4394,7 +4391,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
if (dataType instanceof Pointer || dataType instanceof Array) { if (dataType instanceof Pointer || dataType instanceof Array) {
dataType = DataTypeUtilities.getBaseDataType(dataType); dataType = DataTypeUtilities.getBaseDataType(dataType);
} }
if (!contains(dataType)) { if (dataType == null || !contains(dataType)) {
return false; return false;
} }
List<DataType> relatedByName = findDataTypesSameLocation(dataType); List<DataType> relatedByName = findDataTypesSameLocation(dataType);
@ -4508,7 +4505,7 @@ abstract public class DataTypeManagerDB implements DataTypeManager {
if (dt instanceof Pointer || dt instanceof Array) { if (dt instanceof Pointer || dt instanceof Array) {
continue; continue;
} }
boolean isConflict = dt.getName().contains(DataType.CONFLICT_SUFFIX); boolean isConflict = DataTypeUtilities.isConflictDataType(dt);
String name = DataTypeUtilities.getNameWithoutConflict(dt, false); String name = DataTypeUtilities.getNameWithoutConflict(dt, false);
if (!name.equals(lastBaseName)) { if (!name.equals(lastBaseName)) {
// base name changed // base name changed

View file

@ -62,8 +62,19 @@ public class DataTypeUtilities {
cPrimitiveNameMap.put("long double", LongDoubleDataType.dataType); cPrimitiveNameMap.put("long double", LongDoubleDataType.dataType);
} }
private static final Pattern DATATYPE_CONFLICT_PATTERN = // TODO: Should we drop the handling of "_" use in conflict name. It's unclear
Pattern.compile(Pattern.quote(DataType.CONFLICT_SUFFIX) + "_?[0-9]*"); // when/if this was ever used in the generation of a conflict name.
// NOTE: It is assumed that all BuiltInDataType types (other then possibly Pointers)
// will not utilize conflict names. This includes pointers and arrays whose base
// type is a BuiltInDataType.
// NOTE: The BASE_DATATYPE_CONFLICT_PATTERN may never be applied to a pointer
// or array name as it will always fail to match. Pointer and array decorations
// must be stripped off first.
private static final Pattern BASE_DATATYPE_CONFLICT_PATTERN =
Pattern.compile(Pattern.quote(DataType.CONFLICT_SUFFIX) + "([_]{0,1}\\d+){0,1}$");
private static final Pattern DATATYPE_POINTER_ARRAY_PATTERN =
Pattern.compile("(( \\*\\d*)|(\\[\\d+\\]))+$");
public static Collection<DataType> getContainedDataTypes(DataType rootDataType) { public static Collection<DataType> getContainedDataTypes(DataType rootDataType) {
HashMap<String, DataType> dataTypeMap = new HashMap<>(); HashMap<String, DataType> dataTypeMap = new HashMap<>();
@ -296,81 +307,12 @@ public class DataTypeUtilities {
return dataType1.getClass().equals(dataType2.getClass()); return dataType1.getClass().equals(dataType2.getClass());
} }
/**
* Get the name of a data type with all conflict naming patterns removed.
*
* @param dataType data type
* @param includeCategoryPath if true the category path will be included with the
* returned name (e.g., /mypath/mydt)
* @return name with optional category path included
*/
public static String getNameWithoutConflict(DataType dataType, boolean includeCategoryPath) {
String name = includeCategoryPath ? dataType.getPathName() : dataType.getName();
return getNameWithoutConflict(name);
}
/**
* Get the name of a data type with all conflict naming patterns removed.
*
* @param dataTypeName data type name with optional category path included
* @return name with optional category path included
*/
public static String getNameWithoutConflict(String dataTypeName) {
return DATATYPE_CONFLICT_PATTERN.matcher(dataTypeName).replaceAll("");
}
/**
* Get the conflict value string associated with a conflict datatype name.
*
* @param dataType datatype to be checked
* @return conflict value string. Will be null if name is not a conflict name, or
* empty string if conflict has no number. Otherwise a decimal value string will be returned.
*/
public static String getConflictString(DataType dataType) {
return getConflictString(dataType.getName());
}
/**
* Get the conflict value string associated with a conflict datatype name.
*
* @param dataTypeName datatype name to be checked
* @return conflict value string. Will be one of the following:
* <ol>
* <li>A null value if not a conflict name,</li>
* <li>an empty string if conflict name without a number, or</li>
* <li>a decimal string value which corresponds to the conflict number in the name.</li>
* </ol>
*/
public static String getConflictString(String dataTypeName) {
Matcher matcher = DATATYPE_CONFLICT_PATTERN.matcher(dataTypeName);
if (matcher.find()) {
MatchResult matchResult = matcher.toMatchResult();
return dataTypeName.substring(matchResult.start() + DataType.CONFLICT_SUFFIX.length(),
matchResult.end());
}
return null;
}
/**
* Compares two data type name strings to determine if they are equivalent names, ignoring
* conflict patterns present.
*
* @param name1 the first name
* @param name2 the second name
* @return true if the names are equivalent when conflict suffixes are ignored.
*/
public static boolean equalsIgnoreConflict(String name1, String name2) {
name1 = DATATYPE_CONFLICT_PATTERN.matcher(name1).replaceAll("");
name2 = DATATYPE_CONFLICT_PATTERN.matcher(name2).replaceAll("");
return name1.equals(name2);
}
/** /**
* Get the base data type for the specified data type stripping away pointers and arrays only. A * Get the base data type for the specified data type stripping away pointers and arrays only. A
* null will be returned for a default pointer. * null will be returned for a default pointer.
* *
* @param dt the data type whose base data type is to be determined. * @param dt the data type whose base data type is to be determined.
* @return the base data type. * @return the base data type (may be null for default pointer).
*/ */
public static DataType getBaseDataType(DataType dt) { public static DataType getBaseDataType(DataType dt) {
DataType baseDataType = dt; DataType baseDataType = dt;
@ -840,4 +782,221 @@ public class DataTypeUtilities {
} }
return null; return null;
} }
//
// Conflict naming utilities
//
private static boolean canHaveConflictName(DataType dt) {
if (dt == null) {
return false; // e.g., base type for default pointer
}
return !(dt instanceof BuiltIn) || (dt instanceof Pointer);
}
private static String getPointerArrayDecorations(String dataTypeName) {
// Use of this preliminary check greatly speeds-up the check for cases
// not involving a pointer or array
if (!dataTypeName.contains("*") && !dataTypeName.contains("[")) {
return null;
}
// Return the trailing pointer and array decorations if they exist
Matcher matcher = DATATYPE_POINTER_ARRAY_PATTERN.matcher(dataTypeName);
if (matcher.find()) {
MatchResult matchResult = matcher.toMatchResult();
return dataTypeName.substring(matchResult.start());
}
return null;
}
/**
* Get the name of a data type with all conflict naming patterns removed.
*
* @param dataType data type
* @param includeCategoryPath if true, the category path will be included with the
* returned name (e.g., /mypath/mydt) and any occurance of a forward slash within individual
* path components, including the data type name, will be escaped (e.g., {@code "\/"}).
* @return name with optional category path included
*/
public static String getNameWithoutConflict(DataType dataType, boolean includeCategoryPath) {
String name = getNameWithoutConflict(dataType);
if (includeCategoryPath) {
name = dataType.getCategoryPath().getPath(name);
}
return name;
}
/**
* Get the name of a data type with all conflict naming patterns removed.
*
* @param dataTypeName data type name with optional category path included
* @return name with optional category path included
*/
public static String getNameWithoutConflict(String dataTypeName) {
String decorations = getPointerArrayDecorations(dataTypeName);
String baseDataTypeName = dataTypeName;
if (decorations != null) {
baseDataTypeName =
dataTypeName.substring(0, dataTypeName.length() - decorations.length());
}
String name = BASE_DATATYPE_CONFLICT_PATTERN.matcher(baseDataTypeName).replaceAll("");
if (decorations != null) {
name += decorations;
}
return name;
}
/**
* Get a datatype's name without conflict suffix.
*
* @param dt datatype (pointer and array permitted)
* @return datatype's name without conflict suffix
*/
public static String getNameWithoutConflict(DataType dt) {
String dtName = dt.getName();
if (!canHaveConflictName(dt)) {
return dtName; // e.g., many BuiltIn types
}
DataType baseDataType = getBaseDataType(dt);
if (baseDataType == null) {
return dtName; // e.g., default pointer
}
if (baseDataType != dt && !canHaveConflictName(baseDataType)) {
return dtName; // e.g., pointer to BuiltIn
}
if (baseDataType == dt) {
// Non-pointer/array case
return BASE_DATATYPE_CONFLICT_PATTERN.matcher(dtName).replaceAll("");
}
// Isolate pointer/array decorations
// NOTE: This is faster than using regex to isolate pointer and array decorations
String baseDtName = baseDataType.getName();
String decorations = dtName.substring(baseDtName.length());
// remove conflict suffix
return BASE_DATATYPE_CONFLICT_PATTERN.matcher(baseDtName).replaceAll("") + decorations;
}
private static int getBaseConflictValue(String baseDataTypeName) {
Matcher matcher = BASE_DATATYPE_CONFLICT_PATTERN.matcher(baseDataTypeName);
if (matcher.find()) {
MatchResult matchResult = matcher.toMatchResult();
int startIx = matchResult.start() + DataType.CONFLICT_SUFFIX.length();
if (startIx < baseDataTypeName.length() && baseDataTypeName.charAt(startIx) == '_') {
++startIx;
}
String valueStr = baseDataTypeName.substring(startIx);
if (valueStr.length() == 0) {
return 0;
}
try {
return Integer.parseInt(valueStr);
}
catch (NumberFormatException e) {
return -1;
}
}
return -1;
}
/**
* Get the conflict value string associated with a conflict datatype name.
*
* @param dataType datatype to be checked
* @return conflict value:
* <ol>
* <li>-1: when type does not have a conflict name,</li>
* <li>0: when conflict name does not have a number (i.e., {@code .conflict}), or</li>
* <li>a positive value which corresponds to the conflict number in the name
* (e.g., returns 2 for {@code .conflict2}).</li>
* </ol>
*/
public static int getConflictValue(DataType dataType) {
if (!canHaveConflictName(dataType)) {
return -1; // e.g., many BuiltIn types
}
DataType baseDataType = getBaseDataType(dataType);
if (baseDataType == null) {
return -1; // e.g., default pointer
}
if (baseDataType != dataType && !canHaveConflictName(baseDataType)) {
return -1; // e.g., pointer to BuiltIn
}
return getBaseConflictValue(baseDataType.getName());
}
/**
* Get the conflict value associated with a conflict datatype name.
*
* @param dataTypeName datatype name to be checked
* @return conflict value:
* <ol>
* <li>-1: when name is not have a conflict name,</li>
* <li>0: when conflict name does not have a number (i.e., {@code .conflict}), or</li>
* <li>a positive value which corresponds to the conflict number in the name
* (e.g., returns 2 for {@code .conflict2}).</li>
* </ol>
*/
public static int getConflictValue(String dataTypeName) {
String decorations = getPointerArrayDecorations(dataTypeName);
String baseDataTypeName = dataTypeName;
if (decorations != null) {
baseDataTypeName =
dataTypeName.substring(0, dataTypeName.length() - decorations.length());
}
return getBaseConflictValue(baseDataTypeName);
}
/**
* Determine if the specified data type name is a conflict name.
*
* @param dataTypeName datatype name
* @return true if data type name is a conflict name.
*/
public static boolean isConflictDataTypeName(String dataTypeName) {
String decorations = getPointerArrayDecorations(dataTypeName);
String baseDataTypeName = dataTypeName;
if (decorations != null) {
baseDataTypeName =
dataTypeName.substring(0, dataTypeName.length() - decorations.length());
}
Matcher matcher = BASE_DATATYPE_CONFLICT_PATTERN.matcher(baseDataTypeName);
return matcher.find();
}
/**
* Determine if the specified data type has a conflict name.
* @param dt datatype (pointer and array permitted)
* @return true if data type has a conflict name.
*/
public static boolean isConflictDataType(DataType dt) {
if (!canHaveConflictName(dt)) {
return false; // e.g., many BuiltIn types
}
DataType baseDataType = getBaseDataType(dt);
if (baseDataType == null) {
return false; // e.g., default pointer
}
if (baseDataType != dt && !canHaveConflictName(baseDataType)) {
return false; // e.g., pointer to BuiltIn
}
Matcher matcher = BASE_DATATYPE_CONFLICT_PATTERN.matcher(dt.getName());
return matcher.find();
}
/**
* Compares two data type name strings to determine if they are equivalent names, ignoring
* conflict patterns present.
*
* @param name1 the first name
* @param name2 the second name
* @return true if the names are equivalent when conflict suffixes are ignored.
*/
public static boolean equalsIgnoreConflict(String name1, String name2) {
return getNameWithoutConflict(name1).equals(getNameWithoutConflict(name2));
}
} }

View file

@ -618,24 +618,22 @@ class TypedefDB extends DataTypeDB implements TypeDef {
} }
private String generateTypedefName(CategoryPath path) { private String generateTypedefName(CategoryPath path) {
String newName = TypedefDataType.generateTypedefName(this); String baseName = TypedefDataType.generateTypedefName(this);
DataType dt = dataMgr.getDataType(path, newName); String testName = baseName;
if (dt == null || dt == this) {
return newName;
}
String baseName = newName + DataType.CONFLICT_SUFFIX;
newName = baseName;
int count = 0; int count = 0;
while (true) { while (!isNameUnusedOrMine(path, testName)) {
dt = dataMgr.getDataType(path, newName); testName = baseName + DataType.CONFLICT_SUFFIX;
if (dt == null || dt == this) { if (count > 0) {
break; testName += Integer.toString(count);
} }
count++; ++count;
newName = baseName + count;
} }
return newName; return testName;
}
private boolean isNameUnusedOrMine(CategoryPath path, String newName) {
DataType dt = dataMgr.getDataType(path, newName);
return dt == null || dt == this;
} }
boolean updateAutoName(boolean notify) { boolean updateAutoName(boolean notify) {

View file

@ -563,8 +563,8 @@ public class FunctionManagerDB implements FunctionManager {
} }
@Override @Override
public FunctionIterator getFunctions(Address start, boolean foward) { public FunctionIterator getFunctions(Address start, boolean forward) {
return new FunctionIteratorDB(start, foward); return new FunctionIteratorDB(start, forward);
} }
@Override @Override
@ -585,8 +585,8 @@ public class FunctionManagerDB implements FunctionManager {
} }
@Override @Override
public FunctionIterator getFunctionsNoStubs(Address start, boolean foward) { public FunctionIterator getFunctionsNoStubs(Address start, boolean forward) {
return new FunctionFilteredIterator(new FunctionIteratorDB(start, foward)); return new FunctionFilteredIterator(new FunctionIteratorDB(start, forward));
} }
@Override @Override

View file

@ -54,11 +54,8 @@ public interface Category extends Comparable<Category> {
public abstract DataType[] getDataTypes(); public abstract DataType[] getDataTypes();
/** /**
* Get all data types in this category whose base name matches the base name of the given name. * Get all data types whose name matches the given name once any conflict suffixes have been
* The base name of a name is the first part of the string up to where the first ".conflict" * removed from both the given name and the data types that are being scanned.
* occurs. In other words, finds all data types whose name matches the given name once
* any conflict suffixes have been removed from both the given name and the data types
* that are being scanned.
* <br> * <br>
* NOTE: The {@code name} provided must not contain array or pointer decorations. * NOTE: The {@code name} provided must not contain array or pointer decorations.
* *

View file

@ -19,6 +19,7 @@ import java.util.*;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
/** /**
* A category path is the full path to a particular data type * A category path is the full path to a particular data type
@ -228,18 +229,35 @@ public class CategoryPath implements Comparable<CategoryPath> {
/** /**
* Return the {@link String} representation of this category path including the category name, * Return the {@link String} representation of this category path including the category name,
* where components are delimited with a forward slash. Any component that contains a forward * where components are delimited with a forward slash. Any occurance of a forward slash
* slash will be have the forward slash characters escaped. * within individual path components will be escaped (e.g., {@code "\/"}).
* @return the full category path * @return the full category path
*/ */
public String getPath() { public String getPath() {
if (isRoot()) { if (isRoot()) {
return DELIMITER_STRING; return DELIMITER_STRING;
} }
if (parent.isRoot()) { return parent.getPath(name);
return DELIMITER_CHAR + escapeString(name); }
/**
* Return the {@link String} representation of the specified {@code childName} within this
* category path where all path components are delimited with a forward slash. Any occurance
* of a forward slash within individual path components, including the {@code childName}, will
* be escaped (e.g., {@code "\/"}).
* @param childName child name
* @return full path for a child within this category
*/
public String getPath(String childName) {
if (StringUtils.isBlank(childName)) {
throw new IllegalArgumentException("blank child name");
} }
return parent.getPath() + DELIMITER_CHAR + escapeString(name); String path = getPath();
if (!isRoot()) {
path += DELIMITER_STRING;
}
path += escapeString(childName);
return path;
} }
@Override @Override

View file

@ -177,8 +177,8 @@ public abstract class CompositeDataTypeImpl extends GenericDataType implements C
/** /**
* This method throws an exception if the indicated data type is an ancestor of * This method throws an exception if the indicated data type is an ancestor of
* this data type. In other words, the specified data type has a component or * this data type (i.e., the specified data type has a component or
* sub-component containing this data type. * sub-component containing this data type).
* *
* @param dataType the data type * @param dataType the data type
* @throws IllegalArgumentException if the data type is an ancestor of this data * @throws IllegalArgumentException if the data type is an ancestor of this data

View file

@ -20,6 +20,7 @@ import java.util.Collection;
import ghidra.docking.settings.Settings; import ghidra.docking.settings.Settings;
import ghidra.docking.settings.SettingsDefinition; import ghidra.docking.settings.SettingsDefinition;
import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.mem.MemBuffer; import ghidra.program.model.mem.MemBuffer;
import ghidra.program.model.scalar.Scalar; import ghidra.program.model.scalar.Scalar;
import ghidra.util.InvalidNameException; import ghidra.util.InvalidNameException;
@ -50,6 +51,12 @@ public interface DataType {
@Deprecated @Deprecated
public static final DataType VOID = VoidDataType.dataType; public static final DataType VOID = VoidDataType.dataType;
/**
* Datatype name conflict suffix.
*
* See {@link DataTypeUtilities} for various methods related to conflict name handling.
* Direct use of this string in application/user-level code is discouraged.
*/
public final static String CONFLICT_SUFFIX = ".conflict"; public final static String CONFLICT_SUFFIX = ".conflict";
public final static String TYPEDEF_ATTRIBUTE_PREFIX = "__(("; public final static String TYPEDEF_ATTRIBUTE_PREFIX = "__((";

View file

@ -88,7 +88,9 @@ public interface DataTypeManager {
/** /**
* Returns a unique name not currently used by any other dataType or category * Returns a unique name not currently used by any other dataType or category
* with the same baseName * with the same baseName. This does not produce a conflict name and is intended
* to be used when generating an artifical datatype name only (e.g., {@code temp_1},
* {@code temp_2}; for {@code baseName="temp"}.
* *
* @param path the path of the name * @param path the path of the name
* @param baseName the base name to be made unique * @param baseName the base name to be made unique

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.program.model.data; package ghidra.program.model.data;
import java.util.*; import java.util.Comparator;
import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.data.DataTypeUtilities;
@ -79,8 +79,8 @@ public class DataTypeNameComparator implements Comparator<String> {
} }
// Same base-name, order by conflict // Same base-name, order by conflict
int conflict1 = getConflictValue(dt1Name); int conflict1 = DataTypeUtilities.getConflictValue(dt1Name);
int conflict2 = getConflictValue(dt2Name); int conflict2 = DataTypeUtilities.getConflictValue(dt2Name);
if (conflict1 != conflict2) { if (conflict1 != conflict2) {
return conflict1 - conflict2; return conflict1 - conflict2;
} }
@ -88,15 +88,4 @@ public class DataTypeNameComparator implements Comparator<String> {
return name1.compareTo(name2); return name1.compareTo(name2);
} }
private int getConflictValue(String dtName) {
String conflict = DataTypeUtilities.getConflictString(dtName);
if (conflict == null) {
return -1;
}
if (conflict.length() == 0) {
return 0;
}
return Integer.parseInt(conflict);
}
} }

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.program.model.data; package ghidra.program.database.data;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@ -24,8 +24,9 @@ import org.junit.Test;
import generic.test.AbstractGTest; import generic.test.AbstractGTest;
import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*;
public class DataTypeUtilitiesTest extends AbstractGTest { public class DataTypeUtilities2Test extends AbstractGTest {
@Test @Test
public void testGetContainedDataTypes() { public void testGetContainedDataTypes() {

View file

@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package ghidra.app.plugin.core.datamgr; package ghidra.program.database.data;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.junit.*; import org.junit.*;
import generic.test.AbstractGenericTest; import generic.test.AbstractGenericTest;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB; import ghidra.program.database.ProgramDB;
import ghidra.program.database.data.DataTypeUtilities; import ghidra.program.database.data.DataTypeUtilities;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
@ -32,8 +31,6 @@ import ghidra.program.model.data.*;
*/ */
public class DataTypeUtilitiesTest extends AbstractGenericTest { public class DataTypeUtilitiesTest extends AbstractGenericTest {
private ProgramBuilder builder;
private ProgramDB program;
private DataTypeManager dataTypeManager; private DataTypeManager dataTypeManager;
private int txID; private int txID;
@ -43,70 +40,15 @@ public class DataTypeUtilitiesTest extends AbstractGenericTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
builder = new ProgramBuilder("nameTest", ProgramBuilder._TOY, this); dataTypeManager = new StandAloneDataTypeManager("Test");
dataTypeManager.startTransaction("Test");
assertNotNull(builder);
program = builder.getProgram();
assertNotNull(program);
dataTypeManager = program.getDataTypeManager();
assertNotNull(dataTypeManager);
txID = program.startTransaction("NamingUtilities test");
} }
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
if (txID != 0) { if (txID != 0) {
program.endTransaction(txID, false); dataTypeManager.endTransaction(txID, true);
} }
if (builder != null) {
builder.dispose();
}
}
@Test
public void testIsSameKindDataType() {
assertTrue(
DataTypeUtilities.isSameKindDataType(IntegerDataType.dataType, ShortDataType.dataType));
assertFalse(
DataTypeUtilities.isSameKindDataType(FloatDataType.dataType, ShortDataType.dataType));
assertTrue(
DataTypeUtilities.isSameKindDataType(new PointerDataType(IntegerDataType.dataType),
new PointerDataType(ShortDataType.dataType)));
assertFalse(
DataTypeUtilities.isSameKindDataType(new PointerDataType(FloatDataType.dataType),
new PointerDataType(ShortDataType.dataType)));
assertTrue(
DataTypeUtilities.isSameKindDataType(new StructureDataType("X", 10),
new StructureDataType("Y", 5)));
assertTrue(
DataTypeUtilities.isSameKindDataType(new UnionDataType("X"), new UnionDataType("Y")));
assertFalse(
DataTypeUtilities.isSameKindDataType(new StructureDataType("X", 10),
new UnionDataType("Y")));
assertTrue(
DataTypeUtilities.isSameKindDataType(
new PointerDataType(new StructureDataType("X", 10)),
new PointerDataType(new StructureDataType("Y", 5))));
assertTrue(
DataTypeUtilities.isSameKindDataType(new PointerDataType(new UnionDataType("X")),
new PointerDataType(new UnionDataType("Y"))));
assertFalse(
DataTypeUtilities.isSameKindDataType(
new PointerDataType(new StructureDataType("X", 10)),
new PointerDataType(new UnionDataType("Y"))));
assertTrue(
DataTypeUtilities.isSameKindDataType(
new TypedefDataType("Foo", new PointerDataType(new StructureDataType("X", 10))),
new PointerDataType(new StructureDataType("Y", 5))));
assertFalse(
DataTypeUtilities.isSameKindDataType(
new TypedefDataType("Foo", new PointerDataType(new StructureDataType("X", 10))),
new PointerDataType(new UnionDataType("Y"))));
} }
@Test @Test
@ -393,16 +335,10 @@ public class DataTypeUtilitiesTest extends AbstractGenericTest {
same("/cat1/simpleStruct.conflict1 *32 *64", "/cat1/simpleStruct *32 *64"); same("/cat1/simpleStruct.conflict1 *32 *64", "/cat1/simpleStruct *32 *64");
same("/cat1/simpleStruct.conflict_1234 *64", "/cat1.conflict5/simpleStruct *64");
same("/cat1/simpleStruct", "/cat1/simpleStruct.conflict1"); same("/cat1/simpleStruct", "/cat1/simpleStruct.conflict1");
same("/cat1/simpleStruct.conflict_1234.conflict", "/cat1/simpleStruct");
same("/cat1/simpleStruct.conflict_1234", "/cat1/simpleStruct.conflict3"); same("/cat1/simpleStruct.conflict_1234", "/cat1/simpleStruct.conflict3");
same("simpleStruct.conflict_1234_abc", "simpleStruct.conflict4_abc");
same("simpleStruct.conflict12", "simpleStruct.conflict34"); same("simpleStruct.conflict12", "simpleStruct.conflict34");
same("/cat1/simpleStruct[5]", "/cat1/simpleStruct.conflict1[5]"); same("/cat1/simpleStruct[5]", "/cat1/simpleStruct.conflict1[5]");
@ -525,6 +461,12 @@ public class DataTypeUtilitiesTest extends AbstractGenericTest {
different("/cat1/simpleStruct[11] *32[2]", "/cat1/simpleStruct.conflict1[11] *8[2]"); different("/cat1/simpleStruct[11] *32[2]", "/cat1/simpleStruct.conflict1[11] *8[2]");
different("/cat1/simpleStruct[6] *16[9]", "/cat1/simpleStruct.conflict1[6] *64[9]"); different("/cat1/simpleStruct[6] *16[9]", "/cat1/simpleStruct.conflict1[6] *64[9]");
different("/cat1/simpleStruct.conflict_1234 *64", "/cat1.conflict5/simpleStruct *64");
different("/cat1/simpleStruct.conflict_1234.conflict", "/cat1/simpleStruct");
different("simpleStruct.conflict_1234_abc", "simpleStruct.conflict4_abc");
} }
private void same(String name1, String name2) { private void same(String name1, String name2) {

View file

@ -99,7 +99,7 @@ public class StubFunctionManager implements FunctionManager {
} }
@Override @Override
public FunctionIterator getFunctions(Address start, boolean foward) { public FunctionIterator getFunctions(Address start, boolean forward) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -114,7 +114,7 @@ public class StubFunctionManager implements FunctionManager {
} }
@Override @Override
public FunctionIterator getFunctionsNoStubs(Address start, boolean foward) { public FunctionIterator getFunctionsNoStubs(Address start, boolean forward) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }