diff --git a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTreePlugin/SymbolTree.htm b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTreePlugin/SymbolTree.htm index cbf1d7a164..d09178d123 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/SymbolTreePlugin/SymbolTree.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/SymbolTreePlugin/SymbolTree.htm @@ -184,6 +184,13 @@

Shows all locations that reference the given symbol.

+

Convert Namespace to Class

+ +
+

You can convert a Namespace to a Class. + Right mouse click on a namespace and choose the Convert To Class option.

+
+

Create a Class

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableProvider.java index 3445e4f283..40e27b21ce 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/string/StringTableProvider.java @@ -316,8 +316,6 @@ public class StringTableProvider extends ComponentProviderAdapter implements Dom }; selectionNavigationAction = new SelectionNavigationAction(plugin, table); - selectionNavigationAction.setHelpLocation( - new HelpLocation(HelpTopics.SEARCH, "Strings_Selection_Navigation")); addLocalAction(selectionNavigationAction); addLocalAction(selectAction); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java index 349011a304..ab81ad4a09 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/SymbolTreeProvider.java @@ -206,8 +206,15 @@ public class SymbolTreeProvider extends ComponentProviderAdapter { DockingAction setExternalProgramAction = new SetExternalProgramAction(plugin, this); DockingAction createExternalLocationAction = new CreateExternalLocationAction(plugin); DockingAction editExternalLocationAction = new EditExternalLocationAction(plugin); - DockingAction createClassAction = new CreateClassAction(plugin); - DockingAction createNamespaceAction = new CreateNamespaceAction(plugin); + + String createGroup = "0Create"; + int createGroupIndex = 0; + DockingAction createNamespaceAction = new CreateNamespaceAction(plugin, createGroup, + Integer.toString(createGroupIndex++)); + DockingAction createClassAction = new CreateClassAction(plugin, createGroup, + Integer.toString(createGroupIndex++)); + DockingAction convertToClassAction = new ConvertToClassAction(plugin, createGroup, + Integer.toString(createGroupIndex++)); DockingAction renameAction = new RenameAction(plugin); DockingAction cutAction = new CutAction(plugin, this); @@ -231,6 +238,7 @@ public class SymbolTreeProvider extends ComponentProviderAdapter { tool.addLocalAction(this, editExternalLocationAction); tool.addLocalAction(this, createClassAction); tool.addLocalAction(this, createNamespaceAction); + tool.addLocalAction(this, convertToClassAction); tool.addLocalAction(this, renameAction); tool.addLocalAction(this, cutAction); tool.addLocalAction(this, pasteAction); diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/ConvertToClassAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/ConvertToClassAction.java new file mode 100644 index 0000000000..935715b2ba --- /dev/null +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/ConvertToClassAction.java @@ -0,0 +1,99 @@ +/* ### + * 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.app.plugin.core.symboltree.actions; + +import javax.swing.tree.TreePath; + +import docking.action.MenuData; +import docking.widgets.tree.GTreeNode; +import ghidra.app.plugin.core.symboltree.*; +import ghidra.app.plugin.core.symboltree.nodes.SymbolNode; +import ghidra.app.util.NamespaceUtils; +import ghidra.program.model.listing.Program; +import ghidra.program.model.symbol.*; +import ghidra.util.exception.AssertException; +import ghidra.util.exception.InvalidInputException; + +/** + * Symbol tree action for converting a namespace to a class + */ +public class ConvertToClassAction extends SymbolTreeContextAction { + + private static final String NAME = "Convert to Class"; + + public ConvertToClassAction(SymbolTreePlugin plugin, String group, String subGroup) { + super(NAME, plugin.getName()); + MenuData menuData = new MenuData(new String[] { NAME }, group); + menuData.setMenuSubGroup(subGroup); + setPopupMenuData(menuData); + setEnabled(false); + } + + @Override + public boolean isEnabledForContext(SymbolTreeActionContext context) { + TreePath[] selectionPaths = context.getSelectedSymbolTreePaths(); + if (selectionPaths.length != 1) { + return false; + } + + Object object = selectionPaths[0].getLastPathComponent(); + if (object instanceof SymbolNode) { + SymbolNode symbolNode = (SymbolNode) object; + Symbol symbol = symbolNode.getSymbol(); + return symbol.getSymbolType() == SymbolType.NAMESPACE; + } + return false; + } + + @Override + protected void actionPerformed(SymbolTreeActionContext context) { + TreePath[] selectionPaths = context.getSelectedSymbolTreePaths(); + + Program program = context.getProgram(); + GTreeNode node = (GTreeNode) selectionPaths[0].getLastPathComponent(); + + SymbolGTree tree = context.getSymbolTree(); + GTreeNode root = tree.getViewRoot(); + GTreeNode classesNode = root.getChild(SymbolCategory.CLASS_CATEGORY.getName()); + + Symbol symbol = ((SymbolNode) node).getSymbol(); + Namespace namespace = (Namespace) symbol.getObject(); + if (namespace != null) { + String name = namespace.getName(); + convertToClass(program, namespace); + program.flushEvents(); + context.getSymbolTree().startEditing(classesNode, name); + } + } + + private static void convertToClass(Program program, Namespace ns) { + int id = program.startTransaction(NAME); + boolean success = false; + try { + NamespaceUtils.convertNamespaceToClass(ns); + success = true; + } + catch (InvalidInputException e) { + // This is thrown when the provided namespace is a function + // It was checked in isEnabledForContext and thus cannot occur + throw new AssertException(e); + } + finally { + program.endTransaction(id, success); + } + } + +} diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/CreateClassAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/CreateClassAction.java index 64e1603810..7ebdaef8ca 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/CreateClassAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/CreateClassAction.java @@ -33,9 +33,11 @@ import ghidra.util.exception.InvalidInputException; public class CreateClassAction extends SymbolTreeContextAction { - public CreateClassAction(SymbolTreePlugin plugin) { + public CreateClassAction(SymbolTreePlugin plugin, String group, String subGroup) { super("Create Class", plugin.getName()); - setPopupMenuData(new MenuData(new String[] { "Create Class" }, "0Create")); + MenuData menuData = new MenuData(new String[] { "Create Class" }, group); + menuData.setMenuSubGroup(subGroup); + setPopupMenuData(menuData); setEnabled(false); } @@ -54,23 +56,25 @@ public class CreateClassAction extends SymbolTreeContextAction { protected boolean isEnabledForContext(SymbolTreeActionContext context) { TreePath[] selectionPaths = context.getSelectedSymbolTreePaths(); - if (selectionPaths.length == 1) { - Object object = selectionPaths[0].getLastPathComponent(); - if (object instanceof ClassCategoryNode) { - return true; - } - else if (object instanceof SymbolNode) { - SymbolNode symbolNode = (SymbolNode) object; - Symbol symbol = symbolNode.getSymbol(); - SymbolType symbolType = symbol.getSymbolType(); - if (symbolType == SymbolType.NAMESPACE) { - // allow SymbolType to perform additional checks - Namespace parentNamespace = (Namespace) symbol.getObject(); - return SymbolType.CLASS.isValidParent(context.getProgram(), parentNamespace, - Address.NO_ADDRESS, parentNamespace.isExternal()); - } - return (symbolType == SymbolType.CLASS || symbolType == SymbolType.LIBRARY); + if (selectionPaths.length != 1) { + return false; + } + + Object object = selectionPaths[0].getLastPathComponent(); + if (object instanceof ClassCategoryNode) { + return true; + } + else if (object instanceof SymbolNode) { + SymbolNode symbolNode = (SymbolNode) object; + Symbol symbol = symbolNode.getSymbol(); + SymbolType symbolType = symbol.getSymbolType(); + if (symbolType == SymbolType.NAMESPACE) { + // allow SymbolType to perform additional checks + Namespace parentNamespace = (Namespace) symbol.getObject(); + return SymbolType.CLASS.isValidParent(context.getProgram(), parentNamespace, + Address.NO_ADDRESS, parentNamespace.isExternal()); } + return (symbolType == SymbolType.CLASS || symbolType == SymbolType.LIBRARY); } return false; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/CreateNamespaceAction.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/CreateNamespaceAction.java index 22e8d30e9a..25e301db59 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/CreateNamespaceAction.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/symboltree/actions/CreateNamespaceAction.java @@ -32,9 +32,11 @@ import ghidra.util.exception.InvalidInputException; public class CreateNamespaceAction extends SymbolTreeContextAction { - public CreateNamespaceAction(SymbolTreePlugin plugin) { + public CreateNamespaceAction(SymbolTreePlugin plugin, String group, String subGroup) { super("Create Namespace", plugin.getName()); - setPopupMenuData(new MenuData(new String[] { "Create Namespace" }, "0Create")); + MenuData menuData = new MenuData(new String[] { "Create Namespace" }, group); + menuData.setMenuSubGroup(subGroup); + setPopupMenuData(menuData); setEnabled(false); } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java index bfded77431..e18ca98a78 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/headless/HeadlessAnalyzer.java @@ -1193,7 +1193,8 @@ public class HeadlessAnalyzer { // This can never happen because there is no user interaction in headless! } catch (Exception exc) { - Msg.error(this, domFile.getPathname() + " Error during analysis: " + exc.getMessage()); + Msg.error(this, domFile.getPathname() + " Error during analysis: " + exc.getMessage(), + exc); } finally { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/plugins/importer/tasks/ImportBatchTask.java b/Ghidra/Features/Base/src/main/java/ghidra/plugins/importer/tasks/ImportBatchTask.java index 47f5b45baa..93cb17dc47 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/plugins/importer/tasks/ImportBatchTask.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/plugins/importer/tasks/ImportBatchTask.java @@ -173,7 +173,7 @@ public class ImportBatchTask extends Task { Msg.debug(this, "Batch Import cancelled"); } catch (DuplicateNameException | InvalidNameException | VersionException - | IOException e) { + | IOException | IllegalArgumentException e) { Msg.error(this, "Import failed for " + batchLoadConfig.getPreferredFileName(), e); } } diff --git a/Ghidra/Features/Base/src/test.slow/java/docking/widgets/tree/GTreeEventTest.java b/Ghidra/Features/Base/src/test.slow/java/docking/widgets/tree/GTreeEventTest.java index fc639c9f9e..1e70b03e10 100644 --- a/Ghidra/Features/Base/src/test.slow/java/docking/widgets/tree/GTreeEventTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/docking/widgets/tree/GTreeEventTest.java @@ -36,7 +36,7 @@ public class GTreeEventTest extends AbstractDockingTest { private DockingWindowManager winMgr; - private List events = new ArrayList(); + private List events = new ArrayList<>(); private GTreeNode root; @Before @@ -55,7 +55,7 @@ public class GTreeEventTest extends AbstractDockingTest { @Test public void testNodeAdded() { - root.addNode(new LeafNode("ABC")); + root.addNode(new LeafNode("NEW ABC")); assertEquals(1, events.size()); TreeEvent treeEvent = events.get(0); assertEquals(EventType.INSERTED, treeEvent.eventType); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreePlugin2Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreePlugin2Test.java index 7a023be384..89c63584b1 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreePlugin2Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/symboltree/SymbolTreePlugin2Test.java @@ -62,6 +62,7 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest { private DockingActionIf selectionAction; private DockingActionIf createNamespaceAction; private DockingActionIf createClassAction; + private DockingActionIf convertToClassAction; private ToggleDockingAction goToToggleAction; private SymbolTreeTestUtils util; private SymbolGTree tree; @@ -225,7 +226,7 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest { for (int i = 0; i < count - 1; i++) { GTreeNode n = ghidraNode.getChild(i); Symbol s = ((SymbolNode) n).getSymbol(); - assertTrue(!s.getName().equals("AnotherLocal")); + assertFalse(s.getName().equals("AnotherLocal")); } // test undo/redo @@ -254,7 +255,7 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest { for (int i = 0; i < count - 1; i++) { GTreeNode n = ghidraNode.getChild(i); Symbol s = ((SymbolNode) n).getSymbol(); - assertTrue(!s.getName().equals("AnotherLocal")); + assertNotEquals("AnotherLocal", s.getName()); } } @@ -295,11 +296,21 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest { } - private void performTreeAction(DockingActionIf action, ActionContext context) { - assertTrue(action.isEnabledForContext(context)); - performAction(action, context, true); - program.flushEvents(); - util.waitForTree(); + @Test + public void testConvertNamespaceToClass() throws Exception { + String classNodeName = "MyClass"; + GTreeNode nsNode = rootNode.getChild(SymbolCategory.NAMESPACE_CATEGORY.getName()); + GTreeNode classNode = util.createObject( + nsNode, classNodeName, createNamespaceAction); + + util.selectNode(classNode); + ActionContext context = util.getSymbolTreeContext(); + performTreeAction(convertToClassAction, context); + + GTreeNode classRootNode = rootNode.getChild(SymbolCategory.CLASS_CATEGORY.getName()); + classNode = classRootNode.getChild(classNodeName); + assertNotNull(classNode); + waitForCondition(tree::isEditing); } @Test @@ -313,13 +324,13 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest { util.selectNode(lNode); ActionContext context = util.getSymbolTreeContext(); - assertTrue(!renameAction.isEnabledForContext(context)); - assertTrue(!cutAction.isEnabledForContext(context)); - assertTrue(!pasteAction.isEnabledForContext(context)); - assertTrue(!deleteAction.isEnabledForContext(context)); - assertTrue(!selectionAction.isEnabledForContext(context)); - assertTrue(!createNamespaceAction.isEnabledForContext(context)); - assertTrue(!createClassAction.isEnabledForContext(context)); + assertFalse(renameAction.isEnabledForContext(context)); + assertFalse(cutAction.isEnabledForContext(context)); + assertFalse(pasteAction.isEnabledForContext(context)); + assertFalse(deleteAction.isEnabledForContext(context)); + assertFalse(selectionAction.isEnabledForContext(context)); + assertFalse(createNamespaceAction.isEnabledForContext(context)); + assertFalse(createClassAction.isEnabledForContext(context)); } @Test @@ -329,16 +340,11 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest { util.expandNode(lNode); // add a label - SymbolTable symTable = program.getSymbolTable(); - int transactionID = program.startTransaction("test"); - try { + tx(program, () -> { + SymbolTable symTable = program.getSymbolTable(); symTable.createLabel(util.addr(0x010048a1L), "abcdefg", SourceType.USER_DEFINED); - } - finally { - program.endTransaction(transactionID, true); - } + }); - program.flushEvents(); util.waitForTree(); lNode = rootNode.getChild(3); @@ -355,18 +361,13 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest { // verify that the tree updates Function f = program.getFunctionManager().getFunctionAt(util.addr(0x01002cf5L)); - Symbol s = getUniqueSymbol(program, "AnotherLocal", f); assertNotNull(s); - int transactionID = program.startTransaction("test"); - try { + + tx(program, () -> { s.setName("MyAnotherLocal", SourceType.USER_DEFINED); - } - finally { - program.endTransaction(transactionID, true); - } - program.flushEvents(); - waitForSwing(); + }); + util.waitForTree(); GTreeNode fNode = getFunctionsNode(); @@ -421,11 +422,20 @@ public class SymbolTreePlugin2Test extends AbstractGhidraHeadedIntegrationTest { assertNotNull(createClassAction); createNamespaceAction = getAction(plugin, "Create Namespace"); assertNotNull(createNamespaceAction); + convertToClassAction = getAction(plugin, "Convert to Class"); + assertNotNull(convertToClassAction); goToToggleAction = (ToggleDockingAction) getAction(plugin, "Navigation"); assertNotNull(goToToggleAction); } + private void performTreeAction(DockingActionIf action, ActionContext context) { + assertTrue(action.isEnabledForContext(context)); + performAction(action, context, true); + program.flushEvents(); + util.waitForTree(); + } + private void clickOnNode(GTreeNode node) throws Exception { JTree jTree = (JTree) AbstractGenericTest.getInstanceField("tree", tree); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc index 1155b22c03..8c193d66fa 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.cc @@ -102,7 +102,7 @@ Architecture::Architecture(void) commentdb = (CommentDatabase *)0; stringManager = (StringManager *)0; cpool = (ConstantPool *)0; - symboltab = new Database(this); + symboltab = (Database *)0; context = (ContextDatabase *)0; print = PrintLanguageCapability::getDefault()->buildLanguage(this); printlist.push_back(print); @@ -131,7 +131,8 @@ Architecture::~Architecture(void) for(int4 i=0;igetGlobalScope(); int4 nm = numSpaces(); for(int4 i=0;igetGlobalScope(); - if (globscope == (Scope *)0) { // Make sure global scope exists - globscope = new ScopeInternal("",this); - symboltab->attachScope(globscope,(Scope *)0); - } + symboltab = new Database(this,true); + Scope *globscope = new ScopeInternal(0,"",this); + symboltab->attachScope(globscope,(Scope *)0); return globscope; } @@ -802,7 +803,7 @@ void Architecture::parseDefaultProto(const Element *el) void Architecture::parseGlobal(const Element *el) { - Scope *scope = buildGlobalScope(); + Scope *scope = symboltab->getGlobalScope(); const List &list(el->getChildren()); List::const_iterator iter; @@ -829,15 +830,15 @@ void Architecture::parseGlobal(const Element *el) void Architecture::addOtherSpace(void) { - Scope *scope = buildGlobalScope(); + Scope *scope = symboltab->getGlobalScope(); AddrSpace *otherSpace = getSpaceByName("OTHER"); symboltab->addRange(scope,otherSpace,0,otherSpace->getHighest()); if (otherSpace->isOverlayBase()) { - int4 num = numSpaces(); - for(int4 i=0;igetBaseSpace() != otherSpace) continue; - if (ospc->getBaseSpace() != otherSpace) continue; + int4 num = numSpaces(); + for(int4 i=0;iisOverlay()) continue; + if (((OverlaySpace *)ospc)->getBaseSpace() != otherSpace) continue; symboltab->addRange(scope,ospc,0,otherSpace->getHighest()); } } @@ -1279,9 +1280,11 @@ void Architecture::init(DocumentStorage &store) buildCommentDB(store); buildStringManager(store); buildConstantPool(store); + buildDatabase(store); restoreFromSpec(store); print->getCastStrategy()->setTypeFactory(types); + symboltab->adjustCaches(); // In case the specs created additional address spaces postSpecFile(); // Let subclasses do things after translate is ready buildInstructions(store); // Must be called after translate is built diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh index bfee675511..5e9071b561 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/architecture.hh @@ -206,7 +206,7 @@ protected: void addNoHighPtr(const Range &rng); ///< Add a new region where pointers do not exist // Factory routines for building this architecture - virtual Scope *buildGlobalScope(void); ///< Build the global scope for this executable + virtual Scope *buildDatabase(DocumentStorage &store); ///< Build the database and global scope for this executable /// \brief Build the Translator object /// diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc index b6fc42db88..d5003d9392 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/coreaction.cc @@ -4244,7 +4244,7 @@ Datatype *ActionInferTypes::propagateAddIn2Out(TypeFactory *typegrp,PcodeOp *op, int4 offset = propagateAddPointer(op,inslot); if (offset==-1) return op->getOut()->getTempType(); // Doesn't look like a good pointer add uintb uoffset = AddrSpace::addressToByte(offset,((TypePointer *)rettype)->getWordSize()); - if (tstruct->getSize() > 0) + if (tstruct->getSize() > 0 && !tstruct->isVariableLength()) uoffset = uoffset % tstruct->getSize(); if (uoffset==0) { if (op->code() == CPUI_PTRSUB) // Go down at least one level @@ -4419,7 +4419,7 @@ bool ActionInferTypes::propagateTypeEdge(TypeFactory *typegrp,PcodeOp *op,int4 i } else if (alttype->getMetatype()==TYPE_PTR) { newtype = ((TypePointer *)alttype)->getPtrTo(); - if (newtype->getSize() != outvn->getTempType()->getSize()) // Size must be appropriate + if (newtype->getSize() != outvn->getTempType()->getSize() || newtype->isVariableLength()) // Size must be appropriate newtype = outvn->getTempType(); } else @@ -4432,7 +4432,7 @@ bool ActionInferTypes::propagateTypeEdge(TypeFactory *typegrp,PcodeOp *op,int4 i } else if (alttype->getMetatype()==TYPE_PTR) { newtype = ((TypePointer *)alttype)->getPtrTo(); - if (newtype->getSize() != outvn->getTempType()->getSize()) + if (newtype->getSize() != outvn->getTempType()->getSize() || newtype->isVariableLength()) newtype = outvn->getTempType(); } else diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc index 74f18a1846..b7e8430b87 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.cc @@ -15,6 +15,7 @@ */ #include "database.hh" #include "funcdata.hh" +#include "crc32.hh" #include uint8 Symbol::ID_BASE = 0x4000000000000000L; @@ -812,38 +813,14 @@ MapIterator MapIterator::operator++(int4 i) { return tmp; } -/// Sort first on name, then on dedupId -/// \param op2 is the key to compare with \b this -/// \return \b true if \b this should be ordered before the other key -bool ScopeKey::operator<(const ScopeKey &op2) const - -{ - int4 comp = name.compare(op2.name); - if (comp < 0) return true; - if (comp > 0) return false; - return (dedupId < op2.dedupId); // Use dedupId to (possibly) allow scopes with same name -} - /// Attach the child as an immediate sub-scope of \b this. /// Take responsibility of the child's memory: the child will be freed when this is freed. -/// May throw RecovError if there is a duplicate name issue /// \param child is the Scope to make a child void Scope::attachScope(Scope *child) { child->parent = this; - pair value(ScopeKey(child->name,child->uniqueId),child); - pair res; - if (child->name.size()==0) - throw LowlevelError("Non-global scope has empty name"); - res = children.insert(value); - if (res.second==false) { - ostringstream s; - s << "Duplicate scope name: "; - s << child->getFullName(); - delete child; - throw RecovError(s.str()); - } + children[child->uniqueId] = child; // uniqueId is guaranteed to be unique by Database } /// The indicated child Scope is deleted @@ -856,6 +833,29 @@ void Scope::detachScope(ScopeMap::iterator iter) delete child; } +/// \brief Create a Scope id based on the scope's name and its parent's id +/// +/// Create a globally unique id for a scope simply from its name. +/// \param baseId is the scope id of the parent scope +/// \param nm is the name of scope +/// \return the hash of the parent id and name +uint8 Scope::hashScopeName(uint8 baseId,const string &nm) + +{ + uint4 reg1 = (uint4)(baseId>>32); + uint4 reg2 = (uint4)baseId; + reg1 = crc_update(reg1, 0xa9); + reg2 = crc_update(reg2, reg1); + for(int4 i=0;iname == name) - return scope; - return (Scope *)0; + if (strategy) { + uint8 key = hashScopeName(uniqueId, name); + ScopeMap::const_iterator iter = children.find(key); + if (iter == children.end()) return (Scope *)0; + Scope *scope = (*iter).second; + if (scope->name == name) + return scope; + } + else if (name.length() > 0 && name[0] <= '9' && name[0] >= '0') { + // Allow the string to directly specify the id + istringstream s(name); + s.unsetf(ios::dec | ios::hex | ios::oct); + uint8 key; + s >> key; + ScopeMap::const_iterator iter = children.find(key); + if (iter == children.end()) return (Scope *)0; + return (*iter).second; + } + else { + ScopeMap::const_iterator iter; + for(iter=children.begin();iter!=children.end();++iter) { + Scope *scope = (*iter).second; + if (scope->name == name) + return scope; + } + } + return (Scope *)0; } /// Discover a sub-scope or containing Scope of \b this, that \e owns the given @@ -1395,27 +1415,6 @@ string Scope::getFullName(void) const return fname; } -/// Put the names of \b this and all its parent Scopes into an array in order. -/// The name of the first entry will generally be the name of the global Scope -/// \param vec is the array in which to store the names -void Scope::getNameSegments(vector &vec) const - -{ - int4 count = 0; - const Scope *cur = this; - while(cur != (Scope *)0) { // Count number of segments - count += 1; - cur = cur->parent; - } - vec.resize(count); - cur = this; - while(cur != (Scope *)0) { // Copy each segment - count -= 1; - vec[count] = cur->name; - cur = cur->parent; - } -} - /// Put the parent scopes of \b this into an array in order, starting with the global scope. /// \param vec is storage for the array of scopes void Scope::getScopePath(vector &vec) const @@ -1718,17 +1717,17 @@ bool Scope::isReadOnly(const Address &addr,int4 size,const Address &usepoint) co return ((flags & Varnode::readonly)!=0); } -Scope *ScopeInternal::buildSubScope(const string &nm) +Scope *ScopeInternal::buildSubScope(uint8 id,const string &nm) { - return new ScopeInternal(nm,glb); + return new ScopeInternal(id,nm,glb); } void ScopeInternal::addSymbolInternal(Symbol *sym) { if (sym->symbolId == 0) { - sym->symbolId = Symbol::ID_BASE + (((uint8)uniqueId & 0xffff) << 40) + nextUniqueId; + sym->symbolId = Symbol::ID_BASE + ((uniqueId & 0xffff) << 40) + nextUniqueId; nextUniqueId += 1; } try { @@ -1857,17 +1856,18 @@ list::iterator ScopeInternal::endDynamic(void) return dynamicentry.end(); } +/// \param id is the globally unique id associated with the scope /// \param nm is the name of the Scope /// \param g is the Architecture it belongs to -ScopeInternal::ScopeInternal(const string &nm,Architecture *g) - : Scope(nm,g,this) +ScopeInternal::ScopeInternal(uint8 id,const string &nm,Architecture *g) + : Scope(id,nm,g,this) { nextUniqueId = 0; maptable.resize(g->numSpaces(),(EntryMap *)0); } -ScopeInternal::ScopeInternal(const string &nm,Architecture *g, Scope *own) - : Scope(nm,g,own) +ScopeInternal::ScopeInternal(uint8 id,const string &nm,Architecture *g, Scope *own) + : Scope(id,nm,g,own) { nextUniqueId = 0; maptable.resize(g->numSpaces(),(EntryMap *)0); @@ -2015,6 +2015,12 @@ void ScopeInternal::clearUnlockedCategory(int4 cat) } } +void ScopeInternal::adjustCaches(void) + +{ + maptable.resize(glb->numSpaces(),(EntryMap *)0); +} + void ScopeInternal::removeSymbolMappings(Symbol *symbol) { @@ -2509,45 +2515,18 @@ string ScopeInternal::makeNameUnique(const string &nm) const return resString; } -/// Given a list of name strings, write out each one in an XML \ tag. -/// \param s is the output stream -/// \param vec is the list of names -void ScopeInternal::savePathXml(ostream &s,const vector &vec) - -{ - for(int4 i=0;i"; - xml_escape(s,vec[i].c_str()); - s << "\n"; - } -} - -/// Given an element, parse all of its children (as \ tags) and -/// put each of their content into a string array. -/// \param vec will hold the resulting string array -/// \param el is the XML element -void ScopeInternal::restorePathXml(vector &vec,const Element *el) - -{ - const List &list(el->getChildren()); - List::const_iterator iter; - - for(iter=list.begin();iter!=list.end();++iter) - vec.push_back( (*iter)->getContent() ); -} - void ScopeInternal::saveXml(ostream &s) const { s << "\n"; - vector fname; - getNameSegments(fname); - fname.pop_back(); // Pop 1 level to get parent path - s << "\n"; - savePathXml(s,fname); - s << "\n"; + if (getParent() != (const Scope *)0) { + s << "getId()); + s << "/>\n"; + } getRangeTree().saveXml(s); if (!nametree.empty()) { @@ -2669,8 +2648,11 @@ void ScopeInternal::restoreXml(const Element *el) const Element *subel; iter = list.begin(); - ++iter; // Skip , processed elsewhere subel = *iter; + if (subel->getName() == "parent") { + ++iter; // Skip tag processed elsewhere + subel = *iter; + } if (subel->getName() == "rangelist") { RangeList newrangetree; newrangetree.restoreXml(subel,glb); @@ -2804,17 +2786,18 @@ void Database::clearResolve(Scope *scope) } } -/// This recursively performs clearResolve() on the Scope and any sub-scopes +/// This recursively clears references in idmap or in resolvemap. /// \param scope is the given Scope to clear -void Database::clearResolveRecursive(Scope *scope) +void Database::clearReferences(Scope *scope) { ScopeMap::const_iterator iter = scope->children.begin(); ScopeMap::const_iterator enditer = scope->children.end(); while(iter != enditer) { - clearResolveRecursive((*iter).second); + clearReferences((*iter).second); ++iter; } + idmap.erase(scope->uniqueId); clearResolve(scope); } @@ -2833,6 +2816,18 @@ void Database::fillResolve(Scope *scope) } } +/// Initialize a new symbol table, with no initial scopes or symbols. +/// \param g is the Architecture that owns the symbol table +/// \param isByName is \b true if scope ids are calculated as a hash of the scope name. +Database::Database(Architecture *g,bool idByName) + +{ + glb=g; + globalscope=(Scope *)0; + flagbase.defaultValue()=0; + idByNameHash=idByName; +} + Database::~Database(void) { @@ -2854,27 +2849,47 @@ void Database::attachScope(Scope *newscope,Scope *parent) throw LowlevelError("Multiple global scopes"); if (newscope->name.size() != 0) throw LowlevelError("Global scope does not have empty name"); - newscope->assignId(0); globalscope = newscope; + idmap[globalscope->uniqueId] = globalscope; return; } - newscope->assignId(nextScopeId); - nextScopeId += 1; + if (newscope->name.size()==0) + throw LowlevelError("Non-global scope has empty name"); + pair value(newscope->uniqueId,newscope); + pair res; + res = idmap.insert(value); + if (res.second==false) { + ostringstream s; + s << "Duplicate scope id: "; + s << newscope->getFullName(); + delete newscope; + throw RecovError(s.str()); + } parent->attachScope(newscope); } +/// Give \b this database the chance to inform existing scopes of any change to the +/// configuration, which may have changed since the initial scopes were created. +void Database::adjustCaches(void) + +{ + ScopeMap::iterator iter; + for(iter=idmap.begin();iter!=idmap.end();++iter) { + (*iter).second->adjustCaches(); + } +} + /// \param scope is the given Scope void Database::deleteScope(Scope *scope) { - clearResolveRecursive(scope); + clearReferences(scope); if (globalscope == scope) { globalscope = (Scope *)0; delete scope; } else { - ScopeKey key(scope->name,scope->uniqueId); - ScopeMap::iterator iter = scope->parent->children.find(key); + ScopeMap::iterator iter = scope->parent->children.find(scope->uniqueId); if (iter == scope->parent->children.end()) throw LowlevelError("Could not remove parent reference to: "+scope->name); scope->parent->detachScope(iter); @@ -2892,7 +2907,7 @@ void Database::deleteSubScopes(Scope *scope) while(iter != enditer) { curiter = iter; ++iter; - clearResolveRecursive((*curiter).second); + clearReferences((*curiter).second); scope->detachScope(curiter); } } @@ -2952,42 +2967,33 @@ void Database::removeRange(Scope *scope,AddrSpace *spc,uintb first,uintb last) fillResolve(scope); } -/// Look for an immediate child scope by name in a given parent. If does not exist, -/// create a new scope with the name and attach it to the parent. -/// \param nm is the base name of the desired subscope +/// Look for a Scope by id. If it does not exist, create a new scope +/// with the given name and parent scope. +/// \param id is the global id of the Scope +/// \param nm is the given name of the Scope /// \param parent is the given parent scope to search /// \return the subscope object either found or created -Scope *Database::findCreateSubscope(const string &nm,Scope *parent) +Scope *Database::findCreateScope(uint8 id,const string &nm,Scope *parent) { - Scope *res = parent->resolveScope(nm); + Scope *res = resolveScope(id); if (res != (Scope *)0) return res; - res = globalscope->buildSubScope(nm); + res = globalscope->buildSubScope(id,nm); attachScope(res, parent); return res; } -/// An \e absolute \e path of Scope names must be provided, from the global -/// Scope down to the desired Scope. If the first path name is blank (""), it -/// matches the global Scope. If the first path name is not blank, the -/// global Scope is assumed, and the name is assumed to refer to a child. -/// \param subnames is the \e path of names -/// \return the desired Scope or NULL if a matching name isn't found -Scope *Database::resolveScope(const vector &subnames) const +/// Find a Scope object, given its global id. Return null if id is not mapped to a Scope. +/// \param id is the global id +/// \return the matching Scope or null +Scope *Database::resolveScope(uint8 id) const { - if (subnames.size()==0) return (Scope *)0; - Scope *curScope = globalscope; - int4 i=0; - if (subnames[0].size()==0) // blank name matches global scope - i += 1; - for(;iresolveScope(subnames[i]); - } - return curScope; + ScopeMap::const_iterator iter = idmap.find(id); + if (iter != idmap.end()) + return (*iter).second; + return (Scope *)0; } /// \brief Get the Scope (and base name) associated with a qualified Symbol name @@ -3014,7 +3020,7 @@ Scope *Database::resolveScopeFromSymbolName(const string &fullname,const string endmark = fullname.find(delim,mark); if (endmark == string::npos) break; string scopename = fullname.substr(mark,endmark-mark); - start = start->resolveScope(scopename); + start = start->resolveScope(scopename,idByNameHash); if (start == (Scope *)0) // Was the scope name bad return start; mark = endmark + delim.size(); @@ -3038,6 +3044,8 @@ Scope *Database::resolveScopeFromSymbolName(const string &fullname,const string Scope *Database::findCreateScopeFromSymbolName(const string &fullname,const string &delim,string &basename, Scope *start) { + if (!idByNameHash) + throw LowlevelError("Scope name hashes not allowed"); if (start == (Scope *)0) start = globalscope; @@ -3047,7 +3055,8 @@ Scope *Database::findCreateScopeFromSymbolName(const string &fullname,const stri endmark = fullname.find(delim,mark); if (endmark == string::npos) break; string scopename = fullname.substr(mark,endmark-mark); - start = findCreateSubscope(scopename, start); + uint8 nameId = Scope::hashScopeName(start->uniqueId, scopename); + start = findCreateScope(nameId, scopename, start); mark = endmark + delim.size(); } basename = fullname.substr(mark,endmark); @@ -3130,7 +3139,10 @@ void Database::saveXml(ostream &s) const { partmap::const_iterator piter,penditer; - s << "\n"; + s << "\n"; // Save the property change points piter = flagbase.begin(); penditer = flagbase.end(); @@ -3148,34 +3160,22 @@ void Database::saveXml(ostream &s) const s << "\n"; } -/// \brief Read an XML \ tag for a Scope path -/// -/// The \ tag is assumed to either be the first child of -/// the given element, or the first child of the first child. -/// From the \, the \e name attribute is passed back and -/// a Scope path is parsed from sub-tags. -/// \param el is the given element (with \ as a child) -/// \param name will hold the \e name attribute -/// \param parnames will hold the Scope path -void Database::parseParentTag(const Element *el,string &name,vector &parnames) +/// Parse the given element for the scope id of the parent. +/// Look up the parent scope and return it. +/// Throw an error if there is no matching scope +/// \param el is the given element +/// \return the matching scope +Scope *Database::parseParentTag(const Element *el) { - const List &list1(el->getChildren()); - - const Element *subel1 = *list1.begin(); - if (subel1->getName() == "parent") { - name = el->getAttributeValue("name"); - ScopeInternal::restorePathXml(parnames,subel1); - return; - } - - const List &list2(subel1->getChildren()); - const Element *subel2 = *list2.begin(); - if (subel2->getName() != "parent") - throw LowlevelError("Could not find scopes tag"); - - name = subel1->getAttributeValue("name"); - ScopeInternal::restorePathXml(parnames,subel2); + istringstream s(el->getAttributeValue("id")); + uint8 id = -1; + s.unsetf(ios::dec | ios::hex | ios::oct); + s >> id; + Scope *res = resolveScope(id); + if (res == (Scope *)0) + throw LowlevelError("Could not find scope matching id"); + return res; } /// The children of a \ tag are examined to recover Scope and Symbol objects. @@ -3183,6 +3183,11 @@ void Database::parseParentTag(const Element *el,string &name,vector &par void Database::restoreXml(const Element *el) { + idByNameHash = false; // Default + for(int4 i=0;igetNumAttributes();++i) { + if (el->getAttributeName(i) == "scopeidbyname") + idByNameHash = xml_readbool(el->getAttributeValue(i)); + } const List &list(el->getChildren()); List::const_iterator iter; @@ -3202,31 +3207,40 @@ void Database::restoreXml(const Element *el) for(;iter!=list.end();++iter) { const Element *subel = *iter; - string name; - vector parnames; - parseParentTag(subel,name,parnames); - parnames.push_back(name); - Scope *new_scope = globalscope; - for(int4 i=1;irestoreXml(subel); + string name = subel->getAttributeValue("name"); + istringstream s(subel->getAttributeValue("id")); + s.unsetf(ios::dec | ios::hex | ios::oct); + uint8 id; + s >> id; + const List &sublist(subel->getChildren()); + Scope *parentScope = (Scope *)0; + if (!sublist.empty()) { + const Element *parTag = sublist.front(); + if (parTag->getName() == "parent") + parentScope = parseParentTag(parTag); + } + Scope *newScope = findCreateScope(id, name, parentScope); + newScope->restoreXml(subel); } } /// This allows incremental building of the Database from multiple XML sources. /// An empty Scope must already be allocated. It is registered with \b this Database, -/// and then populated with Symbol objects based as the content of a given \ tag. +/// and then populated with Symbol objects based as the content of a given XML tag. +/// The tag can either be a \ itself, or another tag that wraps a \ tag +/// as its first child. /// \param el is the given \ element -/// \param new_scope is the empty Scope -void Database::restoreXmlScope(const Element *el,Scope *new_scope) +/// \param newScope is the empty Scope +void Database::restoreXmlScope(const Element *el,Scope *newScope) { - Scope *par_scope; - vector parnames; - parseParentTag(el,new_scope->name,parnames); // Shove recovered basename straight into new_scope - par_scope = resolveScope(parnames); - if (par_scope == (Scope *)0) - throw LowlevelError("Bad parent scope"); - attachScope(new_scope,par_scope); - new_scope->restoreXml(el); + const List &list(el->getChildren()); + const Element *parTag = list.front(); + if (el->getName() != "scope") { + const List &sublist(parTag->getChildren()); + parTag = sublist.front(); + } + Scope *parentScope = parseParentTag(parTag); + attachScope(newScope,parentScope); + newScope->restoreXml(el); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh index c651d2ecb2..51d3670536 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database.hh @@ -377,18 +377,7 @@ public: } }; -/// \brief A key for looking up child symbol scopes within a parent, based on name -/// -/// A key for mapping from name to Scope. The class includes a deduplication component -/// if Scopes with the same name are allowed. -class ScopeKey { - string name; ///< The name of the Scope - uintb dedupId; ///< A duplication id for the Scope -public: - ScopeKey(const string &nm,uint4 id) { name = nm; dedupId = id; } ///< Construct given a name and id - bool operator<(const ScopeKey &op2) const; ///< Comparison operator -}; -typedef map ScopeMap; ///< A map from ScopeKey to Scope +typedef map ScopeMap; ///< A map from id to Scope /// \brief A collection of Symbol objects within a single (namespace or functional) scope /// @@ -420,12 +409,12 @@ class Scope { ScopeMap children; ///< Sorted list of child scopes void attachScope(Scope *child); ///< Attach a new child Scope to \b this void detachScope(ScopeMap::iterator iter); ///< Detach a child Scope from \b this - void assignId(uint4 val) { uniqueId = val; } ///< Let the database assign a unique id to \b this scope + static uint8 hashScopeName(uint8 baseId,const string &nm); protected: Architecture *glb; ///< Architecture of \b this scope string name; ///< Name of \b this scope Funcdata *fd; ///< (If non-null) the function which \b this is the local Scope for - uint4 uniqueId; ///< Unique id for the scope, for deduping scope names, assigning symbol ids + uint8 uniqueId; ///< Unique id for the scope, for deduping scope names, assigning symbol ids static const Scope *stackAddr(const Scope *scope1, const Scope *scope2, const Address &addr, @@ -460,9 +449,10 @@ protected: /// /// This is a Scope object \e factory, intended to be called off of the global scope for building /// global namespace scopes. Function scopes are handled differently. + /// \param id is the globally unique id associated with the scope /// \param nm is the name of the new scope /// \return the new Scope object - virtual Scope *buildSubScope(const string &nm)=0; + virtual Scope *buildSubScope(uint8 id,const string &nm)=0; virtual void restrictScope(Funcdata *f); ///< Convert \b this to a local Scope @@ -512,8 +502,8 @@ public: void turnOffDebug(void) const { debugon = false; } #endif /// \brief Construct an empty scope, given a name and Architecture - Scope(const string &nm,Architecture *g,Scope *own) { - name = nm; glb = g; parent = (Scope *)0; fd = (Funcdata *)0; uniqueId = 0; owner=own; + Scope(uint8 id,const string &nm,Architecture *g,Scope *own) { + uniqueId = id; name = nm; glb = g; parent = (Scope *)0; fd = (Funcdata *)0; owner=own; #ifdef OPACTION_DEBUG debugon = false; #endif @@ -530,6 +520,11 @@ public: virtual void clearUnlocked(void)=0; ///< Clear all unlocked symbols from \b this scope virtual void clearUnlockedCategory(int4 cat)=0; ///< Clear unlocked symbols of the given category from \b this scope + /// \brief Let scopes internally adjust any caches + /// + /// This is called once after Architecture configuration is complete. + virtual void adjustCaches(void)=0; + /// \brief Query if the given range is owned by \b this Scope /// /// All bytes in the range must be owned, and ownership can be informed by @@ -680,6 +675,7 @@ public: const Address &addr,const Address &usepoint); const string &getName(void) const { return name; } ///< Get the name of the Scope + uint8 getId(void) const { return uniqueId; } ///< Get the globally unique id bool isGlobal(void) const { return (fd == (Funcdata *)0); } ///< Return \b true if \b this scope is global // The main global querying routines @@ -695,7 +691,7 @@ public: Funcdata *queryExternalRefFunction(const Address &addr) const; ///< Look-up a function thru an \e external \e reference LabSymbol *queryCodeLabel(const Address &addr) const; ///< Look-up a code label by address - Scope *resolveScope(const string &name) const; ///< Find a child Scope of \b this + Scope *resolveScope(const string &name, bool strategy) const; ///< Find a child Scope of \b this Scope *discoverScope(const Address &addr,int4 sz,const Address &usepoint); ///< Find the owning Scope of a given memory range ScopeMap::const_iterator childrenBegin() const { return children.begin(); } ///< Beginning iterator of child scopes ScopeMap::const_iterator childrenEnd() const { return children.end(); } ///< Ending iterator of child scopes @@ -705,7 +701,6 @@ public: void setThisPointer(Symbol *sym,bool val) { sym->setThisPointer(val); } ///< Toggle the given Symbol as the "this" pointer bool isSubScope(const Scope *scp) const; ///< Is this a sub-scope of the given Scope string getFullName(void) const; ///< Get the full name of \b this Scope - void getNameSegments(vector &vec) const; ///< Get the fullname of \b this in segments void getScopePath(vector &vec) const; ///< Get the ordered list of scopes up to \b this const Scope *findDistinguishingScope(const Scope *op2) const; ///< Find first ancestor of \b this not shared by given scope Architecture *getArch(void) const { return glb; } ///< Get the Architecture associated with \b this @@ -735,7 +730,7 @@ class ScopeInternal : public Scope { void insertNameTree(Symbol *sym); SymbolNameTree::const_iterator findFirstByName(const string &name) const; protected: - virtual Scope *buildSubScope(const string &nm); ///< Build an unattached Scope to be associated as a sub-scope of \b this + virtual Scope *buildSubScope(uint8 id,const string &nm); ///< Build an unattached Scope to be associated as a sub-scope of \b this virtual void addSymbolInternal(Symbol *sym); virtual SymbolEntry *addMapInternal(Symbol *sym,uint4 exfl,const Address &addr,int4 off,int4 sz,const RangeList &uselim); virtual SymbolEntry *addDynamicMapInternal(Symbol *sym,uint4 exfl,uint8 hash,int4 off,int4 sz, @@ -747,13 +742,14 @@ protected: SymbolNameTree multiEntrySet; ///< Set of symbols with multiple entries uint8 nextUniqueId; ///< Next available symbol id public: - ScopeInternal(const string &nm,Architecture *g); ///< Construct the Scope - ScopeInternal(const string &nm,Architecture *g, Scope *own); ///< Construct as a cache + ScopeInternal(uint8 id,const string &nm,Architecture *g); ///< Construct the Scope + ScopeInternal(uint8 id,const string &nm,Architecture *g, Scope *own); ///< Construct as a cache virtual void clear(void); virtual void categorySanity(void); ///< Make sure Symbol categories are sane virtual void clearCategory(int4 cat); virtual void clearUnlocked(void); virtual void clearUnlockedCategory(int4 cat); + virtual void adjustCaches(void); virtual ~ScopeInternal(void); virtual MapIterator begin(void) const; virtual MapIterator end(void) const; @@ -797,8 +793,6 @@ public: void assignDefaultNames(int4 &base); ///< Assign a default name (via buildVariableName) to any unnamed symbol set::const_iterator beginMultiEntry(void) const { return multiEntrySet.begin(); } ///< Start of symbols with more than one entry set::const_iterator endMultiEntry(void) const { return multiEntrySet.end(); } ///< End of symbols with more than one entry - static void savePathXml(ostream &s,const vector &vec); ///< Save a path with \ tags - static void restorePathXml(vector &vec,const Element *el); ///< Restore path from \ tags }; /// \brief An Address range associated with the symbol Scope that owns it @@ -850,19 +844,21 @@ typedef rangemap ScopeResolve; ///< A map from address to the owni /// memory ranges. This allows important properties like \e read-only and \e volatile to /// be put down even if the Symbols aren't yet known. class Database { - Architecture *glb; ///< The Architecture to which this symbol table is attached - Scope *globalscope; ///< A quick reference to the \e global Scope - ScopeResolve resolvemap; ///< The Address to \e namespace map + Architecture *glb; ///< Architecture to which this symbol table is attached + Scope *globalscope; ///< Quick reference to the \e global Scope + ScopeResolve resolvemap; ///< Address to \e namespace map + ScopeMap idmap; ///< Map from id to Scope partmap flagbase; ///< Map of global properties - uint4 nextScopeId; ///< Id for next attached scope (0 reserved for global scope) + bool idByNameHash; ///< True if scope ids are built from hash of name void clearResolve(Scope *scope); ///< Clear the \e ownership ranges associated with the given Scope - void clearResolveRecursive(Scope *scope); ///< Clear the \e ownership ranges of a given Scope and its children + void clearReferences(Scope *scope); ///< Clear any map references to the given Scope and its children void fillResolve(Scope *scope); ///< Add the \e ownership ranges of the given Scope to the map - static void parseParentTag(const Element *el,string &name,vector &parnames); + Scope *parseParentTag(const Element *el); ///< Figure out parent scope given \ tag. public: - Database(Architecture *g) { glb=g; globalscope=(Scope *)0; flagbase.defaultValue()=0; nextScopeId=1; } ///< Constructor + Database(Architecture *g,bool idByName); ///< Constructor ~Database(void); ///< Destructor Architecture *getArch(void) const { return glb; } ///< Get the Architecture associate with \b this + void adjustCaches(void); ///< Let scopes adjust after configuration is finished void attachScope(Scope *newscope,Scope *parent); ///< Register a new Scope void deleteScope(Scope *scope); ///< Delete the given Scope and all its sub-scopes void deleteSubScopes(Scope *scope); ///< Delete all sub-scopes of the given Scope @@ -871,9 +867,9 @@ public: void addRange(Scope *scope,AddrSpace *spc,uintb first,uintb last); ///< Add an address range to the \e ownership of a Scope void removeRange(Scope *scope,AddrSpace *spc,uintb first,uintb last); ///< Remove an address range from \e ownership of a Scope Scope *getGlobalScope(void) const { return globalscope; } ///< Get the global Scope - Scope *resolveScope(const vector &subnames) const; ///< Look-up a Scope by name + Scope *resolveScope(uint8 id) const; ///< Look-up a Scope by id Scope *resolveScopeFromSymbolName(const string &fullname,const string &delim,string &basename,Scope *start) const; - Scope *findCreateSubscope(const string &nm,Scope *parent); /// Find (and if not found create) a specific subscope + Scope *findCreateScope(uint8,const string &nm,Scope *parent); /// Find (and if not found create) a specific subscope Scope *findCreateScopeFromSymbolName(const string &fullname,const string &delim,string &basename,Scope *start); const Scope *mapScope(const Scope *qpoint,const Address &addr,const Address &usepoint) const; Scope *mapScope(Scope *qpoint,const Address &addr,const Address &usepoint); @@ -883,7 +879,7 @@ public: const partmap &getProperties(void) const { return flagbase; } ///< Get the entire property map void saveXml(ostream &s) const; ///< Save the whole Database to an XML stream void restoreXml(const Element *el); ///< Recover the whole database from XML - void restoreXmlScope(const Element *el,Scope *new_scope); ///< Register and fill out a single Scope from XML + void restoreXmlScope(const Element *el,Scope *newScope); ///< Register and fill out a single Scope from an XML \ tag }; /// \param sc is the scope containing the new symbol diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc index 3d4f47e952..2bb3935259 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.cc @@ -16,18 +16,19 @@ #include "database_ghidra.hh" #include "funcdata.hh" -Scope *ScopeGhidra::buildSubScope(const string &nm) +Scope *ScopeGhidra::buildSubScope(uint8 id,const string &nm) { - return new ScopeGhidraNamespace(nm,ghidra); + return new ScopeGhidraNamespace(id,nm,ghidra); } /// \param g is the Architecture and connection to the Ghidra client +/// ScopeGhidra::ScopeGhidra(ArchitectureGhidra *g) - : Scope("",g,this) + : Scope(0,"",g,this) { ghidra = g; - cache = new ScopeInternal("",g,this); + cache = new ScopeInternal(0,"",g,this); cacheDirty = false; } @@ -38,7 +39,7 @@ ScopeGhidra::~ScopeGhidra(void) } /// The Ghidra client reports a \e namespace id associated with -/// Symbol. Determine if a matching \e namespac Scope already exists in the cache and build +/// Symbol. Determine if a matching \e namespace Scope already exists in the cache and build /// it if it isn't. This may mean creating a new \e namespace Scope. /// \param id is the ID associated with the Ghidra namespace /// \return the Scope matching the id. @@ -46,15 +47,16 @@ Scope *ScopeGhidra::reresolveScope(uint8 id) const { if (id == 0) return cache; - map::const_iterator miter = namespaceMap.find(id); - if (miter != namespaceMap.end()) - return (*miter).second; // Scope was previously cached + Database *symboltab = ghidra->symboltab; + Scope *cacheScope = symboltab->resolveScope(id); + if (cacheScope != (Scope *)0) + return cacheScope; // Scope was previously cached Document *doc = ghidra->getNamespacePath(id); if (doc == (Document *)0) throw LowlevelError("Could not get namespace info"); - Scope *curscope = glb->symboltab->getGlobalScope(); // Get pointer to ourselves (which is not const) + Scope *curscope = symboltab->getGlobalScope(); // Get pointer to ourselves (which is not const) try { const List &list(doc->getRoot()->getChildren()); List::const_iterator iter = list.begin(); @@ -66,16 +68,7 @@ Scope *ScopeGhidra::reresolveScope(uint8 id) const istringstream s(el->getAttributeValue("id")); s.unsetf(ios::dec | ios::hex | ios::oct); s >> scopeId; - miter = namespaceMap.find(scopeId); - if (miter == namespaceMap.end()) { - curscope = glb->symboltab->findCreateSubscope(el->getContent(), curscope); - ScopeGhidraNamespace *ghidraScope = (ScopeGhidraNamespace *)curscope; - if (ghidraScope->getClientId() == 0) - ghidraScope->setClientId(scopeId); - namespaceMap[scopeId] = curscope; - } - else - curscope = (*miter).second; + curscope = symboltab->findCreateScope(scopeId, el->getContent(), curscope); } delete doc; } @@ -260,7 +253,6 @@ void ScopeGhidra::clear(void) { cache->clear(); holes.clear(); - namespaceMap.clear(); if (cacheDirty) { ghidra->symboltab->setProperties(flagbaseDefault); // Restore database properties to defaults cacheDirty = false; @@ -415,6 +407,6 @@ bool ScopeGhidraNamespace::isNameUsed(const string &nm,const Scope *op2) const if (ArchitectureGhidra::isDynamicSymbolName(nm)) return false; // Just assume default FUN_ and DAT_ names don't collide const ScopeGhidraNamespace *otherScope = dynamic_cast(op2); - uint8 otherId = (otherScope != (const ScopeGhidraNamespace *)0) ? otherScope->getClientId() : 0; - return ghidra->isNameUsed(nm, scopeId, otherId); + uint8 otherId = (otherScope != (const ScopeGhidraNamespace *)0) ? otherScope->getId() : 0; + return ghidra->isNameUsed(nm, uniqueId, otherId); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.hh index 3f5e4e1af8..1de5d3aca0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/database_ghidra.hh @@ -36,7 +36,6 @@ class ScopeGhidra : public Scope { ArchitectureGhidra *ghidra; ///< Architecture and connection to the Ghidra client mutable ScopeInternal *cache; ///< An internal cache of previously fetched Symbol objects mutable RangeList holes; ///< List of (queried) memory ranges with no Symbol in them - mutable map namespaceMap; ///< Map from id to formal global namespace objects vector spacerange; ///< List of address spaces that are in the global range partmap flagbaseDefault; ///< Default boolean properties on memory mutable bool cacheDirty; ///< Is flagbaseDefault different from cache @@ -48,7 +47,7 @@ class ScopeGhidra : public Scope { virtual void removeRange(AddrSpace *spc,uintb first,uintb last) { throw LowlevelError("remove_range should not be performed on ghidra scope"); } - virtual Scope *buildSubScope(const string &nm); + virtual Scope *buildSubScope(uint8 id,const string &nm); virtual void addSymbolInternal(Symbol *sym) { throw LowlevelError("add_symbol_internal unimplemented"); } virtual SymbolEntry *addMapInternal(Symbol *sym,uint4 exfl,const Address &addr,int4 off,int4 sz, const RangeList &uselim) { throw LowlevelError("addMap unimplemented"); } @@ -77,6 +76,7 @@ public: virtual void clearAttribute(Symbol *sym,uint4 attr) { cache->clearAttribute(sym,attr); } virtual void setDisplayFormat(Symbol *sym,uint4 attr) { cache->setDisplayFormat(sym,attr); } + virtual void adjustCaches(void) { cache->adjustCaches(); } virtual SymbolEntry *findAddr(const Address &addr,const Address &usepoint) const; virtual SymbolEntry *findContainer(const Address &addr,int4 size, const Address &usepoint) const; @@ -127,16 +127,13 @@ public: class ScopeGhidraNamespace : public ScopeInternal { friend class ScopeGhidra; ArchitectureGhidra *ghidra; ///< Connection to the Ghidra client - uint8 scopeId; ///< Internal id allowing Ghidra client to reference formal namespaces - void setClientId(uint8 id) { scopeId = id; } protected: virtual SymbolEntry *addMapInternal(Symbol *sym,uint4 exfl,const Address &addr,int4 off,int4 sz, const RangeList &uselim); public: - ScopeGhidraNamespace(const string &nm,ArchitectureGhidra *g) - : ScopeInternal(nm,g) { ghidra = g; scopeId = 0; } ///< Constructor + ScopeGhidraNamespace(uint8 id,const string &nm,ArchitectureGhidra *g) + : ScopeInternal(id,nm,g) { ghidra = g; } ///< Constructor - uint8 getClientId(void) const { return scopeId; } ///< Get the Ghidra specific id virtual bool isNameUsed(const string &nm,const Scope *op2) const; }; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc index c6dcac6380..f7c3e95c33 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata.cc @@ -45,7 +45,15 @@ Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,FunctionSym if (nm.size()==0) localmap = (ScopeLocal *)0; // Filled in by restoreXml else { - ScopeLocal *newMap = new ScopeLocal(stackid,this,glb); + uint8 id; + if (sym != (FunctionSymbol *)0) + id = sym->getId(); + else { + // Missing a symbol, build unique id based on address + id = 0x57AB12CD; + id = (id << 32) | (addr.getOffset() & 0xffffffff); + } + ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb); glb->symboltab->attachScope(newMap,scope); // This may throw and delete newMap localmap = newMap; funcp.setScope(localmap,baseaddr+ -1); @@ -735,7 +743,7 @@ uint8 Funcdata::restoreXml(const Element *el) if ((*iter)->getName() == "localdb") { if (localmap != (ScopeLocal *)0) throw LowlevelError("Pre-existing local scope when restoring: "+name); - ScopeLocal *newMap = new ScopeLocal(stackid,this,glb); + ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb); glb->symboltab->restoreXmlScope(*iter,newMap); // May delete newMap and throw localmap = newMap; } @@ -750,7 +758,7 @@ uint8 Funcdata::restoreXml(const Element *el) else if ((*iter)->getName() == "prototype") { if (localmap == (ScopeLocal *)0) { // If we haven't seen a tag yet, assume we have a default local scope - ScopeLocal *newMap = new ScopeLocal(stackid,this,glb); + ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb); Scope *scope = glb->symboltab->getGlobalScope(); glb->symboltab->attachScope(newMap,scope); // May delete newMap and throw localmap = newMap; @@ -763,7 +771,7 @@ uint8 Funcdata::restoreXml(const Element *el) } if (localmap == (ScopeLocal *)0) { // Seen neither or // This is a function shell, so we provide default locals - ScopeLocal *newMap = new ScopeLocal(stackid,this,glb); + ScopeLocal *newMap = new ScopeLocal(id,stackid,this,glb); Scope *scope = glb->symboltab->getGlobalScope(); glb->symboltab->attachScope(newMap,scope); // May delete newMap and throw localmap = newMap; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc index 6ff7b3d516..9f2352d060 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/funcdata_op.cc @@ -923,7 +923,7 @@ void Funcdata::overrideFlow(const Address &addr,uint4 type) else if (type == Override::CALL) op = findPrimaryBranch(iter,enditer,true,false,true); else if (type == Override::CALL_RETURN) - op = findPrimaryBranch(iter,enditer,true,false,true); + op = findPrimaryBranch(iter,enditer,true,true,true); else if (type == Override::RETURN) op = findPrimaryBranch(iter,enditer,true,true,false); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc index 632193eaca..f0bd755d67 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.cc @@ -325,14 +325,12 @@ Translate *ArchitectureGhidra::buildTranslator(DocumentStorage &store) return new GhidraTranslate(this); } -Scope *ArchitectureGhidra::buildGlobalScope(void) +Scope *ArchitectureGhidra::buildDatabase(DocumentStorage &store) { - Scope *globalscope = symboltab->getGlobalScope(); - if (globalscope == (Scope *)0) { // Make sure global scope exists - globalscope = new ScopeGhidra(this); - symboltab->attachScope(globalscope,(Scope *)0); - } + symboltab = new Database(this,false); + Scope *globalscope = new ScopeGhidra(this); + symboltab->attachScope(globalscope,(Scope *)0); return globalscope; } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh index 6befb115f8..36d565cdea 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_arch.hh @@ -68,7 +68,7 @@ class ArchitectureGhidra : public Architecture { bool sendsyntaxtree; ///< True if the syntax tree should be sent with function output bool sendCcode; ///< True if C code should be sent with function output bool sendParamMeasures; ///< True if measurements for argument and return parameters should be sent - virtual Scope *buildGlobalScope(void); + virtual Scope *buildDatabase(DocumentStorage &store); virtual Translate *buildTranslator(DocumentStorage &store); virtual void buildLoader(DocumentStorage &store); virtual PcodeInjectLibrary *buildPcodeInjectLibrary(void); diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/interface.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/interface.cc index 335543afad..b90f391f6a 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/interface.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/interface.cc @@ -343,16 +343,8 @@ static bool maxmatch(string &res,const string &op1,const string &op2) { // Set res to maximum characters in common // at the beginning of op1 and op2 int4 len; - bool equal; - if (op1.size() == op2.size()) { - len = op1.size(); - equal = true; - } - else { - equal = false; - len = ( op1.size() < op2.size() ) ? op1.size() : op2.size(); - } + len = ( op1.size() < op2.size() ) ? op1.size() : op2.size(); res.erase(); for(int4 i=0;i &expand,istream &s, diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc index 2e7b1b8805..430c64e324 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/printc.cc @@ -1670,21 +1670,7 @@ void PrintC::pushAnnotation(const Varnode *vn,const PcodeOp *op) void PrintC::pushSymbol(const Symbol *sym,const Varnode *vn,const PcodeOp *op) { - Datatype *ct = sym->getType(); EmitXml::syntax_highlight tokenColor; - if (((sym->getFlags()&Varnode::readonly)!=0)&&(ct->getMetatype()==TYPE_ARRAY)) { - Datatype *subct = ((TypeArray *)ct)->getBase(); - if (subct->isCharPrint()) { - SymbolEntry *entry = sym->getFirstWholeMap(); - if (entry != (SymbolEntry *)0) { - ostringstream s; - if (printCharacterConstant(s,entry->getAddr(),subct)) { - pushAtom(Atom(s.str(),vartoken,EmitXml::const_color,op,vn)); - return; - } - } - } - } if (sym->getScope()->isGlobal()) tokenColor = EmitXml::global_color; else if (sym->getCategory() == 0) diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc index 41617b7cab..e620e780dc 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.cc @@ -5627,7 +5627,10 @@ AddTreeState::AddTreeState(Funcdata &d,PcodeOp *op,int4 slot) ptrsize = ptr->getSize(); ptrmask = calc_mask(ptrsize); baseType = ct->getPtrTo(); - size = AddrSpace::byteToAddressInt(baseType->getSize(),ct->getWordSize()); + if (baseType->isVariableLength()) + size = 0; // Open-ended size being pointed to, there will be no "multiples" component + else + size = AddrSpace::byteToAddressInt(baseType->getSize(),ct->getWordSize()); multsum = 0; // Sums start out as zero nonmultsum = 0; correct = 0; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh index 7ec3afcfc6..8a0d0c4382 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/ruleaction.hh @@ -46,7 +46,7 @@ class AddTreeState { const TypePointer *ct; ///< The pointer data-type const Datatype *baseType; ///< The base data-type being pointed at int4 ptrsize; ///< Size of the pointer - int4 size; ///< Size of data-type being pointed to (in address units) + int4 size; ///< Size of data-type being pointed to (in address units) or 0 for open ended pointer uintb ptrmask; ///< Mask for modulo calculations in ptr space uintb offset; ///< Number of bytes we dig into the base data-type uintb correct; ///< Number of bytes being double counted diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc index 408cf0d2f9..da6c32bc05 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.cc @@ -1092,15 +1092,18 @@ void TypeStruct::restoreXml(const Element *el,TypeFactory &typegrp) } /// Turn on the data-type's function prototype +/// \param tfact is the factory that owns \b this /// \param model is the prototype model /// \param outtype is the return type of the prototype /// \param intypes is the list of input parameters /// \param dotdotdot is true if the prototype takes variable arguments /// \param voidtype is the reference "void" data-type -void TypeCode::set(ProtoModel *model, +void TypeCode::set(TypeFactory *tfact,ProtoModel *model, Datatype *outtype,const vector &intypes, bool dotdotdot,Datatype *voidtype) { + factory = tfact; + flags |= variable_length; if (proto != (FuncProto *)0) delete proto; proto = new FuncProto(); @@ -1123,6 +1126,7 @@ TypeCode::TypeCode(const TypeCode &op) : Datatype(op) { proto = (FuncProto *)0; + factory = op.factory; if (op.proto != (FuncProto *)0) { proto = new FuncProto(); proto->copy(*op.proto); @@ -1133,6 +1137,7 @@ TypeCode::TypeCode(const string &nm) : Datatype(1,TYPE_CODE,nm) { proto = (FuncProto *)0; + factory = (TypeFactory *)0; } TypeCode::~TypeCode(void) @@ -1204,6 +1209,14 @@ int4 TypeCode::compareBasic(const TypeCode *op) const return 2; // Carry on with comparison of parameters } +Datatype *TypeCode::getSubType(uintb off,uintb *newoff) const + +{ + if (factory == (TypeFactory *)0) return (Datatype *)0; + *newoff = 0; + return factory->getBase(1, TYPE_CODE); // Return code byte unattached to function prototype +} + int4 TypeCode::compare(const Datatype &op,int4 level) const { @@ -1284,6 +1297,8 @@ void TypeCode::restoreXml(const Element *el,TypeFactory &typegrp) iter = list.begin(); if (iter == list.end()) return; // No underlying prototype Architecture *glb = typegrp.getArch(); + factory = &typegrp; + flags |= variable_length; proto = new FuncProto(); proto->setInternal( glb->defaultfp, typegrp.getTypeVoid() ); proto->restoreXml(*iter,glb); @@ -2107,7 +2122,7 @@ TypeCode *TypeFactory::getTypeCode(ProtoModel *model,Datatype *outtype, bool dotdotdot) { TypeCode tc(""); // getFuncdata type with no name - tc.set(model,outtype,intypes,dotdotdot,getTypeVoid()); + tc.set(this,model,outtype,intypes,dotdotdot,getTypeVoid()); return (TypeCode *) findAdd(tc); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh index 059ec989f5..cb6e879a8b 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/type.hh @@ -342,7 +342,8 @@ class TypeCode : public Datatype { protected: friend class TypeFactory; FuncProto *proto; ///< If non-null, this describes the prototype of the underlying function - void set(ProtoModel *model, + TypeFactory *factory; ///< Factory owning \b this + void set(TypeFactory *tfact,ProtoModel *model, Datatype *outtype,const vector &intypes, bool dotdotdot,Datatype *voidtype); ///< Establish a function pointer virtual void restoreXml(const Element *el,TypeFactory &typegrp); @@ -354,6 +355,7 @@ public: void setProperties(bool isConstructor,bool isDestructor); ///< Set additional function properties virtual ~TypeCode(void); virtual void printRaw(ostream &s) const; + virtual Datatype *getSubType(uintb off,uintb *newoff) const; virtual int4 compare(const Datatype &op,int4 level) const; virtual int4 compareDependency(const Datatype &op) const; virtual Datatype *clone(void) const { return new TypeCode(*this); } diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc b/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc index 37c9ddf78f..af5fe6cef0 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.cc @@ -262,10 +262,11 @@ int4 RangeHint::compare(const RangeHint &op2) const return 0; } +/// \param id is the globally unique id associated with the function scope /// \param spc is the (stack) address space associated with this function's local variables /// \param fd is the function associated with these local variables /// \param g is the Architecture -ScopeLocal::ScopeLocal(AddrSpace *spc,Funcdata *fd,Architecture *g) : ScopeInternal(fd->getName(),g) +ScopeLocal::ScopeLocal(uint8 id,AddrSpace *spc,Funcdata *fd,Architecture *g) : ScopeInternal(id,fd->getName(),g) { space = spc; diff --git a/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.hh b/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.hh index d3e33e989e..8a4f9a0fdb 100644 --- a/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.hh +++ b/Ghidra/Features/Decompiler/src/decompile/cpp/varmap.hh @@ -209,7 +209,7 @@ class ScopeLocal : public ScopeInternal { void addRecommendName(Symbol *sym); ///< Convert the given symbol to a name recommendation void collectNameRecs(void); ///< Collect names of unlocked Symbols on the stack public: - ScopeLocal(AddrSpace *spc,Funcdata *fd,Architecture *g); ///< Constructor + ScopeLocal(uint8 id,AddrSpace *spc,Funcdata *fd,Architecture *g); ///< Constructor virtual ~ScopeLocal(void) {} ///< Destructor AddrSpace *getSpaceId(void) const { return space; } ///< Get the associated (stack) address space diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java index 2c1d50216b..92002b4d14 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileCallback.java @@ -500,7 +500,7 @@ public class DecompileCallback { } String res = getSymbolName(sym); if (debug != null) { - debug.getSymbol(addr, res); + debug.getCodeSymbol(addr, sym.getID(), res, sym.getParentNamespace()); } return res; @@ -563,7 +563,7 @@ public class DecompileCallback { int pathSize = 0; Namespace curspace = namespace; long curId = namespace.getID(); - while (curId != stopId && curId != 0 && !(curspace instanceof Library)) { + while (curId != stopId && curId != 0 && !HighFunction.collapseToGlobal(curspace)) { pathSize += 1; curspace = curspace.getParentNamespace(); curId = curspace.getID(); @@ -610,7 +610,10 @@ public class DecompileCallback { public String getNamespacePath(long id) { Namespace namespace = getNameSpaceByID(id); StringBuilder buf = new StringBuilder(); - HighFunction.createNamespaceTag(buf, namespace, true); + HighFunction.createNamespaceTag(buf, namespace); + if (debug != null) { + debug.getNamespacePath(namespace); + } return buf.toString(); } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java index 16e12029e4..6887834a89 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompileDebug.java @@ -17,6 +17,7 @@ package ghidra.app.decompiler; import java.io.*; import java.util.*; +import java.util.Map.Entry; import ghidra.app.decompiler.DecompileCallback.StringData; import ghidra.app.plugin.processors.sleigh.ContextCache; @@ -49,7 +50,6 @@ public class DecompileDebug { private ArrayList dbscope; // Symbol query: scope private ArrayList database; // description of the symbol private ArrayList dtypes; // Data-types queried - private ArrayList symbol; // Names of code labels private ArrayList context; // Tracked register values associated with an address private ArrayList cpool; // Constant pool results private ArrayList flowoverride; // Flow overrides associated with an address @@ -111,7 +111,6 @@ public class DecompileDebug { dbscope = new ArrayList(); database = new ArrayList(); dtypes = new ArrayList(); - symbol = new ArrayList(); context = new ArrayList(); cpool = new ArrayList(); byteset = new TreeSet(); @@ -192,9 +191,6 @@ public class DecompileDebug { binimage += "\">\n"; debugStream.write(binimage.getBytes()); dumpBytes(debugStream); - for (String element : symbol) { - debugStream.write((element).getBytes()); - } debugStream.write("\n".getBytes()); } @@ -511,50 +507,87 @@ public class DecompileDebug { debugStream.write("\n".getBytes()); } + private ArrayList orderNamespaces() { + TreeMap namespaceMap = new TreeMap(); + for (Namespace namespace : dbscope) { + namespaceMap.put(namespace.getID(), namespace); + } + ArrayList res = new ArrayList(); + while (!namespaceMap.isEmpty()) { + Entry entry = namespaceMap.firstEntry(); + Long curKey = entry.getKey(); + Namespace curSpace = entry.getValue(); + for (;;) { + Long key; + Namespace parent = curSpace.getParentNamespace(); + if (parent == null) { + break; + } + if (HighFunction.collapseToGlobal(parent)) { + key = Long.valueOf(Namespace.GLOBAL_NAMESPACE_ID); + } + else { + key = Long.valueOf(parent.getID()); + } + parent = namespaceMap.get(key); + if (parent == null) { + break; + } + curKey = key; + curSpace = parent; + } + res.add(curSpace); + namespaceMap.remove(curKey); + } + return res; + } + private void dumpDatabases(OutputStream debugStream) throws IOException { Namespace scopename = null; - debugStream.write("\n".getBytes()); - for (int i = 0; i < database.size(); ++i) { - scopename = dbscope.get(i); - if (scopename != null) { - break; - } - } - while (scopename != null) { + ArrayList spaceList = orderNamespaces(); + debugStream.write("\n".getBytes()); + for (Namespace element : spaceList) { + scopename = element; StringBuilder datahead = new StringBuilder(); Namespace parentNamespace; datahead.append("\n"); - HighFunction.createNamespaceTag(datahead, parentNamespace, false); + if (parentNamespace != null) { + long parentId = + HighFunction.collapseToGlobal(parentNamespace) ? Namespace.GLOBAL_NAMESPACE_ID + : parentNamespace.getID(); + datahead.append("\n"); + } if (scopename != globalnamespace) { datahead.append("\n"); } datahead.append("\n"); debugStream.write(datahead.toString().getBytes()); - for (int i = 0; i < database.size(); ++i) { - Namespace namespc = dbscope.get(i); + for (int j = 0; j < database.size(); ++j) { + Namespace namespc = dbscope.get(j); if (namespc == scopename) { - debugStream.write((database.get(i)).getBytes()); - dbscope.set(i, null); + String entry = database.get(j); + if (entry == null) { + continue; // String may be null + } + debugStream.write(entry.getBytes()); } } debugStream.write("\n\n".getBytes()); - scopename = null; - for (Namespace element : dbscope) { - scopename = element; - if (scopename != null) { - break; - } - } } debugStream.write("\n".getBytes()); } @@ -627,19 +660,34 @@ public class DecompileDebug { comments = comm; // Already in XML form } - public void getSymbol(Address addr, String name) { + public void getCodeSymbol(Address addr, long id, String name, Namespace namespace) { StringBuilder buf = new StringBuilder(); - buf.append("\n"); + buf.append(" \n"); - symbol.add(buf.toString()); + buf.append(" \n"); + buf.append(" \n"); + buf.append("\n"); + getMapped(namespace, buf.toString()); + } + + public void getNamespacePath(Namespace namespace) { + while (namespace != null) { + if (HighFunction.collapseToGlobal(namespace)) { + break; // Treat library namespace as root + } + dbscope.add(namespace); // Add namespace to guarantee tag + database.add(null); // Even if there isn't necessarily any symbols + namespace = namespace.getParentNamespace(); + } } public void getMapped(Namespace namespc, String res) { - if (namespc == null) { + if (namespc == null || HighFunction.collapseToGlobal(namespc)) { dbscope.add(globalnamespace); } else { diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java index 1464e1c9fe..b9ec427dd1 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/tree/CoreGTreeNode.java @@ -183,9 +183,7 @@ abstract class CoreGTreeNode implements Cloneable { * @param node the node to add as a child to this node */ protected synchronized void doAddNode(GTreeNode node) { - children().add(node); - node.setParent((GTreeNode) this); - doFireNodeAdded(node); + doAddNode(children().size(), node); } /** @@ -196,6 +194,9 @@ abstract class CoreGTreeNode implements Cloneable { */ protected synchronized void doAddNode(int index, GTreeNode node) { List kids = children(); + if (kids.contains(node)) { + return; + } int insertIndex = Math.min(kids.size(), index); kids.add(insertIndex, node); node.setParent((GTreeNode) this); diff --git a/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeNodeTest.java b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeNodeTest.java index 4cd634a9f9..a7c88467ca 100644 --- a/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeNodeTest.java +++ b/Ghidra/Framework/Docking/src/test/java/docking/widgets/tree/GTreeNodeTest.java @@ -456,6 +456,24 @@ public class GTreeNodeTest { assertNotEquals(nodeA.hashCode(), nodeB.hashCode()); } + @Test + public void testCantAddNodeTwice() { + node0 = new TestNode("No Dups"); + + int childCount = root.getChildCount(); + root.addNode(node0); + assertEquals(childCount + 1, root.getChildCount()); + + // now make sure the count doesn't grow again + root.addNode(node0); + assertEquals(childCount + 1, root.getChildCount()); + + // try adding it with an index, still shouldn't get added + root.addNode(0, node0); + assertEquals(childCount + 1, root.getChildCount()); + + } + @Test public void testCloneEquals() throws CloneNotSupportedException { GTreeNode nodeA = new TestNode("AAA"); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java index faa800e92c..25e3121d4d 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunction.java @@ -615,22 +615,33 @@ public class HighFunction extends PcodeSyntaxTree { } } + /** + * The decompiler treats some namespaces as equivalent to the "global" namespace. + * Return true if the given namespace is treated as equivalent. + * @param namespace is the namespace + * @return true if equivalent + */ + static final public boolean collapseToGlobal(Namespace namespace) { + if (namespace instanceof Library) { + return true; + } + return false; + } + /** * Append an XML <parent> tag to the buffer describing the formal path elements * from the root (global) namespace up to the given namespace * @param buf is the buffer to write to * @param namespace is the namespace being described - * @param includeId is true if the XML tag should include namespace ids */ - static public void createNamespaceTag(StringBuilder buf, Namespace namespace, - boolean includeId) { + static public void createNamespaceTag(StringBuilder buf, Namespace namespace) { buf.append("\n"); if (namespace != null) { ArrayList arr = new ArrayList(); Namespace curspc = namespace; while (curspc != null) { arr.add(0, curspc); - if (curspc instanceof Library) { + if (collapseToGlobal(curspc)) { break; // Treat library namespace as root } curspc = curspc.getParentNamespace(); @@ -639,9 +650,7 @@ public class HighFunction extends PcodeSyntaxTree { for (int i = 1; i < arr.size(); ++i) { Namespace curScope = arr.get(i); buf.append("'); SpecXmlUtils.xmlEscape(buf, curScope.getName()); buf.append("\n"); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java index 2c93cecd7b..b58cdc553c 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/LocalSymbolMap.java @@ -338,7 +338,13 @@ public class LocalSymbolMap { resBuf.append("\n"); - HighFunction.createNamespaceTag(resBuf, namespace, false); + resBuf.append("\n"); resBuf.append("\n"); // Empty address range resBuf.append("\n"); Iterator iter = symbolMap.values().iterator(); diff --git a/Ghidra/Processors/ARM/data/languages/ARMneon.sinc b/Ghidra/Processors/ARM/data/languages/ARMneon.sinc index cbb700b185..9fc275adde 100644 --- a/Ghidra/Processors/ARM/data/languages/ARMneon.sinc +++ b/Ghidra/Processors/ARM/data/languages/ARMneon.sinc @@ -2488,7 +2488,8 @@ buildVldmDdList: Dreg,buildVldmDdList is Dreg & buildVldmDdList [ counter=counte build buildVldmDdList; } -vldmDdList: "{"^buildVldmDdList^"}" is D22 & c1215 & c0007 & buildVldmDdList [ regNum=(D22<<4)+c1215 - 1; counter=c0007>>1; ] { } +vldmDdList: "{"^buildVldmDdList^"}" is TMode=0 & D22 & c1215 & c0007 & buildVldmDdList [ regNum=(D22<<4)+c1215 - 1; counter=c0007>>1; ] { } +vldmDdList: "{"^buildVldmDdList^"}" is TMode=1 & thv_D22 & thv_c1215 & thv_c0007 & buildVldmDdList [ regNum=(thv_D22<<4)+thv_c1215 - 1; counter=thv_c0007>>1; ] { } :vldmia^COND vldmRn,vldmDdList is ( ($(AMODE) & COND & c2327=0x19 & c2121 & c2020=1 & c0811=11 & c0000=0) | ($(TMODE_E) & thv_c2327=0x19 & thv_c2121 & thv_c2020=1 & thv_c0811=11 & thv_c0000=0) ) & vldmRn & vldmDdList & vldmOffset & vldmUpdate @@ -4540,7 +4541,8 @@ buildVstmDdList: Dreg,buildVstmDdList is Dreg & buildVstmDdList [ counter=counte build buildVstmDdList; } -vstmDdList: "{"^buildVstmDdList^"}" is D22 & c1215 & c0007 & buildVstmDdList [ regNum=(D22<<4)+c1215-1; counter=c0007>>1; ] { } +vstmDdList: "{"^buildVstmDdList^"}" is TMode=0 & D22 & c1215 & c0007 & buildVstmDdList [ regNum=(D22<<4)+c1215-1; counter=c0007>>1; ] { } +vstmDdList: "{"^buildVstmDdList^"}" is TMode=1 & thv_D22 & thv_c1215 & thv_c0007 & buildVstmDdList [ regNum=(thv_D22<<4)+thv_c1215-1; counter=thv_c0007>>1; ] { } :vstmia^COND vldmRn,vstmDdList is ( ($(AMODE) & COND & c2327=0x19 & c2121 & c2020=0 & c0811=11 & c0000=0) | ($(TMODE_E) & thv_c2327=0x19 & thv_c2121 & thv_c2020=0 & thv_c0811=11 & thv_c0000=0) ) & vldmRn & vstmDdList & vldmOffset & vldmUpdate diff --git a/Ghidra/Processors/SuperH/certification.manifest b/Ghidra/Processors/SuperH/certification.manifest index 3fb7943de3..2b74928187 100644 --- a/Ghidra/Processors/SuperH/certification.manifest +++ b/Ghidra/Processors/SuperH/certification.manifest @@ -8,3 +8,4 @@ data/languages/superh.cspec||GHIDRA||||END| data/languages/superh.ldefs||GHIDRA||||END| data/languages/superh.pspec||GHIDRA||||END| data/languages/superh.sinc||GHIDRA||||END| +data/languages/superh2a.cspec||GHIDRA||||END| diff --git a/Ghidra/Processors/SuperH/data/languages/superh.cspec b/Ghidra/Processors/SuperH/data/languages/superh.cspec index 0114e1f5f8..2a06d7a5a0 100644 --- a/Ghidra/Processors/SuperH/data/languages/superh.cspec +++ b/Ghidra/Processors/SuperH/data/languages/superh.cspec @@ -29,19 +29,6 @@ - - - - - - - - - - - - - @@ -59,9 +46,6 @@ - - - diff --git a/Ghidra/Processors/SuperH/data/languages/superh.ldefs b/Ghidra/Processors/SuperH/data/languages/superh.ldefs index effc230b68..c105dbd35f 100644 --- a/Ghidra/Processors/SuperH/data/languages/superh.ldefs +++ b/Ghidra/Processors/SuperH/data/languages/superh.ldefs @@ -10,7 +10,7 @@ processorspec="superh.pspec" id="SuperH:BE:32:SH-2A"> SuperH SH-2A processor 32-bit big-endian - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +