diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java index c1fe73d0ee..5e705d7740 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/CallTreeProvider.java @@ -46,6 +46,7 @@ import ghidra.framework.plugintool.ComponentProviderAdapter; import ghidra.program.database.symbol.FunctionSymbol; import ghidra.program.model.address.*; import ghidra.program.model.listing.*; +import ghidra.program.model.symbol.ExternalLocation; import ghidra.program.model.symbol.Symbol; import ghidra.program.util.*; import ghidra.util.HelpLocation; @@ -757,7 +758,22 @@ public class CallTreeProvider extends ComponentProviderAdapter { isFiringNavigationEvent = true; GoToService goToService = tool.getService(GoToService.class); if (goToService != null) { - goToService.goTo(location); + Address locAddr = location.getAddress(); + if (locAddr.isExternalAddress()) { + // NOTE: The simple form of GoTo(ProgramLocation) will always goto the linkage + // point and not the actual external program. We use the special form for + // external location to ensure it respects the navigation options. + Symbol symbol = currentProgram.getSymbolTable().getPrimarySymbol(locAddr); + if (symbol != null) { + ExternalLocation externalLocation = + currentProgram.getExternalManager().getExternalLocation(symbol); + goToService.goToExternalLocation(goToService.getDefaultNavigatable(), + externalLocation, true); + } + } + else { + goToService.goTo(location); + } isFiringNavigationEvent = false; return; } diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/IncomingCallNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/IncomingCallNode.java index ee2785bdbf..1aa10b24a1 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/IncomingCallNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/IncomingCallNode.java @@ -89,10 +89,61 @@ public class IncomingCallNode extends CallNode { private void doGenerateChildren(Address address, List results, TaskMonitor monitor) throws CancelledException { - ReferenceIterator refIter = program.getReferenceManager().getReferencesTo(address); LazyMap> nodesByFunction = LazyMap.lazyMap(new HashMap<>(), k -> new ArrayList<>()); FunctionManager functionManager = program.getFunctionManager(); + + Set
thunkAddrSet = Set.of(); + Function currentFunction = functionManager.getFunctionAt(address); + if (currentFunction != null) { + + if (currentFunction.isThunk() && !callTreeOptions.allowsThunks()) { + // If this is a thunk and thunks are filtered-out we must force current function + // to the real function + currentFunction = currentFunction.getThunkedFunction(true); + } + + // Check to see if current function is thunked + Address[] functionThunkAddresses = + currentFunction.getFunctionThunkAddresses(!callTreeOptions.allowsThunks()); + if (functionThunkAddresses != null) { + thunkAddrSet = Set.of(functionThunkAddresses); + for (Address thunkAddr : functionThunkAddresses) { + if (address.equals(thunkAddr)) { + continue; // avoid possible recursive thunk (should not occur) + } + Function thunkFunction = functionManager.getFunctionAt(thunkAddr); + if (callTreeOptions.allowsThunks()) { + // include thunk node in tree + IncomingCallNode node = + new IncomingCallNode(program, thunkFunction, thunkAddr, true, + callTreeOptions); + addNode(nodesByFunction, node); + } + else { + // Do NOT include thunk in tree but follow references to the thunk + collectIncomingByReference(thunkAddr, nodesByFunction, thunkAddrSet, + monitor); + } + } + } + } + + collectIncomingByReference(address, nodesByFunction, thunkAddrSet, monitor); + + List children = nodesByFunction.values() + .stream() + .flatMap(list -> list.stream()) + .collect(Collectors.toList()); + results.addAll(children); + } + + private void collectIncomingByReference(Address address, + LazyMap> nodesByFunction, + Set
ignoreFromSet, TaskMonitor monitor) + throws CancelledException { + FunctionManager functionManager = program.getFunctionManager(); + ReferenceIterator refIter = program.getReferenceManager().getReferencesTo(address); while (refIter.hasNext()) { monitor.checkCancelled(); Reference ref = refIter.next(); @@ -102,13 +153,8 @@ public class IncomingCallNode extends CallNode { continue; } - // If we are not showing thunks, then replace each thunk with all calls to that thunk - if (caller.isThunk() && !callTreeOptions.allowsThunks()) { - Address callerEntry = caller.getEntryPoint(); - if (!address.equals(callerEntry)) { // recursive reference from thunk to itself - doGenerateChildren(callerEntry, results, monitor); - } - continue; + if (ignoreFromSet.contains(caller.getEntryPoint())) { + continue; // ignore references for a thunk relationship (e.g., jump, etc.) } IncomingCallNode node = @@ -116,12 +162,6 @@ public class IncomingCallNode extends CallNode { callTreeOptions); addNode(nodesByFunction, node); } - - List children = nodesByFunction.values() - .stream() - .flatMap(list -> list.stream()) - .collect(Collectors.toList()); - results.addAll(children); } @Override diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/OutgoingCallNode.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/OutgoingCallNode.java index 92e9e021ed..83ef3f9c25 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/OutgoingCallNode.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/calltree/OutgoingCallNode.java @@ -87,34 +87,19 @@ public class OutgoingCallNode extends CallNode { Function currentFunction = fm.getFunctionContaining(address); LazyMap> nodesByFunction = LazyMap.lazyMap(new HashMap<>(), k -> new ArrayList<>()); - FunctionManager functionManager = program.getFunctionManager(); - ReferenceManager refManager = program.getReferenceManager(); - AddressRangeIterator rangeIter = currentFunction.getBody().getAddressRanges(); - while (rangeIter.hasNext()) { - AddressRange range = rangeIter.next(); - ReferenceIterator refIter = refManager.getReferenceIterator(range.getMinAddress()); - while (refIter.hasNext()) { - monitor.checkCancelled(); - Reference reference = refIter.next(); - if (!range.contains(reference.getFromAddress())) { - break; // go to next AddressRange - } - Address toAddress = reference.getToAddress(); - Function calledFunction = functionManager.getFunctionAt(toAddress); - if (calledFunction == null) { - createNode(nodesByFunction, reference, calledFunction); - continue; - } - - // If we are not showing thunks, then replace the thunk with the thunked function - if (calledFunction.isThunk() && !callTreeOptions.allowsThunks()) { - Function thunkedFunction = calledFunction.getThunkedFunction(true); - createNode(nodesByFunction, reference, thunkedFunction); - continue; - } - - createNode(nodesByFunction, reference, calledFunction); + if (currentFunction.isThunk()) { + Function thunkedFunction = + currentFunction.getThunkedFunction(!callTreeOptions.allowsThunks()); + ThunkReference thunkRef = new ThunkReference(currentFunction.getEntryPoint(), + thunkedFunction.getEntryPoint()); + createNode(nodesByFunction, thunkRef, thunkedFunction); + } + else { + AddressRangeIterator rangeIter = currentFunction.getBody().getAddressRanges(); + while (rangeIter.hasNext()) { + AddressRange range = rangeIter.next(); + collectOutgoingByReference(range, nodesByFunction, monitor); } } @@ -125,6 +110,37 @@ public class OutgoingCallNode extends CallNode { results.addAll(children); } + private void collectOutgoingByReference(AddressRange range, + LazyMap> nodesByFunction, + TaskMonitor monitor) throws CancelledException { + FunctionManager functionManager = program.getFunctionManager(); + ReferenceManager refManager = program.getReferenceManager(); + ReferenceIterator refIter = refManager.getReferenceIterator(range.getMinAddress()); + while (refIter.hasNext()) { + monitor.checkCancelled(); + Reference reference = refIter.next(); + if (!range.contains(reference.getFromAddress())) { + break; // go to next AddressRange + } + Address toAddress = reference.getToAddress(); + Function calledFunction = functionManager.getFunctionAt(toAddress); + if (calledFunction == null) { + // Assume analysis has not be run fully + createNode(nodesByFunction, reference, calledFunction); + continue; + } + + // If we are not showing thunks, then replace the thunk with the thunked function + if (calledFunction.isThunk() && !callTreeOptions.allowsThunks()) { + Function thunkedFunction = calledFunction.getThunkedFunction(true); + createNode(nodesByFunction, reference, thunkedFunction); + continue; + } + + createNode(nodesByFunction, reference, calledFunction); + } + } + private void createNode(LazyMap> nodes, Reference reference, Function calledFunction) { Address fromAddress = reference.getFromAddress();