GT-2932 - Decompiler - review fixes

This commit is contained in:
dragonmacher 2019-06-18 17:48:41 -04:00
parent f5f6b7c18b
commit 194addac9d
21 changed files with 609 additions and 285 deletions

View file

@ -476,6 +476,12 @@ public class ListingMergePanel extends JPanel
return false; return false;
} }
@Override
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation externalLoc,
boolean checkNavigationOption) {
return false;
}
@Override @Override
public GoToOverrideService getOverrideService() { public GoToOverrideService getOverrideService() {
return null; return null;

View file

@ -17,6 +17,8 @@ package ghidra.app.plugin.core.gotoquery;
import java.util.Stack; import java.util.Stack;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.OptionDialog; import docking.widgets.OptionDialog;
import ghidra.app.cmd.refs.SetExternalNameCmd; import ghidra.app.cmd.refs.SetExternalNameCmd;
import ghidra.app.nav.Navigatable; import ghidra.app.nav.Navigatable;
@ -96,6 +98,9 @@ public class GoToHelper {
} }
ExternalLocation externalLoc = ExternalLocation externalLoc =
program.getExternalManager().getExternalLocation(externalSym); 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); 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 * external program association has not yet been established, the user will be prompted to make
* an association if they choose before completing the navigation. * an association if they choose before completing the navigation.
* @param nav Navigatable * @param nav Navigatable
* @param externalLoc external location * @param externalLocation external location
* @param checkNavigationOption if true the {@link NavigationOptions#isGotoExternalProgramEnabled} * @param checkNavigationOption if true the {@link NavigationOptions#isGotoExternalProgramEnabled}
* option will be used to determine if navigation to the external program will be * 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 * 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 * @return true if navigation to the external program was successful or navigation to a
* linkage location was performed. * linkage location was performed.
*/ */
public boolean goToExternalLocation(Navigatable nav, ExternalLocation externalLoc, public boolean goToExternalLocation(Navigatable nav, ExternalLocation externalLocation,
boolean checkNavigationOption) { boolean checkNavigationOption) {
if (checkNavigationOption && !navOptions.isGotoExternalProgramEnabled()) { 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(); Program program = externalSym.getProgram();
String pathName = getExternalLibraryPath(externalLocation, program);
ExternalManager extMgr = program.getExternalManager(); if (pathName == null) {
String extProgName = externalLoc.getLibraryName(); return null;
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;
} }
ProjectData pd = tool.getProject().getProjectData(); ProjectData pd = tool.getProject().getProjectData();
@ -290,36 +310,20 @@ public class GoToHelper {
ProgramManager service = tool.getService(ProgramManager.class); ProgramManager service = tool.getService(ProgramManager.class);
if (domainFile == null || service == null) { if (domainFile == null || service == null) {
tool.setStatusInfo("Unable to navigate to external location. " + tool.setStatusInfo("Unable to navigate to external location. " +
"Destination program [" + pathName + "] does not exist."); "Destination program [" + externalLocation + "] does not exist.");
return false; return null;
}
Program externalProgram = service.openProgram(domainFile, -1, ProgramManager.OPEN_VISIBLE);
if (externalProgram == null) {
return false;
} }
String label = externalLoc.getOriginalImportedName(); return service.openProgram(domainFile, -1, ProgramManager.OPEN_VISIBLE);
if (label == null) { }
label = externalLoc.getLabel();
}
Address addr = externalLoc.getAddress();
// try the address first if it exists. private void performDefaultExternalProgramNavigation(Navigatable nav,
if (addr != null && externalProgram.getMemory().contains(addr)) { ExternalLocation externalLocation, Program externalProgram, Address addr) {
goTo(nav, new AddressFieldLocation(externalProgram, addr), externalProgram);
return true;
}
Symbol symbol = getExternalSymbol(externalProgram, externalLoc); // failed to navigate to address or symbol; alert the user
if (symbol != null) {
goTo(nav, symbol.getProgramLocation(), externalProgram);
return true;
}
// failed to navigate to address or symbol
if (addr != null) { if (addr != null) {
if (externalLoc.getSymbol().getSource() != SourceType.DEFAULT) { if (externalLocation.getSymbol().getSource() != SourceType.DEFAULT) {
tool.setStatusInfo("Symbol [" + getExternalName(externalLoc) + tool.setStatusInfo("Symbol [" + getExternalName(externalLocation) +
"] does not exist, and address [" + addr + "] is not in memory.", true); "] does not exist, and address [" + addr + "] is not in memory.", true);
} }
else { else {
@ -327,14 +331,39 @@ public class GoToHelper {
} }
} }
else { else {
tool.setStatusInfo("Symbol [" + getExternalName(externalLoc) + "] does not exist.", tool.setStatusInfo("Symbol [" + getExternalName(externalLocation) + "] does not exist.",
true); true);
} }
// navigate to top of external program // navigate to top of external program
addr = externalProgram.getMinAddress(); AddressFieldLocation location =
goTo(nav, new AddressFieldLocation(externalProgram, addr), externalProgram); new AddressFieldLocation(externalProgram, externalProgram.getMinAddress());
return false; // return false because we did not go to the requested address 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) { 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 // assume global symbol name if mangled name (same as original name) exists
String label = externalLoc.getOriginalImportedName(); String label = externalLoc.getOriginalImportedName();
SymbolPath symbolPath; SymbolPath symbolPath;
if (label == null) { if (label == null) {
// use alternate symbol (may or may not be mangled) // use alternate symbol (may or may not be mangled)
Symbol s = externalLoc.getSymbol(); Symbol s = externalLoc.getSymbol();

View file

@ -105,7 +105,7 @@ public interface GoToService {
/** /**
* Navigate to either the external program location or address linkage location. * Navigate to either the external program location or address linkage location.
* Specific behavior may vary based upon implementation. * Specific behavior may vary based upon implementation.
* @param nav Navigatable
* @param externalLoc external location * @param externalLoc external location
* @param checkNavigationOption if true the service navigation * @param checkNavigationOption if true the service navigation
* option will be used to determine if navigation to the external program will be * 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, public boolean goToExternalLocation(ExternalLocation externalLoc,
boolean checkNavigationOption); 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: * Parses the input string as either:
* an address/symbol expression (0x1000+5, or LAB1000+5) * 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. * 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 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 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. * @return true if the queryInput is found or appears to be a wildcard search.
*/ */
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener, public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,

View file

@ -125,6 +125,12 @@ public class GoToServiceImpl implements GoToService {
return helper.goToExternalLocation(defaultNavigatable, extLoc, checkNavigationOption); return helper.goToExternalLocation(defaultNavigatable, extLoc, checkNavigationOption);
} }
@Override
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation extLoc,
boolean checkNavigationOption) {
return helper.goToExternalLocation(navigatable, extLoc, checkNavigationOption);
}
@Override @Override
public boolean goToQuery(Navigatable navigatable, Address fromAddr, QueryData queryData, public boolean goToQuery(Navigatable navigatable, Address fromAddr, QueryData queryData,
GoToServiceListener listener, TaskMonitor monitor) { GoToServiceListener listener, TaskMonitor monitor) {
@ -133,9 +139,8 @@ public class GoToServiceImpl implements GoToService {
navigatable = defaultNavigatable; navigatable = defaultNavigatable;
} }
GoToQuery query = GoToQuery query = new GoToQuery(navigatable, plugin, this, queryData, fromAddr, listener,
new GoToQuery(navigatable, plugin, this, queryData, fromAddr, listener, helper.getOptions(), monitor);
helper.getOptions(), monitor);
return query.processQuery(); return query.processQuery();
} }

View file

@ -401,8 +401,8 @@ abstract class OperandFieldHelper extends FieldFactory {
for (int opIndex = 0; opIndex < numOperands; opIndex++) { for (int opIndex = 0; opIndex < numOperands; opIndex++) {
OperandRepresentationList operandRepresentationList = OperandRepresentationList operandRepresentationList =
codeUnitFormat.getOperandRepresentationList(inst, opIndex); codeUnitFormat.getOperandRepresentationList(inst, opIndex);
addElementsForOperand(inst, elements, opIndex, operandRepresentationList, characterOffset = addElementsForOperand(inst, elements, opIndex,
characterOffset); operandRepresentationList, characterOffset);
characterOffset = 0; characterOffset = 0;
} }
@ -504,8 +504,8 @@ abstract class OperandFieldHelper extends FieldFactory {
} }
private boolean containsNonPrimary(Reference[] refs) { private boolean containsNonPrimary(Reference[] refs) {
for (Reference ref : refs) { for (int i = 0; i < refs.length; i++) {
if (!ref.isPrimary()) { if (!refs[i].isPrimary()) {
return true; return true;
} }
} }

View file

@ -46,10 +46,6 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
private final static Class<?>[] SUPPORTED_CLASSES = new Class[] { OperandFieldLocation.class }; private final static Class<?>[] SUPPORTED_CLASSES = new Class[] { OperandFieldLocation.class };
/**
* @see FieldMouseHandlerExtension#fieldElementClicked(Object, Navigatable,
* MouseEvent, ServiceProvider)
*/
@Override @Override
public boolean fieldElementClicked(Object clickedObject, Navigatable navigatable, public boolean fieldElementClicked(Object clickedObject, Navigatable navigatable,
ProgramLocation location, MouseEvent mouseEvent, ServiceProvider serviceProvider) { ProgramLocation location, MouseEvent mouseEvent, ServiceProvider serviceProvider) {
@ -101,9 +97,6 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
return false; return false;
} }
/**
* @see FieldMouseHandlerExtension#getSupportedProgramLocations()
*/
@Override @Override
public Class<?>[] getSupportedProgramLocations() { public Class<?>[] getSupportedProgramLocations() {
return SUPPORTED_CLASSES; return SUPPORTED_CLASSES;
@ -130,19 +123,55 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
private boolean checkExternalReference(Navigatable navigatable, CodeUnit codeUnit, private boolean checkExternalReference(Navigatable navigatable, CodeUnit codeUnit,
OperandFieldLocation loc, GoToService goToService) { OperandFieldLocation loc, GoToService goToService) {
Address refAddr = loc.getRefAddress(); Address refAddr = loc.getRefAddress();
if (refAddr == null || !refAddr.isExternalAddress()) { if (refAddr == null || !refAddr.isExternalAddress()) {
return false; return checkExternalThunkFunctionReference(navigatable, codeUnit, loc, goToService);
} }
Program program = codeUnit.getProgram(); Program program = codeUnit.getProgram();
Symbol s = program.getSymbolTable().getPrimarySymbol(refAddr); Symbol s = program.getSymbolTable().getPrimarySymbol(refAddr);
if (s == null) { if (s == null) {
return false; return false;
} }
ExternalLocation extLoc = program.getExternalManager().getExternalLocation(s); ExternalLocation extLoc = program.getExternalManager().getExternalLocation(s);
return goToService.goToExternalLocation(extLoc, true); 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, private boolean checkVariableReference(Navigatable navigatable, CodeUnit codeUnit,
OperandFieldLocation loc, GoToService goToService) { OperandFieldLocation loc, GoToService goToService) {
@ -150,40 +179,18 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
return false; return false;
} }
VariableOffset variableOffset = loc.getVariableOffset(); if (goToExplicitOperandVariable(navigatable, loc, goToService)) {
if (variableOffset != null) { return true;
Variable variable = variableOffset.getVariable();
if (variable != null) {
goToService.goTo(navigatable,
new VariableNameFieldLocation(navigatable.getProgram(), variable, 0),
navigatable.getProgram());
return true;
}
} }
Program p = codeUnit.getProgram(); Program p = codeUnit.getProgram();
Address cuAddr = codeUnit.getMinAddress(); Address cuAddr = codeUnit.getMinAddress();
Address refAddr = loc.getRefAddress(); Function function = p.getFunctionManager().getFunctionContaining(cuAddr);
Function func = p.getFunctionManager().getFunctionContaining(cuAddr); if (function == null) {
if (func == null) {
return false; return false;
} }
ProgramLocation varLoc = null; if (goToRegisterVariable(navigatable, codeUnit, loc, goToService)) {
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());
return true; return true;
} }
@ -195,20 +202,71 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
Variable variable = p.getFunctionManager().getReferencedVariable(cuAddr, Variable variable = p.getFunctionManager().getReferencedVariable(cuAddr,
reference.getToAddress(), 0, reference.getReferenceType().isRead()); reference.getToAddress(), 0, reference.getReferenceType().isRead());
if (variable != null) { if (variable != null) {
varLoc = new VariableNameFieldLocation(navigatable.getProgram(), variable, 0); ProgramLocation pl =
goToService.goTo(navigatable, varLoc, navigatable.getProgram()); new VariableNameFieldLocation(navigatable.getProgram(), variable, 0);
goToService.goTo(navigatable, pl, navigatable.getProgram());
return true; return true;
} }
if (reference.isStackReference()) { if (reference.isStackReference()) {
ProgramLocation fnLoc = new FunctionSignatureFieldLocation(p, func.getEntryPoint(), ProgramLocation pl = new FunctionSignatureFieldLocation(p, function.getEntryPoint(),
null, 0, func.getPrototypeString(false, false)); null, 0, function.getPrototypeString(false, false));
goToService.goTo(navigatable, fnLoc, navigatable.getProgram()); goToService.goTo(navigatable, pl, navigatable.getProgram());
return true; return true;
} }
return false; 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) { private boolean isWrite(Instruction inst, int operandIndex, Register reg) {
for (Object obj : inst.getResultObjects()) { for (Object obj : inst.getResultObjects()) {
if (obj == reg) { if (obj == reg) {
@ -220,12 +278,12 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
private boolean checkMemRefs(Navigatable navigatable, CodeUnit codeUnit, private boolean checkMemRefs(Navigatable navigatable, CodeUnit codeUnit,
OperandFieldLocation loc, ServiceProvider serviceProvider) { OperandFieldLocation loc, ServiceProvider serviceProvider) {
Address refAddr = loc.getRefAddress(); Address refAddr = loc.getRefAddress();
if (refAddr == null || !refAddr.isMemoryAddress()) { if (refAddr == null || !refAddr.isMemoryAddress()) {
return false; return false;
} }
int opIndex = loc.getOperandIndex();
Reference[] refs = codeUnit.getOperandReferences(loc.getOperandIndex()); Reference[] refs = codeUnit.getOperandReferences(loc.getOperandIndex());
Address[] addrs = getAddressesForReferences(refs, codeUnit, serviceProvider); Address[] addrs = getAddressesForReferences(refs, codeUnit, serviceProvider);
if (addrs.length == 0) { if (addrs.length == 0) {
@ -251,26 +309,8 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
return true; return true;
} }
Address gotoAddr = null; // 1 address found
if (addrs.length == 1) { Address gotoAddr = addrs[0];
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
}
}
}
}
if (gotoAddr == null) { if (gotoAddr == null) {
return false; return false;
} }
@ -294,27 +334,28 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
ServiceProvider serviceProvider, boolean skipExternal) { ServiceProvider serviceProvider, boolean skipExternal) {
Address address = reference.getToAddress(); Address address = reference.getToAddress();
RefType refType = reference.getReferenceType(); RefType refType = reference.getReferenceType();
if (refType.isIndirect()) { if (!refType.isIndirect()) {
Program program = codeUnit.getProgram(); return address;
Data data = program.getListing().getDefinedDataAt(address);
Address indirectAddrress = null;
if (data != null) {
if (data.isPointer()) {
Reference ref = data.getPrimaryReference(0);
indirectAddrress = ref != null ? ref.getToAddress() : (Address) data.getValue();
}
}
else {
PseudoDisassembler pdis = new PseudoDisassembler(program);
indirectAddrress = pdis.getIndirectAddr(address);
}
if (indirectAddrress != null &&
(!indirectAddrress.isExternalAddress() || !skipExternal) &&
followIndirectReference(serviceProvider, indirectAddrress, program)) {
return indirectAddrress;
}
} }
Program program = codeUnit.getProgram();
Data data = program.getListing().getDefinedDataAt(address);
Address indirectAddrress = null;
if (data != null) {
if (data.isPointer()) {
Reference ref = data.getPrimaryReference(0);
indirectAddrress = ref != null ? ref.getToAddress() : (Address) data.getValue();
}
}
else {
PseudoDisassembler pdis = new PseudoDisassembler(program);
indirectAddrress = pdis.getIndirectAddr(address);
}
if (indirectAddrress != null && (!indirectAddrress.isExternalAddress() || !skipExternal) &&
followIndirectReference(serviceProvider, indirectAddrress, program)) {
return indirectAddrress;
}
return address; return address;
} }
@ -324,6 +365,7 @@ public class OperandFieldMouseHandler implements FieldMouseHandlerExtension {
if (optionsService == null) { if (optionsService == null) {
return false; return false;
} }
NavigationOptions navOptions = new NavigationOptions(optionsService); NavigationOptions navOptions = new NavigationOptions(optionsService);
try { try {
if (!navOptions.isFollowIndirectionEnabled()) { if (!navOptions.isFollowIndirectionEnabled()) {

View file

@ -73,7 +73,7 @@ class DualListingGoToService implements GoToService {
@Override @Override
public boolean goTo(Navigatable navigatable, ProgramLocation loc, Program program) { public boolean goTo(Navigatable navigatable, ProgramLocation loc, Program program) {
return dualGoTo(loc); return dualGoTo(loc);
} }
@Override @Override
@ -95,8 +95,8 @@ class DualListingGoToService implements GoToService {
AddressSetView addresses = AddressSetView addresses =
isLeftSide ? dualListing.getLeftAddresses() : dualListing.getRightAddresses(); isLeftSide ? dualListing.getLeftAddresses() : dualListing.getRightAddresses();
if (!addresses.contains(addr)) { if (!addresses.contains(addr)) {
dualListing.setStatusInfo("\"" + addr.toString() + dualListing.setStatusInfo(
"\" is outside the current listing's view."); "\"" + addr.toString() + "\" is outside the current listing's view.");
return false; return false;
} }
return true; return true;
@ -150,6 +150,13 @@ class DualListingGoToService implements GoToService {
"Connot Go To an external address from a dual listing view."); "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 @Override
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener, public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
TaskMonitor monitor) { TaskMonitor monitor) {

View file

@ -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 * Moves the cursor to the given program location and repositions the scrollbar to
* show that location in the screen * show that location in the screen.
* * @param loc the location to move to.
* @param loc the location to move to
* @return true if the 'go to' was successful
*/ */
public boolean goTo(ProgramLocation loc) { public boolean goTo(ProgramLocation loc) {
return goTo(loc, true); 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; * the given location will be placed in the center of the screen;
* when the parameter is false, then the screen will be scrolled * when the parameter is false, then the screen will be scrolled
* only enough to show the cursor. * only enough to show the cursor.
* @return true if the 'go to' was successful
*/ */
public boolean goTo(ProgramLocation loc, boolean centerWhenNotVisible) { public boolean goTo(ProgramLocation loc, boolean centerWhenNotVisible) {
final FieldLocation floc = getFieldLocation(loc); final FieldLocation floc = getFieldLocation(loc);
@ -558,27 +555,15 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
return true; return true;
} }
/** /** Scroll the view of the listing to the given location. */
* Scroll the view of the listing to the given location
* @param location the location
*/
public void scrollTo(ProgramLocation location) { public void scrollTo(ProgramLocation location) {
FieldLocation fieldLocation = getFieldLocation(location); FieldLocation fieldLocation = getFieldLocation(location);
if (fieldLocation == null) {
return; // this can happen when using restricted views
}
fieldPanel.scrollTo(fieldLocation); fieldPanel.scrollTo(fieldLocation);
} }
/** /** Center the view of the listing around the given location. */
* Center the view of the listing around the given location
* @param location the location
*/
public void center(ProgramLocation location) { public void center(ProgramLocation location) {
FieldLocation fieldLocation = getFieldLocation(location); FieldLocation fieldLocation = getFieldLocation(location);
if (fieldLocation == null) {
return; // this can happen when using restricted views
}
fieldPanel.center(fieldLocation); fieldPanel.center(fieldLocation);
} }

View file

@ -17,8 +17,6 @@ package ghidra.program.model.listing;
import java.util.*; import java.util.*;
import org.apache.commons.lang3.StringUtils;
import ghidra.app.util.NamespaceUtils; import ghidra.app.util.NamespaceUtils;
import ghidra.app.util.viewer.field.CommentUtils; import ghidra.app.util.viewer.field.CommentUtils;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
@ -38,8 +36,6 @@ import ghidra.util.MathUtilities;
public class CodeUnitFormat { public class CodeUnitFormat {
private static final String EMPTY = "";
protected static final String PLUS = "+"; protected static final String PLUS = "+";
protected static final String UNDERSCORE = "_"; protected static final String UNDERSCORE = "_";
@ -116,39 +112,37 @@ public class CodeUnitFormat {
*/ */
public String getRepresentationString(CodeUnit cu, boolean includeEOLcomment) { public String getRepresentationString(CodeUnit cu, boolean includeEOLcomment) {
StringBuilder buffy = new StringBuilder(getMnemonicRepresentation(cu)); StringBuffer stringBuffer = new StringBuffer(getMnemonicRepresentation(cu));
if (cu instanceof Instruction) { if (cu instanceof Instruction) {
Instruction instr = (Instruction) cu; Instruction instr = (Instruction) cu;
int n = instr.getNumOperands(); int n = instr.getNumOperands();
if (n > 1) {
buffy.append(' ');
}
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
String sep = instr.getSeparator(i); if (i == 0) {
buffy.append(StringUtils.isBlank(sep) ? EMPTY : sep); stringBuffer.append(" ");
buffy.append(getOperandRepresentationString(cu, i)); }
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 else { // data always has one operand
buffy.append(' '); stringBuffer.append(" ");
buffy.append(getOperandRepresentationString(cu, 0)); stringBuffer.append(getOperandRepresentationString(cu, 0));
} }
if (includeEOLcomment) { if (includeEOLcomment) {
String eolComment = cu.getComment(CodeUnit.EOL_COMMENT); String eolComment = cu.getComment(CodeUnit.EOL_COMMENT);
if (eolComment != null) { if (eolComment != null) {
// fixup annotations // fixup annotations
eolComment = CommentUtils.getDisplayString(eolComment, cu.getProgram()); eolComment = CommentUtils.getDisplayString(eolComment, cu.getProgram());
buffy.append(" // "); stringBuffer.append(" // ");
buffy.append(eolComment); stringBuffer.append(eolComment);
} }
} }
return buffy.toString(); return stringBuffer.toString();
} }
/** /**
@ -158,19 +152,19 @@ public class CodeUnitFormat {
* @return mnemonic representation * @return mnemonic representation
*/ */
public String getMnemonicRepresentation(CodeUnit cu) { public String getMnemonicRepresentation(CodeUnit cu) {
StringBuilder buffy = new StringBuilder(); StringBuffer stringBuffer = new StringBuffer();
String mnemonic = cu.getMnemonicString(); String mnemonic = cu.getMnemonicString();
if (options.showDataMutability && (cu instanceof Data) && mnemonic != null) { if (options.showDataMutability && (cu instanceof Data) && mnemonic != null) {
Data d = (Data) cu; Data d = (Data) cu;
if (d.isConstant()) { if (d.isConstant()) {
buffy.append("const "); stringBuffer.append("const ");
} }
else if (d.isVolatile()) { else if (d.isVolatile()) {
buffy.append("volatile "); stringBuffer.append("volatile ");
} }
} }
buffy.append(mnemonic); stringBuffer.append(mnemonic);
return buffy.toString(); return stringBuffer.toString();
} }
/** /**
@ -279,7 +273,7 @@ public class CodeUnitFormat {
* Perform register markup with explicit and implied register variable * Perform register markup with explicit and implied register variable
* reference. * reference.
* *
* @param instr instruction * @param inst instruction
* @param opIndex * @param opIndex
* @param func function containing instruction * @param func function containing instruction
* @param primaryRef primary reference or null * @param primaryRef primary reference or null
@ -452,17 +446,18 @@ public class CodeUnitFormat {
* @param opIndex operand index * @param opIndex operand index
* @param func function containing instruction * @param func function containing instruction
* @param primaryRef primary reference * @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 * @return true if primaryRef was included in scalar mark-up
*/ */
private boolean performAddressMarkup(Instruction instr, int opIndex, Function func, private boolean performAddressMarkup(Instruction instr, int opIndex, Function func,
Reference primaryRef, List<Object> representationList) { Reference primaryRef, List<Object> representationList) {
if (primaryRef == null || !primaryRef.isMemoryReference()) { if (primaryRef == null || !primaryRef.isMemoryReference()) {
return false; return false;
} }
Address refAddr = primaryRef.getToAddress(); Address refAddr = primaryRef.getToAddress();
int size = representationList.size(); int size = representationList.size();
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
Object obj = representationList.get(i); Object obj = representationList.get(i);
@ -1138,9 +1133,13 @@ public class CodeUnitFormat {
* Get a representation object corresponding to the specified reference. * Get a representation object corresponding to the specified reference.
* Format options are considered when generating label. * Format options are considered when generating label.
* *
* @param cu the code unit * @param cu
* @param ref the reference * @param ref
* @param var variable which corresponds to reference or null * @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 * @return reference representation object
*/ */
private Object getReferenceRepresentation(CodeUnit cu, Reference ref, Variable var) { private Object getReferenceRepresentation(CodeUnit cu, Reference ref, Variable var) {
@ -1418,6 +1417,9 @@ public class CodeUnitFormat {
return symbol.getName(); return symbol.getName();
} }
/**
* Returns ShowBlockName setting
*/
public ShowBlockName getShowBlockName() { public ShowBlockName getShowBlockName() {
return options.showBlockName; return options.showBlockName;
} }

View file

@ -45,6 +45,7 @@ public class AbstractCodeBrowserNavigationTest extends AbstractGhidraHeadedInteg
private DockingActionIf nextFunction; private DockingActionIf nextFunction;
private DockingActionIf prevFunction; private DockingActionIf prevFunction;
protected ToyProgramBuilder builder;
protected Program program; protected Program program;
protected CodeBrowserPlugin cb; protected CodeBrowserPlugin cb;
@ -83,7 +84,7 @@ public class AbstractCodeBrowserNavigationTest extends AbstractGhidraHeadedInteg
} }
private Program buildProgram() throws Exception { 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(".text", "0x1001000", 0x6600);
builder.createMemory(".data", "0x1008000", 0x600); builder.createMemory(".data", "0x1008000", 0x600);
builder.createMemory(".rsrc", "0x100a000", 0x5400); builder.createMemory(".rsrc", "0x100a000", 0x5400);
@ -98,7 +99,6 @@ public class AbstractCodeBrowserNavigationTest extends AbstractGhidraHeadedInteg
builder.applyDataType("0x1001000", PointerDataType.dataType, 1); builder.applyDataType("0x1001000", PointerDataType.dataType, 1);
builder.createExternalReference("0x1001000", "ADVAPI32.dll", "IsTextUnicode", 0); // linkage location builder.createExternalReference("0x1001000", "ADVAPI32.dll", "IsTextUnicode", 0); // linkage location
builder.createExternalReference("0x1001020", "ADVAPI32.dll", "IsTextUnicode", 0); // without pointer builder.createExternalReference("0x1001020", "ADVAPI32.dll", "IsTextUnicode", 0); // without pointer
builder.addBytesBranch("1004000", "1004010"); builder.addBytesBranch("1004000", "1004010");

View file

@ -63,8 +63,22 @@ public class ExternalCodeBrowserNavigationTest extends AbstractCodeBrowserNaviga
int txId = program.startTransaction("Set Path"); int txId = program.startTransaction("Set Path");
program.getExternalManager().setExternalPath("ADVAPI32.dll", "/FILE1", true); 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(); waitForSwing();
} }
@ -180,17 +194,35 @@ public class ExternalCodeBrowserNavigationTest extends AbstractCodeBrowserNaviga
cb.goTo(new OperandFieldLocation(program, addr("1001020"), null, null, null, 0, 0)); cb.goTo(new OperandFieldLocation(program, addr("1001020"), null, null, null, 0, 0));
assertEquals(addr("1001020"), cb.getCurrentAddress()); assertEquals(addr("1001020"), cb.getCurrentAddress());
assertEquals("FILE2", program.getDomainFile().getName()); assertEquals("FILE2", program.getDomainFile().getName());
// verify that navigation to the external program, address 0x1001888, is performed // verify that navigation to the external program, address 0x1001888, is performed
// since navigation initiated from linkage location // since navigation initiated from linkage location
click(cb, 2); click(cb, 2);
assertEquals(addr("1001888"), cb.getCurrentAddress()); assertEquals(addr("1001888"), cb.getCurrentAddress());
assertEquals("FILE1", lastNavigationProgram.getDomainFile().getName()); assertEquals("FILE1", lastNavigationProgram.getDomainFile().getName());
assertEquals(addr("1001888"), lastNavigationLocation.getAddress()); 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 @Mock
public boolean goTo(Invocation inv, final Navigatable navigatable, ProgramLocation loc, public boolean goTo(Invocation inv, final Navigatable navigatable, ProgramLocation loc,
Program p) { Program p) {
// Track last navigation location // Track last navigation location
lastNavigationLocation = loc; lastNavigationLocation = loc;
lastNavigationProgram = p; lastNavigationProgram = p;

View file

@ -84,6 +84,13 @@ public class TestDummyGoToService implements GoToService {
return false; return false;
} }
@Override
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation externalLoc,
boolean checkNavigationOption) {
// stub
return false;
}
@Override @Override
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener, public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
TaskMonitor monitor) { TaskMonitor monitor) {

View file

@ -101,20 +101,17 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
@Override @Override
public void contextChanged() { public void contextChanged() {
// TODO Auto-generated method stub // stub
} }
@Override @Override
public void decompileDataChanged(DecompileData decompileData) { public void decompileDataChanged(DecompileData decompileData) {
// TODO Auto-generated method stub // stub
} }
@Override @Override
public void exportLocation() { public void exportLocation() {
// TODO Auto-generated method stub // stub
} }
@Override @Override
@ -124,20 +121,22 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
@Override @Override
public void goToAddress(Address addr, boolean newWindow) { public void goToAddress(Address addr, boolean newWindow) {
// TODO Auto-generated method stub // stub
} }
@Override @Override
public void goToLabel(String labelName, boolean newWindow) { public void goToLabel(String labelName, boolean newWindow) {
// TODO Auto-generated method stub // stub
} }
@Override @Override
public void goToScalar(long value, boolean newWindow) { public void goToScalar(long value, boolean newWindow) {
// TODO Auto-generated method stub // stub
}
@Override
public void goToFunction(Function function, boolean newWindow) {
// stub
} }
@Override @Override
@ -150,14 +149,12 @@ public class CDisplayPanel extends JPanel implements DecompilerCallbackHandler {
@Override @Override
public void selectionChanged(ProgramSelection programSelection) { public void selectionChanged(ProgramSelection programSelection) {
// TODO Auto-generated method stub // stub
} }
@Override @Override
public void setStatusMessage(String message) { public void setStatusMessage(String message) {
// TODO Auto-generated method stub // stub
} }
public void clearAndShowMessage(String message) { public void clearAndShowMessage(String message) {

View file

@ -16,6 +16,7 @@
package ghidra.app.decompiler.component; package ghidra.app.decompiler.component;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.util.bean.field.AnnotatedTextFieldElement; import ghidra.util.bean.field.AnnotatedTextFieldElement;
@ -42,4 +43,5 @@ public interface DecompilerCallbackHandler {
void exportLocation(); void exportLocation();
void goToFunction(Function function, boolean newWindow);
} }

View file

@ -248,14 +248,11 @@ public class DecompilerController {
} }
void goToFunction(Function function, boolean newWindow) { void goToFunction(Function function, boolean newWindow) {
while (function.isThunk()) { Function thunkedFunction = function.getThunkedFunction(true);
Function thunkedFunction = function.getThunkedFunction(false); if (thunkedFunction != null) {
if (thunkedFunction == null || thunkedFunction.isExternal()) {
break;
}
function = thunkedFunction; function = thunkedFunction;
} }
goToAddress(function.getEntryPoint(), newWindow); callbackHandler.goToFunction(function, newWindow);
} }
void goToLabel(String labelName, boolean newWindow) { void goToLabel(String labelName, boolean newWindow) {

View file

@ -24,6 +24,7 @@ import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.*; import ghidra.program.model.pcode.*;
import ghidra.util.Msg;
public class DecompilerUtils { public class DecompilerUtils {
@ -314,6 +315,11 @@ public class DecompilerUtils {
int nchild = parentNode.numChildren(); int nchild = parentNode.numChildren();
for (int i = 0; i < nchild; i++) { for (int i = 0; i < nchild; i++) {
ClangNode node = parentNode.Child(i); ClangNode node = parentNode.Child(i);
if (node instanceof ClangFuncProto) {
Msg.debug(null, "");
}
if (node.numChildren() > 0) { if (node.numChildren() > 0) {
collectTokens(tokenList, node, addressSet); collectTokens(tokenList, node, addressSet);
} }

View file

@ -43,10 +43,11 @@ import ghidra.framework.plugintool.util.ServiceListener;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; 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.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.Swing;
import ghidra.util.bean.field.AnnotatedTextFieldElement; import ghidra.util.bean.field.AnnotatedTextFieldElement;
import ghidra.util.task.SwingUpdateManager; import ghidra.util.task.SwingUpdateManager;
import resources.Icons; import resources.Icons;
@ -209,16 +210,25 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
@Override @Override
public boolean goTo(Program gotoProgram, ProgramLocation location) { public boolean goTo(Program gotoProgram, ProgramLocation location) {
if (gotoProgram != program) {
if (!isConnected()) { 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!"); tool.setStatusInfo("Program location not applicable for this provider!");
return false; return false;
} }
ProgramManager programManagerService = tool.getService(ProgramManager.class);
if (programManagerService != null) {
programManagerService.setCurrentProgram(gotoProgram);
}
} }
ProgramManager programManagerService = tool.getService(ProgramManager.class);
if (programManagerService != null) {
programManagerService.setCurrentProgram(gotoProgram);
}
setLocation(location, null); setLocation(location, null);
pendingViewerPosition = null; pendingViewerPosition = null;
plugin.locationChanged(this, location); plugin.locationChanged(this, location);
@ -451,13 +461,19 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
plugin.locationChanged(this, programLocation); plugin.locationChanged(this, programLocation);
} }
@Override
public void selectionChanged(ProgramSelection programSelection) {
currentSelection = programSelection;
contextChanged();
plugin.selectionChanged(this, programSelection);
}
@Override @Override
public void annotationClicked(AnnotatedTextFieldElement annotation, boolean newWindow) { public void annotationClicked(AnnotatedTextFieldElement annotation, boolean newWindow) {
Navigatable navigatable = this; Navigatable navigatable = this;
if (newWindow) { if (newWindow) {
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider(); DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
newProvider.doSetProgram(program);
navigatable = newProvider; navigatable = newProvider;
} }
@ -466,6 +482,12 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
@Override @Override
public void goToLabel(String symbolName, boolean newWindow) { public void goToLabel(String symbolName, boolean newWindow) {
GoToService service = tool.getService(GoToService.class);
if (service == null) {
return;
}
SymbolIterator symbolIterator = program.getSymbolTable().getSymbols(symbolName); SymbolIterator symbolIterator = program.getSymbolTable().getSymbols(symbolName);
if (!symbolIterator.hasNext()) { if (!symbolIterator.hasNext()) {
tool.setStatusInfo(symbolName + " not found."); tool.setStatusInfo(symbolName + " not found.");
@ -475,19 +497,21 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
Navigatable navigatable = this; Navigatable navigatable = this;
if (newWindow) { if (newWindow) {
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider(); DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
newProvider.doSetProgram(program);
navigatable = newProvider; navigatable = newProvider;
} }
GoToService service = tool.getService(GoToService.class); QueryData queryData = new QueryData(symbolName, true);
if (service != null) { service.goToQuery(navigatable, null, queryData, null, null);
QueryData queryData = new QueryData(symbolName, true);
service.goToQuery(navigatable, null, queryData, null, null);
}
} }
@Override @Override
public void goToScalar(long value, boolean newWindow) { public void goToScalar(long value, boolean newWindow) {
GoToService service = tool.getService(GoToService.class);
if (service == null) {
return;
}
try { try {
// try space/overlay which contains function // try space/overlay which contains function
AddressSpace space = controller.getFunction().getEntryPoint().getAddressSpace(); AddressSpace space = controller.getFunction().getEntryPoint().getAddressSpace();
@ -510,33 +534,51 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
@Override @Override
public void goToAddress(Address address, boolean newWindow) { public void goToAddress(Address address, boolean newWindow) {
GoToService service = tool.getService(GoToService.class);
if (service == null) {
return;
}
Navigatable navigatable = this; Navigatable navigatable = this;
if (newWindow) { if (newWindow) {
DecompilerProvider newProvider = plugin.createNewDisconnectedProvider(); DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
newProvider.doSetProgram(program);
navigatable = newProvider; navigatable = newProvider;
} }
GoToService service = tool.getService(GoToService.class); service.goTo(navigatable, new ProgramLocation(program, address), program);
if (service != null) {
service.goTo(navigatable, new ProgramLocation(program, address), program);
}
} }
@Override @Override
public void selectionChanged(ProgramSelection programSelection) { public void goToFunction(Function function, boolean newWindow) {
currentSelection = programSelection;
contextChanged(); GoToService service = tool.getService(GoToService.class);
plugin.selectionChanged(this, programSelection); 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 // methods called from other members
//================================================================================================== //==================================================================================================
/**
* Called by the DecompilerClipboardProvider to get the Decompiler Panel
*/
DecompilerPanel getDecompilerPanel() { DecompilerPanel getDecompilerPanel() {
return controller.getDecompilerPanel(); return controller.getDecompilerPanel();
} }
@ -545,7 +587,7 @@ public class DecompilerProvider extends NavigatableComponentProviderAdapter
final DecompilerProvider newProvider = plugin.createNewDisconnectedProvider(); final DecompilerProvider newProvider = plugin.createNewDisconnectedProvider();
// invoke later to give the window manage a chance to create the new window // invoke later to give the window manage a chance to create the new window
// (its done in an invoke later) // (its done in an invoke later)
SwingUtilities.invokeLater(() -> { Swing.runLater(() -> {
newProvider.doSetProgram(program); newProvider.doSetProgram(program);
newProvider.controller.setDecompileData(controller.getDecompileData()); newProvider.controller.setDecompileData(controller.getDecompileData());
newProvider.setLocation(currentLocation, newProvider.setLocation(currentLocation,

View file

@ -15,10 +15,18 @@
*/ */
package ghidra.app.plugin.core.decompile; package ghidra.app.plugin.core.decompile;
import static org.junit.Assert.assertEquals;
import java.util.List;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import docking.widgets.fieldpanel.FieldPanel; 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.app.decompiler.component.DecompilerPanel;
import ghidra.test.AbstractProgramBasedTest; import ghidra.test.AbstractProgramBasedTest;
@ -67,11 +75,43 @@ public abstract class AbstractDecompilerTest extends AbstractProgramBasedTest {
protected void setDecompilerLocation(int line, int charPosition) { protected void setDecompilerLocation(int line, int charPosition) {
runSwing(() -> provider.setCursorLocation(line, charPosition)); runSwing(() -> provider.setCursorLocation(line, charPosition));
DecompilerPanel panel = provider.getDecompilerPanel();
FieldPanel fp = panel.getFieldPanel();
click(fp, 1, true);
} }
protected void doubleClick() { protected void doubleClick() {
DecompilerPanel panel = provider.getDecompilerPanel(); DecompilerPanel panel = provider.getDecompilerPanel();
FieldPanel fp = panel.getFieldPanel(); FieldPanel fp = panel.getFieldPanel();
click(fp, 2, true); 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);
}
} }
} }

View file

@ -15,18 +15,16 @@
*/ */
package ghidra.app.plugin.core.decompile; 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 java.util.Set;
import org.junit.Test; import org.junit.Test;
import docking.action.DockingActionIf; import docking.action.DockingActionIf;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.cmd.comments.SetCommentCmd; import ghidra.app.cmd.comments.SetCommentCmd;
import ghidra.app.decompiler.ClangToken;
import ghidra.app.decompiler.component.ClangTextField; import ghidra.app.decompiler.component.ClangTextField;
import ghidra.app.decompiler.component.DecompilerPanel; import ghidra.app.decompiler.component.DecompilerPanel;
import ghidra.program.model.listing.CodeUnit; import ghidra.program.model.listing.CodeUnit;
@ -196,14 +194,6 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
return field; 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) { private void assertDisplayText(String expected, int line) {
FieldLocation loc = loc(line, 0 /*column*/); FieldLocation loc = loc(line, 0 /*column*/);
ClangTextField field = getFieldForLine(loc.getIndex().intValue()); 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); 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) { private int getTokenIndex(ClangTextField field, FieldLocation loc) {
Integer index = (Integer) invokeInstanceMethod("getTokenIndex", field, Integer index = (Integer) invokeInstanceMethod("getTokenIndex", field,
new Class[] { FieldLocation.class }, new Object[] { loc }); new Class[] { FieldLocation.class }, new Object[] { loc });
@ -237,16 +221,4 @@ public class DecompilerClangTest extends AbstractDecompilerTest {
return index; 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;
}
} }

View file

@ -15,22 +15,28 @@
*/ */
package ghidra.app.plugin.core.decompile; package ghidra.app.plugin.core.decompile;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertTrue;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import ghidra.app.cmd.function.CreateFunctionCmd;
import ghidra.app.nav.Navigatable;
import ghidra.app.plugin.core.codebrowser.CodeViewerProvider; import ghidra.app.plugin.core.codebrowser.CodeViewerProvider;
import ghidra.program.model.listing.Program; import ghidra.app.plugin.core.gotoquery.GoToHelper;
import ghidra.program.model.symbol.RefType; import ghidra.app.plugin.core.navigation.NavigationOptions;
import ghidra.program.model.symbol.SourceType; 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.OperandFieldLocation;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.test.ClassicSampleX86ProgramBuilder; import ghidra.test.ClassicSampleX86ProgramBuilder;
import mockit.*;
public class DecompilerNavigationTest extends AbstractDecompilerTest { public class DecompilerNavigationTest extends AbstractDecompilerTest {
private boolean goToExternalLinkageCalled;
@Before @Before
@Override @Override
public void setUp() throws Exception { public void setUp() throws Exception {
@ -80,4 +86,128 @@ public class DecompilerNavigationTest extends AbstractDecompilerTest {
assertTrue(currentLocation instanceof OperandFieldLocation); assertTrue(currentLocation instanceof OperandFieldLocation);
assertEquals(operandLocation.getAddress(), currentLocation.getAddress()); 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);
}
}
} }

View file

@ -139,6 +139,13 @@ public class DiffGoToService implements GoToService {
return false; // Can only go to locations in the Diff's second program. 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 @Override
public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener, public boolean goToQuery(Address fromAddr, QueryData queryData, GoToServiceListener listener,
TaskMonitor monitor) { TaskMonitor monitor) {