Merge remote-tracking branch 'origin/GP-5498_ghidra1_ExternalSymbolDB--SQUASHED'

This commit is contained in:
ghidra1 2025-06-10 13:55:55 -04:00
commit 5b71018f82
54 changed files with 2538 additions and 1662 deletions

View file

@ -286,12 +286,6 @@ public abstract class AbstractDBTraceSymbol extends DBAnnotatedObject
return getReferenceCollection().size();
}
@Override
public boolean hasMultipleReferences() {
// TODO: Could be slightly more efficient by just iterating twice?
return getReferenceCount() > 1;
}
@Override
public boolean hasReferences() {
return !getReferenceCollection().isEmpty();
@ -564,8 +558,4 @@ public abstract class AbstractDBTraceSymbol extends DBAnnotatedObject
return false;
}
@Override
public boolean isExternalEntryPoint() {
return false;
}
}

View file

@ -27,7 +27,7 @@ import ghidra.util.exception.InvalidInputException;
* @deprecated function signatures should be modified in their entirety using
* either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}.
*/
@Deprecated(since = "11.1")
@Deprecated(forRemoval = true, since = "11.1")
public class AddMemoryParameterCommand extends AddParameterCommand {
private final Address memAddr;

View file

@ -34,7 +34,7 @@ import ghidra.util.exception.InvalidInputException;
* @deprecated function signatures should be modified in their entirety using
* either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}.
*/
@Deprecated(since = "11.1")
@Deprecated(forRemoval = true, since = "11.1")
public class AddParameterCommand implements Command<Program> {
protected final Function function;

View file

@ -27,7 +27,7 @@ import ghidra.util.exception.InvalidInputException;
* @deprecated function signatures should be modified in their entirety using
* either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}.
*/
@Deprecated(since = "11.1")
@Deprecated(forRemoval = true, since = "11.1")
public class AddRegisterParameterCommand extends AddParameterCommand {
private final Register register;

View file

@ -26,7 +26,7 @@ import ghidra.util.exception.InvalidInputException;
* @deprecated function signatures should be modified in their entirety using
* either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}.
*/
@Deprecated(since = "11.1")
@Deprecated(forRemoval = true, since = "11.1")
public class AddStackParameterCommand extends AddParameterCommand {
private final int stackOffset;

View file

@ -48,8 +48,6 @@ import ghidra.framework.options.OptionsChangeListener;
import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.database.symbol.FunctionSymbol;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
@ -736,7 +734,11 @@ public class CodeBrowserClipboardProvider extends ByteCopier
SymbolTable symbolTable = currentProgram.getSymbolTable();
Symbol symbol = symbolTable.getSymbol(reference);
if ((symbol instanceof CodeSymbol) || (symbol instanceof FunctionSymbol)) {
if (symbol == null) {
return false;
}
SymbolType type = symbol.getSymbolType();
if ((type == SymbolType.LABEL) || (type == SymbolType.FUNCTION)) {
RenameLabelCmd cmd = new RenameLabelCmd(symbol, labelName, SourceType.USER_DEFINED);
return tool.execute(cmd, currentProgram);
}

View file

@ -24,8 +24,7 @@ import ghidra.app.context.ListingActionContext;
import ghidra.app.context.ListingContextAction;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.database.symbol.FunctionSymbol;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.*;
/**
* <CODE>RemoveLabelAction</CODE> allows the user to remove a label.
@ -63,8 +62,12 @@ class RemoveLabelAction extends ListingContextAction {
boolean isOnSymbol(ListingActionContext context) {
Symbol s = plugin.getSymbol(context);
return ((s instanceof CodeSymbol) && !s.isDynamic()) ||
((s instanceof FunctionSymbol) && s.getSource() != SourceType.DEFAULT);
if (s == null) {
return false;
}
SymbolType type = s.getSymbolType();
return (type == SymbolType.LABEL && !s.isDynamic()) ||
(type == SymbolType.FUNCTION && s.getSource() != SourceType.DEFAULT);
}
/**

View file

@ -20,7 +20,7 @@ import static ghidra.framework.main.DataTreeDialogType.*;
import java.awt.*;
import java.awt.event.ItemListener;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import javax.swing.*;
import javax.swing.border.*;
@ -270,8 +270,7 @@ class EditExternalLocationPanel extends JPanel {
* Pop up the data tree dialog so the user can choose the external program.
*/
private void popupProgramChooser() {
DataTreeDialog d =
new DataTreeDialog(this.getParent(), "Choose External Program", OPEN);
DataTreeDialog d = new DataTreeDialog(this.getParent(), "Choose External Program", OPEN);
final DataTreeDialog dialog = d;
d.addOkActionListener(e -> {
DomainFile df = dialog.getDomainFile();
@ -402,10 +401,9 @@ class EditExternalLocationPanel extends JPanel {
String locationName = getLocationName();
if (locationName != null && locationName.length() > 0) {
ExternalManager externalManager = program.getExternalManager();
List<ExternalLocation> externalLocations =
Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations(extLibName, locationName);
externalLocations.remove(externalLocation);
if (!externalLocations.isEmpty()) {
if (externalLocations.size() == 1 && !externalLocations.contains(externalLocation)) {
int result = OptionDialog.showYesNoDialog(null, "Duplicate External Name",
"Another symbol named '" + locationName + "' already exists in the '" +
extLibName + "' library. Are you sure you want to create another?");

View file

@ -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.
@ -16,32 +15,37 @@
*/
package ghidra.app.plugin.core.symboltree.actions;
import docking.action.MenuData;
import ghidra.app.context.ProgramSymbolActionContext;
import ghidra.app.context.ProgramSymbolContextAction;
import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.database.symbol.FunctionSymbol;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolType;
import ghidra.util.HelpLocation;
import docking.action.MenuData;
public class PinSymbolAction extends ProgramSymbolContextAction {
public PinSymbolAction(String owner, String popupGroup) {
super("Pin Symbol", owner);
setPopupMenuData(new MenuData(new String[] { "Set Pinned" }, popupGroup));
setDescription("Pins the symbol(s) to the address so that it is unaffected by memory block moves or image base changes.");
setDescription(
"Pins the symbol(s) to the address so that it is unaffected by memory block moves or image base changes.");
setHelpLocation(new HelpLocation("SymbolTablePlugin", "Pinning a Symbol"));
}
private boolean canPinSymbol(Symbol symbol) {
SymbolType type = symbol.getSymbolType();
return (type == SymbolType.LABEL || type == SymbolType.FUNCTION) && !symbol.isExternal() &&
!symbol.isPinned();
}
@Override
protected void actionPerformed(ProgramSymbolActionContext context) {
Program program = context.getProgram();
int transactionID = program.startTransaction("Pin Symbol(s)");
try {
for (Symbol symbol : context.getSymbols()) {
if ((symbol instanceof CodeSymbol || symbol instanceof FunctionSymbol) &&
!symbol.isExternal() && !symbol.isPinned()) {
if (canPinSymbol(symbol)) {
symbol.setPinned(true);
}
}
@ -54,8 +58,7 @@ public class PinSymbolAction extends ProgramSymbolContextAction {
@Override
protected boolean isEnabledForContext(ProgramSymbolActionContext context) {
for (Symbol symbol : context.getSymbols()) {
if ((symbol instanceof CodeSymbol || symbol instanceof FunctionSymbol) &&
!symbol.isExternal() && !symbol.isPinned()) {
if (canPinSymbol(symbol)) {
return true;
}
}

View file

@ -20,6 +20,7 @@ import static org.junit.Assert.*;
import java.awt.Component;
import java.awt.Window;
import java.util.List;
import java.util.Set;
import javax.swing.JComponent;
import javax.swing.border.Border;
@ -926,4 +927,33 @@ public abstract class AbstractExternalMergerTest extends AbstractListingMergeMan
}
return null;
}
protected void assertHasExtAddresses(Set<ExternalLocation> externalLocations,
String... addrStrings) {
Address[] addrs = new Address[addrStrings.length];
for (int i = 0; i < addrs.length; i++) {
addrs[i] = addr(resultProgram, addrStrings[i]);
}
boolean[] foundAddrs = new boolean[addrStrings.length];
int count = 0;
for (ExternalLocation extLoc : externalLocations) {
Address addr = extLoc.getAddress();
boolean found = false;
for (int i = 0; i < addrs.length; i++) {
if (addrs[i].equals(addr)) {
assertFalse("Duplicate ext addr: " + addr, foundAddrs[i]);
foundAddrs[i] = true;
found = true;
++count;
break;
}
}
assertTrue("Unexpected extaddr: " + addr, found);
}
assertEquals("Did not find expected ext addrs", addrs.length, count);
}
}

View file

@ -17,14 +17,13 @@ package ghidra.app.merge.listing;
import static org.junit.Assert.*;
import java.util.List;
import java.util.Set;
import org.junit.Assert;
import org.junit.Test;
import ghidra.app.cmd.function.AddRegisterParameterCommand;
import ghidra.program.database.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register;
@ -1604,35 +1603,18 @@ public class ExternalFunctionMergeManagerTest extends AbstractExternalMergerTest
waitForMergeCompletion();
ExternalManager externalManager = resultProgram.getExternalManager();
List<ExternalLocation> externalLocations =
Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations("user32.dll", "BETTY");
assertEquals(2, externalLocations.size());
assertHasDifferentAddresses(externalLocations, "1002239", "10063b4");
assertHasExtAddresses(externalLocations, "1002239", "10063b4");
List<ExternalLocation> externalLocations2 =
Set<ExternalLocation> externalLocations2 =
externalManager.getExternalLocations("user32.dll", "BARNEY");
assertEquals(2, externalLocations2.size());
assertHasDifferentAddresses(externalLocations2, "77db1020", "77db1130");
assertHasExtAddresses(externalLocations2, "77db1020", "77db1130");
}
private void assertHasDifferentAddresses(List<ExternalLocation> externalLocations,
String addrString1, String addrString2) {
Address addr1 = addr(resultProgram, addrString1);
Address addr2 = addr(resultProgram, addrString2);
Address extAddr1 = externalLocations.get(0).getAddress();
Address extAddr2 = externalLocations.get(1).getAddress();
if (addr1.equals(extAddr1) && addr2.equals(extAddr2)) {
return;
}
if (addr1.equals(extAddr2) && addr2.equals(extAddr1)) {
return;
}
fail("Expected addresses: " + addr1 + ", " + addr2 + " but got " + extAddr1 + ", " +
extAddr2);
}
@Test
public void testAddExternalFunctionsWithFunctionConflicts() throws Exception {
@ -1674,15 +1656,14 @@ public class ExternalFunctionMergeManagerTest extends AbstractExternalMergerTest
waitForMergeCompletion();
ExternalManager externalManager = resultProgram.getExternalManager();
List<ExternalLocation> externalLocations =
Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations("user32.dll", "BARNEY");
assertEquals(2, externalLocations.size());
assertHasDifferentAddresses(externalLocations, "1002239", "77db1020");
assertHasExtAddresses(externalLocations, "1002239", "77db1020");
externalLocations = externalManager.getExternalLocations("user32.dll", "BETTY");
assertEquals(1, externalLocations.size());
assertEquals("01002239", externalLocations.get(0).getAddress().toString());
assertHasExtAddresses(externalLocations, "01002239");
}
@Test

View file

@ -18,6 +18,7 @@ package ghidra.app.merge.listing;
import static org.junit.Assert.*;
import java.util.List;
import java.util.Set;
import org.junit.Test;
@ -438,8 +439,11 @@ public class ExternalMergerAddTest extends AbstractExternalMergerTest {
ExternalManager externalManager = resultProgram.getExternalManager();
assertTrue(externalManager.contains(libname));
List<ExternalLocation> externals = externalManager.getExternalLocations(libname, label);
assertEquals(2, externals.size());
Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations(libname, label);
assertEquals(2, externalLocations.size());
assertHasExtAddresses(externalLocations, address1, address2);
ExternalLocationIterator loc1It = externalManager.getExternalLocations(addr(address1));
assertTrue(loc1It.hasNext());
assertEquals(label, loc1It.next().getLabel());
@ -3403,13 +3407,16 @@ public class ExternalMergerAddTest extends AbstractExternalMergerTest {
ExternalManager externalManager = resultProgram.getExternalManager();
assertTrue(externalManager.contains(libname));
List<ExternalLocation> externalLocations =
Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations(libname, label);
assertEquals(2, externalLocations.size());
assertHasExtAddresses(externalLocations, "00000100", "00000110");
List<ExternalLocation> externalLocations2 =
Set<ExternalLocation> externalLocations2 =
externalManager.getExternalLocations(BLUE_PATH[0], BLUE_PATH[1]);
assertEquals(2, externalLocations2.size());
assertHasExtAddresses(externalLocations, "00000100", "00000110");
}
@Test
@ -3484,13 +3491,15 @@ public class ExternalMergerAddTest extends AbstractExternalMergerTest {
ExternalManager externalManager = resultProgram.getExternalManager();
assertTrue(externalManager.contains(libname));
List<ExternalLocation> externalLocations =
Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations(libname, label);
assertEquals(2, externalLocations.size());
assertHasExtAddresses(externalLocations, "00000100", "00000110");
List<ExternalLocation> externalLocations2 =
Set<ExternalLocation> externalLocations2 =
externalManager.getExternalLocations(BLUE_PATH[0], BLUE_PATH[1]);
assertEquals(2, externalLocations2.size());
assertHasExtAddresses(externalLocations, "00000100", "00000110");
}
}

View file

@ -17,6 +17,7 @@ package ghidra.app.merge.listing;
import static org.junit.Assert.*;
import java.io.File;
import java.util.Arrays;
import org.junit.Assert;
@ -50,10 +51,10 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
@Override
public void modifyLatest(ProgramDB program) {
try {
program.getExternalManager().setExternalPath("COMDLG32.DLL", "//comdlg32.dll",
true);
program.getExternalManager().setExternalPath("KERNEL32.DLL", "//kernel32.dll",
true);
program.getExternalManager()
.setExternalPath("COMDLG32.DLL", "//comdlg32.dll", true);
program.getExternalManager()
.setExternalPath("KERNEL32.DLL", "//kernel32.dll", true);
}
catch (InvalidInputException e) {
// TODO Auto-generated catch block
@ -65,8 +66,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyPrivate(ProgramDB program) {
try {
program.getExternalManager().setExternalPath("GDI32.DLL", "//gdi32.dll", true);
program.getExternalManager().setExternalPath("KERNEL32.DLL", "//kernel32.dll",
true);
program.getExternalManager()
.setExternalPath("KERNEL32.DLL", "//kernel32.dll", true);
}
catch (InvalidInputException e) {
// TODO Auto-generated catch block
@ -104,8 +105,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyLatest(ProgramDB program) {
try {
program.getExternalManager().setExternalPath("ADVAPI32.DLL", "//foo.dll", true);
program.getExternalManager().setExternalPath("KERNEL32.DLL", "//latest.dll",
true);
program.getExternalManager()
.setExternalPath("KERNEL32.DLL", "//latest.dll", true);
}
catch (InvalidInputException e) {
// TODO Auto-generated catch block
@ -191,8 +192,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyLatest(ProgramDB program) {
try {
removeExternalLibrary(program, "ADVAPI32.DLL");
program.getExternalManager().setExternalPath("USER32.DLL", "//latest.dll",
true);
program.getExternalManager()
.setExternalPath("USER32.DLL", "//latest.dll", true);
}
catch (InvalidInputException e) {
// TODO Auto-generated catch block
@ -251,8 +252,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyLatest(ProgramDB program) {
try {
removeExternalLibrary(program, "ADVAPI32.DLL");
program.getExternalManager().setExternalPath("USER32.DLL", "//latest.dll",
true);
program.getExternalManager()
.setExternalPath("USER32.DLL", "//latest.dll", true);
}
catch (InvalidInputException e) {
// TODO Auto-generated catch block
@ -311,8 +312,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyLatest(ProgramDB program) {
try {
removeExternalLibrary(program, "ADVAPI32.DLL");
program.getExternalManager().setExternalPath("USER32.DLL", "//latest.dll",
true);
program.getExternalManager()
.setExternalPath("USER32.DLL", "//latest.dll", true);
}
catch (InvalidInputException e) {
// TODO Auto-generated catch block
@ -487,7 +488,7 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
try {
removeExternalLibrary(program, "ADVAPI32.DLL");
Reference[] refs =
program.getReferenceManager().getReferencesFrom(addr(program, "0x10011e4")); // SetCursor
program.getReferenceManager().getReferencesFrom(addr(program, "0x10011e4")); // setCursor
SymbolTable symTab = program.getSymbolTable();
Symbol s = symTab.getSymbol(refs[0]);
s.setName("SetCursor99", SourceType.USER_DEFINED);
@ -538,13 +539,24 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
// Check that the 2 renames happened.
ExternalLocation extLoc =
resultExtMgr.getUniqueExternalLocation("ADVAPI32.DLL", "RegCreateKeyW");
assertNull(extLoc);
assertNotNull(extLoc);
assertEquals("RegCreateKeyW", extLoc.getOriginalImportedName());
assertEquals("RegCreateKeyW99", extLoc.getLabel());
extLoc = resultExtMgr.getUniqueExternalLocation("ADVAPI32.DLL", "RegCreateKeyW99");
assertNotNull(extLoc);
extLoc = resultExtMgr.getUniqueExternalLocation("USER32.DLL", "SetCursor");
assertNull(extLoc);
assertEquals("RegCreateKeyW", extLoc.getOriginalImportedName());
assertEquals("RegCreateKeyW99", extLoc.getLabel());
extLoc = resultExtMgr.getUniqueExternalLocation("USER32.DLL", "setCursor");
assertNotNull(extLoc);
assertEquals("setCursor", extLoc.getOriginalImportedName());
assertEquals("SetCursor99", extLoc.getLabel());
extLoc = resultExtMgr.getUniqueExternalLocation("USER32.DLL", "SetCursor99");
assertNotNull(extLoc);
assertEquals("setCursor", extLoc.getOriginalImportedName());
assertEquals("SetCursor99", extLoc.getLabel());
}
@ -614,8 +626,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
ExternalLocationIterator iter = extMgr.getExternalLocations(libName);
while (iter.hasNext()) {
ExternalLocation loc = iter.next();
if (!((ExternalManagerDB) extMgr).removeExternalLocation(
loc.getExternalSpaceAddress())) {
if (!((ExternalManagerDB) extMgr)
.removeExternalLocation(loc.getExternalSpaceAddress())) {
Assert.fail("Couldn't remove external location for library " + libName);
}
}

View file

@ -17,7 +17,7 @@ package ghidra.app.merge.listing;
import static org.junit.Assert.*;
import java.util.List;
import java.util.Set;
import javax.swing.JDialog;
@ -944,27 +944,10 @@ public class FunctionMergerThunkTest extends AbstractExternalMergerTest {
ExternalLocation externalLocation = thunkedFunction.getExternalLocation();
assertEquals(addr(resultProgram, "77db1020"), externalLocation.getAddress());
List<ExternalLocation> externalLocations =
Set<ExternalLocation> externalLocations =
resultProgram.getExternalManager().getExternalLocations("user32.dll", "printf");
assertEquals(2, externalLocations.size());
Address address1 = externalLocations.get(0).getAddress();
Address address2 = externalLocations.get(1).getAddress();
assertTrue("Expected one location to be ", areOneOfEach(address1, address2,
addr(resultProgram, "77db1020"), addr(resultProgram, "77db1130")));
}
private <T> boolean areOneOfEach(T t1, T t2, T v1, T v2) {
if (t1.equals(v1) && t2.equals(v2)) {
return true;
}
if (t1.equals(v2) && t2.equals(v1)) {
return true;
}
return false;
assertHasExtAddresses(externalLocations, "77db1020", "77db1130");
}
@Test

View file

@ -99,7 +99,7 @@ public class ExternalCodeBrowserNavigationTest extends AbstractCodeBrowserNaviga
private void addThunkToExternalFunction(String libraryName, String label,
Address thunkAddress) {
ExternalLocation externalLocation =
program.getExternalManager().getExternalLocation(libraryName, label);
program.getExternalManager().getUniqueExternalLocation(libraryName, label);
Function extFunction = externalLocation.getFunction();
CreateThunkFunctionCmd cmd = new CreateThunkFunctionCmd(thunkAddress,

View file

@ -109,10 +109,8 @@ public class SymbolTreeLocationReferencesTest extends AbstractLocationReferences
private Function addExternalFunctionReference(Address refAddr, String libraryName,
String extLabel, RefType refType) throws Exception {
List<ExternalLocation> locations =
program.getExternalManager().getExternalLocations(libraryName, extLabel);
assertEquals(1, locations.size());
ExternalLocation externalLocation = locations.get(0);
ExternalLocation externalLocation =
program.getExternalManager().getUniqueExternalLocation(libraryName, extLabel);
assertNotNull("External location not found: " + libraryName + "::" + extLabel,
externalLocation);

View file

@ -387,6 +387,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
assertEquals(addr(0x0100688c), loc.getByteAddress());
assertTrue(loc instanceof CommentFieldLocation);
//
assertTrue("Search result not found: " + addr(0x01006890), searcher.hasNext());
loc = searcher.next().programLocation();
assertNotNull(loc);
assertEquals(addr(0x01006890), loc.getByteAddress());
@ -394,11 +395,13 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
MnemonicFieldLocation mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic());
//
assertTrue("Search result not found: " + addr(0x01006890), searcher.hasNext());
loc = searcher.next().programLocation();
assertNotNull(loc);
assertEquals(addr(0x01006890), loc.getByteAddress());
assertTrue(loc instanceof CommentFieldLocation);
//
assertTrue("Search result not found: " + addr(0x0100689b), searcher.hasNext());
loc = searcher.next().programLocation();
assertNotNull(loc);
assertEquals(addr(0x0100689b), loc.getByteAddress());
@ -406,6 +409,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic());
//
assertTrue("Search result not found: " + addr(0x0100689f), searcher.hasNext());
loc = searcher.next().programLocation();
assertNotNull(loc);
assertEquals(addr(0x0100689f), loc.getByteAddress());
@ -413,6 +417,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic());
//
assertTrue("Search result not found: " + addr(0x0100689f), searcher.hasNext());
loc = searcher.next().programLocation();
assertNotNull(loc);
assertEquals(addr(0x0100689f), loc.getByteAddress());
@ -478,6 +483,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
assertEquals(addr(0x0100688c), loc.getByteAddress());
assertTrue(loc instanceof CommentFieldLocation);
//
assertTrue("Search result not found: " + addr(0x0100689b), searcher.hasNext());
loc = searcher.next().programLocation();
assertNotNull(loc);
assertEquals(addr(0x0100689b), loc.getByteAddress());
@ -485,6 +491,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
MnemonicFieldLocation mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic());
//
assertTrue("Search result not found: " + addr(0x0100689f), searcher.hasNext());
loc = searcher.next().programLocation();
assertNotNull(loc);
assertEquals(addr(0x0100689f), loc.getByteAddress());
@ -492,6 +499,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic());
//
assertTrue("Search result not found: " + addr(0x0100689f), searcher.hasNext());
loc = searcher.next().programLocation();
assertNotNull(loc);
assertEquals(addr(0x0100689f), loc.getByteAddress());
@ -819,6 +827,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
private void checkTextFound(ArrayList<Address> startList, Class<?> fieldClass) {
for (Address start : startList) {
assertTrue("Search result not found: " + start, searcher.hasNext());
ProgramLocation loc = searcher.next().programLocation();
assertNotNull(loc);
assertTrue(fieldClass.isAssignableFrom(loc.getClass()));

View file

@ -0,0 +1,79 @@
/* ###
* 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 db;
import java.io.IOException;
import java.util.function.Function;
/**
* {@link ConstrainedForwardRecordIterator} provides the ability to both filter and
* translate records returned from an underlying {@link RecordIterator}.
*/
public class ConstrainedForwardRecordIterator implements RecordIterator {
private DBRecord nextConvertedRecord;
private final RecordIterator it;
private final Function<DBRecord, DBRecord> recordPredicateAndTranslate;
/**
* Construct a constrained/filtered record iterator.
* @param it source record iterator
* @param recordPredicateAndTranslate function which enables both filtering of records
* (null returned if record should be skipped) and the ability to translate the record
* to an alternate table/record schema.
*/
public ConstrainedForwardRecordIterator(RecordIterator it,
Function<DBRecord, DBRecord> recordPredicateAndTranslate) {
this.it = it;
this.recordPredicateAndTranslate = recordPredicateAndTranslate;
}
@Override
public boolean hasPrevious() {
throw new UnsupportedOperationException();
}
@Override
public DBRecord previous() {
throw new UnsupportedOperationException();
}
@Override
public boolean delete() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public boolean hasNext() throws IOException {
if (nextConvertedRecord != null) {
return true;
}
while (nextConvertedRecord == null && it.hasNext()) {
nextConvertedRecord = recordPredicateAndTranslate.apply(it.next());
}
return nextConvertedRecord != null;
}
@Override
public DBRecord next() throws IOException {
if (hasNext()) {
DBRecord returnedRecord = nextConvertedRecord;
nextConvertedRecord = null;
return returnedRecord;
}
return null;
}
}

View file

@ -114,8 +114,9 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
* 19-Oct-2023 - version 28 Revised overlay address space table and eliminated min/max.
* Multiple blocks are permitted within a single overlay space.
* 13-Dec-2024 - version 29 Added source file manager.
* 3-Jun-2025 - version 30 Symbol Table schema V4 with external symbol data indexing
*/
static final int DB_VERSION = 29;
static final int DB_VERSION = 30;
/**
* UPGRADE_REQUIRED_BFORE_VERSION should be changed to DB_VERSION anytime the
@ -2103,7 +2104,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
monitor.setMessage("Updating symbols...");
getSymbolTable().setLanguage(translator, monitor);
getExternalManager().setLanguage(translator, monitor);
getFunctionManager().setLanguage(translator, monitor);
}

View file

@ -20,7 +20,8 @@ import ghidra.docking.settings.Settings;
import ghidra.program.database.DBObjectCache;
import ghidra.program.model.address.Address;
import ghidra.program.model.data.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.listing.CommentType;
import ghidra.program.model.listing.Data;
import ghidra.program.model.mem.MemoryAccessException;
/**

View file

@ -17,9 +17,9 @@ package ghidra.program.database.external;
import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.SymbolPath;
import ghidra.program.database.symbol.SymbolDB;
import ghidra.program.database.symbol.FunctionSymbol;
import ghidra.program.database.symbol.MemorySymbol;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*;
@ -28,19 +28,18 @@ import ghidra.util.exception.*;
public class ExternalLocationDB implements ExternalLocation {
private static final char ORIGINAL_IMPORTED_DELIMITER = ',';
private ExternalManagerDB extMgr;
private SymbolDB symbol;
private MemorySymbol symbol;
/**
* Creates an externalLocationDB using a symbol
* at the same external space address.
* @param extMgr the ExternalManager.
* @param symbol the symbol for this external location.
* @param symbol the memory (label or function) symbol for this external location.
* Typically, this will store the original mangled name when and if the original name
* is demangled.
* is demangled, as well as the external program's memory address for the location if known.
*/
ExternalLocationDB(ExternalManagerDB extMgr, SymbolDB symbol) {
ExternalLocationDB(ExternalManagerDB extMgr, MemorySymbol symbol) {
this.extMgr = extMgr;
this.symbol = symbol;
}
@ -50,9 +49,6 @@ public class ExternalLocationDB implements ExternalLocation {
return symbol;
}
/**
* @see ghidra.program.model.symbol.ExternalLocation#getLibraryName()
*/
@Override
public String getLibraryName() {
Library library = getLibrary();
@ -68,17 +64,11 @@ public class ExternalLocationDB implements ExternalLocation {
}
/**
* @see ghidra.program.model.symbol.ExternalLocation#getParentNameSpace()
*/
@Override
public Namespace getParentNameSpace() {
return symbol.getParentNamespace();
}
/**
* @see ghidra.program.model.symbol.ExternalLocation#getParentName()
*/
@Override
public String getParentName() {
return symbol.getParentNamespace().getName();
@ -88,9 +78,6 @@ public class ExternalLocationDB implements ExternalLocation {
return symbol.getParentNamespace().getID();
}
/**
* @see ghidra.program.model.symbol.ExternalLocation#getLabel()
*/
@Override
public String getLabel() {
return symbol.getName();
@ -98,7 +85,7 @@ public class ExternalLocationDB implements ExternalLocation {
@Override
public String getOriginalImportedName() {
return getExternalData(symbol).getOriginalImportedName();
return symbol.getExternalOriginalImportedName();
}
@Override
@ -106,17 +93,11 @@ public class ExternalLocationDB implements ExternalLocation {
return symbol.getSource();
}
/**
* @see ghidra.program.model.symbol.ExternalLocation#getAddress()
*/
@Override
public Address getAddress() {
return getExternalData(symbol).getAddress(extMgr.getAddressMap().getAddressFactory());
return symbol.getExternalProgramAddress();
}
/**
* @see ghidra.program.model.symbol.ExternalLocation#getExternalSpaceAddress()
*/
@Override
public Address getExternalSpaceAddress() {
return symbol.getAddress();
@ -136,9 +117,6 @@ public class ExternalLocationDB implements ExternalLocation {
return symbol.getSymbolType() == SymbolType.FUNCTION;
}
/**
* @see ghidra.program.model.symbol.ExternalLocation#getDataType()
*/
@Override
public DataType getDataType() {
long dataTypeID = symbol.getDataTypeId();
@ -148,15 +126,10 @@ public class ExternalLocationDB implements ExternalLocation {
return extMgr.getProgram().getDataTypeManager().getDataType(dataTypeID);
}
/**
* @see ghidra.program.model.symbol.ExternalLocation#setDataType(ghidra.program.model.data.DataType)
*/
@Override
public void setDataType(DataType dt) {
long dataTypeID = extMgr.getProgram().getDataTypeManager().getResolvedID(dt);
symbol.setDataTypeId(dataTypeID);
// TODO: change notification may be required
}
@Override
@ -173,7 +146,7 @@ public class ExternalLocationDB implements ExternalLocation {
return getFunction();
}
Function function = extMgr.createFunction(this);
symbol = (SymbolDB) function.getSymbol();
symbol = (FunctionSymbol) function.getSymbol();
return function;
}
@ -186,8 +159,7 @@ public class ExternalLocationDB implements ExternalLocation {
* If a namespace is not included within label, the current namespace will be preserved.
* @param source the source of this external symbol:
* Symbol.DEFAULT, Symbol.ANALYSIS, Symbol.IMPORTED, or Symbol.USER_DEFINED
* @throws InvalidInputException
* @see ghidra.program.model.symbol.ExternalLocation#setLabel(java.lang.String)
* @throws InvalidInputException if the name contains illegal characters (space for example)
*/
void setLabel(String label, SourceType source) throws InvalidInputException {
if (label == null) {
@ -223,12 +195,10 @@ public class ExternalLocationDB implements ExternalLocation {
@Override
public void setAddress(Address address) throws InvalidInputException {
String addressString = address != null ? address.toString() : null;
if (addressString == null && getSource() == SourceType.DEFAULT) {
if (address == null && getSource() == SourceType.DEFAULT) {
throw new InvalidInputException("Either an external label or address is required");
}
updateSymbolData(symbol, getExternalData(symbol).getOriginalImportedName(),
addressString);
symbol.setExternalProgramAddress(address, true);
}
public void saveOriginalNameIfNeeded(Namespace oldNamespace, String oldName,
@ -239,11 +209,11 @@ public class ExternalLocationDB implements ExternalLocation {
// if we don't have an original already set and it is an imported symbol, save it
String originalImportedName = getOriginalImportedName();
if (getLabel().equals(originalImportedName)) {
setOriginalImportedName(symbol, null);
symbol.setExternalOriginalImportedName(null, false);
}
else if (wasInLibrary && getSource() != SourceType.DEFAULT &&
oldSource == SourceType.IMPORTED && originalImportedName == null) {
setOriginalImportedName(symbol, oldName);
symbol.setExternalOriginalImportedName(oldName, false);
}
}
@ -288,8 +258,8 @@ public class ExternalLocationDB implements ExternalLocation {
}
try {
Library library = NamespaceUtils.getLibrary(symbol.getParentNamespace());
symbol.setExternalOriginalImportedName(null, false); // clear orig imported name
symbol.setNameAndNamespace(originalName, library, SourceType.IMPORTED);
setOriginalImportedName(symbol, null);
}
catch (CircularDependencyException | DuplicateNameException | InvalidInputException e) {
throw new AssertException("Can't happen here", e);
@ -365,57 +335,4 @@ public class ExternalLocationDB implements ExternalLocation {
}
static ExternalData getExternalData(SymbolDB extSymbol) {
return new ExternalData(extSymbol.getSymbolStringData());
}
static void setOriginalImportedName(SymbolDB extSymbol, String name) {
updateSymbolData(extSymbol, name, getExternalData(extSymbol).getAddressString());
}
static void updateSymbolData(SymbolDB extSymbol, String originalImportedName,
String addressString) {
if (addressString == null && originalImportedName == null) {
extSymbol.setSymbolStringData(null);
}
StringBuilder buf = new StringBuilder();
if (addressString != null) {
buf.append(addressString);
}
if (originalImportedName != null) {
buf.append(ORIGINAL_IMPORTED_DELIMITER);
buf.append(originalImportedName);
}
extSymbol.setSymbolStringData(buf.toString());
}
static class ExternalData {
private String originalImportedName;
private String addressString;
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;
}
}
public String getAddressString() {
return addressString;
}
String getOriginalImportedName() {
return originalImportedName;
}
Address getAddress(AddressFactory addrFactory) {
if (addressString == null) {
return null;
}
return addrFactory.getAddress(addressString);
}
}
}

View file

@ -25,16 +25,16 @@ import ghidra.framework.data.OpenMode;
import ghidra.framework.store.FileSystem;
import ghidra.program.database.ManagerDB;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.external.ExternalLocationDB.ExternalData;
import ghidra.program.database.function.FunctionDB;
import ghidra.program.database.function.FunctionManagerDB;
import ghidra.program.database.map.AddressMap;
import ghidra.program.database.symbol.*;
import ghidra.program.model.address.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Library;
import ghidra.program.model.symbol.*;
import ghidra.program.util.LanguageTranslator;
import ghidra.util.Lock;
import ghidra.util.Msg;
import ghidra.util.exception.*;
@ -46,7 +46,6 @@ import ghidra.util.task.TaskMonitor;
public class ExternalManagerDB implements ManagerDB, ExternalManager {
private AddressMap addrMap;
private NamespaceManager scopeMgr;
private SymbolManager symbolMgr;
private FunctionManagerDB functionMgr;
@ -95,7 +94,6 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
this.program = program;
symbolMgr = program.getSymbolTable();
functionMgr = program.getFunctionManager();
scopeMgr = program.getNamespaceManager();
}
@Override
@ -202,7 +200,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
SourceType locSourceType = checkExternalLabel(extLabel, extAddr, sourceType);
lock.acquire();
try {
Namespace libraryScope = getLibraryScope(extLibraryName);
Library libraryScope = getLibraryScope(extLibraryName);
if (libraryScope == null) {
libraryScope = addExternalName(extLibraryName, null, sourceType);
}
@ -245,7 +243,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
SourceType locSourceType = checkExternalLabel(extLabel, extAddr, sourceType);
lock.acquire();
try {
Namespace libraryScope = getLibraryScope(extLibraryName);
Library libraryScope = getLibraryScope(extLibraryName);
if (libraryScope == null) {
libraryScope = addExternalName(extLibraryName, null,
sourceType != SourceType.DEFAULT ? sourceType : SourceType.ANALYSIS);
@ -284,9 +282,9 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
private SourceType checkExternalLabel(String extLabel, Address extAddr, SourceType source)
throws InvalidInputException {
if (extLabel != null && (StringUtils.isBlank(extLabel) ||
SymbolUtilities.isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory()))) {
extLabel = null;
if (StringUtils.isBlank(extLabel) ||
SymbolUtilities.isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory())) {
extLabel = null; // force use of address
}
if (extLabel == null && extAddr == null) {
throw new InvalidInputException("Either an external label or address is required");
@ -329,12 +327,18 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
if (sourceType == SourceType.DEFAULT) {
extLabel = null;
}
ExternalLocationDB extLoc =
(ExternalLocationDB) getExtLocation(extNamespace, extLabel, extAddr, reuseExisting);
else if (StringUtils.isBlank(extLabel) || SymbolUtilities
.isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory())) {
extLabel = null; // force use of address
}
if (extAddr != null || reuseExisting) {
if (extLoc != null) {
// if there is already a location with the address, then we must use it
if (extAddr != null || reuseExisting) {
ExternalLocationDB extLoc = (ExternalLocationDB) getExtLocation(extNamespace,
extLabel, extAddr, reuseExisting);
if (extLoc != null) {
// if there is already a location with the address, then we must use it
if (extLabel != null && !extLabel.equals(extLoc.getLabel())) {
extLoc.setLabel(extLabel, sourceType);
}
@ -345,21 +349,25 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
return extLoc;
}
}
// ok can't or don't want to reuse an existing one, so make a new one.
SymbolDB s;
MemorySymbol s;
Address externalSpaceAddress = symbolMgr.getNextExternalSymbolAddress();
String extMemAddrString = (extAddr != null) ? extAddr.toString() : null;
if (isFunction) {
Function function = functionMgr.createExternalFunction(externalSpaceAddress,
extLabel, extNamespace, extMemAddrString, sourceType);
s = (SymbolDB) function.getSymbol();
FunctionDB function = functionMgr.createExternalFunction(externalSpaceAddress,
extLabel, extNamespace, null, extAddr, sourceType);
s = (FunctionSymbol) function.getSymbol();
}
else {
s = (SymbolDB) symbolMgr.createCodeSymbol(externalSpaceAddress, extLabel,
extNamespace, sourceType, extMemAddrString);
s = symbolMgr.createExternalCodeSymbol(externalSpaceAddress, extLabel, extNamespace,
sourceType, null, extAddr);
}
return new ExternalLocationDB(this, s);
}
catch (IOException e) {
program.dbError(e); // will not return
return null;
}
finally {
lock.release();
}
@ -377,66 +385,31 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
private ExternalLocation getExtLocation(Namespace library, String extLabel, Address extAddr,
boolean reuseExisting) throws InvalidInputException {
if (extLabel != null && (extLabel.length() == 0 ||
SymbolUtilities.isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory()))) {
extLabel = null; // force use of address
}
// Name match will also consider original import name if library is either null
// or a Library, otherwise only a specific namespaced name match will be considered.
ExternalLocation match =
findMatchingLocationByName(library, extLabel, extAddr, reuseExisting);
if (match != null) {
return match;
}
// So now get all the externalLocations for a library and search them
List<ExternalLocation> locations = getExternalLocations(library);
if (extLabel == null) {
return findMatchingLocationByAddress(locations, extAddr, reuseExisting);
}
return findMatchingLocationByOriginalImportName(locations, extLabel, extAddr);
}
// Find the location whose original imported name matches the given extLabel name.
private ExternalLocation findMatchingLocationByOriginalImportName(
List<ExternalLocation> locations, String extLabel, Address extAddr) {
// this only makes sense if we have label and no address. If we have an address,
// then the address must match and we would have already found it.
if (extLabel != null && extAddr == null) {
for (ExternalLocation externalLocation : locations) {
if (extLabel.equals(externalLocation.getOriginalImportedName())) {
return externalLocation;
}
}
if (extLabel == null) { // assume extAddr is not null (already checked)
return findMatchingLocationByAddress(extAddr, reuseExisting);
}
return null;
}
private List<ExternalLocation> getExternalLocations(Namespace library) {
List<ExternalLocation> list = new ArrayList<>();
SymbolIterator iter = symbolMgr.getSymbols(library);
for (Symbol symbol : iter) {
ExternalLocation extLoc = getExternalLocation(symbol);
if (extLoc != null) {
list.add(extLoc);
}
}
return list;
}
// Find an external location in the given namespace with the given name and address. If
// reuseExisting is true, then also find a match as long as the name and namespace match and
// the address is null.
private ExternalLocation findMatchingLocationByName(Namespace libScope, String extLabel,
private ExternalLocation findMatchingLocationByName(Namespace namespace, String extLabel,
Address extAddr, boolean reuseExisting) {
if (extLabel == null) {
if (StringUtils.isBlank(extLabel)) {
return null;
}
List<ExternalLocation> externalLocations = getExternalLocations(libScope, extLabel);
Set<ExternalLocation> externalLocations = getExternalLocations(namespace, extLabel);
if (externalLocations.isEmpty()) {
return null;
}
@ -460,14 +433,16 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
}
// first look for one without an address
ExternalLocation possibleExtLoc = null;
for (ExternalLocation externalLocation : externalLocations) {
if (externalLocation.getAddress() == null) {
return externalLocation;
}
possibleExtLoc = externalLocation;
}
// if reuse existing, then return any
return reuseExisting ? externalLocations.get(0) : null;
return reuseExisting ? possibleExtLoc : null;
}
// Look through all the locations for one whose address matches the given address AND whose
@ -475,91 +450,100 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
// location with a non-null address and a null label. So an exact match is when the address
// matches and the label is null. If reuseExisting, then we are trying to find and suitable
// location, so as long as the address matches, we can return it.
private ExternalLocation findMatchingLocationByAddress(List<ExternalLocation> locations,
Address extAddr, boolean reuseExisting) {
for (ExternalLocation externalLocation : locations) {
if (extAddr.equals(externalLocation.getAddress())) {
if (reuseExisting || externalLocation.getLabel() == null) {
return externalLocation;
}
private ExternalLocation findMatchingLocationByAddress(Address extAddr, boolean reuseExisting) {
for (Symbol symbol : symbolMgr.getExternalSymbolByMemoryAddress(null, extAddr)) {
ExternalLocation externalLocation = getExternalLocation(symbol);
if (reuseExisting || externalLocation.getLabel() == null) {
return externalLocation;
}
}
return null;
}
@Override
public List<ExternalLocation> getExternalLocations(Namespace libScope, String extLabel) {
List<ExternalLocation> externalLocations = new ArrayList<>();
List<Symbol> symbols = symbolMgr.getSymbols(extLabel, libScope);
for (Symbol symbol : symbols) {
ExternalLocation externalLocation = getExternalLocation(symbol);
if (externalLocation != null) {
externalLocations.add(externalLocation);
public Set<ExternalLocation> getExternalLocations(Namespace namespace, String extLabel) {
if (namespace != null && !namespace.isExternal()) {
return Set.of();
}
Set<ExternalLocation> externalLocations = new HashSet<>();
if (namespace == null || namespace instanceof Library) {
// Check for matching original import name
SymbolIterator matchingSymbols =
symbolMgr.getExternalSymbolByOriginalImportName((Library) namespace, extLabel);
for (Symbol symbol : matchingSymbols) {
ExternalLocation externalLocation = getExternalLocation(symbol);
if (externalLocation != null) {
externalLocations.add(externalLocation);
}
}
}
return externalLocations;
if (namespace != null) {
List<Symbol> symbols = symbolMgr.getSymbols(extLabel, namespace);
for (Symbol symbol : symbols) {
ExternalLocation externalLocation = getExternalLocation(symbol);
if (externalLocation != null) {
externalLocations.add(externalLocation);
}
}
}
else {
for (Symbol symbol : symbolMgr.getExternalSymbols(extLabel)) {
ExternalLocation externalLocation = getExternalLocation(symbol);
if (externalLocation != null) {
externalLocations.add(externalLocation);
}
}
}
return Collections.unmodifiableSet(externalLocations);
}
@Override
public List<ExternalLocation> getExternalLocations(String libraryName, String label) {
Namespace libraryScope = getLibraryScope(libraryName);
if (libraryScope == null) {
return Collections.emptyList();
public Set<ExternalLocation> getExternalLocations(String libraryName, String label) {
Library library = getLibraryScope(libraryName);
if (library == null && !StringUtils.isBlank(libraryName)) {
return Set.of();
}
return getExternalLocations(libraryScope, label);
return getExternalLocations(library, label);
}
@Override
public ExternalLocation getUniqueExternalLocation(Namespace namespace, String label) {
List<ExternalLocation> externalLocations = getExternalLocations(namespace, label);
Set<ExternalLocation> externalLocations = getExternalLocations(namespace, label);
if (externalLocations.size() == 1) {
return externalLocations.get(0);
return externalLocations.iterator().next();
}
return null;
}
@Override
public ExternalLocation getUniqueExternalLocation(String libraryName, String label) {
Namespace libScope = getLibraryScope(libraryName);
if (libScope == null) {
Library library = getLibraryScope(libraryName);
if (library == null && !StringUtils.isBlank(libraryName)) {
return null;
}
return getUniqueExternalLocation(libScope, label);
}
@Override
public ExternalLocation getExternalLocation(String extName, String extLabel) {
Namespace libScope = getLibraryScope(extName);
return getExternalLocation(libScope, extLabel);
}
@Override
public ExternalLocation getExternalLocation(Namespace extNamespace, String extLabel) {
List<ExternalLocation> externalLocations = getExternalLocations(extNamespace, extLabel);
if (externalLocations.isEmpty()) {
return null;
}
return externalLocations.get(0);
return getUniqueExternalLocation(library, label);
}
/**
* Get the default name for an external function or code symbol
* @param sym
* @return default name
* {@return the default name for an external function or code symbol}
* @param sym external label or function symbol
* @throws IllegalArgumentException if external label or function symbol not specified or
* external symbol does not have an external program address.
*/
public static String getDefaultExternalName(SymbolDB sym) {
SymbolType type = sym.getSymbolType();
if ((type != SymbolType.LABEL && type != SymbolType.FUNCTION) || !sym.isExternal()) {
throw new AssertException();
if (!(sym instanceof MemorySymbol) && !sym.isExternal()) {
throw new IllegalArgumentException("External label or function symbol required");
}
ExternalData externalData = ExternalLocationDB.getExternalData(sym);
Address addr = externalData.getAddress(sym.getProgram().getAddressFactory());
Address addr = ((MemorySymbol) sym).getExternalProgramAddress();
if (addr == null) {
throw new AssertException("External should not be default without memory address");
throw new IllegalArgumentException("Default External requires memory address");
}
if (type == SymbolType.FUNCTION) {
if (sym instanceof FunctionSymbol) {
return SymbolUtilities.getDefaultExternalFunctionName(addr);
}
long dataTypeID = sym.getDataTypeId();
DataType dt =
(dataTypeID < 0) ? null : sym.getProgram().getDataTypeManager().getDataType(dataTypeID);
@ -571,8 +555,9 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
}
/**
* Returns the external location associated with the given external address
* {@return the external location associated with the given external address or null}
* @param externalAddr the external address.
* @throws IllegalArgumentException if address is not external
*/
public ExternalLocation getExtLocation(Address externalAddr) {
if (externalAddr.getAddressSpace() != AddressSpace.EXTERNAL_SPACE) {
@ -582,7 +567,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
try {
Symbol[] symbols = symbolMgr.getSymbols(externalAddr);
if (symbols.length == 1) {
return new ExternalLocationDB(this, (SymbolDB) symbols[0]);
return new ExternalLocationDB(this, (MemorySymbol) symbols[0]);
}
if (symbols.length > 2) {
throw new AssertException(
@ -613,6 +598,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
/**
* Removes the external location at the given external address
* @param externalAddr the address at which to remove the external location.
* @return true if external location was successfully removed else false
*/
public boolean removeExternalLocation(Address externalAddr) {
lock.acquire();
@ -651,15 +637,6 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
return true;
}
/**
* Update the external program for all references.
* @param oldName old external program name
* @param newName new external program name
* @param source the source of this external library:
* Symbol.DEFAULT, Symbol.ANALYSIS, Symbol.IMPORTED, or Symbol.USER_DEFINED
* @throws DuplicateNameException
* @throws InvalidInputException
*/
@Override
public void updateExternalLibraryName(String oldName, String newName, SourceType source)
throws DuplicateNameException, InvalidInputException {
@ -691,9 +668,9 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
return (Library) s.getObject();
}
private Namespace getLibraryScope(String name) {
Symbol s = symbolMgr.getLibrarySymbol(name);
return s == null ? null : (Namespace) s.getObject();
private Library getLibraryScope(String name) {
LibrarySymbol s = symbolMgr.getLibrarySymbol(name);
return s == null ? null : s.getObject();
}
@Override
@ -723,9 +700,9 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
@Override
public String getExternalLibraryPath(String externalName) {
SymbolDB s = (SymbolDB) symbolMgr.getLibrarySymbol(externalName);
if (s instanceof LibrarySymbol) {
return s.getSymbolStringData();
LibrarySymbol s = symbolMgr.getLibrarySymbol(externalName);
if (s != null) {
return s.getExternalLibraryPath();
}
return null;
}
@ -743,7 +720,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
lock.acquire();
try {
SymbolDB s = (SymbolDB) symbolMgr.getLibrarySymbol(externalName);
LibrarySymbol s = symbolMgr.getLibrarySymbol(externalName);
if (s == null) {
try {
addExternalName(externalName, externalPath,
@ -753,8 +730,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
throw new AssertException(e);
}
}
else if (s instanceof LibrarySymbol) {
s.setSymbolStringData(externalPath);
else {
s.setExternalLibraryPath(externalPath);
}
}
finally {
@ -784,8 +761,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
if (!(symbol instanceof CodeSymbol)) {
throw new IllegalStateException("Expected external code symbol");
}
//long dtId = symbol.getSymbolData1();
String extData = symbol.getSymbolStringData();
Address extProgAddr = extLoc.getAddress();
String origImpName = extLoc.getOriginalImportedName();
String name = symbol.getName();
Namespace namespace = symbol.getParentNamespace();
Address extAddr = symbol.getAddress();
@ -793,7 +770,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
((CodeSymbol) symbol).delete(true);
return functionMgr.createExternalFunction(extAddr, name, namespace, extData, source);
return functionMgr.createExternalFunction(extAddr, name, namespace, origImpName,
extProgAddr, source);
}
catch (Exception e) {
throw new RuntimeException("Unexpected exception", e);
@ -809,12 +787,12 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
@Override
public ExternalLocationIterator getExternalLocations(Address memoryAddress) {
return new ExternalLocationDBIterator(symbolMgr.getExternalSymbols(), memoryAddress);
return new ExternalLocationDBIterator(null, memoryAddress);
}
@Override
public ExternalLocationIterator getExternalLocations(String externalName) {
Namespace scope = getLibraryScope(externalName);
Library scope = getLibraryScope(externalName);
if (scope != null) {
return new ExternalLocationDBIterator(symbolMgr.getSymbols(scope));
}
@ -830,8 +808,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
ExternalLocationDBIterator() {
}
ExternalLocationDBIterator(SymbolIterator symIter, Address matchingAddress) {
this.symIter = symIter;
ExternalLocationDBIterator(Library library, Address matchingAddress) {
this.symIter = symbolMgr.getExternalSymbolByMemoryAddress(library, matchingAddress);
this.matchingAddress = matchingAddress;
}
@ -883,45 +861,4 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
}
public void setLanguage(LanguageTranslator translator, TaskMonitor monitor)
throws CancelledException {
monitor.setMessage("Translate External Addresses...");
AddressFactory oldAddrFactory = translator.getOldLanguage().getAddressFactory();
SymbolIterator externalSymbols = symbolMgr.getExternalSymbols();
while (externalSymbols.hasNext()) {
monitor.checkCancelled();
SymbolDB s = (SymbolDB) externalSymbols.next();
ExternalData externalData = ExternalLocationDB.getExternalData(s);
String addrStr = externalData.getAddressString();
if (addrStr == null) {
continue;
}
// skip addresses which do not parse by old language - could be
// overlay (although this should generally never occur)
Address addr = oldAddrFactory.getAddress(addrStr);
if (addr == null) {
continue;
}
AddressSpace newAddressSpace =
translator.getNewAddressSpace(addr.getAddressSpace().getName());
if (newAddressSpace == null || !newAddressSpace.isLoadedMemorySpace()) {
// can't really recover from this
throw new AssertException("Failed to map external memory address: " + addrStr);
}
addr = newAddressSpace.getAddress(addr.getOffset());
String newAddrStr = addr.toString();
if (!newAddrStr.equals(addrStr)) {
ExternalLocationDB.updateSymbolData(s, externalData.getOriginalImportedName(),
newAddrStr); // store translated external location address
}
}
}
SymbolDB createSymbolForOriginalName(Address address, Namespace namespace, String oldName,
SourceType oldType) throws InvalidInputException {
return (SymbolDB) symbolMgr.createCodeSymbol(address, oldName, namespace, oldType, null);
}
}

View file

@ -49,7 +49,7 @@ public class FunctionDB extends DatabaseObject implements Function {
private ProgramDB program;
private Address entryPoint;
private Symbol functionSymbol;
private FunctionSymbol functionSymbol;
private DBRecord rec;
private FunctionStackFrame frame;
@ -108,7 +108,7 @@ public class FunctionDB extends DatabaseObject implements Function {
private void init() {
thunkedFunction = manager.getThunkedFunction(this);
functionSymbol = program.getSymbolTable().getSymbol(key);
functionSymbol = (FunctionSymbol) program.getSymbolTable().getSymbol(key);
entryPoint = functionSymbol.getAddress();
}
@ -974,7 +974,7 @@ public class FunctionDB extends DatabaseObject implements Function {
symbolMap.put(v.symbol, v);
}
if (var.getComment() != null) {
v.symbol.setSymbolStringData(var.getComment());
v.symbol.setSymbolComment(var.getComment());
}
manager.functionChanged(this, null);
return v;
@ -1667,7 +1667,7 @@ public class FunctionDB extends DatabaseObject implements Function {
manager.functionChanged(this, PARAMETERS_CHANGED);
}
if (var.getComment() != null) {
p.symbol.setSymbolStringData(var.getComment());
p.symbol.setSymbolComment(var.getComment());
}
updateSignatureSourceAfterVariableChange(source, p.getDataType());
return p;

View file

@ -30,7 +30,6 @@ import ghidra.program.database.DBObjectCache;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.code.CodeManager;
import ghidra.program.database.data.DataTypeManagerDB;
import ghidra.program.database.external.ExternalLocationDB;
import ghidra.program.database.map.AddressMap;
import ghidra.program.database.symbol.*;
import ghidra.program.model.address.*;
@ -150,38 +149,40 @@ public class FunctionManagerDB implements FunctionManager {
/**
* Transform an existing external symbol into an external function.
* This method should only be invoked by an ExternalSymbol
* This method should only be invoked by the Function Manager.
* The {@link ExternalLocation#getOriginalImportedName() original imported name} will initially
* be null.
*
* @param extSpaceAddr the external space address to use when creating this external. Any
* other symbol using this address must first be deleted. Results are unpredictable if this is
* not done.
* @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 externalProgramAddress the external program address (may be null)
* @param originalImportName the original imported name if different from name (may be null)
* @param source the source of this external.
* @return external function
* @throws InvalidInputException if the name is invalid
*/
public Function createExternalFunction(Address extSpaceAddr, String name, Namespace nameSpace,
String extData, SourceType source) throws InvalidInputException {
public FunctionDB createExternalFunction(Address extSpaceAddr, String name, Namespace nameSpace,
String originalImportName, Address externalProgramAddress, SourceType source)
throws InvalidInputException {
lock.acquire();
try {
Symbol symbol =
symbolMgr.createFunctionSymbol(extSpaceAddr, name, nameSpace, source, extData);
FunctionSymbol symbol = symbolMgr.createExternalFunctionSymbol(extSpaceAddr, name,
nameSpace, source, originalImportName, externalProgramAddress);
long returnDataTypeId = program.getDataTypeManager().getResolvedID(DataType.DEFAULT);
try {
DBRecord rec = adapter.createFunctionRecord(symbol.getID(), returnDataTypeId);
DBRecord rec = adapter.createFunctionRecord(symbol.getID(), returnDataTypeId);
FunctionDB funcDB = new FunctionDB(this, cache, addrMap, rec);
FunctionDB funcDB = new FunctionDB(this, cache, addrMap, rec);
program.setObjChanged(ProgramEvent.FUNCTION_ADDED, extSpaceAddr, funcDB, null,
null);
return funcDB;
}
catch (IOException e) {
dbError(e);
}
program.setObjChanged(ProgramEvent.FUNCTION_ADDED, extSpaceAddr, funcDB, null, null);
return funcDB;
}
catch (IOException e) {
dbError(e); // will not return
return null;
}
finally {
@ -278,12 +279,13 @@ public class FunctionManagerDB implements FunctionManager {
}
}
Symbol symbol = symbolMgr.createFunctionSymbol(entryPoint, name, nameSpace, source,
((thunkedFunction != null) ? thunkedFunction.getEntryPoint().toString() : null));
long returnDataTypeId = program.getDataTypeManager().getResolvedID(DataType.DEFAULT);
try {
Symbol symbol =
symbolMgr.createMemoryFunctionSymbol(entryPoint, name, nameSpace, source);
long returnDataTypeId =
program.getDataTypeManager().getResolvedID(DataType.DEFAULT);
if (refFunc != null) {
String oldName = symbol.getName();

View file

@ -176,12 +176,12 @@ public abstract class VariableDB implements Variable {
@Override
public String getComment() {
return symbol.getSymbolStringData();
return symbol.getSymbolComment();
}
@Override
public void setComment(String comment) {
symbol.setSymbolStringData(comment);
symbol.setSymbolComment(comment);
functionMgr.functionChanged(function, null);
}
@ -409,8 +409,11 @@ public abstract class VariableDB implements Variable {
* Update variable storage and data-type associated with the underlying variable symbol.
* If function does not use custom storage, the specified storage will be ignored and set
* to UNASSIGNED.
* @param newStorage
* @param dt
* <P>
* NOTE: Method will trigger a symbol changed event.
*
* @param newStorage variable storage
* @param dt variable datatype
*/
void setStorageAndDataType(VariableStorage newStorage, DataType dt) {
if (this instanceof Parameter && !function.hasCustomVariableStorage()) {

View file

@ -1548,10 +1548,13 @@ public class ReferenceDBManager implements ReferenceManager, ManagerDB, ErrorHan
/**
* Return whether the address is an external entry point
* @param toAddr the address to test for external entry point
* @param toAddr the memory address to test for external entry point
* @return true if the address is an external entry point
*/
public boolean isExternalEntryPoint(Address toAddr) {
if (!toAddr.isMemoryAddress()) {
return false;
}
lock.acquire();
try {
RefList refList = getToRefs(toAddr);

View file

@ -18,6 +18,7 @@ package ghidra.program.database.symbol;
import db.DBRecord;
import ghidra.program.database.DBObjectCache;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.GhidraClass;
import ghidra.program.model.symbol.*;
/**
@ -28,16 +29,13 @@ public class ClassSymbol extends SymbolDB {
private GhidraClassDB ghidraClass;
/**
* Construct a new Class Symbol
* Construct a Ghidra Class symbol from an existing symbol record
* @param symbolMgr the symbol manager
* @param cache symbol object cache
* @param address the address to associate with the symbol
* @param record the record associated with the symbol.
*/
public ClassSymbol(SymbolManager symbolMgr, DBObjectCache<SymbolDB> cache, Address address,
DBRecord record) {
super(symbolMgr, cache, address, record);
ClassSymbol(SymbolManager symbolMgr, DBObjectCache<SymbolDB> cache, DBRecord record) {
super(symbolMgr, cache, Address.NO_ADDRESS, record);
}
@Override
@ -46,10 +44,12 @@ public class ClassSymbol extends SymbolDB {
}
@Override
public Object getObject() {
public GhidraClass getObject() {
lock.acquire();
try {
checkIsValid();
if (!checkIsValid()) {
return null;
}
if (ghidraClass == null) {
ghidraClass = new GhidraClassDB(this, symbolMgr.getProgram().getNamespaceManager());
}

View file

@ -26,13 +26,9 @@ import ghidra.program.util.LabelFieldLocation;
import ghidra.program.util.ProgramLocation;
/**
* Symbols that represent "labels"
*
* Symbol data usage:
* EXTERNAL:
* String stringData - external memory address/label
* Symbols that represent "labels" or external data locations
*/
public class CodeSymbol extends SymbolDB {
public class CodeSymbol extends MemorySymbol {
/**
* Constructs a new CodeSymbol
@ -74,11 +70,6 @@ public class CodeSymbol extends SymbolDB {
return symbolMgr.hasDynamicSymbol(address);
}
@Override
public boolean isExternal() {
return address.isExternalAddress();
}
@Override
public boolean delete() {
boolean keepReferences = !isExternal();
@ -105,21 +96,6 @@ public class CodeSymbol extends SymbolDB {
}
}
@Override
public boolean isPinned() {
if (!isExternal()) {
return doIsPinned();
}
return false;
}
@Override
public void setPinned(boolean pinned) {
if (!isExternal()) {
doSetPinned(pinned);
}
}
@Override
public Object getObject() {
lock.acquire();
@ -219,8 +195,8 @@ public class CodeSymbol extends SymbolDB {
}
return source;
}
if (newName == null || newName.length() == 0 || SymbolUtilities.isReservedDynamicLabelName(
newName, symbolMgr.getProgram().getAddressFactory())) {
if (newName == null || newName.length() == 0 || SymbolUtilities
.isReservedDynamicLabelName(newName, symbolMgr.getProgram().getAddressFactory())) {
return SourceType.DEFAULT;
}
return source;

View file

@ -15,6 +15,7 @@
*/
package ghidra.program.database.symbol;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -35,12 +36,8 @@ import ghidra.util.task.TaskMonitor;
/**
* Symbol class for functions.
*
* Symbol Data Usage:
* EXTERNAL:
* String stringData - external memory address/label
*/
public class FunctionSymbol extends SymbolDB {
public class FunctionSymbol extends MemorySymbol {
private FunctionManagerDB functionMgr;
@ -62,11 +59,6 @@ public class FunctionSymbol extends SymbolDB {
return SymbolType.FUNCTION;
}
@Override
public boolean isExternal() {
return address.isExternalAddress();
}
boolean isThunk() {
return functionMgr.isThunk(key);
}
@ -93,9 +85,13 @@ public class FunctionSymbol extends SymbolDB {
try {
boolean restoreLabel = isExternal() || (getSource() != SourceType.DEFAULT);
String symName = getName();
String extData = null;
Symbol parentSymbol = getParentSymbol();
String extOrigImportName = null;
Address extProgramAddr = null;
if (isExternal()) {
extData = getSymbolStringData(); // preserve external data
// preserve external data
extOrigImportName = getExternalOriginalImportedName();
extProgramAddr = getExternalProgramAddress();
}
Namespace namespace = getParentNamespace();
SourceType source = getSource();
@ -109,54 +105,48 @@ public class FunctionSymbol extends SymbolDB {
}
if (super.delete()) {
if ((parentSymbol instanceof SymbolDB) && ((SymbolDB) parentSymbol).isDeleting()) {
// do not replace function with label if parent namespace is getting removed
return false;
}
if (restoreLabel) {
boolean restored = createLabelForDeletedFunctionName(address, symName, extData,
namespace, source, pinned);
if (!restored && isExternal()) {
// remove all associated external references if label not restored
// Recreate a symbol with the function symbol's name because deleting the function
// does not mean that we want to lose the function name (that is our policy).
Symbol newSymbol;
if (isExternal()) {
newSymbol = symbolMgr.createExternalCodeSymbol(address, symName, namespace,
source, extOrigImportName, extProgramAddr);
}
else {
newSymbol = symbolMgr.createLabel(address, symName, namespace, source);
newSymbol.setPrimary();
if (pinned) {
newSymbol.setPinned(true);
}
}
if (newSymbol == null && isExternal()) {
// remove all associated external references if external symbol not restored
symbolMgr.getReferenceManager().removeAllReferencesTo(getAddress());
}
}
return true;
}
}
catch (InvalidInputException e) {
// This shouldn't happen.
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
catch (IOException e) {
symbolMgr.dbError(e);
}
finally {
lock.release();
}
return false;
}
/**
* Recreate a symbol with the function symbol's name because deleting the function
* does not mean that we want to lose the function name (that is our policy).
*/
private boolean createLabelForDeletedFunctionName(Address entryPoint, String symName,
String stringData, Namespace namespace, SourceType source, boolean pinned) {
Symbol parentSymbol = namespace.getSymbol();
if ((parentSymbol instanceof SymbolDB) && ((SymbolDB) parentSymbol).isDeleting()) {
// do not replace function with label if parent namespace is getting removed
return false;
}
try {
Symbol newSym =
symbolMgr.createCodeSymbol(entryPoint, symName, namespace, source, stringData);
newSym.setPrimary();
if (pinned) {
newSym.setPinned(true);
}
return true;
}
catch (InvalidInputException e) {
// This shouldn't happen.
Msg.error(this, "Unexpected Exception: " + e.getMessage(), e);
}
return false;
}
@Override
public Object getObject() {
public Function getObject() {
return functionMgr.getFunction(key);
}
@ -165,21 +155,6 @@ public class FunctionSymbol extends SymbolDB {
return true;
}
@Override
public boolean isPinned() {
if (!isExternal()) {
return doIsPinned();
}
return false;
}
@Override
public void setPinned(boolean pinned) {
if (!isExternal()) {
doSetPinned(pinned);
}
}
@Override
public ProgramLocation getProgramLocation() {
lock.acquire();
@ -187,7 +162,7 @@ public class FunctionSymbol extends SymbolDB {
if (!checkIsValid()) {
return null;
}
Function f = (Function) getObject();
Function f = getObject();
String signature = f.getPrototypeString(false, false);
return new FunctionReturnTypeFieldLocation(getProgram(), address, 0, signature,
f.getReturnType().getName());
@ -340,27 +315,6 @@ public class FunctionSymbol extends SymbolDB {
}
}
@Override
public boolean hasMultipleReferences() {
lock.acquire();
try {
checkIsValid();
if (super.hasMultipleReferences()) {
return true;
}
List<Long> thunkIds = functionMgr.getThunkFunctionIds(key);
if (thunkIds != null) {
return thunkIds.size() > 1;
}
return false;
}
finally {
lock.release();
}
}
@Override
public boolean hasReferences() {
lock.acquire();

View file

@ -27,7 +27,7 @@ import ghidra.util.exception.InvalidInputException;
*/
class GhidraClassDB implements GhidraClass {
private SymbolDB symbol;
private ClassSymbol symbol;
private NamespaceManager namespaceMgr;
/**
@ -35,7 +35,7 @@ class GhidraClassDB implements GhidraClass {
* @param symbol the symbol for this GhidraClass
* @param namespaceMgr the namespace manager
*/
GhidraClassDB(SymbolDB symbol, NamespaceManager namespaceMgr) {
GhidraClassDB(ClassSymbol symbol, NamespaceManager namespaceMgr) {
this.symbol = symbol;
this.namespaceMgr = namespaceMgr;
}
@ -126,8 +126,8 @@ class GhidraClassDB implements GhidraClass {
* @see ghidra.program.model.symbol.Namespace#setParentNamespace(ghidra.program.model.symbol.Namespace)
*/
@Override
public void setParentNamespace(Namespace parentNamespace) throws DuplicateNameException,
InvalidInputException, CircularDependencyException {
public void setParentNamespace(Namespace parentNamespace)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
symbol.setNamespace(parentNamespace);
}

View file

@ -40,7 +40,7 @@ public class GlobalVariableSymbolDB extends VariableSymbolDB {
VariableStorageManagerDB variableMgr, Address address, DBRecord record) {
super(symbolMgr, cache, SymbolType.GLOBAL_VAR, variableMgr, address, record);
if (record.getLongValue(
SymbolDatabaseAdapter.SYMBOL_PARENT_COL) != Namespace.GLOBAL_NAMESPACE_ID) {
SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL) != Namespace.GLOBAL_NAMESPACE_ID) {
throw new AssertException();
}
}
@ -57,15 +57,8 @@ public class GlobalVariableSymbolDB extends VariableSymbolDB {
}
@Override
public Object getObject() {
if (!checkIsValid()) {
return null;
}
VariableStorage storage = getVariableStorage();
if (storage == null) {
return null;
}
return storage;
public Variable getObject() {
throw new UnsupportedOperationException();
}
@Override

View file

@ -27,7 +27,7 @@ import ghidra.util.exception.InvalidInputException;
* Object to represent an external library.
*/
class LibraryDB implements Library {
private SymbolDB symbol;
private LibrarySymbol symbol;
private NamespaceManager namespaceMgr;
/**
@ -35,62 +35,46 @@ class LibraryDB implements Library {
* @param symbol the library symbol.
* @param namespaceMgr the namespace manager
*/
LibraryDB(SymbolDB symbol, NamespaceManager namespaceMgr) {
LibraryDB(LibrarySymbol symbol, NamespaceManager namespaceMgr) {
this.symbol = symbol;
this.namespaceMgr = namespaceMgr;
}
/**
* @see ghidra.program.model.symbol.Namespace#getSymbol()
*/
@Override
public Symbol getSymbol() {
return symbol;
}
/**
* @see ghidra.program.model.symbol.Namespace#getName()
*/
@Override
public String getName() {
return symbol.getName();
}
/**
* @see ghidra.program.model.symbol.Namespace#getID()
*/
@Override
public long getID() {
return symbol.getID();
}
/**
* @see ghidra.program.model.symbol.Namespace#getParentNamespace()
*/
@Override
public Namespace getParentNamespace() {
return symbol.getParentNamespace();
}
/**
* @see ghidra.program.model.symbol.Namespace#getBody()
*/
@Override
public AddressSetView getBody() {
return namespaceMgr.getAddressSet(this);
}
/**
* @see ghidra.program.model.symbol.Namespace#getName(boolean)
*/
@Override
public String getName(boolean includeNamespacePath) {
return symbol.getName(includeNamespacePath);
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public int hashCode() {
return symbol.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
@ -104,18 +88,15 @@ class LibraryDB implements Library {
return symbol == lib.symbol;
}
/**
* @see ghidra.program.model.symbol.Namespace#setParentNamespace(ghidra.program.model.symbol.Namespace)
*/
@Override
public void setParentNamespace(Namespace parentNamespace) throws DuplicateNameException,
InvalidInputException, CircularDependencyException {
public void setParentNamespace(Namespace parentNamespace)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
symbol.setNamespace(parentNamespace);
}
@Override
public String getAssociatedProgramPath() {
return symbol.getSymbolStringData();
return symbol.getExternalLibraryPath();
}
@Override

View file

@ -40,13 +40,10 @@ public class LibrarySymbol extends SymbolDB {
* Constructs a new Library Symbol
* @param symbolMgr the symbol manager
* @param cache symbol object cache
* @param address the address for this symbol
* @param record the record for this symbol
*/
public LibrarySymbol(SymbolManager symbolMgr, DBObjectCache<SymbolDB> cache, Address address,
DBRecord record) {
super(symbolMgr, cache, address, record);
public LibrarySymbol(SymbolManager symbolMgr, DBObjectCache<SymbolDB> cache, DBRecord record) {
super(symbolMgr, cache, Address.NO_ADDRESS, record);
}
@Override
@ -81,16 +78,6 @@ public class LibrarySymbol extends SymbolDB {
}
}
@Override
public void setSymbolStringData(String newPath) {
String oldPath = getSymbolStringData();
super.setSymbolStringData(newPath);
symbolMgr.getProgram()
.setObjChanged(ProgramEvent.EXTERNAL_PATH_CHANGED, getName(), oldPath, newPath);
}
@Override
public SymbolType getSymbolType() {
return SymbolType.LIBRARY;
@ -102,11 +89,20 @@ public class LibrarySymbol extends SymbolDB {
}
@Override
public Object getObject() {
if (library == null) {
library = new LibraryDB(this, symbolMgr.getProgram().getNamespaceManager());
public Library getObject() {
lock.acquire();
try {
if (!checkIsValid()) {
return null;
}
if (library == null) {
library = new LibraryDB(this, symbolMgr.getProgram().getNamespaceManager());
}
return library;
}
finally {
lock.release();
}
return library;
}
@Override
@ -119,4 +115,38 @@ public class LibrarySymbol extends SymbolDB {
return super.isValidParent(parent) &&
SymbolType.LIBRARY.isValidParent(symbolMgr.getProgram(), parent, address, isExternal());
}
/**
* {@return the library program path within the project (may be null)}
*/
public String getExternalLibraryPath() {
validate(lock);
return record.getString(SymbolDatabaseAdapter.SYMBOL_LIBPATH_COL);
}
/**
* Set the library program path within the project.
* @param libraryPath library program path or null to clear
*/
public void setExternalLibraryPath(String libraryPath) {
String oldPath = getExternalLibraryPath();
lock.acquire();
try {
checkDeleted();
setRecordFields(record, libraryPath);
updateRecord();
}
finally {
lock.release();
}
symbolMgr.getProgram()
.setObjChanged(ProgramEvent.EXTERNAL_PATH_CHANGED, getName(), oldPath, libraryPath);
}
static void setRecordFields(DBRecord record, String libraryPath) {
record.setString(SymbolDatabaseAdapter.SYMBOL_LIBPATH_COL, libraryPath);
}
}

View file

@ -0,0 +1,437 @@
/* ###
* 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.util.ArrayList;
import java.util.Objects;
import db.DBRecord;
import ghidra.program.database.DBObjectCache;
import ghidra.program.database.external.ExternalLocationDB;
import ghidra.program.model.address.Address;
import ghidra.program.model.symbol.*;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.UnknownProgressWrappingTaskMonitor;
/**
* {@link MemorySymbol} corresponds to any symbol that resides at a memory location.
* The corresponding address may be either a {@link Address#isMemoryAddress() memory address}
* or a fake {@link Address#isExternalAddress() external address}. While an external address is
* not a memory address it corresponds to an {@link ExternalLocation} which may identify a
* specific memory address if known.
*/
public abstract class MemorySymbol extends SymbolDB {
/**
* Constructs a new MemorySymbol which corresponds to the specified symbol record,
* @param mgr the symbol manager
* @param cache symbol object cache
* @param addr the address associated with the symbol
* @param record the record for this symbol
*/
protected MemorySymbol(SymbolManager mgr, DBObjectCache<SymbolDB> cache, Address addr,
DBRecord record) {
super(mgr, cache, addr, record);
if (!addr.isMemoryAddress() && !isExternal()) {
throw new IllegalArgumentException("memory or external address required");
}
}
/**
* Constructs a new MemorySymbol which corresponds to the specified symbol key and has
* no record. This is intended to support dynamic label cases which do not have a record
* and do not support an external address.
* @param mgr the symbol manager
* @param cache symbol object cache
* @param addr the address associated with the symbol
* @param key this must be the absolute encoding of addr
*/
protected MemorySymbol(SymbolManager mgr, DBObjectCache<SymbolDB> cache, Address addr,
long key) {
super(mgr, cache, addr, key);
if (!addr.isMemoryAddress()) {
throw new IllegalArgumentException("memory address required");
}
}
@Override
public final boolean isExternalEntryPoint() {
validate(lock);
return symbolMgr.isExternalEntryPoint(address);
}
@Override
public final boolean isExternal() {
return address.isExternalAddress();
}
@Override
public final boolean isPinned() {
if (!isExternal()) {
return doIsPinned();
}
return false;
}
@Override
public final void setPinned(boolean pinned) {
if (!isExternal()) {
doSetPinned(pinned);
}
}
private boolean doIsPinned() {
lock.acquire();
try {
checkIsValid();
if (record == null) {
return false;
}
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
return ((flags & SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG) != 0);
}
finally {
lock.release();
}
}
private void doSetPinned(boolean pinned) {
lock.acquire();
try {
checkDeleted();
if (pinned == isPinned()) {
return;
}
if (record != null) {
updatePinnedFlag(pinned);
updateRecord();
symbolMgr.symbolAnchoredFlagChanged(this);
}
}
finally {
lock.release();
}
}
private void updatePinnedFlag(boolean pinned) {
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
if (pinned) {
flags |= SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
}
else {
flags &= ~SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
}
record.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, flags);
}
/**
* low level record adjustment to move a symbol. Used only when moving a memory block or
* changing the image base.
*
* @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 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();
// 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 (newNamespace != null) {
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL,
newNamespace.getID());
}
if (newSource != null) {
setSourceFlagBit(newSource);
}
updatePinnedFlag(pinned);
updateRecord();
setInvalid();
}
finally {
lock.release();
}
}
private boolean hasExactlyOneSymbolAtAddress(Address addr) {
SymbolIterator it = symbolMgr.getSymbolsAsIterator(addr);
if (!it.hasNext()) {
return false;
}
it.next();
return !it.hasNext();
}
@Override
public int getReferenceCount() {
lock.acquire();
try {
checkIsValid();
ReferenceManager rm = symbolMgr.getReferenceManager();
// If there is only one symbol, then all the references to this address count
if (address.isExternalAddress() || hasExactlyOneSymbolAtAddress(address)) {
return rm.getReferenceCountTo(address);
}
// search through references and see which ones apply specifically to this symbol
ReferenceIterator iter = rm.getReferencesTo(address);
int count = 0;
boolean isPrimary = this.isPrimary();
while (iter.hasNext()) {
Reference ref = iter.next();
long symbolID = ref.getSymbolID();
// references refer to me if it matches my key or I'm primary and it doesn't
// specify a specific symbol id
if (symbolID == key || (isPrimary && symbolID < 0)) {
count++;
}
}
return count;
}
finally {
lock.release();
}
}
@Override
public boolean hasReferences() {
lock.acquire();
try {
checkIsValid();
ReferenceManager rm = symbolMgr.getReferenceManager();
ReferenceIterator iter = rm.getReferencesTo(address);
boolean isPrimary = this.isPrimary();
while (iter.hasNext()) {
Reference ref = iter.next();
long symbolID = ref.getSymbolID();
if (symbolID == key || (isPrimary && symbolID < 0)) {
return true;
}
}
return false;
}
finally {
lock.release();
}
}
@Override
public Reference[] getReferences(TaskMonitor monitor) {
lock.acquire();
try {
checkIsValid();
if (monitor == null) {
monitor = TaskMonitor.DUMMY;
}
if (monitor.getMaximum() == 0) {
// If the monitor has not been initialized, then the progress will not correctly
// display anything as setProgress() is called below. We can't know what to
// initialize to without counting all the references, which is as much work as
// this method.
monitor = new UnknownProgressWrappingTaskMonitor(monitor, 20);
}
ReferenceManager rm = symbolMgr.getReferenceManager();
ReferenceIterator iter = rm.getReferencesTo(address);
boolean isPrimary = this.isPrimary();
ArrayList<Reference> list = new ArrayList<>();
int cnt = 0;
while (iter.hasNext()) {
if (monitor.isCancelled()) {
break; // return partial list
}
Reference ref = iter.next();
long symbolID = ref.getSymbolID();
if (symbolID == key || (isPrimary && symbolID < 0)) {
list.add(ref);
monitor.setProgress(cnt++);
}
}
Reference[] refs = new Reference[list.size()];
return list.toArray(refs);
}
finally {
lock.release();
}
}
/**
* Gets the optional field which is intended to store the original mangled name for an external
* {@link SymbolType#LABEL} or {@link SymbolType#FUNCTION} which has an
* {@link Address#isExternalAddress() external address}. These symbol types correspond
* to {@link CodeSymbol} and {@link FunctionSymbol} DB symbol implementations respectively.
* This is generally set when an {@link ExternalLocationDB} is renamed which generally
* corresponds the external symbol being demangled.
*
* @return original imported external name or null if not external or symbol has not been
* demangled or renamed.
*/
public final String getExternalOriginalImportedName() {
lock.acquire();
try {
checkIsValid();
if (record == null) {
return null;
}
return record.getString(SymbolDatabaseAdapter.SYMBOL_ORIGINAL_IMPORTED_NAME_COL);
}
finally {
lock.release();
}
}
/**
* Sets the symbol's original imported external name field for an external
* {@link SymbolType#LABEL} or {@link SymbolType#FUNCTION} which has an
* {@link Address#isExternalAddress() external address}. These symbol types correspond
* to {@link CodeSymbol} and {@link FunctionSymbol} DB symbol implementations respectively.
* This is generally set when an {@link ExternalLocationDB} is renamed which generally
* corresponds the external symbol being demangled.
*
* @param originalImportedName the original import name or null
* @param notify if true, a program change notification will be generated
* @throws UnsupportedOperationException if symbol is neither an external {@link SymbolType#LABEL}
* or {@link SymbolType#FUNCTION}.
*/
public final void setExternalOriginalImportedName(String originalImportedName, boolean notify) {
SymbolType type = getSymbolType();
if (!getAddress().isExternalAddress() ||
(type != SymbolType.LABEL && type != SymbolType.FUNCTION)) {
throw new javax.help.UnsupportedOperationException(
"Symbol does not support: originalImportedName");
}
lock.acquire();
try {
checkDeleted();
if (record == null) {
return;
}
String oldData =
record.getString(SymbolDatabaseAdapter.SYMBOL_ORIGINAL_IMPORTED_NAME_COL);
if (!Objects.equals(originalImportedName, oldData)) {
record.setString(SymbolDatabaseAdapter.SYMBOL_ORIGINAL_IMPORTED_NAME_COL,
originalImportedName);
updateRecord();
if (notify) {
symbolMgr.symbolDataChanged(this);
}
}
}
finally {
lock.release();
}
}
/**
* Gets the optional field which is intended to store the original mangled name for an external
* {@link SymbolType#LABEL} or {@link SymbolType#FUNCTION} which has an
* {@link Address#isExternalAddress() external address}. These symbol types correspond
* to {@link CodeSymbol} and {@link FunctionSymbol} DB symbol implementations respectively.
* This is generally set when an {@link ExternalLocationDB} is renamed which generally
* corresponds the external symbol being demangled.
*
* @return external Program address or null if not external or unknown
*/
public final Address getExternalProgramAddress() {
lock.acquire();
try {
checkIsValid();
if (record == null) {
return null;
}
// NOTE: String is used to avoid excessive AddressMap segmentation. This does
// prevent address space renaming as facilitated by Language upgrade and transition
// capabilities.
String addrStr = record.getString(SymbolDatabaseAdapter.SYMBOL_EXTERNAL_PROG_ADDR_COL);
if (addrStr == null) {
return null;
}
return symbolMgr.getAddressMap().getAddressFactory().getAddress(addrStr);
}
finally {
lock.release();
}
}
/**
* Sets the symbol's external Program Address field for an external
* {@link SymbolType#LABEL} or {@link SymbolType#FUNCTION} which has an
* {@link Address#isExternalAddress() external address}. These symbol types correspond
* to {@link CodeSymbol} and {@link FunctionSymbol} DB symbol implementations respectively.
*
* @param externalProgramAddress the external Program Address which corresponds to the
* externally linked location (may be null).
* @param notify if true, a program change notification will be generated
* @throws UnsupportedOperationException if symbol is neither an external {@link SymbolType#LABEL}
* or {@link SymbolType#FUNCTION}.
*/
public final void setExternalProgramAddress(Address externalProgramAddress, boolean notify) {
SymbolType type = getSymbolType();
if (!getAddress().isExternalAddress() ||
(type != SymbolType.LABEL && type != SymbolType.FUNCTION)) {
throw new javax.help.UnsupportedOperationException(
"Symbol does not support: external program address");
}
if (externalProgramAddress != null && !externalProgramAddress.isLoadedMemoryAddress()) {
throw new IllegalArgumentException("Memory address required for external program");
}
lock.acquire();
try {
checkDeleted();
if (record == null) {
return;
}
String addrStr =
externalProgramAddress != null ? externalProgramAddress.toString() : null;
String oldData = record.getString(SymbolDatabaseAdapter.SYMBOL_EXTERNAL_PROG_ADDR_COL);
if (!Objects.equals(addrStr, oldData)) {
record.setString(SymbolDatabaseAdapter.SYMBOL_EXTERNAL_PROG_ADDR_COL, addrStr);
updateRecord();
if (notify) {
symbolMgr.symbolDataChanged(this);
}
}
}
finally {
lock.release();
}
}
static void setExternalFields(DBRecord record, String originalImportName,
Address externalProgramAddress) {
String addrStr = externalProgramAddress != null ? externalProgramAddress.toString() : null;
record.setString(SymbolDatabaseAdapter.SYMBOL_EXTERNAL_PROG_ADDR_COL, addrStr);
record.setString(SymbolDatabaseAdapter.SYMBOL_ORIGINAL_IMPORTED_NAME_COL,
originalImportName);
}
}

View file

@ -28,7 +28,7 @@ import ghidra.util.exception.InvalidInputException;
*/
class NamespaceDB implements Namespace {
private SymbolDB symbol;
private NamespaceSymbol symbol;
private NamespaceManager namespaceMgr;
/**
@ -36,62 +36,46 @@ class NamespaceDB implements Namespace {
* @param symbol the symbol associated with this namespace.
* @param namespaceMgr the namespace manager
*/
NamespaceDB(SymbolDB symbol, NamespaceManager namespaceMgr) {
NamespaceDB(NamespaceSymbol symbol, NamespaceManager namespaceMgr) {
this.symbol = symbol;
this.namespaceMgr = namespaceMgr;
}
/**
* @see ghidra.program.model.symbol.Namespace#getSymbol()
*/
@Override
public Symbol getSymbol() {
return symbol;
}
/**
* @see ghidra.program.model.symbol.Namespace#getName()
*/
@Override
public String getName() {
return symbol.getName();
}
/**
* @see ghidra.program.model.symbol.Namespace#getID()
*/
@Override
public long getID() {
return symbol.getID();
}
/**
* @see ghidra.program.model.symbol.Namespace#getParentNamespace()
*/
@Override
public Namespace getParentNamespace() {
return symbol.getParentNamespace();
}
/**
* @see ghidra.program.model.symbol.Namespace#getBody()
*/
@Override
public AddressSetView getBody() {
return namespaceMgr.getAddressSet(this);
}
/**
* @see ghidra.program.model.symbol.Namespace#getName(boolean)
*/
@Override
public String getName(boolean includeNamespacePath) {
return symbol.getName(includeNamespacePath);
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public int hashCode() {
return symbol.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
@ -105,18 +89,12 @@ class NamespaceDB implements Namespace {
return symbol == nameSpace.symbol;
}
/**
* @see ghidra.program.model.symbol.Namespace#setParentNamespace(ghidra.program.model.symbol.Namespace)
*/
@Override
public void setParentNamespace(Namespace parentNamespace) throws DuplicateNameException,
InvalidInputException, CircularDependencyException {
public void setParentNamespace(Namespace parentNamespace)
throws DuplicateNameException, InvalidInputException, CircularDependencyException {
symbol.setNamespace(parentNamespace);
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return getName(true);

View file

@ -31,12 +31,10 @@ public class NamespaceSymbol extends SymbolDB {
* Construct a new namespace symbol
* @param mgr the symbol manager.
* @param cache symbol object cache
* @param addr the address for this symbol.
* @param record the record for this symbol.
*/
NamespaceSymbol(SymbolManager mgr, DBObjectCache<SymbolDB> cache, Address addr,
DBRecord record) {
super(mgr, cache, addr, record);
NamespaceSymbol(SymbolManager mgr, DBObjectCache<SymbolDB> cache, DBRecord record) {
super(mgr, cache, Address.NO_ADDRESS, record);
}
@Override
@ -56,15 +54,20 @@ public class NamespaceSymbol extends SymbolDB {
}
@Override
public Object getObject() {
return getNamespace();
}
private Namespace getNamespace() {
if (namespace == null) {
namespace = new NamespaceDB(this, symbolMgr.getProgram().getNamespaceManager());
public Namespace getObject() {
lock.acquire();
try {
if (!checkIsValid()) {
return null;
}
if (namespace == null) {
namespace = new NamespaceDB(this, symbolMgr.getProgram().getNamespaceManager());
}
return namespace;
}
finally {
lock.release();
}
return namespace;
}
@Override

View file

@ -16,7 +16,8 @@
package ghidra.program.database.symbol;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import db.DBRecord;
import db.Field;
@ -34,16 +35,15 @@ import ghidra.util.Lock;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.UnknownProgressWrappingTaskMonitor;
/**
* Base class for symbols
*/
public abstract class SymbolDB extends DatabaseObject implements Symbol {
private DBRecord record;
private boolean isDeleting = false;
protected DBRecord record;
protected Address address;
protected SymbolManager symbolMgr;
protected Lock lock;
@ -132,48 +132,6 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
addr);
}
/**
* low level record adjustment to move a symbol. Used only when moving a memory block or
* changing the image base.
*
* @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 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();
// 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 (newNamespace != null) {
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, newNamespace.getID());
}
if (newSource != null) {
setSourceFlagBit(newSource);
}
updatePinnedFlag(pinned);
updateRecord();
setInvalid();
}
finally {
lock.release();
}
}
@Override
public final String getName() {
String name = cachedName;
@ -266,158 +224,11 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
@Override
public int getReferenceCount() {
lock.acquire();
try {
checkIsValid();
ReferenceManager rm = symbolMgr.getReferenceManager();
// if there is only one symbol, then all the references to this address count
if (hasExactlyOneSymbolAtAddress(address)) {
return rm.getReferenceCountTo(address);
}
// search through references and see which ones apply specifically to this symbol
ReferenceIterator iter = rm.getReferencesTo(address);
int count = 0;
boolean isPrimary = this.isPrimary();
while (iter.hasNext()) {
Reference ref = iter.next();
long symbolID = ref.getSymbolID();
// references refer to me if it matches my key or I'm primary and it doesn't
// specify a specific symbol id
if (symbolID == key || (isPrimary && symbolID < 0)) {
count++;
}
}
return count;
}
finally {
lock.release();
}
}
private boolean hasExactlyOneSymbolAtAddress(Address addr) {
SymbolIterator it = symbolMgr.getSymbolsAsIterator(addr);
if (!it.hasNext()) {
return false;
}
it.next();
return !it.hasNext();
}
@Override
public Reference[] getReferences(TaskMonitor monitor) {
lock.acquire();
try {
checkIsValid();
if (monitor == null) {
monitor = TaskMonitor.DUMMY;
}
if (monitor.getMaximum() == 0) {
// If the monitor has not been initialized, then the progress will not correctly
// display anything as setProgress() is called below. We can't know what to
// initialize to without counting all the references, which is as much work as
// this method.
monitor = new UnknownProgressWrappingTaskMonitor(monitor, 20);
}
ReferenceManager rm = symbolMgr.getReferenceManager();
ReferenceIterator iter = rm.getReferencesTo(address);
boolean isPrimary = this.isPrimary();
ArrayList<Reference> list = new ArrayList<>();
int cnt = 0;
while (iter.hasNext()) {
if (monitor.isCancelled()) {
break; // return partial list
}
Reference ref = iter.next();
long symbolID = ref.getSymbolID();
if (symbolID == key || (isPrimary && symbolID < 0)) {
list.add(ref);
monitor.setProgress(cnt++);
}
}
Reference[] refs = new Reference[list.size()];
return list.toArray(refs);
}
finally {
lock.release();
}
}
@Override
public Reference[] getReferences() {
return getReferences(TaskMonitor.DUMMY);
}
@Override
public boolean hasMultipleReferences() {
lock.acquire();
try {
checkIsValid();
ReferenceManager rm = symbolMgr.getReferenceManager();
ReferenceIterator iter = rm.getReferencesTo(address);
boolean isPrimary = this.isPrimary();
int count = 0;
while (iter.hasNext()) {
Reference ref = iter.next();
long symbolID = ref.getSymbolID();
if (symbolID == key || (isPrimary && symbolID < 0)) {
count++;
if (count > 1) {
return true;
}
}
}
return false;
}
finally {
lock.release();
}
}
@Override
public boolean hasReferences() {
lock.acquire();
try {
checkIsValid();
ReferenceManager rm = symbolMgr.getReferenceManager();
ReferenceIterator iter = rm.getReferencesTo(address);
boolean isPrimary = this.isPrimary();
while (iter.hasNext()) {
Reference ref = iter.next();
long symbolID = ref.getSymbolID();
if (symbolID == key || (isPrimary && symbolID < 0)) {
return true;
}
}
return false;
}
finally {
lock.release();
}
}
@Override
public boolean isDynamic() {
return (record == null);
}
@Override
public boolean isExternalEntryPoint() {
lock.acquire();
try {
checkIsValid();
return symbolMgr.isExternalEntryPoint(address);
}
finally {
lock.release();
}
}
@Override
public abstract boolean isPrimary();
@ -452,7 +263,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
private void setSourceFlagBit(SourceType newSource) {
protected void setSourceFlagBit(SourceType newSource) {
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
byte clearBits = SymbolDatabaseAdapter.SYMBOL_SOURCE_BITS;
byte setBits = (byte) newSource.ordinal();
@ -479,60 +290,6 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
@Override
public boolean isPinned() {
return false; //most symbols can't be pinned.
}
protected boolean doIsPinned() {
lock.acquire();
try {
checkIsValid();
if (record == null) {
return false;
}
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
return ((flags & SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG) != 0);
}
finally {
lock.release();
}
}
@Override
public void setPinned(boolean pinned) {
throw new UnsupportedOperationException("Only Code and Function Symbols may be pinned.");
}
protected void doSetPinned(boolean pinned) {
lock.acquire();
try {
checkDeleted();
if (pinned == isPinned()) {
return;
}
if (record != null) {
updatePinnedFlag(pinned);
updateRecord();
symbolMgr.symbolAnchoredFlagChanged(this);
}
}
finally {
lock.release();
}
}
private void updatePinnedFlag(boolean pinned) {
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
if (pinned) {
flags |= SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
}
else {
flags &= ~SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
}
record.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, flags);
}
@Override
public void setName(String newName, SourceType source)
throws DuplicateNameException, InvalidInputException {
@ -626,7 +383,8 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, newNamespace.getID());
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL,
newNamespace.getID());
record.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, newName);
updateSymbolSource(record, source);
updateRecord();
@ -750,7 +508,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return (int) key;
}
private void updateRecord() {
protected void updateRecord() {
try {
symbolMgr.getDatabaseAdapter().updateSymbolRecord(record);
}
@ -781,7 +539,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return null;
}
return symbolMgr
.getSymbol(record.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL));
.getSymbol(record.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL));
}
finally {
lock.release();
@ -795,7 +553,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
if (record == null) {
return Namespace.GLOBAL_NAMESPACE_ID;
}
return record.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL);
return record.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL);
}
finally {
lock.release();
@ -811,51 +569,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return true;
}
return record.getLongValue(
SymbolDatabaseAdapter.SYMBOL_PARENT_COL) == Namespace.GLOBAL_NAMESPACE_ID;
}
finally {
lock.release();
}
}
/**
* 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_STRING_DATA_COL);
}
finally {
lock.release();
}
}
/**
* 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_STRING_DATA_COL);
if (Objects.equals(stringData, oldData)) {
return;
}
record.setString(SymbolDatabaseAdapter.SYMBOL_STRING_DATA_COL, stringData);
updateRecord();
symbolMgr.symbolDataChanged(this);
SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL) == Namespace.GLOBAL_NAMESPACE_ID;
}
finally {
lock.release();
@ -863,21 +577,13 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
public long getDataTypeId() {
lock.acquire();
try {
checkIsValid();
if (record != null) {
Field value = record.getFieldValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL);
if (value.isNull()) {
return -1;
}
return value.getLongValue();
}
validate(lock);
// record always present when use of datatype ID is supported (i.e., external location)
Field value = record.getFieldValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL);
if (value.isNull()) {
return -1;
}
finally {
lock.release();
}
return value.getLongValue();
}
/**
@ -899,44 +605,6 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
/**
* gets the generic symbol data 2 data.
* @return the symbol data
*/
protected int getVariableOffset() {
lock.acquire();
try {
checkIsValid();
if (record != null) {
return record.getIntValue(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL);
}
return 0;
}
finally {
lock.release();
}
}
/**
* 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 setVariableOffset(int offset) {
lock.acquire();
try {
checkDeleted();
if (record != null) {
record.setIntValue(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL, offset);
updateRecord();
symbolMgr.symbolDataChanged(this);
}
}
finally {
lock.release();
}
}
protected void doSetPrimary(boolean primary) {
lock.acquire();
try {
@ -1023,4 +691,5 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
this.record = record;
keyChanged(record.getKey());
}
}

View file

@ -39,24 +39,37 @@ abstract class SymbolDatabaseAdapter {
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_PARENT_ID_COL = 2;
static final int SYMBOL_TYPE_COL = 3;
static final int SYMBOL_STRING_DATA_COL = 4;
static final int SYMBOL_FLAGS_COL = 5;
static final int SYMBOL_FLAGS_COL = 4;
// sparse fields - the following fields are not always applicable so they are optional and
// 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 int SYMBOL_HASH_COL = 5;
static final int SYMBOL_PRIMARY_COL = 6;
static final int SYMBOL_DATATYPE_COL = 7;
static final int SYMBOL_VAROFFSET_COL = 8;
static final int SYMBOL_ORIGINAL_IMPORTED_NAME_COL = 9;
static final int SYMBOL_EXTERNAL_PROG_ADDR_COL = 10;
static final int SYMBOL_COMMENT_COL = 11;
static final int SYMBOL_LIBPATH_COL = 12;
static final Schema SYMBOL_SCHEMA = SymbolDatabaseAdapterV3.V3_SYMBOL_SCHEMA;
static final Schema SYMBOL_SCHEMA = SymbolDatabaseAdapterV4.V4_SYMBOL_SCHEMA;
// Bits 0 & 1 are used for the source of the symbol.
static final byte SYMBOL_SOURCE_BITS = (byte) 0x3;
static final byte SYMBOL_PINNED_FLAG = (byte) 0x4; // Bit 2 is flag for "anchored to address".
// Symbol type constants
static final int SYMBOL_TYPE_LABEL = SymbolType.LABEL.getID();
static final int SYMBOL_TYPE_LIBRARY = SymbolType.LIBRARY.getID();
static final int SYMBOL_TYPE_NAMESPACE = SymbolType.NAMESPACE.getID();
static final int SYMBOL_TYPE_CLASS = SymbolType.CLASS.getID();
static final int SYMBOL_TYPE_FUNCTION = SymbolType.FUNCTION.getID();
static final int SYMBOL_TYPE_PARAMETER = SymbolType.PARAMETER.getID();
static final int SYMBOL_TYPE_LOCAL_VAR = SymbolType.LOCAL_VAR.getID();
static final int SYMBOL_TYPE_GLOBAL_VAR = SymbolType.GLOBAL_VAR.getID();
// TODO: NEXT UPGRADE: remove all variable/parameter symbols with NO_ADDRESS
/**
@ -75,11 +88,11 @@ abstract class SymbolDatabaseAdapter {
throws VersionException, CancelledException, IOException {
if (openMode == OpenMode.CREATE) {
return new SymbolDatabaseAdapterV3(dbHandle, addrMap, true);
return new SymbolDatabaseAdapterV4(dbHandle, addrMap, true);
}
try {
SymbolDatabaseAdapter adapter = new SymbolDatabaseAdapterV3(dbHandle, addrMap, false);
SymbolDatabaseAdapter adapter = new SymbolDatabaseAdapterV4(dbHandle, addrMap, false);
return adapter;
}
catch (VersionException e) {
@ -99,26 +112,33 @@ abstract class SymbolDatabaseAdapter {
}
private static SymbolDatabaseAdapter findReadOnlyAdapter(DBHandle handle, AddressMap addrMap)
throws VersionException, IOException {
throws VersionException {
try {
return new SymbolDatabaseAdapterV3(handle, addrMap.getOldAddressMap());
}
catch (VersionException e) {
// failed try older version
}
try {
return new SymbolDatabaseAdapterV2(handle, addrMap.getOldAddressMap());
}
catch (VersionException e1) {
catch (VersionException e) {
// failed try older version
}
try {
return new SymbolDatabaseAdapterV1(handle, addrMap.getOldAddressMap());
}
catch (VersionException e1) {
catch (VersionException e) {
// failed try older version
}
try {
return new SymbolDatabaseAdapterV0(handle, addrMap.getOldAddressMap());
}
catch (VersionException e1) {
catch (VersionException e) {
// failed - can't handle whatever version this is trying to open
}
@ -140,7 +160,7 @@ abstract class SymbolDatabaseAdapter {
dbHandle.deleteTable(SYMBOL_TABLE_NAME);
SymbolDatabaseAdapter newAdapter = new SymbolDatabaseAdapterV3(dbHandle, addrMap, true);
SymbolDatabaseAdapter newAdapter = new SymbolDatabaseAdapterV4(dbHandle, addrMap, true);
copyTempToNewAdapter(tmpAdapter, newAdapter, monitor);
return newAdapter;
@ -166,7 +186,7 @@ abstract class SymbolDatabaseAdapter {
((SymbolDatabaseAdapterV0) oldAdapter).extractLocalSymbols(tmpHandle, monitor);
}
SymbolDatabaseAdapterV3 tmpAdapter = new SymbolDatabaseAdapterV3(tmpHandle, addrMap, true);
SymbolDatabaseAdapterV4 tmpAdapter = new SymbolDatabaseAdapterV4(tmpHandle, addrMap, true);
RecordIterator iter = oldAdapter.getSymbols();
while (iter.hasNext()) {
monitor.checkCancelled();
@ -200,26 +220,22 @@ abstract class SymbolDatabaseAdapter {
}
/**
* Create a new symbol
* Instantiate a new basic symbol record. Caller is responsible for updating any related
* optional record fields and then adding to the table via the
* {@link #updateSymbolRecord(DBRecord)} method.
*
* @param name name of the symbol
* @param address the address for the symbol
* @param namespaceID the id of the containing namespace symbol
* @param address the address for the symbol
* @param symbolType the type of this symbol
* @param stringData place to store a String value that depends on the symbol type
* @param isPrimary if true, symbol record will be tagged as primary (relavent for label and
* function symbols only).
* @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
* @return new symbol record (not yet added to symbol table)
*/
abstract DBRecord createSymbol(String name, Address address, long namespaceID,
SymbolType symbolType, String stringData, Long dataTypeId, Integer varOffset,
SourceType source, boolean isPrimary) throws IOException;
abstract DBRecord createSymbolRecord(String name, long namespaceID, Address address,
SymbolType symbolType, boolean isPrimary, SourceType source);
/**
* Get the record with the given symbol ID
@ -372,10 +388,29 @@ abstract class SymbolDatabaseAdapter {
* This only includes memory-based stored symbols.
*
* @param startName the starting name to search
* @return a symbol record iterator over the symbols
* @throws IOException if a database io error occurs
*/
abstract RecordIterator scanSymbolsByName(String startName) throws IOException;
/**
* Get symbol records which match the specified external original import name.
* @param extLabel external import name label
* @return matching external symbol records (forward iteration only, delete not supported)
* @throws IOException if a database io error occurs
*/
abstract RecordIterator getExternalSymbolsByOriginalImportName(String extLabel)
throws IOException;
/**
* Get symbol records which match the specified external original import name.
* @param extProgAddr external program address
* @return matching external symbol records (forward iteration only, delete not supported)
* @throws IOException if a database io error occurs
*/
abstract RecordIterator getExternalSymbolsByMemoryAddress(Address extProgAddr)
throws IOException;
/**
* Get all symbols contained in the given {@link Namespace} that have the given name
* @param name the symbol name
@ -439,7 +474,8 @@ abstract class SymbolDatabaseAdapter {
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 namespaceQuery =
new FieldMatchQuery(SYMBOL_PARENT_ID_COL, new LongField(namespaceId));
Query nameAndNamespaceQuery = new AndQuery(nameQuery, namespaceQuery);
return new QueryRecordIterator(it, nameAndNamespaceQuery);
}
@ -457,7 +493,8 @@ abstract class SymbolDatabaseAdapter {
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 namespaceQuery =
new FieldMatchQuery(SYMBOL_PARENT_ID_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);

View file

@ -21,6 +21,7 @@ import java.util.Set;
import db.*;
import ghidra.program.database.map.AddressIndexPrimaryKeyIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.database.util.EmptyRecordIterator;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.*;
@ -105,7 +106,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
return symbolTable.getKey();
}
private DBRecord convertRecord(DBRecord record) {
private DBRecord convertV0Record(DBRecord record) {
if (record == null) {
return null;
}
@ -118,32 +119,34 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
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,
addressKey);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, addressKey);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, SymbolType.LABEL.getID());
long namespaceId = Namespace.GLOBAL_NAMESPACE_ID;
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL, namespaceId);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL,
(byte) SourceType.USER_DEFINED.ordinal());
//
// Convert sparse columns
//
Field hash = computeLocatorHash(symbolName, namespaceId, addressKey);
rec.setField(SymbolDatabaseAdapter.SYMBOL_HASH_COL, hash);
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());
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,
String stringData, Long dataTypeId, Integer varOffset, SourceType source,
boolean isPrimary) throws IOException {
DBRecord createSymbolRecord(String name, long namespaceID, Address address,
SymbolType symbolType, boolean isPrimary, SourceType source) {
throw new UnsupportedOperationException();
}
@ -164,7 +167,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
@Override
DBRecord getSymbolRecord(long symbolID) throws IOException {
return convertRecord(symbolTable.getRecord(symbolID));
return convertV0Record(symbolTable.getRecord(symbolID));
}
@Override
@ -210,8 +213,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
}
@Override
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException {
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) throws IOException {
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
@ -240,8 +242,17 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
}
@Override
RecordIterator getSymbolsByNamespace(long id) throws IOException {
RecordIterator getExternalSymbolsByMemoryAddress(Address extProgAddr) throws IOException {
return EmptyRecordIterator.INSTANCE; // External symbols were not supported
}
@Override
RecordIterator getExternalSymbolsByOriginalImportName(String extLabel) throws IOException {
return EmptyRecordIterator.INSTANCE; // External symbols were not supported
}
@Override
RecordIterator getSymbolsByNamespace(long id) throws IOException {
if (id == Namespace.GLOBAL_NAMESPACE_ID) {
return new V0ConvertedRecordIterator(symbolTable.iterator());
}
@ -299,7 +310,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
if (hasNext()) {
DBRecord r = rec;
rec = null;
return convertRecord(r);
return convertV0Record(r);
}
return null;
}
@ -337,8 +348,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
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);
RecordIterator filtered = getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
if (filtered.hasNext()) {
return filtered.next();
}

View file

@ -21,6 +21,7 @@ import java.util.Set;
import db.*;
import ghidra.program.database.map.AddressIndexPrimaryKeyIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.database.util.EmptyRecordIterator;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException;
@ -46,8 +47,8 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
private static final int V1_SYMBOL_ADDR_COL = 1;
private static final int V1_SYMBOL_PARENT_COL = 2;
private static final int V1_SYMBOL_TYPE_COL = 3;
private static final int V1_SYMBOL_DATA1_COL = 4;
private static final int V1_SYMBOL_DATA2_COL = 5;
private static final int V1_SYMBOL_DATA1_COL = 4; // Long data (variable dataTypeId)
private static final int V1_SYMBOL_DATA2_COL = 5; // Int data (primary flag, variable-offset)
private static final int V1_SYMBOL_COMMENT_COL = 6;
private Table symbolTable;
@ -70,9 +71,8 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
}
@Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType,
String stringData, Long dataTypeId, Integer varOffset, SourceType source,
boolean isPrimary) throws IOException {
DBRecord createSymbolRecord(String name, long namespaceID, Address address,
SymbolType symbolType, boolean isPrimary, SourceType source) {
throw new UnsupportedOperationException();
}
@ -121,14 +121,11 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, symbolAddrKey);
long namespaceId = record.getLongValue(V1_SYMBOL_PARENT_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, namespaceId);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_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 (symbolTypeId == SymbolType.FUNCTION.getID()) {
Address symbolAddress = addrMap.decodeAddress(symbolAddrKey);
@ -139,6 +136,13 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
}
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, (byte) source.ordinal());
//
// Convert sparse columns
//
SymbolDatabaseAdapterV3.convertSymbolStringData(symbolTypeId, rec,
record.getString(V1_SYMBOL_COMMENT_COL));
long dataTypeId = record.getLongValue(V1_SYMBOL_DATA1_COL);
if (dataTypeId != -1) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL, dataTypeId);
@ -161,7 +165,9 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
// 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);
@ -211,8 +217,7 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
}
@Override
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException {
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) throws IOException {
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
@ -240,6 +245,16 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
throw new UnsupportedOperationException();
}
@Override
RecordIterator getExternalSymbolsByMemoryAddress(Address extProgAddr) throws IOException {
return EmptyRecordIterator.INSTANCE; // External symbols were not supported
}
@Override
RecordIterator getExternalSymbolsByOriginalImportName(String extLabel) throws IOException {
return EmptyRecordIterator.INSTANCE; // External symbols were not supported
}
@Override
RecordIterator getSymbolsByNamespace(long id) throws IOException {
LongField field = new LongField(id);
@ -293,8 +308,7 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
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);
RecordIterator filtered = getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
if (filtered.hasNext()) {
return filtered.next();
}

View file

@ -16,12 +16,11 @@
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.RecordFilter;
import ghidra.program.database.util.EmptyRecordIterator;
import ghidra.program.model.address.*;
import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolType;
@ -43,20 +42,20 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
// "SymbolData3", "Flags" });
private static final int SYMBOL_VERSION = 2;
private static final int V2_SYMBOL_NAME_COL = 0;
private static final int V2_SYMBOL_ADDR_COL = 1;
private static final int V2_SYMBOL_PARENT_ID_COL = 2;
private static final int V2_SYMBOL_TYPE_COL = 3;
private static final int V2_SYMBOL_DATA1_COL = 4; // Long data (variable dataTypeId)
private static final int V2_SYMBOL_DATA2_COL = 5; // Int data (primary flag, variable-offset)
private static final int V2_SYMBOL_DATA3_COL = 6; // String data (external address)
private static final int V2_SYMBOL_FLAGS_COL = 7;
private Table symbolTable;
private AddressMap addrMap;
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 {
SymbolDatabaseAdapterV2(DBHandle handle, AddressMap addrMap) throws VersionException {
this.addrMap = addrMap;
symbolTable = handle.getTable(SYMBOL_TABLE_NAME);
@ -73,9 +72,8 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
}
@Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType,
String stringData, Long dataTypeId, Integer varOffset, SourceType source,
boolean isPrimary) throws IOException {
DBRecord createSymbolRecord(String name, long namespaceID, Address address,
SymbolType symbolType, boolean isPrimary, SourceType source) {
throw new UnsupportedOperationException();
}
@ -154,8 +152,7 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
}
@Override
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException {
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) throws IOException {
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
@ -174,51 +171,58 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
@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);
}
throw new UnsupportedOperationException();
}
@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();
throw new UnsupportedOperationException();
}
class AnchoredSymbolRecordFilter implements RecordFilter {
private Set<Address> set = new HashSet<Address>();
private String getExternalStringData(DBRecord rec) {
long addrKey = rec.getLongValue(V2_SYMBOL_ADDR_COL);
Address addr = addrMap.decodeAddress(addrKey);
if (addr == null || !addr.isExternalAddress()) {
return null;
}
byte symbolTypeId = rec.getByteValue(V2_SYMBOL_TYPE_COL);
if (symbolTypeId != SYMBOL_TYPE_FUNCTION && symbolTypeId != SYMBOL_TYPE_LABEL) {
// NOTE: I don't think external functions were supported with this version
return null;
}
@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);
if (((flags & SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG) == 0)) {
return true;
return rec.getString(V2_SYMBOL_DATA3_COL);
}
@Override
RecordIterator getExternalSymbolsByMemoryAddress(Address extProgAddr) throws IOException {
if (extProgAddr == null) {
return EmptyRecordIterator.INSTANCE;
}
String matchAddrStr = extProgAddr.toString();
return new ConstrainedForwardRecordIterator(symbolTable.iterator(), rec -> {
String str = getExternalStringData(rec);
if (str != null) {
int indexOf = str.indexOf(","); // [address][,importName]
String addressString = indexOf >= 0 ? str.substring(0, indexOf) : str;
if (matchAddrStr.equals(addressString)) {
return convertV2Record(rec);
}
}
set.add(addr);
return false;
}
return null;
});
}
Set<Address> getAddressesForSkippedRecords() {
return set;
}
@Override
RecordIterator getExternalSymbolsByOriginalImportName(String extLabel) throws IOException {
return EmptyRecordIterator.INSTANCE;
}
@Override
RecordIterator getSymbolsByNamespace(long id) throws IOException {
LongField field = new LongField(id);
RecordIterator it = symbolTable.indexIterator(SYMBOL_PARENT_COL, field, field, true);
RecordIterator it = symbolTable.indexIterator(SYMBOL_PARENT_ID_COL, field, field, true);
return new V2ConvertedRecordIterator(it);
}
@ -236,6 +240,24 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
return new V2ConvertedRecordIterator(it);
}
@Override
RecordIterator getSymbolsByNameAndNamespace(String name, long id) throws IOException {
RecordIterator it = scanSymbolsByName(name);
RecordIterator filtered = getNameAndNamespaceFilterIterator(name, id, it);
return new V2ConvertedRecordIterator(filtered);
}
@Override
DBRecord getSymbolRecord(Address address, String name, long id) throws IOException {
RecordIterator it = scanSymbolsByName(name);
long addressKey = addrMap.getKey(address, false);
RecordIterator filtered = getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
if (filtered.hasNext()) {
return filtered.next();
}
return null;
}
@Override
Address getMaxSymbolAddress(AddressSpace space) throws IOException {
if (space.isMemorySpace()) {
@ -265,27 +287,6 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
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.
@ -303,21 +304,25 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
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);
long namespaceId = record.getLongValue(V2_SYMBOL_PARENT_ID_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL, namespaceId);
byte symbolTypeId = record.getByteValue(V2_SYMBOL_TYPE_COL);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, symbolTypeId);
rec.setString(SymbolDatabaseAdapter.SYMBOL_STRING_DATA_COL,
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL,
record.getByteValue(V2_SYMBOL_FLAGS_COL));
//
// Convert sparse columns
//
SymbolDatabaseAdapterV3.convertSymbolStringData(symbolTypeId, rec,
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);
@ -325,7 +330,7 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
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
// The data2 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)) {

View file

@ -16,13 +16,13 @@
package ghidra.program.database.symbol;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
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;
@ -33,9 +33,10 @@ import ghidra.util.task.TaskMonitor;
/**
* SymbolDatabaseAdapter for version 3
*
* This version provides for fast symbol lookup by namespace and name.
* This version provides for fast symbol lookup by namespace and name and introduced the use of
* sparse table columns for storing optional symbol data (hash, primary, datatype-ID, variable-offset).
* It was created in June 2021 with ProgramDB version 24.
* It will be included in Ghidra starting at version 10.1
* It was first in affect within Ghidra starting at version 10.1
*/
class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
@ -49,95 +50,58 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
// 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 });
/* Do not remove the following commented out schema! It shows the version 3 symbol table schema. */
// 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 static final int V3_SYMBOL_NAME_COL = 0;
private static final int V3_SYMBOL_ADDR_COL = 1;
private static final int V3_SYMBOL_PARENT_ID_COL = 2;
private static final int V3_SYMBOL_TYPE_COL = 3;
private static final int V3_SYMBOL_STRING_DATA_COL = 4; // removed with V4; External [address][,importName]
private static final int V3_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.
private static final int V3_SYMBOL_HASH_COL = 6;
private static final int V3_SYMBOL_PRIMARY_COL = 7;
private static final int V3_SYMBOL_DATATYPE_COL = 8; // External and variable symbol use
private static final int V3_SYMBOL_VAROFFSET_COL = 9;
private Table symbolTable;
private AddressMap addrMap;
SymbolDatabaseAdapterV3(DBHandle handle, AddressMap addrMap, boolean create)
throws VersionException, IOException {
SymbolDatabaseAdapterV3(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, SYMBOL_HASH_COL,
SYMBOL_PRIMARY_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);
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;
DBRecord createSymbolRecord(String name, long namespaceID, Address address,
SymbolType symbolType, boolean isPrimary, SourceType source) {
throw new UnsupportedOperationException();
}
@Override
void removeSymbol(long symbolID) throws IOException {
symbolTable.deleteRecord(symbolID);
throw new UnsupportedOperationException();
}
@Override
@ -160,7 +124,7 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
@Override
DBRecord getSymbolRecord(long symbolID) throws IOException {
return symbolTable.getRecord(symbolID);
return convertV3Record(symbolTable.getRecord(symbolID));
}
@Override
@ -170,49 +134,52 @@ class SymbolDatabaseAdapterV3 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 V3ConvertedRecordIterator(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 V3ConvertedRecordIterator(it);
}
@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);
throw new UnsupportedOperationException();
}
@Override
RecordIterator getSymbols() throws IOException {
return symbolTable.iterator();
return new V3ConvertedRecordIterator(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 V3ConvertedRecordIterator(it);
}
@Override
RecordIterator getSymbols(AddressSetView set, boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
return new V3ConvertedRecordIterator(it);
}
@Override
protected RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_PRIMARY_COL, addrMap, set, forward));
KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_PRIMARY_COL, addrMap, set, forward));
return new V3ConvertedRecordIterator(it);
}
@Override
@ -220,54 +187,92 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
AddressIndexPrimaryKeyIterator it = new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_PRIMARY_COL, addrMap, address, address, true);
if (it.hasNext()) {
return symbolTable.getRecord(it.next());
return convertV3Record(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);
}
throw new UnsupportedOperationException();
}
@Override
Set<Address> deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
throws CancelledException, IOException {
throw new UnsupportedOperationException();
}
AnchoredSymbolRecordFilter filter = new AnchoredSymbolRecordFilter();
AddressRecordDeleter.deleteRecords(symbolTable, SYMBOL_ADDR_COL, addrMap, startAddr,
endAddr, filter);
private String getExternalStringData(DBRecord rec) {
long addrKey = rec.getLongValue(V3_SYMBOL_ADDR_COL);
Address addr = addrMap.decodeAddress(addrKey);
if (addr == null || !addr.isExternalAddress()) {
return null;
}
byte symbolTypeId = rec.getByteValue(V3_SYMBOL_TYPE_COL);
if (symbolTypeId != SYMBOL_TYPE_FUNCTION && symbolTypeId != SYMBOL_TYPE_LABEL) {
return null;
}
return filter.getAddressesForSkippedRecords();
return rec.getString(V3_SYMBOL_STRING_DATA_COL);
}
@Override
RecordIterator getExternalSymbolsByMemoryAddress(Address extProgAddr) throws IOException {
if (extProgAddr == null) {
return EmptyRecordIterator.INSTANCE;
}
String matchAddrStr = extProgAddr.toString();
return new ConstrainedForwardRecordIterator(symbolTable.iterator(), rec -> {
String str = getExternalStringData(rec);
if (str != null) {
int indexOf = str.indexOf(","); // [address][,importName]
String addressString = indexOf >= 0 ? str.substring(0, indexOf) : str;
if (matchAddrStr.equals(addressString)) {
return convertV3Record(rec);
}
}
return null;
});
}
@Override
RecordIterator getExternalSymbolsByOriginalImportName(String extLabel) throws IOException {
if (StringUtils.isBlank(extLabel)) {
return EmptyRecordIterator.INSTANCE;
}
return new ConstrainedForwardRecordIterator(symbolTable.iterator(), rec -> {
String str = getExternalStringData(rec);
if (str != null) {
int indexOf = str.indexOf(","); // [address][,importName]
String originalImportedName = indexOf >= 0 ? str.substring(indexOf + 1) : null;
if (extLabel.equals(originalImportedName)) {
return convertV3Record(rec);
}
}
return null;
});
}
@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_ID_COL, field, field, true);
return new V3ConvertedRecordIterator(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 V3ConvertedRecordIterator(it);
}
@Override
RecordIterator scanSymbolsByName(String startName) throws IOException {
StringField field = new StringField(startName);
return symbolTable.indexIterator(SYMBOL_NAME_COL, field, null, true);
RecordIterator it = symbolTable.indexIterator(SYMBOL_NAME_COL, field, null, true);
return new V3ConvertedRecordIterator(it);
}
@Override
@ -282,7 +287,10 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
Field end = computeLocatorHash(name, id, MAX_ADDRESS_OFFSET);
RecordIterator it = symbolTable.indexIterator(SYMBOL_HASH_COL, start, end, true);
return getNameAndNamespaceFilterIterator(name, id, it);
it = new V3ConvertedRecordIterator(it);
RecordIterator filtered = getNameAndNamespaceFilterIterator(name, id, it);
return new V3ConvertedRecordIterator(filtered);
}
@Override
@ -293,6 +301,8 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
return null;
}
RecordIterator it = symbolTable.indexIterator(SYMBOL_HASH_COL, search, search, true);
it = new V3ConvertedRecordIterator(it);
RecordIterator filtered =
getNameNamespaceAddressFilterIterator(name, namespaceId, addressKey, it);
if (filtered.hasNext()) {
@ -330,24 +340,96 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
return symbolTable;
}
private class AnchoredSymbolRecordFilter implements RecordFilter {
private Set<Address> set = new HashSet<Address>();
/**
* 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 convertV3Record(DBRecord record) {
if (record == null) {
return null;
}
DBRecord rec = SymbolDatabaseAdapter.SYMBOL_SCHEMA.createRecord(record.getKey());
@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;
String symbolName = record.getString(V3_SYMBOL_NAME_COL);
rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, symbolName);
long symbolAddrKey = record.getLongValue(V3_SYMBOL_ADDR_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, symbolAddrKey);
long namespaceId = record.getLongValue(V3_SYMBOL_PARENT_ID_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL, namespaceId);
byte symbolTypeId = record.getByteValue(V3_SYMBOL_TYPE_COL);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, symbolTypeId);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL,
record.getByteValue(V3_SYMBOL_FLAGS_COL));
//
// Convert sparse columns
//
convertSymbolStringData(symbolTypeId, rec, record.getString(V3_SYMBOL_STRING_DATA_COL));
Field hash = record.getFieldValue(V3_SYMBOL_HASH_COL);
if (hash != null) {
rec.setField(SymbolDatabaseAdapter.SYMBOL_HASH_COL, hash);
}
Set<Address> getAddressesForSkippedRecords() {
return set;
Field primaryAddr = record.getFieldValue(V3_SYMBOL_PRIMARY_COL);
if (primaryAddr != null) {
rec.setField(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, primaryAddr);
}
Field dataTypeId = record.getFieldValue(V3_SYMBOL_DATATYPE_COL);
if (dataTypeId != null) {
rec.setField(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL, dataTypeId);
}
Field varOffset = record.getFieldValue(V3_SYMBOL_VAROFFSET_COL);
if (varOffset != null) {
rec.setField(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL, varOffset);
}
return rec;
}
static void convertSymbolStringData(byte symbolTypeId, DBRecord record, String str) {
// Adhoc String field use/format
// External location (label or function): "[<addressStr>][,<originalImportedName>]"
// Library: [externalLibraryPath]
// Variables: [comment]
if (StringUtils.isBlank(str)) {
return;
}
if (symbolTypeId == SYMBOL_TYPE_LABEL || symbolTypeId == SYMBOL_TYPE_FUNCTION) {
int indexOf = str.indexOf(",");
String originalImportedName = indexOf >= 0 ? str.substring(indexOf + 1) : null;
String addressString = indexOf >= 0 ? str.substring(0, indexOf) : str;
record.setString(SYMBOL_EXTERNAL_PROG_ADDR_COL, addressString);
record.setString(SYMBOL_ORIGINAL_IMPORTED_NAME_COL, originalImportedName);
}
else if (symbolTypeId == SYMBOL_TYPE_LOCAL_VAR || symbolTypeId == SYMBOL_TYPE_PARAMETER) {
record.setString(SYMBOL_COMMENT_COL, str);
}
else if (symbolTypeId == SYMBOL_TYPE_LIBRARY) {
record.setString(SYMBOL_LIBPATH_COL, str);
}
}
private class V3ConvertedRecordIterator extends ConvertedRecordIterator {
V3ConvertedRecordIterator(RecordIterator originalIterator) {
super(originalIterator, false);
}
@Override
protected DBRecord convertRecord(DBRecord record) {
return convertV3Record(record);
}
}

View file

@ -0,0 +1,364 @@
/* ###
* 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 added additional sparse columns to store optional data specific to certain
* symbol types. The adhoc string data column was eliminated.
*/
class SymbolDatabaseAdapterV4 extends SymbolDatabaseAdapter {
static final int SYMBOL_VERSION = 4;
// 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 V4_SYMBOL_SCHEMA = new Schema(SYMBOL_VERSION, "Key",
new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE,
ByteField.INSTANCE, ByteField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE,
LongField.INSTANCE, IntField.INSTANCE, StringField.INSTANCE, StringField.INSTANCE,
StringField.INSTANCE, StringField.INSTANCE },
new String[] { "Name", "Address", "Namespace", "Symbol Type", "Flags", "Locator Hash",
"Primary", "Datatype", "Variable Offset", "ExtOrigImportName", "ExtProgAddr", "Comment",
"LibPath" },
new int[] { SYMBOL_HASH_COL, SYMBOL_PRIMARY_COL, SYMBOL_DATATYPE_COL, SYMBOL_VAROFFSET_COL,
SYMBOL_ORIGINAL_IMPORTED_NAME_COL, SYMBOL_EXTERNAL_PROG_ADDR_COL, SYMBOL_COMMENT_COL,
SYMBOL_LIBPATH_COL });
private Table symbolTable;
private AddressMap addrMap;
SymbolDatabaseAdapterV4(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_ID_COL, SYMBOL_HASH_COL,
SYMBOL_PRIMARY_COL, SYMBOL_ORIGINAL_IMPORTED_NAME_COL,
SYMBOL_EXTERNAL_PROG_ADDR_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,
// 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, (byte) source.ordinal(),
// isPrimary);
// }
@Override
DBRecord createSymbolRecord(String name, long namespaceID, Address address,
SymbolType symbolType, boolean isPrimary, SourceType source) {
long nextID = symbolTable.getKey();
// Avoid key 0, as it is reserved for the global namespace
if (nextID == 0) {
nextID++;
}
long addressKey = addrMap.getKey(address, true);
DBRecord rec = symbolTable.getSchema().createRecord(nextID);
rec.setString(SYMBOL_NAME_COL, name);
rec.setLongValue(SYMBOL_ADDR_COL, addressKey);
rec.setLongValue(SYMBOL_PARENT_ID_COL, namespaceID);
rec.setByteValue(SYMBOL_TYPE_COL, symbolType.getID());
rec.setByteValue(SYMBOL_FLAGS_COL, (byte) source.ordinal());
// 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);
}
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_ID_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_ID_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 scanSymbolsByName(String startName) throws IOException {
StringField field = new StringField(startName);
return symbolTable.indexIterator(SYMBOL_NAME_COL, field, null, true);
}
@Override
RecordIterator getExternalSymbolsByOriginalImportName(String extLabel) throws IOException {
StringField extLabelField = new StringField(extLabel);
return symbolTable.indexIterator(SYMBOL_ORIGINAL_IMPORTED_NAME_COL, extLabelField,
extLabelField, true);
}
@Override
RecordIterator getExternalSymbolsByMemoryAddress(Address extProgAddr) throws IOException {
StringField addrField = new StringField(extProgAddr.toString());
return symbolTable.indexIterator(SYMBOL_EXTERNAL_PROG_ADDR_COL, addrField, addrField, 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;
}
}
}

View file

@ -303,7 +303,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
}
catch (InvalidInputException e) {
Symbol parent =
getSymbol(rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL));
getSymbol(rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL));
Msg.warn(this, "Variable symbol upgrade problem: " + parent.getName() + ":" +
rec.getString(SymbolDatabaseAdapter.SYMBOL_NAME_COL));
}
@ -356,7 +356,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
}
catch (InvalidInputException e) {
Symbol parent =
getSymbol(rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL));
getSymbol(rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL));
Msg.warn(this, "Variable symbol upgrade problem: " + parent.getName() +
":" + rec.getString(SymbolDatabaseAdapter.SYMBOL_NAME_COL));
curVarAddr = null;
@ -527,8 +527,11 @@ public class SymbolManager implements SymbolTable, ManagerDB {
Address address = symbol.getAddress();
symbolRemoved(symbol, address, symbol.getName(), oldKey, Namespace.GLOBAL_NAMESPACE_ID,
null);
DBRecord record = adapter.createSymbol(newName, address, newParentID, SymbolType.LABEL,
null, null, null, source, true);
DBRecord record = adapter.createSymbolRecord(newName, newParentID, address,
SymbolType.LABEL, true, source);
adapter.updateSymbolRecord(record);
symbol.setRecord(record);
symbolAdded(symbol);
}
@ -571,7 +574,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, name);
long addressKey = addrMap.getKey(addr, true);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, addressKey);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, namespace.getID());
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL, namespace.getID());
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, type.getID());
if (isPrimary) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, addressKey);
@ -587,19 +590,19 @@ public class SymbolManager implements SymbolTable, ManagerDB {
addrMap.decodeAddress(record.getLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL));
}
if (type == SymbolType.CLASS) {
return new ClassSymbol(this, cache, addr, record);
return new ClassSymbol(this, cache, record); // uses NO_ADDRESS
}
else if (type == SymbolType.LABEL) {
return new CodeSymbol(this, cache, addr, record);
return new CodeSymbol(this, cache, addr, record); // uses NO_ADDRESS
}
else if (type == SymbolType.NAMESPACE) {
return new NamespaceSymbol(this, cache, addr, record);
return new NamespaceSymbol(this, cache, record);
}
else if (type == SymbolType.FUNCTION) {
return new FunctionSymbol(this, cache, addr, record);
}
else if (type == SymbolType.LIBRARY) {
return new LibrarySymbol(this, cache, addr, record);
return new LibrarySymbol(this, cache, record); // uses NO_ADDRESS
}
else if (type == SymbolType.PARAMETER || type == SymbolType.LOCAL_VAR) {
return new VariableSymbolDB(this, cache, type, variableStorageMgr, addr, record);
@ -970,12 +973,12 @@ public class SymbolManager implements SymbolTable, ManagerDB {
}
@Override
public Symbol getLibrarySymbol(String name) {
public LibrarySymbol getLibrarySymbol(String name) {
lock.acquire();
try {
for (Symbol s : getSymbols(name)) {
if (s.getSymbolType() == SymbolType.LIBRARY) {
return s;
return (LibrarySymbol) s;
}
}
}
@ -1111,6 +1114,83 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return null;
}
/**
* Get all external symbols whose original import name matches the specified extLabel with an
* optional constraint on a matching Library.
* @param library optional Library constraint (may be null to ignore).
* @param extLabel external original import name
* @return external symbol iterator
*/
public SymbolIterator getExternalSymbolByOriginalImportName(Library library, String extLabel) {
if (library != null) {
checkValidNamespaceArgument(library);
}
lock.acquire();
try {
long matchLibraryId = library != null ? library.getID() : -1;
RecordIterator recordIter = adapter.getExternalSymbolsByOriginalImportName(extLabel);
return new SymbolRecordConstraintIterator(recordIter, rec -> {
if (matchLibraryId > 0) {
long parentId = rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL);
if (parentId == matchLibraryId) {
return true;
}
Symbol s = getSymbol(parentId);
Library lib = Library.getContainingLibrary(s);
return lib != null && lib.getID() == matchLibraryId;
}
return true;
}, true);
}
catch (IOException e) {
program.dbError(e);
return null; // will not occur
}
finally {
lock.release();
}
}
/**
* Get all external symbols whose original import name matches the specified extLabel with an
* optional constraint on a matching Library.
* @param library optional Library constraint (may be null to ignore).
* @param extProgAddr external program memory address
* @return external symbol iterator
*/
public SymbolIterator getExternalSymbolByMemoryAddress(Library library, Address extProgAddr) {
if (extProgAddr == null || !extProgAddr.isLoadedMemoryAddress()) {
return SymbolIterator.EMPTY_ITERATOR;
}
if (library != null) {
checkValidNamespaceArgument(library);
}
lock.acquire();
try {
long matchLibraryId = library != null ? library.getID() : -1;
RecordIterator recordIter = adapter.getExternalSymbolsByMemoryAddress(extProgAddr);
return new SymbolRecordConstraintIterator(recordIter, rec -> {
if (matchLibraryId > 0) {
long parentId = rec.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL);
if (parentId == matchLibraryId) {
return true;
}
Symbol s = getSymbol(parentId);
Library lib = Library.getContainingLibrary(s);
return lib != null && lib.getID() == matchLibraryId;
}
return true;
}, true);
}
catch (IOException e) {
program.dbError(e);
return null; // will not occur
}
finally {
lock.release();
}
}
@Override
public Namespace getNamespace(String name, Namespace namespace) {
List<Symbol> symbols = getSymbols(name, namespace);
@ -1804,8 +1884,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return true;
}
lock.acquire();
try {
lock.acquire();
while (nextSymbol == null && (forward ? it.hasNext() : it.hasPrevious())) {
DBRecord rec = forward ? it.next() : it.previous();
Symbol sym = getSymbol(rec);
@ -1845,6 +1925,66 @@ public class SymbolManager implements SymbolTable, ManagerDB {
}
}
private class SymbolRecordConstraintIterator implements SymbolIterator {
private Symbol nextSymbol;
private final RecordIterator it;
private final Predicate<DBRecord> predicate;
private final boolean forward;
SymbolRecordConstraintIterator(RecordIterator it, Predicate<DBRecord> predicate,
boolean forward) {
this.it = it;
this.predicate = predicate;
this.forward = forward;
}
@Override
public boolean hasNext() {
if (nextSymbol != null) {
return true;
}
lock.acquire();
try {
while (nextSymbol == null && (forward ? it.hasNext() : it.hasPrevious())) {
DBRecord rec = forward ? it.next() : it.previous();
if (predicate == null || predicate.test(rec)) {
nextSymbol = getSymbol(rec);
}
}
return nextSymbol != null;
}
catch (IOException e) {
program.dbError(e);
}
finally {
lock.release();
}
return false;
}
@Override
public Symbol next() {
if (hasNext()) {
Symbol returnedSymbol = nextSymbol;
nextSymbol = null;
return returnedSymbol;
}
return null;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public Iterator<Symbol> iterator() {
return this;
}
}
private class SymbolQueryIterator implements SymbolIterator {
private SymbolIterator it;
private Symbol nextMatch;
@ -2204,6 +2344,45 @@ public class SymbolManager implements SymbolTable, ManagerDB {
dynamicSymbolAddressMap = new AddressMapImpl((byte) 0x40, addrMap.getAddressFactory());
invalidateCache(true);
variableStorageMgr.setLanguage(translator, monitor);
monitor.setMessage("Translate External Addresses...");
try {
AddressFactory oldAddrFactory = translator.getOldLanguage().getAddressFactory();
SymbolIterator externalSymbols = getExternalSymbols();
while (externalSymbols.hasNext()) {
monitor.checkCancelled();
SymbolDB s = (SymbolDB) externalSymbols.next();
String extAddrStr =
s.record.getString(SymbolDatabaseAdapter.SYMBOL_EXTERNAL_PROG_ADDR_COL);
if (extAddrStr == null) {
continue;
}
// skip addresses which do not parse by old language - could be
// overlay (although this should generally never occur)
Address addr = oldAddrFactory.getAddress(extAddrStr);
if (addr == null) {
continue;
}
AddressSpace newAddressSpace =
translator.getNewAddressSpace(addr.getAddressSpace().getName());
if (newAddressSpace == null || !newAddressSpace.isLoadedMemorySpace()) {
// can't really recover from this
throw new AssertException(
"Failed to map external memory address: " + extAddrStr);
}
addr = newAddressSpace.getAddress(addr.getOffset());
String newAddrStr = addr.toString();
if (!newAddrStr.equals(extAddrStr)) {
s.record.setString(SymbolDatabaseAdapter.SYMBOL_EXTERNAL_PROG_ADDR_COL,
newAddrStr);
adapter.updateSymbolRecord(s.record);
}
}
}
catch (IOException e) {
program.dbError(e);
}
}
public void replaceDataTypes(Map<Long, Long> dataTypeReplacementMap) {
@ -2255,6 +2434,10 @@ public class SymbolManager implements SymbolTable, ManagerDB {
@Override
public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
throws CancelledException {
if (!fromAddr.isMemoryAddress() || !toAddr.isMemoryAddress()) {
throw new IllegalArgumentException(
"moveAddressRange only supported for memory addresses");
}
if (fromAddr.equals(toAddr)) {
return;
}
@ -2274,14 +2457,18 @@ public class SymbolManager implements SymbolTable, ManagerDB {
if (!range.contains(symbol.getAddress())) {
break;
}
Address newAddress = toAddr.add(symbol.getAddress().subtract(fromAddr));
if (!(symbol instanceof MemorySymbol memSym)) {
throw new AssertionError(
"Unexpected symbol type within memory range: " + symbol.getClass());
}
Address newAddress = toAddr.add(memSym.getAddress().subtract(fromAddr));
// any address that has symbols added or removed may have a corrupted primary
// (too many or non-existent)
primaryFixups.add(symbol.getAddress());
primaryFixups.add(memSym.getAddress());
primaryFixups.add(newAddress);
moveSymbolForMemoryBlockMove((SymbolDB) symbol, newAddress);
moveSymbolForMemoryBlockMove(memSym, newAddress);
}
// go back and make sure there is a valid primary symbol at touched addresses
fixupPrimarySymbols(primaryFixups);
@ -2295,17 +2482,17 @@ public class SymbolManager implements SymbolTable, ManagerDB {
// Since this is a memory block move, the destination range can not contain any functions, but
// it may contain labels so we may have to deal with collisions. The other complication we have
// to deal with is functions in the moved block that are currently pinned.
private void moveSymbolForMemoryBlockMove(SymbolDB symbol, Address newAddress) {
Address oldAddress = symbol.getAddress();
private void moveSymbolForMemoryBlockMove(MemorySymbol memSymbol, Address newAddress) {
Address oldAddress = memSymbol.getAddress();
// If the symbol is not pinned go ahead and move it. Only wrinkle is that there may
// be a matching symbol at the destination. In that unlikely event, remove it, but preserve
// its pinned status
if (!symbol.isPinned()) {
if (!memSymbol.isPinned()) {
// if we conflict with a symbol at the destination, delete it (it can't be a function)
// and retain its pinned status if it had one.
boolean shouldPin = deleteMatchingSymbolAndCheckPinnedStatus(symbol, newAddress);
symbol.moveLowLevel(newAddress, null, null, null, shouldPin);
boolean shouldPin = deleteMatchingSymbolAndCheckPinnedStatus(memSymbol, newAddress);
memSymbol.moveLowLevel(newAddress, null, null, null, shouldPin);
moveLabelHistory(oldAddress, newAddress);
return;
}
@ -2314,10 +2501,10 @@ public class SymbolManager implements SymbolTable, ManagerDB {
// the function symbol, but create a replacement label in the pinned source location. Also,
// if there is a primary symbol at the destination, we need to remove it and set the
// function's symbol to that name.
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
String originalName = symbol.getName();
Namespace originalNamespace = symbol.getParentNamespace();
SourceType originalSource = symbol.getSource();
if (memSymbol.getSymbolType() == SymbolType.FUNCTION) {
String originalName = memSymbol.getName();
Namespace originalNamespace = memSymbol.getParentNamespace();
SourceType originalSource = memSymbol.getSource();
String newName = "";
Namespace newNamespace = namespaceMgr.getGlobalNamespace();
SourceType newSource = SourceType.DEFAULT;
@ -2335,7 +2522,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
}
try {
// so move the symbol to the new address and update namespace, source, and pinned state
symbol.moveLowLevel(newAddress, newName, newNamespace, newSource, newPinned);
memSymbol.moveLowLevel(newAddress, newName, newNamespace, newSource, newPinned);
// create a pinned label to replace the pinned function symbol at the source.
Symbol newSymbol =
@ -2444,45 +2631,48 @@ public class SymbolManager implements SymbolTable, ManagerDB {
}
public void imageBaseChanged(Address oldBase, Address newBase) {
if (!oldBase.isLoadedMemoryAddress() || !newBase.isLoadedMemoryAddress()) {
throw new IllegalArgumentException("Loaded memory addresses required");
}
AddressSpace space = newBase.getAddressSpace();
fixupPinnedSymbolsAfterRebase(oldBase, newBase, space.getMinAddress(),
fixupPinnedMemorySymbolsAfterRebase(oldBase, newBase, space.getMinAddress(),
space.getMaxAddress());
}
private void fixupPinnedSymbolsAfterRebase(Address oldBase, Address base, Address minAddr,
private void fixupPinnedMemorySymbolsAfterRebase(Address oldBase, Address base, Address minAddr,
Address maxAddr) {
List<SymbolDB> fixupPinnedSymbols = findPinnedSymbols(minAddr, maxAddr);
List<MemorySymbol> fixupPinnedSymbols = findPinnedMemorySymbols(minAddr, maxAddr);
Set<Address> primaryFixups = new HashSet<>();
for (SymbolDB symbol : fixupPinnedSymbols) {
Address currentAddress = symbol.getAddress();
for (MemorySymbol memSymbol : fixupPinnedSymbols) {
Address currentAddress = memSymbol.getAddress();
Address beforeBaseChangeAddress = oldBase.addWrap(currentAddress.subtract(base));
primaryFixups.add(currentAddress);
primaryFixups.add(beforeBaseChangeAddress);
// see if there is a name collision for the pinned symbol we are about to move back
Symbol match =
getSymbol(symbol.getName(), beforeBaseChangeAddress, symbol.getParentNamespace());
Symbol match = getSymbol(memSymbol.getName(), beforeBaseChangeAddress,
memSymbol.getParentNamespace());
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
fixupPinnedFunctionSymbolAfterRebase(symbol, beforeBaseChangeAddress, match);
if (memSymbol.getSymbolType() == SymbolType.FUNCTION) {
fixupPinnedFunctionSymbolAfterRebase(memSymbol, beforeBaseChangeAddress, match);
}
else {
fixupPinnedLabelSymbolAfterRebase(symbol, beforeBaseChangeAddress, match);
fixupPinnedLabelSymbolAfterRebase(memSymbol, beforeBaseChangeAddress, match);
}
}
fixupPrimarySymbols(primaryFixups);
}
private void fixupPinnedLabelSymbolAfterRebase(SymbolDB symbol, Address newAddress,
private void fixupPinnedLabelSymbolAfterRebase(MemorySymbol memSymbol, Address newAddress,
Symbol match) {
if (match != null) {
match.setPinned(true);
symbol.delete();
memSymbol.delete();
}
else {
symbol.moveLowLevel(newAddress, null, null, null, true);
memSymbol.moveLowLevel(newAddress, null, null, null, true);
}
}
@ -2526,14 +2716,18 @@ public class SymbolManager implements SymbolTable, ManagerDB {
}
}
private List<SymbolDB> findPinnedSymbols(Address minAddr, Address maxAddr) {
List<SymbolDB> pinnedSymbols = new ArrayList<>();
private List<MemorySymbol> findPinnedMemorySymbols(Address minAddr, Address maxAddr) {
List<MemorySymbol> pinnedSymbols = new ArrayList<>();
for (Symbol symbol : getSymbolIterator(minAddr, true)) {
if (symbol.getAddress().compareTo(maxAddr) > 0) {
if (!(symbol instanceof MemorySymbol memSym)) {
throw new AssertionError(
"Unexpected symbol type within memory range: " + symbol.getClass());
}
if (memSym.getAddress().compareTo(maxAddr) > 0) {
break;
}
if (symbol.isPinned()) {
pinnedSymbols.add((SymbolDB) symbol);
if (memSym.isPinned()) {
pinnedSymbols.add(memSym);
}
}
return pinnedSymbols;
@ -2589,16 +2783,24 @@ public class SymbolManager implements SymbolTable, ManagerDB {
try {
source = adjustSourceTypeIfNecessary(name, type, source, storage);
Address varAddr = variableStorageMgr.getVariableStorageAddress(storage, true);
return (VariableSymbolDB) doCreateSpecialSymbol(varAddr, name, function, type, null,
Integer.valueOf(firstUseOffsetOrOrdinal), null, source, true);
DBRecord record =
doCreateBasicSymbolRecord(name, function, varAddr, type, false, source, true);
VariableSymbolDB.setRecordFields(record, firstUseOffsetOrOrdinal, null);
adapter.updateSymbolRecord(record);
VariableSymbolDB newSymbol =
new VariableSymbolDB(this, cache, type, variableStorageMgr, varAddr, record);
symbolAdded(newSymbol);
return newSymbol;
}
catch (IOException e) {
dbError(e);
program.dbError(e); // will not return
return null;
}
finally {
lock.release();
}
return null;
}
private SourceType adjustSourceTypeIfNecessary(String name, SymbolType type, SourceType source,
@ -2616,21 +2818,21 @@ public class SymbolManager implements SymbolTable, ManagerDB {
@Override
public GhidraClass createClass(Namespace parent, String name, SourceType source)
throws DuplicateNameException, InvalidInputException {
SymbolDB s = createClassSymbol(name, parent, source, true);
ClassSymbol s = createClassSymbol(name, parent, source, true);
return new GhidraClassDB(s, namespaceMgr);
}
@Override
public Library createExternalLibrary(String name, SourceType source)
throws DuplicateNameException, InvalidInputException {
SymbolDB s = createLibrarySymbol(name, null, source);
LibrarySymbol s = createLibrarySymbol(name, null, source);
return new LibraryDB(s, namespaceMgr);
}
@Override
public Namespace createNameSpace(Namespace parent, String name, SourceType source)
throws DuplicateNameException, InvalidInputException {
SymbolDB s = createNamespaceSymbol(name, parent, source, true);
NamespaceSymbol s = createNamespaceSymbol(name, parent, source, true);
return new NamespaceDB(s, namespaceMgr);
}
@ -2657,7 +2859,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
// no duplicate check, since this class name will be set to that of the existing namespace
String tempName = "_temp_" + System.nanoTime();
SymbolDB classSymbol = createClassSymbol(tempName, namespace.getParentNamespace(),
ClassSymbol classSymbol = createClassSymbol(tempName, namespace.getParentNamespace(),
originalSource, false /*check for duplicate */);
GhidraClassDB classNamespace = new GhidraClassDB(classSymbol, namespaceMgr);
@ -2705,7 +2907,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
// Note: We know there are no namespaces with the name; do we still have to check for
// duplicates? Assuming yes, as another symbol type may exist with this name.
SymbolDB s = createNamespaceSymbol(name, parent, source, true /*check for duplicates*/);
NamespaceSymbol s =
createNamespaceSymbol(name, parent, source, true /*check for duplicates*/);
return new NamespaceDB(s, namespaceMgr);
}
@ -2728,10 +2931,27 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* or if the given parent namespace is from a different program than that of this
* symbol table.
*/
public SymbolDB createLibrarySymbol(String name, String pathname, SourceType source)
public LibrarySymbol createLibrarySymbol(String name, String pathname, SourceType source)
throws DuplicateNameException, InvalidInputException {
return doCreateSpecialSymbol(Address.NO_ADDRESS, name, null, SymbolType.LIBRARY, null, null,
pathname, source, true);
lock.acquire();
try {
DBRecord record = doCreateBasicSymbolRecord(name, null, Address.NO_ADDRESS,
SymbolType.LIBRARY, false, source, true);
LibrarySymbol.setRecordFields(record, pathname);
adapter.updateSymbolRecord(record);
LibrarySymbol newSymbol = new LibrarySymbol(this, cache, record);
symbolAdded(newSymbol);
return newSymbol;
}
catch (IOException e) {
program.dbError(e); // will not return
return null;
}
finally {
lock.release();
}
}
/**
@ -2749,10 +2969,26 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* or if the given parent namespace is from a different program than that of this
* symbol table.
*/
SymbolDB createClassSymbol(String name, Namespace parent, SourceType source,
ClassSymbol createClassSymbol(String name, Namespace parent, SourceType source,
boolean checkForDuplicates) throws DuplicateNameException, InvalidInputException {
return doCreateSpecialSymbol(Address.NO_ADDRESS, name, parent, SymbolType.CLASS, null, null,
null, source, true);
lock.acquire();
try {
DBRecord record = doCreateBasicSymbolRecord(name, parent, Address.NO_ADDRESS,
SymbolType.CLASS, false, source, checkForDuplicates);
adapter.updateSymbolRecord(record);
ClassSymbol newSymbol = new ClassSymbol(this, cache, record);
symbolAdded(newSymbol);
return newSymbol;
}
catch (IOException e) {
program.dbError(e); // will not return
return null;
}
finally {
lock.release();
}
}
/**
@ -2770,161 +3006,228 @@ public class SymbolManager implements SymbolTable, ManagerDB {
* if the given parent namespace is from a different program than that of this
* symbol table.
*/
SymbolDB createNamespaceSymbol(String name, Namespace parent, SourceType source,
NamespaceSymbol createNamespaceSymbol(String name, Namespace parent, SourceType source,
boolean checkForDuplicates) throws DuplicateNameException, InvalidInputException {
return doCreateSpecialSymbol(Address.NO_ADDRESS, name, parent, SymbolType.NAMESPACE, null,
null, null, source, true);
lock.acquire();
try {
DBRecord record = doCreateBasicSymbolRecord(name, parent, Address.NO_ADDRESS,
SymbolType.NAMESPACE, false, source, checkForDuplicates);
adapter.updateSymbolRecord(record);
NamespaceSymbol newSymbol = new NamespaceSymbol(this, cache, record);
symbolAdded(newSymbol);
return newSymbol;
}
catch (IOException e) {
program.dbError(e); // will not return
return null;
}
finally {
lock.release();
}
}
private SymbolDB doCreateSpecialSymbol(Address addr, String name, Namespace parent,
SymbolType symbolType, Long dataTypeId, Integer variableOffset, String stringData,
SourceType source, boolean checkForDuplicates)
/**
* Create a basic symbol record with required fields. Caller is responsible for updating
* any related optional record fields and then adding to the DB table via the
* {@link SymbolDatabaseAdapter#updateSymbolRecord(DBRecord)} method.
* @param name symbol name
* @param parent symbol parent namespace (glabel namespace assumed if null)
* @param address symbol address
* @param symbolType symbol type
* @param isPrimary if true, symbol record will be tagged as primary (relavent for label and
* function symbols only).
* @param source symbol source type
* @param checkForDuplicates if true a duplicate name check will be performed within the
* specified parent namespace.
* @return return new record
* @throws DuplicateNameException if checkForDuplicates is true and name conflicts with
* another symbol.
* @throws InvalidInputException if name contains invalid characters.
* See {@link SymbolUtilities#validateName(String)}.
*/
private DBRecord doCreateBasicSymbolRecord(String name, Namespace parent, Address address,
SymbolType symbolType, boolean isPrimary, SourceType source, boolean checkForDuplicates)
throws DuplicateNameException, InvalidInputException {
lock.acquire();
try {
parent = validateNamespace(parent, addr, symbolType);
source = validateSource(source, name, addr, symbolType);
name = validateName(name, source);
if (checkForDuplicates) {
checkDuplicateSymbolName(addr, name, parent, symbolType);
// Only one symbol per external address is allowed
if (address.isExternalAddress()) {
Symbol primary = getPrimarySymbol(address);
if (primary != null) {
throw new IllegalArgumentException("external address already used");
}
return doCreateSymbol(name, addr, parent, symbolType, stringData, dataTypeId,
variableOffset, source, false);
}
finally {
lock.release();
}
}
@Override
public Symbol createLabel(Address addr, String name, SourceType source)
throws InvalidInputException {
return createLabel(addr, name, null, source);
}
parent = validateNamespace(parent, address, symbolType);
source = validateSource(source, name, address, symbolType);
name = validateName(name, source);
@Override
public Symbol createLabel(Address addr, String name, Namespace namespace, SourceType source)
throws InvalidInputException {
if (!addr.isMemoryAddress()) {
throw new IllegalArgumentException("Invalid memory address: " + addr);
if (checkForDuplicates) {
checkDuplicateSymbolName(address, name, parent, symbolType);
}
return createCodeSymbol(addr, name, namespace, source, null);
return adapter.createSymbolRecord(name, parent.getID(), address, symbolType, isPrimary,
source);
}
/**
* Internal method for creating label symbols.
* Internal method for creating external data/code symbol.
* <p>
* If identical memory symbol already exists it will be returned.
*
* @param addr the address for the new symbol (memory or external)
* @param name the name of the new symbol
* @param namespace the namespace for the new symbol (null may be specified for global
* namespace)
* @param source the SourceType of the new symbol
* @param stringData special use depending on the symbol type and whether or not it is external
* @return the new symbol
* @throws InvalidInputException if name contains white space, is zero length, or is null for
* non-default source. Also thrown if invalid parent namespace is specified.
* @throws IllegalArgumentException if {@link SourceType#DEFAULT} is improperly specified, or
* an invalid address, or if the given parent namespace is from a different
* program than that of this symbol table.
*/
public Symbol createCodeSymbol(Address addr, String name, Namespace namespace,
SourceType source, String stringData) throws InvalidInputException {
lock.acquire();
try {
namespace = validateNamespace(namespace, addr, SymbolType.LABEL);
source = validateSource(source, name, addr, SymbolType.LABEL);
name = validateName(name, source);
boolean makePrimary = true;
if (addr.isMemoryAddress()) {
Symbol symbol = getSymbol(name, addr, namespace);
if (symbol != null) {
return symbol;
}
// If there is a default named function, rename it to the new symbol name
Symbol functionSymbol = tryUpdatingDefaultFunction(addr, name, namespace, source);
if (functionSymbol != null) {
return functionSymbol;
}
// if there is a dynamic symbol, delete it and make the new symbol primary.
Symbol primary = getPrimarySymbol(addr);
if (primary != null && primary.isDynamic()) {
deleteDynamicSymbol(primary);
primary = null;
}
makePrimary = (primary == null);
}
else if (addr.isExternalAddress()) {
// TODO: remove support for external symbol creation from this method (see GP-3045)
Symbol primary = getPrimarySymbol(addr);
if (primary != null) { // only one symbol per external address is allowed
throw new IllegalArgumentException("external address already used");
}
}
else {
throw new IllegalArgumentException("Invalid memory address: " + addr);
}
return doCreateSymbol(name, addr, namespace, SymbolType.LABEL, stringData, null, null,
source, makePrimary);
}
finally {
lock.release();
}
}
/**
* Internal method for creating function symbols
* NOTE: Any name conflict concerns must be checked before invoking this method.
*
* @param addr the address for the new symbol
* @param name the name of the new symbol
* @param namespace the namespace for the new symbol (null may be specified for global
* namespace)
* @param source the SourceType of the new symbol
* @param stringData special use depending on the symbol type and whether or not it is external.
* @param externalProgramAddress associated external program memory address (may be null)
* @param originalImportName original imported name (i.e., mangled name)
* @return the new symbol
* @throws IOException if IO error occurs
* @throws InvalidInputException if the name contains illegal characters (i.e. space)
*/
public Symbol createFunctionSymbol(Address addr, String name, Namespace namespace,
SourceType source, String stringData) throws InvalidInputException {
public CodeSymbol createExternalCodeSymbol(Address addr, String name, Namespace namespace,
SourceType source, String originalImportName, Address externalProgramAddress)
throws IOException, InvalidInputException {
if (!addr.isExternalAddress()) {
throw new IllegalArgumentException("External address required");
}
if (externalProgramAddress != null && !externalProgramAddress.isLoadedMemoryAddress()) {
throw new IllegalArgumentException("Memory address required for external program");
}
try {
DBRecord record = doCreateBasicSymbolRecord(name, namespace, addr, SymbolType.LABEL,
true, source, false);
MemorySymbol.setExternalFields(record, originalImportName, externalProgramAddress);
adapter.updateSymbolRecord(record);
CodeSymbol newSymbol = new CodeSymbol(this, cache, addr, record);
symbolAdded(newSymbol);
return newSymbol;
}
catch (DuplicateNameException e) {
throw new RuntimeException("Unexpected", e); // duplicate was avoided by caller
}
}
/**
* Internal method for creating external function symbol.
* <p>
* NOTE: Any name conflict concerns must be checked before invoking this method.
*
* @param addr the address for the new symbol
* @param name the name of the new symbol
* @param namespace the namespace for the new symbol (null may be specified for global
* namespace)
* @param source the SourceType of the new symbol
* @param externalProgramAddress associated external program memory address (may be null)
* @param originalImportName original imported name (i.e., mangled name)
* @return the new symbol
* @throws IOException if IO error occurs
* @throws InvalidInputException if the name contains illegal characters (i.e. space)
*/
public FunctionSymbol createExternalFunctionSymbol(Address addr, String name,
Namespace namespace, SourceType source, String originalImportName,
Address externalProgramAddress) throws IOException, InvalidInputException {
if (!addr.isExternalAddress()) {
throw new IllegalArgumentException("External address required");
}
if (externalProgramAddress != null && !externalProgramAddress.isLoadedMemoryAddress()) {
throw new IllegalArgumentException("Memory address required for external program");
}
try {
DBRecord record = doCreateBasicSymbolRecord(name, namespace, addr, SymbolType.FUNCTION,
true, source, false);
MemorySymbol.setExternalFields(record, originalImportName, externalProgramAddress);
adapter.updateSymbolRecord(record);
FunctionSymbol newSymbol = new FunctionSymbol(this, cache, addr, record);
symbolAdded(newSymbol);
return newSymbol;
}
catch (DuplicateNameException e) {
throw new RuntimeException("Unexpected", e); // duplicate was avoided by caller
}
}
@Override
public Symbol createLabel(Address addr, String name, SourceType source)
throws IllegalArgumentException, InvalidInputException {
return createLabel(addr, name, null, source);
}
@Override
public Symbol createLabel(Address addr, String name, Namespace namespace, SourceType source)
throws IllegalArgumentException, InvalidInputException {
if (!addr.isMemoryAddress()) {
throw new IllegalArgumentException("Invalid memory address: " + addr);
}
lock.acquire();
try {
namespace = validateNamespace(namespace, addr, SymbolType.LABEL);
source = validateSource(source, name, addr, SymbolType.LABEL);
name = validateName(name, source);
Symbol symbol = getSymbol(name, addr, namespace);
if (symbol != null) {
return symbol;
}
// If there is a default named function, rename it to the new symbol name
Symbol functionSymbol = tryUpdatingDefaultFunction(addr, name, namespace, source);
if (functionSymbol != null) {
return functionSymbol;
}
// if there is a dynamic symbol, delete it and make the new symbol primary.
Symbol primary = getPrimarySymbol(addr);
if (primary != null && primary.isDynamic()) {
deleteDynamicSymbol(primary);
primary = null;
}
boolean makePrimary = (primary == null);
DBRecord record = doCreateBasicSymbolRecord(name, namespace, addr, SymbolType.LABEL,
makePrimary, source, false);
adapter.updateSymbolRecord(record);
CodeSymbol newSymbol = new CodeSymbol(this, cache, addr, record);
symbolAdded(newSymbol);
return newSymbol;
}
catch (DuplicateNameException e) {
throw new RuntimeException("Unexpected", e); // duplicate was avoided above
}
catch (IOException e) {
program.dbError(e);
return null; // will not occur
}
finally {
lock.release();
}
}
public FunctionSymbol createMemoryFunctionSymbol(Address addr, String name, Namespace namespace,
SourceType source) throws IOException, InvalidInputException {
if (!addr.isMemoryAddress()) {
throw new IllegalArgumentException("memory address required");
}
namespace = validateNamespace(namespace, addr, SymbolType.FUNCTION);
source = validateSource(source, name, addr, SymbolType.FUNCTION);
name = validateName(name, source);
if (addr.isMemoryAddress()) {
return doCreateMemoryFunctionSymbol(addr, name, namespace, source, stringData);
}
else if (addr.isExternalAddress()) {
// only one symbol per external address is allowed
Symbol primary = getPrimarySymbol(addr);
if (primary != null) {
throw new IllegalArgumentException("external address already used");
}
}
else {
throw new IllegalArgumentException("bad function address");
}
return doCreateSymbol(name, addr, namespace, SymbolType.FUNCTION, stringData, null, null,
source, true);
}
private Symbol doCreateMemoryFunctionSymbol(Address addr, String name, Namespace namespace,
SourceType source, String stringData) throws InvalidInputException {
// if there is already a FUNCTION symbol with that name and namespace here, just return it.
Symbol matching = getSymbol(name, addr, namespace);
if (matching != null && matching.getSymbolType() == SymbolType.FUNCTION) {
return matching;
if (matching instanceof FunctionSymbol funcSymbol) {
return funcSymbol;
}
// if there is another function at the same address, throw InvalidInputException
@ -2947,14 +3250,25 @@ public class SymbolManager implements SymbolTable, ManagerDB {
// delete any promoted symbol, dynamic symbol, and make sure all others are not primary
cleanUpSymbols(addr, symbolToPromote, primary);
Symbol symbol = doCreateSymbol(name, addr, namespace, SymbolType.FUNCTION, stringData, null,
null, source, true);
if (needsPinning) {
symbol.setPinned(true);
DBRecord record;
try {
record = doCreateBasicSymbolRecord(name, namespace, addr, SymbolType.FUNCTION, true,
source, false);
adapter.updateSymbolRecord(record);
}
catch (DuplicateNameException e) {
throw new RuntimeException("Unexpected", e); // duplicate was avoided above
}
return symbol;
FunctionSymbol newSymbol = new FunctionSymbol(this, cache, addr, record);
symbolAdded(newSymbol);
if (needsPinning) {
newSymbol.setPinned(true);
}
return newSymbol;
}
/**
@ -3011,24 +3325,6 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return namespace;
}
private SymbolDB doCreateSymbol(String name, Address addr, Namespace namespace, SymbolType type,
String stringData, Long dataTypeId, Integer variableOffset, SourceType source,
boolean isPrimary) {
try {
DBRecord record = adapter.createSymbol(name, addr, namespace.getID(), type, stringData,
dataTypeId, variableOffset, source, isPrimary);
SymbolDB newSymbol = makeSymbol(addr, record, type);
symbolAdded(newSymbol);
return newSymbol;
}
catch (IOException e) {
program.dbError(e);
}
return null;
}
private String validateName(String name, SourceType source) throws InvalidInputException {
if (source == SourceType.DEFAULT) {
return "";
@ -3171,9 +3467,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
void checkValidNamespaceArgument(Namespace namespace) throws IllegalArgumentException {
if (!isMyNamespace(namespace)) {
String kind = (namespace instanceof Function) ? "function" : "namespace";
throw new IllegalArgumentException(
kind + " is from different program instance: " + namespace);
throw new IllegalArgumentException(namespace.getType().friendlyName() +
" is from a different program instance: " + namespace);
}
}

View file

@ -138,7 +138,7 @@ public class VariableSymbolDB extends SymbolDB {
}
@Override
public Object getObject() {
public Variable getObject() {
FunctionDB func = getFunction();
if (func != null) {
return func.getVariable(this);
@ -159,13 +159,12 @@ public class VariableSymbolDB extends SymbolDB {
public FunctionDB getFunction() {
return (FunctionDB) symbolMgr.getFunctionManager()
.getFunction(
getParentNamespace().getID());
.getFunction(getParentNamespace().getID());
}
@Override
public ProgramLocation getProgramLocation() {
Variable var = (Variable) getObject();
Variable var = getObject();
if (var != null) {
return new VariableNameFieldLocation(var.getProgram(), var, 0);
}
@ -311,20 +310,90 @@ public class VariableSymbolDB extends SymbolDB {
try {
checkIsValid();
ReferenceManager rm = symbolMgr.getReferenceManager();
return rm.getReferencesTo((Variable) getObject());
return rm.getReferencesTo(getObject());
}
finally {
lock.release();
}
}
@Override
public boolean hasMultipleReferences() {
return getReferences(null).length > 1;
}
@Override
public boolean hasReferences() {
return getReferences(null).length != 0;
}
/**
* gets the generic symbol data 2 data.
* @return the symbol data
*/
protected int getVariableOffset() {
lock.acquire();
try {
checkIsValid();
if (record != null) {
return record.getIntValue(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL);
}
return 0;
}
finally {
lock.release();
}
}
/**
* 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 setVariableOffset(int offset) {
lock.acquire();
try {
checkDeleted();
if (record != null) {
record.setIntValue(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL, offset);
updateRecord();
symbolMgr.symbolDataChanged(this);
}
}
finally {
lock.release();
}
}
/**
* {@return variable symbol comment}
*/
public String getSymbolComment() {
validate(lock);
return record.getString(SymbolDatabaseAdapter.SYMBOL_COMMENT_COL);
}
/**
* Update variable symbol comment (no change event is issued)
* @param comment variable comment
*/
public void setSymbolComment(String comment) {
lock.acquire();
try {
checkDeleted();
record.setString(SymbolDatabaseAdapter.SYMBOL_COMMENT_COL, comment);
updateRecord();
}
finally {
lock.release();
}
}
/**
* Update variable symbol record fields
* @param record variable symbol record
* @param firstUseOffsetOrOrdinal first use offset or param ordinal (ignored if null)
* @param comment variable comment
*/
static void setRecordFields(DBRecord record, Integer firstUseOffsetOrOrdinal, String comment) {
if (firstUseOffsetOrOrdinal != null) {
record.setIntValue(SymbolDatabaseAdapter.SYMBOL_VAROFFSET_COL, firstUseOffsetOrOrdinal);
}
record.setString(SymbolDatabaseAdapter.SYMBOL_COMMENT_COL, comment);
}
}

View file

@ -115,21 +115,11 @@ public class GlobalSymbol implements Symbol {
return 0;
}
@Override
public boolean hasMultipleReferences() {
return false;
}
@Override
public boolean hasReferences() {
return false;
}
@Override
public Reference[] getReferences() {
return new Reference[0];
}
@Override
public Reference[] getReferences(TaskMonitor monitor) {
return new Reference[0];

View file

@ -81,6 +81,11 @@ public interface Function extends Namespace {
public final static int UNKNOWN_STACK_DEPTH_CHANGE = Integer.MAX_VALUE;
public final static int INVALID_STACK_DEPTH_CHANGE = Integer.MAX_VALUE - 1;
@Override
default Type getType() {
return Type.FUNCTION;
}
/**
* Get the name of this function.
*

View file

@ -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.
@ -18,10 +17,13 @@ package ghidra.program.model.listing;
import ghidra.program.model.symbol.Namespace;
/**
* Interface for representing class objects in the program.
*/
public interface GhidraClass extends Namespace {
@Override
public default Type getType() {
return Type.CLASS;
}
}

View file

@ -15,7 +15,7 @@
*/
package ghidra.program.model.listing;
import ghidra.program.model.symbol.Namespace;
import ghidra.program.model.symbol.*;
/**
* Interface for a Library namespace.
@ -24,9 +24,39 @@ public interface Library extends Namespace {
public static final String UNKNOWN = "<EXTERNAL>";
@Override
public default Type getType() {
return Type.LIBRARY;
}
/**
* @return the associated program within the project which corresponds to this library
*/
public String getAssociatedProgramPath();
/**
* Get the Library which contains the specified external symbol.
* @param symbol external symbol
* @return null if symbol is null or not external
*/
public static Library getContainingLibrary(Symbol symbol) {
if (symbol == null) {
return null;
}
if (symbol.getSymbolType() == SymbolType.LIBRARY) {
return (Library) symbol.getObject();
}
if (symbol.getSymbolType() == SymbolType.NAMESPACE ||
symbol.getSymbolType() == SymbolType.CLASS) {
while (symbol != null && symbol.isExternal()) {
Namespace n = (Namespace) symbol.getObject();
if (n instanceof Library lib) {
return lib;
}
symbol = n.getParentNamespace().getSymbol();
}
}
return null;
}
}

View file

@ -15,7 +15,7 @@
*/
package ghidra.program.model.symbol;
import java.util.List;
import java.util.Set;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
@ -74,15 +74,15 @@ public interface ExternalManager {
* @param oldName the old name of the external library name.
* @param newName the new name of the external library name.
* @param source the source of this external library
* @throws DuplicateNameException if another namespace has the same name
* @throws InvalidInputException on invalid input
* @throws DuplicateNameException if name conflicts with another symbol.
* @throws InvalidInputException if an invalid or null name specified (see
* {@link SymbolUtilities#validateName}).
*/
public void updateExternalLibraryName(String oldName, String newName, SourceType source)
throws DuplicateNameException, InvalidInputException;
/**
* Get an iterator over all external locations associated with the specified
* externalName.
* Get an iterator over all external locations associated with the specified Library.
* @param libraryName the name of the library to get locations for
* @return external location iterator
*/
@ -97,54 +97,46 @@ public interface ExternalManager {
public ExternalLocationIterator getExternalLocations(Address memoryAddress);
/**
* Get an external location.
* @param libraryName the name of the library for which to get an external location
* @param label the name of the external location.
* @return first matching external location
* @deprecated Use {@link #getExternalLocations(String, String)} or
* {@link #getUniqueExternalLocation(String, String)} since duplicate names may exist
*/
@Deprecated
public ExternalLocation getExternalLocation(String libraryName, String label);
/**
* Get an external location.
* @param namespace the namespace containing the external label.
* @param label the name of the external location.
* @return first matching external location
* @deprecated Use {@link #getExternalLocations(Namespace, String)} or
* {@link #getUniqueExternalLocation(Namespace, String)} since duplicate names may exist
*/
@Deprecated
public ExternalLocation getExternalLocation(Namespace namespace, String label);
/**
* Returns a list of External Locations matching the given label name in the given Library.
* Returns a set of External Locations matching the given label name in the specified Library.
* If searching for an original import name which should not be constrained to a specific
* library (e.g., mangled name), null may be specified for the libraryName.
*
* @param libraryName the name of the library
* @param label the name of the label
* @return a list of External Locations matching the given label name in the given Library.
*/
public List<ExternalLocation> getExternalLocations(String libraryName, String label);
public Set<ExternalLocation> getExternalLocations(String libraryName, String label);
/**
* Returns a list of External Locations matching the given label name in the given Namespace.
* @param namespace the Namespace to search
* Returns a set of External Locations matching the given label name in the given Namespace.
* If searching for an original import name which should not be constrained to a specific
* library (e.g., mangled name), null may be specified for the namespace. If a library
* sub-namespace is specified the original import name will not be searched.
*
* @param namespace the external Namespace to search or null
* @param label the name of the labels to search for.
* @return a list of External Locations matching the given label name in the given Namespace.
*/
public List<ExternalLocation> getExternalLocations(Namespace namespace, String label);
public Set<ExternalLocation> getExternalLocations(Namespace namespace, String label);
/**
* Returns the unique external location associated with the given library name and label
* @param libraryName the library name
* Returns the unique external location associated with the given library name and label.
* If searching for an original import name which should not be constrained to a specific
* library (e.g., mangled name), null may be specified for the libraryName.
*
* @param libraryName the library name or null
* @param label the label of the external location
* @return the unique external location or null
*/
public ExternalLocation getUniqueExternalLocation(String libraryName, String label);
/**
* Returns the unique external location associated with the given namespace and label
* @param namespace the namespace
* Returns the unique external location associated with the given namespace and label.
* If searching for an original import name which should not be constrained to a specific
* library (e.g., mangled name), null may be specified for the namespace. If a library
* sub-namespace is specified the original import name will not be searched.
*
* @param namespace the namespace or null
* @param label the label of the external location
* @return the unique external location or null
*/
@ -229,8 +221,7 @@ public interface ExternalManager {
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/
public ExternalLocation addExtLocation(Namespace extNamespace, String extLabel, Address extAddr,
SourceType sourceType, boolean reuseExisting)
throws InvalidInputException;
SourceType sourceType, boolean reuseExisting) throws InvalidInputException;
/**
* Create an external {@link Function} in the external {@link Library} namespace
@ -285,7 +276,6 @@ public interface ExternalManager {
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/
public ExternalLocation addExtFunction(Namespace extNamespace, String extLabel, Address extAddr,
SourceType sourceType, boolean reuseExisting)
throws InvalidInputException;
SourceType sourceType, boolean reuseExisting) throws InvalidInputException;
}

View file

@ -27,6 +27,26 @@ import ghidra.util.exception.InvalidInputException;
*/
public interface Namespace {
/**
* Type of {@link Namespace}.
*/
public enum Type {
NAMESPACE("Namespace"), LIBRARY("Library"), CLASS("Class"), FUNCTION("Function");
private final String friendlyName;
private Type(String friendlyName) {
this.friendlyName = friendlyName;
}
/**
* {@return a friendly name for use in messages}
*/
public String friendlyName() {
return friendlyName;
}
}
static final long GLOBAL_NAMESPACE_ID = 0;
/**
* The delimiter that is used to separate namespace nodes in a namespace
@ -47,6 +67,13 @@ public interface Namespace {
*/
public Symbol getSymbol();
/**
* {@return the type of namespace, e.g., Library, Class, Namespace, Function}
*/
public default Type getType() {
return Type.NAMESPACE;
}
/**
* Returns true if this namespace is external (i.e., associated with a Library)
* @return true if this namespace is external (i.e., associated with a Library)

View file

@ -93,19 +93,26 @@ public interface Symbol {
public SymbolType getSymbolType();
/**
* @return the number of References to this symbol.
* Get the number of References to this symbol or its address.
* <P>
* NOTE: this method differ from {@link #hasReferences()} behavior for memory symbols since this
* method will return {@link ReferenceManager#getReferenceCountTo(Address)} if this is the only
* symbol at its address.
*
* @return the number of References to this symbol or its address.
*/
public int getReferenceCount();
/**
* @return true if this symbol has more than one reference to it.
*/
public boolean hasMultipleReferences();
public default int getReferenceCount() {
return 0;
}
/**
* @return true if this symbol has at least one reference to it.
* Explicit references to other symbols at the same address are not considered
* (see {@link Reference#getSymbolID()} which indicates a specific symbol reference).
*/
public boolean hasReferences();
public default boolean hasReferences() {
return false;
}
/**
* Returns all memory references to the address of this symbol. If you do not have a
@ -117,7 +124,9 @@ public interface Symbol {
* @param monitor the monitor that is used to report progress and to cancel this
* potentially long-running call
*/
public Reference[] getReferences(TaskMonitor monitor);
public default Reference[] getReferences(TaskMonitor monitor) {
return new Reference[0];
}
/**
* Returns all memory references to the address of this symbol.
@ -125,7 +134,9 @@ public interface Symbol {
* @return all memory references to the address of this symbol
* @see #getReferences(TaskMonitor)
*/
public Reference[] getReferences();
public default Reference[] getReferences() {
return getReferences(TaskMonitor.DUMMY);
}
/**
* Returns a program location for this symbol; may be null. This allows implementations to
@ -145,10 +156,9 @@ public interface Symbol {
* @param source the source of this symbol
* <br>Some symbol types, such as function symbols, can set the source to Symbol.DEFAULT.
*
* @throws DuplicateNameException
* if name already exists as the name of another symbol or alias.
* @throws InvalidInputException
* if alias contains blank characters, is zero length, or is null
* @throws DuplicateNameException if name conflicts with another symbol.
* @throws InvalidInputException if an invalid or null name specified (see
* {@link SymbolUtilities#validateName}).
* @throws IllegalArgumentException if you try to set the source to DEFAULT for a symbol type
* that doesn't allow it.
*/
@ -199,7 +209,9 @@ public interface Symbol {
*
* @return true if the symbol is pinned to its current address.
*/
public boolean isPinned();
public default boolean isPinned() {
return false; //most symbols can't be pinned.
}
/**
* <p>Sets whether or not this symbol is pinned to its associated address.</p>
@ -215,7 +227,9 @@ public interface Symbol {
* @param pinned true indicates this symbol is anchored to its address.
* false indicates this symbol is not anchored to its address.
*/
public void setPinned(boolean pinned);
public default void setPinned(boolean pinned) {
throw new UnsupportedOperationException("Only Code and Function Symbols may be pinned.");
}
/**
* @return true if this symbol is a dynamic symbol (not actually defined in the database).
@ -246,7 +260,9 @@ public interface Symbol {
* @return true if the symbol is at an address
* set as a external entry point.
*/
public boolean isExternalEntryPoint();
public default boolean isExternalEntryPoint() {
return false;
}
/**
* @return this symbol's ID.
@ -259,7 +275,7 @@ public interface Symbol {
public Object getObject();
/**
* @return true if the symbol is global
* @return true if the symbol is contained within the global namespace
*/
public boolean isGlobal();

View file

@ -86,7 +86,7 @@ public interface SymbolTable {
* returned without changing the source type. If this is the first non-dynamic symbol defined
* for the address it becomes the primary symbol.
*
* @param addr the address at which to create a symbol
* @param addr the memory address at which to create a symbol
* @param name the name of the symbol
* @param namespace the parent namespace of the symbol, or null for the global namespace.
* @param source the source of this symbol. In general, a source of {@link SourceType#DEFAULT}

View file

@ -23,7 +23,6 @@ import ghidra.program.model.listing.CircularDependencyException;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
// Simple symbol test implementation
public class StubSymbol implements Symbol {
@ -106,31 +105,6 @@ public class StubSymbol implements Symbol {
return SymbolType.LABEL;
}
@Override
public int getReferenceCount() {
return 0;
}
@Override
public boolean hasMultipleReferences() {
return false;
}
@Override
public boolean hasReferences() {
return false;
}
@Override
public Reference[] getReferences(TaskMonitor monitor) {
return null;
}
@Override
public Reference[] getReferences() {
return null;
}
@Override
public void setName(String newName, SourceType source)
throws DuplicateNameException, InvalidInputException {
@ -154,16 +128,6 @@ public class StubSymbol implements Symbol {
return false;
}
@Override
public boolean isPinned() {
return false;
}
@Override
public void setPinned(boolean pinned) {
// nothing
}
@Override
public boolean isDynamic() {
return false;
@ -184,11 +148,6 @@ public class StubSymbol implements Symbol {
return false;
}
@Override
public boolean isExternalEntryPoint() {
return false;
}
@Override
public long getID() {
return name.hashCode();