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(); return getReferenceCollection().size();
} }
@Override
public boolean hasMultipleReferences() {
// TODO: Could be slightly more efficient by just iterating twice?
return getReferenceCount() > 1;
}
@Override @Override
public boolean hasReferences() { public boolean hasReferences() {
return !getReferenceCollection().isEmpty(); return !getReferenceCollection().isEmpty();
@ -564,8 +558,4 @@ public abstract class AbstractDBTraceSymbol extends DBAnnotatedObject
return false; 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 * @deprecated function signatures should be modified in their entirety using
* either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}. * either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}.
*/ */
@Deprecated(since = "11.1") @Deprecated(forRemoval = true, since = "11.1")
public class AddMemoryParameterCommand extends AddParameterCommand { public class AddMemoryParameterCommand extends AddParameterCommand {
private final Address memAddr; 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 * @deprecated function signatures should be modified in their entirety using
* either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}. * either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}.
*/ */
@Deprecated(since = "11.1") @Deprecated(forRemoval = true, since = "11.1")
public class AddParameterCommand implements Command<Program> { public class AddParameterCommand implements Command<Program> {
protected final Function function; 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 * @deprecated function signatures should be modified in their entirety using
* either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}. * either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}.
*/ */
@Deprecated(since = "11.1") @Deprecated(forRemoval = true, since = "11.1")
public class AddRegisterParameterCommand extends AddParameterCommand { public class AddRegisterParameterCommand extends AddParameterCommand {
private final Register register; 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 * @deprecated function signatures should be modified in their entirety using
* either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}. * either {@link UpdateFunctionCommand} or {@link ApplyFunctionSignatureCmd}.
*/ */
@Deprecated(since = "11.1") @Deprecated(forRemoval = true, since = "11.1")
public class AddStackParameterCommand extends AddParameterCommand { public class AddStackParameterCommand extends AddParameterCommand {
private final int stackOffset; private final int stackOffset;

View file

@ -48,8 +48,6 @@ import ghidra.framework.options.OptionsChangeListener;
import ghidra.framework.options.ToolOptions; import ghidra.framework.options.ToolOptions;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.mem.AddressSourceInfo; 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.address.*;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory; import ghidra.program.model.mem.Memory;
@ -736,7 +734,11 @@ public class CodeBrowserClipboardProvider extends ByteCopier
SymbolTable symbolTable = currentProgram.getSymbolTable(); SymbolTable symbolTable = currentProgram.getSymbolTable();
Symbol symbol = symbolTable.getSymbol(reference); 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); RenameLabelCmd cmd = new RenameLabelCmd(symbol, labelName, SourceType.USER_DEFINED);
return tool.execute(cmd, currentProgram); return tool.execute(cmd, currentProgram);
} }

View file

@ -24,8 +24,7 @@ import ghidra.app.context.ListingActionContext;
import ghidra.app.context.ListingContextAction; import ghidra.app.context.ListingContextAction;
import ghidra.program.database.symbol.CodeSymbol; import ghidra.program.database.symbol.CodeSymbol;
import ghidra.program.database.symbol.FunctionSymbol; import ghidra.program.database.symbol.FunctionSymbol;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.*;
import ghidra.program.model.symbol.Symbol;
/** /**
* <CODE>RemoveLabelAction</CODE> allows the user to remove a label. * <CODE>RemoveLabelAction</CODE> allows the user to remove a label.
@ -63,8 +62,12 @@ class RemoveLabelAction extends ListingContextAction {
boolean isOnSymbol(ListingActionContext context) { boolean isOnSymbol(ListingActionContext context) {
Symbol s = plugin.getSymbol(context); Symbol s = plugin.getSymbol(context);
return ((s instanceof CodeSymbol) && !s.isDynamic()) || if (s == null) {
((s instanceof FunctionSymbol) && s.getSource() != SourceType.DEFAULT); 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.*;
import java.awt.event.ItemListener; import java.awt.event.ItemListener;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.Set;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.*; 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. * Pop up the data tree dialog so the user can choose the external program.
*/ */
private void popupProgramChooser() { private void popupProgramChooser() {
DataTreeDialog d = DataTreeDialog d = new DataTreeDialog(this.getParent(), "Choose External Program", OPEN);
new DataTreeDialog(this.getParent(), "Choose External Program", OPEN);
final DataTreeDialog dialog = d; final DataTreeDialog dialog = d;
d.addOkActionListener(e -> { d.addOkActionListener(e -> {
DomainFile df = dialog.getDomainFile(); DomainFile df = dialog.getDomainFile();
@ -402,10 +401,9 @@ class EditExternalLocationPanel extends JPanel {
String locationName = getLocationName(); String locationName = getLocationName();
if (locationName != null && locationName.length() > 0) { if (locationName != null && locationName.length() > 0) {
ExternalManager externalManager = program.getExternalManager(); ExternalManager externalManager = program.getExternalManager();
List<ExternalLocation> externalLocations = Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations(extLibName, locationName); externalManager.getExternalLocations(extLibName, locationName);
externalLocations.remove(externalLocation); if (externalLocations.size() == 1 && !externalLocations.contains(externalLocation)) {
if (!externalLocations.isEmpty()) {
int result = OptionDialog.showYesNoDialog(null, "Duplicate External Name", int result = OptionDialog.showYesNoDialog(null, "Duplicate External Name",
"Another symbol named '" + locationName + "' already exists in the '" + "Another symbol named '" + locationName + "' already exists in the '" +
extLibName + "' library. Are you sure you want to create another?"); extLibName + "' library. Are you sure you want to create another?");

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; package ghidra.app.plugin.core.symboltree.actions;
import docking.action.MenuData;
import ghidra.app.context.ProgramSymbolActionContext; import ghidra.app.context.ProgramSymbolActionContext;
import ghidra.app.context.ProgramSymbolContextAction; 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.listing.Program;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.model.symbol.SymbolType;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import docking.action.MenuData;
public class PinSymbolAction extends ProgramSymbolContextAction { public class PinSymbolAction extends ProgramSymbolContextAction {
public PinSymbolAction(String owner, String popupGroup) { public PinSymbolAction(String owner, String popupGroup) {
super("Pin Symbol", owner); super("Pin Symbol", owner);
setPopupMenuData(new MenuData(new String[] { "Set Pinned" }, popupGroup)); 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")); 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 @Override
protected void actionPerformed(ProgramSymbolActionContext context) { protected void actionPerformed(ProgramSymbolActionContext context) {
Program program = context.getProgram(); Program program = context.getProgram();
int transactionID = program.startTransaction("Pin Symbol(s)"); int transactionID = program.startTransaction("Pin Symbol(s)");
try { try {
for (Symbol symbol : context.getSymbols()) { for (Symbol symbol : context.getSymbols()) {
if ((symbol instanceof CodeSymbol || symbol instanceof FunctionSymbol) && if (canPinSymbol(symbol)) {
!symbol.isExternal() && !symbol.isPinned()) {
symbol.setPinned(true); symbol.setPinned(true);
} }
} }
@ -54,8 +58,7 @@ public class PinSymbolAction extends ProgramSymbolContextAction {
@Override @Override
protected boolean isEnabledForContext(ProgramSymbolActionContext context) { protected boolean isEnabledForContext(ProgramSymbolActionContext context) {
for (Symbol symbol : context.getSymbols()) { for (Symbol symbol : context.getSymbols()) {
if ((symbol instanceof CodeSymbol || symbol instanceof FunctionSymbol) && if (canPinSymbol(symbol)) {
!symbol.isExternal() && !symbol.isPinned()) {
return true; return true;
} }
} }

View file

@ -20,6 +20,7 @@ import static org.junit.Assert.*;
import java.awt.Component; import java.awt.Component;
import java.awt.Window; import java.awt.Window;
import java.util.List; import java.util.List;
import java.util.Set;
import javax.swing.JComponent; import javax.swing.JComponent;
import javax.swing.border.Border; import javax.swing.border.Border;
@ -926,4 +927,33 @@ public abstract class AbstractExternalMergerTest extends AbstractListingMergeMan
} }
return null; 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 static org.junit.Assert.*;
import java.util.List; import java.util.Set;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import ghidra.app.cmd.function.AddRegisterParameterCommand; import ghidra.app.cmd.function.AddRegisterParameterCommand;
import ghidra.program.database.*; import ghidra.program.database.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressSet; import ghidra.program.model.address.AddressSet;
import ghidra.program.model.data.*; import ghidra.program.model.data.*;
import ghidra.program.model.lang.Register; import ghidra.program.model.lang.Register;
@ -1604,35 +1603,18 @@ public class ExternalFunctionMergeManagerTest extends AbstractExternalMergerTest
waitForMergeCompletion(); waitForMergeCompletion();
ExternalManager externalManager = resultProgram.getExternalManager(); ExternalManager externalManager = resultProgram.getExternalManager();
List<ExternalLocation> externalLocations = Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations("user32.dll", "BETTY"); externalManager.getExternalLocations("user32.dll", "BETTY");
assertEquals(2, externalLocations.size()); assertEquals(2, externalLocations.size());
assertHasDifferentAddresses(externalLocations, "1002239", "10063b4"); assertHasExtAddresses(externalLocations, "1002239", "10063b4");
List<ExternalLocation> externalLocations2 = Set<ExternalLocation> externalLocations2 =
externalManager.getExternalLocations("user32.dll", "BARNEY"); externalManager.getExternalLocations("user32.dll", "BARNEY");
assertEquals(2, externalLocations2.size()); 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 @Test
public void testAddExternalFunctionsWithFunctionConflicts() throws Exception { public void testAddExternalFunctionsWithFunctionConflicts() throws Exception {
@ -1674,15 +1656,14 @@ public class ExternalFunctionMergeManagerTest extends AbstractExternalMergerTest
waitForMergeCompletion(); waitForMergeCompletion();
ExternalManager externalManager = resultProgram.getExternalManager(); ExternalManager externalManager = resultProgram.getExternalManager();
List<ExternalLocation> externalLocations = Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations("user32.dll", "BARNEY"); externalManager.getExternalLocations("user32.dll", "BARNEY");
assertEquals(2, externalLocations.size()); assertEquals(2, externalLocations.size());
assertHasDifferentAddresses(externalLocations, "1002239", "77db1020"); assertHasExtAddresses(externalLocations, "1002239", "77db1020");
externalLocations = externalManager.getExternalLocations("user32.dll", "BETTY"); externalLocations = externalManager.getExternalLocations("user32.dll", "BETTY");
assertEquals(1, externalLocations.size()); assertEquals(1, externalLocations.size());
assertEquals("01002239", externalLocations.get(0).getAddress().toString()); assertHasExtAddresses(externalLocations, "01002239");
} }
@Test @Test

View file

@ -18,6 +18,7 @@ package ghidra.app.merge.listing;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.List; import java.util.List;
import java.util.Set;
import org.junit.Test; import org.junit.Test;
@ -438,8 +439,11 @@ public class ExternalMergerAddTest extends AbstractExternalMergerTest {
ExternalManager externalManager = resultProgram.getExternalManager(); ExternalManager externalManager = resultProgram.getExternalManager();
assertTrue(externalManager.contains(libname)); assertTrue(externalManager.contains(libname));
List<ExternalLocation> externals = externalManager.getExternalLocations(libname, label); Set<ExternalLocation> externalLocations =
assertEquals(2, externals.size()); externalManager.getExternalLocations(libname, label);
assertEquals(2, externalLocations.size());
assertHasExtAddresses(externalLocations, address1, address2);
ExternalLocationIterator loc1It = externalManager.getExternalLocations(addr(address1)); ExternalLocationIterator loc1It = externalManager.getExternalLocations(addr(address1));
assertTrue(loc1It.hasNext()); assertTrue(loc1It.hasNext());
assertEquals(label, loc1It.next().getLabel()); assertEquals(label, loc1It.next().getLabel());
@ -3403,13 +3407,16 @@ public class ExternalMergerAddTest extends AbstractExternalMergerTest {
ExternalManager externalManager = resultProgram.getExternalManager(); ExternalManager externalManager = resultProgram.getExternalManager();
assertTrue(externalManager.contains(libname)); assertTrue(externalManager.contains(libname));
List<ExternalLocation> externalLocations = Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations(libname, label); externalManager.getExternalLocations(libname, label);
assertEquals(2, externalLocations.size()); assertEquals(2, externalLocations.size());
assertHasExtAddresses(externalLocations, "00000100", "00000110");
List<ExternalLocation> externalLocations2 = Set<ExternalLocation> externalLocations2 =
externalManager.getExternalLocations(BLUE_PATH[0], BLUE_PATH[1]); externalManager.getExternalLocations(BLUE_PATH[0], BLUE_PATH[1]);
assertEquals(2, externalLocations2.size()); assertEquals(2, externalLocations2.size());
assertHasExtAddresses(externalLocations, "00000100", "00000110");
} }
@Test @Test
@ -3484,13 +3491,15 @@ public class ExternalMergerAddTest extends AbstractExternalMergerTest {
ExternalManager externalManager = resultProgram.getExternalManager(); ExternalManager externalManager = resultProgram.getExternalManager();
assertTrue(externalManager.contains(libname)); assertTrue(externalManager.contains(libname));
List<ExternalLocation> externalLocations = Set<ExternalLocation> externalLocations =
externalManager.getExternalLocations(libname, label); externalManager.getExternalLocations(libname, label);
assertEquals(2, externalLocations.size()); assertEquals(2, externalLocations.size());
assertHasExtAddresses(externalLocations, "00000100", "00000110");
List<ExternalLocation> externalLocations2 = Set<ExternalLocation> externalLocations2 =
externalManager.getExternalLocations(BLUE_PATH[0], BLUE_PATH[1]); externalManager.getExternalLocations(BLUE_PATH[0], BLUE_PATH[1]);
assertEquals(2, externalLocations2.size()); 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 static org.junit.Assert.*;
import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import org.junit.Assert; import org.junit.Assert;
@ -50,10 +51,10 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
@Override @Override
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
try { try {
program.getExternalManager().setExternalPath("COMDLG32.DLL", "//comdlg32.dll", program.getExternalManager()
true); .setExternalPath("COMDLG32.DLL", "//comdlg32.dll", true);
program.getExternalManager().setExternalPath("KERNEL32.DLL", "//kernel32.dll", program.getExternalManager()
true); .setExternalPath("KERNEL32.DLL", "//kernel32.dll", true);
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
@ -65,8 +66,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyPrivate(ProgramDB program) { public void modifyPrivate(ProgramDB program) {
try { try {
program.getExternalManager().setExternalPath("GDI32.DLL", "//gdi32.dll", true); program.getExternalManager().setExternalPath("GDI32.DLL", "//gdi32.dll", true);
program.getExternalManager().setExternalPath("KERNEL32.DLL", "//kernel32.dll", program.getExternalManager()
true); .setExternalPath("KERNEL32.DLL", "//kernel32.dll", true);
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
@ -104,8 +105,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
try { try {
program.getExternalManager().setExternalPath("ADVAPI32.DLL", "//foo.dll", true); program.getExternalManager().setExternalPath("ADVAPI32.DLL", "//foo.dll", true);
program.getExternalManager().setExternalPath("KERNEL32.DLL", "//latest.dll", program.getExternalManager()
true); .setExternalPath("KERNEL32.DLL", "//latest.dll", true);
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
@ -191,8 +192,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
try { try {
removeExternalLibrary(program, "ADVAPI32.DLL"); removeExternalLibrary(program, "ADVAPI32.DLL");
program.getExternalManager().setExternalPath("USER32.DLL", "//latest.dll", program.getExternalManager()
true); .setExternalPath("USER32.DLL", "//latest.dll", true);
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
@ -251,8 +252,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
try { try {
removeExternalLibrary(program, "ADVAPI32.DLL"); removeExternalLibrary(program, "ADVAPI32.DLL");
program.getExternalManager().setExternalPath("USER32.DLL", "//latest.dll", program.getExternalManager()
true); .setExternalPath("USER32.DLL", "//latest.dll", true);
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
@ -311,8 +312,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
public void modifyLatest(ProgramDB program) { public void modifyLatest(ProgramDB program) {
try { try {
removeExternalLibrary(program, "ADVAPI32.DLL"); removeExternalLibrary(program, "ADVAPI32.DLL");
program.getExternalManager().setExternalPath("USER32.DLL", "//latest.dll", program.getExternalManager()
true); .setExternalPath("USER32.DLL", "//latest.dll", true);
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
@ -487,7 +488,7 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
try { try {
removeExternalLibrary(program, "ADVAPI32.DLL"); removeExternalLibrary(program, "ADVAPI32.DLL");
Reference[] refs = Reference[] refs =
program.getReferenceManager().getReferencesFrom(addr(program, "0x10011e4")); // SetCursor program.getReferenceManager().getReferencesFrom(addr(program, "0x10011e4")); // setCursor
SymbolTable symTab = program.getSymbolTable(); SymbolTable symTab = program.getSymbolTable();
Symbol s = symTab.getSymbol(refs[0]); Symbol s = symTab.getSymbol(refs[0]);
s.setName("SetCursor99", SourceType.USER_DEFINED); s.setName("SetCursor99", SourceType.USER_DEFINED);
@ -538,13 +539,24 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
// Check that the 2 renames happened. // Check that the 2 renames happened.
ExternalLocation extLoc = ExternalLocation extLoc =
resultExtMgr.getUniqueExternalLocation("ADVAPI32.DLL", "RegCreateKeyW"); resultExtMgr.getUniqueExternalLocation("ADVAPI32.DLL", "RegCreateKeyW");
assertNull(extLoc); assertNotNull(extLoc);
assertEquals("RegCreateKeyW", extLoc.getOriginalImportedName());
assertEquals("RegCreateKeyW99", extLoc.getLabel());
extLoc = resultExtMgr.getUniqueExternalLocation("ADVAPI32.DLL", "RegCreateKeyW99"); extLoc = resultExtMgr.getUniqueExternalLocation("ADVAPI32.DLL", "RegCreateKeyW99");
assertNotNull(extLoc); assertNotNull(extLoc);
extLoc = resultExtMgr.getUniqueExternalLocation("USER32.DLL", "SetCursor"); assertEquals("RegCreateKeyW", extLoc.getOriginalImportedName());
assertNull(extLoc); 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"); extLoc = resultExtMgr.getUniqueExternalLocation("USER32.DLL", "SetCursor99");
assertNotNull(extLoc); assertNotNull(extLoc);
assertEquals("setCursor", extLoc.getOriginalImportedName());
assertEquals("SetCursor99", extLoc.getLabel());
} }
@ -614,8 +626,8 @@ public class ExternalProgramMergerTest extends AbstractListingMergeManagerTest {
ExternalLocationIterator iter = extMgr.getExternalLocations(libName); ExternalLocationIterator iter = extMgr.getExternalLocations(libName);
while (iter.hasNext()) { while (iter.hasNext()) {
ExternalLocation loc = iter.next(); ExternalLocation loc = iter.next();
if (!((ExternalManagerDB) extMgr).removeExternalLocation( if (!((ExternalManagerDB) extMgr)
loc.getExternalSpaceAddress())) { .removeExternalLocation(loc.getExternalSpaceAddress())) {
Assert.fail("Couldn't remove external location for library " + libName); 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 static org.junit.Assert.*;
import java.util.List; import java.util.Set;
import javax.swing.JDialog; import javax.swing.JDialog;
@ -944,27 +944,10 @@ public class FunctionMergerThunkTest extends AbstractExternalMergerTest {
ExternalLocation externalLocation = thunkedFunction.getExternalLocation(); ExternalLocation externalLocation = thunkedFunction.getExternalLocation();
assertEquals(addr(resultProgram, "77db1020"), externalLocation.getAddress()); assertEquals(addr(resultProgram, "77db1020"), externalLocation.getAddress());
List<ExternalLocation> externalLocations = Set<ExternalLocation> externalLocations =
resultProgram.getExternalManager().getExternalLocations("user32.dll", "printf"); resultProgram.getExternalManager().getExternalLocations("user32.dll", "printf");
assertEquals(2, externalLocations.size()); assertEquals(2, externalLocations.size());
Address address1 = externalLocations.get(0).getAddress(); assertHasExtAddresses(externalLocations, "77db1020", "77db1130");
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;
} }
@Test @Test

View file

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

View file

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

View file

@ -387,6 +387,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
assertEquals(addr(0x0100688c), loc.getByteAddress()); assertEquals(addr(0x0100688c), loc.getByteAddress());
assertTrue(loc instanceof CommentFieldLocation); assertTrue(loc instanceof CommentFieldLocation);
// //
assertTrue("Search result not found: " + addr(0x01006890), searcher.hasNext());
loc = searcher.next().programLocation(); loc = searcher.next().programLocation();
assertNotNull(loc); assertNotNull(loc);
assertEquals(addr(0x01006890), loc.getByteAddress()); assertEquals(addr(0x01006890), loc.getByteAddress());
@ -394,11 +395,13 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
MnemonicFieldLocation mloc = (MnemonicFieldLocation) loc; MnemonicFieldLocation mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic()); assertEquals("float", mloc.getMnemonic());
// //
assertTrue("Search result not found: " + addr(0x01006890), searcher.hasNext());
loc = searcher.next().programLocation(); loc = searcher.next().programLocation();
assertNotNull(loc); assertNotNull(loc);
assertEquals(addr(0x01006890), loc.getByteAddress()); assertEquals(addr(0x01006890), loc.getByteAddress());
assertTrue(loc instanceof CommentFieldLocation); assertTrue(loc instanceof CommentFieldLocation);
// //
assertTrue("Search result not found: " + addr(0x0100689b), searcher.hasNext());
loc = searcher.next().programLocation(); loc = searcher.next().programLocation();
assertNotNull(loc); assertNotNull(loc);
assertEquals(addr(0x0100689b), loc.getByteAddress()); assertEquals(addr(0x0100689b), loc.getByteAddress());
@ -406,6 +409,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
mloc = (MnemonicFieldLocation) loc; mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic()); assertEquals("float", mloc.getMnemonic());
// //
assertTrue("Search result not found: " + addr(0x0100689f), searcher.hasNext());
loc = searcher.next().programLocation(); loc = searcher.next().programLocation();
assertNotNull(loc); assertNotNull(loc);
assertEquals(addr(0x0100689f), loc.getByteAddress()); assertEquals(addr(0x0100689f), loc.getByteAddress());
@ -413,6 +417,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
mloc = (MnemonicFieldLocation) loc; mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic()); assertEquals("float", mloc.getMnemonic());
// //
assertTrue("Search result not found: " + addr(0x0100689f), searcher.hasNext());
loc = searcher.next().programLocation(); loc = searcher.next().programLocation();
assertNotNull(loc); assertNotNull(loc);
assertEquals(addr(0x0100689f), loc.getByteAddress()); assertEquals(addr(0x0100689f), loc.getByteAddress());
@ -478,6 +483,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
assertEquals(addr(0x0100688c), loc.getByteAddress()); assertEquals(addr(0x0100688c), loc.getByteAddress());
assertTrue(loc instanceof CommentFieldLocation); assertTrue(loc instanceof CommentFieldLocation);
// //
assertTrue("Search result not found: " + addr(0x0100689b), searcher.hasNext());
loc = searcher.next().programLocation(); loc = searcher.next().programLocation();
assertNotNull(loc); assertNotNull(loc);
assertEquals(addr(0x0100689b), loc.getByteAddress()); assertEquals(addr(0x0100689b), loc.getByteAddress());
@ -485,6 +491,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
MnemonicFieldLocation mloc = (MnemonicFieldLocation) loc; MnemonicFieldLocation mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic()); assertEquals("float", mloc.getMnemonic());
// //
assertTrue("Search result not found: " + addr(0x0100689f), searcher.hasNext());
loc = searcher.next().programLocation(); loc = searcher.next().programLocation();
assertNotNull(loc); assertNotNull(loc);
assertEquals(addr(0x0100689f), loc.getByteAddress()); assertEquals(addr(0x0100689f), loc.getByteAddress());
@ -492,6 +499,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
mloc = (MnemonicFieldLocation) loc; mloc = (MnemonicFieldLocation) loc;
assertEquals("float", mloc.getMnemonic()); assertEquals("float", mloc.getMnemonic());
// //
assertTrue("Search result not found: " + addr(0x0100689f), searcher.hasNext());
loc = searcher.next().programLocation(); loc = searcher.next().programLocation();
assertNotNull(loc); assertNotNull(loc);
assertEquals(addr(0x0100689f), loc.getByteAddress()); assertEquals(addr(0x0100689f), loc.getByteAddress());
@ -819,6 +827,7 @@ public class ListingDisplaySearcherTest extends AbstractGhidraHeadedIntegrationT
private void checkTextFound(ArrayList<Address> startList, Class<?> fieldClass) { private void checkTextFound(ArrayList<Address> startList, Class<?> fieldClass) {
for (Address start : startList) { for (Address start : startList) {
assertTrue("Search result not found: " + start, searcher.hasNext());
ProgramLocation loc = searcher.next().programLocation(); ProgramLocation loc = searcher.next().programLocation();
assertNotNull(loc); assertNotNull(loc);
assertTrue(fieldClass.isAssignableFrom(loc.getClass())); 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. * 19-Oct-2023 - version 28 Revised overlay address space table and eliminated min/max.
* Multiple blocks are permitted within a single overlay space. * Multiple blocks are permitted within a single overlay space.
* 13-Dec-2024 - version 29 Added source file manager. * 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 * 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..."); monitor.setMessage("Updating symbols...");
getSymbolTable().setLanguage(translator, monitor); getSymbolTable().setLanguage(translator, monitor);
getExternalManager().setLanguage(translator, monitor);
getFunctionManager().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.database.DBObjectCache;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.data.*; 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; 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.NamespaceUtils;
import ghidra.app.util.SymbolPath; 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.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.data.DataType; import ghidra.program.model.data.DataType;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
@ -28,19 +28,18 @@ import ghidra.util.exception.*;
public class ExternalLocationDB implements ExternalLocation { public class ExternalLocationDB implements ExternalLocation {
private static final char ORIGINAL_IMPORTED_DELIMITER = ',';
private ExternalManagerDB extMgr; private ExternalManagerDB extMgr;
private SymbolDB symbol; private MemorySymbol symbol;
/** /**
* Creates an externalLocationDB using a symbol * Creates an externalLocationDB using a symbol
* at the same external space address. * at the same external space address.
* @param extMgr the ExternalManager. * @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 * 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.extMgr = extMgr;
this.symbol = symbol; this.symbol = symbol;
} }
@ -50,9 +49,6 @@ public class ExternalLocationDB implements ExternalLocation {
return symbol; return symbol;
} }
/**
* @see ghidra.program.model.symbol.ExternalLocation#getLibraryName()
*/
@Override @Override
public String getLibraryName() { public String getLibraryName() {
Library library = getLibrary(); Library library = getLibrary();
@ -68,17 +64,11 @@ public class ExternalLocationDB implements ExternalLocation {
} }
/**
* @see ghidra.program.model.symbol.ExternalLocation#getParentNameSpace()
*/
@Override @Override
public Namespace getParentNameSpace() { public Namespace getParentNameSpace() {
return symbol.getParentNamespace(); return symbol.getParentNamespace();
} }
/**
* @see ghidra.program.model.symbol.ExternalLocation#getParentName()
*/
@Override @Override
public String getParentName() { public String getParentName() {
return symbol.getParentNamespace().getName(); return symbol.getParentNamespace().getName();
@ -88,9 +78,6 @@ public class ExternalLocationDB implements ExternalLocation {
return symbol.getParentNamespace().getID(); return symbol.getParentNamespace().getID();
} }
/**
* @see ghidra.program.model.symbol.ExternalLocation#getLabel()
*/
@Override @Override
public String getLabel() { public String getLabel() {
return symbol.getName(); return symbol.getName();
@ -98,7 +85,7 @@ public class ExternalLocationDB implements ExternalLocation {
@Override @Override
public String getOriginalImportedName() { public String getOriginalImportedName() {
return getExternalData(symbol).getOriginalImportedName(); return symbol.getExternalOriginalImportedName();
} }
@Override @Override
@ -106,17 +93,11 @@ public class ExternalLocationDB implements ExternalLocation {
return symbol.getSource(); return symbol.getSource();
} }
/**
* @see ghidra.program.model.symbol.ExternalLocation#getAddress()
*/
@Override @Override
public Address getAddress() { public Address getAddress() {
return getExternalData(symbol).getAddress(extMgr.getAddressMap().getAddressFactory()); return symbol.getExternalProgramAddress();
} }
/**
* @see ghidra.program.model.symbol.ExternalLocation#getExternalSpaceAddress()
*/
@Override @Override
public Address getExternalSpaceAddress() { public Address getExternalSpaceAddress() {
return symbol.getAddress(); return symbol.getAddress();
@ -136,9 +117,6 @@ public class ExternalLocationDB implements ExternalLocation {
return symbol.getSymbolType() == SymbolType.FUNCTION; return symbol.getSymbolType() == SymbolType.FUNCTION;
} }
/**
* @see ghidra.program.model.symbol.ExternalLocation#getDataType()
*/
@Override @Override
public DataType getDataType() { public DataType getDataType() {
long dataTypeID = symbol.getDataTypeId(); long dataTypeID = symbol.getDataTypeId();
@ -148,15 +126,10 @@ public class ExternalLocationDB implements ExternalLocation {
return extMgr.getProgram().getDataTypeManager().getDataType(dataTypeID); return extMgr.getProgram().getDataTypeManager().getDataType(dataTypeID);
} }
/**
* @see ghidra.program.model.symbol.ExternalLocation#setDataType(ghidra.program.model.data.DataType)
*/
@Override @Override
public void setDataType(DataType dt) { public void setDataType(DataType dt) {
long dataTypeID = extMgr.getProgram().getDataTypeManager().getResolvedID(dt); long dataTypeID = extMgr.getProgram().getDataTypeManager().getResolvedID(dt);
symbol.setDataTypeId(dataTypeID); symbol.setDataTypeId(dataTypeID);
// TODO: change notification may be required
} }
@Override @Override
@ -173,7 +146,7 @@ public class ExternalLocationDB implements ExternalLocation {
return getFunction(); return getFunction();
} }
Function function = extMgr.createFunction(this); Function function = extMgr.createFunction(this);
symbol = (SymbolDB) function.getSymbol(); symbol = (FunctionSymbol) function.getSymbol();
return function; 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. * If a namespace is not included within label, the current namespace will be preserved.
* @param source the source of this external symbol: * @param source the source of this external symbol:
* Symbol.DEFAULT, Symbol.ANALYSIS, Symbol.IMPORTED, or Symbol.USER_DEFINED * Symbol.DEFAULT, Symbol.ANALYSIS, Symbol.IMPORTED, or Symbol.USER_DEFINED
* @throws InvalidInputException * @throws InvalidInputException if the name contains illegal characters (space for example)
* @see ghidra.program.model.symbol.ExternalLocation#setLabel(java.lang.String)
*/ */
void setLabel(String label, SourceType source) throws InvalidInputException { void setLabel(String label, SourceType source) throws InvalidInputException {
if (label == null) { if (label == null) {
@ -223,12 +195,10 @@ public class ExternalLocationDB implements ExternalLocation {
@Override @Override
public void setAddress(Address address) throws InvalidInputException { public void setAddress(Address address) throws InvalidInputException {
String addressString = address != null ? address.toString() : null; if (address == null && getSource() == SourceType.DEFAULT) {
if (addressString == null && getSource() == SourceType.DEFAULT) {
throw new InvalidInputException("Either an external label or address is required"); throw new InvalidInputException("Either an external label or address is required");
} }
updateSymbolData(symbol, getExternalData(symbol).getOriginalImportedName(), symbol.setExternalProgramAddress(address, true);
addressString);
} }
public void saveOriginalNameIfNeeded(Namespace oldNamespace, String oldName, 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 // if we don't have an original already set and it is an imported symbol, save it
String originalImportedName = getOriginalImportedName(); String originalImportedName = getOriginalImportedName();
if (getLabel().equals(originalImportedName)) { if (getLabel().equals(originalImportedName)) {
setOriginalImportedName(symbol, null); symbol.setExternalOriginalImportedName(null, false);
} }
else if (wasInLibrary && getSource() != SourceType.DEFAULT && else if (wasInLibrary && getSource() != SourceType.DEFAULT &&
oldSource == SourceType.IMPORTED && originalImportedName == null) { oldSource == SourceType.IMPORTED && originalImportedName == null) {
setOriginalImportedName(symbol, oldName); symbol.setExternalOriginalImportedName(oldName, false);
} }
} }
@ -288,8 +258,8 @@ public class ExternalLocationDB implements ExternalLocation {
} }
try { try {
Library library = NamespaceUtils.getLibrary(symbol.getParentNamespace()); Library library = NamespaceUtils.getLibrary(symbol.getParentNamespace());
symbol.setExternalOriginalImportedName(null, false); // clear orig imported name
symbol.setNameAndNamespace(originalName, library, SourceType.IMPORTED); symbol.setNameAndNamespace(originalName, library, SourceType.IMPORTED);
setOriginalImportedName(symbol, null);
} }
catch (CircularDependencyException | DuplicateNameException | InvalidInputException e) { catch (CircularDependencyException | DuplicateNameException | InvalidInputException e) {
throw new AssertException("Can't happen here", 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.framework.store.FileSystem;
import ghidra.program.database.ManagerDB; import ghidra.program.database.ManagerDB;
import ghidra.program.database.ProgramDB; 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.function.FunctionManagerDB;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.database.symbol.*; 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.data.DataType;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Library; import ghidra.program.model.listing.Library;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.program.util.LanguageTranslator;
import ghidra.util.Lock; import ghidra.util.Lock;
import ghidra.util.Msg; import ghidra.util.Msg;
import ghidra.util.exception.*; import ghidra.util.exception.*;
@ -46,7 +46,6 @@ import ghidra.util.task.TaskMonitor;
public class ExternalManagerDB implements ManagerDB, ExternalManager { public class ExternalManagerDB implements ManagerDB, ExternalManager {
private AddressMap addrMap; private AddressMap addrMap;
private NamespaceManager scopeMgr;
private SymbolManager symbolMgr; private SymbolManager symbolMgr;
private FunctionManagerDB functionMgr; private FunctionManagerDB functionMgr;
@ -95,7 +94,6 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
this.program = program; this.program = program;
symbolMgr = program.getSymbolTable(); symbolMgr = program.getSymbolTable();
functionMgr = program.getFunctionManager(); functionMgr = program.getFunctionManager();
scopeMgr = program.getNamespaceManager();
} }
@Override @Override
@ -202,7 +200,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
SourceType locSourceType = checkExternalLabel(extLabel, extAddr, sourceType); SourceType locSourceType = checkExternalLabel(extLabel, extAddr, sourceType);
lock.acquire(); lock.acquire();
try { try {
Namespace libraryScope = getLibraryScope(extLibraryName); Library libraryScope = getLibraryScope(extLibraryName);
if (libraryScope == null) { if (libraryScope == null) {
libraryScope = addExternalName(extLibraryName, null, sourceType); libraryScope = addExternalName(extLibraryName, null, sourceType);
} }
@ -245,7 +243,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
SourceType locSourceType = checkExternalLabel(extLabel, extAddr, sourceType); SourceType locSourceType = checkExternalLabel(extLabel, extAddr, sourceType);
lock.acquire(); lock.acquire();
try { try {
Namespace libraryScope = getLibraryScope(extLibraryName); Library libraryScope = getLibraryScope(extLibraryName);
if (libraryScope == null) { if (libraryScope == null) {
libraryScope = addExternalName(extLibraryName, null, libraryScope = addExternalName(extLibraryName, null,
sourceType != SourceType.DEFAULT ? sourceType : SourceType.ANALYSIS); 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) private SourceType checkExternalLabel(String extLabel, Address extAddr, SourceType source)
throws InvalidInputException { throws InvalidInputException {
if (extLabel != null && (StringUtils.isBlank(extLabel) || if (StringUtils.isBlank(extLabel) ||
SymbolUtilities.isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory()))) { SymbolUtilities.isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory())) {
extLabel = null; extLabel = null; // force use of address
} }
if (extLabel == null && extAddr == null) { if (extLabel == null && extAddr == null) {
throw new InvalidInputException("Either an external label or address is required"); 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) { if (sourceType == SourceType.DEFAULT) {
extLabel = null; extLabel = null;
} }
ExternalLocationDB extLoc = else if (StringUtils.isBlank(extLabel) || SymbolUtilities
(ExternalLocationDB) getExtLocation(extNamespace, extLabel, extAddr, reuseExisting); .isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory())) {
extLabel = null; // force use of address
}
if (extAddr != null || reuseExisting) {
if (extLoc != null) { ExternalLocationDB extLoc = (ExternalLocationDB) getExtLocation(extNamespace,
// if there is already a location with the address, then we must use it extLabel, extAddr, reuseExisting);
if (extAddr != null || 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())) { if (extLabel != null && !extLabel.equals(extLoc.getLabel())) {
extLoc.setLabel(extLabel, sourceType); extLoc.setLabel(extLabel, sourceType);
} }
@ -345,21 +349,25 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
return extLoc; return extLoc;
} }
} }
// ok can't or don't want to reuse an existing one, so make a new one. // 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(); Address externalSpaceAddress = symbolMgr.getNextExternalSymbolAddress();
String extMemAddrString = (extAddr != null) ? extAddr.toString() : null;
if (isFunction) { if (isFunction) {
Function function = functionMgr.createExternalFunction(externalSpaceAddress, FunctionDB function = functionMgr.createExternalFunction(externalSpaceAddress,
extLabel, extNamespace, extMemAddrString, sourceType); extLabel, extNamespace, null, extAddr, sourceType);
s = (SymbolDB) function.getSymbol(); s = (FunctionSymbol) function.getSymbol();
} }
else { else {
s = (SymbolDB) symbolMgr.createCodeSymbol(externalSpaceAddress, extLabel, s = symbolMgr.createExternalCodeSymbol(externalSpaceAddress, extLabel, extNamespace,
extNamespace, sourceType, extMemAddrString); sourceType, null, extAddr);
} }
return new ExternalLocationDB(this, s); return new ExternalLocationDB(this, s);
} }
catch (IOException e) {
program.dbError(e); // will not return
return null;
}
finally { finally {
lock.release(); lock.release();
} }
@ -377,66 +385,31 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
private ExternalLocation getExtLocation(Namespace library, String extLabel, Address extAddr, private ExternalLocation getExtLocation(Namespace library, String extLabel, Address extAddr,
boolean reuseExisting) throws InvalidInputException { boolean reuseExisting) throws InvalidInputException {
if (extLabel != null && (extLabel.length() == 0 || // Name match will also consider original import name if library is either null
SymbolUtilities.isReservedExternalDefaultName(extLabel, addrMap.getAddressFactory()))) { // or a Library, otherwise only a specific namespaced name match will be considered.
extLabel = null; // force use of address
}
ExternalLocation match = ExternalLocation match =
findMatchingLocationByName(library, extLabel, extAddr, reuseExisting); findMatchingLocationByName(library, extLabel, extAddr, reuseExisting);
if (match != null) { if (match != null) {
return match; return match;
} }
// So now get all the externalLocations for a library and search them if (extLabel == null) { // assume extAddr is not null (already checked)
List<ExternalLocation> locations = getExternalLocations(library); return findMatchingLocationByAddress(extAddr, reuseExisting);
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;
}
}
} }
return null; 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 // 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 // reuseExisting is true, then also find a match as long as the name and namespace match and
// the address is null. // the address is null.
private ExternalLocation findMatchingLocationByName(Namespace libScope, String extLabel, private ExternalLocation findMatchingLocationByName(Namespace namespace, String extLabel,
Address extAddr, boolean reuseExisting) { Address extAddr, boolean reuseExisting) {
if (extLabel == null) { if (StringUtils.isBlank(extLabel)) {
return null; return null;
} }
List<ExternalLocation> externalLocations = getExternalLocations(libScope, extLabel); Set<ExternalLocation> externalLocations = getExternalLocations(namespace, extLabel);
if (externalLocations.isEmpty()) { if (externalLocations.isEmpty()) {
return null; return null;
} }
@ -460,14 +433,16 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
} }
// first look for one without an address // first look for one without an address
ExternalLocation possibleExtLoc = null;
for (ExternalLocation externalLocation : externalLocations) { for (ExternalLocation externalLocation : externalLocations) {
if (externalLocation.getAddress() == null) { if (externalLocation.getAddress() == null) {
return externalLocation; return externalLocation;
} }
possibleExtLoc = externalLocation;
} }
// if reuse existing, then return any // 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 // 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 // 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 // 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. // location, so as long as the address matches, we can return it.
private ExternalLocation findMatchingLocationByAddress(List<ExternalLocation> locations, private ExternalLocation findMatchingLocationByAddress(Address extAddr, boolean reuseExisting) {
Address extAddr, boolean reuseExisting) { for (Symbol symbol : symbolMgr.getExternalSymbolByMemoryAddress(null, extAddr)) {
for (ExternalLocation externalLocation : locations) { ExternalLocation externalLocation = getExternalLocation(symbol);
if (extAddr.equals(externalLocation.getAddress())) { if (reuseExisting || externalLocation.getLabel() == null) {
if (reuseExisting || externalLocation.getLabel() == null) { return externalLocation;
return externalLocation;
}
} }
} }
return null; return null;
} }
@Override @Override
public List<ExternalLocation> getExternalLocations(Namespace libScope, String extLabel) { public Set<ExternalLocation> getExternalLocations(Namespace namespace, String extLabel) {
List<ExternalLocation> externalLocations = new ArrayList<>(); if (namespace != null && !namespace.isExternal()) {
List<Symbol> symbols = symbolMgr.getSymbols(extLabel, libScope); return Set.of();
for (Symbol symbol : symbols) { }
ExternalLocation externalLocation = getExternalLocation(symbol); Set<ExternalLocation> externalLocations = new HashSet<>();
if (externalLocation != null) { if (namespace == null || namespace instanceof Library) {
externalLocations.add(externalLocation); // 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 @Override
public List<ExternalLocation> getExternalLocations(String libraryName, String label) { public Set<ExternalLocation> getExternalLocations(String libraryName, String label) {
Namespace libraryScope = getLibraryScope(libraryName); Library library = getLibraryScope(libraryName);
if (libraryScope == null) { if (library == null && !StringUtils.isBlank(libraryName)) {
return Collections.emptyList(); return Set.of();
} }
return getExternalLocations(libraryScope, label); return getExternalLocations(library, label);
} }
@Override @Override
public ExternalLocation getUniqueExternalLocation(Namespace namespace, String label) { public ExternalLocation getUniqueExternalLocation(Namespace namespace, String label) {
List<ExternalLocation> externalLocations = getExternalLocations(namespace, label); Set<ExternalLocation> externalLocations = getExternalLocations(namespace, label);
if (externalLocations.size() == 1) { if (externalLocations.size() == 1) {
return externalLocations.get(0); return externalLocations.iterator().next();
} }
return null; return null;
} }
@Override @Override
public ExternalLocation getUniqueExternalLocation(String libraryName, String label) { public ExternalLocation getUniqueExternalLocation(String libraryName, String label) {
Namespace libScope = getLibraryScope(libraryName); Library library = getLibraryScope(libraryName);
if (libScope == null) { if (library == null && !StringUtils.isBlank(libraryName)) {
return null; return null;
} }
return getUniqueExternalLocation(libScope, label); return getUniqueExternalLocation(library, 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);
} }
/** /**
* Get the default name for an external function or code symbol * {@return the default name for an external function or code symbol}
* @param sym * @param sym external label or function symbol
* @return default name * @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) { public static String getDefaultExternalName(SymbolDB sym) {
SymbolType type = sym.getSymbolType(); if (!(sym instanceof MemorySymbol) && !sym.isExternal()) {
if ((type != SymbolType.LABEL && type != SymbolType.FUNCTION) || !sym.isExternal()) { throw new IllegalArgumentException("External label or function symbol required");
throw new AssertException();
} }
ExternalData externalData = ExternalLocationDB.getExternalData(sym);
Address addr = externalData.getAddress(sym.getProgram().getAddressFactory()); Address addr = ((MemorySymbol) sym).getExternalProgramAddress();
if (addr == null) { 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); return SymbolUtilities.getDefaultExternalFunctionName(addr);
} }
long dataTypeID = sym.getDataTypeId(); long dataTypeID = sym.getDataTypeId();
DataType dt = DataType dt =
(dataTypeID < 0) ? null : sym.getProgram().getDataTypeManager().getDataType(dataTypeID); (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. * @param externalAddr the external address.
* @throws IllegalArgumentException if address is not external
*/ */
public ExternalLocation getExtLocation(Address externalAddr) { public ExternalLocation getExtLocation(Address externalAddr) {
if (externalAddr.getAddressSpace() != AddressSpace.EXTERNAL_SPACE) { if (externalAddr.getAddressSpace() != AddressSpace.EXTERNAL_SPACE) {
@ -582,7 +567,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
try { try {
Symbol[] symbols = symbolMgr.getSymbols(externalAddr); Symbol[] symbols = symbolMgr.getSymbols(externalAddr);
if (symbols.length == 1) { if (symbols.length == 1) {
return new ExternalLocationDB(this, (SymbolDB) symbols[0]); return new ExternalLocationDB(this, (MemorySymbol) symbols[0]);
} }
if (symbols.length > 2) { if (symbols.length > 2) {
throw new AssertException( throw new AssertException(
@ -613,6 +598,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
/** /**
* Removes the external location at the given external address * Removes the external location at the given external address
* @param externalAddr the address at which to remove the external location. * @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) { public boolean removeExternalLocation(Address externalAddr) {
lock.acquire(); lock.acquire();
@ -651,15 +637,6 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
return true; 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 @Override
public void updateExternalLibraryName(String oldName, String newName, SourceType source) public void updateExternalLibraryName(String oldName, String newName, SourceType source)
throws DuplicateNameException, InvalidInputException { throws DuplicateNameException, InvalidInputException {
@ -691,9 +668,9 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
return (Library) s.getObject(); return (Library) s.getObject();
} }
private Namespace getLibraryScope(String name) { private Library getLibraryScope(String name) {
Symbol s = symbolMgr.getLibrarySymbol(name); LibrarySymbol s = symbolMgr.getLibrarySymbol(name);
return s == null ? null : (Namespace) s.getObject(); return s == null ? null : s.getObject();
} }
@Override @Override
@ -723,9 +700,9 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
@Override @Override
public String getExternalLibraryPath(String externalName) { public String getExternalLibraryPath(String externalName) {
SymbolDB s = (SymbolDB) symbolMgr.getLibrarySymbol(externalName); LibrarySymbol s = symbolMgr.getLibrarySymbol(externalName);
if (s instanceof LibrarySymbol) { if (s != null) {
return s.getSymbolStringData(); return s.getExternalLibraryPath();
} }
return null; return null;
} }
@ -743,7 +720,7 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
lock.acquire(); lock.acquire();
try { try {
SymbolDB s = (SymbolDB) symbolMgr.getLibrarySymbol(externalName); LibrarySymbol s = symbolMgr.getLibrarySymbol(externalName);
if (s == null) { if (s == null) {
try { try {
addExternalName(externalName, externalPath, addExternalName(externalName, externalPath,
@ -753,8 +730,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
throw new AssertException(e); throw new AssertException(e);
} }
} }
else if (s instanceof LibrarySymbol) { else {
s.setSymbolStringData(externalPath); s.setExternalLibraryPath(externalPath);
} }
} }
finally { finally {
@ -784,8 +761,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
if (!(symbol instanceof CodeSymbol)) { if (!(symbol instanceof CodeSymbol)) {
throw new IllegalStateException("Expected external code symbol"); throw new IllegalStateException("Expected external code symbol");
} }
//long dtId = symbol.getSymbolData1(); Address extProgAddr = extLoc.getAddress();
String extData = symbol.getSymbolStringData(); String origImpName = extLoc.getOriginalImportedName();
String name = symbol.getName(); String name = symbol.getName();
Namespace namespace = symbol.getParentNamespace(); Namespace namespace = symbol.getParentNamespace();
Address extAddr = symbol.getAddress(); Address extAddr = symbol.getAddress();
@ -793,7 +770,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
((CodeSymbol) symbol).delete(true); ((CodeSymbol) symbol).delete(true);
return functionMgr.createExternalFunction(extAddr, name, namespace, extData, source); return functionMgr.createExternalFunction(extAddr, name, namespace, origImpName,
extProgAddr, source);
} }
catch (Exception e) { catch (Exception e) {
throw new RuntimeException("Unexpected exception", e); throw new RuntimeException("Unexpected exception", e);
@ -809,12 +787,12 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
@Override @Override
public ExternalLocationIterator getExternalLocations(Address memoryAddress) { public ExternalLocationIterator getExternalLocations(Address memoryAddress) {
return new ExternalLocationDBIterator(symbolMgr.getExternalSymbols(), memoryAddress); return new ExternalLocationDBIterator(null, memoryAddress);
} }
@Override @Override
public ExternalLocationIterator getExternalLocations(String externalName) { public ExternalLocationIterator getExternalLocations(String externalName) {
Namespace scope = getLibraryScope(externalName); Library scope = getLibraryScope(externalName);
if (scope != null) { if (scope != null) {
return new ExternalLocationDBIterator(symbolMgr.getSymbols(scope)); return new ExternalLocationDBIterator(symbolMgr.getSymbols(scope));
} }
@ -830,8 +808,8 @@ public class ExternalManagerDB implements ManagerDB, ExternalManager {
ExternalLocationDBIterator() { ExternalLocationDBIterator() {
} }
ExternalLocationDBIterator(SymbolIterator symIter, Address matchingAddress) { ExternalLocationDBIterator(Library library, Address matchingAddress) {
this.symIter = symIter; this.symIter = symbolMgr.getExternalSymbolByMemoryAddress(library, matchingAddress);
this.matchingAddress = 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 ProgramDB program;
private Address entryPoint; private Address entryPoint;
private Symbol functionSymbol; private FunctionSymbol functionSymbol;
private DBRecord rec; private DBRecord rec;
private FunctionStackFrame frame; private FunctionStackFrame frame;
@ -108,7 +108,7 @@ public class FunctionDB extends DatabaseObject implements Function {
private void init() { private void init() {
thunkedFunction = manager.getThunkedFunction(this); thunkedFunction = manager.getThunkedFunction(this);
functionSymbol = program.getSymbolTable().getSymbol(key); functionSymbol = (FunctionSymbol) program.getSymbolTable().getSymbol(key);
entryPoint = functionSymbol.getAddress(); entryPoint = functionSymbol.getAddress();
} }
@ -974,7 +974,7 @@ public class FunctionDB extends DatabaseObject implements Function {
symbolMap.put(v.symbol, v); symbolMap.put(v.symbol, v);
} }
if (var.getComment() != null) { if (var.getComment() != null) {
v.symbol.setSymbolStringData(var.getComment()); v.symbol.setSymbolComment(var.getComment());
} }
manager.functionChanged(this, null); manager.functionChanged(this, null);
return v; return v;
@ -1667,7 +1667,7 @@ public class FunctionDB extends DatabaseObject implements Function {
manager.functionChanged(this, PARAMETERS_CHANGED); manager.functionChanged(this, PARAMETERS_CHANGED);
} }
if (var.getComment() != null) { if (var.getComment() != null) {
p.symbol.setSymbolStringData(var.getComment()); p.symbol.setSymbolComment(var.getComment());
} }
updateSignatureSourceAfterVariableChange(source, p.getDataType()); updateSignatureSourceAfterVariableChange(source, p.getDataType());
return p; return p;

View file

@ -30,7 +30,6 @@ import ghidra.program.database.DBObjectCache;
import ghidra.program.database.ProgramDB; import ghidra.program.database.ProgramDB;
import ghidra.program.database.code.CodeManager; import ghidra.program.database.code.CodeManager;
import ghidra.program.database.data.DataTypeManagerDB; import ghidra.program.database.data.DataTypeManagerDB;
import ghidra.program.database.external.ExternalLocationDB;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.database.symbol.*; import ghidra.program.database.symbol.*;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@ -150,38 +149,40 @@ public class FunctionManagerDB implements FunctionManager {
/** /**
* Transform an existing external symbol into an external function. * 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 * @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 * other symbol using this address must first be deleted. Results are unpredictable if this is
* not done. * not done.
* @param name the external function name * @param name the external function name
* @param nameSpace the external function namespace * @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. * @param source the source of this external.
* @return external function * @return external function
* @throws InvalidInputException if the name is invalid * @throws InvalidInputException if the name is invalid
*/ */
public Function createExternalFunction(Address extSpaceAddr, String name, Namespace nameSpace, public FunctionDB createExternalFunction(Address extSpaceAddr, String name, Namespace nameSpace,
String extData, SourceType source) throws InvalidInputException { String originalImportName, Address externalProgramAddress, SourceType source)
throws InvalidInputException {
lock.acquire(); lock.acquire();
try { try {
Symbol symbol = FunctionSymbol symbol = symbolMgr.createExternalFunctionSymbol(extSpaceAddr, name,
symbolMgr.createFunctionSymbol(extSpaceAddr, name, nameSpace, source, extData); nameSpace, source, originalImportName, externalProgramAddress);
long returnDataTypeId = program.getDataTypeManager().getResolvedID(DataType.DEFAULT); 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, program.setObjChanged(ProgramEvent.FUNCTION_ADDED, extSpaceAddr, funcDB, null, null);
null); return funcDB;
return funcDB; }
} catch (IOException e) {
catch (IOException e) { dbError(e); // will not return
dbError(e);
}
return null; return null;
} }
finally { 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 { try {
Symbol symbol =
symbolMgr.createMemoryFunctionSymbol(entryPoint, name, nameSpace, source);
long returnDataTypeId =
program.getDataTypeManager().getResolvedID(DataType.DEFAULT);
if (refFunc != null) { if (refFunc != null) {
String oldName = symbol.getName(); String oldName = symbol.getName();

View file

@ -176,12 +176,12 @@ public abstract class VariableDB implements Variable {
@Override @Override
public String getComment() { public String getComment() {
return symbol.getSymbolStringData(); return symbol.getSymbolComment();
} }
@Override @Override
public void setComment(String comment) { public void setComment(String comment) {
symbol.setSymbolStringData(comment); symbol.setSymbolComment(comment);
functionMgr.functionChanged(function, null); 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. * 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 * If function does not use custom storage, the specified storage will be ignored and set
* to UNASSIGNED. * to UNASSIGNED.
* @param newStorage * <P>
* @param dt * NOTE: Method will trigger a symbol changed event.
*
* @param newStorage variable storage
* @param dt variable datatype
*/ */
void setStorageAndDataType(VariableStorage newStorage, DataType dt) { void setStorageAndDataType(VariableStorage newStorage, DataType dt) {
if (this instanceof Parameter && !function.hasCustomVariableStorage()) { 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 * 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 * @return true if the address is an external entry point
*/ */
public boolean isExternalEntryPoint(Address toAddr) { public boolean isExternalEntryPoint(Address toAddr) {
if (!toAddr.isMemoryAddress()) {
return false;
}
lock.acquire(); lock.acquire();
try { try {
RefList refList = getToRefs(toAddr); RefList refList = getToRefs(toAddr);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -40,13 +40,10 @@ public class LibrarySymbol extends SymbolDB {
* Constructs a new Library Symbol * Constructs a new Library Symbol
* @param symbolMgr the symbol manager * @param symbolMgr the symbol manager
* @param cache symbol object cache * @param cache symbol object cache
* @param address the address for this symbol
* @param record the record for this symbol * @param record the record for this symbol
*/ */
public LibrarySymbol(SymbolManager symbolMgr, DBObjectCache<SymbolDB> cache, Address address, public LibrarySymbol(SymbolManager symbolMgr, DBObjectCache<SymbolDB> cache, DBRecord record) {
DBRecord record) { super(symbolMgr, cache, Address.NO_ADDRESS, record);
super(symbolMgr, cache, address, record);
} }
@Override @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 @Override
public SymbolType getSymbolType() { public SymbolType getSymbolType() {
return SymbolType.LIBRARY; return SymbolType.LIBRARY;
@ -102,11 +89,20 @@ public class LibrarySymbol extends SymbolDB {
} }
@Override @Override
public Object getObject() { public Library getObject() {
if (library == null) { lock.acquire();
library = new LibraryDB(this, symbolMgr.getProgram().getNamespaceManager()); try {
if (!checkIsValid()) {
return null;
}
if (library == null) {
library = new LibraryDB(this, symbolMgr.getProgram().getNamespaceManager());
}
return library;
}
finally {
lock.release();
} }
return library;
} }
@Override @Override
@ -119,4 +115,38 @@ public class LibrarySymbol extends SymbolDB {
return super.isValidParent(parent) && return super.isValidParent(parent) &&
SymbolType.LIBRARY.isValidParent(symbolMgr.getProgram(), parent, address, isExternal()); 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 { class NamespaceDB implements Namespace {
private SymbolDB symbol; private NamespaceSymbol symbol;
private NamespaceManager namespaceMgr; private NamespaceManager namespaceMgr;
/** /**
@ -36,62 +36,46 @@ class NamespaceDB implements Namespace {
* @param symbol the symbol associated with this namespace. * @param symbol the symbol associated with this namespace.
* @param namespaceMgr the namespace manager * @param namespaceMgr the namespace manager
*/ */
NamespaceDB(SymbolDB symbol, NamespaceManager namespaceMgr) { NamespaceDB(NamespaceSymbol symbol, NamespaceManager namespaceMgr) {
this.symbol = symbol; this.symbol = symbol;
this.namespaceMgr = namespaceMgr; this.namespaceMgr = namespaceMgr;
} }
/**
* @see ghidra.program.model.symbol.Namespace#getSymbol()
*/
@Override @Override
public Symbol getSymbol() { public Symbol getSymbol() {
return symbol; return symbol;
} }
/**
* @see ghidra.program.model.symbol.Namespace#getName()
*/
@Override @Override
public String getName() { public String getName() {
return symbol.getName(); return symbol.getName();
} }
/**
* @see ghidra.program.model.symbol.Namespace#getID()
*/
@Override @Override
public long getID() { public long getID() {
return symbol.getID(); return symbol.getID();
} }
/**
* @see ghidra.program.model.symbol.Namespace#getParentNamespace()
*/
@Override @Override
public Namespace getParentNamespace() { public Namespace getParentNamespace() {
return symbol.getParentNamespace(); return symbol.getParentNamespace();
} }
/**
* @see ghidra.program.model.symbol.Namespace#getBody()
*/
@Override @Override
public AddressSetView getBody() { public AddressSetView getBody() {
return namespaceMgr.getAddressSet(this); return namespaceMgr.getAddressSet(this);
} }
/**
* @see ghidra.program.model.symbol.Namespace#getName(boolean)
*/
@Override @Override
public String getName(boolean includeNamespacePath) { public String getName(boolean includeNamespacePath) {
return symbol.getName(includeNamespacePath); return symbol.getName(includeNamespacePath);
} }
/** @Override
* @see java.lang.Object#equals(java.lang.Object) public int hashCode() {
*/ return symbol.hashCode();
}
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj == null) { if (obj == null) {
@ -105,18 +89,12 @@ class NamespaceDB implements Namespace {
return symbol == nameSpace.symbol; return symbol == nameSpace.symbol;
} }
/**
* @see ghidra.program.model.symbol.Namespace#setParentNamespace(ghidra.program.model.symbol.Namespace)
*/
@Override @Override
public void setParentNamespace(Namespace parentNamespace) throws DuplicateNameException, public void setParentNamespace(Namespace parentNamespace)
InvalidInputException, CircularDependencyException { throws DuplicateNameException, InvalidInputException, CircularDependencyException {
symbol.setNamespace(parentNamespace); symbol.setNamespace(parentNamespace);
} }
/**
* @see java.lang.Object#toString()
*/
@Override @Override
public String toString() { public String toString() {
return getName(true); return getName(true);

View file

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

View file

@ -16,7 +16,8 @@
package ghidra.program.database.symbol; package ghidra.program.database.symbol;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import db.DBRecord; import db.DBRecord;
import db.Field; import db.Field;
@ -34,16 +35,15 @@ import ghidra.util.Lock;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.UnknownProgressWrappingTaskMonitor;
/** /**
* Base class for symbols * Base class for symbols
*/ */
public abstract class SymbolDB extends DatabaseObject implements Symbol { public abstract class SymbolDB extends DatabaseObject implements Symbol {
private DBRecord record;
private boolean isDeleting = false; private boolean isDeleting = false;
protected DBRecord record;
protected Address address; protected Address address;
protected SymbolManager symbolMgr; protected SymbolManager symbolMgr;
protected Lock lock; protected Lock lock;
@ -132,48 +132,6 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
addr); 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 @Override
public final String getName() { public final String getName() {
String name = cachedName; 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 @Override
public boolean isDynamic() { public boolean isDynamic() {
return (record == null); return (record == null);
} }
@Override
public boolean isExternalEntryPoint() {
lock.acquire();
try {
checkIsValid();
return symbolMgr.isExternalEntryPoint(address);
}
finally {
lock.release();
}
}
@Override @Override
public abstract boolean isPrimary(); 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 flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
byte clearBits = SymbolDatabaseAdapter.SYMBOL_SOURCE_BITS; byte clearBits = SymbolDatabaseAdapter.SYMBOL_SOURCE_BITS;
byte setBits = (byte) newSource.ordinal(); 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 @Override
public void setName(String newName, SourceType source) public void setName(String newName, SourceType source)
throws DuplicateNameException, InvalidInputException { 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); record.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, newName);
updateSymbolSource(record, source); updateSymbolSource(record, source);
updateRecord(); updateRecord();
@ -750,7 +508,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return (int) key; return (int) key;
} }
private void updateRecord() { protected void updateRecord() {
try { try {
symbolMgr.getDatabaseAdapter().updateSymbolRecord(record); symbolMgr.getDatabaseAdapter().updateSymbolRecord(record);
} }
@ -781,7 +539,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return null; return null;
} }
return symbolMgr return symbolMgr
.getSymbol(record.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL)); .getSymbol(record.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL));
} }
finally { finally {
lock.release(); lock.release();
@ -795,7 +553,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
if (record == null) { if (record == null) {
return Namespace.GLOBAL_NAMESPACE_ID; return Namespace.GLOBAL_NAMESPACE_ID;
} }
return record.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL); return record.getLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL);
} }
finally { finally {
lock.release(); lock.release();
@ -811,51 +569,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return true; return true;
} }
return record.getLongValue( return record.getLongValue(
SymbolDatabaseAdapter.SYMBOL_PARENT_COL) == Namespace.GLOBAL_NAMESPACE_ID; SymbolDatabaseAdapter.SYMBOL_PARENT_ID_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);
} }
finally { finally {
lock.release(); lock.release();
@ -863,21 +577,13 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
} }
public long getDataTypeId() { public long getDataTypeId() {
lock.acquire(); validate(lock);
try { // record always present when use of datatype ID is supported (i.e., external location)
checkIsValid(); Field value = record.getFieldValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL);
if (record != null) { if (value.isNull()) {
Field value = record.getFieldValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL);
if (value.isNull()) {
return -1;
}
return value.getLongValue();
}
return -1; return -1;
} }
finally { return value.getLongValue();
lock.release();
}
} }
/** /**
@ -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) { protected void doSetPrimary(boolean primary) {
lock.acquire(); lock.acquire();
try { try {
@ -1023,4 +691,5 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
this.record = record; this.record = record;
keyChanged(record.getKey()); keyChanged(record.getKey());
} }
} }

View file

@ -39,24 +39,37 @@ abstract class SymbolDatabaseAdapter {
static final int SYMBOL_NAME_COL = 0; static final int SYMBOL_NAME_COL = 0;
static final int SYMBOL_ADDR_COL = 1; 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_TYPE_COL = 3;
static final int SYMBOL_STRING_DATA_COL = 4; static final int SYMBOL_FLAGS_COL = 4;
static final int SYMBOL_FLAGS_COL = 5;
// 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. // don't consume space in the database if they aren't used.
static final int SYMBOL_HASH_COL = 6; static final int SYMBOL_HASH_COL = 5;
static final int SYMBOL_PRIMARY_COL = 7; static final int SYMBOL_PRIMARY_COL = 6;
static final int SYMBOL_DATATYPE_COL = 8; static final int SYMBOL_DATATYPE_COL = 7;
static final int SYMBOL_VAROFFSET_COL = 9; 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. // Bits 0 & 1 are used for the source of the symbol.
static final byte SYMBOL_SOURCE_BITS = (byte) 0x3; static final byte SYMBOL_SOURCE_BITS = (byte) 0x3;
static final byte SYMBOL_PINNED_FLAG = (byte) 0x4; // Bit 2 is flag for "anchored to address". 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 // TODO: NEXT UPGRADE: remove all variable/parameter symbols with NO_ADDRESS
/** /**
@ -75,11 +88,11 @@ abstract class SymbolDatabaseAdapter {
throws VersionException, CancelledException, IOException { throws VersionException, CancelledException, IOException {
if (openMode == OpenMode.CREATE) { if (openMode == OpenMode.CREATE) {
return new SymbolDatabaseAdapterV3(dbHandle, addrMap, true); return new SymbolDatabaseAdapterV4(dbHandle, addrMap, true);
} }
try { try {
SymbolDatabaseAdapter adapter = new SymbolDatabaseAdapterV3(dbHandle, addrMap, false); SymbolDatabaseAdapter adapter = new SymbolDatabaseAdapterV4(dbHandle, addrMap, false);
return adapter; return adapter;
} }
catch (VersionException e) { catch (VersionException e) {
@ -99,26 +112,33 @@ abstract class SymbolDatabaseAdapter {
} }
private static SymbolDatabaseAdapter findReadOnlyAdapter(DBHandle handle, AddressMap addrMap) 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 { try {
return new SymbolDatabaseAdapterV2(handle, addrMap.getOldAddressMap()); return new SymbolDatabaseAdapterV2(handle, addrMap.getOldAddressMap());
} }
catch (VersionException e1) { catch (VersionException e) {
// failed try older version // failed try older version
} }
try { try {
return new SymbolDatabaseAdapterV1(handle, addrMap.getOldAddressMap()); return new SymbolDatabaseAdapterV1(handle, addrMap.getOldAddressMap());
} }
catch (VersionException e1) { catch (VersionException e) {
// failed try older version // failed try older version
} }
try { try {
return new SymbolDatabaseAdapterV0(handle, addrMap.getOldAddressMap()); return new SymbolDatabaseAdapterV0(handle, addrMap.getOldAddressMap());
} }
catch (VersionException e1) { catch (VersionException e) {
// failed - can't handle whatever version this is trying to open // failed - can't handle whatever version this is trying to open
} }
@ -140,7 +160,7 @@ abstract class SymbolDatabaseAdapter {
dbHandle.deleteTable(SYMBOL_TABLE_NAME); dbHandle.deleteTable(SYMBOL_TABLE_NAME);
SymbolDatabaseAdapter newAdapter = new SymbolDatabaseAdapterV3(dbHandle, addrMap, true); SymbolDatabaseAdapter newAdapter = new SymbolDatabaseAdapterV4(dbHandle, addrMap, true);
copyTempToNewAdapter(tmpAdapter, newAdapter, monitor); copyTempToNewAdapter(tmpAdapter, newAdapter, monitor);
return newAdapter; return newAdapter;
@ -166,7 +186,7 @@ abstract class SymbolDatabaseAdapter {
((SymbolDatabaseAdapterV0) oldAdapter).extractLocalSymbols(tmpHandle, monitor); ((SymbolDatabaseAdapterV0) oldAdapter).extractLocalSymbols(tmpHandle, monitor);
} }
SymbolDatabaseAdapterV3 tmpAdapter = new SymbolDatabaseAdapterV3(tmpHandle, addrMap, true); SymbolDatabaseAdapterV4 tmpAdapter = new SymbolDatabaseAdapterV4(tmpHandle, addrMap, true);
RecordIterator iter = oldAdapter.getSymbols(); RecordIterator iter = oldAdapter.getSymbols();
while (iter.hasNext()) { while (iter.hasNext()) {
monitor.checkCancelled(); 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 name name of the symbol
* @param address the address for the symbol
* @param namespaceID the id of the containing namespace 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 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 * @param source the source type of this symbol
* Some symbol types, such as function symbols, can set the source to Symbol.DEFAULT * 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 * @return new symbol record (not yet added to symbol table)
* @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
*/ */
abstract DBRecord createSymbol(String name, Address address, long namespaceID, abstract DBRecord createSymbolRecord(String name, long namespaceID, Address address,
SymbolType symbolType, String stringData, Long dataTypeId, Integer varOffset, SymbolType symbolType, boolean isPrimary, SourceType source);
SourceType source, boolean isPrimary) throws IOException;
/** /**
* Get the record with the given symbol ID * Get the record with the given symbol ID
@ -372,10 +388,29 @@ abstract class SymbolDatabaseAdapter {
* This only includes memory-based stored symbols. * This only includes memory-based stored symbols.
* *
* @param startName the starting name to search * @param startName the starting name to search
* @return a symbol record iterator over the symbols
* @throws IOException if a database io error occurs * @throws IOException if a database io error occurs
*/ */
abstract RecordIterator scanSymbolsByName(String startName) throws IOException; 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 * Get all symbols contained in the given {@link Namespace} that have the given name
* @param name the symbol name * @param name the symbol name
@ -439,7 +474,8 @@ abstract class SymbolDatabaseAdapter {
protected static RecordIterator getNameAndNamespaceFilterIterator(String name, long namespaceId, protected static RecordIterator getNameAndNamespaceFilterIterator(String name, long namespaceId,
RecordIterator it) { RecordIterator it) {
Query nameQuery = new FieldMatchQuery(SYMBOL_NAME_COL, new StringField(name)); 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); Query nameAndNamespaceQuery = new AndQuery(nameQuery, namespaceQuery);
return new QueryRecordIterator(it, nameAndNamespaceQuery); return new QueryRecordIterator(it, nameAndNamespaceQuery);
} }
@ -457,7 +493,8 @@ abstract class SymbolDatabaseAdapter {
protected static RecordIterator getNameNamespaceAddressFilterIterator(String name, protected static RecordIterator getNameNamespaceAddressFilterIterator(String name,
long namespaceId, long addressKey, RecordIterator it) { long namespaceId, long addressKey, RecordIterator it) {
Query nameQuery = new FieldMatchQuery(SYMBOL_NAME_COL, new StringField(name)); 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 addressQuery = new FieldMatchQuery(SYMBOL_ADDR_COL, new LongField(addressKey));
Query nameAndNamespaceQuery = new AndQuery(nameQuery, namespaceQuery); Query nameAndNamespaceQuery = new AndQuery(nameQuery, namespaceQuery);
Query fullQuery = new AndQuery(nameAndNamespaceQuery, addressQuery); Query fullQuery = new AndQuery(nameAndNamespaceQuery, addressQuery);

View file

@ -21,6 +21,7 @@ import java.util.Set;
import db.*; import db.*;
import ghidra.program.database.map.AddressIndexPrimaryKeyIterator; import ghidra.program.database.map.AddressIndexPrimaryKeyIterator;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.database.util.EmptyRecordIterator;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.util.exception.*; import ghidra.util.exception.*;
@ -105,7 +106,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
return symbolTable.getKey(); return symbolTable.getKey();
} }
private DBRecord convertRecord(DBRecord record) { private DBRecord convertV0Record(DBRecord record) {
if (record == null) { if (record == null) {
return null; return null;
} }
@ -118,32 +119,34 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
String symbolName = record.getString(V0_SYMBOL_NAME_COL); String symbolName = record.getString(V0_SYMBOL_NAME_COL);
rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, symbolName); rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, symbolName);
long addressKey = record.getLongValue(V0_SYMBOL_ADDR_COL); long addressKey = record.getLongValue(V0_SYMBOL_ADDR_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, addressKey);
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); boolean isPrimary = record.getBooleanValue(V0_SYMBOL_PRIMARY_COL);
if (isPrimary) { if (isPrimary) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, addressKey); 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; return rec;
} }
@Override @Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType, DBRecord createSymbolRecord(String name, long namespaceID, Address address,
String stringData, Long dataTypeId, Integer varOffset, SourceType source, SymbolType symbolType, boolean isPrimary, SourceType source) {
boolean isPrimary) throws IOException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -164,7 +167,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
@Override @Override
DBRecord getSymbolRecord(long symbolID) throws IOException { DBRecord getSymbolRecord(long symbolID) throws IOException {
return convertRecord(symbolTable.getRecord(symbolID)); return convertV0Record(symbolTable.getRecord(symbolID));
} }
@Override @Override
@ -210,8 +213,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
} }
@Override @Override
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) throws IOException {
throws IOException {
KeyToRecordIterator it = KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward)); SYMBOL_ADDR_COL, addrMap, set, forward));
@ -240,8 +242,17 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
} }
@Override @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) { if (id == Namespace.GLOBAL_NAMESPACE_ID) {
return new V0ConvertedRecordIterator(symbolTable.iterator()); return new V0ConvertedRecordIterator(symbolTable.iterator());
} }
@ -299,7 +310,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
if (hasNext()) { if (hasNext()) {
DBRecord r = rec; DBRecord r = rec;
rec = null; rec = null;
return convertRecord(r); return convertV0Record(r);
} }
return null; return null;
} }
@ -337,8 +348,7 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
StringField value = new StringField(name); StringField value = new StringField(name);
RecordIterator it = symbolTable.indexIterator(SYMBOL_NAME_COL, value, value, true); RecordIterator it = symbolTable.indexIterator(SYMBOL_NAME_COL, value, value, true);
long addressKey = addrMap.getKey(address, false); long addressKey = addrMap.getKey(address, false);
RecordIterator filtered = RecordIterator filtered = getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
if (filtered.hasNext()) { if (filtered.hasNext()) {
return filtered.next(); return filtered.next();
} }

View file

@ -21,6 +21,7 @@ import java.util.Set;
import db.*; import db.*;
import ghidra.program.database.map.AddressIndexPrimaryKeyIterator; import ghidra.program.database.map.AddressIndexPrimaryKeyIterator;
import ghidra.program.database.map.AddressMap; import ghidra.program.database.map.AddressMap;
import ghidra.program.database.util.EmptyRecordIterator;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.symbol.*; import ghidra.program.model.symbol.*;
import ghidra.util.exception.CancelledException; 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_ADDR_COL = 1;
private static final int V1_SYMBOL_PARENT_COL = 2; private static final int V1_SYMBOL_PARENT_COL = 2;
private static final int V1_SYMBOL_TYPE_COL = 3; private static final int V1_SYMBOL_TYPE_COL = 3;
private static final int V1_SYMBOL_DATA1_COL = 4; private static final int V1_SYMBOL_DATA1_COL = 4; // Long data (variable dataTypeId)
private static final int V1_SYMBOL_DATA2_COL = 5; 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 static final int V1_SYMBOL_COMMENT_COL = 6;
private Table symbolTable; private Table symbolTable;
@ -70,9 +71,8 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
} }
@Override @Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType, DBRecord createSymbolRecord(String name, long namespaceID, Address address,
String stringData, Long dataTypeId, Integer varOffset, SourceType source, SymbolType symbolType, boolean isPrimary, SourceType source) {
boolean isPrimary) throws IOException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -121,14 +121,11 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, symbolAddrKey); rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, symbolAddrKey);
long namespaceId = record.getLongValue(V1_SYMBOL_PARENT_COL); 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); byte symbolTypeId = record.getByteValue(V1_SYMBOL_TYPE_COL);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, symbolTypeId); 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; SourceType source = SourceType.USER_DEFINED;
if (symbolTypeId == SymbolType.FUNCTION.getID()) { if (symbolTypeId == SymbolType.FUNCTION.getID()) {
Address symbolAddress = addrMap.decodeAddress(symbolAddrKey); Address symbolAddress = addrMap.decodeAddress(symbolAddrKey);
@ -139,6 +136,13 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
} }
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, (byte) source.ordinal()); 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); long dataTypeId = record.getLongValue(V1_SYMBOL_DATA1_COL);
if (dataTypeId != -1) { if (dataTypeId != -1) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL, dataTypeId); rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL, dataTypeId);
@ -161,7 +165,9 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
// also need to store primary for functions // also need to store primary for functions
if (SymbolType.FUNCTION.equals(type)) { if (SymbolType.FUNCTION.equals(type)) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, symbolAddrKey); rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, symbolAddrKey);
} }
Field hash = computeLocatorHash(symbolName, namespaceId, symbolAddrKey); Field hash = computeLocatorHash(symbolName, namespaceId, symbolAddrKey);
rec.setField(SymbolDatabaseAdapter.SYMBOL_HASH_COL, hash); rec.setField(SymbolDatabaseAdapter.SYMBOL_HASH_COL, hash);
@ -211,8 +217,7 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
} }
@Override @Override
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) throws IOException {
throws IOException {
KeyToRecordIterator it = KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward)); SYMBOL_ADDR_COL, addrMap, set, forward));
@ -240,6 +245,16 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
throw new UnsupportedOperationException(); 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 @Override
RecordIterator getSymbolsByNamespace(long id) throws IOException { RecordIterator getSymbolsByNamespace(long id) throws IOException {
LongField field = new LongField(id); LongField field = new LongField(id);
@ -293,8 +308,7 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
DBRecord getSymbolRecord(Address address, String name, long id) throws IOException { DBRecord getSymbolRecord(Address address, String name, long id) throws IOException {
RecordIterator it = getSymbolsByName(name); RecordIterator it = getSymbolsByName(name);
long addressKey = addrMap.getKey(address, false); long addressKey = addrMap.getKey(address, false);
RecordIterator filtered = RecordIterator filtered = getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
getNameNamespaceAddressFilterIterator(name, id, addressKey, it);
if (filtered.hasNext()) { if (filtered.hasNext()) {
return filtered.next(); return filtered.next();
} }

View file

@ -16,12 +16,11 @@
package ghidra.program.database.symbol; package ghidra.program.database.symbol;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import db.*; import db.*;
import ghidra.program.database.map.*; 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.address.*;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolType; import ghidra.program.model.symbol.SymbolType;
@ -43,20 +42,20 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
// "SymbolData3", "Flags" }); // "SymbolData3", "Flags" });
private static final int SYMBOL_VERSION = 2; 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 Table symbolTable;
private AddressMap addrMap; private AddressMap addrMap;
static final int V2_SYMBOL_NAME_COL = 0; SymbolDatabaseAdapterV2(DBHandle handle, AddressMap addrMap) throws VersionException {
static final int V2_SYMBOL_ADDR_COL = 1;
static final int V2_SYMBOL_PARENT_COL = 2;
static final int V2_SYMBOL_TYPE_COL = 3;
static final int V2_SYMBOL_DATA1_COL = 4;
static final int V2_SYMBOL_DATA2_COL = 5;
static final int V2_SYMBOL_DATA3_COL = 6;
static final int V2_SYMBOL_FLAGS_COL = 7;
SymbolDatabaseAdapterV2(DBHandle handle, AddressMap addrMap)
throws VersionException {
this.addrMap = addrMap; this.addrMap = addrMap;
symbolTable = handle.getTable(SYMBOL_TABLE_NAME); symbolTable = handle.getTable(SYMBOL_TABLE_NAME);
@ -73,9 +72,8 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
} }
@Override @Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType, DBRecord createSymbolRecord(String name, long namespaceID, Address address,
String stringData, Long dataTypeId, Integer varOffset, SourceType source, SymbolType symbolType, boolean isPrimary, SourceType source) {
boolean isPrimary) throws IOException {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -154,8 +152,7 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
} }
@Override @Override
RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) throws IOException {
throws IOException {
KeyToRecordIterator it = KeyToRecordIterator it =
new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward)); SYMBOL_ADDR_COL, addrMap, set, forward));
@ -174,51 +171,58 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
@Override @Override
void moveAddress(Address oldAddr, Address newAddr) throws IOException { void moveAddress(Address oldAddr, Address newAddr) throws IOException {
LongField oldKey = new LongField(addrMap.getKey(oldAddr, false)); throw new UnsupportedOperationException();
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 @Override
Set<Address> deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor) Set<Address> deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
throws CancelledException, IOException { throws CancelledException, IOException {
throw new UnsupportedOperationException();
AnchoredSymbolRecordFilter filter = new AnchoredSymbolRecordFilter();
AddressRecordDeleter.deleteRecords(symbolTable, SYMBOL_ADDR_COL, addrMap, startAddr,
endAddr, filter);
return filter.getAddressesForSkippedRecords();
} }
class AnchoredSymbolRecordFilter implements RecordFilter { private String getExternalStringData(DBRecord rec) {
private Set<Address> set = new HashSet<Address>(); 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 return rec.getString(V2_SYMBOL_DATA3_COL);
public boolean matches(DBRecord record) { }
// only move symbols whose anchor flag is not on
Address addr = addrMap.decodeAddress(record.getLongValue(SYMBOL_ADDR_COL)); @Override
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL); RecordIterator getExternalSymbolsByMemoryAddress(Address extProgAddr) throws IOException {
if (((flags & SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG) == 0)) { if (extProgAddr == null) {
return true; 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 null;
return false; });
} }
Set<Address> getAddressesForSkippedRecords() { @Override
return set; RecordIterator getExternalSymbolsByOriginalImportName(String extLabel) throws IOException {
} return EmptyRecordIterator.INSTANCE;
} }
@Override @Override
RecordIterator getSymbolsByNamespace(long id) throws IOException { RecordIterator getSymbolsByNamespace(long id) throws IOException {
LongField field = new LongField(id); 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); return new V2ConvertedRecordIterator(it);
} }
@ -236,6 +240,24 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
return new V2ConvertedRecordIterator(it); 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 @Override
Address getMaxSymbolAddress(AddressSpace space) throws IOException { Address getMaxSymbolAddress(AddressSpace space) throws IOException {
if (space.isMemorySpace()) { if (space.isMemorySpace()) {
@ -265,27 +287,6 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
return symbolTable; 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. * Returns a record matching the current database schema from the version 2 record.
* @param record the record matching the version 2 schema. * @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); long symbolAddrKey = record.getLongValue(V2_SYMBOL_ADDR_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, symbolAddrKey); rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, symbolAddrKey);
long namespaceId = record.getLongValue(V2_SYMBOL_PARENT_COL); long namespaceId = record.getLongValue(V2_SYMBOL_PARENT_ID_COL);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, namespaceId); rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL, namespaceId);
byte symbolTypeId = record.getByteValue(V2_SYMBOL_TYPE_COL); byte symbolTypeId = record.getByteValue(V2_SYMBOL_TYPE_COL);
rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, symbolTypeId); 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)); record.getString(V2_SYMBOL_DATA3_COL));
Field hash = computeLocatorHash(symbolName, namespaceId, symbolAddrKey); Field hash = computeLocatorHash(symbolName, namespaceId, symbolAddrKey);
rec.setField(SymbolDatabaseAdapter.SYMBOL_HASH_COL, hash); 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); long dataTypeId = record.getLongValue(V2_SYMBOL_DATA1_COL);
if (dataTypeId != -1) { if (dataTypeId != -1) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL, dataTypeId); rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_DATATYPE_COL, dataTypeId);
@ -325,7 +330,7 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
SymbolType type = SymbolType.getSymbolType(symbolTypeId); SymbolType type = SymbolType.getSymbolType(symbolTypeId);
int data2 = record.getIntValue(V2_SYMBOL_DATA2_COL); 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 // for non-primary. If the type was a parameter or variable, it stored the ordinal or
// first use offset respectively // first use offset respectively
if (SymbolType.LABEL.equals(type)) { if (SymbolType.LABEL.equals(type)) {

View file

@ -16,13 +16,13 @@
package ghidra.program.database.symbol; package ghidra.program.database.symbol;
import java.io.IOException; import java.io.IOException;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import db.*; import db.*;
import ghidra.program.database.map.*; import ghidra.program.database.map.*;
import ghidra.program.database.util.EmptyRecordIterator; import ghidra.program.database.util.EmptyRecordIterator;
import ghidra.program.database.util.RecordFilter;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.model.symbol.SymbolType; import ghidra.program.model.symbol.SymbolType;
@ -33,9 +33,10 @@ import ghidra.util.task.TaskMonitor;
/** /**
* SymbolDatabaseAdapter for version 3 * 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 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 { 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 // 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. // so that non-primary symbols don't consume any space for this field.
static final Schema V3_SYMBOL_SCHEMA = new Schema(SYMBOL_VERSION, "Key", /* Do not remove the following commented out schema! It shows the version 3 symbol table schema. */
new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, // static final Schema V3_SYMBOL_SCHEMA = new Schema(SYMBOL_VERSION, "Key",
ByteField.INSTANCE, StringField.INSTANCE, ByteField.INSTANCE, // new Field[] { StringField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE,
LongField.INSTANCE, LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE }, // ByteField.INSTANCE, StringField.INSTANCE, ByteField.INSTANCE, LongField.INSTANCE,
new String[] { "Name", "Address", "Namespace", "Symbol Type", "String Data", "Flags", // LongField.INSTANCE, LongField.INSTANCE, IntField.INSTANCE },
"Locator Hash", "Primary", "Datatype", "Variable Offset" }, // new String[] { "Name", "Address", "Namespace", "Symbol Type", "String Data", "Flags",
new int[] { SYMBOL_HASH_COL, SYMBOL_PRIMARY_COL, SYMBOL_DATATYPE_COL, // "Locator Hash", "Primary", "Datatype", "Variable Offset" },
SYMBOL_VAROFFSET_COL }); // 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 Table symbolTable;
private AddressMap addrMap; private AddressMap addrMap;
SymbolDatabaseAdapterV3(DBHandle handle, AddressMap addrMap, boolean create) SymbolDatabaseAdapterV3(DBHandle handle, AddressMap addrMap) throws VersionException {
throws VersionException, IOException {
this.addrMap = addrMap; this.addrMap = addrMap;
if (create) { symbolTable = handle.getTable(SYMBOL_TABLE_NAME);
symbolTable = handle.createTable(SYMBOL_TABLE_NAME, SYMBOL_SCHEMA, if (symbolTable == null) {
new int[] { SYMBOL_ADDR_COL, SYMBOL_NAME_COL, SYMBOL_PARENT_COL, SYMBOL_HASH_COL, throw new VersionException("Missing Table: " + SYMBOL_TABLE_NAME);
SYMBOL_PRIMARY_COL });
} }
else { if (symbolTable.getSchema().getVersion() != SYMBOL_VERSION) {
symbolTable = handle.getTable(SYMBOL_TABLE_NAME); int version = symbolTable.getSchema().getVersion();
if (symbolTable == null) { if (version < SYMBOL_VERSION) {
throw new VersionException("Missing Table: " + SYMBOL_TABLE_NAME); throw new VersionException(true);
}
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);
} }
throw new VersionException(VersionException.NEWER_VERSION, false);
} }
} }
@Override @Override
DBRecord createSymbol(String name, Address address, long namespaceID, SymbolType symbolType, DBRecord createSymbolRecord(String name, long namespaceID, Address address,
String stringData, Long dataTypeId, Integer varOffset, SourceType source, SymbolType symbolType, boolean isPrimary, SourceType source) {
boolean isPrimary) throws IOException { throw new UnsupportedOperationException();
long nextID = symbolTable.getKey();
// avoiding key 0, as it is reserved for the global namespace
if (nextID == 0) {
nextID++;
}
return createSymbol(nextID, name, address, namespaceID, symbolType, stringData,
(byte) source.ordinal(), dataTypeId, varOffset, isPrimary);
}
private DBRecord createSymbol(long id, String name, Address address, long namespaceID,
SymbolType symbolType, String stringData, byte flags,
Long dataTypeId, Integer varOffset, boolean isPrimary) throws IOException {
long addressKey = addrMap.getKey(address, true);
DBRecord rec = symbolTable.getSchema().createRecord(id);
rec.setString(SYMBOL_NAME_COL, name);
rec.setLongValue(SYMBOL_ADDR_COL, addressKey);
rec.setLongValue(SYMBOL_PARENT_COL, namespaceID);
rec.setByteValue(SYMBOL_TYPE_COL, symbolType.getID());
rec.setString(SYMBOL_STRING_DATA_COL, stringData);
rec.setByteValue(SYMBOL_FLAGS_COL, flags);
// sparse columns - these columns don't apply to all symbols.
// they default to null unless specifically set. Null values don't consume space.
rec.setField(SYMBOL_HASH_COL,
computeLocatorHash(name, namespaceID, addressKey));
if (isPrimary) {
rec.setLongValue(SYMBOL_PRIMARY_COL, addressKey);
}
if (dataTypeId != null) {
rec.setLongValue(SYMBOL_DATATYPE_COL, dataTypeId);
}
if (varOffset != null) {
rec.setIntValue(SYMBOL_VAROFFSET_COL, varOffset);
}
symbolTable.putRecord(rec);
return rec;
} }
@Override @Override
void removeSymbol(long symbolID) throws IOException { void removeSymbol(long symbolID) throws IOException {
symbolTable.deleteRecord(symbolID); throw new UnsupportedOperationException();
} }
@Override @Override
@ -160,7 +124,7 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
@Override @Override
DBRecord getSymbolRecord(long symbolID) throws IOException { DBRecord getSymbolRecord(long symbolID) throws IOException {
return symbolTable.getRecord(symbolID); return convertV3Record(symbolTable.getRecord(symbolID));
} }
@Override @Override
@ -170,49 +134,52 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
@Override @Override
RecordIterator getSymbolsByAddress(boolean forward) throws IOException { RecordIterator getSymbolsByAddress(boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, KeyToRecordIterator it = new KeyToRecordIterator(symbolTable,
new AddressIndexPrimaryKeyIterator(symbolTable, SYMBOL_ADDR_COL, addrMap, forward)); new AddressIndexPrimaryKeyIterator(symbolTable, SYMBOL_ADDR_COL, addrMap, forward));
return new V3ConvertedRecordIterator(it);
} }
@Override @Override
RecordIterator getSymbolsByAddress(Address startAddr, boolean forward) throws IOException { RecordIterator getSymbolsByAddress(Address startAddr, boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, KeyToRecordIterator it =
SYMBOL_ADDR_COL, addrMap, startAddr, forward)); new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, startAddr, forward));
return new V3ConvertedRecordIterator(it);
} }
@Override @Override
void updateSymbolRecord(DBRecord record) throws IOException { void updateSymbolRecord(DBRecord record) throws IOException {
// make sure hash is updated to current name and name space throw new UnsupportedOperationException();
String name = record.getString(SYMBOL_NAME_COL);
long namespaceId = record.getLongValue(SYMBOL_PARENT_COL);
long addressKey = record.getLongValue(SYMBOL_ADDR_COL);
record.setField(SYMBOL_HASH_COL,
computeLocatorHash(name, namespaceId, addressKey));
symbolTable.putRecord(record);
} }
@Override @Override
RecordIterator getSymbols() throws IOException { RecordIterator getSymbols() throws IOException {
return symbolTable.iterator(); return new V3ConvertedRecordIterator(symbolTable.iterator());
} }
@Override @Override
RecordIterator getSymbols(Address start, Address end, boolean forward) throws IOException { RecordIterator getSymbols(Address start, Address end, boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, KeyToRecordIterator it =
SYMBOL_ADDR_COL, addrMap, start, end, forward)); new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, start, end, forward));
return new V3ConvertedRecordIterator(it);
} }
@Override @Override
RecordIterator getSymbols(AddressSetView set, boolean forward) throws IOException { RecordIterator getSymbols(AddressSetView set, boolean forward) throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, KeyToRecordIterator it =
SYMBOL_ADDR_COL, addrMap, set, forward)); new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_ADDR_COL, addrMap, set, forward));
return new V3ConvertedRecordIterator(it);
} }
@Override @Override
protected RecordIterator getPrimarySymbols(AddressSetView set, boolean forward) protected RecordIterator getPrimarySymbols(AddressSetView set, boolean forward)
throws IOException { throws IOException {
return new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable, KeyToRecordIterator it =
SYMBOL_PRIMARY_COL, addrMap, set, forward)); new KeyToRecordIterator(symbolTable, new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_PRIMARY_COL, addrMap, set, forward));
return new V3ConvertedRecordIterator(it);
} }
@Override @Override
@ -220,54 +187,92 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
AddressIndexPrimaryKeyIterator it = new AddressIndexPrimaryKeyIterator(symbolTable, AddressIndexPrimaryKeyIterator it = new AddressIndexPrimaryKeyIterator(symbolTable,
SYMBOL_PRIMARY_COL, addrMap, address, address, true); SYMBOL_PRIMARY_COL, addrMap, address, address, true);
if (it.hasNext()) { if (it.hasNext()) {
return symbolTable.getRecord(it.next()); return convertV3Record(symbolTable.getRecord(it.next()));
} }
return null; return null;
} }
void deleteExternalEntries(Address start, Address end) throws IOException {
AddressRecordDeleter.deleteRecords(symbolTable, SYMBOL_ADDR_COL, addrMap, start, end, null);
}
@Override @Override
void moveAddress(Address oldAddr, Address newAddr) throws IOException { void moveAddress(Address oldAddr, Address newAddr) throws IOException {
LongField oldKey = new LongField(addrMap.getKey(oldAddr, false)); throw new UnsupportedOperationException();
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 @Override
Set<Address> deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor) Set<Address> deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
throws CancelledException, IOException { throws CancelledException, IOException {
throw new UnsupportedOperationException();
}
AnchoredSymbolRecordFilter filter = new AnchoredSymbolRecordFilter(); private String getExternalStringData(DBRecord rec) {
AddressRecordDeleter.deleteRecords(symbolTable, SYMBOL_ADDR_COL, addrMap, startAddr, long addrKey = rec.getLongValue(V3_SYMBOL_ADDR_COL);
endAddr, filter); 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 @Override
RecordIterator getSymbolsByNamespace(long id) throws IOException { RecordIterator getSymbolsByNamespace(long id) throws IOException {
LongField field = new LongField(id); 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 @Override
RecordIterator getSymbolsByName(String name) throws IOException { RecordIterator getSymbolsByName(String name) throws IOException {
StringField field = new StringField(name); 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 @Override
RecordIterator scanSymbolsByName(String startName) throws IOException { RecordIterator scanSymbolsByName(String startName) throws IOException {
StringField field = new StringField(startName); 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 @Override
@ -282,7 +287,10 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
Field end = computeLocatorHash(name, id, MAX_ADDRESS_OFFSET); Field end = computeLocatorHash(name, id, MAX_ADDRESS_OFFSET);
RecordIterator it = symbolTable.indexIterator(SYMBOL_HASH_COL, start, end, true); 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 @Override
@ -293,6 +301,8 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
return null; return null;
} }
RecordIterator it = symbolTable.indexIterator(SYMBOL_HASH_COL, search, search, true); RecordIterator it = symbolTable.indexIterator(SYMBOL_HASH_COL, search, search, true);
it = new V3ConvertedRecordIterator(it);
RecordIterator filtered = RecordIterator filtered =
getNameNamespaceAddressFilterIterator(name, namespaceId, addressKey, it); getNameNamespaceAddressFilterIterator(name, namespaceId, addressKey, it);
if (filtered.hasNext()) { if (filtered.hasNext()) {
@ -330,24 +340,96 @@ class SymbolDatabaseAdapterV3 extends SymbolDatabaseAdapter {
return symbolTable; 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 String symbolName = record.getString(V3_SYMBOL_NAME_COL);
public boolean matches(DBRecord record) { rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, symbolName);
// only move symbols whose anchor flag is not on
Address addr = addrMap.decodeAddress(record.getLongValue(SYMBOL_ADDR_COL)); long symbolAddrKey = record.getLongValue(V3_SYMBOL_ADDR_COL);
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL); rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, symbolAddrKey);
boolean pinned = (flags & SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG) != 0;
if (!pinned) { long namespaceId = record.getLongValue(V3_SYMBOL_PARENT_ID_COL);
return true; rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_ID_COL, namespaceId);
}
set.add(addr); byte symbolTypeId = record.getByteValue(V3_SYMBOL_TYPE_COL);
return false; 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() { Field primaryAddr = record.getFieldValue(V3_SYMBOL_PRIMARY_COL);
return set; 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) { catch (InvalidInputException e) {
Symbol parent = 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() + ":" + Msg.warn(this, "Variable symbol upgrade problem: " + parent.getName() + ":" +
rec.getString(SymbolDatabaseAdapter.SYMBOL_NAME_COL)); rec.getString(SymbolDatabaseAdapter.SYMBOL_NAME_COL));
} }
@ -356,7 +356,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
catch (InvalidInputException e) { catch (InvalidInputException e) {
Symbol parent = 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() + Msg.warn(this, "Variable symbol upgrade problem: " + parent.getName() +
":" + rec.getString(SymbolDatabaseAdapter.SYMBOL_NAME_COL)); ":" + rec.getString(SymbolDatabaseAdapter.SYMBOL_NAME_COL));
curVarAddr = null; curVarAddr = null;
@ -527,8 +527,11 @@ public class SymbolManager implements SymbolTable, ManagerDB {
Address address = symbol.getAddress(); Address address = symbol.getAddress();
symbolRemoved(symbol, address, symbol.getName(), oldKey, Namespace.GLOBAL_NAMESPACE_ID, symbolRemoved(symbol, address, symbol.getName(), oldKey, Namespace.GLOBAL_NAMESPACE_ID,
null); 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); symbol.setRecord(record);
symbolAdded(symbol); symbolAdded(symbol);
} }
@ -571,7 +574,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, name); rec.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, name);
long addressKey = addrMap.getKey(addr, true); long addressKey = addrMap.getKey(addr, true);
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL, addressKey); 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()); rec.setByteValue(SymbolDatabaseAdapter.SYMBOL_TYPE_COL, type.getID());
if (isPrimary) { if (isPrimary) {
rec.setLongValue(SymbolDatabaseAdapter.SYMBOL_PRIMARY_COL, addressKey); 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)); addrMap.decodeAddress(record.getLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL));
} }
if (type == SymbolType.CLASS) { 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) { 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) { else if (type == SymbolType.NAMESPACE) {
return new NamespaceSymbol(this, cache, addr, record); return new NamespaceSymbol(this, cache, record);
} }
else if (type == SymbolType.FUNCTION) { else if (type == SymbolType.FUNCTION) {
return new FunctionSymbol(this, cache, addr, record); return new FunctionSymbol(this, cache, addr, record);
} }
else if (type == SymbolType.LIBRARY) { 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) { else if (type == SymbolType.PARAMETER || type == SymbolType.LOCAL_VAR) {
return new VariableSymbolDB(this, cache, type, variableStorageMgr, addr, record); return new VariableSymbolDB(this, cache, type, variableStorageMgr, addr, record);
@ -970,12 +973,12 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
@Override @Override
public Symbol getLibrarySymbol(String name) { public LibrarySymbol getLibrarySymbol(String name) {
lock.acquire(); lock.acquire();
try { try {
for (Symbol s : getSymbols(name)) { for (Symbol s : getSymbols(name)) {
if (s.getSymbolType() == SymbolType.LIBRARY) { if (s.getSymbolType() == SymbolType.LIBRARY) {
return s; return (LibrarySymbol) s;
} }
} }
} }
@ -1111,6 +1114,83 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return null; 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 @Override
public Namespace getNamespace(String name, Namespace namespace) { public Namespace getNamespace(String name, Namespace namespace) {
List<Symbol> symbols = getSymbols(name, namespace); List<Symbol> symbols = getSymbols(name, namespace);
@ -1804,8 +1884,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
return true; return true;
} }
lock.acquire();
try { try {
lock.acquire();
while (nextSymbol == null && (forward ? it.hasNext() : it.hasPrevious())) { while (nextSymbol == null && (forward ? it.hasNext() : it.hasPrevious())) {
DBRecord rec = forward ? it.next() : it.previous(); DBRecord rec = forward ? it.next() : it.previous();
Symbol sym = getSymbol(rec); 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 class SymbolQueryIterator implements SymbolIterator {
private SymbolIterator it; private SymbolIterator it;
private Symbol nextMatch; private Symbol nextMatch;
@ -2204,6 +2344,45 @@ public class SymbolManager implements SymbolTable, ManagerDB {
dynamicSymbolAddressMap = new AddressMapImpl((byte) 0x40, addrMap.getAddressFactory()); dynamicSymbolAddressMap = new AddressMapImpl((byte) 0x40, addrMap.getAddressFactory());
invalidateCache(true); invalidateCache(true);
variableStorageMgr.setLanguage(translator, monitor); 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) { public void replaceDataTypes(Map<Long, Long> dataTypeReplacementMap) {
@ -2255,6 +2434,10 @@ public class SymbolManager implements SymbolTable, ManagerDB {
@Override @Override
public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor) public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
throws CancelledException { throws CancelledException {
if (!fromAddr.isMemoryAddress() || !toAddr.isMemoryAddress()) {
throw new IllegalArgumentException(
"moveAddressRange only supported for memory addresses");
}
if (fromAddr.equals(toAddr)) { if (fromAddr.equals(toAddr)) {
return; return;
} }
@ -2274,14 +2457,18 @@ public class SymbolManager implements SymbolTable, ManagerDB {
if (!range.contains(symbol.getAddress())) { if (!range.contains(symbol.getAddress())) {
break; 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 // any address that has symbols added or removed may have a corrupted primary
// (too many or non-existent) // (too many or non-existent)
primaryFixups.add(symbol.getAddress()); primaryFixups.add(memSym.getAddress());
primaryFixups.add(newAddress); primaryFixups.add(newAddress);
moveSymbolForMemoryBlockMove((SymbolDB) symbol, newAddress); moveSymbolForMemoryBlockMove(memSym, newAddress);
} }
// go back and make sure there is a valid primary symbol at touched addresses // go back and make sure there is a valid primary symbol at touched addresses
fixupPrimarySymbols(primaryFixups); 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 // 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 // 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. // to deal with is functions in the moved block that are currently pinned.
private void moveSymbolForMemoryBlockMove(SymbolDB symbol, Address newAddress) { private void moveSymbolForMemoryBlockMove(MemorySymbol memSymbol, Address newAddress) {
Address oldAddress = symbol.getAddress(); Address oldAddress = memSymbol.getAddress();
// If the symbol is not pinned go ahead and move it. Only wrinkle is that there may // 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 // be a matching symbol at the destination. In that unlikely event, remove it, but preserve
// its pinned status // 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) // 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. // and retain its pinned status if it had one.
boolean shouldPin = deleteMatchingSymbolAndCheckPinnedStatus(symbol, newAddress); boolean shouldPin = deleteMatchingSymbolAndCheckPinnedStatus(memSymbol, newAddress);
symbol.moveLowLevel(newAddress, null, null, null, shouldPin); memSymbol.moveLowLevel(newAddress, null, null, null, shouldPin);
moveLabelHistory(oldAddress, newAddress); moveLabelHistory(oldAddress, newAddress);
return; 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, // 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 // if there is a primary symbol at the destination, we need to remove it and set the
// function's symbol to that name. // function's symbol to that name.
if (symbol.getSymbolType() == SymbolType.FUNCTION) { if (memSymbol.getSymbolType() == SymbolType.FUNCTION) {
String originalName = symbol.getName(); String originalName = memSymbol.getName();
Namespace originalNamespace = symbol.getParentNamespace(); Namespace originalNamespace = memSymbol.getParentNamespace();
SourceType originalSource = symbol.getSource(); SourceType originalSource = memSymbol.getSource();
String newName = ""; String newName = "";
Namespace newNamespace = namespaceMgr.getGlobalNamespace(); Namespace newNamespace = namespaceMgr.getGlobalNamespace();
SourceType newSource = SourceType.DEFAULT; SourceType newSource = SourceType.DEFAULT;
@ -2335,7 +2522,7 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
try { try {
// so move the symbol to the new address and update namespace, source, and pinned state // 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. // create a pinned label to replace the pinned function symbol at the source.
Symbol newSymbol = Symbol newSymbol =
@ -2444,45 +2631,48 @@ public class SymbolManager implements SymbolTable, ManagerDB {
} }
public void imageBaseChanged(Address oldBase, Address newBase) { public void imageBaseChanged(Address oldBase, Address newBase) {
if (!oldBase.isLoadedMemoryAddress() || !newBase.isLoadedMemoryAddress()) {
throw new IllegalArgumentException("Loaded memory addresses required");
}
AddressSpace space = newBase.getAddressSpace(); AddressSpace space = newBase.getAddressSpace();
fixupPinnedSymbolsAfterRebase(oldBase, newBase, space.getMinAddress(), fixupPinnedMemorySymbolsAfterRebase(oldBase, newBase, space.getMinAddress(),
space.getMaxAddress()); space.getMaxAddress());
} }
private void fixupPinnedSymbolsAfterRebase(Address oldBase, Address base, Address minAddr, private void fixupPinnedMemorySymbolsAfterRebase(Address oldBase, Address base, Address minAddr,
Address maxAddr) { Address maxAddr) {
List<SymbolDB> fixupPinnedSymbols = findPinnedSymbols(minAddr, maxAddr); List<MemorySymbol> fixupPinnedSymbols = findPinnedMemorySymbols(minAddr, maxAddr);
Set<Address> primaryFixups = new HashSet<>(); Set<Address> primaryFixups = new HashSet<>();
for (SymbolDB symbol : fixupPinnedSymbols) { for (MemorySymbol memSymbol : fixupPinnedSymbols) {
Address currentAddress = symbol.getAddress(); Address currentAddress = memSymbol.getAddress();
Address beforeBaseChangeAddress = oldBase.addWrap(currentAddress.subtract(base)); Address beforeBaseChangeAddress = oldBase.addWrap(currentAddress.subtract(base));
primaryFixups.add(currentAddress); primaryFixups.add(currentAddress);
primaryFixups.add(beforeBaseChangeAddress); primaryFixups.add(beforeBaseChangeAddress);
// see if there is a name collision for the pinned symbol we are about to move back // see if there is a name collision for the pinned symbol we are about to move back
Symbol match = Symbol match = getSymbol(memSymbol.getName(), beforeBaseChangeAddress,
getSymbol(symbol.getName(), beforeBaseChangeAddress, symbol.getParentNamespace()); memSymbol.getParentNamespace());
if (symbol.getSymbolType() == SymbolType.FUNCTION) { if (memSymbol.getSymbolType() == SymbolType.FUNCTION) {
fixupPinnedFunctionSymbolAfterRebase(symbol, beforeBaseChangeAddress, match); fixupPinnedFunctionSymbolAfterRebase(memSymbol, beforeBaseChangeAddress, match);
} }
else { else {
fixupPinnedLabelSymbolAfterRebase(symbol, beforeBaseChangeAddress, match); fixupPinnedLabelSymbolAfterRebase(memSymbol, beforeBaseChangeAddress, match);
} }
} }
fixupPrimarySymbols(primaryFixups); fixupPrimarySymbols(primaryFixups);
} }
private void fixupPinnedLabelSymbolAfterRebase(SymbolDB symbol, Address newAddress, private void fixupPinnedLabelSymbolAfterRebase(MemorySymbol memSymbol, Address newAddress,
Symbol match) { Symbol match) {
if (match != null) { if (match != null) {
match.setPinned(true); match.setPinned(true);
symbol.delete(); memSymbol.delete();
} }
else { 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) { private List<MemorySymbol> findPinnedMemorySymbols(Address minAddr, Address maxAddr) {
List<SymbolDB> pinnedSymbols = new ArrayList<>(); List<MemorySymbol> pinnedSymbols = new ArrayList<>();
for (Symbol symbol : getSymbolIterator(minAddr, true)) { 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; break;
} }
if (symbol.isPinned()) { if (memSym.isPinned()) {
pinnedSymbols.add((SymbolDB) symbol); pinnedSymbols.add(memSym);
} }
} }
return pinnedSymbols; return pinnedSymbols;
@ -2589,16 +2783,24 @@ public class SymbolManager implements SymbolTable, ManagerDB {
try { try {
source = adjustSourceTypeIfNecessary(name, type, source, storage); source = adjustSourceTypeIfNecessary(name, type, source, storage);
Address varAddr = variableStorageMgr.getVariableStorageAddress(storage, true); Address varAddr = variableStorageMgr.getVariableStorageAddress(storage, true);
return (VariableSymbolDB) doCreateSpecialSymbol(varAddr, name, function, type, null, DBRecord record =
Integer.valueOf(firstUseOffsetOrOrdinal), null, source, true); 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) { catch (IOException e) {
dbError(e); program.dbError(e); // will not return
return null;
} }
finally { finally {
lock.release(); lock.release();
} }
return null;
} }
private SourceType adjustSourceTypeIfNecessary(String name, SymbolType type, SourceType source, private SourceType adjustSourceTypeIfNecessary(String name, SymbolType type, SourceType source,
@ -2616,21 +2818,21 @@ public class SymbolManager implements SymbolTable, ManagerDB {
@Override @Override
public GhidraClass createClass(Namespace parent, String name, SourceType source) public GhidraClass createClass(Namespace parent, String name, SourceType source)
throws DuplicateNameException, InvalidInputException { throws DuplicateNameException, InvalidInputException {
SymbolDB s = createClassSymbol(name, parent, source, true); ClassSymbol s = createClassSymbol(name, parent, source, true);
return new GhidraClassDB(s, namespaceMgr); return new GhidraClassDB(s, namespaceMgr);
} }
@Override @Override
public Library createExternalLibrary(String name, SourceType source) public Library createExternalLibrary(String name, SourceType source)
throws DuplicateNameException, InvalidInputException { throws DuplicateNameException, InvalidInputException {
SymbolDB s = createLibrarySymbol(name, null, source); LibrarySymbol s = createLibrarySymbol(name, null, source);
return new LibraryDB(s, namespaceMgr); return new LibraryDB(s, namespaceMgr);
} }
@Override @Override
public Namespace createNameSpace(Namespace parent, String name, SourceType source) public Namespace createNameSpace(Namespace parent, String name, SourceType source)
throws DuplicateNameException, InvalidInputException { throws DuplicateNameException, InvalidInputException {
SymbolDB s = createNamespaceSymbol(name, parent, source, true); NamespaceSymbol s = createNamespaceSymbol(name, parent, source, true);
return new NamespaceDB(s, namespaceMgr); 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 // no duplicate check, since this class name will be set to that of the existing namespace
String tempName = "_temp_" + System.nanoTime(); String tempName = "_temp_" + System.nanoTime();
SymbolDB classSymbol = createClassSymbol(tempName, namespace.getParentNamespace(), ClassSymbol classSymbol = createClassSymbol(tempName, namespace.getParentNamespace(),
originalSource, false /*check for duplicate */); originalSource, false /*check for duplicate */);
GhidraClassDB classNamespace = new GhidraClassDB(classSymbol, namespaceMgr); 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 // 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. // 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); 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 * or if the given parent namespace is from a different program than that of this
* symbol table. * symbol table.
*/ */
public SymbolDB createLibrarySymbol(String name, String pathname, SourceType source) public LibrarySymbol createLibrarySymbol(String name, String pathname, SourceType source)
throws DuplicateNameException, InvalidInputException { throws DuplicateNameException, InvalidInputException {
return doCreateSpecialSymbol(Address.NO_ADDRESS, name, null, SymbolType.LIBRARY, null, null, lock.acquire();
pathname, source, true); 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 * or if the given parent namespace is from a different program than that of this
* symbol table. * symbol table.
*/ */
SymbolDB createClassSymbol(String name, Namespace parent, SourceType source, ClassSymbol createClassSymbol(String name, Namespace parent, SourceType source,
boolean checkForDuplicates) throws DuplicateNameException, InvalidInputException { boolean checkForDuplicates) throws DuplicateNameException, InvalidInputException {
return doCreateSpecialSymbol(Address.NO_ADDRESS, name, parent, SymbolType.CLASS, null, null, lock.acquire();
null, source, true); 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 * if the given parent namespace is from a different program than that of this
* symbol table. * symbol table.
*/ */
SymbolDB createNamespaceSymbol(String name, Namespace parent, SourceType source, NamespaceSymbol createNamespaceSymbol(String name, Namespace parent, SourceType source,
boolean checkForDuplicates) throws DuplicateNameException, InvalidInputException { boolean checkForDuplicates) throws DuplicateNameException, InvalidInputException {
return doCreateSpecialSymbol(Address.NO_ADDRESS, name, parent, SymbolType.NAMESPACE, null, lock.acquire();
null, null, source, true); 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, * Create a basic symbol record with required fields. Caller is responsible for updating
SourceType source, boolean checkForDuplicates) * 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 { throws DuplicateNameException, InvalidInputException {
lock.acquire(); // Only one symbol per external address is allowed
try { if (address.isExternalAddress()) {
parent = validateNamespace(parent, addr, symbolType); Symbol primary = getPrimarySymbol(address);
source = validateSource(source, name, addr, symbolType); if (primary != null) {
name = validateName(name, source); throw new IllegalArgumentException("external address already used");
if (checkForDuplicates) {
checkDuplicateSymbolName(addr, name, parent, symbolType);
} }
return doCreateSymbol(name, addr, parent, symbolType, stringData, dataTypeId,
variableOffset, source, false);
} }
finally {
lock.release();
}
}
@Override parent = validateNamespace(parent, address, symbolType);
public Symbol createLabel(Address addr, String name, SourceType source) source = validateSource(source, name, address, symbolType);
throws InvalidInputException { name = validateName(name, source);
return createLabel(addr, name, null, source);
}
@Override if (checkForDuplicates) {
public Symbol createLabel(Address addr, String name, Namespace namespace, SourceType source) checkDuplicateSymbolName(address, name, parent, symbolType);
throws InvalidInputException {
if (!addr.isMemoryAddress()) {
throw new IllegalArgumentException("Invalid memory address: " + addr);
} }
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> * <p>
* If identical memory symbol already exists it will be returned. * NOTE: Any name conflict concerns must be checked before invoking this method.
*
* @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
* *
* @param addr the address for the new symbol * @param addr the address for the new symbol
* @param name the name of 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 * @param namespace the namespace for the new symbol (null may be specified for global
* namespace) * namespace)
* @param source the SourceType of the new symbol * @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 * @return the new symbol
* @throws IOException if IO error occurs
* @throws InvalidInputException if the name contains illegal characters (i.e. space) * @throws InvalidInputException if the name contains illegal characters (i.e. space)
*/ */
public Symbol createFunctionSymbol(Address addr, String name, Namespace namespace, public CodeSymbol createExternalCodeSymbol(Address addr, String name, Namespace namespace,
SourceType source, String stringData) throws InvalidInputException { 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); namespace = validateNamespace(namespace, addr, SymbolType.FUNCTION);
source = validateSource(source, name, addr, SymbolType.FUNCTION); source = validateSource(source, name, addr, SymbolType.FUNCTION);
name = validateName(name, source); 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. // if there is already a FUNCTION symbol with that name and namespace here, just return it.
Symbol matching = getSymbol(name, addr, namespace); Symbol matching = getSymbol(name, addr, namespace);
if (matching != null && matching.getSymbolType() == SymbolType.FUNCTION) { if (matching instanceof FunctionSymbol funcSymbol) {
return matching; return funcSymbol;
} }
// if there is another function at the same address, throw InvalidInputException // 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 // delete any promoted symbol, dynamic symbol, and make sure all others are not primary
cleanUpSymbols(addr, symbolToPromote, primary); cleanUpSymbols(addr, symbolToPromote, primary);
Symbol symbol = doCreateSymbol(name, addr, namespace, SymbolType.FUNCTION, stringData, null, DBRecord record;
null, source, true); try {
record = doCreateBasicSymbolRecord(name, namespace, addr, SymbolType.FUNCTION, true,
if (needsPinning) { source, false);
symbol.setPinned(true); 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; 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 { private String validateName(String name, SourceType source) throws InvalidInputException {
if (source == SourceType.DEFAULT) { if (source == SourceType.DEFAULT) {
return ""; return "";
@ -3171,9 +3467,8 @@ public class SymbolManager implements SymbolTable, ManagerDB {
void checkValidNamespaceArgument(Namespace namespace) throws IllegalArgumentException { void checkValidNamespaceArgument(Namespace namespace) throws IllegalArgumentException {
if (!isMyNamespace(namespace)) { if (!isMyNamespace(namespace)) {
String kind = (namespace instanceof Function) ? "function" : "namespace"; throw new IllegalArgumentException(namespace.getType().friendlyName() +
throw new IllegalArgumentException( " is from a different program instance: " + namespace);
kind + " is from different program instance: " + namespace);
} }
} }

View file

@ -138,7 +138,7 @@ public class VariableSymbolDB extends SymbolDB {
} }
@Override @Override
public Object getObject() { public Variable getObject() {
FunctionDB func = getFunction(); FunctionDB func = getFunction();
if (func != null) { if (func != null) {
return func.getVariable(this); return func.getVariable(this);
@ -159,13 +159,12 @@ public class VariableSymbolDB extends SymbolDB {
public FunctionDB getFunction() { public FunctionDB getFunction() {
return (FunctionDB) symbolMgr.getFunctionManager() return (FunctionDB) symbolMgr.getFunctionManager()
.getFunction( .getFunction(getParentNamespace().getID());
getParentNamespace().getID());
} }
@Override @Override
public ProgramLocation getProgramLocation() { public ProgramLocation getProgramLocation() {
Variable var = (Variable) getObject(); Variable var = getObject();
if (var != null) { if (var != null) {
return new VariableNameFieldLocation(var.getProgram(), var, 0); return new VariableNameFieldLocation(var.getProgram(), var, 0);
} }
@ -311,20 +310,90 @@ public class VariableSymbolDB extends SymbolDB {
try { try {
checkIsValid(); checkIsValid();
ReferenceManager rm = symbolMgr.getReferenceManager(); ReferenceManager rm = symbolMgr.getReferenceManager();
return rm.getReferencesTo((Variable) getObject()); return rm.getReferencesTo(getObject());
} }
finally { finally {
lock.release(); lock.release();
} }
} }
@Override
public boolean hasMultipleReferences() {
return getReferences(null).length > 1;
}
@Override @Override
public boolean hasReferences() { public boolean hasReferences() {
return getReferences(null).length != 0; 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; return 0;
} }
@Override
public boolean hasMultipleReferences() {
return false;
}
@Override @Override
public boolean hasReferences() { public boolean hasReferences() {
return false; return false;
} }
@Override
public Reference[] getReferences() {
return new Reference[0];
}
@Override @Override
public Reference[] getReferences(TaskMonitor monitor) { public Reference[] getReferences(TaskMonitor monitor) {
return new Reference[0]; 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 UNKNOWN_STACK_DEPTH_CHANGE = Integer.MAX_VALUE;
public final static int INVALID_STACK_DEPTH_CHANGE = Integer.MAX_VALUE - 1; 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. * Get the name of this function.
* *

View file

@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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; import ghidra.program.model.symbol.Namespace;
/** /**
* Interface for representing class objects in the program. * Interface for representing class objects in the program.
*/ */
public interface GhidraClass extends Namespace { public interface GhidraClass extends Namespace {
@Override
public default Type getType() {
return Type.CLASS;
}
} }

View file

@ -15,7 +15,7 @@
*/ */
package ghidra.program.model.listing; package ghidra.program.model.listing;
import ghidra.program.model.symbol.Namespace; import ghidra.program.model.symbol.*;
/** /**
* Interface for a Library namespace. * Interface for a Library namespace.
@ -24,9 +24,39 @@ public interface Library extends Namespace {
public static final String UNKNOWN = "<EXTERNAL>"; 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 * @return the associated program within the project which corresponds to this library
*/ */
public String getAssociatedProgramPath(); 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; package ghidra.program.model.symbol;
import java.util.List; import java.util.Set;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
@ -74,15 +74,15 @@ public interface ExternalManager {
* @param oldName the old name of the external library name. * @param oldName the old name of the external library name.
* @param newName the new name of the external library name. * @param newName the new name of the external library name.
* @param source the source of this external library * @param source the source of this external library
* @throws DuplicateNameException if another namespace has the same name * @throws DuplicateNameException if name conflicts with another symbol.
* @throws InvalidInputException on invalid input * @throws InvalidInputException if an invalid or null name specified (see
* {@link SymbolUtilities#validateName}).
*/ */
public void updateExternalLibraryName(String oldName, String newName, SourceType source) public void updateExternalLibraryName(String oldName, String newName, SourceType source)
throws DuplicateNameException, InvalidInputException; throws DuplicateNameException, InvalidInputException;
/** /**
* Get an iterator over all external locations associated with the specified * Get an iterator over all external locations associated with the specified Library.
* externalName.
* @param libraryName the name of the library to get locations for * @param libraryName the name of the library to get locations for
* @return external location iterator * @return external location iterator
*/ */
@ -97,54 +97,46 @@ public interface ExternalManager {
public ExternalLocationIterator getExternalLocations(Address memoryAddress); public ExternalLocationIterator getExternalLocations(Address memoryAddress);
/** /**
* Get an external location. * Returns a set of External Locations matching the given label name in the specified Library.
* @param libraryName the name of the library for which to get an external location * If searching for an original import name which should not be constrained to a specific
* @param label the name of the external location. * library (e.g., mangled name), null may be specified for the libraryName.
* @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.
* @param libraryName the name of the library * @param libraryName the name of the library
* @param label the name of the label * @param label the name of the label
* @return a list of External Locations matching the given label name in the given Library. * @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. * Returns a set of External Locations matching the given label name in the given Namespace.
* @param namespace the Namespace to search * 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. * @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. * @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 * Returns the unique external location associated with the given library name and label.
* @param libraryName the library name * 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 * @param label the label of the external location
* @return the unique external location or null * @return the unique external location or null
*/ */
public ExternalLocation getUniqueExternalLocation(String libraryName, String label); public ExternalLocation getUniqueExternalLocation(String libraryName, String label);
/** /**
* Returns the unique external location associated with the given namespace and label * Returns the unique external location associated with the given namespace and label.
* @param namespace the 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 namespace or null
* @param label the label of the external location * @param label the label of the external location
* @return the unique external location or null * @return the unique external location or null
*/ */
@ -229,8 +221,7 @@ public interface ExternalManager {
* @throws IllegalArgumentException if an invalid {@code extAddr} was specified. * @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public ExternalLocation addExtLocation(Namespace extNamespace, String extLabel, Address extAddr, public ExternalLocation addExtLocation(Namespace extNamespace, String extLabel, Address extAddr,
SourceType sourceType, boolean reuseExisting) SourceType sourceType, boolean reuseExisting) throws InvalidInputException;
throws InvalidInputException;
/** /**
* Create an external {@link Function} in the external {@link Library} namespace * 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. * @throws IllegalArgumentException if an invalid {@code extAddr} was specified.
*/ */
public ExternalLocation addExtFunction(Namespace extNamespace, String extLabel, Address extAddr, public ExternalLocation addExtFunction(Namespace extNamespace, String extLabel, Address extAddr,
SourceType sourceType, boolean reuseExisting) SourceType sourceType, boolean reuseExisting) throws InvalidInputException;
throws InvalidInputException;
} }

View file

@ -27,6 +27,26 @@ import ghidra.util.exception.InvalidInputException;
*/ */
public interface Namespace { 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; static final long GLOBAL_NAMESPACE_ID = 0;
/** /**
* The delimiter that is used to separate namespace nodes in a namespace * The delimiter that is used to separate namespace nodes in a namespace
@ -47,6 +67,13 @@ public interface Namespace {
*/ */
public Symbol getSymbol(); 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) * 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) * @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(); 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(); public default int getReferenceCount() {
return 0;
/** }
* @return true if this symbol has more than one reference to it.
*/
public boolean hasMultipleReferences();
/** /**
* @return true if this symbol has at least one reference to it. * @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 * 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 * @param monitor the monitor that is used to report progress and to cancel this
* potentially long-running call * 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. * 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 * @return all memory references to the address of this symbol
* @see #getReferences(TaskMonitor) * @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 * 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 * @param source the source of this symbol
* <br>Some symbol types, such as function symbols, can set the source to Symbol.DEFAULT. * <br>Some symbol types, such as function symbols, can set the source to Symbol.DEFAULT.
* *
* @throws DuplicateNameException * @throws DuplicateNameException if name conflicts with another symbol.
* if name already exists as the name of another symbol or alias. * @throws InvalidInputException if an invalid or null name specified (see
* @throws InvalidInputException * {@link SymbolUtilities#validateName}).
* if alias contains blank characters, is zero length, or is null
* @throws IllegalArgumentException if you try to set the source to DEFAULT for a symbol type * @throws IllegalArgumentException if you try to set the source to DEFAULT for a symbol type
* that doesn't allow it. * that doesn't allow it.
*/ */
@ -199,7 +209,9 @@ public interface Symbol {
* *
* @return true if the symbol is pinned to its current address. * @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> * <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. * @param pinned true indicates this symbol is anchored to its address.
* false indicates this symbol is not 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). * @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 * @return true if the symbol is at an address
* set as a external entry point. * set as a external entry point.
*/ */
public boolean isExternalEntryPoint(); public default boolean isExternalEntryPoint() {
return false;
}
/** /**
* @return this symbol's ID. * @return this symbol's ID.
@ -259,7 +275,7 @@ public interface Symbol {
public Object getObject(); public Object getObject();
/** /**
* @return true if the symbol is global * @return true if the symbol is contained within the global namespace
*/ */
public boolean isGlobal(); 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 * returned without changing the source type. If this is the first non-dynamic symbol defined
* for the address it becomes the primary symbol. * 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 name the name of the symbol
* @param namespace the parent namespace of the symbol, or null for the global namespace. * @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} * @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.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.DuplicateNameException;
import ghidra.util.exception.InvalidInputException; import ghidra.util.exception.InvalidInputException;
import ghidra.util.task.TaskMonitor;
// Simple symbol test implementation // Simple symbol test implementation
public class StubSymbol implements Symbol { public class StubSymbol implements Symbol {
@ -106,31 +105,6 @@ public class StubSymbol implements Symbol {
return SymbolType.LABEL; 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 @Override
public void setName(String newName, SourceType source) public void setName(String newName, SourceType source)
throws DuplicateNameException, InvalidInputException { throws DuplicateNameException, InvalidInputException {
@ -154,16 +128,6 @@ public class StubSymbol implements Symbol {
return false; return false;
} }
@Override
public boolean isPinned() {
return false;
}
@Override
public void setPinned(boolean pinned) {
// nothing
}
@Override @Override
public boolean isDynamic() { public boolean isDynamic() {
return false; return false;
@ -184,11 +148,6 @@ public class StubSymbol implements Symbol {
return false; return false;
} }
@Override
public boolean isExternalEntryPoint() {
return false;
}
@Override @Override
public long getID() { public long getID() {
return name.hashCode(); return name.hashCode();