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;
}
@Override
public boolean goToExternalLocation(Navigatable navigatable, ExternalLocation externalLoc,
boolean checkNavigationOption) {
return false;
}
@Override
public GoToOverrideService getOverrideService() {
return null;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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