mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
parent
793ad1faae
commit
09d28eb30c
46 changed files with 3421 additions and 937 deletions
|
@ -927,22 +927,43 @@
|
|||
<P><B>Display Local Block -</B> Prepends the name of the memory block containing the XREF
|
||||
source address to each XREF.</P>
|
||||
|
||||
<P><B>Namespace Options: </B>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><B>Display Non-local Namespace -</B> Select this option to prepend the namespace to all
|
||||
XREFs that are not from an instruction within the current Function's body. Currently,
|
||||
this would only affect XREFs that originate in some other function.<BR>
|
||||
</P>
|
||||
|
||||
<P><B>Display Library in Namespace -</B> Include the library name in the namespace.<BR>
|
||||
</P>
|
||||
|
||||
<P><B>Display Local Namespace -</B> Select this option to prepend the namespace to all
|
||||
XREFs that are from the current Function.<BR>
|
||||
</P>
|
||||
<BLOCKQUOTE>
|
||||
<P><B>Use Local Namespace Override</B> - Select this
|
||||
option to show a fixed prefix for local XREFs instead of the function's name. This
|
||||
option is only available if the "Display Local Namespace" option is on. The text box
|
||||
contains the prefix to use for local XREFs.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<P><B>Display Reference Type -</B> Shows a single letter to represent the type of reference.
|
||||
Some of the possible types are:
|
||||
<CODE>Read (R), Write (W), Data (*), Call (c), Jump (j) and Thunk (T)</CODE>.
|
||||
|
||||
<P><B>Group by Function -</B> Groups all references by the containing source function.
|
||||
With this option off, all references within a function are displayed on their on row.
|
||||
With this feature on, each function will get a single row, with all references displayed on
|
||||
that row.
|
||||
|
||||
<P><B>Maximum Number of XREFs To Display -</B> The maximum number of lines used to display
|
||||
XREFs. Additional XREFs will not be displayed.</P>
|
||||
|
||||
<P><B>Display Non-local Namespace -</B> Select this option to prepend the namespace to all
|
||||
XREFs that are not from an instruction within the current Function's body. Currently,
|
||||
this would only affect XREFs that originate in some other function.<BR>
|
||||
</P>
|
||||
|
||||
<P><B>Display Local Namespace -</B> Select this option to prepend the namespace to all
|
||||
XREFs that are from the current Function.<BR>
|
||||
</P>
|
||||
|
||||
<P><SPAN style="font-weight: bold;">Use Local Namespace Override</SPAN> - Select this
|
||||
option to show a fixed prefix for local XREFs instead of the function's name. This
|
||||
option is only available if the "Display Local Namespace" option is on. The text box
|
||||
contains the prefix to use for local XREFs.</P>
|
||||
|
||||
<P><B>Sort References by -</B> Allows the references to be sorted by Address or by type.
|
||||
This is most useful when <B>Group by Function</B> is off.
|
||||
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
|
|
|
@ -988,8 +988,8 @@ public class CodeBrowserPlugin extends Plugin
|
|||
return; // not sure if this can happen
|
||||
}
|
||||
|
||||
Set<Reference> refs = XReferenceUtil.getAllXrefs(location);
|
||||
XReferenceUtil.showAllXrefs(connectedProvider, tool, service, location, refs);
|
||||
Set<Reference> refs = XReferenceUtils.getAllXrefs(location);
|
||||
XReferenceUtils.showXrefs(connectedProvider, tool, service, location, refs);
|
||||
}
|
||||
|
||||
private GhidraProgramTableModel<Address> createTableModel(CodeUnitIterator iterator,
|
||||
|
@ -1065,16 +1065,13 @@ public class CodeBrowserPlugin extends Plugin
|
|||
*/
|
||||
public boolean goToField(Address a, String fieldName, int occurrence, int row, int col,
|
||||
boolean scroll) {
|
||||
|
||||
boolean result = SystemUtilities
|
||||
.runSwingNow(() -> doGoToField(a, fieldName, occurrence, row, col, scroll));
|
||||
return result;
|
||||
return Swing.runNow(() -> doGoToField(a, fieldName, occurrence, row, col, scroll));
|
||||
}
|
||||
|
||||
private boolean doGoToField(Address a, String fieldName, int occurrence, int row, int col,
|
||||
boolean scroll) {
|
||||
|
||||
SystemUtilities.assertThisIsTheSwingThread("GoTo must be performed on the Swing thread");
|
||||
Swing.assertSwingThread("'Go To' must be performed on the Swing thread");
|
||||
|
||||
// make sure that the code browser is ready to go--sometimes it is not, due to timing
|
||||
// during the testing process, like when the tool is first loaded.
|
||||
|
|
|
@ -32,10 +32,12 @@ import ghidra.util.table.ReferencesFromTableModel;
|
|||
import ghidra.util.table.field.ReferenceEndpoint;
|
||||
|
||||
/**
|
||||
* A utility class to handle the generation of
|
||||
* direct and offcut cross-reference (xref) lists
|
||||
* A utility class to handle the generation of direct and offcut cross-reference (xref) lists
|
||||
* on code units and stack variables.
|
||||
*
|
||||
* @deprecated deprecated for 10.1; removal for 10.3 or later
|
||||
*/
|
||||
@Deprecated // Use XReferenceUtils instead
|
||||
public class XReferenceUtil {
|
||||
private final static Address[] EMPTY_ADDR_ARRAY = new Address[0];
|
||||
private final static Reference[] EMPTY_REF_ARRAY = new Reference[0];
|
||||
|
@ -59,11 +61,11 @@ public class XReferenceUtil {
|
|||
/**
|
||||
* Returns an array containing the first <b><code>maxNumber</code></b>
|
||||
* direct xref addresses to the specified code unit.
|
||||
*
|
||||
*
|
||||
* @param cu the code unit to generate the xrefs
|
||||
* @param maxNumber max number of xrefs to get,
|
||||
* or -1 to get all references
|
||||
*
|
||||
*
|
||||
* @return array first <b><code>maxNumber</code></b> xrefs to the code unit
|
||||
*/
|
||||
public final static Address[] getXRefList(CodeUnit cu, int maxNumber) {
|
||||
|
@ -71,9 +73,8 @@ public class XReferenceUtil {
|
|||
if (prog == null) {
|
||||
return EMPTY_ADDR_ARRAY;
|
||||
}
|
||||
List<Address> xrefList = new ArrayList<Address>();
|
||||
//lookup the direct xrefs to the current code unit
|
||||
//
|
||||
List<Address> xrefList = new ArrayList<>();
|
||||
// lookup the direct xrefs to the current code unit
|
||||
ReferenceIterator iter = prog.getReferenceManager().getReferencesTo(cu.getMinAddress());
|
||||
while (iter.hasNext()) {
|
||||
Reference ref = iter.next();
|
||||
|
@ -91,11 +92,11 @@ public class XReferenceUtil {
|
|||
/**
|
||||
* Returns an array containing the first <b><code>maxNumber</code></b>
|
||||
* direct xref references to the specified code unit.
|
||||
*
|
||||
*
|
||||
* @param cu the code unit to generate the xrefs
|
||||
* @param maxNumber max number of xrefs to get,
|
||||
* or -1 to get all references
|
||||
*
|
||||
*
|
||||
* @return array first <b><code>maxNumber</code></b> xrefs to the code unit
|
||||
*/
|
||||
public final static Reference[] getXReferences(CodeUnit cu, int maxNumber) {
|
||||
|
@ -103,7 +104,7 @@ public class XReferenceUtil {
|
|||
if (prog == null) {
|
||||
return EMPTY_REF_ARRAY;
|
||||
}
|
||||
List<Reference> xrefList = new ArrayList<Reference>();
|
||||
List<Reference> xrefList = new ArrayList<>();
|
||||
//lookup the direct xrefs to the current code unit
|
||||
//
|
||||
ReferenceIterator iter = prog.getReferenceManager().getReferencesTo(cu.getMinAddress());
|
||||
|
@ -156,7 +157,7 @@ public class XReferenceUtil {
|
|||
if (prog == null) {
|
||||
return EMPTY_ADDR_ARRAY;
|
||||
}
|
||||
List<Address> offcutList = new ArrayList<Address>();
|
||||
List<Address> offcutList = new ArrayList<>();
|
||||
// Lookup the offcut xrefs...
|
||||
//
|
||||
if (cu.getLength() > 1) {
|
||||
|
@ -195,7 +196,7 @@ public class XReferenceUtil {
|
|||
if (prog == null) {
|
||||
return EMPTY_REF_ARRAY;
|
||||
}
|
||||
List<Reference> offcutList = new ArrayList<Reference>();
|
||||
List<Reference> offcutList = new ArrayList<>();
|
||||
// Lookup the offcut xrefs...
|
||||
//
|
||||
if (cu.getLength() > 1) {
|
||||
|
@ -227,6 +228,7 @@ public class XReferenceUtil {
|
|||
* @return count of all offcut xrefs to the code unit
|
||||
*/
|
||||
public static int getOffcutXRefCount(CodeUnit cu) {
|
||||
|
||||
Program prog = cu.getProgram();
|
||||
if (prog == null) {
|
||||
return 0;
|
||||
|
@ -300,7 +302,7 @@ public class XReferenceUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* Shows all xrefs to the given location in a new table. These xrefs are retrieved
|
||||
* Shows all xrefs to the given location in a new table. These xrefs are retrieved
|
||||
* from the given supplier. Thus, it is up to the client to determine which xrefs to show.
|
||||
*
|
||||
* @param navigatable the navigatable used for navigation from the table
|
||||
|
@ -322,7 +324,7 @@ public class XReferenceUtil {
|
|||
|
||||
/**
|
||||
* Returns all xrefs to the given location. If in data, then xrefs to the specific data
|
||||
* component will be returned. Otherwise, the code unit containing the address of the
|
||||
* component will be returned. Otherwise, the code unit containing the address of the
|
||||
* given location will be used as the source of the xrefs.
|
||||
*
|
||||
* @param location the location for which to get xrefs
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.plugin.core.table.TableComponentProvider;
|
||||
import ghidra.app.util.query.TableService;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.data.DataUtilities;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.util.table.ReferencesFromTableModel;
|
||||
import ghidra.util.table.field.ReferenceEndpoint;
|
||||
|
||||
public class XReferenceUtils {
|
||||
|
||||
// Methods in this class treat -1 as a key to return all references and
|
||||
// not cap the result set.
|
||||
private final static int ALL_REFS = -1;
|
||||
|
||||
/**
|
||||
* Returns an array containing the first <b><code>max</code></b>
|
||||
* direct xref references to the specified code unit.
|
||||
*
|
||||
* @param cu the code unit to generate the xrefs
|
||||
* @param max max number of xrefs to get, or -1 to get all references
|
||||
*
|
||||
* @return array first <b><code>max</code></b> xrefs to the code unit
|
||||
*/
|
||||
public final static List<Reference> getXReferences(CodeUnit cu, int max) {
|
||||
Program program = cu.getProgram();
|
||||
if (program == null) {
|
||||
Collections.emptyList();
|
||||
}
|
||||
|
||||
// lookup the direct xrefs to the current code unit
|
||||
List<Reference> xrefs = new ArrayList<>();
|
||||
Address minAddress = cu.getMinAddress();
|
||||
ReferenceIterator it = program.getReferenceManager().getReferencesTo(minAddress);
|
||||
while (it.hasNext()) {
|
||||
if (xrefs.size() - max == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
Reference ref = it.next();
|
||||
xrefs.add(ref);
|
||||
}
|
||||
|
||||
// Check for thunk reference
|
||||
Function func = program.getFunctionManager().getFunctionAt(minAddress);
|
||||
if (func != null) {
|
||||
Address[] thunkAddrs = func.getFunctionThunkAddresses();
|
||||
if (thunkAddrs != null) {
|
||||
for (Address thunkAddr : thunkAddrs) {
|
||||
xrefs.add(new ThunkReference(thunkAddr, func.getEntryPoint()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return xrefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all offcut xref references to the specified code unit
|
||||
*
|
||||
* @param cu the code unit to generate the offcut xrefs
|
||||
* @param max max number of offcut xrefs to get, or -1 to get all offcut references
|
||||
* @return array of all offcut xrefs to the code unit
|
||||
*/
|
||||
public static List<Reference> getOffcutXReferences(CodeUnit cu, int max) {
|
||||
Program program = cu.getProgram();
|
||||
if (program == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (cu.getLength() <= 1) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Reference> offcuts = new ArrayList<>();
|
||||
ReferenceManager refMgr = program.getReferenceManager();
|
||||
AddressSet set = new AddressSet(cu.getMinAddress().add(1), cu.getMaxAddress());
|
||||
AddressIterator it = refMgr.getReferenceDestinationIterator(set, true);
|
||||
while (it.hasNext()) {
|
||||
Address addr = it.next();
|
||||
ReferenceIterator refIter = refMgr.getReferencesTo(addr);
|
||||
while (refIter.hasNext()) {
|
||||
if (offcuts.size() - max == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
Reference ref = refIter.next();
|
||||
offcuts.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
return offcuts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the provided lists with the direct and offcut xrefs to the specified variable
|
||||
*
|
||||
* @param var variable to get references
|
||||
* @param xrefs list to put direct references in
|
||||
* @param offcuts list to put offcut references in
|
||||
*/
|
||||
public static void getVariableRefs(Variable var, List<Reference> xrefs,
|
||||
List<Reference> offcuts) {
|
||||
getVariableRefs(var, xrefs, offcuts, ALL_REFS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the provided lists with the direct and offcut xrefs to the specified variable
|
||||
*
|
||||
* @param var variable to get references
|
||||
* @param xrefs list to put direct references in
|
||||
* @param offcuts list to put offcut references in
|
||||
* @param max max number of xrefs to get, or -1 to get all references
|
||||
*/
|
||||
public static void getVariableRefs(Variable var, List<Reference> xrefs,
|
||||
List<Reference> offcuts, int max) {
|
||||
|
||||
Address addr = var.getMinAddress();
|
||||
if (addr == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Program program = var.getFunction().getProgram();
|
||||
ReferenceManager refMgr = program.getReferenceManager();
|
||||
Reference[] refs = refMgr.getReferencesTo(var);
|
||||
int total = 0;
|
||||
for (Reference vref : refs) {
|
||||
if (total++ - max == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (addr.equals(vref.getToAddress())) {
|
||||
xrefs.add(vref);
|
||||
}
|
||||
else {
|
||||
offcuts.add(vref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all xrefs to the given location. If in data, then xrefs to the specific data
|
||||
* component will be returned. Otherwise, the code unit containing the address of the
|
||||
* given location will be used as the source of the xrefs.
|
||||
*
|
||||
* @param location the location for which to get xrefs
|
||||
* @return the xrefs
|
||||
*/
|
||||
public static Set<Reference> getAllXrefs(ProgramLocation location) {
|
||||
|
||||
CodeUnit cu = DataUtilities.getDataAtLocation(location);
|
||||
if (cu == null) {
|
||||
Address toAddress = location.getAddress();
|
||||
Listing listing = location.getProgram().getListing();
|
||||
cu = listing.getCodeUnitContaining(toAddress);
|
||||
}
|
||||
|
||||
if (cu == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
List<Reference> xrefs = getXReferences(cu, ALL_REFS);
|
||||
List<Reference> offcuts = getOffcutXReferences(cu, ALL_REFS);
|
||||
|
||||
// Remove duplicates
|
||||
Set<Reference> set = new HashSet<>();
|
||||
set.addAll(xrefs);
|
||||
set.addAll(offcuts);
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows all xrefs to the given location in a new table.
|
||||
*
|
||||
* @param navigatable the navigatable used for navigation from the table
|
||||
* @param serviceProvider the service provider needed to wire navigation
|
||||
* @param service the service needed to show the table
|
||||
* @param location the location for which to find references
|
||||
* @param xrefs the xrefs to show
|
||||
*/
|
||||
public static void showXrefs(Navigatable navigatable, ServiceProvider serviceProvider,
|
||||
TableService service, ProgramLocation location, Collection<Reference> xrefs) {
|
||||
|
||||
ReferencesFromTableModel model =
|
||||
new ReferencesFromTableModel(new ArrayList<>(xrefs), serviceProvider,
|
||||
location.getProgram());
|
||||
TableComponentProvider<ReferenceEndpoint> provider = service.showTable(
|
||||
"XRefs to " + location.getAddress().toString(), "XRefs", model, "XRefs", navigatable);
|
||||
provider.installRemoveItemsAction();
|
||||
}
|
||||
}
|
|
@ -17,12 +17,11 @@ package ghidra.app.util.exporter;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.app.util.XReferenceUtil;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.app.util.XReferenceUtils;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceManager;
|
||||
import ghidra.program.model.symbol.*;
|
||||
|
||||
class ReferenceLineDispenser extends AbstractLineDispenser {
|
||||
|
||||
|
@ -35,48 +34,46 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
|
|||
private Memory memory;
|
||||
private ReferenceManager referenceManager;
|
||||
|
||||
private List<String> lines = new ArrayList<String>();
|
||||
private List<String> lines = new ArrayList<>();
|
||||
|
||||
ReferenceLineDispenser() {
|
||||
}
|
||||
|
||||
ReferenceLineDispenser(boolean forwardRefs, CodeUnit cu, Program program, ProgramTextOptions options) {
|
||||
this.memory = program.getMemory();
|
||||
ReferenceLineDispenser(boolean forwardRefs, CodeUnit cu, Program program,
|
||||
ProgramTextOptions options) {
|
||||
this.memory = program.getMemory();
|
||||
this.referenceManager = program.getReferenceManager();
|
||||
this.displayRefHeader = options.isShowReferenceHeaders();
|
||||
this.prefix = options.getCommentPrefix();
|
||||
this.header = (forwardRefs ? " FWD" : "XREF");
|
||||
this.headerWidth = options.getRefHeaderWidth();
|
||||
this.width = options.getRefWidth();
|
||||
this.fillAmount = options.getAddrWidth()
|
||||
+ options.getBytesWidth()
|
||||
+ options.getLabelWidth();
|
||||
this.fillAmount =
|
||||
options.getAddrWidth() + options.getBytesWidth() + options.getLabelWidth();
|
||||
this.isHTML = options.isHTML();
|
||||
|
||||
Address [] refs = (forwardRefs ? getForwardRefs(cu) : XReferenceUtil.getXRefList(cu));
|
||||
Address [] offcuts = (forwardRefs ? EMPTY_ADDR_ARR : XReferenceUtil.getOffcutXRefList(cu));
|
||||
Address[] refs = (forwardRefs ? getForwardRefs(cu) : getXRefList(cu));
|
||||
Address[] offcuts = (forwardRefs ? EMPTY_ADDR_ARR : getOffcutXRefList(cu));
|
||||
|
||||
processRefs(cu.getMinAddress(), refs, offcuts);
|
||||
}
|
||||
|
||||
ReferenceLineDispenser(Variable var, Program program, ProgramTextOptions options) {
|
||||
this.memory = program.getMemory();
|
||||
this.memory = program.getMemory();
|
||||
this.referenceManager = program.getReferenceManager();
|
||||
this.displayRefHeader = options.isShowReferenceHeaders();
|
||||
this.header = "XREF";
|
||||
this.headerWidth = options.getRefHeaderWidth();
|
||||
this.prefix = options.getCommentPrefix();
|
||||
this.width = options.getStackVarXrefWidth();
|
||||
this.fillAmount = options.getStackVarPreNameWidth()
|
||||
+ options.getStackVarNameWidth()
|
||||
+ options.getStackVarDataTypeWidth()
|
||||
+ options.getStackVarOffsetWidth()
|
||||
+ options.getStackVarCommentWidth();
|
||||
this.fillAmount = options.getStackVarPreNameWidth() + options.getStackVarNameWidth() +
|
||||
options.getStackVarDataTypeWidth() + options.getStackVarOffsetWidth() +
|
||||
options.getStackVarCommentWidth();
|
||||
this.isHTML = options.isHTML();
|
||||
|
||||
List<Reference> xrefs = new ArrayList<Reference>();
|
||||
List<Reference> offcuts = new ArrayList<Reference>();
|
||||
XReferenceUtil.getVariableRefs(var, xrefs, offcuts);
|
||||
List<Reference> xrefs = new ArrayList<>();
|
||||
List<Reference> offcuts = new ArrayList<>();
|
||||
XReferenceUtils.getVariableRefs(var, xrefs, offcuts);
|
||||
Address[] xrefAddr = extractFromAddr(xrefs);
|
||||
Address[] offcutsAddr = extractFromAddr(offcuts);
|
||||
|
||||
|
@ -84,9 +81,9 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
|
|||
xrefAddr, offcutsAddr);
|
||||
}
|
||||
|
||||
private Address [] extractFromAddr(List<Reference> refs) {
|
||||
Address [] addrs = new Address[refs.size()];
|
||||
for (int i=0; i < addrs.length; i++) {
|
||||
private Address[] extractFromAddr(List<Reference> refs) {
|
||||
Address[] addrs = new Address[refs.size()];
|
||||
for (int i = 0; i < addrs.length; i++) {
|
||||
addrs[i] = refs.get(i).getFromAddress();
|
||||
}
|
||||
Arrays.sort(addrs);
|
||||
|
@ -113,18 +110,18 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
|
|||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
private Address [] getForwardRefs(CodeUnit cu) {
|
||||
private Address[] getForwardRefs(CodeUnit cu) {
|
||||
boolean showRefs = false;
|
||||
|
||||
Address cuAddr = cu.getMinAddress();
|
||||
Reference [] monRefs = cu.getMnemonicReferences();
|
||||
Reference[] monRefs = cu.getMnemonicReferences();
|
||||
Reference primMonRef = referenceManager.getPrimaryReferenceFrom(cuAddr, CodeUnit.MNEMONIC);
|
||||
showRefs = (monRefs.length == 1 && primMonRef == null) || (monRefs.length > 1);
|
||||
|
||||
if (!showRefs) {
|
||||
int opCount = cu.getNumOperands();
|
||||
for (int i = 0 ; i < opCount ; ++i) {
|
||||
Reference [] opRefs = cu.getOperandReferences(i);
|
||||
for (int i = 0; i < opCount; ++i) {
|
||||
Reference[] opRefs = cu.getOperandReferences(i);
|
||||
if (opRefs.length > 1) {
|
||||
showRefs = true;
|
||||
break;
|
||||
|
@ -136,9 +133,9 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
|
|||
return EMPTY_ADDR_ARR;
|
||||
}
|
||||
|
||||
Reference [] mRefs = cu.getReferencesFrom();
|
||||
Address [] refs = new Address[mRefs.length];
|
||||
for (int i = 0 ; i < mRefs.length ; ++i) {
|
||||
Reference[] mRefs = cu.getReferencesFrom();
|
||||
Address[] refs = new Address[mRefs.length];
|
||||
for (int i = 0; i < mRefs.length; ++i) {
|
||||
refs[i] = mRefs[i].getToAddress();
|
||||
}
|
||||
Arrays.sort(refs);
|
||||
|
@ -147,7 +144,7 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
|
|||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void processRefs(Address addr, Address [] refs, Address [] offcuts) {
|
||||
private void processRefs(Address addr, Address[] refs, Address[] offcuts) {
|
||||
if (width < 1) {
|
||||
return;
|
||||
}
|
||||
|
@ -157,8 +154,8 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
|
|||
|
||||
StringBuffer buf = new StringBuffer();
|
||||
|
||||
Address [] all = new Address[refs.length + offcuts.length];
|
||||
System.arraycopy( refs, 0, all, 0, refs.length);
|
||||
Address[] all = new Address[refs.length + offcuts.length];
|
||||
System.arraycopy(refs, 0, all, 0, refs.length);
|
||||
System.arraycopy(offcuts, 0, all, refs.length, offcuts.length);
|
||||
|
||||
if (displayRefHeader) {
|
||||
|
@ -224,4 +221,51 @@ class ReferenceLineDispenser extends AbstractLineDispenser {
|
|||
buf.delete(0, buf.length());
|
||||
}
|
||||
}
|
||||
|
||||
public static Address[] getXRefList(CodeUnit cu) {
|
||||
Program prog = cu.getProgram();
|
||||
if (prog == null) {
|
||||
return new Address[0];
|
||||
}
|
||||
List<Address> xrefList = new ArrayList<>();
|
||||
//lookup the direct xrefs to the current code unit
|
||||
//
|
||||
ReferenceIterator iter = prog.getReferenceManager().getReferencesTo(cu.getMinAddress());
|
||||
while (iter.hasNext()) {
|
||||
Reference ref = iter.next();
|
||||
xrefList.add(ref.getFromAddress());
|
||||
}
|
||||
Address[] arr = new Address[xrefList.size()];
|
||||
xrefList.toArray(arr);
|
||||
Arrays.sort(arr);
|
||||
return arr;
|
||||
}
|
||||
|
||||
private static Address[] getOffcutXRefList(CodeUnit cu) {
|
||||
Program prog = cu.getProgram();
|
||||
if (prog == null) {
|
||||
return new Address[0];
|
||||
}
|
||||
List<Address> offcutList = new ArrayList<>();
|
||||
// Lookup the offcut xrefs...
|
||||
//
|
||||
if (cu.getLength() > 1) {
|
||||
ReferenceManager refMgr = prog.getReferenceManager();
|
||||
AddressSet set =
|
||||
new AddressSet(cu.getMinAddress().add(1), cu.getMaxAddress());
|
||||
AddressIterator iter = refMgr.getReferenceDestinationIterator(set, true);
|
||||
while (iter.hasNext()) {
|
||||
Address addr = iter.next();
|
||||
ReferenceIterator refIter = refMgr.getReferencesTo(addr);
|
||||
while (refIter.hasNext()) {
|
||||
Reference ref = refIter.next();
|
||||
offcutList.add(ref.getFromAddress());
|
||||
}
|
||||
}
|
||||
}
|
||||
Address[] arr = new Address[offcutList.size()];
|
||||
offcutList.toArray(arr);
|
||||
Arrays.sort(arr);
|
||||
return arr;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,18 +15,16 @@
|
|||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.proxy.EmptyProxy;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
||||
import docking.widgets.fieldpanel.internal.PaintContext;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.proxy.EmptyProxy;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
|
||||
/**
|
||||
* Field responsible for drawing +/- symbols when over an aggregate datatype that
|
||||
|
@ -177,7 +175,8 @@ public class IndentField implements ListingField {
|
|||
|
||||
@Override
|
||||
public void paint(JComponent c, Graphics g, PaintContext context,
|
||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc, int rowHeight) {
|
||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
|
||||
int rowHeight) {
|
||||
g.setColor(Color.LIGHT_GRAY);
|
||||
|
||||
// draw the vertical lines to the left of the data (these are shown when there are vertical
|
||||
|
@ -228,6 +227,11 @@ public class IndentField implements ListingField {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumDataRows() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumRows() {
|
||||
return 1;
|
||||
|
@ -307,7 +311,7 @@ public class IndentField implements ListingField {
|
|||
|
||||
@Override
|
||||
public RowColLocation textOffsetToScreenLocation(int textOffset) {
|
||||
return new RowColLocation(0, 0);
|
||||
return new DefaultRowColLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,8 @@ package ghidra.app.util.viewer.field;
|
|||
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JComponent;
|
||||
|
||||
|
@ -34,7 +36,7 @@ import ghidra.app.util.viewer.proxy.ProxyObj;
|
|||
*/
|
||||
public class ListingTextField implements ListingField, TextField {
|
||||
|
||||
private ProxyObj proxy;
|
||||
private ProxyObj<?> proxy;
|
||||
private FieldFactory factory;
|
||||
protected TextField field;
|
||||
|
||||
|
@ -47,9 +49,11 @@ public class ListingTextField implements ListingField, TextField {
|
|||
* @param startX the starting X position of the field
|
||||
* @param width the width of the field
|
||||
* @param provider the highlight provider.
|
||||
* @return the text field.
|
||||
*/
|
||||
public static ListingTextField createSingleLineTextField(FieldFactory factory, ProxyObj proxy,
|
||||
FieldElement fieldElement, int startX, int width, HighlightProvider provider) {
|
||||
public static ListingTextField createSingleLineTextField(FieldFactory factory,
|
||||
ProxyObj<?> proxy, FieldElement fieldElement, int startX, int width,
|
||||
HighlightProvider provider) {
|
||||
|
||||
HighlightFactory hlFactory =
|
||||
new FieldHighlightFactory(provider, factory.getClass(), proxy.getObject());
|
||||
|
@ -58,7 +62,7 @@ public class ListingTextField implements ListingField, TextField {
|
|||
}
|
||||
|
||||
public static ListingTextField createSingleLineTextFieldWithReverseClipping(
|
||||
AddressFieldFactory factory, ProxyObj proxy, FieldElement fieldElement, int startX,
|
||||
AddressFieldFactory factory, ProxyObj<?> proxy, FieldElement fieldElement, int startX,
|
||||
int width, HighlightProvider provider) {
|
||||
HighlightFactory hlFactory =
|
||||
new FieldHighlightFactory(provider, factory.getClass(), proxy.getObject());
|
||||
|
@ -67,7 +71,7 @@ public class ListingTextField implements ListingField, TextField {
|
|||
}
|
||||
|
||||
/**
|
||||
* Displays the given text, word-wrapping as needed to avoid clipping (up to the max number of
|
||||
* Displays the given text, word-wrapping as needed to avoid clipping (up to the max number of
|
||||
* lines.)
|
||||
* @param factory the field factory that generated this field
|
||||
* @param proxy the object used to populate this field
|
||||
|
@ -77,9 +81,10 @@ public class ListingTextField implements ListingField, TextField {
|
|||
* @param width the width of the field
|
||||
* @param maxLines the maxLines to display.
|
||||
* @param provider the highlight provider.
|
||||
* @return the text field.
|
||||
*/
|
||||
public static ListingTextField createWordWrappedTextField(FieldFactory factory, ProxyObj proxy,
|
||||
FieldElement fieldElement, int startX, int width, int maxLines,
|
||||
public static ListingTextField createWordWrappedTextField(FieldFactory factory,
|
||||
ProxyObj<?> proxy, FieldElement fieldElement, int startX, int width, int maxLines,
|
||||
HighlightProvider provider) {
|
||||
|
||||
HighlightFactory hlFactory =
|
||||
|
@ -100,14 +105,16 @@ public class ListingTextField implements ListingField, TextField {
|
|||
* @param width the width of the field
|
||||
* @param maxLines the maxLines to display.
|
||||
* @param provider the highlight provider.
|
||||
* @return the text field.
|
||||
*/
|
||||
public static ListingTextField createPackedTextField(FieldFactory factory, ProxyObj proxy,
|
||||
public static ListingTextField createPackedTextField(FieldFactory factory, ProxyObj<?> proxy,
|
||||
FieldElement[] textElements, int startX, int width, int maxLines,
|
||||
HighlightProvider provider) {
|
||||
|
||||
HighlightFactory hlFactory =
|
||||
new FieldHighlightFactory(provider, factory.getClass(), proxy.getObject());
|
||||
TextField field = new FlowLayoutTextField(textElements, startX, width, maxLines, hlFactory);
|
||||
List<FieldElement> list = Arrays.asList(textElements);
|
||||
TextField field = new FlowLayoutTextField(list, startX, width, maxLines, hlFactory);
|
||||
return new ListingTextField(factory, proxy, field);
|
||||
}
|
||||
|
||||
|
@ -118,22 +125,24 @@ public class ListingTextField implements ListingField, TextField {
|
|||
* @param textElements the array of elements for the field.
|
||||
* Each of these holds text, attributes and location information.
|
||||
* @param startX the starting X position of the field
|
||||
* @param width the widht of the field
|
||||
* @param width the width of the field
|
||||
* @param maxLines the maxLines to display.
|
||||
* @param provider the highlight provider
|
||||
* @return the text field.
|
||||
*/
|
||||
public static ListingTextField createMultilineTextField(FieldFactory factory, ProxyObj proxy,
|
||||
public static ListingTextField createMultilineTextField(FieldFactory factory, ProxyObj<?> proxy,
|
||||
FieldElement[] textElements, int startX, int width, int maxLines,
|
||||
HighlightProvider provider) {
|
||||
|
||||
HighlightFactory hlFactory =
|
||||
new FieldHighlightFactory(provider, factory.getClass(), proxy.getObject());
|
||||
List<FieldElement> list = Arrays.asList(textElements);
|
||||
TextField field =
|
||||
new VerticalLayoutTextField(textElements, startX, width, maxLines, hlFactory);
|
||||
new VerticalLayoutTextField(list, startX, width, maxLines, hlFactory);
|
||||
return new ListingTextField(factory, proxy, field);
|
||||
}
|
||||
|
||||
protected ListingTextField(FieldFactory factory, ProxyObj proxy, TextField field) {
|
||||
protected ListingTextField(FieldFactory factory, ProxyObj<?> proxy, TextField field) {
|
||||
this.factory = factory;
|
||||
this.proxy = proxy;
|
||||
this.field = field;
|
||||
|
@ -186,7 +195,8 @@ public class ListingTextField implements ListingField, TextField {
|
|||
|
||||
@Override
|
||||
public void paint(JComponent c, Graphics g, PaintContext context,
|
||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc, int rowHeight) {
|
||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
|
||||
int rowHeight) {
|
||||
field.paint(c, g, context, clip, map, cursorLoc, rowHeight);
|
||||
}
|
||||
|
||||
|
@ -195,6 +205,11 @@ public class ListingTextField implements ListingField, TextField {
|
|||
return field.contains(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumDataRows() {
|
||||
return field.getNumDataRows();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumRows() {
|
||||
return field.getNumRows();
|
||||
|
@ -281,7 +296,7 @@ public class ListingTextField implements ListingField, TextField {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ProxyObj getProxy() {
|
||||
public ProxyObj<?> getProxy() {
|
||||
if (proxy == null) {
|
||||
return EmptyProxy.EMPTY_PROXY;
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ public class NamespacePropertyEditor extends PropertyEditorSupport implements Cu
|
|||
|
||||
private static final String DISPLAY_LOCAL_NAMESPACE_LABEL = "Display Local Namespace";
|
||||
private static final String DISPLAY_NON_LOCAL_NAMESPACE_LABEL = "Display Non-local Namespace";
|
||||
private static final String LOCAL_NAMESPACE_PREFIX_LABEL = "Local namespace prefix";
|
||||
private static final String DISPLAY_LIBRARY_IN_NAMESPACE_LABEL = "Display library in namespace";
|
||||
private static final String LOCAL_NAMESPACE_PREFIX_LABEL = "Local Namespace Prefix";
|
||||
private static final String DISPLAY_LIBRARY_IN_NAMESPACE_LABEL = "Display library in Namespace";
|
||||
|
||||
private static final String[] NAMES =
|
||||
{ DISPLAY_LOCAL_NAMESPACE_LABEL, DISPLAY_NON_LOCAL_NAMESPACE_LABEL,
|
||||
|
@ -94,7 +94,7 @@ public class NamespacePropertyEditor extends PropertyEditorSupport implements Cu
|
|||
|
||||
showLocalCheckBox.addItemListener(e -> {
|
||||
boolean enabled = showLocalCheckBox.isSelected();
|
||||
// only enable the text field if we are showing namespaces AND we are
|
||||
// only enable the text field if we are showing namespaces AND we are
|
||||
// overriding the display value
|
||||
localPrefixField.setEnabled(enabled && useLocalPrefixCheckBox.isSelected());
|
||||
useLocalPrefixCheckBox.setEnabled(enabled);
|
||||
|
|
|
@ -22,8 +22,7 @@ import javax.swing.JComponent;
|
|||
|
||||
import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
|
||||
import docking.widgets.fieldpanel.internal.PaintContext;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.proxy.EmptyProxy;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
|
@ -153,15 +152,16 @@ public class OpenCloseField implements ListingField {
|
|||
|
||||
@Override
|
||||
public void paint(JComponent c, Graphics g, PaintContext context,
|
||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc, int rowHeight) {
|
||||
|
||||
Rectangle clip, FieldBackgroundColorManager map, RowColLocation cursorLoc,
|
||||
int rowHeight) {
|
||||
|
||||
// center in the heightAbove area (negative, since 0 is the baseline of text, which is at
|
||||
// the bottom of the heightAbove)
|
||||
// the bottom of the heightAbove)
|
||||
int toggleHandleStartY = -((heightAbove / 2) + (toggleHandleSize / 2));
|
||||
int toggleHandleStartX = startX + (indentLevel * fieldWidth) + insetSpace;
|
||||
|
||||
// TODO: If we're in printing mode, trying to render these open/close images
|
||||
// causes the JVM to bomb. We'd like to eventually figure out why but in
|
||||
// causes the JVM to bomb. We'd like to eventually figure out why but in
|
||||
// the meantime we can safely comment this out and still generate an acceptable
|
||||
// image.
|
||||
//
|
||||
|
@ -178,7 +178,7 @@ public class OpenCloseField implements ListingField {
|
|||
|
||||
g.setColor(Color.LIGHT_GRAY);
|
||||
|
||||
// draw the vertical lines to the left of the toggle handle (these are shown when
|
||||
// draw the vertical lines to the left of the toggle handle (these are shown when
|
||||
// there are vertical bars drawn for inset data)
|
||||
int fieldTopY = -heightAbove;
|
||||
int fieldBottomY = heightBelow;
|
||||
|
@ -205,7 +205,7 @@ public class OpenCloseField implements ListingField {
|
|||
|
||||
boolean lastAndClosed = isLast && !isOpen;
|
||||
if (!lastAndClosed) {
|
||||
// extended vertical line below toggle handle
|
||||
// extended vertical line below toggle handle
|
||||
int buttonBottomY = toggleHandleStartY + toggleHandleSize;
|
||||
g.drawLine(midpointX, buttonBottomY, midpointX, fieldBottomY);
|
||||
}
|
||||
|
@ -231,6 +231,11 @@ public class OpenCloseField implements ListingField {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumDataRows() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumRows() {
|
||||
return 1;
|
||||
|
@ -310,7 +315,7 @@ public class OpenCloseField implements ListingField {
|
|||
|
||||
@Override
|
||||
public RowColLocation textOffsetToScreenLocation(int textOffset) {
|
||||
return new RowColLocation(0, 0);
|
||||
return new DefaultRowColLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -332,7 +337,7 @@ public class OpenCloseField implements ListingField {
|
|||
|
||||
//==================================================================================================
|
||||
// Static Methods
|
||||
//==================================================================================================
|
||||
//==================================================================================================
|
||||
|
||||
static int getOpenCloseHandleSize() {
|
||||
return openImage.getIconWidth();
|
||||
|
|
|
@ -141,26 +141,23 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
}
|
||||
|
||||
CodeUnit cu = (CodeUnit) proxy.getObject();
|
||||
List<FieldElement> elementList = new ArrayList<>(10);
|
||||
List<FieldElement> elements = new ArrayList<>(10);
|
||||
boolean isClipped = false;
|
||||
String commentText = getCommentText(cu);
|
||||
if ((commentText == null) || (commentText.isEmpty())) {
|
||||
generateDefaultPlate(elementList, cu);
|
||||
generateDefaultPlate(elements, cu);
|
||||
}
|
||||
else {
|
||||
isClipped = generateFormattedPlateComment(elementList, cu);
|
||||
isClipped = generateFormattedPlateComment(elements, cu);
|
||||
}
|
||||
|
||||
addBlankLines(elementList, cu);
|
||||
addBlankLines(elements, cu);
|
||||
|
||||
if (elementList.size() == 0) {
|
||||
if (elements.size() == 0) {
|
||||
// no real or default comment
|
||||
return null;
|
||||
}
|
||||
|
||||
FieldElement[] fields = new FieldElement[elementList.size()];
|
||||
elementList.toArray(fields);
|
||||
|
||||
if (isNestedDataAtSameAddressAsParent(proxy)) {
|
||||
// This is data at the same address as the parent, which happens with the first
|
||||
// element in a structure. We do not want to the plate comment here, but only at the
|
||||
|
@ -169,7 +166,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
}
|
||||
|
||||
PlateFieldTextField textField =
|
||||
new PlateFieldTextField(fields, this, proxy, startX, width, commentText, isClipped);
|
||||
new PlateFieldTextField(elements, this, proxy, startX, width, commentText, isClipped);
|
||||
return new PlateListingTextField(proxy, textField);
|
||||
}
|
||||
|
||||
|
@ -706,7 +703,7 @@ public class PlateFieldFactory extends FieldFactory {
|
|||
private boolean isCommentClipped;
|
||||
private String commentText;
|
||||
|
||||
public PlateFieldTextField(FieldElement[] textElements, PlateFieldFactory factory,
|
||||
public PlateFieldTextField(List<FieldElement> textElements, PlateFieldFactory factory,
|
||||
ProxyObj<?> proxy, int startX, int width, String commentText,
|
||||
boolean isCommentClipped) {
|
||||
super(textElements, startX, width, Integer.MAX_VALUE,
|
||||
|
|
|
@ -23,7 +23,7 @@ import docking.widgets.fieldpanel.field.*;
|
|||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import ghidra.app.util.HighlightProvider;
|
||||
import ghidra.app.util.XReferenceUtil;
|
||||
import ghidra.app.util.XReferenceUtils;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
import ghidra.framework.options.Options;
|
||||
|
@ -36,8 +36,6 @@ import ghidra.program.util.VariableXRefFieldLocation;
|
|||
|
||||
/**
|
||||
* Variable Cross-reference Field Factory
|
||||
* <br>
|
||||
*
|
||||
*/
|
||||
public class VariableXRefFieldFactory extends XRefFieldFactory {
|
||||
|
||||
|
@ -76,9 +74,6 @@ public class VariableXRefFieldFactory extends XRefFieldFactory {
|
|||
initDisplayOptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.util.viewer.field.FieldFactory#getField(ProxyObj, int)
|
||||
*/
|
||||
@Override
|
||||
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
|
||||
Object obj = proxy.getObject();
|
||||
|
@ -89,7 +84,7 @@ public class VariableXRefFieldFactory extends XRefFieldFactory {
|
|||
Variable var = (Variable) obj;
|
||||
List<Reference> xrefs = new ArrayList<>();
|
||||
List<Reference> offcuts = new ArrayList<>();
|
||||
XReferenceUtil.getVariableRefs(var, xrefs, offcuts);
|
||||
XReferenceUtils.getVariableRefs(var, xrefs, offcuts, maxXRefs);
|
||||
|
||||
if (xrefs.size() + offcuts.size() == 0) {
|
||||
return null;
|
||||
|
@ -164,9 +159,6 @@ public class VariableXRefFieldFactory extends XRefFieldFactory {
|
|||
width, maxXRefs, hlProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.util.viewer.field.FieldFactory#getFieldLocation(ghidra.app.util.viewer.field.ListingField, BigInteger, int, ghidra.program.util.ProgramLocation)
|
||||
*/
|
||||
@Override
|
||||
public FieldLocation getFieldLocation(ListingField bf, BigInteger index, int fieldNum,
|
||||
ProgramLocation loc) {
|
||||
|
@ -188,9 +180,6 @@ public class VariableXRefFieldFactory extends XRefFieldFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.util.viewer.field.FieldFactory#getProgramLocation(int, int, ghidra.app.util.viewer.field.ListingField)
|
||||
*/
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(int row, int col, ListingField bf) {
|
||||
Object obj = bf.getProxy().getObject();
|
||||
|
@ -207,7 +196,7 @@ public class VariableXRefFieldFactory extends XRefFieldFactory {
|
|||
Variable var = (Variable) obj;
|
||||
List<Reference> xrefs = new ArrayList<>();
|
||||
List<Reference> offcuts = new ArrayList<>();
|
||||
XReferenceUtil.getVariableRefs(var, xrefs, offcuts);
|
||||
XReferenceUtils.getVariableRefs(var, xrefs, offcuts, maxXRefs);
|
||||
|
||||
Reference ref = null;
|
||||
if (index < xrefs.size()) {
|
||||
|
@ -225,9 +214,6 @@ public class VariableXRefFieldFactory extends XRefFieldFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ghidra.app.util.viewer.field.FieldFactory#acceptsType(int, java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public boolean acceptsType(int category, Class<?> proxyObjectClass) {
|
||||
if (!Variable.class.isAssignableFrom(proxyObjectClass)) {
|
||||
|
@ -239,7 +225,7 @@ public class VariableXRefFieldFactory extends XRefFieldFactory {
|
|||
|
||||
@Override
|
||||
public FieldFactory newInstance(FieldFormatModel formatModel, HighlightProvider provider,
|
||||
ToolOptions displayOptions, ToolOptions fieldOptions) {
|
||||
return new VariableXRefFieldFactory(formatModel, provider, displayOptions, fieldOptions);
|
||||
ToolOptions options, ToolOptions fieldOptions) {
|
||||
return new VariableXRefFieldFactory(formatModel, provider, options, fieldOptions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,16 +15,18 @@
|
|||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.util.XReferenceUtil;
|
||||
import ghidra.app.util.XReferenceUtils;
|
||||
import ghidra.app.util.query.TableService;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.listing.Variable;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceManager;
|
||||
import ghidra.program.util.*;
|
||||
|
||||
/**
|
||||
|
@ -79,7 +81,24 @@ public class VariableXRefFieldMouseHandler extends XRefFieldMouseHandler {
|
|||
VariableLocation variableLocation = (VariableLocation) location;
|
||||
Variable variable = variableLocation.getVariable();
|
||||
|
||||
Set<Reference> refs = XReferenceUtil.getVariableRefs(variable);
|
||||
XReferenceUtil.showAllXrefs(navigatable, serviceProvider, service, location, refs);
|
||||
Set<Reference> refs = getVariableRefs(variable);
|
||||
XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs);
|
||||
}
|
||||
|
||||
private Set<Reference> getVariableRefs(Variable var) {
|
||||
|
||||
Set<Reference> results = new HashSet<>();
|
||||
Address addr = var.getMinAddress();
|
||||
if (addr == null) {
|
||||
return results;
|
||||
}
|
||||
|
||||
Program program = var.getFunction().getProgram();
|
||||
ReferenceManager refMgr = program.getReferenceManager();
|
||||
Reference[] refs = refMgr.getReferencesTo(var);
|
||||
for (Reference vref : refs) {
|
||||
results.add(vref);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,18 +16,19 @@
|
|||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.FontMetrics;
|
||||
import java.beans.PropertyEditor;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
import ghidra.app.util.HighlightProvider;
|
||||
import ghidra.app.util.XReferenceUtil;
|
||||
import ghidra.app.util.XReferenceUtils;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.options.OptionsGui;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
|
@ -41,6 +42,7 @@ import ghidra.program.util.ProgramLocation;
|
|||
import ghidra.program.util.XRefFieldLocation;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.exception.AssertException;
|
||||
import util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Cross-reference Field Factory
|
||||
|
@ -58,17 +60,19 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
protected SORT_CHOICE sortChoice = SORT_CHOICE.Address;
|
||||
|
||||
private static final String GROUP_TITLE = "XREFs Field";
|
||||
private static final String DELIMITER_MSG = GROUP_TITLE + Options.DELIMITER + "Delimiter";
|
||||
private static final String MAX_XREFS_MSG =
|
||||
private static final String DELIMITER_KEY = GROUP_TITLE + Options.DELIMITER + "Delimiter";
|
||||
static final String MAX_XREFS_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Maximum Number of XREFs to Display";
|
||||
private static final String DISPLAY_BLOCK_NAME_MSG =
|
||||
private static final String DISPLAY_BLOCK_NAME_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Display Local Block";
|
||||
private static final String SORT_OPTION =
|
||||
GROUP_TITLE + Options.DELIMITER + "Sort References By";
|
||||
private static final String DISPLAY_REFERENCE_TYPE_MSG =
|
||||
private static final String SORT_OPTION_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Sort References by";
|
||||
private static final String DISPLAY_REFERENCE_TYPE_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Display Reference Type";
|
||||
private final static String NAMESPACE_OPTIONS =
|
||||
private final static String NAMESPACE_OPTIONS_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Display Namespace";
|
||||
static final String GROUP_BY_FUNCTION_KEY =
|
||||
GROUP_TITLE + Options.DELIMITER + "Group by Function";
|
||||
|
||||
private PropertyEditor namespaceOptionsEditor = new NamespacePropertyEditor();
|
||||
|
||||
|
@ -78,7 +82,7 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
protected Color otherColor;
|
||||
protected String delim = DELIMITER;
|
||||
protected boolean displayBlockName;
|
||||
|
||||
protected boolean groupByFunction;
|
||||
protected int maxXRefs = MAX_XREFS;
|
||||
protected boolean displayRefType = true;
|
||||
protected Comparator<Reference> typeComparator;
|
||||
|
@ -90,9 +94,6 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
private BrowserCodeUnitFormat codeUnitFormat;
|
||||
private ChangeListener codeUnitFormatListener = e -> XRefFieldFactory.this.model.update();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public XRefFieldFactory() {
|
||||
this(FIELD_NAME);
|
||||
}
|
||||
|
@ -128,15 +129,18 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
super(name, model, hlProvider, displayOptions, fieldOptions);
|
||||
|
||||
HelpLocation hl = new HelpLocation("CodeBrowserPlugin", "XREFs_Field");
|
||||
fieldOptions.registerOption(DELIMITER_MSG, DELIMITER, hl,
|
||||
fieldOptions.registerOption(DELIMITER_KEY, DELIMITER, hl,
|
||||
"Delimiter string used for separating multiple xrefs.");
|
||||
fieldOptions.registerOption(DISPLAY_BLOCK_NAME_MSG, false, hl,
|
||||
fieldOptions.registerOption(DISPLAY_BLOCK_NAME_KEY, false, hl,
|
||||
"Prepends xref addresses with the " +
|
||||
"name of the memory block containing the xref address.");
|
||||
fieldOptions.registerOption(MAX_XREFS_MSG, MAX_XREFS, hl,
|
||||
fieldOptions.registerOption(MAX_XREFS_KEY, MAX_XREFS, hl,
|
||||
"Sets the maximum number of xrefs to display.");
|
||||
fieldOptions.registerOption(DISPLAY_REFERENCE_TYPE_MSG, true, hl, "Appends xref type.");
|
||||
fieldOptions.registerOption(SORT_OPTION, SORT_CHOICE.Address, hl, "How to sort the xrefs");
|
||||
fieldOptions.registerOption(DISPLAY_REFERENCE_TYPE_KEY, true, hl, "Appends xref type.");
|
||||
fieldOptions.registerOption(SORT_OPTION_KEY, SORT_CHOICE.Address, hl,
|
||||
"How to sort the xrefs");
|
||||
fieldOptions.registerOption(GROUP_BY_FUNCTION_KEY, false, hl,
|
||||
"True signals to group all xrefs by the containing calling function.");
|
||||
|
||||
offcutColor = displayOptions.getColor(OptionsGui.XREF_OFFCUT.getColorOptionName(),
|
||||
OptionsGui.XREF_OFFCUT.getDefaultColor());
|
||||
|
@ -154,12 +158,13 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
return r1.getReferenceType().toString().compareTo(r2.getReferenceType().toString());
|
||||
};
|
||||
|
||||
delim = fieldOptions.getString(DELIMITER_MSG, DELIMITER);
|
||||
displayBlockName = fieldOptions.getBoolean(DISPLAY_BLOCK_NAME_MSG, false);
|
||||
delim = fieldOptions.getString(DELIMITER_KEY, DELIMITER);
|
||||
displayBlockName = fieldOptions.getBoolean(DISPLAY_BLOCK_NAME_KEY, false);
|
||||
|
||||
maxXRefs = fieldOptions.getInt(MAX_XREFS_MSG, MAX_XREFS);
|
||||
sortChoice = fieldOptions.getEnum(SORT_OPTION, SORT_CHOICE.Address);
|
||||
displayRefType = fieldOptions.getBoolean(DISPLAY_REFERENCE_TYPE_MSG, true);
|
||||
maxXRefs = fieldOptions.getInt(MAX_XREFS_KEY, MAX_XREFS);
|
||||
sortChoice = fieldOptions.getEnum(SORT_OPTION_KEY, SORT_CHOICE.Address);
|
||||
displayRefType = fieldOptions.getBoolean(DISPLAY_REFERENCE_TYPE_KEY, true);
|
||||
groupByFunction = fieldOptions.getBoolean(GROUP_BY_FUNCTION_KEY, false);
|
||||
|
||||
fieldOptions.getOptions(GROUP_TITLE).setOptionsHelpLocation(hl);
|
||||
|
||||
|
@ -172,15 +177,17 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
|
||||
private void setupNamespaceOptions(Options fieldOptions) {
|
||||
// we need to install a custom editor that allows us to edit a group of related options
|
||||
fieldOptions.registerOption(NAMESPACE_OPTIONS, OptionType.CUSTOM_TYPE,
|
||||
fieldOptions.registerOption(NAMESPACE_OPTIONS_KEY, OptionType.CUSTOM_TYPE,
|
||||
new NamespaceWrappedOption(), null, "Adjusts the XREFs Field namespace display",
|
||||
namespaceOptionsEditor);
|
||||
CustomOption customOption = fieldOptions.getCustomOption(NAMESPACE_OPTIONS, null);
|
||||
fieldOptions.getOptions(NAMESPACE_OPTIONS).setOptionsHelpLocation(
|
||||
new HelpLocation("CodeBrowserPlugin", "XREFs_Field"));
|
||||
CustomOption customOption = fieldOptions.getCustomOption(NAMESPACE_OPTIONS_KEY, null);
|
||||
fieldOptions.getOptions(NAMESPACE_OPTIONS_KEY)
|
||||
.setOptionsHelpLocation(
|
||||
new HelpLocation("CodeBrowserPlugin", "XREFs_Field"));
|
||||
if (!(customOption instanceof NamespaceWrappedOption)) {
|
||||
throw new AssertException(
|
||||
"Someone set an option for " + NAMESPACE_OPTIONS + " that is not the expected " +
|
||||
"Someone set an option for " + NAMESPACE_OPTIONS_KEY +
|
||||
" that is not the expected " +
|
||||
"ghidra.app.util.viewer.field.NamespaceWrappedOption type.");
|
||||
}
|
||||
|
||||
|
@ -222,33 +229,40 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
public void fieldOptionsChanged(Options options, String optionName, Object oldValue,
|
||||
Object newValue) {
|
||||
super.fieldOptionsChanged(options, optionName, oldValue, newValue);
|
||||
if (optionName.equals(DELIMITER_MSG)) {
|
||||
if (optionName.equals(DELIMITER_KEY)) {
|
||||
delim = (String) newValue;
|
||||
model.update();
|
||||
}
|
||||
else if (optionName.equals(DISPLAY_BLOCK_NAME_MSG)) {
|
||||
displayBlockName = ((Boolean) newValue).booleanValue();
|
||||
else if (optionName.equals(DISPLAY_BLOCK_NAME_KEY)) {
|
||||
displayBlockName = (Boolean) newValue;
|
||||
model.update();
|
||||
}
|
||||
else if (optionName.equals(DISPLAY_REFERENCE_TYPE_MSG)) {
|
||||
displayRefType = ((Boolean) newValue).booleanValue();
|
||||
else if (optionName.equals(DISPLAY_REFERENCE_TYPE_KEY)) {
|
||||
displayRefType = (Boolean) newValue;
|
||||
model.update();
|
||||
}
|
||||
else if (optionName.equals(MAX_XREFS_MSG)) {
|
||||
else if (optionName.equals(MAX_XREFS_KEY)) {
|
||||
setMaxSize(((Integer) newValue).intValue(), options);
|
||||
model.update();
|
||||
}
|
||||
else if (optionName.equals(SORT_OPTION)) {
|
||||
else if (optionName.equals(SORT_OPTION_KEY)) {
|
||||
sortChoice = (SORT_CHOICE) newValue;
|
||||
model.update();
|
||||
}
|
||||
else if (optionName.equals(NAMESPACE_OPTIONS)) {
|
||||
else if (optionName.equals(NAMESPACE_OPTIONS_KEY)) {
|
||||
setupNamespaceOptions(options);
|
||||
model.update();
|
||||
}
|
||||
else if (optionName.equals(GROUP_BY_FUNCTION_KEY)) {
|
||||
groupByFunction = (Boolean) newValue;
|
||||
model.update();
|
||||
}
|
||||
}
|
||||
|
||||
private void setMaxSize(int n, Options options) {
|
||||
if (n < 1) {
|
||||
n = 1;
|
||||
options.setInt(MAX_XREFS_MSG, 1);
|
||||
options.setInt(MAX_XREFS_KEY, 1);
|
||||
}
|
||||
maxXRefs = n;
|
||||
}
|
||||
|
@ -265,87 +279,362 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (obj == null || !(obj instanceof CodeUnit)) {
|
||||
if (!(obj instanceof CodeUnit)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CodeUnit cu = (CodeUnit) obj;
|
||||
Program pgm = cu.getProgram();
|
||||
|
||||
Reference[] xrefs = XReferenceUtil.getXReferences(cu, maxXRefs + 1);
|
||||
|
||||
int maxOffcuts = Math.max(0, maxXRefs - xrefs.length);
|
||||
Reference[] offcuts = XReferenceUtil.getOffcutXReferences(cu, maxOffcuts);
|
||||
List<Reference> xrefs = XReferenceUtils.getXReferences(cu, maxXRefs + 1);
|
||||
int maxOffcuts = Math.max(0, maxXRefs - xrefs.size());
|
||||
List<Reference> offcuts = XReferenceUtils.getOffcutXReferences(cu, maxOffcuts);
|
||||
if (sortChoice == SORT_CHOICE.Address) {
|
||||
Arrays.sort(xrefs);
|
||||
Arrays.sort(offcuts);
|
||||
xrefs.sort(null);
|
||||
offcuts.sort(null);
|
||||
}
|
||||
else {
|
||||
Arrays.sort(xrefs, typeComparator);
|
||||
Arrays.sort(offcuts, typeComparator);
|
||||
xrefs.sort(typeComparator);
|
||||
offcuts.sort(typeComparator);
|
||||
}
|
||||
int totalXrefs = xrefs.length + offcuts.length;
|
||||
|
||||
if (groupByFunction) {
|
||||
return getFieldByFunction(proxy, varWidth, xrefs, offcuts);
|
||||
}
|
||||
return getFieldByAddress(proxy, varWidth, xrefs, offcuts);
|
||||
}
|
||||
|
||||
/*
|
||||
Create a series of fields: 1 row per function and the xrefs it contains and 1 wrapping
|
||||
field for all xrefs not in any function. The wrapping field will go below the function
|
||||
based xrefs. It will look something like this:
|
||||
|
||||
foo1: 123, 223
|
||||
foo2: 323, 333
|
||||
423, 433, 567,
|
||||
899, [more]
|
||||
|
||||
The fields and elements created by this method have this structure:
|
||||
|
||||
XrefListingField
|
||||
|
||||
CompositeVerticalLayoutTextField
|
||||
|
||||
0+ ClippingTextField
|
||||
CompositeFieldElement
|
||||
XrefFieldEleent
|
||||
XrefAttributedString
|
||||
|
||||
0+ FlowLayoutTextField
|
||||
XrefFieldEleent
|
||||
XrefAttributedString
|
||||
|
||||
|
||||
*/
|
||||
private ListingField getFieldByFunction(ProxyObj<?> proxy, int varWidth,
|
||||
List<Reference> xrefs, List<Reference> offcuts) {
|
||||
|
||||
int totalXrefs = xrefs.size() + offcuts.size();
|
||||
if (totalXrefs == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean tooMany = totalXrefs > maxXRefs;
|
||||
|
||||
AttributedString delimiter = new AttributedString(delim, Color.BLACK, getMetrics());
|
||||
FieldElement[] elements = new FieldElement[tooMany ? maxXRefs + 1 : totalXrefs];
|
||||
Function currentFunction =
|
||||
pgm.getFunctionManager().getFunctionContaining(cu.getMinAddress());
|
||||
int count = 0;
|
||||
for (; count < xrefs.length && count < elements.length; count++) {
|
||||
String prefix = getPrefix(pgm, xrefs[count], currentFunction);
|
||||
String addressString = xrefs[count].getFromAddress().toString(prefix);
|
||||
AttributedString as = new AttributedString(addressString, color, getMetrics());
|
||||
if (displayRefType) {
|
||||
as = createRefTypeAttributedString(xrefs[count], as);
|
||||
}
|
||||
if (count < totalXrefs - 1) {
|
||||
as = new CompositeAttributedString(new AttributedString[] { as, delimiter });
|
||||
Object obj = proxy.getObject();
|
||||
CodeUnit cu = (CodeUnit) obj;
|
||||
Program program = cu.getProgram();
|
||||
FontMetrics metrics = getMetrics();
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
|
||||
//
|
||||
// Bin all xrefs by containing function, which may be null
|
||||
//
|
||||
List<Reference> noFunction = new ArrayList<>();
|
||||
TreeMap<Function, List<Reference>> xrefsByFunction = new TreeMap<>((f1, f2) -> {
|
||||
return f1.getEntryPoint().compareTo(f2.getEntryPoint());
|
||||
});
|
||||
for (Reference ref : CollectionUtils.asIterable(xrefs, offcuts)) {
|
||||
|
||||
Function function = functionManager.getFunctionContaining(ref.getFromAddress());
|
||||
if (function == null) {
|
||||
noFunction.add(ref);
|
||||
}
|
||||
else {
|
||||
// This added to prevent a situation where resizing field to a particular size,
|
||||
// resulted in layout of references to be strange
|
||||
char[] charSpaces = new char[delimiter.length()];
|
||||
Arrays.fill(charSpaces, ' ');
|
||||
AttributedString spaces =
|
||||
new AttributedString(new String(charSpaces), color, getMetrics());
|
||||
as = new CompositeAttributedString(new AttributedString[] { as, spaces });
|
||||
xrefsByFunction.computeIfAbsent(function, r -> new ArrayList<>()).add(ref);
|
||||
}
|
||||
elements[count] = new TextFieldElement(as, count, 0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < offcuts.length && count < elements.length; i++, count++) {
|
||||
String prefix = getPrefix(pgm, offcuts[i], currentFunction);
|
||||
String addressString = offcuts[i].getFromAddress().toString(prefix);
|
||||
AttributedString as = new AttributedString(addressString, offcutColor, getMetrics());
|
||||
if (displayRefType) {
|
||||
as = createRefTypeAttributedString(offcuts[i], as);
|
||||
}
|
||||
if (count < totalXrefs - 1) {
|
||||
as = new CompositeAttributedString(new AttributedString[] { as, delimiter });
|
||||
}
|
||||
else {
|
||||
// This added to prevent a situation where resizing field to a particular size,
|
||||
// resulted in layout of references to be strange
|
||||
char[] charSpaces = new char[delimiter.length()];
|
||||
Arrays.fill(charSpaces, ' ');
|
||||
AttributedString spaces =
|
||||
new AttributedString(new String(charSpaces), offcutColor, getMetrics());
|
||||
as = new CompositeAttributedString(new AttributedString[] { as, spaces });
|
||||
}
|
||||
elements[count] = new TextFieldElement(as, count, 0);
|
||||
}
|
||||
//
|
||||
// Create the function rows
|
||||
//
|
||||
Set<Reference> offcutSet = new HashSet<>(offcuts);
|
||||
Predicate<Reference> isOffcut = r -> offcutSet.contains(r);
|
||||
HighlightFactory hlFactory =
|
||||
new FieldHighlightFactory(hlProvider, getClass(), proxy.getObject());
|
||||
Function currentFunction = functionManager.getFunctionContaining(cu.getMinAddress());
|
||||
List<TextField> functionRows =
|
||||
createXrefRowsByFunction(program, currentFunction, xrefsByFunction, isOffcut, varWidth,
|
||||
hlFactory);
|
||||
|
||||
//
|
||||
// TODO maxXRefs makes sense when simply displaying xrefs. What does max mean when
|
||||
// binning xrefs by function. Currently, we use the max as the max row count, but
|
||||
// this may need to be changed and it may require a new tool option.
|
||||
//
|
||||
|
||||
int maxLines = maxXRefs;
|
||||
int availableLines = maxLines - functionRows.size();
|
||||
if (tooMany) {
|
||||
AttributedString as = new AttributedString(MORE_XREFS_STRING, color, getMetrics());
|
||||
elements[elements.length - 1] = new TextFieldElement(as, count - 1, 0);
|
||||
// save room for the "more" field at the end
|
||||
availableLines -= 1;
|
||||
}
|
||||
|
||||
return ListingTextField.createPackedTextField(this, proxy, elements, startX + varWidth,
|
||||
width, maxXRefs, hlProvider);
|
||||
//
|
||||
// Create the row for xrefs not in a function
|
||||
//
|
||||
|
||||
//
|
||||
// Note: the objects we build here want the 'data' row as a parameter, not the screen row.
|
||||
// Out screen rows are what we are building to display; a data row we are here
|
||||
// defining to be a single xref. This is a somewhat arbitrary decision.
|
||||
int dataRow = totalXrefs - noFunction.size();
|
||||
TextField noFunctionXrefsField =
|
||||
createWrappingXrefRow(program, dataRow, noFunction, currentFunction, isOffcut,
|
||||
availableLines, hlFactory);
|
||||
|
||||
List<TextField> allFields = new ArrayList<>();
|
||||
allFields.addAll(functionRows);
|
||||
if (noFunctionXrefsField != null) {
|
||||
allFields.add(noFunctionXrefsField);
|
||||
}
|
||||
|
||||
int newStartX = startX + varWidth;
|
||||
if (tooMany) {
|
||||
// add the [more] element
|
||||
int lastRow = allFields.size() - 1;
|
||||
AttributedString as = new AttributedString(MORE_XREFS_STRING, color, metrics);
|
||||
TextFieldElement moreElement = new TextFieldElement(as, lastRow, 0);
|
||||
ClippingTextField ctf = new ClippingTextField(newStartX, width, moreElement, hlFactory);
|
||||
allFields.add(ctf);
|
||||
}
|
||||
|
||||
CompositeVerticalLayoutTextField compositefield =
|
||||
new CompositeVerticalLayoutTextField(allFields, newStartX, width, maxXRefs, hlFactory);
|
||||
return new XrefListingField(this, proxy, compositefield);
|
||||
}
|
||||
|
||||
private List<TextField> createXrefRowsByFunction(Program program, Function currentFunction,
|
||||
TreeMap<Function, List<Reference>> xrefsByFunction, Predicate<Reference> isOffcut,
|
||||
int varWidth,
|
||||
HighlightFactory hlFactory) {
|
||||
|
||||
FontMetrics metrics = getMetrics();
|
||||
AttributedString delimiter = new AttributedString(delim, Color.BLACK, metrics);
|
||||
|
||||
int row = 0;
|
||||
List<FieldElement> elements = new ArrayList<>();
|
||||
Set<Entry<Function, List<Reference>>> entries = xrefsByFunction.entrySet();
|
||||
for (Entry<Function, List<Reference>> entry : entries) {
|
||||
|
||||
//
|
||||
// Example row: functionName: 1234(c), 1238(c)
|
||||
//
|
||||
|
||||
List<Reference> refs = entry.getValue();
|
||||
Function fromFunction = entry.getKey();
|
||||
String functionName = fromFunction.getName();
|
||||
int refCount = refs.size();
|
||||
String sizeText = ": ";
|
||||
if (refCount > 1) {
|
||||
sizeText = "[" + refs.size() + "]: ";
|
||||
}
|
||||
String text = functionName + sizeText;
|
||||
AttributedString nameString =
|
||||
new AttributedString(text, color, metrics);
|
||||
List<XrefFieldElement> rowElements = new ArrayList<>();
|
||||
Reference firstRef = refs.get(0);
|
||||
XrefAttributedString xrefString =
|
||||
new XrefAttributedString(firstRef, nameString);
|
||||
rowElements.add(new XrefFieldElement(xrefString, row, 0));
|
||||
|
||||
//
|
||||
// TODO how many xrefs to display per function?
|
||||
//
|
||||
int n = Math.min(10, refs.size());
|
||||
for (int i = 0; i < n; i++) {
|
||||
|
||||
boolean isLast = i == n - 1;
|
||||
Reference ref = refs.get(i);
|
||||
String prefix = getMergedPrefix(program, ref, currentFunction, fromFunction);
|
||||
XrefFieldElement element =
|
||||
createFunctionElement(program, prefix, ref, row, isLast ? null : delimiter,
|
||||
isOffcut.test(ref));
|
||||
rowElements.add(element);
|
||||
}
|
||||
|
||||
elements.add(new CompositeFieldElement(rowElements));
|
||||
|
||||
row++;
|
||||
}
|
||||
|
||||
int newStartX = startX + varWidth;
|
||||
List<TextField> textFields = new ArrayList<>();
|
||||
for (FieldElement element : elements) {
|
||||
textFields.add(new ClippingTextField(newStartX, width, element, hlFactory));
|
||||
}
|
||||
|
||||
return textFields;
|
||||
}
|
||||
|
||||
private TextField createWrappingXrefRow(Program program, int startRow, List<Reference> xrefs,
|
||||
Function currentFunction, Predicate<Reference> isOffcut, int availableLines,
|
||||
HighlightFactory hlFactory) {
|
||||
|
||||
FontMetrics metrics = getMetrics();
|
||||
AttributedString delimiter = new AttributedString(delim, Color.BLACK, metrics);
|
||||
int row = startRow;
|
||||
List<XrefFieldElement> elements = new ArrayList<>();
|
||||
for (Reference ref : xrefs) {
|
||||
|
||||
String prefix = getPrefix(program, ref, currentFunction, null);
|
||||
XrefFieldElement element =
|
||||
createReferenceElement(program, prefix, ref, row, delimiter, isOffcut.test(ref));
|
||||
elements.add(element);
|
||||
row++;
|
||||
}
|
||||
|
||||
// add all elements to a field that will wrap as needed
|
||||
if (!elements.isEmpty()) {
|
||||
List<FieldElement> fieldElements = toFieldElements(elements, false);
|
||||
return new FlowLayoutTextField(fieldElements, startX, width, availableLines, hlFactory);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
Create a series of fields: 1 row per function and the xrefs it contains and 1 wrapping
|
||||
field for all xrefs not in any function. The wrapping field will go below the function
|
||||
based xrefs. It will look something like this:
|
||||
|
||||
foo1:423,
|
||||
foo1:433,
|
||||
foo2:567,
|
||||
899, [more]
|
||||
|
||||
The fields and elements created by this method have this structure:
|
||||
|
||||
XrefListingField
|
||||
1+ FlowLayoutTextField
|
||||
XrefFieldEleent
|
||||
XrefAttributedString
|
||||
|
||||
|
||||
*/
|
||||
private ListingField getFieldByAddress(ProxyObj<?> proxy, int varWidth, List<Reference> xrefs,
|
||||
List<Reference> offcuts) {
|
||||
|
||||
int totalXrefs = xrefs.size() + offcuts.size();
|
||||
if (totalXrefs == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object obj = proxy.getObject();
|
||||
CodeUnit cu = (CodeUnit) obj;
|
||||
Program program = cu.getProgram();
|
||||
FontMetrics metrics = getMetrics();
|
||||
AttributedString delimiter = new AttributedString(delim, Color.BLACK, metrics);
|
||||
|
||||
Set<Reference> offcutSet = new HashSet<>(offcuts);
|
||||
Predicate<Reference> isOffcut = r -> offcutSet.contains(r);
|
||||
|
||||
boolean tooMany = totalXrefs > maxXRefs;
|
||||
List<XrefFieldElement> elements = new ArrayList<>();
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
Function currentFunction = functionManager.getFunctionContaining(cu.getMinAddress());
|
||||
int n = tooMany ? maxXRefs + 1 : totalXrefs;
|
||||
int count = 0;
|
||||
for (; count < xrefs.size() && count < n; count++) {
|
||||
Reference ref = xrefs.get(count);
|
||||
String prefix = getPrefix(program, ref, currentFunction);
|
||||
elements.add(
|
||||
createReferenceElement(program, prefix, ref, count, delimiter, isOffcut.test(ref)));
|
||||
}
|
||||
|
||||
for (int i = 0; i < offcuts.size() && count < n; i++, count++) {
|
||||
Reference ref = offcuts.get(i);
|
||||
String prefix = getPrefix(program, ref, currentFunction);
|
||||
elements.add(
|
||||
createReferenceElement(program, prefix, ref, count, delimiter, isOffcut.test(ref)));
|
||||
}
|
||||
|
||||
if (!tooMany) {
|
||||
XrefFieldElement lastElement = elements.get(elements.size() - 1);
|
||||
lastElement.hideDelimiter();
|
||||
}
|
||||
|
||||
List<FieldElement> fieldElements = toFieldElements(elements, tooMany);
|
||||
return createPackedTextField(proxy, varWidth, fieldElements);
|
||||
}
|
||||
|
||||
// note: this method was inspired by ListingTextField.createPackedTextField()
|
||||
private XrefListingField createPackedTextField(ProxyObj<?> proxy, int varWidth,
|
||||
List<FieldElement> list) {
|
||||
|
||||
// assumption: the given array has been limited to the maxXref size already
|
||||
int n = list.size();
|
||||
HighlightFactory hlFactory =
|
||||
new FieldHighlightFactory(hlProvider, getClass(), proxy.getObject());
|
||||
TextField field =
|
||||
new FlowLayoutTextField(list, startX + varWidth, width, n, hlFactory);
|
||||
return new XrefListingField(this, proxy, field);
|
||||
}
|
||||
|
||||
private List<FieldElement> toFieldElements(List<XrefFieldElement> list, boolean showEllipses) {
|
||||
|
||||
List<FieldElement> fieldElements = new ArrayList<>(list);
|
||||
if (showEllipses) {
|
||||
// add the 'more' string
|
||||
int lastRow = list.size() - 1;
|
||||
AttributedString as = new AttributedString(MORE_XREFS_STRING, color, getMetrics());
|
||||
fieldElements.add(new TextFieldElement(as, lastRow, 0));
|
||||
}
|
||||
return fieldElements;
|
||||
}
|
||||
|
||||
private XrefFieldElement createFunctionElement(Program program, String prefix, Reference ref,
|
||||
int row, AttributedString delimiter, boolean isOffcut) {
|
||||
|
||||
FontMetrics metrics = getMetrics();
|
||||
String addressString = ref.getFromAddress().toString(prefix);
|
||||
Color refColor = isOffcut ? offcutColor : color;
|
||||
AttributedString addressPart = new AttributedString(addressString, refColor, metrics);
|
||||
if (displayRefType) {
|
||||
addressPart = createRefTypeAttributedString(ref, addressPart);
|
||||
}
|
||||
|
||||
XrefAttributedString xrefString =
|
||||
new XrefAttributedString(ref, addressPart, delimiter);
|
||||
if (delimiter == null) {
|
||||
xrefString.hideDelimiter();
|
||||
}
|
||||
|
||||
return new XrefFieldElement(xrefString, row, 0);
|
||||
}
|
||||
|
||||
private XrefFieldElement createReferenceElement(Program program, String prefix, Reference ref,
|
||||
int row, AttributedString delimiter, boolean isOffcut) {
|
||||
|
||||
FontMetrics metrics = getMetrics();
|
||||
String addressString = ref.getFromAddress().toString(prefix);
|
||||
Color refColor = isOffcut ? offcutColor : color;
|
||||
AttributedString as = new AttributedString(addressString, refColor, metrics);
|
||||
if (displayRefType) {
|
||||
as = createRefTypeAttributedString(ref, as);
|
||||
}
|
||||
|
||||
XrefAttributedString xrefString =
|
||||
new XrefAttributedString(ref, as, delimiter);
|
||||
return new XrefFieldElement(xrefString, row, 0);
|
||||
}
|
||||
|
||||
protected AttributedString createRefTypeAttributedString(Reference reference,
|
||||
|
@ -378,8 +667,16 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
}
|
||||
|
||||
protected String getPrefix(Program program, Reference reference, Function currentFunction) {
|
||||
String prefix = "";
|
||||
|
||||
Address fromAddress = reference.getFromAddress();
|
||||
Function fromFunction = program.getListing().getFunctionContaining(fromAddress);
|
||||
return getPrefix(program, reference, currentFunction, fromFunction);
|
||||
}
|
||||
|
||||
private String getMergedPrefix(Program program, Reference reference, Function currentFunction,
|
||||
Function fromFunction) {
|
||||
|
||||
String prefix = "";
|
||||
Address fromAddress = reference.getFromAddress();
|
||||
if (displayBlockName) {
|
||||
prefix = getBlockName(program, fromAddress) + ":";
|
||||
|
@ -389,15 +686,34 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
return prefix; // no namespaces being shown
|
||||
}
|
||||
|
||||
Function refFunction = program.getListing().getFunctionContaining(fromAddress);
|
||||
if (refFunction == null) {
|
||||
boolean isLocal = Objects.equals(currentFunction, fromFunction);
|
||||
if (isLocal && useLocalPrefixOverride) {
|
||||
return prefix + localPrefixText;
|
||||
}
|
||||
return prefix;
|
||||
}
|
||||
|
||||
private String getPrefix(Program program, Reference reference, Function currentFunction,
|
||||
Function fromFunction) {
|
||||
|
||||
String prefix = "";
|
||||
Address fromAddress = reference.getFromAddress();
|
||||
if (displayBlockName) {
|
||||
prefix = getBlockName(program, fromAddress) + ":";
|
||||
}
|
||||
|
||||
if (!displayLocalNamespace && !displayNonLocalNamespace) {
|
||||
return prefix; // no namespaces being shown
|
||||
}
|
||||
|
||||
if (fromFunction == null) {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
boolean isLocal = refFunction.equals(currentFunction);
|
||||
boolean isLocal = fromFunction.equals(currentFunction);
|
||||
if (!isLocal) {
|
||||
if (displayNonLocalNamespace) {
|
||||
return prefix + refFunction.getName() + ":";
|
||||
return prefix + fromFunction.getName() + ":";
|
||||
}
|
||||
return prefix; // this means different function, but not displaying other namespaces
|
||||
}
|
||||
|
@ -412,7 +728,6 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
return prefix + localPrefixText;
|
||||
}
|
||||
return prefix + currentFunction.getName() + ":";
|
||||
|
||||
}
|
||||
|
||||
private String getRefTypeDisplayString(Reference reference) {
|
||||
|
@ -460,60 +775,45 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
|
||||
protected FieldLocation createFieldLocation(int xrefPos, int xrefIndex, ListingTextField field,
|
||||
BigInteger index, int fieldNum) {
|
||||
|
||||
RowColLocation loc = field.dataToScreenLocation(xrefIndex, xrefPos);
|
||||
|
||||
return new FieldLocation(index, fieldNum, loc.row(), loc.col());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProgramLocation getProgramLocation(int row, int col, ListingField bf) {
|
||||
Object obj = bf.getProxy().getObject();
|
||||
public ProgramLocation getProgramLocation(int row, int col, ListingField listingField) {
|
||||
Object obj = listingField.getProxy().getObject();
|
||||
if (obj == null || !(obj instanceof CodeUnit)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CodeUnit cu = (CodeUnit) obj;
|
||||
ListingTextField field = (ListingTextField) getField(bf.getProxy(), 0);
|
||||
if (field == null) {
|
||||
if (!(listingField instanceof XrefListingField)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CodeUnit cu = (CodeUnit) obj;
|
||||
|
||||
int[] cpath = null;
|
||||
if (cu instanceof Data) {
|
||||
cpath = ((Data) cu).getComponentPath();
|
||||
}
|
||||
|
||||
XrefListingField field = (XrefListingField) listingField;
|
||||
FieldElement element = field.getFieldElement(row, col);
|
||||
RowColLocation loc = field.screenToDataLocation(row, col);
|
||||
int index = loc.row();
|
||||
Reference[] xrefs = XReferenceUtil.getXReferences(cu, maxXRefs + 1);
|
||||
if (sortChoice == SORT_CHOICE.Address) {
|
||||
Arrays.sort(xrefs);
|
||||
}
|
||||
else {
|
||||
Arrays.sort(xrefs, typeComparator);
|
||||
}
|
||||
|
||||
Address refAddr = null;
|
||||
if (index < xrefs.length) {
|
||||
refAddr = xrefs[index].getFromAddress();
|
||||
}
|
||||
else {
|
||||
Reference[] offcuts = XReferenceUtil.getOffcutXReferences(cu, maxXRefs);
|
||||
if (sortChoice == SORT_CHOICE.Address) {
|
||||
Arrays.sort(offcuts);
|
||||
}
|
||||
else {
|
||||
Arrays.sort(offcuts, typeComparator);
|
||||
}
|
||||
if (index < xrefs.length + offcuts.length) {
|
||||
refAddr = offcuts[index - xrefs.length].getFromAddress();
|
||||
}
|
||||
}
|
||||
|
||||
if (refAddr != null) {
|
||||
int[] cpath = null;
|
||||
if (cu instanceof Data) {
|
||||
cpath = ((Data) cu).getComponentPath();
|
||||
}
|
||||
return new XRefFieldLocation(cu.getProgram(), cu.getMinAddress(), cpath, refAddr, index,
|
||||
if (element instanceof XrefFieldElement) {
|
||||
XrefFieldElement xrefElement = (XrefFieldElement) element;
|
||||
Reference xref = xrefElement.getXref();
|
||||
Address refAddr = xref.getFromAddress();
|
||||
return new XRefFieldLocation(cu.getProgram(), cu.getMinAddress(), cpath, refAddr, row,
|
||||
loc.col());
|
||||
}
|
||||
|
||||
String text = element.getText();
|
||||
if (MORE_XREFS_STRING.equals(text)) {
|
||||
return new XRefFieldLocation(cu.getProgram(), cu.getMinAddress(), cpath, null, row,
|
||||
loc.col());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -561,4 +861,77 @@ public class XRefFieldFactory extends FieldFactory {
|
|||
ToolOptions toolOptions, ToolOptions fieldOptions) {
|
||||
return new XRefFieldFactory(formatModel, provider, toolOptions, fieldOptions);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class XrefAttributedString extends CompositeAttributedString {
|
||||
|
||||
private AttributedString content;
|
||||
private AttributedString delimiter;
|
||||
private Reference xref;
|
||||
|
||||
public XrefAttributedString(Reference xref, AttributedString content) {
|
||||
super(content);
|
||||
this.content = content;
|
||||
this.xref = xref;
|
||||
}
|
||||
|
||||
public XrefAttributedString(Reference xref, AttributedString content,
|
||||
AttributedString delimiter) {
|
||||
super(content, delimiter);
|
||||
this.content = content;
|
||||
this.delimiter = delimiter;
|
||||
this.xref = xref;
|
||||
}
|
||||
|
||||
void hideDelimiter() {
|
||||
AttributedString source = delimiter;
|
||||
if (source == null) {
|
||||
source = content;
|
||||
}
|
||||
|
||||
int length = delimiter == null ? 1 : delimiter.length();
|
||||
|
||||
// Use spaces instead of an empty string; this added to prevent a situation where
|
||||
// resizing field to a particular size, resulted in layout of references to be strange
|
||||
char[] charSpaces = new char[length];
|
||||
Arrays.fill(charSpaces, ' ');
|
||||
AttributedString spaces =
|
||||
new AttributedString(new String(charSpaces), source.getColor(0),
|
||||
source.getFontMetrics(0));
|
||||
attributedStrings[attributedStrings.length - 1] = spaces;
|
||||
}
|
||||
|
||||
Reference getXref() {
|
||||
return xref;
|
||||
}
|
||||
}
|
||||
|
||||
private class XrefFieldElement extends TextFieldElement {
|
||||
|
||||
private XrefAttributedString xrefString;
|
||||
|
||||
public XrefFieldElement(XrefAttributedString xrefString, int row, int column) {
|
||||
super(xrefString, row, column);
|
||||
this.xrefString = xrefString;
|
||||
}
|
||||
|
||||
void hideDelimiter() {
|
||||
xrefString.hideDelimiter();
|
||||
}
|
||||
|
||||
Reference getXref() {
|
||||
return xrefString.getXref();
|
||||
}
|
||||
}
|
||||
|
||||
private class XrefListingField extends ListingTextField {
|
||||
|
||||
XrefListingField(XRefFieldFactory factory, ProxyObj<?> proxy, TextField field) {
|
||||
super(factory, proxy, field);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import docking.widgets.fieldpanel.field.FieldElement;
|
|||
import docking.widgets.fieldpanel.field.TextField;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.services.GoToService;
|
||||
import ghidra.app.util.XReferenceUtil;
|
||||
import ghidra.app.util.XReferenceUtils;
|
||||
import ghidra.app.util.query.TableService;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
@ -51,7 +51,7 @@ public class XRefFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
return false;
|
||||
}
|
||||
|
||||
// If I double-click on the XRef Header, show references to this place, also works on
|
||||
// If I double-click on the XRef Header, show references to this place, also works on
|
||||
// 'more' field. This is much nicer if you have multiple references to navigate.
|
||||
if (isXREFHeaderLocation(location)) {
|
||||
showXRefDialog(sourceNavigatable, location, serviceProvider);
|
||||
|
@ -105,8 +105,8 @@ public class XRefFieldMouseHandler implements FieldMouseHandlerExtension {
|
|||
return;
|
||||
}
|
||||
|
||||
Set<Reference> refs = XReferenceUtil.getAllXrefs(location);
|
||||
XReferenceUtil.showAllXrefs(navigatable, serviceProvider, service, location, refs);
|
||||
Set<Reference> refs = XReferenceUtils.getAllXrefs(location);
|
||||
XReferenceUtils.showXrefs(navigatable, serviceProvider, service, location, refs);
|
||||
}
|
||||
|
||||
protected ProgramLocation getReferredToLocation(Navigatable sourceNavigatable,
|
||||
|
|
|
@ -16,17 +16,19 @@
|
|||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.List;
|
||||
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.app.util.HighlightProvider;
|
||||
import ghidra.app.util.XReferenceUtil;
|
||||
import ghidra.app.util.XReferenceUtils;
|
||||
import ghidra.app.util.viewer.format.FieldFormatModel;
|
||||
import ghidra.app.util.viewer.proxy.ProxyObj;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
import ghidra.program.util.XRefHeaderFieldLocation;
|
||||
|
||||
|
@ -126,14 +128,20 @@ public class XRefHeaderFieldFactory extends XRefFieldFactory {
|
|||
return null;
|
||||
}
|
||||
Program prog = cu.getProgram();
|
||||
int xrefCnt = prog.getReferenceManager().getReferenceCountTo(cu.getMinAddress());
|
||||
int offcutCnt = XReferenceUtil.getOffcutXRefCount(cu);
|
||||
int xrefCount = prog.getReferenceManager().getReferenceCountTo(cu.getMinAddress());
|
||||
List<Reference> offcuts = XReferenceUtils.getOffcutXReferences(cu, maxXRefs);
|
||||
int offcutCount = offcuts.size();
|
||||
|
||||
if (offcutCnt > 0) {
|
||||
return "XREF[" + xrefCnt + "," + offcutCnt + "]: ";
|
||||
if (offcutCount > 0) {
|
||||
String modifier = "";
|
||||
if (offcutCount == maxXRefs) {
|
||||
modifier = "+";
|
||||
}
|
||||
return "XREF[" + xrefCount + "," + offcutCount + modifier + "]: ";
|
||||
}
|
||||
if (xrefCnt > 0) {
|
||||
return "XREF[" + xrefCnt + "]: ";
|
||||
|
||||
if (xrefCount > 0) {
|
||||
return "XREF[" + xrefCount + "]: ";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -41,10 +41,6 @@ public class EolCommentFieldFactoryTest extends AbstractGhidraHeadedIntegrationT
|
|||
private Options fieldOptions;
|
||||
private Program program;
|
||||
|
||||
public EolCommentFieldFactoryTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
|
@ -87,7 +83,7 @@ public class EolCommentFieldFactoryTest extends AbstractGhidraHeadedIntegrationT
|
|||
//==================================================================================================
|
||||
|
||||
private ProgramDB buildProgram() throws Exception {
|
||||
ProgramBuilder builder = new ProgramBuilder("notepad", ProgramBuilder._TOY, this);
|
||||
ProgramBuilder builder = new ProgramBuilder("sample", ProgramBuilder._TOY, this);
|
||||
builder.createMemory(".text", "0x1001000", 0x6600);
|
||||
builder.createEmptyFunction(null, "0x1002000", 20, null);
|
||||
|
||||
|
|
|
@ -0,0 +1,623 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.junit.*;
|
||||
|
||||
import docking.widgets.fieldpanel.field.FieldElement;
|
||||
import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
||||
import ghidra.app.plugin.core.table.TableComponentProvider;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.database.ProgramDB;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.data.DataType;
|
||||
import ghidra.program.model.listing.*;
|
||||
import ghidra.program.model.symbol.Reference;
|
||||
import ghidra.program.model.symbol.ReferenceManager;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.test.TestEnv;
|
||||
import ghidra.util.table.GhidraProgramTableModel;
|
||||
|
||||
public class XRefFieldFactoryTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private TestEnv env;
|
||||
private ProgramBuilder builder;
|
||||
private Program program;
|
||||
|
||||
private CodeBrowserPlugin cb;
|
||||
private Options fieldOptions;
|
||||
|
||||
private int callerCount;
|
||||
private int functionWithNoCalls;
|
||||
private int functionCalledByOneOtherFunction;
|
||||
private int functionCalledByMultipleFunctions;
|
||||
private int functionWithAllTypesOfCalls;
|
||||
private int nonFunctionOffset;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
program = buildProgram();
|
||||
|
||||
env = new TestEnv();
|
||||
env.launchDefaultTool(program);
|
||||
cb = env.getPlugin(CodeBrowserPlugin.class);
|
||||
fieldOptions = cb.getFormatManager().getFieldOptions();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
env.dispose();
|
||||
}
|
||||
|
||||
private ProgramDB buildProgram() throws Exception {
|
||||
builder = new ProgramBuilder("test", ProgramBuilder._TOY, this);
|
||||
builder.createMemory(".text", "0x0", 0x100000);
|
||||
|
||||
/*
|
||||
Create a few functions that call other functions
|
||||
|
||||
Create some function calls outside of functions
|
||||
*/
|
||||
|
||||
int callerOffset = 0x20000;
|
||||
Function caller1 = caller(callerOffset);
|
||||
Function caller2 = caller(callerOffset + 1000);
|
||||
Function caller3 = caller(callerOffset + 2000);
|
||||
Function caller4 = caller(callerOffset + 3000);
|
||||
Function caller5 = caller(callerOffset + 4000);
|
||||
Function caller6 = caller(callerOffset + 5000);
|
||||
|
||||
// function with no calls
|
||||
functionWithNoCalls = 0x0000;
|
||||
function(functionWithNoCalls);
|
||||
|
||||
// function called by one function once
|
||||
functionCalledByOneOtherFunction = 0x1000;
|
||||
function(functionCalledByOneOtherFunction);
|
||||
createCallerReference(functionCalledByOneOtherFunction, caller1, 1);
|
||||
|
||||
// function called by multiple functions multiple times each
|
||||
functionCalledByMultipleFunctions = 0x2000;
|
||||
function(functionCalledByMultipleFunctions);
|
||||
createCallerReference(functionCalledByMultipleFunctions, caller2, 3);
|
||||
createCallerReference(functionCalledByMultipleFunctions, caller3, 5);
|
||||
|
||||
// function called my multiple functions multiple times each and calls from not in functions
|
||||
functionWithAllTypesOfCalls = 0x3000;
|
||||
function(functionWithAllTypesOfCalls);
|
||||
createCallerReference(functionWithAllTypesOfCalls, caller4, 2);
|
||||
createCallerReference(functionWithAllTypesOfCalls, caller5, 5);
|
||||
createCallerReference(functionWithAllTypesOfCalls, caller6, 3);
|
||||
|
||||
nonFunctionOffset = 0x30000;
|
||||
createNonFunctionReferences(functionWithAllTypesOfCalls, nonFunctionOffset, 10);
|
||||
|
||||
return builder.getProgram();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_DefaultView() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4:00020bbc(c),
|
||||
callerFunction4:00020bc0(c),
|
||||
callerFunction5:00020fa4(c),
|
||||
callerFunction5:00020fa8(c),
|
||||
callerFunction5:00020fac(c),
|
||||
callerFunction5:00020fb0(c),
|
||||
callerFunction5:00020fb4(c),
|
||||
callerFunction6:0002138c(c),
|
||||
callerFunction6:00021390(c),
|
||||
callerFunction6:00021394(c),
|
||||
00030004(c), 00030008(c),
|
||||
0003000c(c), 00030010(c),
|
||||
00030014(c), 00030018(c),
|
||||
0003001c(c), 00030020(c),
|
||||
00030024(c), 00030028(c)
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(false);
|
||||
|
||||
goToXrefField(functionWithAllTypesOfCalls);
|
||||
|
||||
ListingTextField tf = (ListingTextField) cb.getCurrentField();
|
||||
|
||||
assertContainsRow(tf, "callerFunction4:00020bbc(c)");
|
||||
assertContainsRow(tf, "00030004(c), 00030008(c),");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_GroupByFunctionView_CallsFromInFunctionsOnly() {
|
||||
|
||||
/*
|
||||
XREF[8]: callerFunction2[3]: 000203ec(c),
|
||||
callerFunction3[5]: 000207d4(c),
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(true);
|
||||
|
||||
goToXrefField(functionCalledByMultipleFunctions);
|
||||
|
||||
ListingTextField tf = (ListingTextField) cb.getCurrentField();
|
||||
assertEquals(2, tf.getNumRows());
|
||||
assertContainsRow(tf, "callerFunction2[3]: 000203ec(c)");
|
||||
assertContainsRow(tf, "allerFunction3[5]: 000207d4(c)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_GroupByFunctionView_CallsFromInFunctionsAndNotInFunctions() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4[2]: 00020bbc(c),
|
||||
callerFunction5[5]: 00020fa4(c),
|
||||
callerFunction6[3]: 0002138c(c),
|
||||
00030004(c), 00030008(c),
|
||||
0003000c(c), 00030010(c),
|
||||
00030014(c), 00030018(c),
|
||||
0003001c(c), 00030020(c),
|
||||
00030024(c), 00030028(c)
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(true);
|
||||
|
||||
goToXrefField(functionWithAllTypesOfCalls);
|
||||
|
||||
ListingTextField tf = (ListingTextField) cb.getCurrentField();
|
||||
|
||||
assertContainsRow(tf, "callerFunction4[2]: 00020bbc(c)");
|
||||
assertContainsRow(tf, "callerFunction5[5]: 00020fa4(c)");
|
||||
assertContainsRow(tf, "00030004(c), 00030008(c),");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_DefaultView_NoXrefs() {
|
||||
|
||||
setGroupByFunctionOption(false);
|
||||
|
||||
assertFalse(hasXrefField(functionWithNoCalls));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_DefaultView_DoubleClickFunctionName() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4:00020bbc(c),
|
||||
callerFunction4:00020bc0(c),
|
||||
callerFunction5:00020fa4(c),
|
||||
callerFunction5:00020fa8(c),
|
||||
callerFunction5:00020fac(c),
|
||||
callerFunction5:00020fb0(c),
|
||||
callerFunction5:00020fb4(c),
|
||||
callerFunction6:0002138c(c),
|
||||
callerFunction6:00021390(c),
|
||||
callerFunction6:00021394(c),
|
||||
00030004(c), 00030008(c),
|
||||
0003000c(c), 00030010(c),
|
||||
00030014(c), 00030018(c),
|
||||
0003001c(c), 00030020(c),
|
||||
00030024(c), 00030028(c)
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(false);
|
||||
|
||||
String callerFunction = "callerFunction4";
|
||||
goToXrefField(functionWithAllTypesOfCalls, callerFunction);
|
||||
|
||||
doubleClick();
|
||||
|
||||
assertInFunction(callerFunction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_GroupByFunctionView_DoubleClickFunctionName() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4[2]: 00020bbc(c),
|
||||
callerFunction5[5]: 00020fa4(c),
|
||||
callerFunction6[3]: 0002138c(c),
|
||||
00030004(c), 00030008(c),
|
||||
0003000c(c), 00030010(c),
|
||||
00030014(c), 00030018(c),
|
||||
0003001c(c), 00030020(c),
|
||||
00030024(c), 00030028(c)
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(true);
|
||||
|
||||
goToXrefField(functionWithAllTypesOfCalls);
|
||||
|
||||
String callerFunction = "callerFunction6";
|
||||
goToXrefField(functionWithAllTypesOfCalls, callerFunction);
|
||||
|
||||
doubleClick();
|
||||
|
||||
assertInFunction(callerFunction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_GroupByFunctionView_DoubleClickAddressInFunction() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4[2]: 00020bbc(c),
|
||||
callerFunction5[5]: 00020fa4(c),
|
||||
callerFunction6[3]: 0002138c(c),
|
||||
00030004(c), 00030008(c),
|
||||
0003000c(c), 00030010(c),
|
||||
00030014(c), 00030018(c),
|
||||
0003001c(c), 00030020(c),
|
||||
00030024(c), 00030028(c)
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(true);
|
||||
|
||||
goToXrefField(functionWithAllTypesOfCalls);
|
||||
|
||||
String callerFunction = "callerFunction5";
|
||||
goToXrefField(functionWithAllTypesOfCalls, "00020fa4");
|
||||
|
||||
doubleClick();
|
||||
|
||||
assertInFunction(callerFunction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_DefaultView_DoubleClickAddressNotInFunction() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4:00020bbc(c),
|
||||
callerFunction4:00020bc0(c),
|
||||
callerFunction5:00020fa4(c),
|
||||
callerFunction5:00020fa8(c),
|
||||
callerFunction5:00020fac(c),
|
||||
callerFunction5:00020fb0(c),
|
||||
callerFunction5:00020fb4(c),
|
||||
callerFunction6:0002138c(c),
|
||||
callerFunction6:00021390(c),
|
||||
callerFunction6:00021394(c),
|
||||
00030004(c), 00030008(c),
|
||||
0003000c(c), 00030010(c),
|
||||
00030014(c), 00030018(c),
|
||||
0003001c(c), 00030020(c),
|
||||
00030024(c), 00030028(c)
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(false);
|
||||
|
||||
goToXrefField(functionWithAllTypesOfCalls);
|
||||
|
||||
String addressNotInFunction = "00030018";
|
||||
goToXrefField(functionWithAllTypesOfCalls, addressNotInFunction);
|
||||
|
||||
doubleClick();
|
||||
|
||||
assertAtAddress(addressNotInFunction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_GroupByFunctionView_DoubleClickAddressNotInFunction() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4[2]: 00020bbc(c),
|
||||
callerFunction5[5]: 00020fa4(c),
|
||||
callerFunction6[3]: 0002138c(c),
|
||||
00030004(c), 00030008(c),
|
||||
0003000c(c), 00030010(c),
|
||||
00030014(c), 00030018(c),
|
||||
0003001c(c), 00030020(c),
|
||||
00030024(c), 00030028(c)
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(true);
|
||||
|
||||
goToXrefField(functionWithAllTypesOfCalls);
|
||||
|
||||
String addressNotInFunction = "00030018";
|
||||
goToXrefField(functionWithAllTypesOfCalls, addressNotInFunction);
|
||||
|
||||
doubleClick();
|
||||
|
||||
assertAtAddress(addressNotInFunction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_DefaultView_DoubleClickToShowAllXrefs() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4:00020bbc(c),
|
||||
callerFunction4:00020bc0(c),
|
||||
callerFunction5:00020fa4(c),
|
||||
callerFunction5:00020fa8(c),
|
||||
callerFunction5:00020fac(c),
|
||||
callerFunction5:00020fb0(c),
|
||||
callerFunction5:00020fb4(c),
|
||||
callerFunction6:0002138c(c),
|
||||
callerFunction6:00021390(c),
|
||||
callerFunction6:00021394(c),
|
||||
00030004(c), 00030008(c),
|
||||
0003000c(c), 00030010(c),
|
||||
00030014(c), 00030018(c),
|
||||
0003001c(c), 00030020(c),
|
||||
00030024(c), 00030028(c)
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(false);
|
||||
|
||||
goToXrefHeaderField(functionWithAllTypesOfCalls);
|
||||
|
||||
doubleClick();
|
||||
|
||||
assertTableShowing();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_GroupByFunctionView_DoubleClickToShowAllXrefs() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4[2]: 00020bbc(c),
|
||||
callerFunction5[5]: 00020fa4(c),
|
||||
callerFunction6[3]: 0002138c(c),
|
||||
00030004(c), 00030008(c),
|
||||
0003000c(c), 00030010(c),
|
||||
00030014(c), 00030018(c),
|
||||
0003001c(c), 00030020(c),
|
||||
00030024(c), 00030028(c)
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(true);
|
||||
|
||||
goToXrefHeaderField(functionWithAllTypesOfCalls);
|
||||
|
||||
doubleClick();
|
||||
|
||||
assertTableShowing();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_DefaultView_DoubleClick_More_Text() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4:00020bbc(c),
|
||||
callerFunction4:00020bc0(c),
|
||||
callerFunction5:00020fa4(c),
|
||||
callerFunction5:00020fa8(c),
|
||||
callerFunction5:00020fac(c),
|
||||
callerFunction5:00020fb0(c),
|
||||
[more]
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(false);
|
||||
setMaxXrefs(5);
|
||||
|
||||
goToXrefField(functionWithAllTypesOfCalls, "more");
|
||||
|
||||
doubleClick();
|
||||
|
||||
assertTableShowing();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testXrefs_GroupByFunctionView_DoubleClick_More_Text() {
|
||||
|
||||
/*
|
||||
XREF[20]: callerFunction4[2]: 00020bbc(c),
|
||||
callerFunction5[4]: 00020fa4(c),
|
||||
[more]
|
||||
*/
|
||||
|
||||
setGroupByFunctionOption(true);
|
||||
setMaxXrefs(5);
|
||||
|
||||
goToXrefField(functionWithAllTypesOfCalls, "more");
|
||||
|
||||
doubleClick();
|
||||
|
||||
assertTableShowing();
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Private Methods
|
||||
//==================================================================================================
|
||||
|
||||
private void assertTableShowing() {
|
||||
TableComponentProvider<?> table = waitForComponentProvider(TableComponentProvider.class);
|
||||
GhidraProgramTableModel<?> model = table.getModel();
|
||||
waitForCondition(() -> model.getRowCount() > 0);
|
||||
}
|
||||
|
||||
private void assertAtAddress(String expected) {
|
||||
Address actual = cb.getCurrentAddress();
|
||||
assertEquals(expected, actual.toString());
|
||||
}
|
||||
|
||||
private void assertInFunction(String text) {
|
||||
|
||||
Address addr = cb.getCurrentAddress();
|
||||
FunctionManager functionManager = program.getFunctionManager();
|
||||
Function function = functionManager.getFunctionContaining(addr);
|
||||
assertEquals(text, function.getName());
|
||||
}
|
||||
|
||||
private void doubleClick() {
|
||||
click(cb, 2, true);
|
||||
}
|
||||
|
||||
private void assertContainsRow(ListingTextField tf, String text) {
|
||||
assertTrue("Expected '" + tf.getText() + "' to contain '" + text + "'",
|
||||
tf.getText().contains(text));
|
||||
}
|
||||
|
||||
private void goToXrefField(int addrOffset) {
|
||||
assertTrue("Unable to navigate to xref field at " + Long.toHexString(addrOffset),
|
||||
cb.goToField(addr(addrOffset), XRefFieldFactory.FIELD_NAME, 1, 1));
|
||||
}
|
||||
|
||||
private void goToXrefHeaderField(int addrOffset) {
|
||||
assertTrue("Unable to navigate to xref header field at " + Long.toHexString(addrOffset),
|
||||
cb.goToField(addr(addrOffset), XRefHeaderFieldFactory.XREF_FIELD_NAME, 1, 1));
|
||||
}
|
||||
|
||||
private void goToXrefField(int addrOffset, String text) {
|
||||
|
||||
// is there a better way to find a field when given an address and some text?
|
||||
|
||||
assertTrue("Unable to navigate to xref field at " + Long.toHexString(addrOffset),
|
||||
cb.goToField(addr(addrOffset), XRefFieldFactory.FIELD_NAME, 1, 1));
|
||||
|
||||
ListingTextField tf = (ListingTextField) cb.getCurrentField();
|
||||
int rows = tf.getNumRows();
|
||||
for (int row = 0; row < rows; row++) {
|
||||
|
||||
String rowText = getRowText(tf, row);
|
||||
int col = rowText.indexOf(text);
|
||||
if (col >= 0) {
|
||||
col++; // move past the start position to ensure we are inside of the field
|
||||
assertTrue("Unable to navigate to xref field at " + Long.toHexString(addrOffset),
|
||||
cb.goToField(addr(addrOffset), XRefFieldFactory.FIELD_NAME, row, col));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fail("Uanble to find text at " + Long.toHexString(addrOffset) + "; text: '" + text + "'");
|
||||
}
|
||||
|
||||
private String getRowText(ListingTextField tf, int row) {
|
||||
|
||||
List<FieldElement> rowElements = getRowElements(tf, row);
|
||||
return StringUtils.join(rowElements);
|
||||
}
|
||||
|
||||
private List<FieldElement> getRowElements(ListingTextField tf, int row) {
|
||||
|
||||
List<FieldElement> elements = new ArrayList<>();
|
||||
int cols = tf.getNumCols(row);
|
||||
for (int col = 0; col < cols; col++) {
|
||||
FieldElement element = tf.getFieldElement(row, col);
|
||||
if (!elements.contains(element)) {
|
||||
elements.add(element);
|
||||
}
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
private boolean hasXrefField(int addrOffset) {
|
||||
return cb.goToField(addr(addrOffset), XRefFieldFactory.FIELD_NAME, 1, 1);
|
||||
}
|
||||
|
||||
private Function function(int addr) throws Exception {
|
||||
return ensureFunction(addr);
|
||||
}
|
||||
|
||||
private Function caller(int addr) throws Exception {
|
||||
String name = "callerFunction" + (++callerCount);
|
||||
return ensureFunction(addr, name);
|
||||
}
|
||||
|
||||
private Function ensureFunction(long from) throws Exception {
|
||||
ProgramDB p = builder.getProgram();
|
||||
FunctionManager fm = p.getFunctionManager();
|
||||
Function f = fm.getFunctionAt(addr(from));
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
|
||||
String a = Long.toHexString(from);
|
||||
return ensureFunction(from, "Function_" + a);
|
||||
}
|
||||
|
||||
private Function ensureFunction(long from, String name) throws Exception {
|
||||
ProgramDB p = builder.getProgram();
|
||||
FunctionManager fm = p.getFunctionManager();
|
||||
Function f = fm.getFunctionAt(addr(from));
|
||||
if (f != null) {
|
||||
return f;
|
||||
}
|
||||
|
||||
String a = Long.toHexString(from);
|
||||
return builder.createEmptyFunction(name, "0x" + a, 500, DataType.DEFAULT);
|
||||
}
|
||||
|
||||
// creates n references from within caller to the given address
|
||||
private void createCallerReference(int toAddr, Function caller, int n) {
|
||||
int addr = (int) caller.getEntryPoint().getOffset();
|
||||
createMemoryReferencesReference(toAddr, addr, n);
|
||||
}
|
||||
|
||||
// create call reference to the given address
|
||||
private void createNonFunctionReferences(int toAddr, int fromAddrRangeStart, int n) {
|
||||
createMemoryReferencesReference(toAddr, fromAddrRangeStart, n);
|
||||
}
|
||||
|
||||
private void createMemoryReferencesReference(int toAddr, int fromAddrRangeStart, int n) {
|
||||
|
||||
int offset = 4;
|
||||
int addr = fromAddrRangeStart;
|
||||
for (int i = 0; i < n; i++) {
|
||||
addr += offset;
|
||||
createReference(addr, toAddr);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean createReference(long from, long to) {
|
||||
ProgramDB p = builder.getProgram();
|
||||
ReferenceManager rm = p.getReferenceManager();
|
||||
Reference existing = rm.getReference(addr(from), addr(to), 0);
|
||||
if (existing != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
builder.createMemoryCallReference("0x" + Long.toHexString(from),
|
||||
"0x" + Long.toHexString(to));
|
||||
return true;
|
||||
}
|
||||
|
||||
private Address addr(long addr) {
|
||||
return builder.addr(addr);
|
||||
}
|
||||
|
||||
private void setGroupByFunctionOption(boolean b) {
|
||||
setBooleanOption(XRefFieldFactory.GROUP_BY_FUNCTION_KEY, b);
|
||||
}
|
||||
|
||||
private void setMaxXrefs(int n) {
|
||||
setIntOptions(XRefFieldFactory.MAX_XREFS_KEY, n);
|
||||
}
|
||||
|
||||
private void setBooleanOption(String name, boolean value) {
|
||||
|
||||
assertTrue("No such option '" + name + "'", fieldOptions.contains(name));
|
||||
|
||||
runSwing(() -> fieldOptions.setBoolean(name, value));
|
||||
waitForSwing();
|
||||
cb.updateNow();
|
||||
}
|
||||
|
||||
private void setIntOptions(String name, int value) {
|
||||
assertTrue("No such option '" + name + "'", fieldOptions.contains(name));
|
||||
|
||||
runSwing(() -> fieldOptions.setInt(name, value));
|
||||
waitForSwing();
|
||||
cb.updateNow();
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue