Merge remote-tracking branch 'origin/GP-1082_ghidravore_improving_symbol_table_performance'

This commit is contained in:
Ryan Kurtz
2021-09-28 12:52:32 -04:00
32 changed files with 1938 additions and 821 deletions
@@ -526,7 +526,7 @@ class SymbolMerger extends AbstractListingMerger {
renamed = true;
}
// boolean commentChanged =
// !SystemUtilities.isEqual(newSym.getSymbolData3(), oldSym.getSymbolData3());
// !SystemUtilities.isEqual(newSym.getSymbolStringData(), oldSym.getSymbolStringData());
boolean sourceChanged = newSym.getSource() != oldSym.getSource();
// if (commentChanged) {
// commentChanges.add(id);
@@ -920,12 +920,12 @@ class SymbolMerger extends AbstractListingMerger {
// if (commentChangedInMy) {
// if (!commentChangedInLatest) {
// if (resultSym != null) {
// resultSym.setSymbolData3(mySym.getSymbolData3());
// resultSym.setSymbolStringData(mySym.getSymbolStringData());
// }
// }
// else {
// Symbol latestSym = latestSymTab.getSymbol(id);
// if (!SystemUtilities.isEqual(latestSym.getSymbolData3(), mySym.getSymbolData3())) {
// if (!SystemUtilities.isEqual(latestSym.getSymbolStringData(), mySym.getSymbolStringData())) {
// saveCommentConflict(id);
// }
// }
@@ -1008,11 +1008,11 @@ class SymbolMerger extends AbstractListingMerger {
// if (commentChangedInMy) {
// if (!commentChangedInLatest) {
// // Use My version's comment since Latest didn't change it.
// resultSym.setSymbolData3(mySym.getSymbolData3());
// resultSym.setSymbolStringData(mySym.getSymbolStringData());
// }
// else {
// Symbol latestSym = latestSymTab.getSymbol(id);
// if (!SystemUtilities.isEqual(latestSym.getSymbolData3(), mySym.getSymbolData3())) {
// if (!SystemUtilities.isEqual(latestSym.getSymbolStringData(), mySym.getSymbolStringData())) {
// saveFunctionCommentConflict(id); // My & Latest changed comment differently.
// }
// }
@@ -1181,11 +1181,11 @@ class SymbolMerger extends AbstractListingMerger {
// Handle Symbol comments.
// if (commentChangedInMy) {
// if (!commentChangedInLatest) {
// resultSym.setSymbolData3(mySym.getSymbolData3()); // Use My version's comment since Latest didn't change it.
// resultSym.setSymbolStringData(mySym.getSymbolStringData()); // Use My version's comment since Latest didn't change it.
// }
// else {
// Symbol latestSym = latestSymTab.getSymbol(id);
// if (!SystemUtilities.isEqual(latestSym.getSymbolData3(), mySym.getSymbolData3())) {
// if (!SystemUtilities.isEqual(latestSym.getSymbolStringData(), mySym.getSymbolStringData())) {
// saveAddFunctionCommentConflict(id); // My & Latest changed comment differently.
// }
// }
@@ -1372,8 +1372,8 @@ class SymbolMerger extends AbstractListingMerger {
// private void processAddedSymbolComment(Symbol resultSym, Symbol mySym) {
// // My version added a symbol that matches on in the result version.
// String resultComment = resultSym.getSymbolData3();
// String myComment = mySym.getSymbolData3();
// String resultComment = resultSym.getSymbolStringData();
// String myComment = mySym.getSymbolStringData();
// if (SystemUtilities.isEqual(resultComment, myComment)) {
// return; // Already has My symbol comment.
// }
@@ -1381,7 +1381,7 @@ class SymbolMerger extends AbstractListingMerger {
// return; // My version isn't setting a symbol comment.
// }
// if (resultComment == null) {
// resultSym.setSymbolData3(myComment); // Latest didn't set a comment, but My did so use My symbol comment.
// resultSym.setSymbolStringData(myComment); // Latest didn't set a comment, but My did so use My symbol comment.
// }
// else if (!myComment.equals(resultComment)) {
// saveAddCommentConflict(mySym.getID()); // Both set a different symbol comment, so conflict
@@ -1711,7 +1711,7 @@ class SymbolMerger extends AbstractListingMerger {
// currentAddress = addr;
// currentSymbol = mySym;
// Symbol resultSym = getResultSymbolFromMySymbol(mySym);
// currentSymbolComment = (resultSym != null) ? resultSym.getSymbolData3() : "";
// currentSymbolComment = (resultSym != null) ? resultSym.getSymbolStringData() : "";
// currentBackgroundSet = new AddressSet(resultAddressFactory, addr, addr);
// if (askUser && mergeManager != null) {
// boolean useForAll = (symbolAddCommentChoice != ASK_USER);
@@ -1912,10 +1912,10 @@ class SymbolMerger extends AbstractListingMerger {
// return;
// }
// if ((chosenConflictOption & KEEP_LATEST) != 0) {
// result.setSymbolData3(latest.getSymbolData3());
// result.setSymbolStringData(latest.getSymbolStringData());
// }
// else if ((chosenConflictOption & KEEP_MY) != 0) {
// result.setSymbolData3(my.getSymbolData3());
// result.setSymbolStringData(my.getSymbolStringData());
// }
// }
@@ -1933,10 +1933,10 @@ class SymbolMerger extends AbstractListingMerger {
// return;
// }
// if ((chosenConflictOption & KEEP_RESULT) != 0) {
// result.setSymbolData3(currentSymbolComment);
// result.setSymbolStringData(currentSymbolComment);
// }
// else if ((chosenConflictOption & KEEP_MY) != 0) {
// result.setSymbolData3(my.getSymbolData3());
// result.setSymbolStringData(my.getSymbolStringData());
// }
// }
@@ -2253,7 +2253,7 @@ class SymbolMerger extends AbstractListingMerger {
Symbol newSymbol)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
renameSymbol(oldProgram, oldSymbol, newProgram, newSymbol);
// oldSymbol.setSymbolData3(newSymbol.getSymbolData3());
// oldSymbol.setSymbolStringData(newSymbol.getSymbolStringData());
// Handle primary.
if (newSymbol.isPrimary() && !oldSymbol.isPrimary()) {
oldSymbol.setPrimary();
@@ -2446,7 +2446,7 @@ class SymbolMerger extends AbstractListingMerger {
private Symbol createSymbol(String name, SymbolType type, Address resultAddr,
Namespace resultParentNs, Program srcPgm, long srcSymID, SourceType source)
throws DuplicateNameException, InvalidInputException {
// String comment = srcSymbol.getSymbolData3();
// String comment = srcSymbol.getSymbolStringData();
Symbol symbol = null;
if (type == SymbolType.LABEL) {
symbol = resultSymTab.createLabel(resultAddr, name, resultParentNs, source);
@@ -2468,7 +2468,7 @@ class SymbolMerger extends AbstractListingMerger {
symbol = resultSymTab.getLibrarySymbol(name);
}
if (symbol != null) {
// symbol.setSymbolData3(comment);
// symbol.setSymbolStringData(comment);
if (symbol.getParentNamespace().equals(resultParentNs)) {
long resolveSymID = symbol.getID();
updateResolveIDs(srcPgm, srcSymID, resolveSymID);
@@ -2671,7 +2671,7 @@ class SymbolMerger extends AbstractListingMerger {
String symbolName, ChangeListener listener) {
Symbol latest =
latestSymTab.getNamespace(symbolName, DiffUtility.getNamespace(myNamespace, latestPgm))
.getSymbol();
.getSymbol();
Symbol my = mySymTab.getNamespace(symbolName, myNamespace).getSymbol();
String text = "Namespace Conflict";
conflictPanel.clear();
@@ -2864,7 +2864,7 @@ class SymbolMerger extends AbstractListingMerger {
// info[1] = s.getName(false);
// info[2] = s.getParentNamespace().getSymbol().getName();
// info[3] = s.getSymbolType().toString();
// info[4] = ConflictUtility.getTruncatedHTMLString(s.getSymbolData3(), TRUNCATE_LENGTH);
// info[4] = ConflictUtility.getTruncatedHTMLString(s.getSymbolStringData(), TRUNCATE_LENGTH);
// return info;
// }
@@ -391,8 +391,8 @@ class SymbolMerge {
if (replace && toSymbol.isPinned() != pinned) {
toSymbol.setPinned(pinned);
}
// String fromComment = fromSymbol.getSymbolData3();
// String toComment = toSymbol.getSymbolData3();
// String fromComment = fromSymbol.getSymbolStringData();
// String toComment = toSymbol.getSymbolStringData();
// if (!SystemUtilities.isEqual(fromComment, toComment)) {
// String newComment;
// if (replace) {
@@ -401,7 +401,7 @@ class SymbolMerge {
// else {
// newComment = StringUtilities.mergeStrings(fromComment, toComment);
// }
// toSymbol.setSymbolData3(newComment);
// toSymbol.setSymbolStringData(newComment);
// }
}
}
@@ -462,7 +462,7 @@ class SymbolMerge {
boolean isFromDefaultThunk = FunctionMerge.isDefaultThunk(fromFunc);
String fromName = fromSymbol.getName();
Namespace fromNamespace = // default thunks will lie about their namespace
isFromDefaultThunk ? fromProgram.getGlobalNamespace() : fromSymbol.getParentNamespace();
isFromDefaultThunk ? fromProgram.getGlobalNamespace() : fromSymbol.getParentNamespace();
Symbol toSymbol;
if (toFunc == null) {
@@ -551,7 +551,8 @@ class SymbolMerge {
boolean fromDefault = fromSymbol.getSource() == SourceType.DEFAULT;
boolean isFromDefaultThunk = FunctionMerge.isDefaultThunk(fromFunc);
Namespace fromNamespace = // default thunks will lie about their namespace
isFromDefaultThunk ? fromProgram.getGlobalNamespace() : fromSymbol.getParentNamespace();
isFromDefaultThunk ? fromProgram.getGlobalNamespace()
: fromSymbol.getParentNamespace();
Namespace resolveNamespace = resolveNamespace(fromNamespace, conflictSymbolIDMap);
if ((toFunc != null) && replacePrimary && !fromDefault) {
@@ -573,7 +574,7 @@ class SymbolMerge {
if (isFromDefaultThunk && FunctionMerge.isDefaultThunk(toFunc)) {
return;
}
if (toFunc.getSymbol().getSource() == SourceType.DEFAULT) {
// Default "to" function so replace
replaceFunctionSymbol(fromEntryPoint, toEntryPoint, conflictSymbolIDMap,
@@ -81,8 +81,9 @@ public class SymbolMergeManagerSourceTest extends AbstractListingMergeManagerTes
int txId = program.startTransaction("Modify My Program");
boolean commit = false;
try {
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.USER_DEFINED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.USER_DEFINED);
commit = true;
}
catch (Exception e) {
@@ -101,8 +102,9 @@ public class SymbolMergeManagerSourceTest extends AbstractListingMergeManagerTes
int txId = program.startTransaction("Modify My Program");
boolean commit = false;
try {
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.USER_DEFINED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.USER_DEFINED);
commit = true;
}
catch (Exception e) {
@@ -141,11 +143,11 @@ public class SymbolMergeManagerSourceTest extends AbstractListingMergeManagerTes
// try {
// Symbol s = program.getSymbolTable().createSymbol(addr(program, "0x10032a7"),
// "Lucy", null, SourceType.USER_DEFINED);
// s.setSymbolData3("This is a symbol comment.");
// s.setSymbolStringData("This is a symbol comment.");
//
// s = program.getSymbolTable().createSymbol(addr(program, "0x1004bf4"),
// "red", null, SourceType.IMPORTED);
// s.setSymbolData3(longComment1);
// s.setSymbolStringData(longComment1);
// commit = true;
// } catch (Exception e) {
// Assert.fail(e.getMessage());
@@ -163,11 +165,11 @@ public class SymbolMergeManagerSourceTest extends AbstractListingMergeManagerTes
// try {
// Symbol s = program.getSymbolTable().createSymbol(addr(program, "0x10032a7"), "Lucy", null,
// SourceType.USER_DEFINED);
// s.setSymbolData3("This is a symbol comment.");
// s.setSymbolStringData("This is a symbol comment.");
//
// s = program.getSymbolTable().createSymbol(addr(program, "0x1004bf4"),
// "red", null, SourceType.IMPORTED);
// s.setSymbolData3(longComment1);
// s.setSymbolStringData(longComment1);
// commit = true;
// } catch (Exception e) {
// Assert.fail(e.getMessage());
@@ -186,13 +188,13 @@ public class SymbolMergeManagerSourceTest extends AbstractListingMergeManagerTes
// checkSymbol(s, "Lucy", true);
// assertTrue(s.isPrimary());
// assertEquals(SourceType.USER_DEFINED, s.getSource());
// assertEquals("This is a symbol comment.", s.getSymbolData3());
// assertEquals("This is a symbol comment.", s.getSymbolStringData());
//
// s = symtab.getPrimarySymbol(addr("0x1004bf4"));
// checkSymbol(s, "red", true);
// assertTrue(s.isPrimary());
// assertEquals(SourceType.IMPORTED, s.getSource());
// assertEquals(longComment1, s.getSymbolData3());
// assertEquals(longComment1, s.getSymbolStringData());
// }
/** Test add of same symbol and source to both Latest and My, but different symbol comments. */
@@ -351,12 +353,15 @@ public class SymbolMergeManagerSourceTest extends AbstractListingMergeManagerTes
int txId = program.startTransaction("Modify My Program");
boolean commit = false;
try {
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Charlie",
null, SourceType.ANALYSIS);
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.IMPORTED);
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Linus", null,
SourceType.USER_DEFINED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Charlie",
null, SourceType.ANALYSIS);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.IMPORTED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Linus", null,
SourceType.USER_DEFINED);
commit = true;
}
catch (Exception e) {
@@ -375,12 +380,15 @@ public class SymbolMergeManagerSourceTest extends AbstractListingMergeManagerTes
int txId = program.startTransaction("Modify My Program");
boolean commit = false;
try {
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Charlie",
null, SourceType.USER_DEFINED);
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.ANALYSIS);
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Linus", null,
SourceType.IMPORTED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Charlie",
null, SourceType.USER_DEFINED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.ANALYSIS);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Linus", null,
SourceType.IMPORTED);
commit = true;
}
catch (Exception e) {
@@ -426,12 +434,15 @@ public class SymbolMergeManagerSourceTest extends AbstractListingMergeManagerTes
int txId = program.startTransaction("Modify My Program");
boolean commit = false;
try {
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.IMPORTED);
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Linus", null,
SourceType.ANALYSIS);
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Sally", null,
SourceType.USER_DEFINED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.IMPORTED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Linus", null,
SourceType.ANALYSIS);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Sally", null,
SourceType.USER_DEFINED);
commit = true;
}
catch (Exception e) {
@@ -493,12 +504,15 @@ public class SymbolMergeManagerSourceTest extends AbstractListingMergeManagerTes
int txId = program.startTransaction("Modify My Program");
boolean commit = false;
try {
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Charlie",
null, SourceType.USER_DEFINED);
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.ANALYSIS);
program.getSymbolTable().createLabel(addr(program, "0x10032a7"), "Linus", null,
SourceType.IMPORTED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Charlie",
null, SourceType.USER_DEFINED);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Lucy", null,
SourceType.ANALYSIS);
program.getSymbolTable()
.createLabel(addr(program, "0x10032a7"), "Linus", null,
SourceType.IMPORTED);
commit = true;
}
catch (Exception e) {
@@ -32,7 +32,6 @@ import ghidra.program.model.lang.PrototypeModel;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitorAdapter;
@@ -53,8 +52,9 @@ public class FunctionManagerTest extends AbstractGhidraHeadedIntegrationTest {
space = program.getAddressFactory().getDefaultAddressSpace();
functionManager = program.getFunctionManager();
transactionID = program.startTransaction("Test");
program.getMemory().createInitializedBlock("temp", addr(0), 10000, (byte) 0,
TaskMonitorAdapter.DUMMY_MONITOR, false);
program.getMemory()
.createInitializedBlock("temp", addr(0), 10000, (byte) 0,
TaskMonitorAdapter.DUMMY_MONITOR, false);
}
@After
@@ -71,7 +71,7 @@ public class FunctionManagerTest extends AbstractGhidraHeadedIntegrationTest {
}
private Function createFunction(String name, Address entryPt, AddressSetView body)
throws DuplicateNameException, InvalidInputException, OverlappingFunctionException {
throws InvalidInputException, OverlappingFunctionException {
functionManager.createFunction(name, entryPt, body, SourceType.USER_DEFINED);
Function f = functionManager.getFunctionAt(entryPt);
@@ -209,8 +209,10 @@ public class FunctionManagerTest extends AbstractGhidraHeadedIntegrationTest {
f = functionManager.getFunctionAt(addr(250));
assertEquals(new AddressSet(addr(250), addr(350)), f.getBody());
assertTrue(program.getSymbolTable().getPrimarySymbol(
addr(201)).getSymbolType() != SymbolType.FUNCTION);
assertTrue(program.getSymbolTable()
.getPrimarySymbol(
addr(201))
.getSymbolType() != SymbolType.FUNCTION);
}
@Test
@@ -301,16 +303,19 @@ public class FunctionManagerTest extends AbstractGhidraHeadedIntegrationTest {
createFunction("foo1", addr(250), new AddressSet(addr(250), addr(350)));
Function foo2 = createFunction("foo2", addr(201), new AddressSet(addr(201), addr(249)));
Function fum = program.getExternalManager().addExtLocation("lib", "fum", null,
SourceType.USER_DEFINED).createFunction();
Function fum = program.getExternalManager()
.addExtLocation("lib", "fum", null,
SourceType.USER_DEFINED)
.createFunction();
program.getMemory().setInt(addr(50), 201);
program.getListing().createData(addr(50), PointerDataType.dataType);
assertEquals(foo2, program.getFunctionManager().getReferencedFunction(addr(50)));
program.getReferenceManager().addExternalReference(addr(50), 0,
program.getExternalManager().getExternalLocation(fum.getSymbol()),
SourceType.USER_DEFINED, RefType.DATA);
program.getReferenceManager()
.addExternalReference(addr(50), 0,
program.getExternalManager().getExternalLocation(fum.getSymbol()),
SourceType.USER_DEFINED, RefType.DATA);
assertEquals(fum, program.getFunctionManager().getReferencedFunction(addr(50)));
@@ -59,7 +59,7 @@ public class BinaryField extends Field {
}
@Override
boolean isNull() {
public boolean isNull() {
return data == null;
}
@@ -112,7 +112,7 @@ public abstract class Field implements Comparable<Field> {
* is reserved for use in the special-purpose byte value 0x88.
* (see {@link LegacyIndexField})
*/
static final byte LEGACY_INDEX_LONG_TYPE = 8;
static final byte LEGACY_INDEX_LONG_TYPE = 8;
// Available field types (6): 0x9..0xE
@@ -424,7 +424,7 @@ public abstract class Field implements Comparable<Field> {
* Determine if the field has been set to a null-state or value.
* @return true if field has been set to a null state or value, else false
*/
abstract boolean isNull();
public abstract boolean isNull();
/**
* Set this field to its null-state. For variable-length field this will
@@ -47,7 +47,7 @@ abstract class FixedField extends BinaryField {
}
@Override
final boolean isNull() {
public final boolean isNull() {
return isNull;
}
@@ -56,7 +56,7 @@ class IndexField extends Field {
}
@Override
boolean isNull() {
public boolean isNull() {
return false; // not-applicable
}
@@ -42,7 +42,7 @@ abstract class PrimitiveField extends Field {
}
@Override
final boolean isNull() {
public final boolean isNull() {
return isNull;
}
@@ -67,7 +67,7 @@ public final class StringField extends Field {
}
@Override
boolean isNull() {
public boolean isNull() {
return bytes == null;
}
@@ -98,7 +98,7 @@ public class ExternalLocationDB implements ExternalLocation {
@Override
public String getOriginalImportedName() {
return getExternalData3(symbol).getOriginalImportedName();
return getExternalData(symbol).getOriginalImportedName();
}
@Override
@@ -111,7 +111,7 @@ public class ExternalLocationDB implements ExternalLocation {
*/
@Override
public Address getAddress() {
return getExternalData3(symbol).getAddress(extMgr.getAddressMap().getAddressFactory());
return getExternalData(symbol).getAddress(extMgr.getAddressMap().getAddressFactory());
}
/**
@@ -141,7 +141,7 @@ public class ExternalLocationDB implements ExternalLocation {
*/
@Override
public DataType getDataType() {
long dataTypeID = symbol.getSymbolData1();
long dataTypeID = symbol.getDataTypeId();
if (dataTypeID < 0) {
return null;
}
@@ -154,7 +154,7 @@ public class ExternalLocationDB implements ExternalLocation {
@Override
public void setDataType(DataType dt) {
long dataTypeID = extMgr.getProgram().getDataTypeManager().getResolvedID(dt);
symbol.setSymbolData1(dataTypeID);
symbol.setDataTypeId(dataTypeID);
// TODO: change notification may be required
}
@@ -227,7 +227,7 @@ public class ExternalLocationDB implements ExternalLocation {
if (addressString == null && getSource() == SourceType.DEFAULT) {
throw new InvalidInputException("Either an external label or address is required");
}
updateSymbolData3(symbol, getExternalData3(symbol).getOriginalImportedName(),
updateSymbolData(symbol, getExternalData(symbol).getOriginalImportedName(),
addressString);
}
@@ -365,18 +365,18 @@ public class ExternalLocationDB implements ExternalLocation {
}
static ExternalData3 getExternalData3(SymbolDB extSymbol) {
return new ExternalData3(extSymbol.getSymbolData3());
static ExternalData getExternalData(SymbolDB extSymbol) {
return new ExternalData(extSymbol.getSymbolStringData());
}
static void setOriginalImportedName(SymbolDB extSymbol, String name) {
updateSymbolData3(extSymbol, name, getExternalData3(extSymbol).getAddressString());
updateSymbolData(extSymbol, name, getExternalData(extSymbol).getAddressString());
}
static void updateSymbolData3(SymbolDB extSymbol, String originalImportedName,
static void updateSymbolData(SymbolDB extSymbol, String originalImportedName,
String addressString) {
if (addressString == null && originalImportedName == null) {
extSymbol.setSymbolData3(null);
extSymbol.setSymbolStringData(null);
}
StringBuilder buf = new StringBuilder();
if (addressString != null) {
@@ -386,18 +386,18 @@ public class ExternalLocationDB implements ExternalLocation {
buf.append(ORIGINAL_IMPORTED_DELIMITER);
buf.append(originalImportedName);
}
extSymbol.setSymbolData3(buf.toString());
extSymbol.setSymbolStringData(buf.toString());
}
static class ExternalData3 {
static class ExternalData {
private String originalImportedName;
private String addressString;
ExternalData3(String data3) {
if (data3 != null) {
int indexOf = data3.indexOf(ORIGINAL_IMPORTED_DELIMITER);
originalImportedName = indexOf >= 0 ? data3.substring(indexOf + 1) : null;
addressString = indexOf >= 0 ? data3.substring(0, indexOf) : data3;
ExternalData(String stringData) {
if (stringData != null) {
int indexOf = stringData.indexOf(ORIGINAL_IMPORTED_DELIMITER);
originalImportedName = indexOf >= 0 ? stringData.substring(indexOf + 1) : null;
addressString = indexOf >= 0 ? stringData.substring(0, indexOf) : stringData;
}
}
@@ -22,7 +22,7 @@ import db.*;
import ghidra.framework.store.FileSystem;
import ghidra.program.database.ManagerDB;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.external.ExternalLocationDB.ExternalData3;
import ghidra.program.database.external.ExternalLocationDB.ExternalData;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.database.map.AddressMap;
import ghidra.program.database.symbol.*;
@@ -556,15 +556,15 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
if ((type != SymbolType.LABEL && type != SymbolType.FUNCTION) || !sym.isExternal()) {
throw new AssertException();
}
ExternalData3 externalData3 = ExternalLocationDB.getExternalData3(sym);
Address addr = externalData3.getAddress(sym.getProgram().getAddressFactory());
ExternalData externalData = ExternalLocationDB.getExternalData(sym);
Address addr = externalData.getAddress(sym.getProgram().getAddressFactory());
if (addr == null) {
throw new AssertException("External should not be default without memory address");
}
if (type == SymbolType.FUNCTION) {
return SymbolUtilities.getDefaultExternalFunctionName(addr);
}
long dataTypeID = sym.getSymbolData1();
long dataTypeID = sym.getDataTypeId();
DataType dt =
(dataTypeID < 0) ? null : sym.getProgram().getDataTypeManager().getDataType(dataTypeID);
return SymbolUtilities.getDefaultExternalName(addr, dt);
@@ -695,7 +695,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
private Library addExternalName(String name, String pathname, SourceType source)
throws DuplicateNameException, InvalidInputException {
SymbolDB s = symbolMgr.createSpecialSymbol(Address.NO_ADDRESS, name,
scopeMgr.getGlobalNamespace(), SymbolType.LIBRARY, -1, 0, pathname, source); // 0 set first id for external names
scopeMgr.getGlobalNamespace(), SymbolType.LIBRARY, null, null, pathname, source);
return (Library) s.getObject();
}
@@ -742,7 +742,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
public String getExternalLibraryPath(String externalName) {
SymbolDB s = (SymbolDB) symbolMgr.getLibrarySymbol(externalName);
if (s instanceof LibrarySymbol) {
return s.getSymbolData3();
return s.getSymbolStringData();
}
return null;
}
@@ -771,7 +771,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
}
}
else if (s instanceof LibrarySymbol) {
s.setSymbolData3(externalPath);
s.setSymbolStringData(externalPath);
}
}
finally {
@@ -802,7 +802,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
return null;
}
//long dtId = symbol.getSymbolData1();
String extData3 = symbol.getSymbolData3();
String extData = symbol.getSymbolStringData();
String name = symbol.getName();
Namespace namespace = symbol.getParentNamespace();
Address extAddr = symbol.getAddress();
@@ -810,7 +810,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
((CodeSymbol) symbol).delete(true);
return functionMgr.createExternalFunction(extAddr, name, namespace, extData3, source);
return functionMgr.createExternalFunction(extAddr, name, namespace, extData, source);
}
catch (Exception e) {
e.printStackTrace();
@@ -927,8 +927,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
while (externalSymbols.hasNext()) {
monitor.checkCanceled();
SymbolDB s = (SymbolDB) externalSymbols.next();
ExternalData3 externalData3 = ExternalLocationDB.getExternalData3(s);
String addrStr = externalData3.getAddressString();
ExternalData externalData = ExternalLocationDB.getExternalData(s);
String addrStr = externalData.getAddressString();
if (addrStr == null) {
continue;
}
@@ -947,7 +947,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
addr = newAddressSpace.getAddress(addr.getOffset());
String newAddrStr = addr.toString();
if (!newAddrStr.equals(addrStr)) {
ExternalLocationDB.updateSymbolData3(s, externalData3.getOriginalImportedName(),
ExternalLocationDB.updateSymbolData(s, externalData.getOriginalImportedName(),
newAddrStr); // store translated external location address
}
}
@@ -990,7 +990,7 @@ public class FunctionDB extends DatabaseObject implements Function {
symbolMap.put(v.symbol, v);
}
if (var.getComment() != null) {
v.symbol.setSymbolData3(var.getComment());
v.symbol.setSymbolStringData(var.getComment());
}
manager.functionChanged(this, 0);
return v;
@@ -1678,7 +1678,7 @@ public class FunctionDB extends DatabaseObject implements Function {
manager.functionChanged(this, ChangeManager.FUNCTION_CHANGED_PARAMETERS);
}
if (var.getComment() != null) {
p.symbol.setSymbolData3(var.getComment());
p.symbol.setSymbolStringData(var.getComment());
}
updateSignatureSourceAfterVariableChange(source, p.getDataType());
return p;
@@ -273,22 +273,22 @@ public class FunctionManagerDB implements FunctionManager {
* Transform an existing external symbol into an external function.
* This method should only be invoked by an ExternalSymbol
* @param extSpaceAddr the external space address to use when creating this external.
* @param name
* @param nameSpace
* @param extData3 internal symbol-data-3 string (see {@link ExternalLocationDB})
* @param name the external function name
* @param nameSpace the external function namespace
* @param extData the external data string to store additional info (see {@link ExternalLocationDB})
* @param source the source of this external.
* @return external function
* @throws InvalidInputException
* @throws DuplicateNameException
* @throws InvalidInputException if the name is invalid
* @throws DuplicateNameException if the name is an invalid duplicate
*/
public Function createExternalFunction(Address extSpaceAddr, String name, Namespace nameSpace,
String extData3, SourceType source)
String extData, SourceType source)
throws DuplicateNameException, InvalidInputException {
lock.acquire();
try {
Symbol symbol = symbolMgr.createSpecialSymbol(extSpaceAddr, name, nameSpace,
SymbolType.FUNCTION, -1, -1, extData3, source);
SymbolType.FUNCTION, null, null, extData, source);
long returnDataTypeId = program.getDataTypeManager().getResolvedID(DataType.DEFAULT);
@@ -167,12 +167,12 @@ public abstract class VariableDB implements Variable {
@Override
public String getComment() {
return symbol.getSymbolData3();
return symbol.getSymbolStringData();
}
@Override
public void setComment(String comment) {
symbol.setSymbolData3(comment);
symbol.setSymbolStringData(comment);
functionMgr.functionChanged(function, 0);
}
@@ -20,26 +20,21 @@ import java.util.Iterator;
import db.DBRecord;
import db.RecordIterator;
import ghidra.program.database.util.Query;
import ghidra.program.database.util.QueryRecordIterator;
import ghidra.program.model.address.*;
import ghidra.program.database.util.*;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolIterator;
/**
*
* Iterator (in address order) over primary symbols in an address set.
* Iterator (in address order) over all symbols that match the given query in an address set.
*
*
*/
class AddressSetFilteredSymbolIterator implements SymbolIterator {
private SymbolManager symbolMgr;
private AddressRangeIterator rangeIter;
private QueryRecordIterator recIter;
private Symbol currentSymbol;
private SymbolDatabaseAdapter adapter;
private boolean forward;
private Query query;
/**
* Construct a new AddressSetFilteredSymbolIterator.
@@ -51,53 +46,40 @@ class AddressSetFilteredSymbolIterator implements SymbolIterator {
AddressSetFilteredSymbolIterator(SymbolManager symbolMgr, AddressSetView set, Query query,
boolean forward) {
this.symbolMgr = symbolMgr;
rangeIter = set.getAddressRanges(forward);
adapter = symbolMgr.getDatabaseAdapter();
this.forward = forward;
this.query = query;
try {
RecordIterator it = adapter.getSymbols(set, forward);
recIter = new QueryRecordIterator(it, query, forward);
}
catch (IOException e) {
symbolMgr.dbError(e);
recIter = new QueryRecordIterator(new EmptyRecordIterator(), query, forward);
}
}
@Override
public boolean hasNext() {
if (currentSymbol == null) {
try {
findNext();
}
catch (IOException e) {
symbolMgr.dbError(e);
}
try {
return recIter.hasNext();
}
return currentSymbol != null;
catch (IOException e) {
symbolMgr.dbError(e);
}
return false;
}
@Override
public Symbol next() {
if (hasNext()) {
Symbol s = currentSymbol;
currentSymbol = null;
return s;
}
return null;
}
private void findNext() throws IOException {
if (recIter != null && recIter.hasNext()) {
DBRecord rec = recIter.next();
currentSymbol = symbolMgr.getSymbol(rec);
}
else {
while (rangeIter.hasNext()) {
AddressRange range = rangeIter.next();
RecordIterator it =
adapter.getSymbols(range.getMinAddress(), range.getMaxAddress(), forward);
recIter = new QueryRecordIterator(it, query, forward);
if (recIter.hasNext()) {
DBRecord rec = recIter.next();
currentSymbol = symbolMgr.getSymbol(rec);
break;
}
try {
DBRecord rec = recIter.next();
return symbolMgr.getSymbol(rec);
}
catch (IOException e) {
symbolMgr.dbError(e);
}
}
return null;
}
@Override
@@ -30,10 +30,7 @@ import ghidra.program.util.ProgramLocation;
*
* Symbol data usage:
* EXTERNAL:
* long data1 - external data type
* String data3 - external memory address
* NON-EXTERNAL:
* int data2 - primary flag
* String stringData - external memory address/label
*/
public class CodeSymbol extends SymbolDB {
@@ -162,7 +159,7 @@ public class CodeSymbol extends SymbolDB {
if (getSource() == SourceType.DEFAULT || isExternal()) {
return true;
}
return getSymbolData2() == 1;
return doCheckIsPrimary();
}
/**
@@ -201,7 +198,7 @@ public class CodeSymbol extends SymbolDB {
}
void setPrimary(boolean primary) {
setSymbolData2(primary ? 1 : 0);
doSetPrimary(primary);
}
/**
@@ -39,10 +39,7 @@ import ghidra.util.task.TaskMonitor;
*
* Symbol Data Usage:
* EXTERNAL:
* long data1 - external data type
* String data3 - external memory address
* NON-EXTERNAL:
* - not used -
* String stringData - external memory address/label
*/
public class FunctionSymbol extends SymbolDB {
@@ -103,9 +100,9 @@ public class FunctionSymbol extends SymbolDB {
try {
boolean restoreLabel = isExternal() || (getSource() != SourceType.DEFAULT);
String symName = getName();
String extData3 = null;
String extData = null;
if (isExternal()) {
extData3 = getSymbolData3(); // preserve external data
extData = getSymbolStringData(); // preserve external data
}
Namespace namespace = getParentNamespace();
SourceType source = getSource();
@@ -120,7 +117,7 @@ public class FunctionSymbol extends SymbolDB {
if (super.delete()) {
if (restoreLabel) {
boolean restored = createLabelForDeletedFunctionName(address, symName, extData3,
boolean restored = createLabelForDeletedFunctionName(address, symName, extData,
namespace, source, pinned);
if (!restored && isExternal()) {
removeAllReferencesTo();
@@ -140,7 +137,7 @@ public class FunctionSymbol extends SymbolDB {
* does not mean that we want to lose the function name (that is our policy).
*/
private boolean createLabelForDeletedFunctionName(Address entryPoint, String symName,
String data3, Namespace namespace, SourceType source, boolean pinned) {
String stringData, Namespace namespace, SourceType source, boolean pinned) {
if (isExternal()) {
SymbolDB parent = (SymbolDB) namespace.getSymbol();
if (parent.isDeleting()) {
@@ -151,7 +148,7 @@ public class FunctionSymbol extends SymbolDB {
SymbolDB newSym;
try {
newSym = symbolMgr.createSpecialSymbol(entryPoint, symName, namespace,
SymbolType.LABEL, -1, -1, data3, source);
SymbolType.LABEL, null, null, stringData, source);
if (pinned) {
newSym.setPinned(true);
}
@@ -161,7 +158,7 @@ public class FunctionSymbol extends SymbolDB {
newSym = (SymbolDB) symbolMgr.createLabel(entryPoint, symName,
program.getGlobalNamespace(), source);
}
newSym.setSymbolData3(data3); // reserved for external location use
newSym.setSymbolStringData(stringData);
newSym.setPrimary();
return true;
}
@@ -275,56 +272,11 @@ public class FunctionSymbol extends SymbolDB {
return super.doGetParentNamespace();
}
// @Override
// public void setNameAndNamespace(String newName, Namespace newNamespace, SourceType source)
// throws DuplicateNameException, InvalidInputException, CircularDependencyException {
//
// if (!isExternal()) {
//
// // Check thunk function name - if name matches thunked function
// // name switch to using DEFAULT name
//
// // NOTE: Removed this since this prevents having a second symbol
//
// Symbol thunkedSymbol = getThunkedSymbol();
// if (thunkedSymbol != null) {
// String thunkedName = thunkedSymbol.getName();
// if (thunkedName.equals(newName)) {
// newName = "";
// source = SourceType.DEFAULT;
// }
// }
// }
//
// super.setNameAndNamespace(newName, newNamespace, source);
// }
private Symbol getThunkedSymbol() {
long thunkedFunctionId = functionMgr.getThunkedFunctionId(key);
return (thunkedFunctionId >= 0) ? symbolMgr.getSymbol(thunkedFunctionId) : null;
}
// @Override
// public void setNameAndNamespace(String newName, Namespace newNamespace, SourceType source)
// throws DuplicateNameException, InvalidInputException, CircularDependencyException {
//
// long thunkedFunctionId = functionMgr.getThunkedFunctionId(key);
// if (thunkedFunctionId >= 0) {
// if (!newName.startsWith(Function.THUNK_PREFIX)) {
// // ignore
// return;
// }
// // rename thunked function based on thunk name specified
// Symbol s = symbolMgr.getSymbol(thunkedFunctionId);
// if (s != null) {
// s.setName(newName.substring(Function.THUNK_PREFIX.length()), source);
// }
// return;
// }
//
// super.setNameAndNamespace(newName, newNamespace, source);
// }
@Override
protected SourceType validateNameSource(String newName, SourceType source) {
// if (isThunk()) {
@@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -109,7 +108,7 @@ class LibraryDB implements Library {
@Override
public String getAssociatedProgramPath() {
return symbol.getSymbolData3();
return symbol.getSymbolStringData();
}
@Override
@@ -31,8 +31,7 @@ import ghidra.util.exception.InvalidInputException;
* Class for library symbols.
*
* Symbol data usage:
* int data2 - set to 0 (not used)
* String data3 - associated program project file path
* String stringData - associated program project file path
*/
public class LibrarySymbol extends SymbolDB {
@@ -64,8 +63,9 @@ public class LibrarySymbol extends SymbolDB {
super.setName(newName, source);
if (!oldName.equals(getName())) {
symbolMgr.getProgram().setObjChanged(ChangeManager.DOCR_EXTERNAL_NAME_CHANGED,
(Address) null, null, oldName, newName);
symbolMgr.getProgram()
.setObjChanged(ChangeManager.DOCR_EXTERNAL_NAME_CHANGED,
(Address) null, null, oldName, newName);
}
}
@@ -77,19 +77,21 @@ public class LibrarySymbol extends SymbolDB {
super.setNameAndNamespace(newName, newNamespace, source);
if (!oldName.equals(getName())) {
symbolMgr.getProgram().setObjChanged(ChangeManager.DOCR_EXTERNAL_NAME_CHANGED,
(Address) null, null, oldName, newName);
symbolMgr.getProgram()
.setObjChanged(ChangeManager.DOCR_EXTERNAL_NAME_CHANGED,
(Address) null, null, oldName, newName);
}
}
@Override
public void setSymbolData3(String newPath) {
String oldPath = getSymbolData3();
public void setSymbolStringData(String newPath) {
String oldPath = getSymbolStringData();
super.setSymbolData3(newPath);
super.setSymbolStringData(newPath);
symbolMgr.getProgram().setObjChanged(ChangeManager.DOCR_EXTERNAL_PATH_CHANGED, getName(),
oldPath, newPath);
symbolMgr.getProgram()
.setObjChanged(ChangeManager.DOCR_EXTERNAL_PATH_CHANGED, getName(),
oldPath, newPath);
}
public SymbolType getSymbolType() {
@@ -16,10 +16,10 @@
package ghidra.program.database.symbol;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import db.DBRecord;
import db.Field;
import ghidra.program.database.*;
import ghidra.program.database.external.ExternalLocationDB;
import ghidra.program.database.external.ExternalManagerDB;
@@ -141,21 +141,25 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
* @param newAddress the new address for the symbol
* @param newName the new name for the symbol (or null if the name should stay the same)
* @param newNamespace the new namespace for the symbol (or null if it should stay the same)
* @param newSource
* @param newSource the new SourceType for the symbol (or null if it should stay the same)
* @param pinned the new pinned state
*/
protected void moveLowLevel(Address newAddress, String newName, Namespace newNamespace,
SourceType newSource, boolean pinned) {
lock.acquire();
try {
checkDeleted();
Address oldAddress = address;
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL,
symbolMgr.getAddressMap().getKey(newAddress, true));
// update the address to the new location
long newAddressKey = symbolMgr.getAddressMap().getKey(newAddress, true);
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, newAddressKey);
// if the primary field is set, be sure to update it to the new address as well
if (record.getFieldValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL) != null) {
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, newAddressKey);
}
if (newName != null) {
record.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, newName);
if (newName.length() == 0) {
setSourceFlagBit(SourceType.DEFAULT);
}
}
if (newNamespace != null) {
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, newNamespace.getID());
@@ -793,32 +797,42 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
public String getSymbolData3() {
/**
* Returns the symbol's string data which has different meanings depending on the symbol type
* and whether or not it is external
* @return the symbol's string data
*/
public String getSymbolStringData() {
lock.acquire();
try {
checkIsValid();
if (record == null) {
return null;
}
return record.getString(SymbolDatabaseAdapter.SYMBOL_DATA3_COL);
return record.getString(SymbolDatabaseAdapter.SYMBOL_STRING_DATA_COL);
}
finally {
lock.release();
}
}
public void setSymbolData3(String data3) {
/**
* Sets the symbol's string data field. This field's data has different uses depending on the
* symbol type and whether or not it is external.
* @param stringData the string to store in the string data field
*/
public void setSymbolStringData(String stringData) {
lock.acquire();
try {
checkDeleted();
if (record == null) {
return;
}
String oldData = record.getString(SymbolDatabaseAdapter.SYMBOL_DATA3_COL);
if (SystemUtilities.isEqual(data3, oldData)) {
String oldData = record.getString(SymbolDatabaseAdapter.SYMBOL_STRING_DATA_COL);
if (Objects.equals(stringData, oldData)) {
return;
}
record.setString(SymbolDatabaseAdapter.SYMBOL_DATA3_COL, data3);
record.setString(SymbolDatabaseAdapter.SYMBOL_STRING_DATA_COL, stringData);
updateRecord();
symbolMgr.symbolDataChanged(this);
}
@@ -836,14 +850,18 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
public long getSymbolData1() {
public long getDataTypeId() {
lock.acquire();
try {
checkIsValid();
if (record != null) {
return record.getLongValue(SymbolDatabaseAdapter.SYMBOL_DATA1_COL);
Field value = record.getFieldValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL);
if (value.isNull()) {
return -1;
}
return value.getLongValue();
}
return 0;
return -1;
}
finally {
lock.release();
@@ -854,12 +872,12 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
* Sets the generic symbol data 1.
* @param value the value to set as symbol data 1.
*/
public void setSymbolData1(long value) {
public void setDataTypeId(long value) {
lock.acquire();
try {
checkDeleted();
if (record != null) {
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATA1_COL, value);
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL, value);
updateRecord();
symbolMgr.symbolDataChanged(this);
}
@@ -873,12 +891,12 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
* gets the generic symbol data 2 data.
* @return the symbol data
*/
public int getSymbolData2() {
protected int getVariableOffset() {
lock.acquire();
try {
checkIsValid();
if (record != null) {
return record.getIntValue(SymbolDatabaseAdapter.SYMBOL_DATA2_COL);
return record.getIntValue(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL);
}
return 0;
}
@@ -888,15 +906,16 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
/**
* Sets the generic symbol data 2 data
* @param value the value to set as the symbols data 2 value.
* Sets the symbol's variable offset. For parameters, this is the ordinal, for locals, it is
* the first use offset
* @param offset the value to set as the symbols variable offset.
*/
public void setSymbolData2(int value) {
public void setVariableOffset(int offset) {
lock.acquire();
try {
checkDeleted();
if (record != null) {
record.setIntValue(SymbolDatabaseAdapter.SYMBOL_DATA2_COL, value);
record.setIntValue(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL, offset);
updateRecord();
symbolMgr.symbolDataChanged(this);
}
@@ -906,6 +925,42 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
protected void doSetPrimary(boolean primary) {
lock.acquire();
try {
checkDeleted();
if (record != null) {
if (primary) {
long addrKey = record.getLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL);
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, addrKey);
}
else {
record.setField(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, null);
}
updateRecord();
symbolMgr.symbolDataChanged(this);
}
}
finally {
lock.release();
}
}
protected boolean doCheckIsPrimary() {
lock.acquire();
try {
checkIsValid();
if (record != null) {
return !record.getFieldValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL).isNull();
}
return false;
}
finally {
lock.release();
}
}
@Override
public boolean delete() {
lock.acquire();
@@ -16,14 +16,16 @@
package ghidra.program.database.symbol;
import java.io.IOException;
import java.util.Objects;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import db.*;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolType;
import ghidra.program.database.util.*;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
@@ -32,24 +34,23 @@ import ghidra.util.task.TaskMonitor;
* Adapter to access records in the symbol table.
*/
abstract class SymbolDatabaseAdapter {
static final String SYMBOL_TABLE_NAME = "Symbols";
static final Schema SYMBOL_SCHEMA = new Schema(2, "Key",
new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE,
ByteField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE, StringField.INSTANCE,
ByteField.INSTANCE },
new String[] { "Name", "Address", "Parent", "Symbol Type", "SymbolData1", "SymbolData2",
"SymbolData3", "Flags" });
static final int SYMBOL_NAME_COL = 0;
static final int SYMBOL_ADDR_COL = 1;
static final int SYMBOL_PARENT_COL = 2;
static final int SYMBOL_TYPE_COL = 3;
static final int SYMBOL_DATA1_COL = 4;
static final int SYMBOL_DATA2_COL = 5;
static final int SYMBOL_DATA3_COL = 6;
static final int SYMBOL_FLAGS_COL = 7;
static final int SYMBOL_STRING_DATA_COL = 4;
static final int SYMBOL_FLAGS_COL = 5;
// sparse fields - the following fields are not always applicable so they are optional and
// don't consume space in the database if they aren't used.
static final int SYMBOL_HASH_COL = 6;
static final int SYMBOL_PRIMARY_COL = 7;
static final int SYMBOL_DATATYPE_COL = 8;
static final int SYMBOL_VAROFFSET_COL = 9;
static final Schema SYMBOL_SCHEMA = SymbolDatabaseAdapterV3.V3_SYMBOL_SCHEMA;
// Bits 0 & 1 are used for the source of the symbol.
static final byte SYMBOL_SOURCE_BITS = (byte) 0x3;
@@ -59,26 +60,24 @@ abstract class SymbolDatabaseAdapter {
/**
* Gets a new SymbolDatabaseAdapter
* @param dbHandle the database handle.
* @param openMode the openmode
* @param dbHandle the database handle
* @param openMode the open mode. See {@link DBConstants}
* @param addrMap the address map
* @param monitor the progress monitor.
* @throws VersionException if the database table does not match the adapter.
* @throws CancelledException if the user cancels an upgrade.
* @throws IOException if a database io error occurs.
* @param monitor the progress monitor
* @return a new SymbolDatabaseAdapter
* @throws VersionException if the database table does not match the adapter
* @throws CancelledException if the user cancels an upgrade
* @throws IOException if a database io error occurs
*/
static SymbolDatabaseAdapter getAdapter(DBHandle dbHandle, int openMode, AddressMap addrMap,
TaskMonitor monitor) throws VersionException, CancelledException, IOException {
if (openMode == DBConstants.CREATE) {
return new SymbolDatabaseAdapterV2(dbHandle, addrMap, true);
return new SymbolDatabaseAdapterV3(dbHandle, addrMap, true);
}
try {
SymbolDatabaseAdapter adapter = new SymbolDatabaseAdapterV2(dbHandle, addrMap, false);
if (addrMap.isUpgraded()) {
throw new VersionException(true);
}
SymbolDatabaseAdapter adapter = new SymbolDatabaseAdapterV3(dbHandle, addrMap, false);
return adapter;
}
catch (VersionException e) {
@@ -87,7 +86,7 @@ abstract class SymbolDatabaseAdapter {
}
SymbolDatabaseAdapter adapter = findReadOnlyAdapter(dbHandle, addrMap);
if (openMode == DBConstants.UPGRADE) {
adapter = SymbolDatabaseAdapterV2.upgrade(dbHandle, addrMap, adapter, monitor);
adapter = upgrade(dbHandle, addrMap, adapter, monitor);
}
else if (adapter instanceof SymbolDatabaseAdapterV0) {
// Upgrade required - read-only use not supported
@@ -101,39 +100,125 @@ abstract class SymbolDatabaseAdapter {
throws VersionException, IOException {
try {
return new SymbolDatabaseAdapterV2(handle, addrMap.getOldAddressMap(), false);
return new SymbolDatabaseAdapterV2(handle, addrMap.getOldAddressMap());
}
catch (VersionException e1) {
// failed try older version
}
try {
return new SymbolDatabaseAdapterV1(handle, addrMap.getOldAddressMap());
}
catch (VersionException e1) {
// failed try older version
}
return new SymbolDatabaseAdapterV0(handle, addrMap);
try {
return new SymbolDatabaseAdapterV0(handle, addrMap.getOldAddressMap());
}
catch (VersionException e1) {
// failed - can't handle whatever version this is trying to open
}
throw new VersionException(false);
}
static SymbolDatabaseAdapter upgrade(DBHandle dbHandle, AddressMap addrMap,
SymbolDatabaseAdapter oldAdapter, TaskMonitor monitor)
throws VersionException, IOException, CancelledException {
monitor.setMessage("Upgrading Symbol Table...");
monitor.initialize(oldAdapter.getSymbolCount() * 2);
DBHandle tmpHandle = dbHandle.getScratchPad();
try {
SymbolDatabaseAdapter tmpAdapter =
copyToTempAndFixupRecords(addrMap, oldAdapter, tmpHandle, monitor);
dbHandle.deleteTable(SYMBOL_TABLE_NAME);
SymbolDatabaseAdapter newAdapter =
new SymbolDatabaseAdapterV3(dbHandle, addrMap, true);
copyTempToNewAdapter(tmpAdapter, newAdapter, monitor);
return newAdapter;
}
finally {
tmpHandle.deleteTable(SYMBOL_TABLE_NAME);
}
}
private static SymbolDatabaseAdapter copyToTempAndFixupRecords(AddressMap addrMap,
SymbolDatabaseAdapter oldAdapter, DBHandle tmpHandle, TaskMonitor monitor)
throws IOException, CancelledException, VersionException {
AddressMap oldAddrMap = addrMap.getOldAddressMap();
long nextKey = 1; // only used for V0 upgrade if a record with key 0 is encountered
if (oldAdapter instanceof SymbolDatabaseAdapterV0) {
// V0 is so old that there is not enough info in the current record to create new
// records. So store the current info in a temp database table and complete the upgrade
// when SymbolManager.programReady() is called. The missing info can be retrieved from
// other managers in the program at that point.
nextKey =
((SymbolDatabaseAdapterV0) oldAdapter).extractLocalSymbols(tmpHandle, monitor);
}
SymbolDatabaseAdapterV3 tmpAdapter = new SymbolDatabaseAdapterV3(tmpHandle, addrMap, true);
RecordIterator iter = oldAdapter.getSymbols();
while (iter.hasNext()) {
monitor.checkCanceled();
DBRecord rec = iter.next();
Address addr = oldAddrMap.decodeAddress(rec.getLongValue(SYMBOL_ADDR_COL));
rec.setLongValue(SYMBOL_ADDR_COL, addrMap.getKey(addr, true));
// We don't allow 0 keys starting with V1, set its key to next available
// which we got from the call to extractLocalSymbols() above
if (rec.getKey() == 0) {
rec.setKey(Math.max(1, nextKey));
}
tmpAdapter.updateSymbolRecord(rec);
monitor.incrementProgress(1);
}
return tmpAdapter;
}
private static void copyTempToNewAdapter(SymbolDatabaseAdapter tmpAdapter,
SymbolDatabaseAdapter newAdapter, TaskMonitor monitor)
throws IOException, CancelledException {
RecordIterator iter = tmpAdapter.getSymbols();
while (iter.hasNext()) {
monitor.checkCanceled();
newAdapter.updateSymbolRecord(iter.next());
monitor.incrementProgress(1);
}
}
/**
* Create a new symbol
* @param name name of the symbol
* @param addr address of the symbol
* @param parentSymbolID the id of the containing namespace symbol
* @param address the address for the symbol
* @param namespaceID the id of the containing namespace symbol
* @param symbolType the type of this symbol
* @param data1 place to store a long value that depends on the symbol type
* @param data2 place to store an int value that depends on the symbol type
* @param data3 place to store a String value that depends on the symbol type
* @param source the source of this symbol
* <br>Some symbol types, such as function symbols, can set the source to Symbol.DEFAULT.
* @param stringData place to store a String value that depends on the symbol type
* @param source the source type of this symbol
* Some symbol types, such as function symbols, can set the source to Symbol.DEFAULT
* @param dataTypeId the id of an associated datatype or null if there is no associated datatype
* @param varOffset the variable offset will be the ordinal for a parameter or first use offset
* for a local variable
* @param isPrimary true if the symbol is primary. Only applicable for labels and functions
* @return the new record
* @throws IOException if there was a problem accessing the database
* @throws IllegalArgumentException if you try to set the source to DEFAULT for a symbol type
* that doesn't allow it.
* that doesn't allow it
*/
abstract DBRecord createSymbol(String name, Address address, long parentSymbolID,
SymbolType symbolType, long data1, int data2, String data3, SourceType source)
throws IOException;
abstract DBRecord createSymbol(String name, Address address, long namespaceID,
SymbolType symbolType, String stringData, Long dataTypeId, Integer varOffset,
SourceType source, boolean isPrimary) throws IOException;
/**
* Get the record with the given symbol ID
@@ -160,58 +245,96 @@ abstract class SymbolDatabaseAdapter {
abstract boolean hasSymbol(Address addr) throws IOException;
/**
* Get the symbolIDs at the given address.
* Get the symbolIDs at the given address
* @param addr address to filter on
* @return array of database LongField keys contained within a Field array.
* @return array of database LongField keys contained within a Field array
* @throws IOException if there was a problem accessing the database
*/
abstract Field[] getSymbolIDs(Address addr) throws IOException;
/**
* Get the number of symbols.
* Get the number of symbols
* @return the number of symbols
*/
abstract int getSymbolCount();
/**
* Get an iterator over all the symbols in ascending address order.
* @return
* Get an iterator over all the symbols in ascending address order
* @param forward the direction to iterator
* @return a record iterator over all symbols
* @throws IOException if there was a problem accessing the database
*/
abstract RecordIterator getSymbolsByAddress(boolean forward) throws IOException;
/**
* Get an iterator over all the symbols starting at startAddr.
* Get an iterator over all the symbols starting at startAddr
* @param startAddr start address of where to get symbols
* @param forward true to iterate from low to high addresses
* @return a record iterator over all symbols starting at the given start address
* @throws IOException if there was a problem accessing the database
*/
abstract RecordIterator getSymbolsByAddress(Address startAddr, boolean forward)
throws IOException;
/**
* Update the table with the given record.
* @param record
* Update the table with the given record
* @param record the record to update in the database
* @throws IOException if there was a problem accessing the database
*/
abstract void updateSymbolRecord(DBRecord record) throws IOException;
/**
* Get all of the symbols.
* @return a record iterator over all symbols
* @throws IOException if there was a problem accessing the database
*/
abstract RecordIterator getSymbols() throws IOException;
/**
* Get symbols in the given range.
* @throws IOException if there was a problem accessing the database
* Get symbols in the given range
* @param start the start address of the range
* @param end the last address of the range
* @param forward true if iterating from start to end, otherwise iterate from end to start
* @return a record iterator for all symbols in the range
* @throws IOException if a database io error occurs
*/
abstract RecordIterator getSymbols(Address start, Address end, boolean forward)
throws IOException;
/**
* Get symbols in the given range
* @param set the set of addresses to iterate over
* @param forward true if iterating from start to end, otherwise iterate from end to start
* @return a record iterator for all symbols in the range
* @throws IOException if a database io error occurs
*/
abstract RecordIterator getSymbols(AddressSetView set, boolean forward)
throws IOException;
/**
* Returns an iterator over the primary symbols in the given range
* @param set the address set to iterator over when getting primary symbol records
* @param forward true if iterating from start to end, otherwise iterate from end to start
* @return a record iterator for all symbols in the range
* @throws IOException if a database io error occurs
*/
abstract RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException;
/**
* Returns the symbol record for the primary symbol at the given address
* @param address the address to get its primary symbol record
* @return the primary symbol record at the given address or null if no label or function
* exists at that address
* @throws IOException if a database io error occurs
*/
abstract DBRecord getPrimarySymbol(Address address) throws IOException;
/**
* Update the address in all records to reflect the movement of a symbol address.
* @param oldAddr the original symbol address
* @param newAddr the new symbol address
* @throws IOException
* @throws IOException if a database io error occurs
*/
abstract void moveAddress(Address oldAddr, Address newAddr) throws IOException;
@@ -221,38 +344,125 @@ abstract class SymbolDatabaseAdapter {
* @param endAddr maximum address in range
* @param monitor progress monitor
* @return returns the set of addresses where symbols where not deleted because they were anchored
* @throws CancelledException
* @throws IOException
* @throws CancelledException if the user cancels the operation
* @throws IOException if a database io error occurs
*/
abstract Set<Address> deleteAddressRange(Address startAddr, Address endAddr,
TaskMonitor monitor) throws CancelledException, IOException;
/**
* Get all symbols contained within the specified namespace
* @param id the namespace id.
* @return an iterator over all symbols in the given namespace.
* @throws IOException
* @param id the namespace id
* @return an iterator over all symbols in the given namespace
* @throws IOException if a database io error occurs
*/
abstract RecordIterator getSymbolsByNamespace(long id) throws IOException;
/**
* Get symbols starting with the specified name in name order
* @param name name to start with.
* @return a record iterator over the symbols.
* @throws IOException if a database io error occurs.
* Get symbols that have the specified name
* @param name name to search
* @return a record iterator over the symbols with the given name
* @throws IOException if a database io error occurs
*/
abstract RecordIterator getSymbolsByName(String name) throws IOException;
/**
* Returns the maximum symbol address within the specified address space.
* Intended for update use only.
* Get all symbols contained in the given {@link Namespace} that have the given name
* @param name the symbol name
* @param id the id of the parent namespace
* @return a record iterator all the symbols in the given namespace with the given name
* @throws IOException if a database io error occurs
*/
abstract RecordIterator getSymbolsByNameAndNamespace(String name, long id) throws IOException;
/**
* Get the symbol Record with the given address, name, and namespace id or null if there is
* no match
* @param address the symbol address
* @param name the symbol name
* @param namespaceId the id of the parent namespace of the symbol
* @return a record that matches the address, name, and namespaceId or null if there is no match
* @throws IOException if a database io error occurs
*/
abstract DBRecord getSymbolRecord(Address address, String name, long namespaceId)
throws IOException;
/**
* Returns the maximum symbol address within the specified address space
* Intended for update use only
* @param space address space
* @return maximum symbol address within space or null if none are found.
* @return maximum symbol address within space or null if none are found
* @throws IOException if a database io error occurs
*/
abstract Address getMaxSymbolAddress(AddressSpace space) throws IOException;
/**
* Returns the underlying symbol table (for upgrade use only).
* Returns the underlying symbol table (for upgrade use only)
* @return the database table for this adapter
*/
abstract Table getTable();
/**
* Computes a hash value for a symbol that facilitates fast lookups of symbols given
* a name, namespace, and address. The hash is formed so that it can also be used for fast
* lookups of all symbols that have the same name and namespace regardless of address.
* @param name the symbol name
* @param namespaceID the namespace id
* @param addressKey the encoded address
* @return a database Long field containing the computed hash
*/
protected static LongField computeLocatorHash(String name, long namespaceID,
long addressKey) {
// Default functions have no name, no point in storing a hash for those.
if (StringUtils.isEmpty(name)) {
return null;
}
// store the name/namespace hash in upper 32 bits of the resulting hash and the
// addressKey's lower 32 bits in the lower 32 bits of the resulting hash
long nameNamespaceHash = Objects.hash(name, namespaceID);
long combinedHash = (nameNamespaceHash << 32) | (addressKey & 0xFFFFFFFFL);
return new LongField(combinedHash);
}
// This wraps a record iterator to make sure it only returns records for symbols that match
// the given name and name space.
protected static RecordIterator getNameAndNamespaceFilterIterator(String name,
long namespaceId, RecordIterator it) {
Query nameQuery = new FieldMatchQuery(SYMBOL_NAME_COL, new StringField(name));
Query namespaceQuery = new FieldMatchQuery(SYMBOL_PARENT_COL, new LongField(namespaceId));
Query nameAndNamespaceQuery = new AndQuery(nameQuery, namespaceQuery);
return new QueryRecordIterator(it, nameAndNamespaceQuery);
}
/**
* Wraps a record iterator to make sure it only returns records for symbols that match
* the given name and name space and address
* @param name the name of the symbol
* @param namespaceId the name space id of the symbol
* @param addressKey the address key of the symbol
* @param it the record iterator to wrap with the query
* @return a filtered RecordIterator that only returns records that match the name, name space,
* and address
*/
protected static RecordIterator getNameNamespaceAddressFilterIterator(String name,
long namespaceId, long addressKey, RecordIterator it) {
Query nameQuery = new FieldMatchQuery(SYMBOL_NAME_COL, new StringField(name));
Query namespaceQuery = new FieldMatchQuery(SYMBOL_PARENT_COL, new LongField(namespaceId));
Query addressQuery = new FieldMatchQuery(SYMBOL_ADDR_COL, new LongField(addressKey));
Query nameAndNamespaceQuery = new AndQuery(nameQuery, namespaceQuery);
Query fullQuery = new AndQuery(nameAndNamespaceQuery, addressQuery);
return new QueryRecordIterator(it, fullQuery);
}
/**
* Wraps a record iterator to filter out any symbols that are not primary
* @param it the record iterator to wrap
* @return a record iterator that only returns primary symbols
*/
protected static RecordIterator getPrimaryFilterRecordIterator(RecordIterator it) {
Query query = record -> !record.getFieldValue(SYMBOL_PRIMARY_COL).isNull();
return new QueryRecordIterator(it, query);
}
}
@@ -21,8 +21,7 @@ import java.util.Set;
import db.*;
import ghidra.program.database.map.AddressIndexPrimaryKeyIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
@@ -58,13 +57,10 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
private AddressMap addrMap;
/**
* Construct a Version-0 Symbol Table adadpter.
* Construct a Version-0 Symbol Table adapter.
* @param handle the database handle.
* @param addrMap the address map
* @param namespaceMgr namespace manager which already contains function namespaces
* @throws VersionException if the database version doesn't match this adapter.
* @throws IOException if a database io error occurs.
* @throws CancelledException if the user cancels the upgrade.
*/
SymbolDatabaseAdapterV0(DBHandle handle, AddressMap addrMap) throws VersionException {
this.addrMap = addrMap.getOldAddressMap();
@@ -77,10 +73,22 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
}
}
/**
* Stores local symbols information in a temporary database table because this version
* is so old, we don't have enough information in the record to upgrade during the normal
* upgrade time. So we store off the information and will complete this upgrade when
* {@link SymbolManager#programReady(int, int, TaskMonitor)} is called
*
* @param handle handle to temporary database
* @param monitor the {@link TaskMonitor}
* @return the next available database key after all the records are store
* @throws IOException if a database I/O error occurs
* @throws CancelledException if the user cancels the upgrade
*/
long extractLocalSymbols(DBHandle handle, TaskMonitor monitor)
throws IOException, CancelledException {
monitor.setMessage("Extracting Local and Dynamic Symbols...");
monitor.setMessage("Extracting Local Symbols...");
monitor.initialize(symbolTable.getRecordCount());
int cnt = 0;
RecordIterator iter = symbolTable.iterator();
@@ -106,22 +114,36 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
throw new AssertException("Unexpected Symbol");
}
DBRecord rec = SymbolDatabaseAdapter.SYMBOL_SCHEMA.createRecord(record.getKey());
rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, record.getString(V0_SYMBOL_NAME_COL));
String symbolName = record.getString(V0_SYMBOL_NAME_COL);
rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, symbolName);
long addressKey = record.getLongValue(V0_SYMBOL_ADDR_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL,
record.getLongValue(V0_SYMBOL_ADDR_COL));
rec.setIntValue(SymbolDatabaseAdapter.SYMBOL_DATA2_COL,
record.getBooleanValue(V0_SYMBOL_PRIMARY_COL) ? 1 : 0);
addressKey);
boolean isPrimary = record.getBooleanValue(V0_SYMBOL_PRIMARY_COL);
if (isPrimary) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, addressKey);
}
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, SymbolType.LABEL.getID());
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATA1_COL, -1); // not applicable
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, Namespace.GLOBAL_NAMESPACE_ID);
long namespaceId = Namespace.GLOBAL_NAMESPACE_ID;
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, namespaceId);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL,
(byte) SourceType.USER_DEFINED.ordinal());
Field hash = computeLocatorHash(symbolName, namespaceId, addressKey);
rec.setField(SymbolDatabaseAdapter.SYMBOL_HASH_COL, hash);
return rec;
}
@Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType,
long data1, int data2, String data3, SourceType source) {
String stringData, Long dataTypeId, Integer varOffset, SourceType source,
boolean isPrimary) throws IOException {
throw new UnsupportedOperationException();
}
@@ -175,16 +197,37 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
@Override
RecordIterator getSymbols(Address start, Address end, boolean forward) throws IOException {
if (!forward)
throw new UnsupportedOperationException();
//TODO: Is there any reason we need to support reverse symbol iteration ???
// Yes, to search text backwards!
return new V0ConvertedRecordIterator(
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
V0_SYMBOL_ADDR_COL, addrMap, start, end, forward)));
}
@Override
RecordIterator getSymbols(AddressSetView set, boolean forward) throws IOException {
return new V0ConvertedRecordIterator(
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
V0_SYMBOL_ADDR_COL, addrMap, set, forward)));
}
@Override
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException {
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
return getPrimaryFilterRecordIterator(new V0ConvertedRecordIterator(it));
}
@Override
DBRecord getPrimarySymbol(Address address) throws IOException {
RecordIterator it = getPrimarySymbols(new AddressSet(address, address), true);
if (it.hasNext()) {
return it.next();
}
return null;
}
@Override
void moveAddress(Address oldAddr, Address newAddr) throws IOException {
throw new UnsupportedOperationException();
@@ -219,8 +262,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
/**
* Construct a symbol filtered record iterator
* @param iter
* @param locals if true
* @param symIter the {@link RecordIterator} to wrap so that records are adapter to new schema
*/
V0ConvertedRecordIterator(RecordIterator symIter) {
this.symIter = symIter;
@@ -277,4 +319,23 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
throw new UnsupportedOperationException();
}
@Override
RecordIterator getSymbolsByNameAndNamespace(String name, long id) throws IOException {
RecordIterator symbolsByName = getSymbolsByName(name);
return getNameAndNamespaceFilterIterator(name, id, symbolsByName);
}
@Override
DBRecord getSymbolRecord(Address address, String name, long id) throws IOException {
StringField value = new StringField(name);
RecordIterator it = symbolTable.indexIterator(SYMBOL_NAME_COL, value, value, true);
long addressKey = addrMap.getKey(address, false);
RecordIterator filtered =
getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
if (filtered.hasNext()) {
return filtered.next();
}
return null;
}
}
@@ -21,8 +21,7 @@ import java.util.Set;
import db.*;
import ghidra.program.database.map.AddressIndexPrimaryKeyIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
@@ -72,7 +71,8 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
@Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType,
long data1, int data2, String data3, SourceType source) {
String stringData, Long dataTypeId, Integer varOffset, SourceType source,
boolean isPrimary) throws IOException {
throw new UnsupportedOperationException();
}
@@ -116,20 +116,21 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
DBRecord rec = SymbolDatabaseAdapter.SYMBOL_SCHEMA.createRecord(record.getKey());
String symbolName = record.getString(V1_SYMBOL_NAME_COL);
rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, symbolName);
long symbolAddrKey = record.getLongValue(V1_SYMBOL_ADDR_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, symbolAddrKey);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL,
record.getLongValue(V1_SYMBOL_PARENT_COL));
byte symbolType = record.getByteValue(V1_SYMBOL_TYPE_COL);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, symbolType);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATA1_COL,
record.getLongValue(V1_SYMBOL_DATA1_COL));
rec.setIntValue(SymbolDatabaseAdapter.SYMBOL_DATA2_COL,
record.getIntValue(V1_SYMBOL_DATA2_COL));
rec.setString(SymbolDatabaseAdapter.SYMBOL_DATA3_COL,
long namespaceId = record.getLongValue(V1_SYMBOL_PARENT_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, namespaceId);
byte symbolTypeId = record.getByteValue(V1_SYMBOL_TYPE_COL);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, symbolTypeId);
rec.setString(SymbolDatabaseAdapter.SYMBOL_STRING_DATA_COL,
record.getString(V1_SYMBOL_COMMENT_COL));
SourceType source = SourceType.USER_DEFINED;
if (symbolType == SymbolType.FUNCTION.getID()) {
if (symbolTypeId == SymbolType.FUNCTION.getID()) {
Address symbolAddress = addrMap.decodeAddress(symbolAddrKey);
String defaultName = SymbolUtilities.getDefaultFunctionName(symbolAddress);
if (symbolName.equals(defaultName)) {
@@ -137,6 +138,33 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
}
}
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, (byte) source.ordinal());
long dataTypeId = record.getLongValue(V1_SYMBOL_DATA1_COL);
if (dataTypeId != -1) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL, dataTypeId);
}
SymbolType type = SymbolType.getSymbolType(symbolTypeId);
int data2 = record.getIntValue(V1_SYMBOL_DATA2_COL);
// The data1 field was used in two ways for label symbols, it stored a 1 for primary and 0
// for non-primary. If the type was a parameter or variable, it stored the ordinal or
// first use offset respectively.
if (SymbolType.LABEL.equals(type)) {
if (data2 == 1) { // if it was primary, put the address in the indexed primary col
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, symbolAddrKey);
}
}
else if (SymbolType.PARAMETER.equals(type) || SymbolType.LOCAL_VAR.equals(type)) {
rec.setIntValue(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL, data2);
}
// also need to store primary for functions
if (SymbolType.FUNCTION.equals(type)) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, symbolAddrKey);
}
Field hash = computeLocatorHash(symbolName, namespaceId, symbolAddrKey);
rec.setField(SymbolDatabaseAdapter.SYMBOL_HASH_COL, hash);
return rec;
}
@@ -175,8 +203,30 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
V1_SYMBOL_ADDR_COL, addrMap, start, end, forward)));
}
RecordIterator getSymbolsByName() throws IOException {
return new V1ConvertedRecordIterator(symbolTable.indexIterator(V1_SYMBOL_NAME_COL));
@Override
RecordIterator getSymbols(AddressSetView set, boolean forward) throws IOException {
return new V1ConvertedRecordIterator(
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
V1_SYMBOL_ADDR_COL, addrMap, set, forward)));
}
@Override
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException {
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
return getPrimaryFilterRecordIterator(new V1ConvertedRecordIterator(it));
}
@Override
DBRecord getPrimarySymbol(Address address) throws IOException {
RecordIterator it = getPrimarySymbols(new AddressSet(address, address), true);
if (it.hasNext()) {
return it.next();
}
return null;
}
@Override
@@ -225,4 +275,23 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
Address getMaxSymbolAddress(AddressSpace space) throws IOException {
throw new UnsupportedOperationException();
}
@Override
RecordIterator getSymbolsByNameAndNamespace(String name, long id) throws IOException {
RecordIterator symbolsByName = getSymbolsByName(name);
return getNameAndNamespaceFilterIterator(name, id, symbolsByName);
}
@Override
DBRecord getSymbolRecord(Address address, String name, long id) throws IOException {
RecordIterator it = getSymbolsByName(name);
long addressKey = addrMap.getKey(address, false);
RecordIterator filtered =
getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
if (filtered.hasNext()) {
return filtered.next();
}
return null;
}
}
@@ -22,10 +22,11 @@ import java.util.Set;
import db.*;
import ghidra.program.database.map.*;
import ghidra.program.database.util.RecordFilter;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.*;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolType;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
/**
@@ -33,185 +34,54 @@ import ghidra.util.task.TaskMonitor;
*/
class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
/* Do not remove the following commented out schema! It shows the version 2 symbol table schema. */
// static final Schema SYMBOL_SCHEMA = new Schema(2, "Key",
// new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE,
// ByteField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE, StringField.INSTANCE,
// ByteField.INSTANCE },
// new String[] { "Name", "Address", "Parent", "Symbol Type", "SymbolData1", "SymbolData2",
// "SymbolData3", "Flags" });
private static final int SYMBOL_VERSION = 2;
private Table symbolTable;
private AddressMap addrMap;
SymbolDatabaseAdapterV2(DBHandle handle, AddressMap addrMap, boolean create)
throws VersionException, IOException {
static final int V2_SYMBOL_NAME_COL = 0;
static final int V2_SYMBOL_ADDR_COL = 1;
static final int V2_SYMBOL_PARENT_COL = 2;
static final int V2_SYMBOL_TYPE_COL = 3;
static final int V2_SYMBOL_DATA1_COL = 4;
static final int V2_SYMBOL_DATA2_COL = 5;
static final int V2_SYMBOL_DATA3_COL = 6;
static final int V2_SYMBOL_FLAGS_COL = 7;
SymbolDatabaseAdapterV2(DBHandle handle, AddressMap addrMap)
throws VersionException {
this.addrMap = addrMap;
if (create) {
symbolTable = handle.createTable(SYMBOL_TABLE_NAME, SYMBOL_SCHEMA,
new int[] { SYMBOL_ADDR_COL, SYMBOL_NAME_COL, SYMBOL_PARENT_COL });
symbolTable = handle.getTable(SYMBOL_TABLE_NAME);
if (symbolTable == null) {
throw new VersionException("Missing Table: " + SYMBOL_TABLE_NAME);
}
else {
symbolTable = handle.getTable(SYMBOL_TABLE_NAME);
if (symbolTable == null) {
throw new VersionException("Missing Table: " + SYMBOL_TABLE_NAME);
}
if (symbolTable.getSchema().getVersion() != SYMBOL_VERSION) {
int version = symbolTable.getSchema().getVersion();
if (version < SYMBOL_VERSION) {
throw new VersionException(true);
}
throw new VersionException(VersionException.NEWER_VERSION, false);
}
}
}
static SymbolDatabaseAdapter upgrade(DBHandle dbHandle, AddressMap addrMap,
SymbolDatabaseAdapter oldAdapter, TaskMonitor monitor)
throws VersionException, IOException, CancelledException {
AddressMap oldAddrMap = addrMap.getOldAddressMap();
DBHandle tmpHandle = dbHandle.getScratchPad();
long nextKey = 1;
try {
if (oldAdapter instanceof SymbolDatabaseAdapterV0) {
// Defer upgrade of local symbols and remove dynamic symbols
nextKey =
((SymbolDatabaseAdapterV0) oldAdapter).extractLocalSymbols(tmpHandle, monitor);
}
monitor.setMessage("Upgrading Symbol Table...");
monitor.initialize((oldAdapter.getSymbolCount()) * 2);
int count = 0;
SymbolDatabaseAdapterV2 tmpAdapter =
new SymbolDatabaseAdapterV2(tmpHandle, addrMap, true);
RecordIterator iter = oldAdapter.getSymbols();
DBRecord zeroRecord = null;
while (iter.hasNext()) {
if (monitor.isCancelled()) {
throw new CancelledException();
}
DBRecord rec = iter.next();
Address addr = oldAddrMap.decodeAddress(rec.getLongValue(SYMBOL_ADDR_COL));
rec.setLongValue(SYMBOL_ADDR_COL, addrMap.getKey(addr, true));
if (rec.getKey() == 0) {
zeroRecord = rec;
}
else {
tmpAdapter.symbolTable.putRecord(rec);
}
monitor.setProgress(++count);
}
if (zeroRecord != null) {
tmpAdapter.createSymbol(Math.max(1, nextKey), zeroRecord);
}
// TODO keep this until I fix up SymbolManager
// AddressKeyIterator entryPts = oldAdapter.getExternalEntryInterator();
// while (entryPts.hasNext()) {
// if (monitor.isCancelled()) {
// throw new CancelledException();
// }
// Address addr = oldAddrMap.decodeAddress(entryPts.next());
// tmpAdapter.setExternalEntry(addr);
// monitor.setProgress(++count);
// }
dbHandle.deleteTable(SYMBOL_TABLE_NAME);
SymbolDatabaseAdapterV2 newAdapter =
new SymbolDatabaseAdapterV2(dbHandle, addrMap, true);
iter = tmpAdapter.getSymbols();
while (iter.hasNext()) {
if (monitor.isCancelled()) {
throw new CancelledException();
}
DBRecord rec = iter.next();
// Make sure user symbols do not start with reserved prefix
String name = rec.getString(SYMBOL_NAME_COL);
if (SymbolUtilities.startsWithDefaultDynamicPrefix(name)) {
rec.setString(SYMBOL_NAME_COL,
fixSymbolName(tmpAdapter, name, rec.getLongValue(SYMBOL_PARENT_COL)));
}
// TODO May want to check for default name to set flags when upgrading.
// long addr = rec.getLongValue(SYMBOL_ADDR_COL);
// Address address = addrMap.decodeAddress(addr);
// String defaultName = ???;
// byte flags = name.equals(defaultName) ? SYMBOL_DEFAULT_FLAG : SYMBOL_USER_DEFINED_FLAG;
// rec.setByteValue(SYMBOL_FLAGS_COL, SYMBOL_USER_DEFINED_FLAG);
newAdapter.symbolTable.putRecord(rec);
monitor.setProgress(++count);
}
return newAdapter;
}
finally {
tmpHandle.deleteTable(SYMBOL_TABLE_NAME);
}
}
/**
* @param zeroRecord
* @throws IOException
*/
private void createSymbol(long nextKey, DBRecord zeroRecord) throws IOException {
zeroRecord.setKey(nextKey);
symbolTable.putRecord(zeroRecord);
}
private static String fixSymbolName(SymbolDatabaseAdapter tmpAdapter, String name,
long namespaceId) throws IOException {
String baseName = "_" + name; // dynamic prefix is reserved
String newName = baseName;
int cnt = 0;
while (true) {
try {
RecordIterator iter = tmpAdapter.getSymbolsByName(newName);
while (iter.hasNext()) {
DBRecord otherRec = iter.next();
if (namespaceId == otherRec.getLongValue(SYMBOL_PARENT_COL)) {
throw new DuplicateNameException();
}
}
return newName;
}
catch (DuplicateNameException e) {
newName = baseName + "_" + (++cnt);
if (symbolTable.getSchema().getVersion() != SYMBOL_VERSION) {
int version = symbolTable.getSchema().getVersion();
if (version < SYMBOL_VERSION) {
throw new VersionException(true);
}
throw new VersionException(VersionException.NEWER_VERSION, false);
}
}
@Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType,
long data1, int data2, String data3, SourceType source) throws IOException {
long nextID = symbolTable.getKey();
// avoiding key 0, because we use the negative of the address offset as keys for dynamic symbols
if (nextID == 0) {
nextID++;
}
return createSymbol(nextID, name, address, namespaceID, symbolType, data1, data2, data3,
(byte) source.ordinal());
}
private DBRecord createSymbol(long id, String name, Address address, long namespaceID,
SymbolType symbolType, long data1, int data2, String data3, byte flags)
throws IOException {
DBRecord rec = symbolTable.getSchema().createRecord(id);
rec.setString(SYMBOL_NAME_COL, name);
rec.setLongValue(SYMBOL_ADDR_COL, addrMap.getKey(address, true));
rec.setLongValue(SYMBOL_PARENT_COL, namespaceID);
rec.setByteValue(SYMBOL_TYPE_COL, symbolType.getID());
rec.setLongValue(SYMBOL_DATA1_COL, data1);
rec.setIntValue(SYMBOL_DATA2_COL, data2);
rec.setString(SYMBOL_DATA3_COL, data3);
rec.setByteValue(SYMBOL_FLAGS_COL, flags);
symbolTable.putRecord(rec);
return rec;
String stringData, Long dataTypeId, Integer varOffset, SourceType source,
boolean isPrimary) throws IOException {
throw new UnsupportedOperationException();
}
@Override
void removeSymbol(long symbolID) throws IOException {
symbolTable.deleteRecord(symbolID);
throw new UnsupportedOperationException();
}
@Override
@@ -234,7 +104,7 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
@Override
DBRecord getSymbolRecord(long symbolID) throws IOException {
return symbolTable.getRecord(symbolID);
return convertV2Record(symbolTable.getRecord(symbolID));
}
@Override
@@ -244,30 +114,62 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
@Override
RecordIterator getSymbolsByAddress(boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable,
KeyToRecordIterator it = new KeyToRecordIterator(symbolTable,
new AddressIndexPrimaryKeyIterator(symbolTable, SYMBOL_ADDR_COL, addrMap, forward));
return new V2ConvertedRecordIterator(it);
}
@Override
RecordIterator getSymbolsByAddress(Address startAddr, boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, startAddr, forward));
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, startAddr, forward));
return new V2ConvertedRecordIterator(it);
}
@Override
void updateSymbolRecord(DBRecord record) throws IOException {
symbolTable.putRecord(record);
throw new UnsupportedOperationException();
}
@Override
RecordIterator getSymbols() throws IOException {
return symbolTable.iterator();
return new V2ConvertedRecordIterator(symbolTable.iterator());
}
@Override
RecordIterator getSymbols(Address start, Address end, boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, start, end, forward));
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, start, end, forward));
return new V2ConvertedRecordIterator(it);
}
@Override
RecordIterator getSymbols(AddressSetView set, boolean forward) throws IOException {
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
return new V2ConvertedRecordIterator(it);
}
@Override
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException {
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
return getPrimaryFilterRecordIterator(new V2ConvertedRecordIterator(it));
}
@Override
DBRecord getPrimarySymbol(Address address) throws IOException {
RecordIterator it = getPrimarySymbols(new AddressSet(address, address), true);
if (it.hasNext()) {
return it.next();
}
return null;
}
@Override
@@ -316,13 +218,15 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
@Override
RecordIterator getSymbolsByNamespace(long id) throws IOException {
LongField field = new LongField(id);
return symbolTable.indexIterator(SYMBOL_PARENT_COL, field, field, true);
RecordIterator it = symbolTable.indexIterator(SYMBOL_PARENT_COL, field, field, true);
return new V2ConvertedRecordIterator(it);
}
@Override
RecordIterator getSymbolsByName(String name) throws IOException {
StringField field = new StringField(name);
return symbolTable.indexIterator(SYMBOL_NAME_COL, field, field, true);
RecordIterator it = symbolTable.indexIterator(SYMBOL_NAME_COL, field, field, true);
return new V2ConvertedRecordIterator(it);
}
@Override
@@ -353,4 +257,97 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
Table getTable() {
return symbolTable;
}
@Override
RecordIterator getSymbolsByNameAndNamespace(String name, long id) throws IOException {
StringField value = new StringField(name);
RecordIterator it = symbolTable.indexIterator(SYMBOL_NAME_COL, value, value, true);
RecordIterator filtered = getNameAndNamespaceFilterIterator(name, id, it);
return new V2ConvertedRecordIterator(filtered);
}
@Override
DBRecord getSymbolRecord(Address address, String name, long id) throws IOException {
StringField value = new StringField(name);
RecordIterator it = symbolTable.indexIterator(SYMBOL_NAME_COL, value, value, true);
long addressKey = addrMap.getKey(address, false);
RecordIterator filtered =
getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
if (filtered.hasNext()) {
return filtered.next();
}
return null;
}
/**
* Returns a record matching the current database schema from the version 2 record.
* @param record the record matching the version 2 schema.
* @return a current symbol record.
*/
private DBRecord convertV2Record(DBRecord record) {
if (record == null) {
return null;
}
DBRecord rec = SymbolDatabaseAdapter.SYMBOL_SCHEMA.createRecord(record.getKey());
String symbolName = record.getString(V2_SYMBOL_NAME_COL);
rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, symbolName);
long symbolAddrKey = record.getLongValue(V2_SYMBOL_ADDR_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, symbolAddrKey);
long namespaceId = record.getLongValue(V2_SYMBOL_PARENT_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, namespaceId);
byte symbolTypeId = record.getByteValue(V2_SYMBOL_TYPE_COL);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, symbolTypeId);
rec.setString(SymbolDatabaseAdapter.SYMBOL_STRING_DATA_COL,
record.getString(V2_SYMBOL_DATA3_COL));
Field hash = computeLocatorHash(symbolName, namespaceId, symbolAddrKey);
rec.setField(SymbolDatabaseAdapter.SYMBOL_HASH_COL, hash);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL,
record.getByteValue(V2_SYMBOL_FLAGS_COL));
long dataTypeId = record.getLongValue(V2_SYMBOL_DATA1_COL);
if (dataTypeId != -1) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL, dataTypeId);
}
SymbolType type = SymbolType.getSymbolType(symbolTypeId);
int data2 = record.getIntValue(V2_SYMBOL_DATA2_COL);
// The data1 field was used in two ways for label symbols, it stored a 1 for primary and 0
// for non-primary. If the type was a parameter or variable, it stored the ordinal or
// first use offset respectively
if (SymbolType.LABEL.equals(type)) {
if (data2 == 1) { // if it was primary, put the address in the indexed primary col
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, symbolAddrKey);
}
}
else if (SymbolType.PARAMETER.equals(type) || SymbolType.LOCAL_VAR.equals(type)) {
rec.setIntValue(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL, data2);
}
// also need to store primary for functions
if (SymbolType.FUNCTION.equals(type)) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, symbolAddrKey);
}
return rec;
}
private class V2ConvertedRecordIterator extends ConvertedRecordIterator {
V2ConvertedRecordIterator(RecordIterator originalIterator) {
super(originalIterator, false);
}
@Override
protected DBRecord convertRecord(DBRecord record) {
return convertV2Record(record);
}
}
}
@@ -0,0 +1,348 @@
/* ###
* 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.program.database.symbol;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import db.*;
import ghidra.program.database.map.*;
import ghidra.program.database.util.EmptyRecordIterator;
import ghidra.program.database.util.RecordFilter;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolType;
import ghidra.util.exception.CancelledException;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
/**
* SymbolDatabaseAdapter for version 3
*
* This version provides for fast symbol lookup by namespace and name.
* It was created in June 2021 with ProgramDB version 24.
* It will be included in Ghidra starting at version 10.1
*/
class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
static final int SYMBOL_VERSION = 3;
// Used to create a range when searching symbols by name/namespace but don't care about address
private static final long MIN_ADDRESS_OFFSET = 0;
private static final long MAX_ADDRESS_OFFSET = -1;
// NOTE: the primary field duplicates the symbol's address when the symbol is primary. This
// allows us to index this field and quickly find the primary symbols. The field is sparse
// so that non-primary symbols don't consume any space for this field.
static final Schema V3_SYMBOL_SCHEMA = new Schema(SYMBOL_VERSION, "Key",
new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE,
ByteField.INSTANCE, StringField.INSTANCE, ByteField.INSTANCE,
LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE },
new String[] { "Name", "Address", "Namespace", "Symbol Type", "String Data", "Flags",
"Locator Hash", "Primary", "Datatype", "Variable Offset" },
new int[] { SYMBOL_HASH_COL, SYMBOL_PRIMARY_COL, SYMBOL_DATATYPE_COL,
SYMBOL_VAROFFSET_COL });
private Table symbolTable;
private AddressMap addrMap;
SymbolDatabaseAdapterV3(DBHandle handle, AddressMap addrMap, boolean create)
throws VersionException, IOException {
this.addrMap = addrMap;
if (create) {
symbolTable = handle.createTable(SYMBOL_TABLE_NAME, SYMBOL_SCHEMA,
new int[] { SYMBOL_ADDR_COL, SYMBOL_NAME_COL, SYMBOL_PARENT_COL, SYMBOL_HASH_COL,
SYMBOL_PRIMARY_COL });
}
else {
symbolTable = handle.getTable(SYMBOL_TABLE_NAME);
if (symbolTable == null) {
throw new VersionException("Missing Table: " + SYMBOL_TABLE_NAME);
}
if (symbolTable.getSchema().getVersion() != SYMBOL_VERSION) {
int version = symbolTable.getSchema().getVersion();
if (version < SYMBOL_VERSION) {
throw new VersionException(true);
}
throw new VersionException(VersionException.NEWER_VERSION, false);
}
}
}
@Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType,
String stringData, Long dataTypeId, Integer varOffset, SourceType source,
boolean isPrimary) throws IOException {
long nextID = symbolTable.getKey();
// avoiding key 0, as it is reserved for the global namespace
if (nextID == 0) {
nextID++;
}
return createSymbol(nextID, name, address, namespaceID, symbolType, stringData,
(byte) source.ordinal(), dataTypeId, varOffset, isPrimary);
}
private DBRecord createSymbol(long id, String name, Address address, long namespaceID,
SymbolType symbolType, String stringData, byte flags,
Long dataTypeId, Integer varOffset, boolean isPrimary) throws IOException {
long addressKey = addrMap.getKey(address, true);
DBRecord rec = symbolTable.getSchema().createRecord(id);
rec.setString(SYMBOL_NAME_COL, name);
rec.setLongValue(SYMBOL_ADDR_COL, addressKey);
rec.setLongValue(SYMBOL_PARENT_COL, namespaceID);
rec.setByteValue(SYMBOL_TYPE_COL, symbolType.getID());
rec.setString(SYMBOL_STRING_DATA_COL, stringData);
rec.setByteValue(SYMBOL_FLAGS_COL, flags);
// sparse columns - these columns don't apply to all symbols.
// they default to null unless specifically set. Null values don't consume space.
rec.setField(SYMBOL_HASH_COL,
computeLocatorHash(name, namespaceID, addressKey));
if (isPrimary) {
rec.setLongValue(SYMBOL_PRIMARY_COL, addressKey);
}
if (dataTypeId != null) {
rec.setLongValue(SYMBOL_DATATYPE_COL, dataTypeId);
}
if (varOffset != null) {
rec.setIntValue(SYMBOL_VAROFFSET_COL, varOffset);
}
symbolTable.putRecord(rec);
return rec;
}
@Override
void removeSymbol(long symbolID) throws IOException {
symbolTable.deleteRecord(symbolID);
}
@Override
boolean hasSymbol(Address addr) throws IOException {
long key = addrMap.getKey(addr, false);
if (key == AddressMap.INVALID_ADDRESS_KEY && !addr.equals(Address.NO_ADDRESS)) {
return false;
}
return symbolTable.hasRecord(new LongField(key), SYMBOL_ADDR_COL);
}
@Override
Field[] getSymbolIDs(Address addr) throws IOException {
long key = addrMap.getKey(addr, false);
if (key == AddressMap.INVALID_ADDRESS_KEY && !addr.equals(Address.NO_ADDRESS)) {
return Field.EMPTY_ARRAY;
}
return symbolTable.findRecords(new LongField(key), SYMBOL_ADDR_COL);
}
@Override
DBRecord getSymbolRecord(long symbolID) throws IOException {
return symbolTable.getRecord(symbolID);
}
@Override
int getSymbolCount() {
return symbolTable.getRecordCount();
}
@Override
RecordIterator getSymbolsByAddress(boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable,
new AddressIndexPrimaryKeyIterator(symbolTable, SYMBOL_ADDR_COL, addrMap, forward));
}
@Override
RecordIterator getSymbolsByAddress(Address startAddr, boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, startAddr, forward));
}
@Override
void updateSymbolRecord(DBRecord record) throws IOException {
// make sure hash is updated to current name and name space
String name = record.getString(SYMBOL_NAME_COL);
long namespaceId = record.getLongValue(SYMBOL_PARENT_COL);
long addressKey = record.getLongValue(SYMBOL_ADDR_COL);
record.setField(SYMBOL_HASH_COL,
computeLocatorHash(name, namespaceId, addressKey));
symbolTable.putRecord(record);
}
@Override
RecordIterator getSymbols() throws IOException {
return symbolTable.iterator();
}
@Override
RecordIterator getSymbols(Address start, Address end, boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, start, end, forward));
}
@Override
RecordIterator getSymbols(AddressSetView set, boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
}
@Override
protected RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_PRIMARY_COL, addrMap, set, forward));
}
@Override
protected DBRecord getPrimarySymbol(Address address) throws IOException {
AddressIndexPrimaryKeyIterator it = new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_PRIMARY_COL, addrMap, address, address, true);
if (it.hasNext()) {
return symbolTable.getRecord(it.next());
}
return null;
}
void deleteExternalEntries(Address start, Address end) throws IOException {
AddressRecordDeleter.deleteRecords(symbolTable, SYMBOL_ADDR_COL, addrMap, start, end, null);
}
@Override
void moveAddress(Address oldAddr, Address newAddr) throws IOException {
LongField oldKey = new LongField(addrMap.getKey(oldAddr, false));
long newKey = addrMap.getKey(newAddr, true);
Field[] keys = symbolTable.findRecords(oldKey, SYMBOL_ADDR_COL);
for (Field key : keys) {
DBRecord rec = symbolTable.getRecord(key);
rec.setLongValue(SYMBOL_ADDR_COL, newKey);
symbolTable.putRecord(rec);
}
}
@Override
Set<Address> deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
throws CancelledException, IOException {
AnchoredSymbolRecordFilter filter = new AnchoredSymbolRecordFilter();
AddressRecordDeleter.deleteRecords(symbolTable, SYMBOL_ADDR_COL, addrMap, startAddr,
endAddr, filter);
return filter.getAddressesForSkippedRecords();
}
@Override
RecordIterator getSymbolsByNamespace(long id) throws IOException {
LongField field = new LongField(id);
return symbolTable.indexIterator(SYMBOL_PARENT_COL, field, field, true);
}
@Override
RecordIterator getSymbolsByName(String name) throws IOException {
StringField field = new StringField(name);
return symbolTable.indexIterator(SYMBOL_NAME_COL, field, field, true);
}
@Override
RecordIterator getSymbolsByNameAndNamespace(String name, long id) throws IOException {
// create a range of hash fields for all symbols with this name and namespace id over all
// possible addresses
Field start = computeLocatorHash(name, id, MIN_ADDRESS_OFFSET);
if (start == null) {
return EmptyRecordIterator.INSTANCE;
}
Field end = computeLocatorHash(name, id, MAX_ADDRESS_OFFSET);
RecordIterator it = symbolTable.indexIterator(SYMBOL_HASH_COL, start, end, true);
return getNameAndNamespaceFilterIterator(name, id, it);
}
@Override
DBRecord getSymbolRecord(Address address, String name, long namespaceId) throws IOException {
long addressKey = addrMap.getKey(address, false);
Field search = computeLocatorHash(name, namespaceId, addressKey);
if (search == null) {
return null;
}
RecordIterator it = symbolTable.indexIterator(SYMBOL_HASH_COL, search, search, true);
RecordIterator filtered =
getNameNamespaceAddressFilterIterator(name, namespaceId, addressKey, it);
if (filtered.hasNext()) {
return filtered.next();
}
return null;
}
@Override
Address getMaxSymbolAddress(AddressSpace space) throws IOException {
if (space.isMemorySpace()) {
AddressIndexKeyIterator addressKeyIterator = new AddressIndexKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, space.getMinAddress(), space.getMaxAddress(), false);
if (addressKeyIterator.hasNext()) {
return addrMap.decodeAddress(addressKeyIterator.next());
}
}
else {
LongField max = new LongField(addrMap.getKey(space.getMaxAddress(), false));
DBFieldIterator iterator =
symbolTable.indexFieldIterator(null, max, false, SYMBOL_ADDR_COL);
if (iterator.hasPrevious()) {
LongField val = (LongField) iterator.previous();
Address addr = addrMap.decodeAddress(val.getLongValue());
if (space.equals(addr.getAddressSpace())) {
return addr;
}
}
}
return null;
}
@Override
Table getTable() {
return symbolTable;
}
private class AnchoredSymbolRecordFilter implements RecordFilter {
private Set<Address> set = new HashSet<Address>();
@Override
public boolean matches(DBRecord record) {
// only move symbols whose anchor flag is not on
Address addr = addrMap.decodeAddress(record.getLongValue(SYMBOL_ADDR_COL));
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
boolean pinned = (flags & SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG) != 0;
if (!pinned) {
return true;
}
set.add(addr);
return false;
}
Set<Address> getAddressesForSkippedRecords() {
return set;
}
}
}
@@ -36,9 +36,7 @@ import ghidra.util.task.TaskMonitor;
* Symbol class for function variables.
*
* Symbol Data Usage:
* long data1 - data type ID
* int data2 - first-use-offset / ordinal
* String data3 - variable comment
* String stringData - variable comment
*/
public class VariableSymbolDB extends SymbolDB {
@@ -174,8 +172,9 @@ public class VariableSymbolDB extends SymbolDB {
}
public FunctionDB getFunction() {
return (FunctionDB) symbolMgr.getFunctionManager().getFunction(
getParentNamespace().getID());
return (FunctionDB) symbolMgr.getFunctionManager()
.getFunction(
getParentNamespace().getID());
}
/**
@@ -252,7 +251,7 @@ public class VariableSymbolDB extends SymbolDB {
}
public DataType getDataType() {
DataType dt = symbolMgr.getDataType(getSymbolData1());
DataType dt = symbolMgr.getDataType(getDataTypeId());
if (dt == null) {
VariableStorage storage = getVariableStorage();
if (storage == null) {
@@ -286,8 +285,8 @@ public class VariableSymbolDB extends SymbolDB {
Address newAddr = variableMgr.getVariableStorageAddress(newStorage, true);
setAddress(newAddr); // this may be the only symbol which changes its address
if (dataTypeID != getSymbolData1()) {
setSymbolData1(dataTypeID);
if (dataTypeID != getDataTypeId()) {
setDataTypeId(dataTypeID);
}
else {
symbolMgr.symbolDataChanged(this);
@@ -302,22 +301,22 @@ public class VariableSymbolDB extends SymbolDB {
}
public int getFirstUseOffset() {
return type == SymbolType.PARAMETER ? 0 : getSymbolData2();
return type == SymbolType.PARAMETER ? 0 : getVariableOffset();
}
public void setFirstUseOffset(int firstUseOffset) {
if (type == SymbolType.LOCAL_VAR) {
setSymbolData2(firstUseOffset);
setVariableOffset(firstUseOffset);
}
}
public int getOrdinal() {
return type == SymbolType.PARAMETER ? getSymbolData2() : Integer.MIN_VALUE;
return type == SymbolType.PARAMETER ? getVariableOffset() : Integer.MIN_VALUE;
}
public void setOrdinal(int ordinal) {
if (type == SymbolType.PARAMETER) {
setSymbolData2(ordinal);
setVariableOffset(ordinal);
}
}
@@ -23,7 +23,9 @@ import db.RecordIterator;
/**
* Implementation of a RecordIterator that is always empty.
*/
public class EmptyRecordIterator implements RecordIterator {
public static final RecordIterator INSTANCE = new EmptyRecordIterator();
/**
* @see db.RecordIterator#hasNext()
@@ -760,6 +760,29 @@ public class SymbolUtilities {
return addr.getAddressSpace().getName() + Long.toHexString(addr.getOffset());
}
/**
* Returns true if the given name is a possible default parameter name or local variable name
*
* @param name the name to check to see if it is a possible default local or parameter name
* @return true if the given name is a possible default parameter name or local variable name
*/
public static boolean isPossibleDefaultLocalOrParamName(String name) {
if (isDefaultParameterName(name)) {
return true;
}
return name.startsWith(Function.DEFAULT_LOCAL_PREFIX);
}
/**
* Checks if the given name could be a default external location name
*
* @param name the name to check
* @return true if the given name is a possible default external location name
*/
public static boolean isPossibleDefaultExternalName(String name) {
return name.startsWith(DEFAULT_EXTERNAL_ENTRY_PREFIX);
}
public static boolean isDefaultLocalStackName(String name) {
if (name == null || name.length() == 0) {
return true;
@@ -1042,4 +1065,5 @@ public class SymbolUtilities {
public static Comparator<Symbol> getSymbolNameComparator() {
return CASE_INSENSITIVE_SYMBOL_NAME_COMPARATOR;
}
}
@@ -193,8 +193,8 @@ public interface ChangeManager {
public static final int DOCR_SYMBOL_ASSOCIATION_REMOVED = 51;
/**
* Symbol data changed. This corresponds to unspecified data
* changes within the symbol (e.g., Data1, Data2, Data3, or VariableStorage).
* Symbol data changed. This corresponds to various
* changes within the symbol (e.g., primary status, datatype, external path or VariableStorage).
*/
public static final int DOCR_SYMBOL_DATA_CHANGED = 52;
@@ -592,7 +592,6 @@ public interface ChangeManager {
*/
public final static int DOCR_TAG_REMOVED_FROM_FUNCTION = 157;
////////////////////////////////////////////////////////////////////////////
//
// DOCR_FUNCTION_CHANGED - Sub Event Types