mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 10:19:23 +02:00
GT-2932 - Decompiler - review fixes
This commit is contained in:
parent
f5f6b7c18b
commit
194addac9d
21 changed files with 609 additions and 285 deletions
|
@ -476,6 +476,12 @@ public class ListingMergePanel extends JPanel
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation externalLoc,
|
||||
boolean checkNavigationOption) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GoToOverrideService getOverrideService() {
|
||||
return null;
|
||||
|
|
|
@ -17,6 +17,8 @@ package ghidra.app.plugin.core.gotoquery;
|
|||
|
||||
import java.util.Stack;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import docking.widgets.OptionDialog;
|
||||
import ghidra.app.cmd.refs.SetExternalNameCmd;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
|
@ -96,6 +98,9 @@ public class GoToHelper {
|
|||
}
|
||||
ExternalLocation externalLoc =
|
||||
program.getExternalManager().getExternalLocation(externalSym);
|
||||
|
||||
// TODO - this seems like a mistake to always pass 'false' here; please doc why we
|
||||
// wish to ignore the user options for when to navigate to external programs
|
||||
return goToExternalLinkage(navigatable, externalLoc, false);
|
||||
}
|
||||
|
||||
|
@ -248,7 +253,7 @@ public class GoToHelper {
|
|||
* external program association has not yet been established, the user will be prompted to make
|
||||
* an association if they choose before completing the navigation.
|
||||
* @param nav Navigatable
|
||||
* @param externalLoc external location
|
||||
* @param externalLocation external location
|
||||
* @param checkNavigationOption if true the {@link NavigationOptions#isGotoExternalProgramEnabled}
|
||||
* option will be used to determine if navigation to the external program will be
|
||||
* attempted, or if navigation to the external linkage location within the current
|
||||
|
@ -257,32 +262,47 @@ public class GoToHelper {
|
|||
* @return true if navigation to the external program was successful or navigation to a
|
||||
* linkage location was performed.
|
||||
*/
|
||||
public boolean goToExternalLocation(Navigatable nav, ExternalLocation externalLoc,
|
||||
public boolean goToExternalLocation(Navigatable nav, ExternalLocation externalLocation,
|
||||
boolean checkNavigationOption) {
|
||||
|
||||
if (checkNavigationOption && !navOptions.isGotoExternalProgramEnabled()) {
|
||||
return goToExternalLinkage(nav, externalLoc, true);
|
||||
return goToExternalLinkage(nav, externalLocation, true);
|
||||
}
|
||||
|
||||
Symbol externalSym = externalLoc.getSymbol();
|
||||
Program externalProgram = openExternalProgram(externalLocation);
|
||||
if (externalProgram == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// try the address first if it exists
|
||||
Address addr = externalLocation.getAddress();
|
||||
if (addr != null && externalProgram.getMemory().contains(addr)) {
|
||||
goTo(nav, new AddressFieldLocation(externalProgram, addr), externalProgram);
|
||||
return true;
|
||||
}
|
||||
|
||||
// then try the symbol
|
||||
Symbol symbol = getExternalSymbol(externalProgram, externalLocation);
|
||||
if (symbol != null) {
|
||||
goTo(nav, symbol.getProgramLocation(), externalProgram);
|
||||
return true;
|
||||
}
|
||||
|
||||
performDefaultExternalProgramNavigation(nav, externalLocation, externalProgram, addr);
|
||||
return false; // return false because we did not go to the requested address
|
||||
}
|
||||
|
||||
private Program openExternalProgram(ExternalLocation externalLocation) {
|
||||
|
||||
if (externalLocation == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Symbol externalSym = externalLocation.getSymbol();
|
||||
Program program = externalSym.getProgram();
|
||||
|
||||
ExternalManager extMgr = program.getExternalManager();
|
||||
String extProgName = externalLoc.getLibraryName();
|
||||
if (Library.UNKNOWN.equals(extProgName)) {
|
||||
tool.setStatusInfo(" External location refers to " + Library.UNKNOWN +
|
||||
" library. Unable to " + "perform navigation.", true);
|
||||
return false;
|
||||
}
|
||||
String pathName = extMgr.getExternalLibraryPath(extProgName);
|
||||
if (pathName == null || pathName.length() == 0) {
|
||||
createExternalAssociation(program, extProgName);
|
||||
pathName = extMgr.getExternalLibraryPath(extProgName);
|
||||
}
|
||||
if (pathName == null || pathName.length() == 0) {
|
||||
tool.setStatusInfo(
|
||||
" External location is not resolved. Unable to " + "perform navigation.", true);
|
||||
return false;
|
||||
String pathName = getExternalLibraryPath(externalLocation, program);
|
||||
if (pathName == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ProjectData pd = tool.getProject().getProjectData();
|
||||
|
@ -290,36 +310,20 @@ public class GoToHelper {
|
|||
ProgramManager service = tool.getService(ProgramManager.class);
|
||||
if (domainFile == null || service == null) {
|
||||
tool.setStatusInfo("Unable to navigate to external location. " +
|
||||
"Destination program [" + pathName + "] does not exist.");
|
||||
return false;
|
||||
}
|
||||
Program externalProgram = service.openProgram(domainFile, -1, ProgramManager.OPEN_VISIBLE);
|
||||
if (externalProgram == null) {
|
||||
return false;
|
||||
"Destination program [" + externalLocation + "] does not exist.");
|
||||
return null;
|
||||
}
|
||||
|
||||
String label = externalLoc.getOriginalImportedName();
|
||||
if (label == null) {
|
||||
label = externalLoc.getLabel();
|
||||
}
|
||||
Address addr = externalLoc.getAddress();
|
||||
|
||||
// try the address first if it exists.
|
||||
if (addr != null && externalProgram.getMemory().contains(addr)) {
|
||||
goTo(nav, new AddressFieldLocation(externalProgram, addr), externalProgram);
|
||||
return true;
|
||||
return service.openProgram(domainFile, -1, ProgramManager.OPEN_VISIBLE);
|
||||
}
|
||||
|
||||
Symbol symbol = getExternalSymbol(externalProgram, externalLoc);
|
||||
if (symbol != null) {
|
||||
goTo(nav, symbol.getProgramLocation(), externalProgram);
|
||||
return true;
|
||||
}
|
||||
private void performDefaultExternalProgramNavigation(Navigatable nav,
|
||||
ExternalLocation externalLocation, Program externalProgram, Address addr) {
|
||||
|
||||
// failed to navigate to address or symbol
|
||||
// failed to navigate to address or symbol; alert the user
|
||||
if (addr != null) {
|
||||
if (externalLoc.getSymbol().getSource() != SourceType.DEFAULT) {
|
||||
tool.setStatusInfo("Symbol [" + getExternalName(externalLoc) +
|
||||
if (externalLocation.getSymbol().getSource() != SourceType.DEFAULT) {
|
||||
tool.setStatusInfo("Symbol [" + getExternalName(externalLocation) +
|
||||
"] does not exist, and address [" + addr + "] is not in memory.", true);
|
||||
}
|
||||
else {
|
||||
|
@ -327,14 +331,39 @@ public class GoToHelper {
|
|||
}
|
||||
}
|
||||
else {
|
||||
tool.setStatusInfo("Symbol [" + getExternalName(externalLoc) + "] does not exist.",
|
||||
tool.setStatusInfo("Symbol [" + getExternalName(externalLocation) + "] does not exist.",
|
||||
true);
|
||||
}
|
||||
|
||||
// navigate to top of external program
|
||||
addr = externalProgram.getMinAddress();
|
||||
goTo(nav, new AddressFieldLocation(externalProgram, addr), externalProgram);
|
||||
return false; // return false because we did not go to the requested address
|
||||
AddressFieldLocation location =
|
||||
new AddressFieldLocation(externalProgram, externalProgram.getMinAddress());
|
||||
goTo(nav, location, externalProgram);
|
||||
}
|
||||
|
||||
private String getExternalLibraryPath(ExternalLocation externalLocation, Program program) {
|
||||
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
String extProgName = externalLocation.getLibraryName();
|
||||
if (Library.UNKNOWN.equals(extProgName)) {
|
||||
tool.setStatusInfo(" External location refers to " + Library.UNKNOWN +
|
||||
" library. Unable to " + "perform navigation.", true);
|
||||
return null;
|
||||
}
|
||||
|
||||
String pathName = externalManager.getExternalLibraryPath(extProgName);
|
||||
if (StringUtils.isBlank(pathName)) {
|
||||
createExternalAssociation(program, extProgName);
|
||||
pathName = externalManager.getExternalLibraryPath(extProgName);
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(pathName)) {
|
||||
tool.setStatusInfo(" External location is not resolved. Unable to perform navigation.",
|
||||
true);
|
||||
return null;
|
||||
}
|
||||
|
||||
return pathName;
|
||||
}
|
||||
|
||||
private Stack<String> getExternalNamespaceStack(ExternalLocation externalLoc) {
|
||||
|
@ -375,9 +404,7 @@ public class GoToHelper {
|
|||
|
||||
// assume global symbol name if mangled name (same as original name) exists
|
||||
String label = externalLoc.getOriginalImportedName();
|
||||
|
||||
SymbolPath symbolPath;
|
||||
|
||||
if (label == null) {
|
||||
// use alternate symbol (may or may not be mangled)
|
||||
Symbol s = externalLoc.getSymbol();
|
||||
|
|
|
@ -105,7 +105,7 @@ public interface GoToService {
|
|||
/**
|
||||
* Navigate to either the external program location or address linkage location.
|
||||
* Specific behavior may vary based upon implementation.
|
||||
* @param nav Navigatable
|
||||
|
||||
* @param externalLoc external location
|
||||
* @param checkNavigationOption if true the service navigation
|
||||
* option will be used to determine if navigation to the external program will be
|
||||
|
@ -118,6 +118,23 @@ public interface GoToService {
|
|||
public boolean goToExternalLocation(ExternalLocation externalLoc,
|
||||
boolean checkNavigationOption);
|
||||
|
||||
/**
|
||||
* Navigate to either the external program location or address linkage location.
|
||||
* Specific behavior may vary based upon implementation.
|
||||
*
|
||||
* @param navigatable Navigatable
|
||||
* @param externalLoc external location
|
||||
* @param checkNavigationOption if true the service navigation
|
||||
* option will be used to determine if navigation to the external program will be
|
||||
* attempted, or if navigation to the external linkage location within the current
|
||||
* program will be attempted. If false, the implementations default behavior
|
||||
* will be performed.
|
||||
* @return true if either navigation to the external program or to a
|
||||
* linkage location was completed successfully.
|
||||
*/
|
||||
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation externalLoc,
|
||||
boolean checkNavigationOption);
|
||||
|
||||
/**
|
||||
* Parses the input string as either:
|
||||
* an address/symbol expression (0x1000+5, or LAB1000+5)
|
||||
|
@ -131,10 +148,10 @@ public interface GoToService {
|
|||
*
|
||||
* The listener will be notified after query and will indicate the query status.
|
||||
*
|
||||
* @param queryInput this input string to parse.
|
||||
* @param fromAddr The address used to determine the scope of the query
|
||||
* @param caseSensitive if true, the search must match case.
|
||||
* @param queryData the query input data
|
||||
* @param listener the listener that will be notified when the query completes.
|
||||
* @param monitor the task monitor
|
||||
* @return true if the queryInput is found or appears to be a wildcard search.
|
||||
*/
|
||||
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
|
||||
|
|
|
@ -125,6 +125,12 @@ public class GoToServiceImpl implements GoToService {
|
|||
return helper.goToExternalLocation(defaultNavigatable, extLoc, checkNavigationOption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation extLoc,
|
||||
boolean checkNavigationOption) {
|
||||
return helper.goToExternalLocation(navigatable, extLoc, checkNavigationOption);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToQuery(Navigatable navigatable, Address fromAddr, QueryData queryData,
|
||||
GoToServiceListener listener, TaskMonitor monitor) {
|
||||
|
@ -133,8 +139,7 @@ public class GoToServiceImpl implements GoToService {
|
|||
navigatable = defaultNavigatable;
|
||||
}
|
||||
|
||||
GoToQuery query =
|
||||
new GoToQuery(navigatable, plugin, this, queryData, fromAddr, listener,
|
||||
GoToQuery query = new GoToQuery(navigatable, plugin, this, queryData, fromAddr, listener,
|
||||
helper.getOptions(), monitor);
|
||||
|
||||
return query.processQuery();
|
||||
|
|
|
@ -401,8 +401,8 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||
for (int opIndex = 0; opIndex < numOperands; opIndex++) {
|
||||
OperandRepresentationList operandRepresentationList =
|
||||
codeUnitFormat.getOperandRepresentationList(inst, opIndex);
|
||||
addElementsForOperand(inst, elements, opIndex, operandRepresentationList,
|
||||
characterOffset);
|
||||
characterOffset = addElementsForOperand(inst, elements, opIndex,
|
||||
operandRepresentationList, characterOffset);
|
||||
characterOffset = 0;
|
||||
}
|
||||
|
||||
|
@ -504,8 +504,8 @@ abstract class OperandFieldHelper extends FieldFactory {
|
|||
}
|
||||
|
||||
private boolean containsNonPrimary(Reference[] refs) {
|
||||
for (Reference ref : refs) {
|
||||
if (!ref.isPrimary()) {
|
||||
for (int i = 0; i < refs.length; i++) {
|
||||
if (!refs[i].isPrimary()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,10 +46,6 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
|
||||
private final static Class<?>[] SUPPORTED_CLASSES = new Class[] { OperandFieldLocation.class };
|
||||
|
||||
/**
|
||||
* @see FieldMouseHandlerExtension#fieldElementClicked(Object, Navigatable,
|
||||
* MouseEvent, ServiceProvider)
|
||||
*/
|
||||
@Override
|
||||
public boolean fieldElementClicked(Object clickedObject, Navigatable navigatable,
|
||||
ProgramLocation location, MouseEvent mouseEvent, ServiceProvider serviceProvider) {
|
||||
|
@ -101,9 +97,6 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see FieldMouseHandlerExtension#getSupportedProgramLocations()
|
||||
*/
|
||||
@Override
|
||||
public Class<?>[] getSupportedProgramLocations() {
|
||||
return SUPPORTED_CLASSES;
|
||||
|
@ -130,19 +123,55 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
|
||||
private boolean checkExternalReference(Navigatable navigatable, CodeUnit codeUnit,
|
||||
OperandFieldLocation loc, GoToService goToService) {
|
||||
|
||||
Address refAddr = loc.getRefAddress();
|
||||
if (refAddr == null || !refAddr.isExternalAddress()) {
|
||||
return false;
|
||||
return checkExternalThunkFunctionReference(navigatable, codeUnit, loc, goToService);
|
||||
}
|
||||
|
||||
Program program = codeUnit.getProgram();
|
||||
Symbol s = program.getSymbolTable().getPrimarySymbol(refAddr);
|
||||
if (s == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ExternalLocation extLoc = program.getExternalManager().getExternalLocation(s);
|
||||
return goToService.goToExternalLocation(extLoc, true);
|
||||
}
|
||||
|
||||
private boolean checkExternalThunkFunctionReference(Navigatable navigatable, CodeUnit codeUnit,
|
||||
OperandFieldLocation loc, GoToService goToService) {
|
||||
|
||||
Address refAddr = loc.getRefAddress();
|
||||
Program program = codeUnit.getProgram();
|
||||
Symbol s = program.getSymbolTable().getPrimarySymbol(refAddr);
|
||||
if (s == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SymbolType type = s.getSymbolType();
|
||||
if (type != SymbolType.FUNCTION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Function refFunction = (Function) s.getObject();
|
||||
Function thunked = refFunction.getThunkedFunction(true);
|
||||
if (thunked == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (thunked.getBody().contains(codeUnit.getAddress())) {
|
||||
// this handles the unlikely case where the user double-clicks a reference to a
|
||||
// local thunk label--don't navigate externally
|
||||
return false;
|
||||
}
|
||||
|
||||
Symbol thunkedSymbol = thunked.getSymbol();
|
||||
ExternalLocation extLoc = program.getExternalManager().getExternalLocation(thunkedSymbol);
|
||||
boolean success = goToService.goToExternalLocation(extLoc, true);
|
||||
return success;
|
||||
}
|
||||
|
||||
private boolean checkVariableReference(Navigatable navigatable, CodeUnit codeUnit,
|
||||
OperandFieldLocation loc, GoToService goToService) {
|
||||
|
||||
|
@ -150,40 +179,18 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
return false;
|
||||
}
|
||||
|
||||
VariableOffset variableOffset = loc.getVariableOffset();
|
||||
if (variableOffset != null) {
|
||||
Variable variable = variableOffset.getVariable();
|
||||
if (variable != null) {
|
||||
goToService.goTo(navigatable,
|
||||
new VariableNameFieldLocation(navigatable.getProgram(), variable, 0),
|
||||
navigatable.getProgram());
|
||||
if (goToExplicitOperandVariable(navigatable, loc, goToService)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Program p = codeUnit.getProgram();
|
||||
Address cuAddr = codeUnit.getMinAddress();
|
||||
Address refAddr = loc.getRefAddress();
|
||||
Function func = p.getFunctionManager().getFunctionContaining(cuAddr);
|
||||
if (func == null) {
|
||||
Function function = p.getFunctionManager().getFunctionContaining(cuAddr);
|
||||
if (function == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProgramLocation varLoc = null;
|
||||
if (refAddr != null && !refAddr.isStackAddress()) {
|
||||
Register reg = p.getRegister(refAddr, 1);
|
||||
if (reg != null) {
|
||||
Variable variable = p.getFunctionManager().getReferencedVariable(cuAddr, refAddr,
|
||||
reg.getMinimumByteSize(),
|
||||
!isWrite((Instruction) codeUnit, loc.getOperandIndex(), reg));
|
||||
if (variable != null) {
|
||||
varLoc = new VariableNameFieldLocation(navigatable.getProgram(), variable, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (varLoc != null) {
|
||||
goToService.goTo(navigatable, varLoc, navigatable.getProgram());
|
||||
if (goToRegisterVariable(navigatable, codeUnit, loc, goToService)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -195,20 +202,71 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
Variable variable = p.getFunctionManager().getReferencedVariable(cuAddr,
|
||||
reference.getToAddress(), 0, reference.getReferenceType().isRead());
|
||||
if (variable != null) {
|
||||
varLoc = new VariableNameFieldLocation(navigatable.getProgram(), variable, 0);
|
||||
goToService.goTo(navigatable, varLoc, navigatable.getProgram());
|
||||
ProgramLocation pl =
|
||||
new VariableNameFieldLocation(navigatable.getProgram(), variable, 0);
|
||||
goToService.goTo(navigatable, pl, navigatable.getProgram());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (reference.isStackReference()) {
|
||||
ProgramLocation fnLoc = new FunctionSignatureFieldLocation(p, func.getEntryPoint(),
|
||||
null, 0, func.getPrototypeString(false, false));
|
||||
goToService.goTo(navigatable, fnLoc, navigatable.getProgram());
|
||||
ProgramLocation pl = new FunctionSignatureFieldLocation(p, function.getEntryPoint(),
|
||||
null, 0, function.getPrototypeString(false, false));
|
||||
goToService.goTo(navigatable, pl, navigatable.getProgram());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean goToRegisterVariable(Navigatable navigatable, CodeUnit codeUnit,
|
||||
OperandFieldLocation loc, GoToService goToService) {
|
||||
|
||||
Address refAddr = loc.getRefAddress();
|
||||
Address cuAddr = codeUnit.getMinAddress();
|
||||
if (refAddr == null || refAddr.isStackAddress()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Program p = codeUnit.getProgram();
|
||||
Register reg = p.getRegister(refAddr, 1);
|
||||
if (reg == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Variable variable = p.getFunctionManager().getReferencedVariable(cuAddr, refAddr,
|
||||
reg.getMinimumByteSize(), !isWrite((Instruction) codeUnit, loc.getOperandIndex(), reg));
|
||||
if (variable == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ProgramLocation pl = new VariableNameFieldLocation(navigatable.getProgram(), variable, 0);
|
||||
goToService.goTo(navigatable, pl, navigatable.getProgram());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the variable, when directly supplied by the field location
|
||||
*
|
||||
* @param navigatable the navigatable to which we should navigate
|
||||
* @param loc the source location
|
||||
* @param goToService the GoTo service
|
||||
* @return true if we decide to attempt navigation
|
||||
*/
|
||||
private boolean goToExplicitOperandVariable(Navigatable navigatable, OperandFieldLocation loc,
|
||||
GoToService goToService) {
|
||||
|
||||
VariableOffset variableOffset = loc.getVariableOffset();
|
||||
if (variableOffset != null) {
|
||||
Variable variable = variableOffset.getVariable();
|
||||
if (variable != null) {
|
||||
goToService.goTo(navigatable,
|
||||
new VariableNameFieldLocation(navigatable.getProgram(), variable, 0),
|
||||
navigatable.getProgram());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isWrite(Instruction inst, int operandIndex, Register reg) {
|
||||
for (Object obj : inst.getResultObjects()) {
|
||||
if (obj == reg) {
|
||||
|
@ -220,12 +278,12 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
|
||||
private boolean checkMemRefs(Navigatable navigatable, CodeUnit codeUnit,
|
||||
OperandFieldLocation loc, ServiceProvider serviceProvider) {
|
||||
|
||||
Address refAddr = loc.getRefAddress();
|
||||
if (refAddr == null || !refAddr.isMemoryAddress()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int opIndex = loc.getOperandIndex();
|
||||
Reference[] refs = codeUnit.getOperandReferences(loc.getOperandIndex());
|
||||
Address[] addrs = getAddressesForReferences(refs, codeUnit, serviceProvider);
|
||||
if (addrs.length == 0) {
|
||||
|
@ -251,26 +309,8 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
return true;
|
||||
}
|
||||
|
||||
Address gotoAddr = null;
|
||||
if (addrs.length == 1) {
|
||||
gotoAddr = addrs[0];
|
||||
}
|
||||
else {
|
||||
gotoAddr = codeUnit.getAddress(opIndex);
|
||||
if (gotoAddr == null) {
|
||||
Scalar scalar = codeUnit.getScalar(opIndex);
|
||||
if (scalar != null) {
|
||||
Address minAddress = codeUnit.getMinAddress();
|
||||
try {
|
||||
gotoAddr = minAddress.getNewAddress(scalar.getUnsignedValue(), true);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// handled below by checking for null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 1 address found
|
||||
Address gotoAddr = addrs[0];
|
||||
if (gotoAddr == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -294,7 +334,10 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
ServiceProvider serviceProvider, boolean skipExternal) {
|
||||
Address address = reference.getToAddress();
|
||||
RefType refType = reference.getReferenceType();
|
||||
if (refType.isIndirect()) {
|
||||
if (!refType.isIndirect()) {
|
||||
return address;
|
||||
}
|
||||
|
||||
Program program = codeUnit.getProgram();
|
||||
Data data = program.getListing().getDefinedDataAt(address);
|
||||
Address indirectAddrress = null;
|
||||
|
@ -308,13 +351,11 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
PseudoDisassembler pdis = new PseudoDisassembler(program);
|
||||
indirectAddrress = pdis.getIndirectAddr(address);
|
||||
}
|
||||
if (indirectAddrress != null &&
|
||||
(!indirectAddrress.isExternalAddress() || !skipExternal) &&
|
||||
|
||||
if (indirectAddrress != null && (!indirectAddrress.isExternalAddress() || !skipExternal) &&
|
||||
followIndirectReference(serviceProvider, indirectAddrress, program)) {
|
||||
return indirectAddrress;
|
||||
}
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
|
@ -324,6 +365,7 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
if (optionsService == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NavigationOptions navOptions = new NavigationOptions(optionsService);
|
||||
try {
|
||||
if (!navOptions.isFollowIndirectionEnabled()) {
|
||||
|
|
|
@ -95,8 +95,8 @@ class DualListingGoToService implements GoToService {
|
|||
AddressSetView addresses =
|
||||
isLeftSide ? dualListing.getLeftAddresses() : dualListing.getRightAddresses();
|
||||
if (!addresses.contains(addr)) {
|
||||
dualListing.setStatusInfo("\"" + addr.toString() +
|
||||
"\" is outside the current listing's view.");
|
||||
dualListing.setStatusInfo(
|
||||
"\"" + addr.toString() + "\" is outside the current listing's view.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -150,6 +150,13 @@ class DualListingGoToService implements GoToService {
|
|||
"Connot Go To an external address from a dual listing view.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation extLoc,
|
||||
boolean checkNavigationOption) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Connot Go To an external address from a dual listing view.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
|
||||
TaskMonitor monitor) {
|
||||
|
|
|
@ -519,10 +519,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
|||
|
||||
/**
|
||||
* Moves the cursor to the given program location and repositions the scrollbar to
|
||||
* show that location in the screen
|
||||
*
|
||||
* @param loc the location to move to
|
||||
* @return true if the 'go to' was successful
|
||||
* show that location in the screen.
|
||||
* @param loc the location to move to.
|
||||
*/
|
||||
public boolean goTo(ProgramLocation loc) {
|
||||
return goTo(loc, true);
|
||||
|
@ -538,7 +536,6 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
|||
* the given location will be placed in the center of the screen;
|
||||
* when the parameter is false, then the screen will be scrolled
|
||||
* only enough to show the cursor.
|
||||
* @return true if the 'go to' was successful
|
||||
*/
|
||||
public boolean goTo(ProgramLocation loc, boolean centerWhenNotVisible) {
|
||||
final FieldLocation floc = getFieldLocation(loc);
|
||||
|
@ -558,27 +555,15 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll the view of the listing to the given location
|
||||
* @param location the location
|
||||
*/
|
||||
/** Scroll the view of the listing to the given location. */
|
||||
public void scrollTo(ProgramLocation location) {
|
||||
FieldLocation fieldLocation = getFieldLocation(location);
|
||||
if (fieldLocation == null) {
|
||||
return; // this can happen when using restricted views
|
||||
}
|
||||
fieldPanel.scrollTo(fieldLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Center the view of the listing around the given location
|
||||
* @param location the location
|
||||
*/
|
||||
/** Center the view of the listing around the given location. */
|
||||
public void center(ProgramLocation location) {
|
||||
FieldLocation fieldLocation = getFieldLocation(location);
|
||||
if (fieldLocation == null) {
|
||||
return; // this can happen when using restricted views
|
||||
}
|
||||
fieldPanel.center(fieldLocation);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,6 @@ package ghidra.program.model.listing;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.app.util.viewer.field.CommentUtils;
|
||||
import ghidra.program.model.address.*;
|
||||
|
@ -38,8 +36,6 @@ import ghidra.util.MathUtilities;
|
|||
|
||||
public class CodeUnitFormat {
|
||||
|
||||
private static final String EMPTY = "";
|
||||
|
||||
protected static final String PLUS = "+";
|
||||
protected static final String UNDERSCORE = "_";
|
||||
|
||||
|
@ -116,39 +112,37 @@ public class CodeUnitFormat {
|
|||
*/
|
||||
public String getRepresentationString(CodeUnit cu, boolean includeEOLcomment) {
|
||||
|
||||
StringBuilder buffy = new StringBuilder(getMnemonicRepresentation(cu));
|
||||
StringBuffer stringBuffer = new StringBuffer(getMnemonicRepresentation(cu));
|
||||
if (cu instanceof Instruction) {
|
||||
Instruction instr = (Instruction) cu;
|
||||
int n = instr.getNumOperands();
|
||||
if (n > 1) {
|
||||
buffy.append(' ');
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
String sep = instr.getSeparator(i);
|
||||
buffy.append(StringUtils.isBlank(sep) ? EMPTY : sep);
|
||||
buffy.append(getOperandRepresentationString(cu, i));
|
||||
if (i == 0) {
|
||||
stringBuffer.append(" ");
|
||||
}
|
||||
else {
|
||||
String separator = instr.getSeparator(i);
|
||||
if (separator != null && separator.length() != 0) {
|
||||
stringBuffer.append(separator);
|
||||
}
|
||||
}
|
||||
stringBuffer.append(getOperandRepresentationString(cu, i));
|
||||
}
|
||||
|
||||
// grab any trailing separator at n (index + 1; see Instruction.getSeparator())
|
||||
String sep = instr.getSeparator(n);
|
||||
buffy.append(StringUtils.isBlank(sep) ? EMPTY : sep);
|
||||
}
|
||||
else { // data always has one operand
|
||||
buffy.append(' ');
|
||||
buffy.append(getOperandRepresentationString(cu, 0));
|
||||
stringBuffer.append(" ");
|
||||
stringBuffer.append(getOperandRepresentationString(cu, 0));
|
||||
}
|
||||
|
||||
if (includeEOLcomment) {
|
||||
String eolComment = cu.getComment(CodeUnit.EOL_COMMENT);
|
||||
if (eolComment != null) {
|
||||
// fixup annotations
|
||||
eolComment = CommentUtils.getDisplayString(eolComment, cu.getProgram());
|
||||
buffy.append(" // ");
|
||||
buffy.append(eolComment);
|
||||
stringBuffer.append(" // ");
|
||||
stringBuffer.append(eolComment);
|
||||
}
|
||||
}
|
||||
return buffy.toString();
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,19 +152,19 @@ public class CodeUnitFormat {
|
|||
* @return mnemonic representation
|
||||
*/
|
||||
public String getMnemonicRepresentation(CodeUnit cu) {
|
||||
StringBuilder buffy = new StringBuilder();
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
String mnemonic = cu.getMnemonicString();
|
||||
if (options.showDataMutability && (cu instanceof Data) && mnemonic != null) {
|
||||
Data d = (Data) cu;
|
||||
if (d.isConstant()) {
|
||||
buffy.append("const ");
|
||||
stringBuffer.append("const ");
|
||||
}
|
||||
else if (d.isVolatile()) {
|
||||
buffy.append("volatile ");
|
||||
stringBuffer.append("volatile ");
|
||||
}
|
||||
}
|
||||
buffy.append(mnemonic);
|
||||
return buffy.toString();
|
||||
stringBuffer.append(mnemonic);
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -279,7 +273,7 @@ public class CodeUnitFormat {
|
|||
* Perform register markup with explicit and implied register variable
|
||||
* reference.
|
||||
*
|
||||
* @param instr instruction
|
||||
* @param inst instruction
|
||||
* @param opIndex
|
||||
* @param func function containing instruction
|
||||
* @param primaryRef primary reference or null
|
||||
|
@ -452,17 +446,18 @@ public class CodeUnitFormat {
|
|||
* @param opIndex operand index
|
||||
* @param func function containing instruction
|
||||
* @param primaryRef primary reference
|
||||
* @param representationList the representation objects
|
||||
* @param referencedVariable optional variable associated with reference
|
||||
* @param regIndexMap register index map
|
||||
* @param representationList
|
||||
* @return true if primaryRef was included in scalar mark-up
|
||||
*/
|
||||
private boolean performAddressMarkup(Instruction instr, int opIndex, Function func,
|
||||
Reference primaryRef, List<Object> representationList) {
|
||||
|
||||
if (primaryRef == null || !primaryRef.isMemoryReference()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Address refAddr = primaryRef.getToAddress();
|
||||
|
||||
int size = representationList.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
Object obj = representationList.get(i);
|
||||
|
@ -1138,9 +1133,13 @@ public class CodeUnitFormat {
|
|||
* Get a representation object corresponding to the specified reference.
|
||||
* Format options are considered when generating label.
|
||||
*
|
||||
* @param cu the code unit
|
||||
* @param ref the reference
|
||||
* @param cu
|
||||
* @param ref
|
||||
* @param var variable which corresponds to reference or null
|
||||
* @param showIndirectValue if true, indirect memory references which refer
|
||||
* to a pointer will get an additional "=value" appended where
|
||||
* value corresponds to data pointed to by the referenced
|
||||
* pointer.
|
||||
* @return reference representation object
|
||||
*/
|
||||
private Object getReferenceRepresentation(CodeUnit cu, Reference ref, Variable var) {
|
||||
|
@ -1418,6 +1417,9 @@ public class CodeUnitFormat {
|
|||
return symbol.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ShowBlockName setting
|
||||
*/
|
||||
public ShowBlockName getShowBlockName() {
|
||||
return options.showBlockName;
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ public class AbstractCodeBrowserNavigationTest extends AbstractGhidraHeadedInteg
|
|||
private DockingActionIf nextFunction;
|
||||
private DockingActionIf prevFunction;
|
||||
|
||||
protected ToyProgramBuilder builder;
|
||||
protected Program program;
|
||||
protected CodeBrowserPlugin cb;
|
||||
|
||||
|
@ -83,7 +84,7 @@ public class AbstractCodeBrowserNavigationTest extends AbstractGhidraHeadedInteg
|
|||
}
|
||||
|
||||
private Program buildProgram() throws Exception {
|
||||
ToyProgramBuilder builder = new ToyProgramBuilder("Test", true, this);
|
||||
builder = new ToyProgramBuilder("Test", true, this);
|
||||
builder.createMemory(".text", "0x1001000", 0x6600);
|
||||
builder.createMemory(".data", "0x1008000", 0x600);
|
||||
builder.createMemory(".rsrc", "0x100a000", 0x5400);
|
||||
|
@ -98,7 +99,6 @@ public class AbstractCodeBrowserNavigationTest extends AbstractGhidraHeadedInteg
|
|||
|
||||
builder.applyDataType("0x1001000", PointerDataType.dataType, 1);
|
||||
builder.createExternalReference("0x1001000", "ADVAPI32.dll", "IsTextUnicode", 0); // linkage location
|
||||
|
||||
builder.createExternalReference("0x1001020", "ADVAPI32.dll", "IsTextUnicode", 0); // without pointer
|
||||
|
||||
builder.addBytesBranch("1004000", "1004010");
|
||||
|
|
|
@ -63,8 +63,22 @@ public class ExternalCodeBrowserNavigationTest extends AbstractCodeBrowserNaviga
|
|||
|
||||
int txId = program.startTransaction("Set Path");
|
||||
program.getExternalManager().setExternalPath("ADVAPI32.dll", "/FILE1", true);
|
||||
program.endTransaction(txId, true);
|
||||
|
||||
//
|
||||
// Create a call reference to a thunk function
|
||||
// Create a thunk function to an external location
|
||||
//
|
||||
String arbitraryAddress = "0x1001000";
|
||||
ExternalLocation externalLocation = builder.createExternalFunction(arbitraryAddress,
|
||||
"ADVAPI32.dll", "externalFunctionXyz", "_Zxyz");
|
||||
String thunkAddress = "0x1006300";
|
||||
Function thunk = builder.createFunction(thunkAddress);
|
||||
thunk.setThunkedFunction(externalLocation.getFunction());
|
||||
|
||||
builder.createMemoryCallReference("0x1001030", thunkAddress);
|
||||
|
||||
program.endTransaction(txId, true);
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
|
@ -180,17 +194,35 @@ public class ExternalCodeBrowserNavigationTest extends AbstractCodeBrowserNaviga
|
|||
|
||||
cb.goTo(new OperandFieldLocation(program, addr("1001020"), null, null, null, 0, 0));
|
||||
assertEquals(addr("1001020"), cb.getCurrentAddress());
|
||||
|
||||
assertEquals("FILE2", program.getDomainFile().getName());
|
||||
|
||||
// verify that navigation to the external program, address 0x1001888, is performed
|
||||
// since navigation initiated from linkage location
|
||||
click(cb, 2);
|
||||
assertEquals(addr("1001888"), cb.getCurrentAddress());
|
||||
|
||||
assertEquals("FILE1", lastNavigationProgram.getDomainFile().getName());
|
||||
assertEquals(addr("1001888"), lastNavigationLocation.getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperandExternalProgramNavigation_OnThunk() throws Exception {
|
||||
|
||||
getTool().getOptions("Navigation").setEnum("External Navigation",
|
||||
NavigationOptions.ExternalNavigationEnum.NavigateToExternalProgram);
|
||||
|
||||
String fromAddress = "1001030";
|
||||
cb.goTo(new OperandFieldLocation(program, addr(fromAddress), null, null, null, 0, 0));
|
||||
assertEquals(addr(fromAddress), cb.getCurrentAddress());
|
||||
assertEquals("FILE2", program.getDomainFile().getName());
|
||||
|
||||
// verify that navigation to the external program, address 0x1001888, is performed
|
||||
// since navigation initiated from linkage location
|
||||
click(cb, 2);
|
||||
|
||||
String otherAddress = "0x01001000";
|
||||
assertEquals(addr(otherAddress), cb.getCurrentAddress());
|
||||
assertEquals("FILE1", lastNavigationProgram.getDomainFile().getName());
|
||||
assertEquals(addr(otherAddress), lastNavigationLocation.getAddress());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -270,6 +302,7 @@ public class ExternalCodeBrowserNavigationTest extends AbstractCodeBrowserNaviga
|
|||
@Mock
|
||||
public boolean goTo(Invocation inv, final Navigatable navigatable, ProgramLocation loc,
|
||||
Program p) {
|
||||
|
||||
// Track last navigation location
|
||||
lastNavigationLocation = loc;
|
||||
lastNavigationProgram = p;
|
||||
|
|
|
@ -84,6 +84,13 @@ public class TestDummyGoToService implements GoToService {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation externalLoc,
|
||||
boolean checkNavigationOption) {
|
||||
// stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
|
||||
TaskMonitor monitor) {
|
||||
|
|
|
@ -101,20 +101,17 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
|
|||
|
||||
@Override
|
||||
public void contextChanged() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decompileDataChanged(DecompileData decompileData) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportLocation() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -124,20 +121,22 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
|
|||
|
||||
@Override
|
||||
public void goToAddress(Address addr, boolean newWindow) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToLabel(String labelName, boolean newWindow) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToScalar(long value, boolean newWindow) {
|
||||
// TODO Auto-generated method stub
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToFunction(Function function, boolean newWindow) {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -150,14 +149,12 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
|
|||
|
||||
@Override
|
||||
public void selectionChanged(ProgramSelection programSelection) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStatusMessage(String message) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
// stub
|
||||
}
|
||||
|
||||
public void clearAndShowMessage(String message) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.app.decompiler.component;
|
||||
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||
|
@ -42,4 +43,5 @@ public interface DecompilerCallbackHandler {
|
|||
|
||||
void exportLocation();
|
||||
|
||||
void goToFunction(Function function, boolean newWindow);
|
||||
}
|
||||
|
|
|
@ -248,14 +248,11 @@ public class DecompilerController {
|
|||
}
|
||||
|
||||
void goToFunction(Function function, boolean newWindow) {
|
||||
while (function.isThunk()) {
|
||||
Function thunkedFunction = function.getThunkedFunction(false);
|
||||
if (thunkedFunction == null || thunkedFunction.isExternal()) {
|
||||
break;
|
||||
}
|
||||
Function thunkedFunction = function.getThunkedFunction(true);
|
||||
if (thunkedFunction != null) {
|
||||
function = thunkedFunction;
|
||||
}
|
||||
goToAddress(function.getEntryPoint(), newWindow);
|
||||
callbackHandler.goToFunction(function, newWindow);
|
||||
}
|
||||
|
||||
void goToLabel(String labelName, boolean newWindow) {
|
||||
|
|
|
@ -24,6 +24,7 @@ import ghidra.program.model.address.*;
|
|||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.pcode.*;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class DecompilerUtils {
|
||||
|
||||
|
@ -314,6 +315,11 @@ public class DecompilerUtils {
|
|||
int nchild = parentNode.numChildren();
|
||||
for (int i = 0; i < nchild; i++) {
|
||||
ClangNode node = parentNode.Child(i);
|
||||
|
||||
if (node instanceof ClangFuncProto) {
|
||||
Msg.debug(null, "");
|
||||
}
|
||||
|
||||
if (node.numChildren() > 0) {
|
||||
collectTokens(tokenList, node, addressSet);
|
||||
}
|
||||
|
|
|
@ -43,10 +43,11 @@ import ghidra.framework.plugintool.util.ServiceListener;
|
|||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Function;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.SymbolIterator;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.bean.field.AnnotatedTextFieldElement;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
import resources.Icons;
|
||||
|
@ -209,16 +210,25 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
|
||||
@Override
|
||||
public boolean goTo(Program gotoProgram, ProgramLocation location) {
|
||||
if (gotoProgram != program) {
|
||||
|
||||
if (!isConnected()) {
|
||||
if (program == null) {
|
||||
// Special Case: this 'disconnected' provider is waiting to be initialized
|
||||
// with the first goTo() callback
|
||||
doSetProgram(gotoProgram);
|
||||
}
|
||||
else if (gotoProgram != program) {
|
||||
// this disconnected provider only works with its given program
|
||||
tool.setStatusInfo("Program location not applicable for this provider!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ProgramManager programManagerService = tool.getService(ProgramManager.class);
|
||||
if (programManagerService != null) {
|
||||
programManagerService.setCurrentProgram(gotoProgram);
|
||||
}
|
||||
}
|
||||
|
||||
setLocation(location, null);
|
||||
pendingViewerPosition = null;
|
||||
plugin.locationChanged(this, location);
|
||||
|
@ -451,13 +461,19 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
plugin.locationChanged(this, programLocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectionChanged(ProgramSelection programSelection) {
|
||||
currentSelection = programSelection;
|
||||
contextChanged();
|
||||
plugin.selectionChanged(this, programSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void annotationClicked(AnnotatedTextFieldElement annotation, boolean newWindow) {
|
||||
|
||||
Navigatable navigatable = this;
|
||||
if (newWindow) {
|
||||
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
|
||||
newProvider.doSetProgram(program);
|
||||
navigatable = newProvider;
|
||||
}
|
||||
|
||||
|
@ -466,6 +482,12 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
|
||||
@Override
|
||||
public void goToLabel(String symbolName, boolean newWindow) {
|
||||
|
||||
GoToService service = tool.getService(GoToService.class);
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SymbolIterator symbolIterator = program.getSymbolTable().getSymbols(symbolName);
|
||||
if (!symbolIterator.hasNext()) {
|
||||
tool.setStatusInfo(symbolName + " not found.");
|
||||
|
@ -475,19 +497,21 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
Navigatable navigatable = this;
|
||||
if (newWindow) {
|
||||
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
|
||||
newProvider.doSetProgram(program);
|
||||
navigatable = newProvider;
|
||||
}
|
||||
|
||||
GoToService service = tool.getService(GoToService.class);
|
||||
if (service != null) {
|
||||
QueryData queryData = new QueryData(symbolName, true);
|
||||
service.goToQuery(navigatable, null, queryData, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goToScalar(long value, boolean newWindow) {
|
||||
|
||||
GoToService service = tool.getService(GoToService.class);
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// try space/overlay which contains function
|
||||
AddressSpace space = controller.getFunction().getEntryPoint().getAddressSpace();
|
||||
|
@ -510,33 +534,51 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
|
||||
@Override
|
||||
public void goToAddress(Address address, boolean newWindow) {
|
||||
|
||||
GoToService service = tool.getService(GoToService.class);
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigatable navigatable = this;
|
||||
if (newWindow) {
|
||||
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
|
||||
newProvider.doSetProgram(program);
|
||||
navigatable = newProvider;
|
||||
}
|
||||
|
||||
GoToService service = tool.getService(GoToService.class);
|
||||
if (service != null) {
|
||||
service.goTo(navigatable, new ProgramLocation(program, address), program);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectionChanged(ProgramSelection programSelection) {
|
||||
currentSelection = programSelection;
|
||||
contextChanged();
|
||||
plugin.selectionChanged(this, programSelection);
|
||||
public void goToFunction(Function function, boolean newWindow) {
|
||||
|
||||
GoToService service = tool.getService(GoToService.class);
|
||||
if (service == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigatable navigatable = this;
|
||||
if (newWindow) {
|
||||
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
|
||||
navigatable = newProvider;
|
||||
}
|
||||
|
||||
Symbol symbol = function.getSymbol();
|
||||
ExternalManager externalManager = program.getExternalManager();
|
||||
ExternalLocation externalLocation = externalManager.getExternalLocation(symbol);
|
||||
if (externalLocation != null) {
|
||||
service.goToExternalLocation(navigatable, externalLocation, true);
|
||||
}
|
||||
else {
|
||||
Address address = function.getEntryPoint();
|
||||
service.goTo(navigatable, new ProgramLocation(program, address), program);
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// methods called from other members
|
||||
//==================================================================================================
|
||||
|
||||
/**
|
||||
* Called by the DecompilerClipboardProvider to get the Decompiler Panel
|
||||
*/
|
||||
DecompilerPanel getDecompilerPanel() {
|
||||
return controller.getDecompilerPanel();
|
||||
}
|
||||
|
@ -545,7 +587,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
|
|||
final DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
|
||||
// invoke later to give the window manage a chance to create the new window
|
||||
// (its done in an invoke later)
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
Swing.runLater(() -> {
|
||||
newProvider.doSetProgram(program);
|
||||
newProvider.controller.setDecompileData(controller.getDecompileData());
|
||||
newProvider.setLocation(currentLocation,
|
||||
|
|
|
@ -15,10 +15,18 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.decompile;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldPanel;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.component.ClangTextField;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.test.AbstractProgramBasedTest;
|
||||
|
||||
|
@ -67,11 +75,43 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
|
|||
|
||||
protected void setDecompilerLocation(int line, int charPosition) {
|
||||
runSwing(() -> provider.setCursorLocation(line, charPosition));
|
||||
|
||||
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||
FieldPanel fp = panel.getFieldPanel();
|
||||
click(fp, 1, true);
|
||||
}
|
||||
|
||||
protected void doubleClick() {
|
||||
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||
FieldPanel fp = panel.getFieldPanel();
|
||||
click(fp, 2, true);
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
protected FieldLocation loc(int lineNumber, int col) {
|
||||
FieldLocation loc = new FieldLocation(lineNumber, 0, 0, col);
|
||||
return loc;
|
||||
}
|
||||
|
||||
protected ClangTextField getFieldForLine(int lineNumber) {
|
||||
|
||||
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||
List<Field> fields = panel.getFields();
|
||||
Field line = fields.get(lineNumber - 1); // 0-based
|
||||
return (ClangTextField) line;
|
||||
}
|
||||
|
||||
protected String getTokenText(FieldLocation loc) {
|
||||
ClangTextField field = getFieldForLine(loc.getIndex().intValue());
|
||||
ClangToken token = field.getToken(loc);
|
||||
return token.getText();
|
||||
}
|
||||
|
||||
protected void assertToken(String tokenText, int line, int... cols) {
|
||||
for (int col : cols) {
|
||||
FieldLocation loc = loc(line, col);
|
||||
String text = getTokenText(loc);
|
||||
assertEquals(tokenText, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,18 +15,16 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.decompile;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.app.cmd.comments.SetCommentCmd;
|
||||
import ghidra.app.decompiler.ClangToken;
|
||||
import ghidra.app.decompiler.component.ClangTextField;
|
||||
import ghidra.app.decompiler.component.DecompilerPanel;
|
||||
import ghidra.program.model.listing.CodeUnit;
|
||||
|
@ -196,14 +194,6 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||
return field;
|
||||
}
|
||||
|
||||
private void assertToken(String tokenText, int line, int... cols) {
|
||||
for (int col : cols) {
|
||||
FieldLocation loc = loc(line, col);
|
||||
String text = getTokenText(loc);
|
||||
assertEquals(tokenText, text);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertDisplayText(String expected, int line) {
|
||||
FieldLocation loc = loc(line, 0 /*column*/);
|
||||
ClangTextField field = getFieldForLine(loc.getIndex().intValue());
|
||||
|
@ -219,12 +209,6 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||
assertEquals("Decompiler cursor is not at the expected location", expected, actual);
|
||||
}
|
||||
|
||||
private String getTokenText(FieldLocation loc) {
|
||||
ClangTextField field = getFieldForLine(loc.getIndex().intValue());
|
||||
ClangToken token = field.getToken(loc);
|
||||
return token.getText();
|
||||
}
|
||||
|
||||
private int getTokenIndex(ClangTextField field, FieldLocation loc) {
|
||||
Integer index = (Integer) invokeInstanceMethod("getTokenIndex", field,
|
||||
new Class[] { FieldLocation.class }, new Object[] { loc });
|
||||
|
@ -237,16 +221,4 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
|
|||
return index;
|
||||
}
|
||||
|
||||
private FieldLocation loc(int lineNumber, int col) {
|
||||
FieldLocation loc = new FieldLocation(lineNumber, 0, 0, col);
|
||||
return loc;
|
||||
}
|
||||
|
||||
private ClangTextField getFieldForLine(int lineNumber) {
|
||||
|
||||
DecompilerPanel panel = provider.getDecompilerPanel();
|
||||
List<Field> fields = panel.getFields();
|
||||
Field line = fields.get(lineNumber - 1); // 0-based
|
||||
return (ClangTextField) line;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,22 +15,28 @@
|
|||
*/
|
||||
package ghidra.app.plugin.core.decompile;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.app.cmd.function.CreateFunctionCmd;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.RefType;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.app.plugin.core.gotoquery.GoToHelper;
|
||||
import ghidra.app.plugin.core.navigation.NavigationOptions;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.OperandFieldLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.test.ClassicSampleX86ProgramBuilder;
|
||||
import mockit.*;
|
||||
|
||||
public class DecompilerNavigationTest extends AbstractDecompilerTest {
|
||||
|
||||
private boolean goToExternalLinkageCalled;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
|
@ -80,4 +86,128 @@ public class DecompilerNavigationTest extends AbstractDecompilerTest {
|
|||
assertTrue(currentLocation instanceof OperandFieldLocation);
|
||||
assertEquals(operandLocation.getAddress(), currentLocation.getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionNavigation_ExternalProgramFunction_OptionNavigateToExternal()
|
||||
throws Exception {
|
||||
|
||||
// this call triggers jMockit to load our spy
|
||||
new SpyGoToHelper();
|
||||
|
||||
tool.getOptions("Navigation").setEnum("External Navigation",
|
||||
NavigationOptions.ExternalNavigationEnum.NavigateToExternalProgram);
|
||||
|
||||
//
|
||||
// Take an existing function with a call reference and change it to call a thunk with
|
||||
// an external program reference.
|
||||
//
|
||||
|
||||
/*
|
||||
01005a32 e8 be d2 CALL ghidra
|
||||
ff ff
|
||||
*/
|
||||
|
||||
String thunkAddress = "1002cf5"; // function 'ghidra'
|
||||
createThunkToExternal(thunkAddress);
|
||||
|
||||
decompile("10059a3"); // function that calls 'ghidra'
|
||||
|
||||
int line = 35;
|
||||
int character = 1;
|
||||
assertToken("ghidra", line, character);
|
||||
setDecompilerLocation(line, character);
|
||||
doubleClick();
|
||||
|
||||
assertExternalNavigationPerformed();
|
||||
assertNotEquals(thunkAddress, codeBrowser.getCurrentAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionNavigation_ExternalProgramFunction_OptionNavigateToLinkage()
|
||||
throws Exception {
|
||||
|
||||
// this call triggers jMockit to load our spy
|
||||
new SpyGoToHelper();
|
||||
|
||||
tool.getOptions("Navigation").setEnum("External Navigation",
|
||||
NavigationOptions.ExternalNavigationEnum.NavigateToLinkage);
|
||||
|
||||
//
|
||||
// Take an existing function with a call reference and change it to call a thunk with
|
||||
// an external program reference.
|
||||
//
|
||||
|
||||
/*
|
||||
01005a32 e8 be d2 CALL ghidra
|
||||
ff ff
|
||||
*/
|
||||
|
||||
String thunkAddress = "1002cf5"; // function 'ghidra'
|
||||
createThunkToExternal(thunkAddress);
|
||||
|
||||
decompile("10059a3"); // function that calls 'ghidra'
|
||||
|
||||
int line = 35;
|
||||
int character = 1;
|
||||
assertToken("ghidra", line, character);
|
||||
setDecompilerLocation(line, character);
|
||||
doubleClick();
|
||||
|
||||
assertExternalNavigationNotPerformed();
|
||||
assertEquals(addr(thunkAddress), codeBrowser.getCurrentAddress());
|
||||
}
|
||||
|
||||
private void assertExternalNavigationPerformed() {
|
||||
// going to the 'external linkage' means we went to the thunk function and not the
|
||||
// external program
|
||||
assertFalse("External navigation did not take place", goToExternalLinkageCalled);
|
||||
}
|
||||
|
||||
private void assertExternalNavigationNotPerformed() {
|
||||
// going to the 'external linkage' means we went to the thunk function and not the
|
||||
// external program
|
||||
assertTrue("External navigation should not have taken place", goToExternalLinkageCalled);
|
||||
}
|
||||
|
||||
private void createThunkToExternal(String addressString) throws Exception {
|
||||
|
||||
int txId = program.startTransaction("Set External Location");
|
||||
try {
|
||||
|
||||
program.getExternalManager().setExternalPath("ADVAPI32.dll", "/FILE1", true);
|
||||
|
||||
Address address = addr(addressString);
|
||||
CreateFunctionCmd cmd = new CreateFunctionCmd(address);
|
||||
cmd.applyTo(program);
|
||||
|
||||
String extAddress = "0x1001000";
|
||||
ExternalManager em = program.getExternalManager();
|
||||
|
||||
// "ADVAPI32.dll", "externalFunctionXyz", "_Zxyz"
|
||||
ExternalLocation externalLocation =
|
||||
em.addExtFunction(Library.UNKNOWN, "_Zxyz", addr(extAddress), SourceType.IMPORTED);
|
||||
Library lib = em.addExternalLibraryName("ADVAPI32.dll", SourceType.IMPORTED);
|
||||
externalLocation.setName(lib, "externalFunctionXyz", SourceType.IMPORTED);
|
||||
|
||||
Function function = program.getFunctionManager().getFunctionAt(addr(addressString));
|
||||
function.setThunkedFunction(externalLocation.getFunction());
|
||||
}
|
||||
finally {
|
||||
program.endTransaction(txId, true);
|
||||
}
|
||||
|
||||
program.flushEvents();
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
public class SpyGoToHelper extends MockUp<GoToHelper> {
|
||||
|
||||
@Mock
|
||||
private boolean goToExternalLinkage(Invocation invocation, Navigatable nav,
|
||||
ExternalLocation externalLoc, boolean popupAllowed) {
|
||||
|
||||
goToExternalLinkageCalled = true;
|
||||
return invocation.proceed(nav, externalLoc, popupAllowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,6 +139,13 @@ public class DiffGoToService implements GoToService {
|
|||
return false; // Can only go to locations in the Diff's second program.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation extLoc,
|
||||
boolean checkNavigationOption) {
|
||||
showProgramFailureStatus();
|
||||
return false; // Can only go to locations in the Diff's second program.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
|
||||
TaskMonitor monitor) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue