From 14c10409ac7bfeb49ae1227305a51130b91cf74d Mon Sep 17 00:00:00 2001 From: dragonmacher <48328597+dragonmacher@users.noreply.github.com> Date: Tue, 22 Apr 2025 15:18:09 -0400 Subject: [PATCH] GP-5453 - Decompiler - Updated location broadcasting to better navigate the listing --- .../core/debug/stack/StackUnwinderTest.java | 13 +- .../viewer/field/VariableLocFieldFactory.java | 6 +- .../viewer/listingpanel/ListingPanel.java | 8 + .../AbstractMatchedTokensAction.java | 6 +- .../app/decompiler/DecompilerLocation.java | 151 +++-------------- .../decompiler/DecompilerLocationInfo.java | 153 ++++++++++++++++++ .../decompiler/component/DecompilerPanel.java | 99 +++++++++++- .../location/DefaultDecompilerLocation.java | 113 +++++++++++++ .../FunctionNameDecompilerLocation.java | 108 +++++++++++++ .../location/VariableDecompilerLocation.java | 109 +++++++++++++ .../decompile/DecompilerNavigationTest.java | 117 +++++++++++++- .../program/database/symbol/ClassSymbol.java | 28 +--- .../program/database/symbol/CodeSymbol.java | 23 +-- .../database/symbol/FunctionSymbol.java | 7 +- .../database/symbol/LibrarySymbol.java | 15 +- .../database/symbol/NamespaceSymbol.java | 31 +--- .../database/symbol/VariableSymbolDB.java | 30 +--- .../program/model/address/GlobalSymbol.java | 10 +- .../model/pcode/HighFunctionDBUtil.java | 70 ++++++++ .../ghidra/program/model/symbol/Symbol.java | 9 +- .../ghidra/program/util/FunctionLocation.java | 9 +- .../program/util/LabelFieldLocation.java | 17 +- .../ghidra/program/util/ProgramLocation.java | 42 +++-- .../util/VariableLocFieldLocation.java | 20 ++- .../program/model/symbol/StubSymbol.java | 8 +- 25 files changed, 880 insertions(+), 322 deletions(-) create mode 100644 Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompilerLocationInfo.java create mode 100644 Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/DefaultDecompilerLocation.java create mode 100644 Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/FunctionNameDecompilerLocation.java create mode 100644 Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/VariableDecompilerLocation.java diff --git a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/stack/StackUnwinderTest.java b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/stack/StackUnwinderTest.java index 0e87881278..eedcc5c3eb 100644 --- a/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/stack/StackUnwinderTest.java +++ b/Ghidra/Debug/Debugger/src/test/java/ghidra/app/plugin/core/debug/stack/StackUnwinderTest.java @@ -36,6 +36,7 @@ import docking.widgets.fieldpanel.support.FieldLocation; import generic.Unique; import ghidra.app.decompiler.*; import ghidra.app.decompiler.component.*; +import ghidra.app.decompiler.location.DefaultDecompilerLocation; import ghidra.app.plugin.assembler.*; import ghidra.app.plugin.assembler.sleigh.sem.*; import ghidra.app.plugin.core.analysis.*; @@ -1610,10 +1611,14 @@ public class StackUnwinderTest extends AbstractGhidraHeadedDebuggerTest { FieldLocation fLoc = new FieldLocation(i, j, r, c); ClangToken token = clangField.getToken(fLoc); if (token != null && tokText.equals(token.getText())) { - DecompilerLocation loc = token.getMinAddress() == null ? null - : new DecompilerLocation(program, token.getMinAddress(), - function.getEntryPoint(), results, token, i.intValue(), - 0); + + Address entryPoint = function.getEntryPoint(); + DecompilerLocationInfo info = + new DecompilerLocationInfo(entryPoint, results, token, + i.intValue(), 0); + DefaultDecompilerLocation loc = token.getMinAddress() == null ? null + : new DefaultDecompilerLocation(program, + token.getMinAddress(), info); return new HoverLocation(loc, fLoc, field, token); } } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableLocFieldFactory.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableLocFieldFactory.java index 0984235843..ecdeb59dcc 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableLocFieldFactory.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/field/VariableLocFieldFactory.java @@ -4,9 +4,9 @@ * 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. @@ -110,7 +110,7 @@ public class VariableLocFieldFactory extends AbstractVariableFieldFactory { VariableProxy variableProxy = (VariableProxy) proxy; Variable sv = variableProxy.getObject(); return new VariableLocFieldLocation(sv.getProgram(), variableProxy.getLocationAddress(), - variableProxy.getObject(), col); + sv, col); } return null; diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java index f27ec1a0fc..be65954dd6 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/viewer/listingpanel/ListingPanel.java @@ -20,6 +20,7 @@ import java.awt.event.*; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import javax.swing.*; import javax.swing.event.ChangeListener; @@ -873,6 +874,13 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc * @param view the set of address to include in the view. */ public void setView(AddressSetView view) { + + AddressIndexMap currentMap = layoutModel.getAddressIndexMap(); + AddressSetView originalView = currentMap.getOriginalAddressSet(); + if (Objects.equals(originalView, view)) { + return; + } + layoutModel.setAddressSet(view); updateProviders(); } diff --git a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/AbstractMatchedTokensAction.java b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/AbstractMatchedTokensAction.java index e00c3384db..b15152cc34 100644 --- a/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/AbstractMatchedTokensAction.java +++ b/Ghidra/Features/CodeCompare/src/main/java/ghidra/features/codecompare/decompile/AbstractMatchedTokensAction.java @@ -4,9 +4,9 @@ * 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. @@ -30,7 +30,7 @@ import ghidra.program.model.listing.Program; import ghidra.util.datastruct.Duo.Side; /** - * This is a base class for actions in a {@link DecompilerDiffCodeComparisonPanel} + * This is a base class for actions in a {@link DecompilerCodeComparisonPanel} */ public abstract class AbstractMatchedTokensAction extends DockingAction { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompilerLocation.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompilerLocation.java index 8d4bfd18bb..40c69aa955 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompilerLocation.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompilerLocation.java @@ -4,9 +4,9 @@ * 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. @@ -15,150 +15,43 @@ */ package ghidra.app.decompiler; -import java.util.Objects; - -import ghidra.framework.options.SaveState; import ghidra.program.model.address.Address; -import ghidra.program.model.listing.Program; import ghidra.program.util.ProgramLocation; -public class DecompilerLocation extends ProgramLocation { - private Address functionEntryPoint; - private DecompileResults results; - private ClangToken token; - private String tokenName; - private int lineNumber; - private int charPos; +/** + * Represents a location in the Decompiler. This interface allows the Decompiler to subclass more + * general {@link ProgramLocation}s while adding more detailed Decompiler information. + */ +public interface DecompilerLocation { - public DecompilerLocation(Program program, Address address, Address functionEntryPoint, - DecompileResults results, ClangToken token, int lineNumber, int charPos) { - super(program, address); - this.functionEntryPoint = functionEntryPoint; - this.results = results; - this.token = token; - this.tokenName = token.getText(); - this.lineNumber = lineNumber; - this.charPos = charPos; - } - - /** - * Default constructor required for restoring a program location from XML. - */ - public DecompilerLocation() { - } - - public Address getFunctionEntryPoint() { - return functionEntryPoint; - } + public Address getFunctionEntryPoint(); /** * Results from the decompilation * * @return C-AST, DFG, and CFG object. null if there are no results attached to this location */ - public DecompileResults getDecompile() { - return results; - } + public DecompileResults getDecompile(); /** * C text token at the current cursor location * * @return token at this location, could be null if there are no decompiler results */ - public ClangToken getToken() { - return token; - } + public ClangToken getToken(); - public String getTokenName() { - return tokenName; - } + /** + * {@return the name of the token for the current location} + */ + public String getTokenName(); - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + charPos; - result = - prime * result + ((functionEntryPoint == null) ? 0 : functionEntryPoint.hashCode()); - result = prime * result + lineNumber; - result = prime * result + ((tokenName == null) ? 0 : tokenName.hashCode()); - return result; - } + /** + * {@return the line number} + */ + public int getLineNumber(); - @Override - public boolean equals(Object obj) { - if (obj == null) { - return false; - } - if (this == obj) { - return true; - } - if (getClass() != obj.getClass()) { - return false; - } - - if (!super.equals(obj)) { - return false; - } - - DecompilerLocation other = (DecompilerLocation) obj; - if (charPos != other.charPos) { - return false; - } - - if (lineNumber != other.lineNumber) { - return false; - } - - if (!Objects.equals(functionEntryPoint, other.functionEntryPoint)) { - return false; - } - - if (!Objects.equals(tokenName, other.tokenName)) { - return false; - } - return true; - } - - @Override - public void saveState(SaveState saveState) { - super.saveState(saveState); - saveState.putString("_FUNCTION_ENTRY", functionEntryPoint.toString()); - saveState.putString("_TOKEN_TEXT", tokenName); - saveState.putInt("_LINE_NUM", lineNumber); - saveState.putInt("_CHAR_POS", charPos); - } - - @Override - public void restoreState(Program program1, SaveState obj) { - super.restoreState(program1, obj); - String addrStr = obj.getString("_FUNCTION_ENTRY", "0"); - functionEntryPoint = program1.parseAddress(addrStr)[0]; - tokenName = obj.getString("_TOKEN_TEXT", ""); - lineNumber = obj.getInt("_LINE_NUM", 0); - charPos = obj.getInt("_CHAR_POS", 0); - } - - public int getLineNumber() { - return lineNumber; - } - - public int getCharPos() { - return charPos; - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append(getClass().getSimpleName()); - buf.append('@'); - buf.append(addr.toString()); - buf.append(", line="); - buf.append(lineNumber); - buf.append(", character="); - buf.append(charPos); - buf.append(", token="); - buf.append(tokenName); - return buf.toString(); - } + /** + * {@return the character position} + */ + public int getCharPos(); } diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompilerLocationInfo.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompilerLocationInfo.java new file mode 100644 index 0000000000..ba1c23fc94 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/DecompilerLocationInfo.java @@ -0,0 +1,153 @@ +/* ### + * 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.decompiler; + +import java.util.Objects; + +import ghidra.framework.options.SaveState; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; + +public class DecompilerLocationInfo { + + private Address entryPoint; + private DecompileResults results; + private ClangToken token; + private String tokenName; + private int lineNumber; + private int charPos; + + public DecompilerLocationInfo(Address entryPoint, DecompileResults results, + ClangToken token, int lineNumber, int charPos) { + this.entryPoint = entryPoint; + this.results = results; + this.token = token; + this.tokenName = token.getText(); + this.lineNumber = lineNumber; + this.charPos = charPos; + } + + /** + * Default constructor required for restoring a program location from XML. + */ + public DecompilerLocationInfo() { + } + + public Address getFunctionEntryPoint() { + return entryPoint; + } + + /** + * Results from the decompilation + * + * @return C-AST, DFG, and CFG object. null if there are no results attached to this location + */ + public DecompileResults getDecompile() { + return results; + } + + /** + * C text token at the current cursor location + * + * @return token at this location, could be null if there are no decompiler results + */ + public ClangToken getToken() { + return token; + } + + public String getTokenName() { + return tokenName; + } + + public int getLineNumber() { + return lineNumber; + } + + public int getCharPos() { + return charPos; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + charPos; + result = + prime * result + ((entryPoint == null) ? 0 : entryPoint.hashCode()); + result = prime * result + lineNumber; + result = prime * result + ((tokenName == null) ? 0 : tokenName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (getClass() != obj.getClass()) { + return false; + } + + DecompilerLocationInfo other = (DecompilerLocationInfo) obj; + if (charPos != other.charPos) { + return false; + } + + if (lineNumber != other.lineNumber) { + return false; + } + + if (!Objects.equals(entryPoint, other.entryPoint)) { + return false; + } + + if (!Objects.equals(tokenName, other.tokenName)) { + return false; + } + return true; + } + + public void saveState(SaveState saveState) { + saveState.putString("_FUNCTION_ENTRY", entryPoint.toString()); + saveState.putString("_TOKEN_TEXT", tokenName); + saveState.putInt("_LINE_NUM", lineNumber); + saveState.putInt("_CHAR_POS", charPos); + } + + public void restoreState(Program program1, SaveState obj) { + String addrStr = obj.getString("_FUNCTION_ENTRY", "0"); + entryPoint = program1.parseAddress(addrStr)[0]; + tokenName = obj.getString("_TOKEN_TEXT", ""); + lineNumber = obj.getInt("_LINE_NUM", 0); + charPos = obj.getInt("_CHAR_POS", 0); + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(getClass().getSimpleName()); + buf.append(", line="); + buf.append(lineNumber); + buf.append(", character="); + buf.append(charPos); + buf.append(", token="); + buf.append(tokenName); + return buf.toString(); + } +} diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java index 8ff7a62df2..b5d74bf4a8 100644 --- a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/component/DecompilerPanel.java @@ -41,13 +41,14 @@ import generic.theme.GColor; import ghidra.app.decompiler.*; import ghidra.app.decompiler.component.hover.DecompilerHoverService; import ghidra.app.decompiler.component.margin.*; +import ghidra.app.decompiler.location.*; import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider; import ghidra.app.plugin.core.decompile.actions.DecompilerSearchLocation; import ghidra.app.util.viewer.util.ScrollpaneAlignedHorizontalLayout; import ghidra.program.model.address.*; -import ghidra.program.model.listing.Function; -import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.*; import ghidra.program.model.pcode.*; +import ghidra.program.model.symbol.Symbol; import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramSelection; import ghidra.util.*; @@ -979,17 +980,103 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field if (token == null) { return null; } + Address address = DecompilerUtils.getClosestAddress(getProgram(), token); if (address == null) { address = DecompilerUtils.findAddressBefore(layoutController.getFields(), token); } + + Function function = decompileData.getFunction(); if (address == null) { - address = decompileData.getFunction().getEntryPoint(); + address = function.getEntryPoint(); } - return new DecompilerLocation(decompileData.getProgram(), address, - decompileData.getFunction().getEntryPoint(), decompileData.getDecompileResults(), token, - location.getIndex().intValue(), location.col); + Address entryPoint = function.getEntryPoint(); + DecompileResults results = decompileData.getDecompileResults(); + int lineNumber = location.getIndex().intValue(); + int charPos = location.col; + DecompilerLocationInfo info = + new DecompilerLocationInfo(entryPoint, results, token, lineNumber, charPos); + Program program = decompileData.getProgram(); + ProgramLocation signatureLocation = createFunctionSignatureLocation(token, address, info); + if (signatureLocation != null) { + return signatureLocation; + } + + return new DefaultDecompilerLocation(program, address, info); + } + + private ProgramLocation createFunctionSignatureLocation(ClangToken token, Address address, + DecompilerLocationInfo info) { + + Function function = decompileData.getFunction(); + Address entryPoint = function.getEntryPoint(); + if (!entryPoint.equals(address)) { + // Another address implies that we are not on the function signature + return null; + } + + if (token instanceof ClangFuncNameToken ft) { + // if the token address is the entry point of this function, then create a location that + // will place the cursor on the function signature in the listing + Program program = decompileData.getProgram(); + String functionName = ft.getText(); + return new FunctionNameDecompilerLocation(program, entryPoint, functionName, info); + } + else if (token instanceof ClangVariableToken cvt) { + return createVariableDeclarationLocation(cvt, address, info); + } + return null; + } + + private ProgramLocation createVariableDeclarationLocation(ClangVariableToken cvt, + Address address, DecompilerLocationInfo info) { + + Function function = decompileData.getFunction(); + Address entryPoint = function.getEntryPoint(); + Program program = decompileData.getProgram(); + Variable variable = getVariable(cvt); + if (variable != null) { + return new VariableDecompilerLocation(program, entryPoint, variable, info); + } + + HighVariable highVar = cvt.getHighVariable(); + if (highVar == null) { + // decomp param that is not in the listing; put on signature + return new FunctionNameDecompilerLocation(program, entryPoint, cvt.getText(), info); + } + + HighSymbol highSymbol = highVar.getSymbol(); + if (highSymbol.isParameter()) { + // decomp param that is not in the listing; put on signature + return new FunctionNameDecompilerLocation(program, entryPoint, cvt.getText(), info); + } + + return null; + } + + private Variable getVariable(ClangVariableToken token) { + + HighVariable highVar = token.getHighVariable(); + if (highVar == null) { + return null; + } + HighSymbol highSymbol = highVar.getSymbol(); + Variable variable = HighFunctionDBUtil.getFunctionVariable(highSymbol); + if (variable != null) { + return variable; + } + + Function function = decompileData.getFunction(); + Symbol symbol = highSymbol.getSymbol(); + Variable[] locals = function.getLocalVariables(); + for (Variable local : locals) { + Symbol localSymbol = local.getSymbol(); + if (symbol == localSymbol) { + return local; + } + } + return null; } public void setSearchResults(SearchLocation searchLocation) { diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/DefaultDecompilerLocation.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/DefaultDecompilerLocation.java new file mode 100644 index 0000000000..6b4765fbca --- /dev/null +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/DefaultDecompilerLocation.java @@ -0,0 +1,113 @@ +/* ### + * 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.decompiler.location; + +import java.util.Objects; + +import ghidra.app.decompiler.*; +import ghidra.framework.options.SaveState; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; +import ghidra.program.util.ProgramLocation; + +/** + * The default location handed out when the user clicks inside of the Decompiler. + */ +public class DefaultDecompilerLocation extends ProgramLocation implements DecompilerLocation { + + private DecompilerLocationInfo info; + + public DefaultDecompilerLocation(Program program, Address address, + DecompilerLocationInfo info) { + super(program, address); + + this.info = info; + } + + public DefaultDecompilerLocation() { + // for restoring from xml + } + + @Override + public Address getFunctionEntryPoint() { + return info.getFunctionEntryPoint(); + } + + @Override + public DecompileResults getDecompile() { + return info.getDecompile(); + } + + @Override + public ClangToken getToken() { + return info.getToken(); + } + + @Override + public String getTokenName() { + return info.getTokenName(); + } + + @Override + public int getLineNumber() { + return info.getLineNumber(); + } + + @Override + public int getCharPos() { + return info.getCharPos(); + } + + @Override + public void saveState(SaveState ss) { + super.saveState(ss); + info.saveState(ss); + } + + @Override + public void restoreState(Program p, SaveState ss) { + super.restoreState(p, ss); + info.restoreState(p, ss); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = info.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (getClass() != obj.getClass()) { + return false; + } + + if (!super.equals(obj)) { + return false; + } + + DefaultDecompilerLocation other = (DefaultDecompilerLocation) obj; + return Objects.equals(info, other.info); + } + +} diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/FunctionNameDecompilerLocation.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/FunctionNameDecompilerLocation.java new file mode 100644 index 0000000000..2233104145 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/FunctionNameDecompilerLocation.java @@ -0,0 +1,108 @@ +/* ### + * 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.decompiler.location; + +import java.util.Objects; + +import ghidra.app.decompiler.*; +import ghidra.framework.options.SaveState; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; +import ghidra.program.util.FunctionNameFieldLocation; + +/** + * A location created when a function name is clicked in the Decompiler. + */ +public class FunctionNameDecompilerLocation extends FunctionNameFieldLocation + implements DecompilerLocation { + + private DecompilerLocationInfo info; + + public FunctionNameDecompilerLocation(Program program, Address address, String funcionName, + DecompilerLocationInfo info) { + super(program, address, funcionName); + this.info = info; + } + + @Override + public Address getFunctionEntryPoint() { + return info.getFunctionEntryPoint(); + } + + @Override + public DecompileResults getDecompile() { + return info.getDecompile(); + } + + @Override + public ClangToken getToken() { + return info.getToken(); + } + + @Override + public String getTokenName() { + return info.getTokenName(); + } + + @Override + public int getLineNumber() { + return info.getLineNumber(); + } + + @Override + public int getCharPos() { + return info.getCharPos(); + } + + @Override + public void saveState(SaveState ss) { + super.saveState(ss); + info.saveState(ss); + } + + @Override + public void restoreState(Program p, SaveState ss) { + super.restoreState(p, ss); + info.restoreState(p, ss); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = info.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (getClass() != obj.getClass()) { + return false; + } + + if (!super.equals(obj)) { + return false; + } + + FunctionNameDecompilerLocation other = (FunctionNameDecompilerLocation) obj; + return Objects.equals(info, other.info); + } +} diff --git a/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/VariableDecompilerLocation.java b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/VariableDecompilerLocation.java new file mode 100644 index 0000000000..14f26ce3a9 --- /dev/null +++ b/Ghidra/Features/Decompiler/src/main/java/ghidra/app/decompiler/location/VariableDecompilerLocation.java @@ -0,0 +1,109 @@ +/* ### + * 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.decompiler.location; + +import java.util.Objects; + +import ghidra.app.decompiler.*; +import ghidra.framework.options.SaveState; +import ghidra.program.model.address.Address; +import ghidra.program.model.listing.Program; +import ghidra.program.model.listing.Variable; +import ghidra.program.util.VariableLocFieldLocation; + +/** + * A location created when a function variable is clicked in the Decompiler. + */ +public class VariableDecompilerLocation extends VariableLocFieldLocation + implements DecompilerLocation { + + private DecompilerLocationInfo info; + + public VariableDecompilerLocation(Program program, Address locationAddr, Variable var, + DecompilerLocationInfo info) { + super(program, locationAddr, var, 0); + this.info = info; + } + + @Override + public Address getFunctionEntryPoint() { + return info.getFunctionEntryPoint(); + } + + @Override + public DecompileResults getDecompile() { + return info.getDecompile(); + } + + @Override + public ClangToken getToken() { + return info.getToken(); + } + + @Override + public String getTokenName() { + return info.getTokenName(); + } + + @Override + public int getLineNumber() { + return info.getLineNumber(); + } + + @Override + public int getCharPos() { + return info.getCharPos(); + } + + @Override + public void saveState(SaveState ss) { + super.saveState(ss); + info.saveState(ss); + } + + @Override + public void restoreState(Program p, SaveState ss) { + super.restoreState(p, ss); + info.restoreState(p, ss); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = info.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (getClass() != obj.getClass()) { + return false; + } + + if (!super.equals(obj)) { + return false; + } + + VariableDecompilerLocation other = (VariableDecompilerLocation) obj; + return Objects.equals(info, other.info); + } +} diff --git a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/DecompilerNavigationTest.java b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/DecompilerNavigationTest.java index 6af1f03675..8b42114e5e 100644 --- a/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/DecompilerNavigationTest.java +++ b/Ghidra/Features/Decompiler/src/test.slow/java/ghidra/app/plugin/core/decompile/DecompilerNavigationTest.java @@ -17,8 +17,7 @@ package ghidra.app.plugin.core.decompile; import static org.junit.Assert.*; -import java.util.function.BooleanSupplier; -import java.util.function.Supplier; +import java.util.function.*; import org.junit.Before; import org.junit.Test; @@ -34,10 +33,11 @@ import ghidra.app.plugin.core.navigation.NextPrevAddressPlugin; import ghidra.app.services.GoToService; import ghidra.app.util.navigation.GoToServiceImpl; import ghidra.program.model.address.Address; -import ghidra.program.model.listing.*; +import ghidra.program.model.listing.Function; +import ghidra.program.model.listing.Library; +import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.*; -import ghidra.program.util.OperandFieldLocation; -import ghidra.program.util.ProgramLocation; +import ghidra.program.util.*; import ghidra.test.ClassicSampleX86ProgramBuilder; public class DecompilerNavigationTest extends AbstractDecompilerTest { @@ -77,7 +77,7 @@ public class DecompilerNavigationTest extends AbstractDecompilerTest { private Program buildProgram() throws Exception { ClassicSampleX86ProgramBuilder builder = - new ClassicSampleX86ProgramBuilder("notepad", false, this); + new ClassicSampleX86ProgramBuilder("sample", false, this); // need a default label at 01002cf0, so make up a reference builder.createMemoryReference("01002ce5", "01002cf0", RefType.FALL_THROUGH, @@ -230,6 +230,111 @@ public class DecompilerNavigationTest extends AbstractDecompilerTest { assertCurrentAddress(f1); } + @Test + public void testDecompilerLocationEvent_VariableDeclaration() { + + /* + undefined4 FUN_010059a3(undefined4 param_1,undefined4 param_2,int param_3) + + { + int iVar1; + int iVar2; + int *piVar3; + undefined4 uVar4; + int iVar5; + bool bVar6; + int local_14; + int local_10; + undefined1 local_c [4]; + undefined4 local_8; + */ + + decompile("010059a3"); // FUN_010059a3 + + // 16: undefined4 local_8; + int line = 16; + int character = 13; + assertToken("local_8", line, character); + setDecompilerLocation(line, character); + + assertLocationType(loc -> loc instanceof VariableLocFieldLocation); + } + + @Test + public void testDecompilerLocationEvent_FunctionSignature_Parameter() { + + /* + undefined4 FUN_010059a3(undefined4 param_1,undefined4 param_2,int param_3) + + { + int iVar1; + int iVar2; + int *piVar3; + undefined4 uVar4; + int iVar5; + bool bVar6; + int local_14; + int local_10; + undefined1 local_c [4]; + undefined4 local_8; + */ + + decompile("010059a3"); // FUN_010059a3 + + // 4: undefined4 FUN_010059a3(undefined4 param_1 + int line = 4; + int character = 36; + assertToken("param_1", line, character); + setDecompilerLocation(line, character); + + assertLocationType(loc -> loc instanceof VariableLocFieldLocation); + } + + @Test + public void testDecompilerLocationEvent_FunctionSignature_FunctionName() { + + /* + undefined4 FUN_010059a3(undefined4 param_1,undefined4 param_2,int param_3) + + { + int iVar1; + int iVar2; + int *piVar3; + undefined4 uVar4; + int iVar5; + bool bVar6; + int local_14; + int local_10; + undefined1 local_c [4]; + undefined4 local_8; + */ + + decompile("010059a3"); // FUN_010059a3 + + // 4: undefined4 FUN_010059a3(undefined4 param_1 + int line = 4; + int character = 12; + assertToken("FUN_010059a3", line, character); + setDecompilerLocation(line, character); + + assertLocationType(loc -> loc instanceof FunctionNameFieldLocation); + } + + private void assertLocationType(Predicate predicate) { + waitForSwing(); + + BooleanSupplier success = () -> { + ProgramLocation loc = codeBrowser.getCurrentLocation(); + return predicate.test(loc); + }; + + Supplier failureMessage = + () -> "Listing is not at the expected field location. Current location: " + + codeBrowser.getCurrentLocation(); + + waitForCondition(success, failureMessage); + } + @Override public void assertCurrentAddress(Address expected) { codeBrowser.updateNow(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/ClassSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/ClassSymbol.java index d6c7c55b83..28b6c3f156 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/ClassSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/ClassSymbol.java @@ -4,9 +4,9 @@ * 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. @@ -19,12 +19,10 @@ import db.DBRecord; import ghidra.program.database.DBObjectCache; import ghidra.program.model.address.Address; import ghidra.program.model.symbol.*; -import ghidra.program.util.ProgramLocation; /** - * Symbols that represent "classes" + * Symbols that represent classes */ - public class ClassSymbol extends SymbolDB { private GhidraClassDB ghidraClass; @@ -42,17 +40,11 @@ public class ClassSymbol extends SymbolDB { } - /** - * @see ghidra.program.model.symbol.Symbol#getSymbolType() - */ @Override public SymbolType getSymbolType() { return SymbolType.CLASS; } - /** - * @see ghidra.program.model.symbol.Symbol#getObject() - */ @Override public Object getObject() { lock.acquire(); @@ -68,9 +60,6 @@ public class ClassSymbol extends SymbolDB { } } - /** - * @see ghidra.program.model.symbol.Symbol#isPrimary() - */ @Override public boolean isPrimary() { return true; @@ -82,17 +71,6 @@ public class ClassSymbol extends SymbolDB { return parentSymbol != null ? parentSymbol.isExternal() : false; } - /** - * @see ghidra.program.model.symbol.Symbol#getProgramLocation() - */ - @Override - public ProgramLocation getProgramLocation() { - return null; - } - - /** - * @see ghidra.program.model.symbol.Symbol#isValidParent(ghidra.program.model.symbol.Namespace) - */ @Override public boolean isValidParent(Namespace parent) { return super.isValidParent(parent) && diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/CodeSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/CodeSymbol.java index e9bf315650..1d35cd4951 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/CodeSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/CodeSymbol.java @@ -4,9 +4,9 @@ * 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. @@ -32,7 +32,6 @@ import ghidra.program.util.ProgramLocation; * EXTERNAL: * String stringData - external memory address/label */ - public class CodeSymbol extends SymbolDB { /** @@ -58,9 +57,6 @@ public class CodeSymbol extends SymbolDB { super(mgr, cache, addr, key); } - /** - * @see ghidra.program.model.symbol.Symbol#getSymbolType() - */ @Override public SymbolType getSymbolType() { return SymbolType.LABEL; @@ -124,9 +120,6 @@ public class CodeSymbol extends SymbolDB { } } - /** - * @see ghidra.program.model.symbol.Symbol#getObject() - */ @Override public Object getObject() { lock.acquire(); @@ -155,9 +148,6 @@ public class CodeSymbol extends SymbolDB { return null; } - /** - * @see ghidra.program.model.symbol.Symbol#isPrimary() - */ @Override public boolean isPrimary() { if (getSource() == SourceType.DEFAULT || isExternal()) { @@ -166,9 +156,6 @@ public class CodeSymbol extends SymbolDB { return doCheckIsPrimary(); } - /** - * @see ghidra.program.model.symbol.Symbol#setPrimary() - */ @Override public boolean setPrimary() { lock.acquire(); @@ -205,17 +192,11 @@ public class CodeSymbol extends SymbolDB { doSetPrimary(primary); } - /** - * @see ghidra.program.model.symbol.Symbol#getProgramLocation() - */ @Override public ProgramLocation getProgramLocation() { return new LabelFieldLocation(this); } - /** - * @see ghidra.program.model.symbol.Symbol#isValidParent(ghidra.program.model.symbol.Namespace) - */ @Override public boolean isValidParent(Namespace parent) { return super.isValidParent(parent) && diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/FunctionSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/FunctionSymbol.java index 5756f6303f..9004e43323 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/FunctionSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/FunctionSymbol.java @@ -4,9 +4,9 @@ * 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. @@ -57,9 +57,6 @@ public class FunctionSymbol extends SymbolDB { this.functionMgr = symbolMgr.getFunctionManager(); } - /** - * @see ghidra.program.model.symbol.Symbol#getSymbolType() - */ @Override public SymbolType getSymbolType() { return SymbolType.FUNCTION; diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LibrarySymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LibrarySymbol.java index e666d62254..b4f1d88ad7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LibrarySymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/LibrarySymbol.java @@ -4,9 +4,9 @@ * 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. @@ -22,7 +22,6 @@ import ghidra.program.model.listing.CircularDependencyException; import ghidra.program.model.listing.Library; import ghidra.program.model.symbol.*; import ghidra.program.util.ProgramEvent; -import ghidra.program.util.ProgramLocation; import ghidra.util.Msg; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.InvalidInputException; @@ -33,7 +32,6 @@ import ghidra.util.exception.InvalidInputException; * Symbol data usage: * String stringData - associated program project file path */ - public class LibrarySymbol extends SymbolDB { private LibraryDB library; @@ -93,6 +91,7 @@ public class LibrarySymbol extends SymbolDB { .setObjChanged(ProgramEvent.EXTERNAL_PATH_CHANGED, getName(), oldPath, newPath); } + @Override public SymbolType getSymbolType() { return SymbolType.LIBRARY; } @@ -115,14 +114,6 @@ public class LibrarySymbol extends SymbolDB { return true; } - /** - * @see ghidra.program.model.symbol.Symbol#getProgramLocation() - */ - public ProgramLocation getProgramLocation() { - // TODO Auto-generated method stub - return null; - } - @Override public boolean isValidParent(Namespace parent) { return super.isValidParent(parent) && diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceSymbol.java index c9793101b0..70c664d8de 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/NamespaceSymbol.java @@ -4,9 +4,9 @@ * 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. @@ -19,15 +19,13 @@ import db.DBRecord; import ghidra.program.database.DBObjectCache; import ghidra.program.model.address.Address; import ghidra.program.model.symbol.*; -import ghidra.program.util.ProgramLocation; /** * Symbol class for namespaces. */ - public class NamespaceSymbol extends SymbolDB { - NamespaceDB namespace; + private NamespaceDB namespace; /** * Construct a new namespace symbol @@ -36,13 +34,11 @@ public class NamespaceSymbol extends SymbolDB { * @param addr the address for this symbol. * @param record the record for this symbol. */ - NamespaceSymbol(SymbolManager mgr, DBObjectCache cache, Address addr, DBRecord record) { + NamespaceSymbol(SymbolManager mgr, DBObjectCache cache, Address addr, + DBRecord record) { super(mgr, cache, addr, record); } - /** - * @see ghidra.program.database.symbol.SymbolDB#isPrimary() - */ @Override public boolean isPrimary() { return true; @@ -54,25 +50,11 @@ public class NamespaceSymbol extends SymbolDB { return parentSymbol != null ? parentSymbol.isExternal() : false; } - /** - * @see ghidra.program.model.symbol.Symbol#getSymbolType() - */ @Override public SymbolType getSymbolType() { return SymbolType.NAMESPACE; } - /** - * @see ghidra.program.model.symbol.Symbol#getProgramLocation() - */ - @Override - public ProgramLocation getProgramLocation() { - return null; - } - - /** - * @see ghidra.program.model.symbol.Symbol#getObject() - */ @Override public Object getObject() { return getNamespace(); @@ -85,9 +67,6 @@ public class NamespaceSymbol extends SymbolDB { return namespace; } - /** - * @see ghidra.program.model.symbol.Symbol#isValidParent(ghidra.program.model.symbol.Namespace) - */ @Override public boolean isValidParent(Namespace parent) { // TODO: Not sure what other constraints should be placed on namespace movement diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableSymbolDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableSymbolDB.java index 8850f7adcb..8f0b35f838 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableSymbolDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/symbol/VariableSymbolDB.java @@ -4,9 +4,9 @@ * 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. @@ -100,9 +100,6 @@ public class VariableSymbolDB extends SymbolDB { return variableStorage; } - /** - * @see ghidra.program.model.symbol.Symbol#getSymbolType() - */ @Override public SymbolType getSymbolType() { return type; @@ -115,18 +112,12 @@ public class VariableSymbolDB extends SymbolDB { return isValid; } - /** - * @see ghidra.program.database.symbol.SymbolDB#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { // TODO: not sure what constitutes equality since address will differ return obj == this; } - /** - * @see ghidra.program.model.symbol.Symbol#delete() - */ @Override public boolean delete() { lock.acquire(); @@ -146,9 +137,6 @@ public class VariableSymbolDB extends SymbolDB { } } - /** - * @see ghidra.program.model.symbol.Symbol#getObject() - */ @Override public Object getObject() { FunctionDB func = getFunction(); @@ -158,9 +146,6 @@ public class VariableSymbolDB extends SymbolDB { return null; } - /** - * @see ghidra.program.model.symbol.Symbol#isPrimary() - */ @Override public boolean isPrimary() { return false; @@ -178,9 +163,6 @@ public class VariableSymbolDB extends SymbolDB { getParentNamespace().getID()); } - /** - * @see ghidra.program.model.symbol.Symbol#getProgramLocation() - */ @Override public ProgramLocation getProgramLocation() { Variable var = (Variable) getObject(); @@ -190,9 +172,6 @@ public class VariableSymbolDB extends SymbolDB { return null; } - /** - * @see ghidra.program.model.symbol.Symbol#isValidParent(ghidra.program.model.symbol.Namespace) - */ @Override public boolean isValidParent(Namespace parent) { // symbol is locked to single function and can't be moved @@ -270,9 +249,8 @@ public class VariableSymbolDB extends SymbolDB { } /** - * Change the storage address and data-type associated with this - * variable symbol. - * @param newStorage + * Change the storage address and data-type associated with this variable symbol. + * @param newStorage the new storage * @param dt data-type */ public void setStorageAndDataType(VariableStorage newStorage, DataType dt) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GlobalSymbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GlobalSymbol.java index a68df7736f..c9e5b9c6e1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GlobalSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/GlobalSymbol.java @@ -4,9 +4,9 @@ * 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. @@ -18,7 +18,6 @@ package ghidra.program.model.address; import ghidra.program.model.listing.CircularDependencyException; import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.*; -import ghidra.program.util.ProgramLocation; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.InvalidInputException; import ghidra.util.task.TaskMonitor; @@ -140,11 +139,6 @@ public class GlobalSymbol implements Symbol { return new Reference[0]; } - @Override - public ProgramLocation getProgramLocation() { - return null; - } - @Override public void setName(String newName, SourceType source) throws DuplicateNameException, InvalidInputException { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java index 98ed7b6339..eed39ff6f8 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/pcode/HighFunctionDBUtil.java @@ -385,6 +385,44 @@ public class HighFunctionDBUtil { return res; } + private static Variable getLocalVariable(Function function, VariableStorage storage, + Address pcAddr) { + + if (storage.isHashStorage()) { + + long hashVal = storage.getFirstVarnode().getOffset(); + for (Variable ul : function.getLocalVariables(VariableFilter.UNIQUE_VARIABLE_FILTER)) { + // Note: assumes there is only one hash method used for unique locals + if (ul.getFirstStorageVarnode().getOffset() == hashVal) { + return ul; + } + } + return null; + } + + int firstUseOffset = 0; + if (pcAddr != null) { + firstUseOffset = (int) pcAddr.subtract(function.getEntryPoint()); + } + + for (Variable otherVar : function.getLocalVariables()) { + if (otherVar.getFirstUseOffset() != firstUseOffset) { + // other than parameters we will have a hard time identifying + // local variable conflicts due to differences in scope (i.e., first-use) + continue; + } + + VariableStorage otherStorage = otherVar.getVariableStorage(); + if (otherStorage.intersects(storage)) { + if (otherStorage.equals(storage)) { + return otherVar; + } + } + } + + return null; + } + /** * Low-level routine for clearing any variables in the * database which conflict with this variable and return @@ -477,6 +515,38 @@ public class HighFunctionDBUtil { return parameters[slot]; } + public static Variable getFunctionVariable(HighSymbol highSymbol) { + + HighFunction highFunction = highSymbol.getHighFunction(); + Function function = highFunction.getFunction(); + HighVariable highVar = highSymbol.getHighVariable(); + if (highSymbol.isParameter()) { + + int slot = ((HighParam) highVar).getSlot(); + Parameter parameter = function.getParameter(slot); + return parameter; + } + + if (highSymbol.isGlobal()) { + return null; + } + + VariableStorage storage = highSymbol.getStorage(); + Address pcAddr = highSymbol.getPCAddress(); + Variable localVariable = getLocalVariable(function, storage, pcAddr); + + if (!storage.isHashStorage() && highVar != null && highVar.requiresDynamicStorage()) { + DynamicEntry entry = DynamicEntry.build(highVar.getRepresentative()); + storage = entry.getStorage(); + pcAddr = entry.getPCAdress(); // The address may change from original Varnode + } + + if (localVariable != null) { + return localVariable; + } + return null; + } + /** * Rename and/or retype the specified variable in the database. All parameters may be flushed * to the database if typed parameter inconsistency detected. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/Symbol.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/Symbol.java index f5bee575aa..0140f918db 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/Symbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/symbol/Symbol.java @@ -128,9 +128,14 @@ public interface Symbol { public Reference[] getReferences(); /** - * @return a program location corresponding to this symbol + * Returns a program location for this symbol; may be null. This allows implementations to + * return a more specific program location than what is typically used by the system. + * + * @return the location */ - public ProgramLocation getProgramLocation(); + public default ProgramLocation getProgramLocation() { + return null; + } /** * Sets the name this symbol. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FunctionLocation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FunctionLocation.java index 994870d021..4f1a391cf7 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FunctionLocation.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/FunctionLocation.java @@ -4,9 +4,9 @@ * 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. @@ -38,7 +38,7 @@ public class FunctionLocation extends ProgramLocation { * @param functionAddr the function address * @param row the row in the field * @param col the display piece on the row - * @param charOffset the character position within the display piece specifed by row,col + * @param charOffset the character position within the display piece specified by row,col */ protected FunctionLocation(Program program, Address locationAddr, Address functionAddr, int row, int col, int charOffset) { @@ -53,9 +53,6 @@ public class FunctionLocation extends ProgramLocation { protected FunctionLocation() { } - /** - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { if (super.equals(obj)) { diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LabelFieldLocation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LabelFieldLocation.java index a3299ca8c1..973515ccfd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LabelFieldLocation.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/LabelFieldLocation.java @@ -25,18 +25,16 @@ import ghidra.program.model.listing.Program; import ghidra.program.model.symbol.*; /** - * The LableFieldLocation class contains specific location information - * within the LABEL field of a CodeUnitLocation object. + * This class contains specific location information within the label field of a + * {@link CodeUnitLocation} */ public class LabelFieldLocation extends CodeUnitLocation { private SymbolPath symbolPath; /** - * Default constructor needed for restoring - * a label field location from XML + * Default constructor needed for restoring a label field location from XML */ public LabelFieldLocation() { - } /** @@ -45,8 +43,9 @@ public class LabelFieldLocation extends CodeUnitLocation { * @param program the program of the location * @param addr address of the location; should not be null * @param componentPath array of indexes for each nested data component; the - * index is the data component's index within its parent; may be null + * index is the data component's index within its parent; may be null. * @param label the label String at this location. + * @param namespace the namespace; may be null. * @param row the row in list of labels as displayed by the label field. Only used for * program location comparison purposes. * @param charOffset the column position within the label string for this location. @@ -113,9 +112,6 @@ public class LabelFieldLocation extends CodeUnitLocation { } } - /** - * Return the label string at this location. - */ public String getName() { return symbolPath.getName(); } @@ -143,9 +139,6 @@ public class LabelFieldLocation extends CodeUnitLocation { return symbolPath; } - /** - * Returns a String representation of this location. - */ @Override public String toString() { return super.toString() + ", Label = " + symbolPath.getPath(); diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java index 086c78a787..b4cfcfe7dd 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/ProgramLocation.java @@ -4,9 +4,9 @@ * 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. @@ -48,6 +48,7 @@ public class ProgramLocation implements Cloneable, Comparable { private int row; private int col; private int charOffset; + private boolean hasBeenRestored; /** * Construct a new ProgramLocation. @@ -237,24 +238,31 @@ public class ProgramLocation implements Cloneable, Comparable { /** * Restore this program location using the given program and save state object. * - * @param program1 program to restore from + * @param newProgram program to restore from * @param obj the save state to restore from */ - public void restoreState(Program program1, SaveState obj) { - this.program = program1; + public void restoreState(Program newProgram, SaveState obj) { + + if (hasBeenRestored) { + // ProgramLoations are intended to be immutable. Calling this repeatedly breaks that. + Msg.debug(this, "restoreState() has been called multiple times"); + return; + } + hasBeenRestored = true; + + program = newProgram; String addrStr = obj.getString("_ADDRESS", "0"); String byteAddrStr = obj.getString("_BYTE_ADDR", addrStr); String refAddrStr = obj.getString("_REF_ADDRESS", null); componentPath = obj.getInts("_COMP_PATH", null); - addr = ProgramUtilities.parseAddress(program1, addrStr); - byteAddr = ProgramUtilities.parseAddress(program1, byteAddrStr); + addr = ProgramUtilities.parseAddress(program, addrStr); + byteAddr = ProgramUtilities.parseAddress(program, byteAddrStr); if (refAddrStr != null) { - refAddr = ProgramUtilities.parseAddress(program1, refAddrStr); + refAddr = ProgramUtilities.parseAddress(program, refAddrStr); } col = obj.getInt("_COLUMN", 0); row = obj.getInt("_ROW", 0); charOffset = obj.getInt("_CHAR_OFFSET", 0); - } /** @@ -271,18 +279,24 @@ public class ProgramLocation implements Cloneable, Comparable { } try { - Class locClass = Class.forName(className); - ProgramLocation loc = (ProgramLocation) locClass.getConstructor().newInstance(); + Class locationClass = Class.forName(className); + if (locationClass.isInterface()) { + // This check is needed due to a refactoring that has changed a class into an + // interface. The class name may have been saved into the tool. Upon restoring we + // may try to restore that class. If that class is now an interface, the restore + // will not work. + return null; + } + + ProgramLocation loc = (ProgramLocation) locationClass.getConstructor().newInstance(); loc.restoreState(program, saveState); if (loc.getAddress() != null) { return loc; } // no address, it must be in a removed block; we can't use it } - catch (RuntimeException e) { // state may not parse the address if it is no longer valid - } catch (ClassNotFoundException e) { - // not sure why we are ignoring this--if you know, then please let everyone else know + // this can happen for locations created by plugins that are no longer installed } catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) { Msg.showError(ProgramLocation.class, null, "Programming Error", diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/VariableLocFieldLocation.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/VariableLocFieldLocation.java index 59728100c2..a17a39b7bb 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/VariableLocFieldLocation.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/util/VariableLocFieldLocation.java @@ -4,9 +4,9 @@ * 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. @@ -68,6 +68,7 @@ public class VariableLocFieldLocation extends VariableLocation { /** * Gets the location string. (For stack variables this is the offset as a string.) + * @return the location string */ public String getLoc() { return loc; @@ -83,19 +84,24 @@ public class VariableLocFieldLocation extends VariableLocation { @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (!super.equals(obj)) + } + if (!super.equals(obj)) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } VariableLocFieldLocation other = (VariableLocFieldLocation) obj; if (loc == null) { - if (other.loc != null) + if (other.loc != null) { return false; + } } - else if (!loc.equals(other.loc)) + else if (!loc.equals(other.loc)) { return false; + } return true; } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/symbol/StubSymbol.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/symbol/StubSymbol.java index 708a9350ea..cc03b33b71 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/symbol/StubSymbol.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/symbol/StubSymbol.java @@ -21,7 +21,6 @@ import java.util.List; import ghidra.program.model.address.Address; import ghidra.program.model.listing.CircularDependencyException; import ghidra.program.model.listing.Program; -import ghidra.program.util.ProgramLocation; import ghidra.util.exception.DuplicateNameException; import ghidra.util.exception.InvalidInputException; import ghidra.util.task.TaskMonitor; @@ -98,7 +97,7 @@ public class StubSymbol implements Symbol { } @Override - public boolean isValidParent(Namespace parent) { + public boolean isValidParent(Namespace nsParent) { return false; } @@ -132,11 +131,6 @@ public class StubSymbol implements Symbol { return null; } - @Override - public ProgramLocation getProgramLocation() { - return null; - } - @Override public void setName(String newName, SourceType source) throws DuplicateNameException, InvalidInputException {