mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-3434 - Updated AddEditDialog to allow users to create namespaces that
match the label name
This commit is contained in:
parent
d66027e27a
commit
7ad8505dcf
9 changed files with 386 additions and 200 deletions
|
@ -243,14 +243,14 @@ public class ThunkReferenceAddressDialog extends DialogComponentProvider {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Namespace> namespaces = NamespaceUtils.getNamespaces(parentNs, null, program);
|
List<Namespace> namespaces = NamespaceUtils.getNamespaceByPath(program, null, parentNs);
|
||||||
|
|
||||||
if (namespaces.isEmpty()) {
|
if (namespaces.isEmpty()) {
|
||||||
SymbolTable symbolTable = program.getSymbolTable();
|
SymbolTable symbolTable = program.getSymbolTable();
|
||||||
for (String libraryName : program.getExternalManager().getExternalLibraryNames()) {
|
for (String libraryName : program.getExternalManager().getExternalLibraryNames()) {
|
||||||
Symbol librarySymbol = symbolTable.getLibrarySymbol(libraryName);
|
Symbol librarySymbol = symbolTable.getLibrarySymbol(libraryName);
|
||||||
namespaces = NamespaceUtils.getNamespaces(parentNs,
|
namespaces = NamespaceUtils.getNamespaceByPath(program,
|
||||||
(Library) librarySymbol.getObject(), program);
|
(Library) librarySymbol.getObject(), parentNs);
|
||||||
if (!namespaces.isEmpty()) {
|
if (!namespaces.isEmpty()) {
|
||||||
break; // use first library containing namespace
|
break; // use first library containing namespace
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@ public class MatchSymbol {
|
||||||
SymbolPath namespacePath = aSymbolPath.getParent();
|
SymbolPath namespacePath = aSymbolPath.getParent();
|
||||||
if (!aSymbolIdentifier.isExternalSymbol() && namespacePath != null &&
|
if (!aSymbolIdentifier.isExternalSymbol() && namespacePath != null &&
|
||||||
!aSymbolPath.equals(bSymbolIdentifier.symbolPath) &&
|
!aSymbolPath.equals(bSymbolIdentifier.symbolPath) &&
|
||||||
NamespaceUtils.getNamespace(bProgram, namespacePath, null) != null) {
|
NamespaceUtils.getNonFunctionNamespace(bProgram, namespacePath) != null) {
|
||||||
// skip match with namespace mismatch when source namespace exists in destination
|
// skip match with namespace mismatch when source namespace exists in destination
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,24 +172,39 @@ public class AddEditDialog extends DialogComponentProvider {
|
||||||
if (parentPath == null) {
|
if (parentPath == null) {
|
||||||
return rootNamespace;
|
return rootNamespace;
|
||||||
}
|
}
|
||||||
String relativeParentPath = parentPath.getPath();
|
|
||||||
|
|
||||||
SymbolPath absoluteParentPath =
|
//
|
||||||
new SymbolPath(rootNamespace.getSymbol()).append(parentPath);
|
// Prefer a non-function namespace. This allows us to put a function inside of a namespace
|
||||||
Namespace parentNamespace = NamespaceUtils.getNamespace(program, absoluteParentPath, addr);
|
// sharing the same name.
|
||||||
if (parentNamespace != null) {
|
//
|
||||||
return parentNamespace;
|
SymbolPath fullPath = new SymbolPath(rootNamespace.getSymbol()).append(parentPath);
|
||||||
|
Namespace nonFunctionNs = NamespaceUtils.getNonFunctionNamespace(program, fullPath);
|
||||||
|
if (nonFunctionNs != null) {
|
||||||
|
return nonFunctionNs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// run the create namespaces command
|
//
|
||||||
CreateNamespacesCmd command =
|
// At this point we can either reuse an existing function namespace or we have to create
|
||||||
new CreateNamespacesCmd(relativeParentPath, rootNamespace, SourceType.USER_DEFINED);
|
// a new non-function namespaces, depending upon the names being used. Only use an
|
||||||
|
// existing function as a namespace if none of namespace path entries match the function
|
||||||
if (tool.execute(command, program)) {
|
// name.
|
||||||
return command.getNamespace();
|
//
|
||||||
|
String name = symbolPath.getName();
|
||||||
|
if (!parentPath.containsPathEntry(name)) {
|
||||||
|
Namespace functionNamespace =
|
||||||
|
NamespaceUtils.getFunctionNamespaceContaining(program, parentPath, addr);
|
||||||
|
if (functionNamespace != null) {
|
||||||
|
return functionNamespace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatusText(command.getStatusMsg());
|
CreateNamespacesCmd cmd =
|
||||||
|
new CreateNamespacesCmd(parentPath.getPath(), rootNamespace, SourceType.USER_DEFINED);
|
||||||
|
if (tool.execute(cmd, program)) {
|
||||||
|
return cmd.getNamespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
setStatusText(cmd.getStatusMsg());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,12 +75,12 @@ class FunctionsXmlMgr {
|
||||||
* <!ELEMENT FUNCTION (RETURN_TYPE?, ADDRESS_RANGE*, REGULAR_CMT?, REPEATABLE_CMT?, TYPEINFO_CMT?, STACK_FRAME?, REGISTER_VAR*)>
|
* <!ELEMENT FUNCTION (RETURN_TYPE?, ADDRESS_RANGE*, REGULAR_CMT?, REPEATABLE_CMT?, TYPEINFO_CMT?, STACK_FRAME?, REGISTER_VAR*)>
|
||||||
* </code></pre>
|
* </code></pre>
|
||||||
* <p>
|
* <p>
|
||||||
* @param parser
|
* @param parser the parser
|
||||||
* @param overwriteConflicts
|
* @param overwriteConflicts true to overwrite any conflicts
|
||||||
* @param ignoreStackFrames
|
* @param ignoreStackFrames true to ignore stack frames
|
||||||
* @param monitor
|
* @param monitor the task monitor
|
||||||
* @throws AddressFormatException
|
* @throws AddressFormatException if any address is not parsable
|
||||||
* @throws CancelledException
|
* @throws CancelledException if the operation is cancelled through the monitor
|
||||||
*/
|
*/
|
||||||
void read(XmlPullParser parser, boolean overwriteConflicts, boolean ignoreStackFrames,
|
void read(XmlPullParser parser, boolean overwriteConflicts, boolean ignoreStackFrames,
|
||||||
TaskMonitor monitor) throws AddressFormatException, CancelledException {
|
TaskMonitor monitor) throws AddressFormatException, CancelledException {
|
||||||
|
@ -94,9 +94,7 @@ class FunctionsXmlMgr {
|
||||||
dtParser = new DtParser(dataManager);
|
dtParser = new DtParser(dataManager);
|
||||||
|
|
||||||
while (parser.peek().isStart()) {
|
while (parser.peek().isStart()) {
|
||||||
if (monitor.isCancelled()) {
|
monitor.checkCanceled();
|
||||||
throw new CancelledException();
|
|
||||||
}
|
|
||||||
|
|
||||||
final XmlElement functionElement = parser.start("FUNCTION");
|
final XmlElement functionElement = parser.start("FUNCTION");
|
||||||
|
|
||||||
|
@ -155,7 +153,8 @@ class FunctionsXmlMgr {
|
||||||
try {
|
try {
|
||||||
Symbol symbol = func.getSymbol();
|
Symbol symbol = func.getSymbol();
|
||||||
Namespace namespace =
|
Namespace namespace =
|
||||||
NamespaceUtils.getNamespace(program, namespacePath, entryPoint);
|
NamespaceUtils.getFunctionNamespaceAt(program, namespacePath,
|
||||||
|
entryPoint);
|
||||||
if (namespace == null) {
|
if (namespace == null) {
|
||||||
namespace = program.getGlobalNamespace();
|
namespace = program.getGlobalNamespace();
|
||||||
}
|
}
|
||||||
|
@ -258,13 +257,6 @@ class FunctionsXmlMgr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add local vars to a function.
|
|
||||||
*
|
|
||||||
* @param function
|
|
||||||
* @param variables
|
|
||||||
* @throws InvalidInputException
|
|
||||||
*/
|
|
||||||
private void addLocalVars(Function function, List<Variable> variables,
|
private void addLocalVars(Function function, List<Variable> variables,
|
||||||
boolean overwriteConflicts) throws InvalidInputException {
|
boolean overwriteConflicts) throws InvalidInputException {
|
||||||
for (Variable v : variables) {
|
for (Variable v : variables) {
|
||||||
|
@ -275,8 +267,9 @@ class FunctionsXmlMgr {
|
||||||
try {
|
try {
|
||||||
String name = v.getName();
|
String name = v.getName();
|
||||||
boolean isDefaultVariableName = (name == null) ||
|
boolean isDefaultVariableName = (name == null) ||
|
||||||
SymbolUtilities.getDefaultLocalName(program, v.getStackOffset(), 0).equals(
|
SymbolUtilities.getDefaultLocalName(program, v.getStackOffset(), 0)
|
||||||
name);
|
.equals(
|
||||||
|
name);
|
||||||
|
|
||||||
SourceType sourceType =
|
SourceType sourceType =
|
||||||
isDefaultVariableName ? SourceType.DEFAULT : SourceType.USER_DEFINED;
|
isDefaultVariableName ? SourceType.DEFAULT : SourceType.USER_DEFINED;
|
||||||
|
@ -305,13 +298,9 @@ class FunctionsXmlMgr {
|
||||||
return dtParser.parseDataType(dtName, cp, size);
|
return dtParser.parseDataType(dtName, cp, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* Returns the text embedded in an optional xml element. If the next element in the stream is not
|
* Returns the text embedded in an optional xml element. If the next element in the stream is not
|
||||||
* the "expectedElementName", the xml parser stream is unchanged.
|
* the "expectedElementName", the xml parser stream is unchanged
|
||||||
* <p>
|
|
||||||
* @param parser
|
|
||||||
* @param expectedElementName
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
private String getElementText(XmlPullParser parser, String expectedElementName) {
|
private String getElementText(XmlPullParser parser, String expectedElementName) {
|
||||||
String result = null;
|
String result = null;
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.*;
|
||||||
|
|
||||||
import ghidra.app.cmd.function.CreateFunctionCmd;
|
import ghidra.app.cmd.function.CreateFunctionCmd;
|
||||||
import ghidra.app.cmd.label.AddLabelCmd;
|
import ghidra.app.cmd.label.AddLabelCmd;
|
||||||
|
import ghidra.app.cmd.label.CreateNamespacesCmd;
|
||||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||||
import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin;
|
import ghidra.app.plugin.core.navigation.GoToAddressLabelPlugin;
|
||||||
import ghidra.app.util.AddEditDialog;
|
import ghidra.app.util.AddEditDialog;
|
||||||
|
@ -93,6 +94,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() throws Exception {
|
public void tearDown() throws Exception {
|
||||||
|
dialog.close();
|
||||||
env.dispose();
|
env.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,14 +109,14 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals(globalScope, scope);
|
assertEquals(globalScope, scope);
|
||||||
assertTrue(primaryCheckBox.isSelected());
|
assertTrue(primaryCheckBox.isSelected());
|
||||||
assertTrue(!primaryCheckBox.isEnabled());
|
assertTrue(!primaryCheckBox.isEnabled());
|
||||||
dialogCancel();
|
pressCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLabelChangeOne() {
|
public void testLabelChangeOne() {
|
||||||
addLabel(addr(0x0100642a));
|
addLabel(addr(0x0100642a));
|
||||||
setText("printf");
|
setText("printf");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
Symbol s = getUniqueSymbol(program, "printf", null);
|
Symbol s = getUniqueSymbol(program, "printf", null);
|
||||||
assertNotNull(s);
|
assertNotNull(s);
|
||||||
|
@ -126,14 +128,14 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
public void testDuplicateLabelForAdd() {
|
public void testDuplicateLabelForAdd() {
|
||||||
addLabel(addr(0x0100642a));
|
addLabel(addr(0x0100642a));
|
||||||
setText("printf");
|
setText("printf");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
addLabel(addr(0x0100642c));
|
addLabel(addr(0x0100642c));
|
||||||
setText("printf");
|
setText("printf");
|
||||||
assertEquals(" ", dialog.getStatusText());
|
assertEquals(" ", dialog.getStatusText());
|
||||||
dialogOK();
|
pressOk();
|
||||||
assertTrue(dialog.getStatusText().length() > 0);
|
assertTrue(dialog.getStatusText().length() > 0);
|
||||||
dialogCancel();
|
pressCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -141,7 +143,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
addLabel(addr(0x0100642a));
|
addLabel(addr(0x0100642a));
|
||||||
setText("printf");
|
setText("printf");
|
||||||
setCheckbox(entryCheckBox, true);
|
setCheckbox(entryCheckBox, true);
|
||||||
dialogOK();
|
pressOk();
|
||||||
assertTrue(!dialog.isVisible());
|
assertTrue(!dialog.isVisible());
|
||||||
Symbol s = getUniqueSymbol(program, "printf", null);
|
Symbol s = getUniqueSymbol(program, "printf", null);
|
||||||
assertNotNull(s);
|
assertNotNull(s);
|
||||||
|
@ -153,7 +155,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
addLabel(addr(0x0100642a));
|
addLabel(addr(0x0100642a));
|
||||||
setText("printf");
|
setText("printf");
|
||||||
setCheckbox(pinnedCheckBox, true);
|
setCheckbox(pinnedCheckBox, true);
|
||||||
dialogOK();
|
pressOk();
|
||||||
assertTrue(!dialog.isVisible());
|
assertTrue(!dialog.isVisible());
|
||||||
Symbol s = getUniqueSymbol(program, "printf", null);
|
Symbol s = getUniqueSymbol(program, "printf", null);
|
||||||
assertNotNull(s);
|
assertNotNull(s);
|
||||||
|
@ -161,7 +163,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
setCheckbox(pinnedCheckBox, false);
|
setCheckbox(pinnedCheckBox, false);
|
||||||
dialogOK();
|
pressOk();
|
||||||
assertTrue(!s.isPinned());
|
assertTrue(!s.isPinned());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -169,7 +171,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyLabel() {
|
public void testEmptyLabel() {
|
||||||
addLabel(addr(0x0100642a));
|
addLabel(addr(0x0100642a));
|
||||||
dialogOK();
|
pressOk();
|
||||||
assertTrue(dialog.isVisible());
|
assertTrue(dialog.isVisible());
|
||||||
assertTrue(dialog.getStatusText().length() > 0);
|
assertTrue(dialog.getStatusText().length() > 0);
|
||||||
|
|
||||||
|
@ -239,7 +241,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
setText("printf");
|
setText("printf");
|
||||||
Namespace scope = st.getNamespace(a);
|
Namespace scope = st.getNamespace(a);
|
||||||
setScope(scope);
|
setScope(scope);
|
||||||
dialogOK();
|
pressOk();
|
||||||
s = getUniqueSymbol(program, "printf", scope);
|
s = getUniqueSymbol(program, "printf", scope);
|
||||||
assertNotNull(s);
|
assertNotNull(s);
|
||||||
assertTrue(!s.isGlobal());
|
assertTrue(!s.isGlobal());
|
||||||
|
@ -259,7 +261,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
setText("printf");
|
setText("printf");
|
||||||
|
|
||||||
setCheckbox(primaryCheckBox, true);
|
setCheckbox(primaryCheckBox, true);
|
||||||
dialogOK();
|
pressOk();
|
||||||
s = getUniqueSymbol(program, "entry", null);
|
s = getUniqueSymbol(program, "entry", null);
|
||||||
assertTrue(!s.isPrimary());
|
assertTrue(!s.isPrimary());
|
||||||
s = getUniqueSymbol(program, "printf", null);
|
s = getUniqueSymbol(program, "printf", null);
|
||||||
|
@ -286,7 +288,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
addLabel(addr(0x100642a));
|
addLabel(addr(0x100642a));
|
||||||
setText("aaaa");
|
setText("aaaa");
|
||||||
assertEquals(0, recentLabels.size());
|
assertEquals(0, recentLabels.size());
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
assertEquals(1, recentLabels.size());
|
assertEquals(1, recentLabels.size());
|
||||||
assertEquals("aaaa", recentLabels.get(0));
|
assertEquals("aaaa", recentLabels.get(0));
|
||||||
|
@ -295,7 +297,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertEquals("", getText());
|
assertEquals("", getText());
|
||||||
//assertEquals("aaaa", getText());
|
//assertEquals("aaaa", getText());
|
||||||
setText("bbbb");
|
setText("bbbb");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
assertEquals(2, recentLabels.size());
|
assertEquals(2, recentLabels.size());
|
||||||
assertEquals("bbbb", recentLabels.get(0));
|
assertEquals("bbbb", recentLabels.get(0));
|
||||||
|
@ -308,7 +310,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
addLabel(addr(0x100642a));
|
addLabel(addr(0x100642a));
|
||||||
setText("l" + i);
|
setText("l" + i);
|
||||||
dialogOK();
|
pressOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(10, recentLabels.size());
|
assertEquals(10, recentLabels.size());
|
||||||
|
@ -328,7 +330,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertTrue(entryCheckBox.isSelected());
|
assertTrue(entryCheckBox.isSelected());
|
||||||
assertTrue(primaryCheckBox.isSelected());
|
assertTrue(primaryCheckBox.isSelected());
|
||||||
assertTrue(!primaryCheckBox.isEnabled());
|
assertTrue(!primaryCheckBox.isEnabled());
|
||||||
dialogCancel();
|
pressCancel();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,7 +354,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
assertEquals("entry", getText());
|
assertEquals("entry", getText());
|
||||||
setText("bob");
|
setText("bob");
|
||||||
dialogOK();
|
pressOk();
|
||||||
program.flushEvents();
|
program.flushEvents();
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
assertEquals("bob", function.getName());
|
assertEquals("bob", function.getName());
|
||||||
|
@ -378,7 +380,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
editLabel(fredSymbol);
|
editLabel(fredSymbol);
|
||||||
setCheckbox(primaryCheckBox, true);
|
setCheckbox(primaryCheckBox, true);
|
||||||
dialogOK();
|
pressOk();
|
||||||
program.flushEvents();
|
program.flushEvents();
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
assertEquals("fred", function.getName());
|
assertEquals("fred", function.getName());
|
||||||
|
@ -400,7 +402,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
Object selectedItem = namespacesComboBox.getSelectedItem();
|
Object selectedItem = namespacesComboBox.getSelectedItem();
|
||||||
assertEquals(ns, ((AddEditDialog.NamespaceWrapper) selectedItem).getNamespace());
|
assertEquals(ns, ((AddEditDialog.NamespaceWrapper) selectedItem).getNamespace());
|
||||||
dialogCancel();
|
pressCancel();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +416,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertTrue(primaryCheckBox.isSelected());
|
assertTrue(primaryCheckBox.isSelected());
|
||||||
assertTrue(!primaryCheckBox.isEnabled());
|
assertTrue(!primaryCheckBox.isEnabled());
|
||||||
setText("aaaa");
|
setText("aaaa");
|
||||||
dialogOK();
|
pressOk();
|
||||||
s = st.getPrimarySymbol(refAddr);
|
s = st.getPrimarySymbol(refAddr);
|
||||||
assertEquals("aaaa", s.getName());
|
assertEquals("aaaa", s.getName());
|
||||||
}
|
}
|
||||||
|
@ -426,7 +428,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
|
|
||||||
setText("fred");
|
setText("fred");
|
||||||
dialogOK();
|
pressOk();
|
||||||
assertEquals("fred", s.getName());
|
assertEquals("fred", s.getName());
|
||||||
|
|
||||||
s = st.getPrimarySymbol(a);
|
s = st.getPrimarySymbol(a);
|
||||||
|
@ -438,17 +440,17 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
public void testDuplicateLabelForEdit() {
|
public void testDuplicateLabelForEdit() {
|
||||||
addLabel(addr(0x100642a));
|
addLabel(addr(0x100642a));
|
||||||
setText("printf");
|
setText("printf");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
addLabel(addr(0x100642a));
|
addLabel(addr(0x100642a));
|
||||||
setText("fred");
|
setText("fred");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
Symbol s = getUniqueSymbol(program, "printf", null);
|
Symbol s = getUniqueSymbol(program, "printf", null);
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
|
|
||||||
setText("fred");
|
setText("fred");
|
||||||
dialogOK();
|
pressOk();
|
||||||
assertTrue(dialog.isVisible());
|
assertTrue(dialog.isVisible());
|
||||||
assertTrue(dialog.getStatusText().length() > 0);
|
assertTrue(dialog.getStatusText().length() > 0);
|
||||||
|
|
||||||
|
@ -459,18 +461,18 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
Address a = addr(0x100642a);
|
Address a = addr(0x100642a);
|
||||||
addLabel(a);
|
addLabel(a);
|
||||||
setText("aaaa");
|
setText("aaaa");
|
||||||
dialogOK();
|
pressOk();
|
||||||
addLabel(a);
|
addLabel(a);
|
||||||
setText("bbbb");
|
setText("bbbb");
|
||||||
dialogOK();
|
pressOk();
|
||||||
addLabel(a);
|
addLabel(a);
|
||||||
setText("cccc");
|
setText("cccc");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
Symbol s = getUniqueSymbol(program, "bbbb", null);
|
Symbol s = getUniqueSymbol(program, "bbbb", null);
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
setText("zzzz");
|
setText("zzzz");
|
||||||
dialogOK();
|
pressOk();
|
||||||
assertNotNull(getUniqueSymbol(program, "aaaa", null));
|
assertNotNull(getUniqueSymbol(program, "aaaa", null));
|
||||||
assertNotNull(getUniqueSymbol(program, "zzzz", null));
|
assertNotNull(getUniqueSymbol(program, "zzzz", null));
|
||||||
assertNotNull(getUniqueSymbol(program, "cccc", null));
|
assertNotNull(getUniqueSymbol(program, "cccc", null));
|
||||||
|
@ -491,7 +493,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertTrue(!primaryCheckBox.isSelected());
|
assertTrue(!primaryCheckBox.isSelected());
|
||||||
assertTrue(primaryCheckBox.isEnabled());
|
assertTrue(primaryCheckBox.isEnabled());
|
||||||
setText("foo");
|
setText("foo");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
s = st.getPrimarySymbol(a);
|
s = st.getPrimarySymbol(a);
|
||||||
assertNotNull(s);
|
assertNotNull(s);
|
||||||
|
@ -509,16 +511,16 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
|
|
||||||
addLabel(s.getAddress());
|
addLabel(s.getAddress());
|
||||||
setText("aaaa");
|
setText("aaaa");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
s = getUniqueSymbol(program, "aaaa", null);
|
s = getUniqueSymbol(program, "aaaa", null);
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
setText("zzzz");
|
setText("zzzz");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
setText("bbbb");
|
setText("bbbb");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
assertEquals(3, recentLabels.size());
|
assertEquals(3, recentLabels.size());
|
||||||
assertEquals("bbbb", recentLabels.get(0));
|
assertEquals("bbbb", recentLabels.get(0));
|
||||||
|
@ -532,13 +534,13 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
addLabel(s.getAddress());
|
addLabel(s.getAddress());
|
||||||
setScope(s.getParentNamespace());
|
setScope(s.getParentNamespace());
|
||||||
setText("foo");
|
setText("foo");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
s = getUniqueSymbol(program, "foo", null);
|
s = getUniqueSymbol(program, "foo", null);
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
assertTrue(primaryCheckBox.isEnabled());
|
assertTrue(primaryCheckBox.isEnabled());
|
||||||
assertTrue(!primaryCheckBox.isSelected());
|
assertTrue(!primaryCheckBox.isSelected());
|
||||||
dialogOK();
|
pressOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -579,7 +581,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
Address entryAddress = s.getAddress();
|
Address entryAddress = s.getAddress();
|
||||||
addLabel(entryAddress);
|
addLabel(entryAddress);
|
||||||
setText("label_1");
|
setText("label_1");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
assertTrue("Encountered a problem adding a label to the Global " + "namespace",
|
assertTrue("Encountered a problem adding a label to the Global " + "namespace",
|
||||||
!dialog.isVisible());
|
!dialog.isVisible());
|
||||||
|
@ -588,7 +590,7 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
s = getUniqueSymbol(program, "label_1", null);
|
s = getUniqueSymbol(program, "label_1", null);
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
setText("namespace_1::label_1");
|
setText("namespace_1::label_1");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
// make sure there were no problems
|
// make sure there were no problems
|
||||||
assertTrue(
|
assertTrue(
|
||||||
|
@ -599,18 +601,128 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
s = st.getSymbols("label_1").next();
|
s = st.getSymbols("label_1").next();
|
||||||
editLabel(s);
|
editLabel(s);
|
||||||
setText("Global::label_1");
|
setText("Global::label_1");
|
||||||
dialogOK();
|
pressOk();
|
||||||
|
|
||||||
assertTrue("Encountered a problem changing a symbol's namespace to " +
|
assertTrue("Encountered a problem changing a symbol's namespace to " +
|
||||||
"the Global namespace while editing the symbol", !dialog.isVisible());
|
"the Global namespace while editing the symbol", !dialog.isVisible());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEntryNamespacePathWithAmbiguousFunctionName() {
|
public void testSetLabelNamespace_InsideFunctionBody_ToFunctionNamespace() {
|
||||||
Symbol origEntry = getUniqueSymbol(program, "entry", null);
|
Symbol entry = getUniqueSymbol(program, "entry", null);
|
||||||
assertNotNull(origEntry);
|
assertNotNull(entry);
|
||||||
|
|
||||||
Symbol dupEntry = st.getPrimarySymbol(addr(0x1002239));
|
Address inBodyAddress = entry.getAddress().add(1);
|
||||||
|
addLabel(inBodyAddress);
|
||||||
|
String newName = "label_1";
|
||||||
|
setText("entry" + Namespace.NAMESPACE_DELIMITER + newName);
|
||||||
|
pressOk();
|
||||||
|
assertFalse("Encountered a problem adding a label to the Global namespace",
|
||||||
|
dialog.isVisible());
|
||||||
|
|
||||||
|
Symbol label1 = getSymbol(newName);
|
||||||
|
Namespace parentNamespace = label1.getParentNamespace();
|
||||||
|
assertTrue("Namespace is not a function", parentNamespace instanceof Function);
|
||||||
|
Function fun = (Function) parentNamespace;
|
||||||
|
assertEquals(entry.getAddress(), fun.getEntryPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetLabelNamespace_InsideFunctionBody_ToExistingNonFunctionNamespace() {
|
||||||
|
|
||||||
|
Symbol entry = getUniqueSymbol(program, "entry", null);
|
||||||
|
assertNotNull(entry);
|
||||||
|
|
||||||
|
String namespaceName = "NewNamespace";
|
||||||
|
createNamespace(namespaceName);
|
||||||
|
|
||||||
|
Address inBodyAddress = entry.getAddress().add(1);
|
||||||
|
addLabel(inBodyAddress);
|
||||||
|
String newName = "label_1";
|
||||||
|
setText(namespaceName + Namespace.NAMESPACE_DELIMITER + newName);
|
||||||
|
pressOk();
|
||||||
|
assertFalse("Encountered a problem adding a label to the Global namespace",
|
||||||
|
dialog.isVisible());
|
||||||
|
|
||||||
|
Symbol label1 = getSymbol(newName);
|
||||||
|
Namespace parentNamespace = label1.getParentNamespace();
|
||||||
|
assertFalse("Namespace is not a function", parentNamespace instanceof Function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetLabelNamespace_InsideFunctionBody_ToNonExistentNonFunctionNamespace() {
|
||||||
|
|
||||||
|
Symbol entry = getUniqueSymbol(program, "entry", null);
|
||||||
|
assertNotNull(entry);
|
||||||
|
|
||||||
|
String namespaceName = "NewNamespace";
|
||||||
|
Address inBodyAddress = entry.getAddress().add(1);
|
||||||
|
addLabel(inBodyAddress);
|
||||||
|
String newName = "label_1";
|
||||||
|
setText(namespaceName + Namespace.NAMESPACE_DELIMITER + newName);
|
||||||
|
pressOk();
|
||||||
|
assertFalse("Encountered a problem adding a label to the Global namespace",
|
||||||
|
dialog.isVisible());
|
||||||
|
|
||||||
|
Symbol label1 = getSymbol(newName);
|
||||||
|
Namespace parentNamespace = label1.getParentNamespace();
|
||||||
|
assertFalse("Namespace is not a function", parentNamespace instanceof Function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEntryNamespacePathWithAmbiguousFunctionName() {
|
||||||
|
Symbol entry = getUniqueSymbol(program, "entry", null);
|
||||||
|
assertNotNull(entry);
|
||||||
|
|
||||||
|
Address otherEntryAddress = addr(0x1002239);
|
||||||
|
Symbol dupEntry = createOtherEntry(otherEntryAddress);
|
||||||
|
|
||||||
|
Address inBodyAddress = otherEntryAddress.add(1);
|
||||||
|
addLabel(inBodyAddress);
|
||||||
|
String newName = "label_1";
|
||||||
|
setText("entry" + Namespace.NAMESPACE_DELIMITER + newName);
|
||||||
|
pressOk();
|
||||||
|
assertFalse("Encountered a problem adding a label to the Global namespace",
|
||||||
|
dialog.isVisible());
|
||||||
|
|
||||||
|
Symbol label1 = getSymbol(newName);
|
||||||
|
Namespace parentNamespace = label1.getParentNamespace();
|
||||||
|
assertTrue("Namespace is not a function", parentNamespace instanceof Function);
|
||||||
|
Function fun = (Function) parentNamespace;
|
||||||
|
assertEquals(dupEntry.getAddress(), fun.getEntryPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetNamespace_NonExistentNamespace_SameNameAsFunction() throws Exception {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test that we can create a new namespace using the dialog when:
|
||||||
|
// 1) that namespace does not exist
|
||||||
|
// 2) the namespace matches the existing function name
|
||||||
|
// 3) the function name is not default
|
||||||
|
//
|
||||||
|
|
||||||
|
String functionName = "Foo";
|
||||||
|
Symbol function = createFunction(functionName);
|
||||||
|
|
||||||
|
editLabel(function);
|
||||||
|
String nsName = functionName;
|
||||||
|
setText(nsName + Namespace.NAMESPACE_DELIMITER + functionName);
|
||||||
|
pressOk();
|
||||||
|
assertFalse("Rename unsuccesful", dialog.isShowing());
|
||||||
|
|
||||||
|
Symbol newFunction = getSymbol(functionName);
|
||||||
|
Namespace parentNs = newFunction.getParentNamespace();
|
||||||
|
assertFalse(parentNs instanceof Function);
|
||||||
|
assertEquals(nsName, parentNs.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Private Methods
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
private Symbol createOtherEntry(Address otherAddress) {
|
||||||
|
Symbol dupEntry = st.getPrimarySymbol(otherAddress);
|
||||||
assertNotNull(dupEntry);
|
assertNotNull(dupEntry);
|
||||||
|
|
||||||
int id = program.startTransaction("test");
|
int id = program.startTransaction("test");
|
||||||
|
@ -618,42 +730,48 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
dupEntry.setName("entry", SourceType.USER_DEFINED);
|
dupEntry.setName("entry", SourceType.USER_DEFINED);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
fail("Got Exception trying to set the function name to entry at 0x1002239 ");
|
fail("Got Exception trying to set the function name to entry at " + otherAddress);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
program.endTransaction(id, true);
|
program.endTransaction(id, true);
|
||||||
}
|
}
|
||||||
|
return dupEntry;
|
||||||
Address entryAddress = dupEntry.getAddress();
|
}
|
||||||
addLabel(entryAddress);
|
|
||||||
setText("entry::label_1");
|
private Symbol getSymbol(String functionName) {
|
||||||
dialogOK();
|
|
||||||
|
SymbolIterator it = st.getSymbols(functionName);
|
||||||
assertTrue("Encountered a problem adding a label to the Global " + "namespace",
|
Symbol newLabel = it.next();
|
||||||
!dialog.isVisible());
|
assertNotNull(newLabel);
|
||||||
|
assertEquals(functionName, newLabel.getName());
|
||||||
SymbolIterator symbolIt = st.getSymbols("label_1");
|
return newLabel;
|
||||||
Symbol label1 = symbolIt.next();
|
}
|
||||||
assertNotNull(label1);
|
|
||||||
Namespace parentNamespace = label1.getParentNamespace();
|
private Symbol createFunction(String name) throws Exception {
|
||||||
assertTrue(parentNamespace instanceof Function);
|
Symbol entry = getUniqueSymbol(program, "entry", null);
|
||||||
Function fun = (Function) parentNamespace;
|
createEntryFunction();
|
||||||
assertEquals(dupEntry.getAddress(), fun.getEntryPoint());
|
editLabel(entry);
|
||||||
|
setText(name);
|
||||||
|
pressOk();
|
||||||
|
|
||||||
|
SymbolIterator it = st.getSymbols(name);
|
||||||
|
Symbol newLabel = it.next();
|
||||||
|
assertNotNull(newLabel);
|
||||||
|
assertEquals(name, newLabel.getName());
|
||||||
|
return newLabel;
|
||||||
}
|
}
|
||||||
//==================================================================================================
|
|
||||||
// Private Methods
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
private Address addr(long addr) {
|
private Address addr(long addr) {
|
||||||
return program.getAddressFactory().getAddress(Long.toHexString(addr));
|
return program.getAddressFactory().getAddress(Long.toHexString(addr));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dialogCancel() {
|
private void pressCancel() {
|
||||||
runSwing(() -> invokeInstanceMethod("cancelCallback", dialog));
|
runSwing(() -> invokeInstanceMethod("cancelCallback", dialog));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dialogOK() {
|
private void pressOk() {
|
||||||
runSwing(() -> invokeInstanceMethod("okCallback", dialog));
|
runSwing(() -> invokeInstanceMethod("okCallback", dialog), false);
|
||||||
|
waitForSwing();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setText(final String text) {
|
private void setText(final String text) {
|
||||||
|
@ -738,4 +856,8 @@ public class AddEditDialoglTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
assertTrue(tool.execute(cmd, program));
|
assertTrue(tool.execute(cmd, program));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createNamespace(String name) {
|
||||||
|
applyCmd(program, new CreateNamespacesCmd(name, SourceType.USER_DEFINED));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1379,7 +1379,7 @@ public class SymbolTablePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
||||||
Command command = new CreateNamespacesCmd(namespaceName, SourceType.USER_DEFINED);
|
Command command = new CreateNamespacesCmd(namespaceName, SourceType.USER_DEFINED);
|
||||||
if (tool.execute(command, program)) {
|
if (tool.execute(command, program)) {
|
||||||
List<Namespace> namespaces =
|
List<Namespace> namespaces =
|
||||||
NamespaceUtils.getNamespaces(namespaceName, null, program);
|
NamespaceUtils.getNamespaceByPath(program, null, namespaceName);
|
||||||
|
|
||||||
if (namespaces.size() != 1) {
|
if (namespaces.size() != 1) {
|
||||||
Assert.fail("Unable to find the newly created parent namespace.");
|
Assert.fail("Unable to find the newly created parent namespace.");
|
||||||
|
|
|
@ -436,7 +436,7 @@ public class PdbParser {
|
||||||
monitor.checkCanceled();
|
monitor.checkCanceled();
|
||||||
boolean isClass = namespaceMap.get(path);
|
boolean isClass = namespaceMap.get(path);
|
||||||
Namespace parentNamespace =
|
Namespace parentNamespace =
|
||||||
NamespaceUtils.getNamespace(program, path.getParent(), null);
|
NamespaceUtils.getNonFunctionNamespace(program, path.getParent());
|
||||||
if (parentNamespace == null) {
|
if (parentNamespace == null) {
|
||||||
String type = isClass ? "class" : "namespace";
|
String type = isClass ? "class" : "namespace";
|
||||||
log.appendMsg("Error: failed to define " + type + ": " + path);
|
log.appendMsg("Error: failed to define " + type + ": " + path);
|
||||||
|
|
|
@ -37,9 +37,6 @@ import ghidra.util.exception.*;
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>All elements of a namespace path should be namespace symbols and not other
|
* <li>All elements of a namespace path should be namespace symbols and not other
|
||||||
* symbol types.
|
* symbol types.
|
||||||
* <li>If the method takes an address, then the path can contain a function name provided
|
|
||||||
* the address is in the body of the function; otherwise
|
|
||||||
* the names must all be namespaces other than functions.
|
|
||||||
* <li>Absolute paths can optionally start with the global namespace.
|
* <li>Absolute paths can optionally start with the global namespace.
|
||||||
* <li>You can provide a relative path that will start at the given
|
* <li>You can provide a relative path that will start at the given
|
||||||
* parent namespace (or global if there is no parent provided).
|
* parent namespace (or global if there is no parent provided).
|
||||||
|
@ -129,6 +126,28 @@ public class NamespaceUtils {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all namespaces with the given name in the parent namespace
|
||||||
|
*
|
||||||
|
* @param program the program to search
|
||||||
|
* @param parent the parent namespace from which to find all namespaces with the given name;
|
||||||
|
* if null, the global namespace will be used
|
||||||
|
* @param namespaceName the name of the namespaces to retrieve
|
||||||
|
* @return a list of all namespaces that match the given name in the given parent namespace.
|
||||||
|
*/
|
||||||
|
public static List<Namespace> getNamespacesByName(Program program, Namespace parent,
|
||||||
|
String namespaceName) {
|
||||||
|
validate(program, parent);
|
||||||
|
List<Namespace> namespaceList = new ArrayList<>();
|
||||||
|
List<Symbol> symbols = program.getSymbolTable().getSymbols(namespaceName, parent);
|
||||||
|
for (Symbol symbol : symbols) {
|
||||||
|
if (symbol.getSymbolType().isNamespace()) {
|
||||||
|
namespaceList.add((Namespace) symbol.getObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return namespaceList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of namespaces that match the given path. The path can be
|
* Returns a list of namespaces that match the given path. The path can be
|
||||||
* relative to the given root namespace or absolute if the path begins with
|
* relative to the given root namespace or absolute if the path begins with
|
||||||
|
@ -136,36 +155,37 @@ public class NamespaceUtils {
|
||||||
*
|
*
|
||||||
* <P>Note: this path must only contain Namespace names and no other symbol types.
|
* <P>Note: this path must only contain Namespace names and no other symbol types.
|
||||||
*
|
*
|
||||||
* @param namespacePath the path to the desired namespace.
|
* @param program the program to search
|
||||||
* @param rootNamespace the namespace to use as the root for relative paths. If null, the
|
* @param parent the namespace to use as the root for relative paths. If null, the
|
||||||
* global namespace will be used.
|
* global namespace will be used
|
||||||
* @param program the program to search.
|
* @param pathString the path to the desired namespace
|
||||||
* @return a list of namespaces that match the given path.
|
* @return a list of namespaces that match the given path
|
||||||
*/
|
*/
|
||||||
public static List<Namespace> getNamespaces(String namespacePath, Namespace rootNamespace,
|
public static List<Namespace> getNamespaceByPath(Program program, Namespace parent,
|
||||||
Program program) {
|
String pathString) {
|
||||||
|
|
||||||
validate(program, rootNamespace);
|
validate(program, parent);
|
||||||
|
|
||||||
rootNamespace = adjustForNullRootNamespace(rootNamespace, namespacePath, program);
|
parent = adjustForNullRootNamespace(parent, pathString, program);
|
||||||
|
|
||||||
SymbolPath path = new SymbolPath(rootNamespace.getSymbol());
|
SymbolPath path = new SymbolPath(parent.getSymbol());
|
||||||
if (namespacePath != null) {
|
if (pathString != null) {
|
||||||
path = path.append(new SymbolPath(namespacePath));
|
path = path.append(new SymbolPath(pathString));
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> namespaceNames = path.asList();
|
List<String> namespaceNames = path.asList();
|
||||||
|
List<Namespace> namespaces = doGetNamespaces(namespaceNames, parent, program);
|
||||||
List<Namespace> namespaces = doGetNamespaces(namespaceNames, rootNamespace, program);
|
|
||||||
return namespaces;
|
return namespaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Namespace> doGetNamespaces(List<String> namespaceNames,
|
private static List<Namespace> doGetNamespaces(List<String> namespaceNames,
|
||||||
Namespace rootNamespace, Program program) {
|
Namespace root, Program program) {
|
||||||
if (rootNamespace == null) {
|
|
||||||
rootNamespace = program.getGlobalNamespace();
|
if (root == null) {
|
||||||
|
root = program.getGlobalNamespace();
|
||||||
}
|
}
|
||||||
List<Namespace> parents = Arrays.asList(rootNamespace);
|
|
||||||
|
List<Namespace> parents = Arrays.asList(root);
|
||||||
for (String name : namespaceNames) {
|
for (String name : namespaceNames) {
|
||||||
List<Namespace> matches = getMatchingNamespaces(name, parents, program);
|
List<Namespace> matches = getMatchingNamespaces(name, parents, program);
|
||||||
parents = matches;
|
parents = matches;
|
||||||
|
@ -174,19 +194,19 @@ public class NamespaceUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list all namespaces that have the given name in any of the given namespaces.
|
* Returns a list all namespaces that have the given name in any of the given namespaces
|
||||||
*
|
*
|
||||||
* @param childName the name of the namespaces to retrieve.
|
* @param childName the name of the namespaces to retrieve
|
||||||
* @param parents a list of all namespaces to search for child namespaces with the given name.
|
* @param parents a list of all namespaces to search for child namespaces with the given name
|
||||||
* @param program the program to search.
|
* @param program the program to search
|
||||||
* @return a list all namespaces that have the given name in any of the given namespaces.
|
* @return a list all namespaces that have the given name in any of the given namespaces
|
||||||
*/
|
*/
|
||||||
public static List<Namespace> getMatchingNamespaces(String childName, List<Namespace> parents,
|
public static List<Namespace> getMatchingNamespaces(String childName, List<Namespace> parents,
|
||||||
Program program) {
|
Program program) {
|
||||||
validate(program, parents);
|
validate(program, parents);
|
||||||
List<Namespace> list = new ArrayList<>();
|
List<Namespace> list = new ArrayList<>();
|
||||||
for (Namespace parent : parents) {
|
for (Namespace parent : parents) {
|
||||||
list.addAll(getNamespaces(parent, childName, program));
|
list.addAll(getNamespacesByName(program, parent, childName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
|
@ -250,27 +270,6 @@ public class NamespaceUtils {
|
||||||
return searchForAllSymbolsInAnyOfTheseNamespaces(parents, symbolPath.getName(), program);
|
return searchForAllSymbolsInAnyOfTheseNamespaces(parents, symbolPath.getName(), program);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of all namespaces with the given name in the parent namespace.
|
|
||||||
* @param parent the parent namespace from which to find all namespaces with the given name.
|
|
||||||
* @param namespaceName the name of the namespaces to retrieve.
|
|
||||||
* @param program the program to search.
|
|
||||||
* @return a list of all namespaces that match the given name in
|
|
||||||
* the given parent namespace.
|
|
||||||
*/
|
|
||||||
public static List<Namespace> getNamespaces(Namespace parent, String namespaceName,
|
|
||||||
Program program) {
|
|
||||||
validate(program, parent);
|
|
||||||
List<Namespace> namespaceList = new ArrayList<>();
|
|
||||||
List<Symbol> symbols = program.getSymbolTable().getSymbols(namespaceName, parent);
|
|
||||||
for (Symbol symbol : symbols) {
|
|
||||||
if (symbol.getSymbolType().isNamespace()) {
|
|
||||||
namespaceList.add((Namespace) symbol.getObject());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return namespaceList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first namespace with the given name and that is NOT a function that
|
* Returns the first namespace with the given name and that is NOT a function that
|
||||||
* is within the parent namespace. (ie. the first namespace that is not tied to a program
|
* is within the parent namespace. (ie. the first namespace that is not tied to a program
|
||||||
|
@ -331,17 +330,22 @@ public class NamespaceUtils {
|
||||||
* and uses the given address to resolve functions with duplicate names. When
|
* and uses the given address to resolve functions with duplicate names. When
|
||||||
* resolving down the namespace path, a function that matches a name will only
|
* resolving down the namespace path, a function that matches a name will only
|
||||||
* be used if the given address is contained in the body of that function.
|
* be used if the given address is contained in the body of that function.
|
||||||
* <P>
|
|
||||||
* The root namespace can be a function.
|
|
||||||
*
|
*
|
||||||
* @param namespacePath The namespace name or path string to be parsed.
|
* <p>The root namespace can be a function.
|
||||||
* This value should not include a trailing symbol name, only namespace names.
|
*
|
||||||
|
* <p>If an address is passed, then the path can contain a function name provided the
|
||||||
|
* address is in the body of the function; otherwise the names must all be namespaces other
|
||||||
|
* than functions.
|
||||||
|
*
|
||||||
|
* @param namespacePath The namespace name or path string to be parsed
|
||||||
|
* This value should not include a trailing symbol name, only namespace names
|
||||||
* @param rootNamespace The parent namespace under which the desired
|
* @param rootNamespace The parent namespace under which the desired
|
||||||
* namespace or path resides. If this value is null, then the
|
* namespace or path resides. If this value is null, then the
|
||||||
* global namespace will be used.
|
* global namespace will be used.
|
||||||
* @param program The current program in which the desired namespace
|
* @param program The current program in which the desired namespace
|
||||||
* resides.
|
* resides
|
||||||
* @param address the address used to resolve possible functions with duplicate names.
|
* @param address the address used to resolve possible functions with duplicate names; may
|
||||||
|
* be null
|
||||||
* @param source the source of the namespace
|
* @param source the source of the namespace
|
||||||
* @return The namespace that matches the given path. This can be either an existing
|
* @return The namespace that matches the given path. This can be either an existing
|
||||||
* namespace or a newly created one.
|
* namespace or a newly created one.
|
||||||
|
@ -358,8 +362,8 @@ public class NamespaceUtils {
|
||||||
if (namespacePath == null) {
|
if (namespacePath == null) {
|
||||||
return rootNamespace;
|
return rootNamespace;
|
||||||
}
|
}
|
||||||
SymbolPath path = new SymbolPath(namespacePath);
|
|
||||||
|
|
||||||
|
SymbolPath path = new SymbolPath(namespacePath);
|
||||||
List<String> namespacesList = path.asList();
|
List<String> namespacesList = path.asList();
|
||||||
|
|
||||||
SymbolTable symbolTable = program.getSymbolTable();
|
SymbolTable symbolTable = program.getSymbolTable();
|
||||||
|
@ -382,40 +386,76 @@ public class NamespaceUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the namespace for the given symbolPath. Since this method
|
* Returns the existing Function at the given address if its {@link SymbolPath} matches the
|
||||||
* takes an address, the symbolPath can contain a function name provided the address
|
* given path
|
||||||
* lives in the body of that function.
|
|
||||||
*
|
*
|
||||||
* @param program the program from which to get the namespace.
|
* @param program the program
|
||||||
* @param symbolPath the path of namespace names including the name of the desired namespace.
|
* @param symbolPath the path of namespace
|
||||||
* @param address the address used to determine if any function namespaces can be used
|
* @param address the address
|
||||||
* to resolve the path. For a function to be used, it must contain this address in it's body.
|
* @return the namespace represented by the given path, or null if no such namespace exists
|
||||||
* @return the namespace represented by the given path, or null if no such namespace exists.
|
|
||||||
*/
|
*/
|
||||||
public static Namespace getNamespace(Program program, SymbolPath symbolPath, Address address) {
|
public static Namespace getFunctionNamespaceAt(Program program, SymbolPath symbolPath,
|
||||||
if (address == null) {
|
Address address) {
|
||||||
if (symbolPath == null) {
|
|
||||||
return program.getGlobalNamespace();
|
if (symbolPath == null || address == null) {
|
||||||
}
|
|
||||||
List<Symbol> symbols = getSymbols(symbolPath, program);
|
|
||||||
for (Symbol symbol : symbols) {
|
|
||||||
if (symbol.getSymbolType() != SymbolType.FUNCTION &&
|
|
||||||
symbol.getSymbolType().isNamespace()) {
|
|
||||||
return (Namespace) symbol.getObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (symbolPath == null) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
Symbol[] symbols = program.getSymbolTable().getSymbols(address);
|
Symbol[] symbols = program.getSymbolTable().getSymbols(address);
|
||||||
for (Symbol symbol : symbols) {
|
for (Symbol symbol : symbols) {
|
||||||
symbol.getSymbolType();
|
if (symbol.getSymbolType() == SymbolType.FUNCTION &&
|
||||||
if (symbol.getSymbolType() == SymbolType.FUNCTION &&
|
symbolPath.matchesPathOf(symbol)) {
|
||||||
symbolPath.equals(new SymbolPath(symbol))) {
|
return (Function) symbol.getObject();
|
||||||
return (Function) symbol.getObject();
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the existing Function containing the given address if its
|
||||||
|
* {@link SymbolPath} matches the given path
|
||||||
|
*
|
||||||
|
* @param program the program
|
||||||
|
* @param symbolPath the path of namespace
|
||||||
|
* @param address the address
|
||||||
|
* @return the namespace represented by the given path, or null if no such namespace exists
|
||||||
|
*/
|
||||||
|
public static Namespace getFunctionNamespaceContaining(Program program, SymbolPath symbolPath,
|
||||||
|
Address address) {
|
||||||
|
|
||||||
|
if (symbolPath == null || address == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionManager fm = program.getFunctionManager();
|
||||||
|
Function f = fm.getFunctionContaining(address);
|
||||||
|
if (f != null) {
|
||||||
|
if (symbolPath.matchesPathOf(f.getSymbol())) {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the namespace for the given symbol path <b>that is not a function</b>
|
||||||
|
*
|
||||||
|
* @param program the program from which to get the namespace
|
||||||
|
* @param symbolPath the path of namespace names including the name of the desired namespace
|
||||||
|
* @return the namespace represented by the given path, or null if no such namespace exists or
|
||||||
|
* the namespace is a function
|
||||||
|
*/
|
||||||
|
public static Namespace getNonFunctionNamespace(Program program, SymbolPath symbolPath) {
|
||||||
|
|
||||||
|
if (symbolPath == null) {
|
||||||
|
return program.getGlobalNamespace();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Symbol> symbols = getSymbols(symbolPath, program);
|
||||||
|
for (Symbol symbol : symbols) {
|
||||||
|
if (symbol.getSymbolType() != SymbolType.FUNCTION &&
|
||||||
|
symbol.getSymbolType().isNamespace()) {
|
||||||
|
return (Namespace) symbol.getObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -483,11 +523,12 @@ public class NamespaceUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a namespace to a class by copying all namespace children into a newly created class
|
* Convert a namespace to a class by copying all namespace children into a newly created class
|
||||||
* and then removing the old namespace.
|
* and then removing the old namespace
|
||||||
|
*
|
||||||
* @param namespace namespace to be converted
|
* @param namespace namespace to be converted
|
||||||
* @return new class namespace
|
* @return new class namespace
|
||||||
* @throws InvalidInputException if namespace was contained within a function and can not be
|
* @throws InvalidInputException if namespace was contained within a function and can not be
|
||||||
* converted to a class
|
* converted to a class
|
||||||
*/
|
*/
|
||||||
public static GhidraClass convertNamespaceToClass(Namespace namespace)
|
public static GhidraClass convertNamespaceToClass(Namespace namespace)
|
||||||
throws InvalidInputException {
|
throws InvalidInputException {
|
||||||
|
@ -530,7 +571,7 @@ public class NamespaceUtils {
|
||||||
"Namespace contained within Function may not be converted to a class: " + name);
|
"Namespace contained within Function may not be converted to a class: " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// move everything from old namespace into new class namespce
|
// move everything from old namespace into new class namespace
|
||||||
try {
|
try {
|
||||||
for (Symbol s : symbolTable.getSymbols(namespace)) {
|
for (Symbol s : symbolTable.getSymbols(namespace)) {
|
||||||
s.setNamespace(classNamespace);
|
s.setNamespace(classNamespace);
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.util.*;
|
||||||
import ghidra.program.model.address.GlobalNamespace;
|
import ghidra.program.model.address.GlobalNamespace;
|
||||||
import ghidra.program.model.listing.Library;
|
import ghidra.program.model.listing.Library;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience object for parsing a namespace path to a symbol.
|
* A convenience object for parsing a namespace path to a symbol.
|
||||||
|
@ -190,6 +189,16 @@ public class SymbolPath implements Comparable<SymbolPath> {
|
||||||
return new SymbolPath(list);
|
return new SymbolPath(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this path contains any path entry matching the given text
|
||||||
|
*
|
||||||
|
* @param text the text for which to search
|
||||||
|
* @return true if any path entry matches the given text
|
||||||
|
*/
|
||||||
|
public boolean containsPathEntry(String text) {
|
||||||
|
return asList().contains(text);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
|
@ -211,15 +220,25 @@ public class SymbolPath implements Comparable<SymbolPath> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
SymbolPath other = (SymbolPath) obj;
|
SymbolPath other = (SymbolPath) obj;
|
||||||
if (!SystemUtilities.isEqual(parentPath, other.parentPath)) {
|
if (!Objects.equals(parentPath, other.parentPath)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!SystemUtilities.isEqual(symbolName, other.symbolName)) {
|
if (!Objects.equals(symbolName, other.symbolName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A convenience method to check if the given symbol's symbol path matches this path
|
||||||
|
*
|
||||||
|
* @param s the symbol to check
|
||||||
|
* @return true if the symbol paths match
|
||||||
|
*/
|
||||||
|
public boolean matchesPathOf(Symbol s) {
|
||||||
|
return equals(new SymbolPath(s));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of names of the symbols in the symbol path, starting with the name just
|
* Returns a list of names of the symbols in the symbol path, starting with the name just
|
||||||
* below the global namespace.
|
* below the global namespace.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue