Merge remote-tracking branch

'origin/GP-5453-dragonmacher-decompiler-locations--SQUASHED'
(Closes #7518)
This commit is contained in:
Ryan Kurtz 2025-04-23 10:41:42 -04:00
commit 4aa78ae6d0
25 changed files with 880 additions and 322 deletions

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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();
}

View file

@ -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 {

View file

@ -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;
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.
* 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 DecompilerLocation() {
}
public interface 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();
}

View file

@ -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();
}
}

View file

@ -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) {

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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<ProgramLocation> predicate) {
waitForSwing();
BooleanSupplier success = () -> {
ProgramLocation loc = codeBrowser.getCurrentLocation();
return predicate.test(loc);
};
Supplier<String> failureMessage =
() -> "Listing is not at the expected field location. Current location: " +
codeBrowser.getCurrentLocation();
waitForCondition(success, failureMessage);
}
@Override
public void assertCurrentAddress(Address expected) {
codeBrowser.updateNow();

View file

@ -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) &&

View file

@ -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) &&

View file

@ -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;

View file

@ -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) &&

View file

@ -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<SymbolDB> cache, Address addr, DBRecord record) {
NamespaceSymbol(SymbolManager mgr, DBObjectCache<SymbolDB> 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

View file

@ -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) {

View file

@ -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 {

View file

@ -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.

View file

@ -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.

View file

@ -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)) {

View file

@ -25,18 +25,16 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.*;
/**
* The <CODE>LableFieldLocation</CODE> 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();

View file

@ -48,6 +48,7 @@ public class ProgramLocation implements Cloneable, Comparable<ProgramLocation> {
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<ProgramLocation> {
/**
* 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<ProgramLocation> {
}
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",

View file

@ -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))
return false;
if (getClass() != obj.getClass())
return false;
VariableLocFieldLocation other = (VariableLocFieldLocation) obj;
if (loc == null) {
if (other.loc != null)
}
if (!super.equals(obj)) {
return false;
}
else if (!loc.equals(other.loc))
if (getClass() != obj.getClass()) {
return false;
}
VariableLocFieldLocation other = (VariableLocFieldLocation) obj;
if (loc == null) {
if (other.loc != null) {
return false;
}
}
else if (!loc.equals(other.loc)) {
return false;
}
return true;
}

View file

@ -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 {