Merge branch 'GP-6653_ghidra1_CallTreeWithThunks' into patch

This commit is contained in:
ghidra1
2026-04-06 13:00:04 -04:00
3 changed files with 114 additions and 42 deletions
@@ -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;
}
@@ -89,10 +89,61 @@ public class IncomingCallNode extends CallNode {
private void doGenerateChildren(Address address, List<GTreeNode> results, TaskMonitor monitor)
throws CancelledException {
ReferenceIterator refIter = program.getReferenceManager().getReferencesTo(address);
LazyMap<Function, List<GTreeNode>> nodesByFunction =
LazyMap.lazyMap(new HashMap<>(), k -> new ArrayList<>());
FunctionManager functionManager = program.getFunctionManager();
Set<Address> 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<GTreeNode> children = nodesByFunction.values()
.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
results.addAll(children);
}
private void collectIncomingByReference(Address address,
LazyMap<Function, List<GTreeNode>> nodesByFunction,
Set<Address> 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<GTreeNode> children = nodesByFunction.values()
.stream()
.flatMap(list -> list.stream())
.collect(Collectors.toList());
results.addAll(children);
}
@Override
@@ -87,34 +87,19 @@ public class OutgoingCallNode extends CallNode {
Function currentFunction = fm.getFunctionContaining(address);
LazyMap<Function, List<GTreeNode>> 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<Function, List<GTreeNode>> 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<Function, List<GTreeNode>> nodes, Reference reference,
Function calledFunction) {
Address fromAddress = reference.getFromAddress();