Merge branch 'stable'

This commit is contained in:
ghidra1
2019-04-03 15:45:25 -04:00
97 changed files with 1263 additions and 968 deletions
@@ -6,6 +6,45 @@
</HEAD> </HEAD>
<BODY> <BODY>
<H1 align="center">Ghidra 9.0.2 Change History (April 2019)</H1>
<blockquote><p><u>Bugs</u></p></blockquote>
<blockquote>
<ul>
<li><I>Analysis.</I> Constant reference analysis boundary controls for speculative references has been fixed. Speculative references are references created from computed constants passed as parameters, stored to a location, or from indexed offsets from a register. (Issue #228)</li>
<li><I>Decompiler. </I> Fixed rendering bug in the Decompiler when the "Find" dialog is closed. (Issue #282) </li>
<li><I>Decompiler. </I> Fixed decompiler handling of Function Definition data types. (Issue #247) </li>
<li><I>Decompiler. </I> Fixed "Free Varnode" exception in RuleConditionalMove. (Issue #294) </li>
<li><I>Diff. </I> Fixed exceptions that can occur in the Diff View for programs with overlays. </li>
<li><I>Documentation. </I> Corrected the spelling of "listener" throughout the source code. (Issue #235) </li>
<li><I>Exporter. </I> Exporting a selection as Intel Hex will now allow a selection of any length. Previously this was restricted to multiples of 16 bytes. (Issue #260) </li>
<li><I>GUI. </I> Fixed exception that occurs after disabling MyProgramChangesDisplayPlugin. </li>
<li><I>GUI.</I> Updated the "Open Program" dialog to disallow file drop operations. (Issue #252)
<li><I>Languages. </I> The ARM Thumb CMP.W and LSL isntructions have been changed to correctly decode. There are still issues to work out with Unpredictable execution when Rd is the PC. (Issue #280) </li>
<li><I>Multi-User:Ghidra Server. </I> Corrected bug introduced into ghidraSvr.bat which could prevent Ghidra Server startup (Issue #279) </li>
<li><I>Scripting.</I> MultiInstructionMemReference script has been corrected to consider input and output registers when placing a reference on an instruction.</li>
</ul>
</blockquote>
<blockquote><p><u>Security</u></p></blockquote>
<blockquote>
<ul>
<li><I>Basic Infrastructure. </I> Added a property to support/launch.properties to prevent log4j from using jansi.dll on Windows. (Issue #286) </li>
</ul>
</blockquote>
<H1 align="center">Ghidra 9.0.1 Change History (March 2019)</H1> <H1 align="center">Ghidra 9.0.1 Change History (March 2019)</H1>
<blockquote><p><u>New Features</u></p></blockquote> <blockquote><p><u>New Features</u></p></blockquote>
@@ -22,7 +22,7 @@
<file_extension extension=".java" icon="images/famfamfam_silk_icons_v013/page_white_cup.png" /> <file_extension extension=".java" icon="images/famfamfam_silk_icons_v013/page_white_cup.png" />
<file_extension extension=".kext" icon="images/famfamfam_silk_icons_v013/bullet_pink.png" /> <file_extension extension=".kext" icon="images/famfamfam_silk_icons_v013/bullet_pink.png" />
<file_extension extension=".lib" icon="images/famfamfam_silk_icons_v013/server.png" /> <file_extension extension=".lib" icon="images/famfamfam_silk_icons_v013/server.png" />
<file_extension extension=".obj" icon="images/oxygen/16x16/subrip.png" /> <file_extension extension=".obj" icon="images/oxygen/16x16/application-x-subrip.png" />
<file_extension extension=".p" icon="images/oxygen/16x16/text-x-pascal.png" /> <file_extension extension=".p" icon="images/oxygen/16x16/text-x-pascal.png" />
<file_extension extension=".pdf" icon="images/oxygen/16x16/application-pdf.png" /> <file_extension extension=".pdf" icon="images/oxygen/16x16/application-pdf.png" />
<file_extension extension=".plist" icon="images/oxygen/16x16/insert-table.png" /> <file_extension extension=".plist" icon="images/oxygen/16x16/insert-table.png" />
@@ -39,37 +39,59 @@
//@category Analysis //@category Analysis
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
import ghidra.app.script.GhidraScript; import ghidra.app.script.GhidraScript;
import ghidra.program.model.address.*; import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.address.AddressRangeImpl;
import ghidra.program.model.address.AddressRangeIterator;
import ghidra.program.model.address.AddressSet;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.address.AddressSpace;
import ghidra.program.model.block.CodeBlock; import ghidra.program.model.block.CodeBlock;
import ghidra.program.model.block.PartitionCodeSubModel; import ghidra.program.model.block.PartitionCodeSubModel;
import ghidra.program.model.lang.*; import ghidra.program.model.lang.OperandType;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Instruction; import ghidra.program.model.listing.Instruction;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.model.symbol.RefType; import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.Reference;
import ghidra.program.model.symbol.SourceType; import ghidra.program.model.symbol.SourceType;
import ghidra.program.util.*; import ghidra.program.util.ContextEvaluator;
import ghidra.program.util.ContextEvaluatorAdapter;
import ghidra.program.util.OperandFieldLocation;
import ghidra.program.util.SymbolicPropogator;
import ghidra.program.util.VarnodeContext;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public class MultiInstructionMemReference extends GhidraScript { public class MultiInstructionMemReference extends GhidraScript {
Address memReferenceLocation = null; Address memReferenceLocation = null;
private Address curInstrloc; private Address curInstrloc;
private Object[] inputObjects;
private Object[] resultObjects;
private Register singleRegister;
private boolean registerInOut;
@Override @Override
public void run() throws Exception { public void run() throws Exception {
long numInstructions = currentProgram.getListing().getNumInstructions(); long numInstructions = currentProgram.getListing().getNumInstructions();
monitor.initialize((int) (numInstructions)); monitor.initialize((int) (numInstructions));
monitor.setMessage("Multi-Instruction Reference Markup"); monitor.setMessage("Multi-Instruction Reference Markup");
int currentOpIndex = 0; int currentOpIndex = -1;
Address start = currentLocation.getAddress(); Address start = currentLocation.getAddress();
if ((currentSelection == null || currentSelection.isEmpty()) && if ((currentSelection == null || currentSelection.isEmpty()) &&
currentLocation instanceof OperandFieldLocation) { currentLocation instanceof OperandFieldLocation) {
currentOpIndex = ((OperandFieldLocation) currentLocation).getOperandIndex(); OperandFieldLocation operandLocation = (OperandFieldLocation) currentLocation;
currentOpIndex = operandLocation.getOperandIndex();
int subOpIndex = operandLocation.getSubOperandIndex();
singleRegister = getRegister(start, currentOpIndex, subOpIndex);
} }
// set up the address set to restrict processing // set up the address set to restrict processing
@@ -81,6 +103,34 @@ public class MultiInstructionMemReference extends GhidraScript {
findMemRefAtOperand(currentOpIndex, refLocationsSet); findMemRefAtOperand(currentOpIndex, refLocationsSet);
} }
/**
* Get the register at the location
*
* @param opIndex index into operands for instruction
* @param subOpIndex index into operands for an operand location
*
* @return register if there is one at the location
*/
private Register getRegister(Address addr, int opIndex, int subOpIndex) {
if (addr == null) {
return null;
}
Instruction instr = currentProgram.getListing().getInstructionContaining(addr);
if (instr == null) {
return null;
}
List<Object> defOpRep = instr.getDefaultOperandRepresentationList(opIndex);
if (subOpIndex >= 0 && subOpIndex < defOpRep.size()) {
Object obj = defOpRep.get(subOpIndex);
if (obj instanceof Register) {
return (Register) obj;
}
}
return instr.getRegister(opIndex);
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
private boolean isSingleInstructions(AddressSet restrictedSet) { private boolean isSingleInstructions(AddressSet restrictedSet) {
if (restrictedSet.isEmpty()) { if (restrictedSet.isEmpty()) {
@@ -106,53 +156,154 @@ public class MultiInstructionMemReference extends GhidraScript {
// use context to fill out addresses on certain instructions // use context to fill out addresses on certain instructions
ContextEvaluator eval = new ContextEvaluatorAdapter() { ContextEvaluator eval = new ContextEvaluatorAdapter() {
@Override
public boolean evaluateContextBefore(VarnodeContext context, Instruction instr) {
// if the requested reference was on an input op-object, get context before exec
return checkContext(true, opIndex, context, instr);
}
@Override @Override
public boolean evaluateContext(VarnodeContext context, Instruction instr) { public boolean evaluateContext(VarnodeContext context, Instruction instr) {
// TODO: could look at instructions like LEA, that are an address to create a reference to something. // if the requested reference was on an output op-object, get context after exec
return checkContext(false, opIndex, context, instr);
}
private boolean checkContext(boolean input, final int opIndex, VarnodeContext context, Instruction instr) {
if (instr.getMinAddress().equals(curInstrloc)) { if (instr.getMinAddress().equals(curInstrloc)) {
if (checkInstructionMatch(opIndex, context, instr)) { if (checkInstructionMatch(opIndex, input, context, instr)) {
return true; return true;
} }
// if instruction is in delayslot, assume reference is good. // if instruction is in delayslot, assume reference is good.
if (instr.getDelaySlotDepth() > 0) { if (instr.getDelaySlotDepth() > 0) {
instr = instr.getNext(); instr = instr.getNext();
return checkInstructionMatch(opIndex, context, instr); return checkInstructionMatch(opIndex, input, context, instr);
} }
} }
return false; return false;
} }
private boolean checkInstructionMatch(final int opIdx, VarnodeContext context,
@Override
public boolean evaluateReference(VarnodeContext context, Instruction instr, int pcodeop, Address address,
int size, RefType refType) {
return super.evaluateReference(context, instr, pcodeop, address, size, refType);
}
private boolean checkInstructionMatch(final int opIdx, boolean input, VarnodeContext context,
Instruction instr) { Instruction instr) {
int firstIndex = opIdx; List<Object> list = Arrays.asList(input ? inputObjects : resultObjects);
if (instr.getRegister(firstIndex) == null) {
firstIndex = 0;
}
for (int index = firstIndex; index < instr.getNumOperands(); index++) {
Object[] opObjects = instr.getOpObjects(index);
for (int indexOpObj = 0; indexOpObj < opObjects.length; indexOpObj++) {
if (!(opObjects[indexOpObj] instanceof Register)) {
continue;
}
Register reg = (Register) opObjects[indexOpObj];
RegisterValue rval = context.getRegisterValue(reg);
if (rval == null) {
continue;
}
BigInteger uval = rval.getUnsignedValue();
if (uval == null) {
continue;
}
long offset = uval.longValue();
AddressSpace space = instr.getMinAddress().getAddressSpace();
Address addr = space.getTruncatedAddress(offset, true);
// assume that they want the reference, don't worry it isn't in memory
makeReference(instr, index, addr, monitor);
return false;
for (int index = opIdx; index < instr.getNumOperands(); index++)
{
if (getRefsForOperand(context, instr, list, index)) {
// register is both an in/out check if symbolic on out
if (registerInOut) {
break;
}
return true;
} }
} }
if (addSymbolicRefs(input, context, instr, list)) {
return true;
}
return false;
}
/**
* Check the current operand for references based on input/outputs
*
* @param context - context holding values
* @param instr - instruction under consideration
* @param list - input/output lists
* @param opIndex - index of operand to check
*
* @return true if a reference was found
*/
private boolean getRefsForOperand(VarnodeContext context, Instruction instr, List<Object> list, int opIndex) {
Object[] opObjects = instr.getOpObjects(opIndex);
for (int indexOpObj = 0; indexOpObj < opObjects.length; indexOpObj++) {
if (!(opObjects[indexOpObj] instanceof Register)) {
continue;
}
Register reg = (Register) opObjects[indexOpObj];
// if operand has a single register and this isn't it
if (singleRegister != null && !reg.equals(singleRegister)) {
continue;
}
// check that the register is on the correct input/output list
if (!list.contains(reg)) {
continue;
}
RegisterValue rval = context.getRegisterValue(reg);
if (rval == null) {
continue;
}
BigInteger uval = rval.getUnsignedValue();
if (uval == null) {
continue;
}
long offset = uval.longValue();
AddressSpace space = instr.getMinAddress().getAddressSpace();
Address addr = space.getTruncatedAddress(offset, true);
// assume that they want the reference, don't worry it isn't in memory
makeReference(instr, opIndex, addr);
return true;
}
return false;
}
private boolean addSymbolicRefs(boolean input, VarnodeContext context, Instruction instr, List<Object> list) {
// get the value of the single register to see if this is the value desired
if (singleRegister == null) {
return false;
}
// check that the register is on the correct input/output list
if (!list.contains(singleRegister)) {
return false;
}
Varnode registerVarnodeValue = context.getRegisterVarnodeValue(singleRegister);
if (!context.isSymbol(registerVarnodeValue) && !registerVarnodeValue.isRegister()) {
return false;
}
Address symAddr = registerVarnodeValue.getAddress();
if (symAddr == context.BAD_ADDRESS) {
return false;
}
String valStr = "";
if (registerVarnodeValue.isRegister()) {
valStr = context.getRegister(registerVarnodeValue).toString();
} else {
// is an offset from a space
String name = symAddr.getAddressSpace().getName();
BigInteger offset = symAddr.getOffsetAsBigInteger();
valStr = name + " + 0x" + offset.toString(16);
}
Address lastSetLocation = context.getLastSetLocation(singleRegister, null);
String comment = instr.getComment(Instruction.EOL_COMMENT);
if (comment == null) {
comment = "";
}
String inoutChar = (input ? " " : "\'");
String lastStr = (lastSetLocation != null ? " @" + lastSetLocation : "");
String markup = singleRegister+inoutChar+"= "+ valStr + lastStr;
if (comment.replace('\'',' ').contains(markup.replace('\'',' '))) {
return false;
}
comment = (comment.trim().length()==0 ? markup : comment + "\n" + markup);
instr.setComment(Instruction.EOL_COMMENT, comment);
return false; return false;
} }
@@ -188,8 +339,14 @@ public class MultiInstructionMemReference extends GhidraScript {
} }
} }
// if the instruction attempting to markup is in the delayslot, backup an instruction
Instruction instr = currentProgram.getListing().getInstructionAt(curInstrloc); Instruction instr = currentProgram.getListing().getInstructionAt(curInstrloc);
if (instr != null) {
inputObjects = instr.getInputObjects();
resultObjects = instr.getResultObjects();
registerInOut = checkRegisterInOut(singleRegister, inputObjects, resultObjects);
}
// if the instruction attempting to markup is in the delayslot, backup an instruction
if (instr != null && instr.isInDelaySlot()) { if (instr != null && instr.isInDelaySlot()) {
instr = instr.getPrevious(); instr = instr.getPrevious();
if (instr != null) { if (instr != null) {
@@ -209,16 +366,24 @@ public class MultiInstructionMemReference extends GhidraScript {
} }
} }
/** private boolean checkRegisterInOut(Register reg, Object[] in, Object[] out) {
* @param instruction if (reg == null || in == null || out == null) {
* @param space return false;
* @param scalar }
* @param nextInstr
* @param addend List<Object> inList = Arrays.asList(in);
* @param taskMonitor List<Object> outList = Arrays.asList(out);
return inList.contains(reg) && outList.contains(reg);
}
/** Make the reference on the instruction at the correct location.
*
* @param instruction to receive reference
* @param space reference created in this space
* @param scalar used as offset into address space
*/ */
private void makeReference(Instruction instruction, int opIndex, Address addr, private void makeReference(Instruction instruction, int opIndex, Address addr) {
TaskMonitor taskMonitor) {
if (instruction.getPrototype().hasDelaySlots()) { if (instruction.getPrototype().hasDelaySlots()) {
instruction = instruction.getNext(); instruction = instruction.getNext();
if (instruction == null) { if (instruction == null) {
@@ -239,6 +404,13 @@ public class MultiInstructionMemReference extends GhidraScript {
opIndex = instruction.getNumOperands() - 1; opIndex = instruction.getNumOperands() - 1;
} }
// check if it already has the reference
Reference[] referencesFrom = instruction.getReferencesFrom();
boolean hasRef = Arrays.stream(referencesFrom).anyMatch(p -> p.getToAddress().equals(addr));
if (hasRef) {
return;
}
if (opIndex == -1) { if (opIndex == -1) {
instruction.addMnemonicReference(addr, RefType.DATA, SourceType.ANALYSIS); instruction.addMnemonicReference(addr, RefType.DATA, SourceType.ANALYSIS);
} }
@@ -306,6 +306,12 @@
<LI><B>Address Space</B> - Specifies which address space to export as Intel Hex format <LI><B>Address Space</B> - Specifies which address space to export as Intel Hex format
only supports one address space. This option will be intialized to the "default" only supports one address space. This option will be intialized to the "default"
address space.</LI> address space.</LI>
<LI><B>Record Size</B> - Specifies the size (in bytes) of each record in the
output file. The default 16.</LI>
<LI><B>Align To Record Size</B> - If checked, this will ensure that <b>only</b> records matching
the record size will be output. eg: if you set the record size to 16 but there are
18 bytes selected, you will see only one line of 16 bytes in the output; the remaining
2 bytes will be dropped.</LI>
</UL> </UL>
</BLOCKQUOTE> </BLOCKQUOTE>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,21 +15,29 @@
*/ */
package foundation; package foundation;
import ghidra.app.factory.*; import ghidra.app.factory.GhidraToolStateFactory;
import ghidra.app.util.*; import ghidra.app.util.GhidraFileOpenDataFlavorHandlerService;
import ghidra.framework.*; import ghidra.framework.ModuleInitializer;
import ghidra.framework.data.*; import ghidra.framework.PluggableServiceRegistry;
import ghidra.framework.main.datatree.*; import ghidra.framework.data.ToolStateFactory;
import ghidra.framework.main.datatree.GhidraDataFlavorHandlerService;
import ghidra.program.database.*; import ghidra.program.database.*;
public class FoundationInitializer implements ModuleInitializer { public class FoundationInitializer implements ModuleInitializer {
public void run() { @Override
PluggableServiceRegistry.registerPluggableService( ToolStateFactory.class, new GhidraToolStateFactory() ); public void run() {
PluggableServiceRegistry.registerPluggableService( DataFlavorHandlerService.class, new GhidraDataFlavorHandlerService() ); PluggableServiceRegistry.registerPluggableService(ToolStateFactory.class,
PluggableServiceRegistry.registerPluggableService( FileOpenDataFlavorHandlerService.class, new GhidraFileOpenDataFlavorHandlerService() ); new GhidraToolStateFactory());
PluggableServiceRegistry.registerPluggableService( DataTypeArchiveMergeManagerFactory.class, new GhidraDataTypeArchiveMergeManagerFactory() ); PluggableServiceRegistry.registerPluggableService(GhidraDataFlavorHandlerService.class,
PluggableServiceRegistry.registerPluggableService( ProgramMultiUserMergeManagerFactory.class, new GhidraProgramMultiUserMergeManagerFactory() ); new GhidraDataFlavorHandlerService());
} PluggableServiceRegistry.registerPluggableService(
GhidraFileOpenDataFlavorHandlerService.class,
new GhidraFileOpenDataFlavorHandlerService());
PluggableServiceRegistry.registerPluggableService(DataTypeArchiveMergeManagerFactory.class,
new GhidraDataTypeArchiveMergeManagerFactory());
PluggableServiceRegistry.registerPluggableService(ProgramMultiUserMergeManagerFactory.class,
new GhidraProgramMultiUserMergeManagerFactory());
}
@Override @Override
public String getName() { public String getName() {
@@ -67,11 +67,17 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
protected static final int MINKNOWNREFADDRESS_OPTION_DEFAULT_VALUE = 4; protected static final int MINKNOWNREFADDRESS_OPTION_DEFAULT_VALUE = 4;
protected static final String MINSPECULATIVEREFADDRESS_OPTION_NAME = protected static final String MINSPECULATIVEREFADDRESS_OPTION_NAME =
"Min speculative reference"; "Speculative reference min";
protected static final String MINSPECULATIVEREFADDRESS_OPTION_DESCRIPTION = protected static final String MINSPECULATIVEREFADDRESS_OPTION_DESCRIPTION =
"Minimum speculative reference address for offsets and parameters"; "Minimum speculative reference address for offsets and parameters";
protected static final int MINSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE = 1024; protected static final int MINSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE = 1024;
protected static final String MAXSPECULATIVEREFADDRESS_OPTION_NAME =
"Speculative reference max";
protected static final String MAXSPECULATIVEREFADDRESS_OPTION_DESCRIPTION =
"Maxmimum speculative reference address offset from the end of memory for offsets and parameters";
protected static final int MAXSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE = 256;
protected final static int NOTIFICATION_INTERVAL = 100; protected final static int NOTIFICATION_INTERVAL = 100;
protected boolean checkParamRefsOption = OPTION_DEFAULT_VALUE; protected boolean checkParamRefsOption = OPTION_DEFAULT_VALUE;
@@ -80,6 +86,7 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
protected int maxThreadCount = MAXTHREADCOUNT_OPTION_DEFAULT_VALUE; protected int maxThreadCount = MAXTHREADCOUNT_OPTION_DEFAULT_VALUE;
protected long minStoreLoadRefAddress = MINKNOWNREFADDRESS_OPTION_DEFAULT_VALUE; protected long minStoreLoadRefAddress = MINKNOWNREFADDRESS_OPTION_DEFAULT_VALUE;
protected long minSpeculativeRefAddress = MINSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE; protected long minSpeculativeRefAddress = MINSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE;
protected long maxSpeculativeRefAddress = MAXSPECULATIVEREFADDRESS_OPTION_DEFAULT_VALUE;
protected boolean followConditional = false; protected boolean followConditional = false;
@@ -391,7 +398,7 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
throws CancelledException { throws CancelledException {
ContextEvaluator eval = new ConstantPropagationContextEvaluator(trustWriteMemOption, ContextEvaluator eval = new ConstantPropagationContextEvaluator(trustWriteMemOption,
minStoreLoadRefAddress, minSpeculativeRefAddress); minStoreLoadRefAddress, minSpeculativeRefAddress, maxSpeculativeRefAddress);
return symEval.flowConstants(flowStart, flowSet, eval, true, monitor); return symEval.flowConstants(flowStart, flowSet, eval, true, monitor);
} }
@@ -461,9 +468,13 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
MINKNOWNREFADDRESS_OPTION_DESCRIPTION); MINKNOWNREFADDRESS_OPTION_DESCRIPTION);
long size = program.getAddressFactory().getDefaultAddressSpace().getSize(); long size = program.getAddressFactory().getDefaultAddressSpace().getSize();
minSpeculativeRefAddress = size * 8; minSpeculativeRefAddress = size * 16;
options.registerOption(MINSPECULATIVEREFADDRESS_OPTION_NAME, minSpeculativeRefAddress, null, options.registerOption(MINSPECULATIVEREFADDRESS_OPTION_NAME, minSpeculativeRefAddress, null,
MINSPECULATIVEREFADDRESS_OPTION_DESCRIPTION); MINSPECULATIVEREFADDRESS_OPTION_DESCRIPTION);
maxSpeculativeRefAddress = size * 8;
options.registerOption(MAXSPECULATIVEREFADDRESS_OPTION_NAME, maxSpeculativeRefAddress, null,
MAXSPECULATIVEREFADDRESS_OPTION_DESCRIPTION);
} }
@Override @Override
@@ -479,6 +490,8 @@ public class ConstantPropagationAnalyzer extends AbstractAnalyzer {
options.getLong(MINKNOWNREFADDRESS_OPTION_NAME, minStoreLoadRefAddress); options.getLong(MINKNOWNREFADDRESS_OPTION_NAME, minStoreLoadRefAddress);
minSpeculativeRefAddress = minSpeculativeRefAddress =
options.getLong(MINSPECULATIVEREFADDRESS_OPTION_NAME, minSpeculativeRefAddress); options.getLong(MINSPECULATIVEREFADDRESS_OPTION_NAME, minSpeculativeRefAddress);
maxSpeculativeRefAddress =
options.getLong(MAXSPECULATIVEREFADDRESS_OPTION_NAME, maxSpeculativeRefAddress);
} }
} }
@@ -42,7 +42,9 @@ public class ConstantPropagationContextEvaluator extends ContextEvaluatorAdapter
protected AddressSet destSet = new AddressSet(); protected AddressSet destSet = new AddressSet();
private boolean trustMemoryWrite = false; private boolean trustMemoryWrite = false;
private long minStoreLoadOffset = 4; private long minStoreLoadOffset = 4;
private long minSpeculativeOffset = 1024; private long minSpeculativeOffset = 1024; // from the beginning of memory
private long maxSpeculativeOffset = 256; // from the end of memory
public ConstantPropagationContextEvaluator() { public ConstantPropagationContextEvaluator() {
} }
@@ -55,10 +57,10 @@ public class ConstantPropagationContextEvaluator extends ContextEvaluatorAdapter
} }
public ConstantPropagationContextEvaluator(boolean trustWriteMemOption, public ConstantPropagationContextEvaluator(boolean trustWriteMemOption,
long minStoreLoadRefAddress, long minSpeculativeRefAddress) { long minStoreLoadRefAddress, long minSpeculativeRefAddress, long maxSpeculativeRefAddress) {
this(trustWriteMemOption); this(trustWriteMemOption);
this.minStoreLoadOffset = minStoreLoadRefAddress; this.minStoreLoadOffset = minStoreLoadRefAddress;
this.minSpeculativeOffset = minSpeculativeRefAddress; this.maxSpeculativeOffset = maxSpeculativeRefAddress;
} }
/** /**
@@ -85,7 +87,7 @@ public class ConstantPropagationContextEvaluator extends ContextEvaluatorAdapter
long wordOffset = constant.getOffset(); long wordOffset = constant.getOffset();
if (((wordOffset >= 0 && wordOffset < minSpeculativeOffset) || if (((wordOffset >= 0 && wordOffset < minSpeculativeOffset) ||
(Math.abs(maxAddrOffset - wordOffset) < minSpeculativeOffset)) && (Math.abs(maxAddrOffset - wordOffset) < maxSpeculativeOffset)) &&
!space.isExternalSpace()) { !space.isExternalSpace()) {
return null; return null;
} }
@@ -93,7 +93,7 @@ public class ArchivePlugin extends Plugin implements FrontEndOnly, ProjectListen
private volatile boolean isArchiving; private volatile boolean isArchiving;
private volatile boolean isRestoring; private volatile boolean isRestoring;
private TaskListener archivingListener; private TaskListener archivingListener;
private TaskListener restoringListner; private TaskListener restoringListener;
////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////
@@ -282,7 +282,7 @@ public class ArchivePlugin extends Plugin implements FrontEndOnly, ProjectListen
isRestoring = true; isRestoring = true;
restoringListner = new TaskListener() { restoringListener = new TaskListener() {
@Override @Override
public void taskCompleted(Task task) { public void taskCompleted(Task task) {
isRestoring = false; isRestoring = false;
@@ -295,7 +295,7 @@ public class ArchivePlugin extends Plugin implements FrontEndOnly, ProjectListen
}; };
Task task = new RestoreTask(lastRestoreLocator, archiveJar, this); Task task = new RestoreTask(lastRestoreLocator, archiveJar, this);
task.addTaskListener(restoringListner); task.addTaskListener(restoringListener);
new TaskLauncher(task, tool.getToolFrame()); new TaskLauncher(task, tool.getToolFrame());
} }
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,6 +15,12 @@
*/ */
package ghidra.app.plugin.core.datamgr; package ghidra.app.plugin.core.datamgr;
import java.util.ArrayList;
import java.util.List;
import javax.swing.tree.TreePath;
import docking.widgets.tree.GTreeNode;
import ghidra.app.context.ProgramActionContext; import ghidra.app.context.ProgramActionContext;
import ghidra.app.plugin.core.datamgr.archive.ProjectArchive; import ghidra.app.plugin.core.datamgr.archive.ProjectArchive;
import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree; import ghidra.app.plugin.core.datamgr.tree.DataTypeArchiveGTree;
@@ -24,13 +29,6 @@ import ghidra.framework.main.datatable.DomainFileProvider;
import ghidra.framework.model.DomainFile; import ghidra.framework.model.DomainFile;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import java.util.ArrayList;
import java.util.List;
import javax.swing.tree.TreePath;
import docking.widgets.tree.GTreeNode;
public class DataTypesActionContext extends ProgramActionContext implements DomainFileProvider { public class DataTypesActionContext extends ProgramActionContext implements DomainFileProvider {
private final GTreeNode clickedNode; private final GTreeNode clickedNode;
private final boolean isToolbarAction; private final boolean isToolbarAction;
@@ -242,6 +242,10 @@ public class MyProgramChangesDisplayPlugin extends ProgramPlugin implements Doma
public void dispose() { public void dispose() {
worker.dispose(); worker.dispose();
if (currentProgram != null) {
currentProgram.removeTransactionListener(transactionListener);
currentProgram.removeListener(this);
}
tool.getProject().getProjectData().removeDomainFolderChangeListener(folderListener); tool.getProject().getProjectData().removeDomainFolderChangeListener(folderListener);
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,14 +15,13 @@
*/ */
package ghidra.app.util; package ghidra.app.util;
import ghidra.framework.main.datatree.*;
import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.DataFlavor;
public class GhidraFileOpenDataFlavorHandlerService extends FileOpenDataFlavorHandlerService { import ghidra.framework.main.datatree.*;
@Override public class GhidraFileOpenDataFlavorHandlerService {
protected void doRegisterDataFlavorHandlers() {
public GhidraFileOpenDataFlavorHandlerService() {
try { try {
DataFlavor linuxFileUrlFlavor = DataFlavor linuxFileUrlFlavor =
@@ -34,15 +32,15 @@ public class GhidraFileOpenDataFlavorHandlerService extends FileOpenDataFlavorHa
// should never happen as it is using java.lang.String // should never happen as it is using java.lang.String
} }
LocalTreeNodeFlavorHandler localHandler = new LocalTreeNodeFlavorHandler(); LocalTreeNodeHandler localHandler = new LocalTreeNodeHandler();
FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileFlavor, FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileFlavor,
localHandler); localHandler);
FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor, FileOpenDropHandler.addDataFlavorHandler(DataTreeDragNDropHandler.localDomainFileTreeFlavor,
localHandler); localHandler);
FileOpenDropHandler.addDataFlavorHandler(DataFlavor.javaFileListFlavor,
new JavaFileListFlavorHandler());
FileOpenDropHandler.addDataFlavorHandler( FileOpenDropHandler.addDataFlavorHandler(VersionInfoTransferable.localVersionInfoFlavor,
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localHandler); new LocalVersionInfoHandler());
FileOpenDropHandler.addDataFlavorHandler(DataFlavor.javaFileListFlavor,
new JavaFileListHandler());
} }
} }
@@ -1,48 +0,0 @@
/* ###
* 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.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File;
import java.util.List;
import ghidra.app.services.FileImporterService;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import util.CollectionUtils;
final class JavaFileListFlavorHandler implements FileOpenDataFlavorHandler {
@Override
public void handle(PluginTool tool, Object obj, DropTargetDropEvent e, DataFlavor f) {
List<File> files = CollectionUtils.asList((List<?>) obj, File.class);
FileImporterService im = tool.getService(FileImporterService.class);
if (im == null) {
tool.setStatusInfo("ERROR: Could not get importer service.");
return;
}
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
if (files.size() == 1 && files.get(0).isFile()) {
im.importFile(rootFolder, files.get(0));
}
else {
im.importFiles(rootFolder, files);
}
}
}
@@ -96,6 +96,15 @@ public class Option {
this.listener = listener; this.listener = listener;
} }
/**
* Override if you want to provide a custom widget for selecting your
* options.
* <p>
* Important! If you override this you MUST also override the {@link #copy()}
* method so it returns a new instance of your custom editor.
*
* @return the custom editor
*/
public Component getCustomEditorComponent() { public Component getCustomEditorComponent() {
return null; return null;
} }
@@ -25,17 +25,17 @@ import ghidra.util.Msg;
public class Compare { public class Compare {
public static void compare(ArrayList<String> expectedList, File actualFile) throws Exception { public static void compare(ArrayList<String> expectedList, File actualFile) throws Exception {
int index = 0; int index = 0;
BufferedReader reader = new BufferedReader(new FileReader(actualFile));
boolean hasFailure = false; boolean hasFailure = false;
try { try (BufferedReader reader = new BufferedReader(new FileReader(actualFile))) {
int excess = 0; int excess = 0;
while (true) { while (true) {
String actualLine = reader.readLine(); String actualLine = reader.readLine();
if (actualLine == null) { if (actualLine == null) {
break; break;
} }
if (index >= expectedList.size()) { if (index >= expectedList.size()) {
++excess; ++excess;
continue; continue;
@@ -73,8 +73,5 @@ public class Compare {
Assert.fail("One or more failures--see output for data"); Assert.fail("One or more failures--see output for data");
} }
} }
finally {
reader.close();
}
} }
} }
@@ -15,10 +15,15 @@
*/ */
package ghidra.app.util.exporter; package ghidra.app.util.exporter;
import java.awt.BorderLayout;
import java.awt.Component;
import java.io.*; import java.io.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.*;
import docking.widgets.textfield.HintTextField;
import ghidra.app.util.*; import ghidra.app.util.*;
import ghidra.app.util.opinion.IntelHexRecord; import ghidra.app.util.opinion.IntelHexRecord;
import ghidra.app.util.opinion.IntelHexRecordWriter; import ghidra.app.util.opinion.IntelHexRecordWriter;
@@ -29,18 +34,55 @@ import ghidra.program.model.mem.*;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
/**
* Exports the current program (or program selection) as bytes in Intel Hex format.
* <p>
* The output defaults to lines of 16-bytes but this is configurable using the
* {@link #recordSizeOption} attribute. This allows users to select any record size
* up to the max of 0xFF. Users may also choose to <code>Drop Extra Bytes</code>, which will
* cause only lines that match the max record size to be printed; any other
* bytes will be dropped. If this option is not set, every byte will be represented in the output.
*/
public class IntelHexExporter extends Exporter { public class IntelHexExporter extends Exporter {
protected final static int MAX_BYTES_PER_LINE = 0x00000010;
protected Option option; /** Option allowing the user to select the address space */
protected Option addressSpaceOption;
/** Option allowing the user to select the number of bytes in each line of output */
protected RecordSizeOption recordSizeOption;
private static final int DEFAULT_RECORD_SIZE = 0x10;
/** /**
* Constructs a new Intel Hex exporter. * Constructs a new Intel Hex exporter. This will use a record size of 16 (the default)
* and will export ALL bytes in the program or selection (even if the total length
* is not a multiple of 16.
*/ */
public IntelHexExporter() { public IntelHexExporter() {
this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex")); this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex"));
} }
/**
* Constructs a new Intel Hex exporter with a custom record size.
*
* @param recordSize the record size to use when writing to the output file
* @param dropBytes if true, bytes at the end of the file that don't match the specified
* record size will be dropped
*/
public IntelHexExporter(int recordSize, boolean dropBytes) {
this("Intel Hex", "hex", new HelpLocation("ExporterPlugin", "intel_hex"));
recordSizeOption = new RecordSizeOption("Record Size", Integer.class);
recordSizeOption.setRecordSize(recordSize);
recordSizeOption.setDropBytes(dropBytes);
}
/**
* Constructor
*
* @param name the name of the exporter
* @param extension the extension to use for the output file
* @param help location of Ghidra help
*/
protected IntelHexExporter(String name, String extension, HelpLocation help) { protected IntelHexExporter(String name, String extension, HelpLocation help) {
super(name, extension, help); super(name, extension, help);
} }
@@ -55,16 +97,49 @@ public class IntelHexExporter extends Exporter {
} }
Program program = (Program) domainObject; Program program = (Program) domainObject;
option = new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace()); addressSpaceOption =
new Option("Address Space", program.getAddressFactory().getDefaultAddressSpace());
if (recordSizeOption == null) {
recordSizeOption = new RecordSizeOption("Record Size", Integer.class);
}
optionsList.add(addressSpaceOption);
optionsList.add(recordSizeOption);
optionsList.add(option);
return optionsList; return optionsList;
} }
@Override @Override
public void setOptions(List<Option> options) throws OptionException { public void setOptions(List<Option> options) throws OptionException {
if (!options.isEmpty()) { if (!options.isEmpty()) {
option = options.get(0); addressSpaceOption = options.get(0);
recordSizeOption = (RecordSizeOption) options.get(1);
}
}
/**
* Verifier for a {@link HintTextField} that ensures input is a numeric value between
* 0 and 0xFF.
* <p>
* Input may be specified in either decimal or hex.
*/
private class BoundedIntegerVerifier extends InputVerifier {
@Override
public boolean verify(JComponent input) {
HintTextField field = (HintTextField) input;
String text = field.getText();
int val;
try {
val = Integer.decode(text);
}
catch (NumberFormatException e) {
return false;
}
return val <= 0xFF && val >= 0;
} }
} }
@@ -84,33 +159,31 @@ public class IntelHexExporter extends Exporter {
return false; return false;
} }
if (option == null) { if (addressSpaceOption == null || recordSizeOption == null) {
getOptions(() -> program); getOptions(() -> program);
} }
PrintWriter writer = new PrintWriter(new FileOutputStream(file)); try (PrintWriter writer = new PrintWriter(new FileOutputStream(file))) {
Memory memory = program.getMemory(); Memory memory = program.getMemory();
if (addrSet == null) { if (addrSet == null) {
addrSet = memory; addrSet = memory;
}
try {
List<IntelHexRecord> records = dumpMemory(program, memory, addrSet, monitor);
for (IntelHexRecord record : records) {
writer.println(record.format());
} }
}
catch (MemoryAccessException e) {
throw new ExporterException(e);
}
finally {
// Close the PrintWriter
//
writer.close();
option = null; try {
List<IntelHexRecord> records = dumpMemory(program, memory, addrSet, monitor);
for (IntelHexRecord record : records) {
writer.println(record.format());
}
}
catch (MemoryAccessException e) {
throw new ExporterException(e);
}
finally {
addressSpaceOption = null;
recordSizeOption = null;
}
} }
return true; return true;
@@ -118,15 +191,19 @@ public class IntelHexExporter extends Exporter {
protected List<IntelHexRecord> dumpMemory(Program program, Memory memory, protected List<IntelHexRecord> dumpMemory(Program program, Memory memory,
AddressSetView addrSetView, TaskMonitor monitor) throws MemoryAccessException { AddressSetView addrSetView, TaskMonitor monitor) throws MemoryAccessException {
IntelHexRecordWriter writer = new IntelHexRecordWriter(MAX_BYTES_PER_LINE);
int size = (int) recordSizeOption.getValue();
boolean dropBytes = recordSizeOption.dropExtraBytes();
IntelHexRecordWriter writer = new IntelHexRecordWriter(size, dropBytes);
AddressSet set = new AddressSet(addrSetView); AddressSet set = new AddressSet(addrSetView);
MemoryBlock[] blocks = memory.getBlocks(); MemoryBlock[] blocks = memory.getBlocks();
for (int i = 0; i < blocks.length; ++i) { for (MemoryBlock block : blocks) {
if (!blocks[i].isInitialized() || if (!block.isInitialized() ||
blocks[i].getStart().getAddressSpace() != option.getValue()) { block.getStart().getAddressSpace() != addressSpaceOption.getValue()) {
set.delete(new AddressRangeImpl(blocks[i].getStart(), blocks[i].getEnd())); set.delete(new AddressRangeImpl(block.getStart(), block.getEnd()));
} }
} }
@@ -148,4 +225,113 @@ public class IntelHexExporter extends Exporter {
} }
return writer.finish(entryPoint); return writer.finish(entryPoint);
} }
/**
* Option for exporting Intel Hex records that allows users to specify a record size for the
* output. Users may also optionally select the <code>Drop Extra Bytes</code> option that
* will cause only those records that match the maximum size to be output to the file.
*
* @see RecordSizeComponent
*/
private class RecordSizeOption extends Option {
private final RecordSizeComponent comp = new RecordSizeComponent(DEFAULT_RECORD_SIZE);
public RecordSizeOption(String name, Class<?> valueClass) {
super(name, valueClass);
}
public RecordSizeOption(String name, Class<?> valueClass, Object value, String arg,
String group) {
super(name, valueClass, value, arg, group);
}
@Override
public Component getCustomEditorComponent() {
return comp;
}
@Override
public Option copy() {
return new RecordSizeOption(getName(), getValueClass(), getValue(), getArg(),
getGroup());
}
@Override
public Object getValue() {
return comp.getValue();
}
@Override
public Class<?> getValueClass() {
return Integer.class;
}
public boolean dropExtraBytes() {
return comp.dropExtraBytes();
}
public void setRecordSize(int recordSize) {
comp.setRecordSize(recordSize);
}
public void setDropBytes(boolean dropBytes) {
comp.setDropBytes(dropBytes);
}
}
/**
* Component that displays two widgets for setting export options:
*
* <ul>
* <li><code>input</code>: a {@link HintTextField} for entering numeric digits; these
* represent the record size for each line of output</li>
* <li>dropCb: a {@link JCheckBox} for specifying a setting that enforces that every line in
* the output matches the specified record size</li>
* </ul>
*
* Note: If the <code>Drop Extra Bytes</code> option is set, any bytes that are left over
* after outputting all lines that match the record size will be omitted from the output.
*/
private class RecordSizeComponent extends JPanel {
private HintTextField input;
private JCheckBox dropCb;
public RecordSizeComponent(int recordSize) {
setLayout(new BorderLayout());
input = new HintTextField(Integer.toString(recordSize), false, new BoundedIntegerVerifier());
dropCb = new JCheckBox("Align To Record Size");
input.setText(Integer.toString(recordSize));
add(input, BorderLayout.CENTER);
add(dropCb, BorderLayout.EAST);
}
public int getValue() {
String val = input.getText();
if (!input.isFieldValid()) {
// If the user clears the input field, revert to the default
// record size (16).
return DEFAULT_RECORD_SIZE;
}
return Integer.valueOf(val);
}
public boolean dropExtraBytes() {
return dropCb.isSelected();
}
public void setRecordSize(int recordSize) {
input.setText(Integer.toString(recordSize));
}
public void setDropBytes(boolean dropBytes) {
dropCb.setSelected(dropBytes);
}
}
} }
@@ -20,21 +20,31 @@ import java.util.*;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
public class IntelHexRecordWriter { public class IntelHexRecordWriter {
private final int maxBytesPerLine; private final int maxBytesPerLine;
private final boolean dropExtraBytes;
private Address startAddress = null; private Address startAddress = null;
private Long oldSegment = null; private Long oldSegment = null;
private ArrayList<Byte> bytes = new ArrayList<Byte>(); private ArrayList<Byte> bytes = new ArrayList<>();
private Boolean isSegmented = null; private Boolean isSegmented = null;
private ArrayList<IntelHexRecord> results = new ArrayList<IntelHexRecord>(); private ArrayList<IntelHexRecord> results = new ArrayList<>();
private boolean done = false; private boolean done = false;
public IntelHexRecordWriter(int maxBytesPerLine) { /**
* Constructor
*
* @param maxBytesPerLine the maximum number of bytes to write per line in the hex output
* @param dropExtraBytes if true, only lines matching {@link #maxBytesPerLine} will be output;
* remaining bytes will be left out
*/
public IntelHexRecordWriter(int maxBytesPerLine, boolean dropExtraBytes) {
if (maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH) { if (maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH) {
throw new IllegalArgumentException("maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH"); throw new IllegalArgumentException("maxBytesPerLine > IntelHexRecord.MAX_RECORD_LENGTH");
} }
this.maxBytesPerLine = maxBytesPerLine; this.maxBytesPerLine = maxBytesPerLine;
this.dropExtraBytes = dropExtraBytes;
} }
public void addByte(Address address, byte b) { public void addByte(Address address, byte b) {
@@ -117,6 +127,14 @@ public class IntelHexRecordWriter {
} }
public List<IntelHexRecord> finish(Address entryPoint) { public List<IntelHexRecord> finish(Address entryPoint) {
// Before finalizing things, write out any remaining bytes that haven't yet been written, if
// the user has specified to do so via the drop extra bytes option (false =
// write out everything).
if (bytes.size() > 0 && !dropExtraBytes) {
emitData();
}
if (entryPoint != null && isSegmented != null) { if (entryPoint != null && isSegmented != null) {
final long offset = entryPoint.getOffset(); final long offset = entryPoint.getOffset();
byte[] data = new byte[4]; byte[] data = new byte[4];
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,6 +15,7 @@
*/ */
package ghidra.app.util.viewer.field; package ghidra.app.util.viewer.field;
import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.Highlight; import docking.widgets.fieldpanel.support.Highlight;
import docking.widgets.fieldpanel.support.HighlightFactory; import docking.widgets.fieldpanel.support.HighlightFactory;
import ghidra.app.util.HighlightProvider; import ghidra.app.util.HighlightProvider;
@@ -37,19 +37,15 @@ public class FieldHighlightFactory implements HighlightFactory {
* @param fieldFactoryClass the class of the field factory that generated the field to be rendered. * @param fieldFactoryClass the class of the field factory that generated the field to be rendered.
* @param obj the object that holds the information that will be rendered (usually a code unit) * @param obj the object that holds the information that will be rendered (usually a code unit)
*/ */
public FieldHighlightFactory(HighlightProvider provider, Class<? extends FieldFactory> fieldFactoryClass, Object obj) { public FieldHighlightFactory(HighlightProvider provider,
Class<? extends FieldFactory> fieldFactoryClass, Object obj) {
this.provider = provider; this.provider = provider;
this.fieldFactoryClass = fieldFactoryClass; this.fieldFactoryClass = fieldFactoryClass;
this.obj = obj; this.obj = obj;
} }
/** @Override
* Returns the highlights for the given text. public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
* @param text the text to be considered for highlighting.
* @return an array of highlights to be rendered.
*/
public Highlight[] getHighlights(String text, int cursorTextOffset) {
return provider.getHighlights(text, obj, fieldFactoryClass, cursorTextOffset); return provider.getHighlights(text, obj, fieldFactoryClass, cursorTextOffset);
} }
} }
@@ -51,7 +51,8 @@ public class OptionsGui extends JPanel {
private static final Color DARK_ORANGE = new Color(255, 128, 0); private static final Color DARK_ORANGE = new Color(255, 128, 0);
private static final Color DARK_RED = new Color(130, 0, 75); private static final Color DARK_RED = new Color(130, 0, 75);
private static final Highlight[] NO_HIGHLIGHTS = new Highlight[0]; private static final Highlight[] NO_HIGHLIGHTS = new Highlight[0];
private static final HighlightFactory hlFactory = (text, cursorTextOffset) -> NO_HIGHLIGHTS; private static final HighlightFactory hlFactory =
(field, text, cursorTextOffset) -> NO_HIGHLIGHTS;
public static final ScreenElement COMMENT_AUTO = public static final ScreenElement COMMENT_AUTO =
new ScreenElement("Comment, Automatic", Color.LIGHT_GRAY); new ScreenElement("Comment, Automatic", Color.LIGHT_GRAY);
@@ -26,6 +26,7 @@ import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import docking.*; import docking.*;
import docking.event.mouse.GMouseListenerAdapter;
import docking.widgets.tree.support.GTreeSelectionEvent; import docking.widgets.tree.support.GTreeSelectionEvent;
import docking.widgets.tree.support.GTreeSelectionListener; import docking.widgets.tree.support.GTreeSelectionListener;
import ghidra.framework.main.datatree.ClearCutAction; import ghidra.framework.main.datatree.ClearCutAction;
@@ -40,7 +41,7 @@ import ghidra.util.layout.PairLayout;
* Dialog to open or save domain data items to a new location or name. * Dialog to open or save domain data items to a new location or name.
*/ */
public class DataTreeDialog extends DialogComponentProvider public class DataTreeDialog extends DialogComponentProvider
implements GTreeSelectionListener, ActionListener { implements GTreeSelectionListener, ActionListener {
/** /**
* Dialog type for opening domain data files. * Dialog type for opening domain data files.
@@ -540,10 +541,11 @@ implements GTreeSelectionListener, ActionListener {
protected void addTreeListeners() { protected void addTreeListeners() {
if (type == OPEN) { if (type == OPEN) {
treePanel.addTreeMouseListener(new MouseAdapter() {
treePanel.addTreeMouseListener(new GMouseListenerAdapter() {
@Override @Override
public void mousePressed(MouseEvent e) { public void doubleClickTriggered(MouseEvent e) {
if (e.getClickCount() == 2 && okButton.isEnabled()) { if (okButton.isEnabled()) {
okCallback(); okCallback();
} }
} }
@@ -671,7 +673,7 @@ implements GTreeSelectionListener, ActionListener {
// populate the combo box // populate the combo box
DefaultComboBoxModel<String> model = DefaultComboBoxModel<String> model =
(DefaultComboBoxModel<String>) projectComboBox.getModel(); (DefaultComboBoxModel<String>) projectComboBox.getModel();
model.removeAllElements(); model.removeAllElements();
Set<String> map = new HashSet<>(); Set<String> map = new HashSet<>();
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -18,9 +17,10 @@ package ghidra.framework.main.datatree;
import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.DataFlavor;
public class GhidraDataFlavorHandlerService extends DataFlavorHandlerService { public class GhidraDataFlavorHandlerService {
@Override
protected void doRegisterDataFlavorHandlers() { public GhidraDataFlavorHandlerService() {
try { try {
DataFlavor linuxFileUrlFlavor = DataFlavor linuxFileUrlFlavor =
new DataFlavor("application/x-java-serialized-object;class=java.lang.String"); new DataFlavor("application/x-java-serialized-object;class=java.lang.String");
@@ -31,15 +31,12 @@ public class GhidraDataFlavorHandlerService extends DataFlavorHandlerService {
// should never happen as it is using java.lang.String // should never happen as it is using java.lang.String
} }
final LocalTreeNodeHandler localTreeNodeHandler = new LocalTreeNodeHandler(); LocalTreeNodeHandler localNodeHandler = new LocalTreeNodeHandler();
DataTreeDragNDropHandler.addActiveDataFlavorHandler( DataTreeDragNDropHandler.addActiveDataFlavorHandler(
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler); DataTreeDragNDropHandler.localDomainFileTreeFlavor, localNodeHandler);
DataTreeDragNDropHandler.addActiveDataFlavorHandler(DataFlavor.javaFileListFlavor, DataTreeDragNDropHandler.addActiveDataFlavorHandler(DataFlavor.javaFileListFlavor,
new JavaFileListHandler()); new JavaFileListHandler());
DataTreeDragNDropHandler.addActiveDataFlavorHandler( DataTreeDragNDropHandler.addActiveDataFlavorHandler(
VersionInfoTransferable.localVersionInfoFlavor, new LocalVersionInfoHandler()); VersionInfoTransferable.localVersionInfoFlavor, new LocalVersionInfoHandler());
DataTreeDragNDropHandler.addInactiveDataFlavorHandler(
DataTreeDragNDropHandler.localDomainFileTreeFlavor, localTreeNodeHandler);
} }
} }
@@ -19,13 +19,15 @@
package ghidra.framework.main.datatree; package ghidra.framework.main.datatree;
import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.DataFlavor;
import java.awt.dnd.DropTargetDropEvent;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeNode;
import ghidra.app.services.FileImporterService; import ghidra.app.services.FileImporterService;
import ghidra.framework.main.FrontEndTool; import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.model.DomainFolder; import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool;
import ghidra.util.Msg; import ghidra.util.Msg;
import util.CollectionUtils; import util.CollectionUtils;
@@ -33,24 +35,43 @@ import util.CollectionUtils;
* A drag-and-drop handler for trees that is specific to List&ltFile&gt. (see * A drag-and-drop handler for trees that is specific to List&ltFile&gt. (see
* {@link DataFlavor#javaFileListFlavor}). * {@link DataFlavor#javaFileListFlavor}).
*/ */
final class JavaFileListHandler implements DataFlavorHandler { public final class JavaFileListHandler implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
@Override
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
DomainFolder folder = getDomainFolder(destinationNode);
FileImporterService im = tool.getService(FileImporterService.class); @Override
if (im == null) { public void handle(PluginTool tool, Object transferData, DropTargetDropEvent e, DataFlavor f) {
Msg.showError(this, dataTree, "Could Not Import", "Could not find importer service");
FileImporterService importer = tool.getService(FileImporterService.class);
if (importer == null) {
Msg.showError(this, null, "Could Not Import", "Could not find Importer Service");
return; return;
} }
List<File> fileList = CollectionUtils.asList((List<?>) transferData, File.class); DomainFolder folder = tool.getProject().getProjectData().getRootFolder();
doImport(importer, folder, transferData);
}
@Override
public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) {
FileImporterService importer = tool.getService(FileImporterService.class);
if (importer == null) {
Msg.showError(this, dataTree, "Could Not Import", "Could not find Importer Service");
return;
}
DomainFolder folder = getDomainFolder(destinationNode);
doImport(importer, folder, transferData);
}
private void doImport(FileImporterService importer, DomainFolder folder, Object files) {
List<File> fileList = CollectionUtils.asList((List<?>) files, File.class);
if (fileList.size() == 1 && fileList.get(0).isFile()) { if (fileList.size() == 1 && fileList.get(0).isFile()) {
im.importFile(folder, fileList.get(0)); importer.importFile(folder, fileList.get(0));
} }
else { else {
im.importFiles(folder, fileList); importer.importFiles(folder, fileList);
} }
} }
@@ -27,7 +27,6 @@ import java.util.function.Function;
import docking.widgets.tree.GTreeNode; import docking.widgets.tree.GTreeNode;
import ghidra.app.services.FileImporterService; import ghidra.app.services.FileImporterService;
import ghidra.app.util.FileOpenDataFlavorHandler; import ghidra.app.util.FileOpenDataFlavorHandler;
import ghidra.framework.main.FrontEndTool;
import ghidra.framework.model.DomainFolder; import ghidra.framework.model.DomainFolder;
import ghidra.framework.plugintool.PluginTool; import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProvider; import ghidra.framework.plugintool.ServiceProvider;
@@ -38,11 +37,11 @@ import ghidra.util.Msg;
* duty in that it opens files for DataTrees and for Tools (signaled via the interfaces it * duty in that it opens files for DataTrees and for Tools (signaled via the interfaces it
* implements). * implements).
*/ */
public final class LinuxFileUrlHandler implements DataFlavorHandler, FileOpenDataFlavorHandler { public final class LinuxFileUrlHandler implements DataTreeFlavorHandler, FileOpenDataFlavorHandler {
@Override @Override
// This is for the DataFlavorHandler interface for handling node drops in DataTrees // This is for the DataFlavorHandler interface for handling node drops in DataTrees
public void handle(FrontEndTool tool, DataTree dataTree, GTreeNode destinationNode, public void handle(PluginTool tool, DataTree dataTree, GTreeNode destinationNode,
Object transferData, int dropAction) { Object transferData, int dropAction) {
DomainFolder folder = getDomainFolder(destinationNode); DomainFolder folder = getDomainFolder(destinationNode);
@@ -193,11 +193,11 @@ public class DiffUtility extends SimpleDiffUtility {
return otherProgram.getSymbolTable().createExternalLibrary(namespace.getName(), source); return otherProgram.getSymbolTable().createExternalLibrary(namespace.getName(), source);
} }
else if (namespace instanceof GhidraClass) { else if (namespace instanceof GhidraClass) {
return otherProgram.getSymbolTable().createClass(otherParentNamespace, return otherProgram.getSymbolTable()
namespace.getName(), source); .createClass(otherParentNamespace, namespace.getName(), source);
} }
return otherProgram.getSymbolTable().createNameSpace(otherParentNamespace, return otherProgram.getSymbolTable()
namespace.getName(), source); .createNameSpace(otherParentNamespace, namespace.getName(), source);
} }
// /** // /**
@@ -329,11 +329,11 @@ public class DiffUtility extends SimpleDiffUtility {
if (toAddr == null) { if (toAddr == null) {
return null; return null;
} }
return otherProgram.getReferenceManager().getReference(fromAddr, toAddr, return otherProgram.getReferenceManager()
ref.getOperandIndex()); .getReference(fromAddr, toAddr, ref.getOperandIndex());
} }
Reference otherRef = otherProgram.getReferenceManager().getPrimaryReferenceFrom(fromAddr, Reference otherRef = otherProgram.getReferenceManager()
ref.getOperandIndex()); .getPrimaryReferenceFrom(fromAddr, ref.getOperandIndex());
if (otherRef != null && ref.getToAddress().hasSameAddressSpace(otherRef.getToAddress())) { if (otherRef != null && ref.getToAddress().hasSameAddressSpace(otherRef.getToAddress())) {
return otherRef; return otherRef;
} }
@@ -357,11 +357,11 @@ public class DiffUtility extends SimpleDiffUtility {
if (toAddr1 == null) { if (toAddr1 == null) {
return null; return null;
} }
return program.getReferenceManager().getReference(fromAddr1, toAddr1, return program.getReferenceManager()
p2Ref.getOperandIndex()); .getReference(fromAddr1, toAddr1, p2Ref.getOperandIndex());
} }
Reference p1Ref = program.getReferenceManager().getPrimaryReferenceFrom(fromAddr1, Reference p1Ref = program.getReferenceManager()
p2Ref.getOperandIndex()); .getPrimaryReferenceFrom(fromAddr1, p2Ref.getOperandIndex());
if (p1Ref != null && p1Ref.getToAddress().hasSameAddressSpace(p2Ref.getToAddress())) { if (p1Ref != null && p1Ref.getToAddress().hasSameAddressSpace(p2Ref.getToAddress())) {
return p1Ref; return p1Ref;
} }
@@ -385,8 +385,9 @@ public class DiffUtility extends SimpleDiffUtility {
otherAddr = getCompatibleAddress(program, addr, otherProgram); otherAddr = getCompatibleAddress(program, addr, otherProgram);
} }
// FIXME Should this be passing the Namespace? // FIXME Should this be passing the Namespace?
return otherProgram.getExternalManager().addExtLocation(extLoc.getLibraryName(), return otherProgram.getExternalManager()
extLoc.getLabel(), otherAddr, extLoc.getSource()); .addExtLocation(extLoc.getLibraryName(), extLoc.getLabel(), otherAddr,
extLoc.getSource());
} }
/** /**
@@ -707,6 +708,9 @@ public class DiffUtility extends SimpleDiffUtility {
Address refAddress = getCompatibleAddress(program, location.refAddr, otherProgram); Address refAddress = getCompatibleAddress(program, location.refAddr, otherProgram);
if (address != null) { if (address != null) {
if (byteAddress == null) {
byteAddress = address; // Make sure the byte address isn't null.
}
ProgramLocation otherLocation = new ProgramLocation(otherProgram, address, byteAddress, ProgramLocation otherLocation = new ProgramLocation(otherProgram, address, byteAddress,
location.getComponentPath(), refAddress, 0, 0, 0); location.getComponentPath(), refAddress, 0, 0, 0);
return otherLocation; return otherLocation;
@@ -2555,7 +2555,7 @@ public class ProgramDiffDetails {
} }
private void addColorAddress(StyledDocument doc, Address addr) { private void addColorAddress(StyledDocument doc, Address addr) {
String text = addr.toString(); String text = (addr != null) ? addr.toString() : "no matching address";
color(ADDRESS_COLOR); color(ADDRESS_COLOR);
try { try {
doc.insertString(doc.getLength(), text, textAttrSet); doc.insertString(doc.getLength(), text, textAttrSet);
@@ -43,7 +43,7 @@ public class SymbolicPropogator {
// 1. How are "register-relative" varnodes distinguished based upon target space ? Not sure how we handle wrapping/truncation concerns. // 1. How are "register-relative" varnodes distinguished based upon target space ? Not sure how we handle wrapping/truncation concerns.
// 1) The offset is the only thing that could be used as a reference. // 1) The offset is the only thing that could be used as a reference.
private static final int _POINTER_MIN_BOUNDS = 0x7fff; private static final int _POINTER_MIN_BOUNDS = 0x100;
// mask for sub-piece extraction // mask for sub-piece extraction
private static long[] maskSize = { 0xffL, 0xffL, 0xffffL, 0xffffffL, 0xffffffffL, 0xffffffffffL, private static long[] maskSize = { 0xffL, 0xffL, 0xffffL, 0xffffffL, 0xffffffffL, 0xffffffffffL,
@@ -1836,7 +1836,7 @@ public class SymbolicPropogator {
// see if the offset is a large constant offset from the symbolic space // see if the offset is a large constant offset from the symbolic space
long offset = refLocation.getOffset(); long offset = refLocation.getOffset();
if (checkPossibleOffsetAddr(offset)) { if (evaluator != null) {
// symbolic spaces will have the name of the symbolic space be the register space // symbolic spaces will have the name of the symbolic space be the register space
// String spaceName = refLocation.getAddress().getAddressSpace().getName(); // String spaceName = refLocation.getAddress().getAddressSpace().getName();
// Register register = vContext.getRegister(spaceName); // Register register = vContext.getRegister(spaceName);
@@ -1850,7 +1850,7 @@ public class SymbolicPropogator {
// } // }
// } else // } else
if (evaluator == null) { if (!vContext.isStackSymbolicSpace(refLocation) && evaluator != null) {
Address constant = program.getAddressFactory().getAddress( Address constant = program.getAddressFactory().getAddress(
(int) targetSpaceID.getOffset(), offset); (int) targetSpaceID.getOffset(), offset);
Address newTarget = evaluator.evaluateConstant(vContext, instruction, Address newTarget = evaluator.evaluateConstant(vContext, instruction,
@@ -2051,7 +2051,7 @@ public class SymbolicPropogator {
*/ */
private int getReferenceSpaceID(Instruction instruction, long offset) { private int getReferenceSpaceID(Instruction instruction, long offset) {
// TODO: this should be passed to the client callback to make the decision // TODO: this should be passed to the client callback to make the decision
if (offset <= 4096 && offset >= -1) { if (offset <= 4 && offset >= -1) {
return -1; // don't make speculative reference to certain offset values return -1; // don't make speculative reference to certain offset values
} }
@@ -312,7 +312,7 @@ public class VarnodeContext implements ProcessorContext {
/** /**
* Return true if this varnode is stored in the symbolic stack space * Return true if this varnode is stored in the symbolic stack space
*/ */
private boolean isStackSymbolicSpace(Varnode varnode) { public boolean isStackSymbolicSpace(Varnode varnode) {
// symbolic spaces are off of a register, find the space // symbolic spaces are off of a register, find the space
AddressSpace regSpace = addrFactory.getAddressSpace(varnode.getSpace()); AddressSpace regSpace = addrFactory.getAddressSpace(varnode.getSpace());
@@ -785,7 +785,9 @@ public class VarnodeContext implements ProcessorContext {
* return the location that this register was last set * return the location that this register was last set
* This is a transient thing, so it should only be used as a particular flow is being processed... * This is a transient thing, so it should only be used as a particular flow is being processed...
* *
* @param reg * @param reg register to find last set location
* @param bval value to look for to differentiate set locations, null if don't care
*
* @return address that the register was set. * @return address that the register was set.
*/ */
public Address getLastSetLocation(Register reg, BigInteger bval) { public Address getLastSetLocation(Register reg, BigInteger bval) {
@@ -1256,6 +1258,13 @@ public class VarnodeContext implements ProcessorContext {
// too big anyway,already extended as far as it will go. // too big anyway,already extended as far as it will go.
vnodeVal = createConstantVarnode(vnodeVal.getOffset(), out.getSize()); vnodeVal = createConstantVarnode(vnodeVal.getOffset(), out.getSize());
} }
} else if (vnodeVal.isRegister() && vnodeVal.getSize() < out.getSize()) {
Register reg = getRegister(vnodeVal);
if (reg == null) {
throw notFoundExc;
}
int spaceID = getAddressSpace(reg.getName());
vnodeVal = createVarnode(0,spaceID,out.getSize());
} }
return vnodeVal; return vnodeVal;
} }
@@ -623,7 +623,7 @@ public class OptionsTest extends AbstractGenericTest {
@Override @Override
public int hashCode() { public int hashCode() {
return 1;// set so that this listener gets called after the storingOptionsListnere return 1;// set so that this listener gets called after the storingOptionsListener
} }
} }
@@ -15,8 +15,6 @@
*/ */
package ghidra.app.plugin.core.byteviewer; package ghidra.app.plugin.core.byteviewer;
import ghidra.util.ColorUtils;
import java.awt.*; import java.awt.*;
import java.math.BigInteger; import java.math.BigInteger;
@@ -28,6 +26,7 @@ import docking.widgets.fieldpanel.internal.FieldBackgroundColorManager;
import docking.widgets.fieldpanel.internal.PaintContext; import docking.widgets.fieldpanel.internal.PaintContext;
import docking.widgets.fieldpanel.support.HighlightFactory; import docking.widgets.fieldpanel.support.HighlightFactory;
import docking.widgets.fieldpanel.support.RowColLocation; import docking.widgets.fieldpanel.support.RowColLocation;
import ghidra.util.ColorUtils;
/** /**
* Fields for the ByteViewer. This class extends the SimpleTextField to include * Fields for the ByteViewer. This class extends the SimpleTextField to include
@@ -52,7 +51,8 @@ public class ByteField extends SimpleTextField {
* @param hlFactory the factory used to create highlights * @param hlFactory the factory used to create highlights
*/ */
public ByteField(String text, FontMetrics fontMetrics, int startX, int width, public ByteField(String text, FontMetrics fontMetrics, int startX, int width,
boolean allowCursorAtEnd, int fieldOffset, BigInteger index, HighlightFactory hlFactory) { boolean allowCursorAtEnd, int fieldOffset, BigInteger index,
HighlightFactory hlFactory) {
super(text, fontMetrics, startX, width, allowCursorAtEnd, hlFactory); super(text, fontMetrics, startX, width, allowCursorAtEnd, hlFactory);
this.fieldOffset = fieldOffset; this.fieldOffset = fieldOffset;
@@ -64,7 +64,7 @@ public class ByteField extends SimpleTextField {
public void paint(JComponent c, Graphics g, PaintContext context, public void paint(JComponent c, Graphics g, PaintContext context,
FieldBackgroundColorManager colorManager, RowColLocation cursorLoc, int rowHeight) { FieldBackgroundColorManager colorManager, RowColLocation cursorLoc, int rowHeight) {
paintSelection(g, colorManager, 0); paintSelection(g, colorManager, 0);
paintHighlights(g, hlFactory.getHighlights(text, -1)); paintHighlights(g, hlFactory.getHighlights(this, text, -1));
g.setFont(metrics.getFont()); g.setFont(metrics.getFont());
if (foregroundColor == null) { if (foregroundColor == null) {
foregroundColor = context.getForeground(); foregroundColor = context.getForeground();
@@ -213,7 +213,7 @@ class FieldFactory {
} }
@Override @Override
public Highlight[] getHighlights(String text, int cursorTextOffset) { public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
return provider.getHighlights(text, null, null, -1); return provider.getHighlights(text, null, null, -1);
} }
} }
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,8 +15,6 @@
*/ */
package ghidra.app.plugin.core.byteviewer; package ghidra.app.plugin.core.byteviewer;
import ghidra.app.plugin.core.format.ByteBlockInfo;
import java.awt.Color; import java.awt.Color;
import java.awt.FontMetrics; import java.awt.FontMetrics;
import java.math.BigInteger; import java.math.BigInteger;
@@ -26,6 +23,7 @@ import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.field.SimpleTextField; import docking.widgets.fieldpanel.field.SimpleTextField;
import docking.widgets.fieldpanel.support.Highlight; import docking.widgets.fieldpanel.support.Highlight;
import docking.widgets.fieldpanel.support.HighlightFactory; import docking.widgets.fieldpanel.support.HighlightFactory;
import ghidra.app.plugin.core.format.ByteBlockInfo;
/** /**
* Implementation for the index/address field. * Implementation for the index/address field.
@@ -145,7 +143,7 @@ class IndexFieldFactory {
} }
@Override @Override
public Highlight[] getHighlights(String text, int cursorTextOffset) { public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
return NO_HIGHLIGHTS; return NO_HIGHLIGHTS;
} }
} }
@@ -7333,8 +7333,12 @@ bool RuleConditionalMove::BoolExpress::evaluatePropagation(FlowBlock *root,FlowB
if (root == branch) return true; // Can always propagate if there is no branch if (root == branch) return true; // Can always propagate if there is no branch
if (op->getParent() != branch) return true; // Can propagate if value formed before branch if (op->getParent() != branch) return true; // Can propagate if value formed before branch
mustreconstruct = true; // Final op is performed in branch, so it must be reconstructed mustreconstruct = true; // Final op is performed in branch, so it must be reconstructed
if (in0->isFree() && !in0->isConstant()) return false;
if (in0->isWritten() && (in0->getDef()->getParent()==branch)) return false; if (in0->isWritten() && (in0->getDef()->getParent()==branch)) return false;
if ((optype==2) && in1->isWritten() && (in1->getDef()->getParent()==branch)) return false; if (optype == 2) {
if (in1->isFree() && !in1->isConstant()) return false;
if (in1->isWritten() && (in1->getDef()->getParent()==branch)) return false;
}
return true; return true;
} }
@@ -1,5 +1,6 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -197,4 +198,8 @@ public class ClangTextField extends WrappingVerticalLayoutTextField {
return lineNumberFieldElement.getStringWidth(); return lineNumberFieldElement.getStringWidth();
} }
public int getLineNumber() {
String text = lineNumberFieldElement.getText().trim();
return Integer.parseInt(text);
}
} }
@@ -76,8 +76,9 @@ public class DecompilerHoverProvider extends AbstractHoverProvider {
Varnode vn = token.getVarnode(); Varnode vn = token.getVarnode();
if (vn != null) { if (vn != null) {
if (vn.getHigh() instanceof HighGlobal) { HighVariable highVar = vn.getHigh();
reference = vn.getAddress(); if (highVar instanceof HighGlobal) {
reference = highVar.getRepresentative().getAddress();
} }
} }
@@ -51,7 +51,7 @@ public class DecompilerManager {
this.decompilerController = decompilerController; this.decompilerController = decompilerController;
runManager = new RunManager("Decompiler", null); runManager = new RunManager("Decompiler", null);
decompiler = new Decompiler(options, options.getDefaultTimeout()); decompiler = new Decompiler(options, 0);
updateManager = new SwingUpdateManager(500, () -> doPendingDecompile()); updateManager = new SwingUpdateManager(500, () -> doPendingDecompile());
} }
@@ -37,11 +37,11 @@ import docking.widgets.indexedscrollpane.IndexedScrollPane;
import ghidra.app.decompiler.*; import ghidra.app.decompiler.*;
import ghidra.app.decompiler.component.hover.DecompilerHoverService; import ghidra.app.decompiler.component.hover.DecompilerHoverService;
import ghidra.app.plugin.core.decompile.DecompileClipboardProvider; import ghidra.app.plugin.core.decompile.DecompileClipboardProvider;
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.pcode.PcodeOp; import ghidra.program.model.pcode.*;
import ghidra.program.model.pcode.Varnode;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.program.util.ProgramSelection; import ghidra.program.util.ProgramSelection;
import ghidra.util.*; import ghidra.util.*;
@@ -175,6 +175,9 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
if (clipboard != null) { if (clipboard != null) {
clipboard.selectionChanged(null); clipboard.selectionChanged(null);
} }
// don't highlight search results across functions
currentSearchLocation = null;
} }
private void setLocation(DecompileData oldData, DecompileData newData) { private void setLocation(DecompileData oldData, DecompileData newData) {
@@ -419,6 +422,10 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
} }
} }
HighVariable highVar = vn.getHigh();
if (highVar instanceof HighGlobal) {
vn = highVar.getRepresentative();
}
if (vn.isAddress()) { if (vn.isAddress()) {
Address addr = vn.getAddress(); Address addr = vn.getAddress();
if (addr.isMemoryAddress()) { if (addr.isMemoryAddress()) {
@@ -696,9 +703,21 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
} }
class SearchHighlightFactory implements HighlightFactory { class SearchHighlightFactory implements HighlightFactory {
@Override @Override
public Highlight[] getHighlights(String text, int cursorTextOffset) { public Highlight[] getHighlights(Field field, String text, int cursorTextOffset) {
if (currentSearchLocation == null || cursorTextOffset == -1) { if (currentSearchLocation == null) {
return new Highlight[0];
}
ClangTextField cField = (ClangTextField) field;
int highlightLine = cField.getLineNumber();
FieldLocation searchCursorLocation =
((FieldBasedSearchLocation) currentSearchLocation).getFieldLocation();
int searchLineNumber = searchCursorLocation.getIndex().intValue() + 1;
if (highlightLine != searchLineNumber) {
// only highlight the match on the actual line
return new Highlight[0]; return new Highlight[0];
} }
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -39,4 +38,9 @@ public class FieldBasedSearchLocation extends SearchLocation {
public CursorPosition getCursorPosition() { public CursorPosition getCursorPosition() {
return new DecompilerCursorPosition(fieldLocation); return new DecompilerCursorPosition(fieldLocation);
} }
@Override
protected String fieldsToString() {
return super.fieldsToString() + ", fieldLocation=" + fieldLocation;
}
} }
@@ -1,5 +1,6 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -77,13 +78,20 @@ public class FindAction extends DockingAction {
if (text != null) { if (text != null) {
dialog.setSearchText(text); dialog.setSearchText(text);
} }
// show over the root frame, so the user can still see the Decompiler window // show over the root frame, so the user can still see the Decompiler window
tool.showDialog(dialog); tool.showDialog(dialog);
} }
protected FindDialog getFindDialog() { protected FindDialog getFindDialog() {
if (findDialog == null) { if (findDialog == null) {
findDialog = new FindDialog("Decompiler Find Text", new DecompilerSearcher()); findDialog = new FindDialog("Decompiler Find Text", new DecompilerSearcher()) {
@Override
protected void dialogClosed() {
// clear the search results when the dialog is closed
decompilerPanel.setSearchResults(null);
}
};
findDialog.setHelpLocation(new HelpLocation("DecompilePlugin", "Find")); findDialog.setHelpLocation(new HelpLocation("DecompilePlugin", "Find"));
} }
return findDialog; return findDialog;
@@ -441,6 +441,9 @@ public class ProgramDiffPlugin extends ProgramPlugin
Address primaryByteAddr = SimpleDiffUtility.getCompatibleAddress(secondaryDiffProgram, Address primaryByteAddr = SimpleDiffUtility.getCompatibleAddress(secondaryDiffProgram,
byteAddr, primaryProgram); byteAddr, primaryProgram);
if (primaryByteAddr == null) {
primaryByteAddr = primaryAddr; // Make sure the byte address isn't null.
}
Address primaryRefAddr = SimpleDiffUtility.getCompatibleAddress(secondaryDiffProgram, Address primaryRefAddr = SimpleDiffUtility.getCompatibleAddress(secondaryDiffProgram,
refAddr, primaryProgram); refAddr, primaryProgram);
ProgramLocation newP1Location = new ProgramLocation(primaryProgram, primaryAddr, ProgramLocation newP1Location = new ProgramLocation(primaryProgram, primaryAddr,
@@ -463,7 +466,9 @@ public class ProgramDiffPlugin extends ProgramPlugin
} }
ProgramLocation previousP1LocationAsP2 = DiffUtility ProgramLocation previousP1LocationAsP2 = DiffUtility
.getCompatibleProgramLocation(primaryProgram, location, secondaryDiffProgram); .getCompatibleProgramLocation(primaryProgram, location, secondaryDiffProgram);
diffListingPanel.setCursorPosition(previousP1LocationAsP2); if (previousP1LocationAsP2 != null) {
diffListingPanel.setCursorPosition(previousP1LocationAsP2);
}
if (diffDetailsProvider != null && diffDetails != null) { if (diffDetailsProvider != null && diffDetails != null) {
diffDetailsProvider.locationChanged(previousP1Location); diffDetailsProvider.locationChanged(previousP1Location);
} }
@@ -749,7 +754,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
runSwing(() -> { runSwing(() -> {
MarkerSet selectionMarkers = getSelectionMarkers(); MarkerSet selectionMarkers = getSelectionMarkers();
selectionMarkers.clearAll(); selectionMarkers.clearAll();
selectionMarkers.add(p2SelectionAsP1); selectionMarkers.add(p2Selection);
}); });
diffListingPanel.setSelection(p2SelectionAsP1); diffListingPanel.setSelection(p2SelectionAsP1);
@@ -798,9 +803,9 @@ public class ProgramDiffPlugin extends ProgramPlugin
// Limit the apply to the selection in the view. // Limit the apply to the selection in the view.
AddressSet p2SelectionAsP1 = AddressSet p2SelectionAsP1 =
DiffUtility.getCompatibleAddressSet(p2Selection, primaryProgram); DiffUtility.getCompatibleAddressSet(p2Selection, primaryProgram);
AddressSet p1ApplySet = AddressSet p1ApplySet = p2SelectionAsP1.intersect(p1ViewAddrSet)
p2SelectionAsP1.intersect(p1ViewAddrSet).subtract(addressesOnlyInP1).subtract( .subtract(addressesOnlyInP1)
compatibleOnlyInP2); .subtract(compatibleOnlyInP2);
if (p1ApplySet.isEmpty()) { if (p1ApplySet.isEmpty()) {
Msg.showInfo(getClass(), tool.getToolFrame(), "Apply Differences", Msg.showInfo(getClass(), tool.getToolFrame(), "Apply Differences",
(p2Selection.isEmpty()) ? "No diff selection in the current view." (p2Selection.isEmpty()) ? "No diff selection in the current view."
@@ -860,7 +865,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
// Right side markers need p1 addresses since they use p1 indexMap. // Right side markers need p1 addresses since they use p1 indexMap.
MarkerSet diffMarkers = getDiffMarkers(); // Get right side markers for program 2. MarkerSet diffMarkers = getDiffMarkers(); // Get right side markers for program 2.
diffMarkers.clearAll(); diffMarkers.clearAll();
diffMarkers.add(p2DiffSetAsP1); diffMarkers.add(p2DiffSet);
MarkerSet codeViewerDiffMarkers = getCodeViewerMarkers(); // Get left side markers for program 1. MarkerSet codeViewerDiffMarkers = getCodeViewerMarkers(); // Get left side markers for program 1.
codeViewerDiffMarkers.clearAll(); codeViewerDiffMarkers.clearAll();
@@ -884,7 +889,7 @@ public class ProgramDiffPlugin extends ProgramPlugin
AddressSet p1DiffHighlightSet = AddressSet p1DiffHighlightSet =
DiffUtility.getCompatibleAddressSet(p2Highlight, primaryProgram); DiffUtility.getCompatibleAddressSet(p2Highlight, primaryProgram);
p2DiffHighlight = p2Highlight; p2DiffHighlight = p2Highlight;
diffMarkers.add(p1DiffHighlightSet); diffMarkers.add(p2Highlight);
codeViewerDiffMarkers.add(p1DiffHighlightSet); codeViewerDiffMarkers.add(p1DiffHighlightSet);
} }
@@ -1603,7 +1608,14 @@ public class ProgramDiffPlugin extends ProgramPlugin
diffListingPanel.goTo(currentLocation); diffListingPanel.goTo(currentLocation);
MarkerSet cursorMarkers = getCursorMarkers(); MarkerSet cursorMarkers = getCursorMarkers();
cursorMarkers.setAddressSet(new AddressSet(currentLocation.getAddress())); Address currentP2Address = currentLocation.getAddress();
if (currentLocation.getProgram() != secondaryDiffProgram) { // Make sure address is from P2.
currentP2Address = SimpleDiffUtility.getCompatibleAddress(currentLocation.getProgram(),
currentLocation.getAddress(), secondaryDiffProgram);
}
if (currentP2Address != null) {
cursorMarkers.setAddressSet(new AddressSet(currentP2Address));
}
updatePgm2Enablement(); updatePgm2Enablement();
@@ -47,7 +47,12 @@ public class ActionContext {
} }
/** /**
* For Testing * Constructor
*
* @param provider the ComponentProvider that generated this context.
* @param contextObject an optional contextObject that the ComponentProvider can provide
* @param sourceObject an optional source object; this can be anything that actions wish to
* later retrieve
*/ */
public ActionContext(ComponentProvider provider, Object contextObject, Object sourceObject) { public ActionContext(ComponentProvider provider, Object contextObject, Object sourceObject) {
this(provider, contextObject); this(provider, contextObject);
@@ -55,8 +60,8 @@ public class ActionContext {
} }
/** /**
* Returns the {@link #ComponentProvider} that generated this ActionContext * Returns the {@link ComponentProvider} that generated this ActionContext
* @return * @return the provider
*/ */
public ComponentProvider getComponentProvider() { public ComponentProvider getComponentProvider() {
return provider; return provider;
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -60,6 +59,10 @@ public class SearchLocation {
@Override @Override
public String toString() { public String toString() {
return searchText + "[start=" + startIndexInclusive + ", end=" + endIndexInclusive + "]"; return searchText + "[" + fieldsToString() + "]";
}
protected String fieldsToString() {
return startIndexInclusive + ", end=" + endIndexInclusive;
} }
} }
@@ -63,7 +63,7 @@ public class ObjectChooserDialog<T> extends DialogComponentProvider {
table.addSelectionListener(t -> objectSelected(t)); table.addSelectionListener(t -> objectSelected(t));
table.setItemPickListner(t -> objectPicked(t)); table.setItemPickListener(t -> objectPicked(t));
return table; return table;
} }
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -129,8 +128,8 @@ public class ClippingTextField implements TextField {
int x = findX(col) + startX; int x = findX(col) + startX;
return new Rectangle(x, -textElement.getHeightAbove(), 2, textElement.getHeightAbove() + return new Rectangle(x, -textElement.getHeightAbove(), 2,
textElement.getHeightBelow()); textElement.getHeightAbove() + textElement.getHeightBelow());
} }
/** /**
@@ -315,7 +314,7 @@ public class ClippingTextField implements TextField {
if (cursorLoc != null) { if (cursorLoc != null) {
cursorTextOffset = screenLocationToTextOffset(cursorLoc.row(), cursorLoc.col()); cursorTextOffset = screenLocationToTextOffset(cursorLoc.row(), cursorLoc.col());
} }
paintHighlights(g, hlFactory.getHighlights(getString(), cursorTextOffset)); paintHighlights(g, hlFactory.getHighlights(this, getString(), cursorTextOffset));
} }
protected void paintSelection(Graphics g, FieldBackgroundColorManager colorManager, int row, protected void paintSelection(Graphics g, FieldBackgroundColorManager colorManager, int row,
@@ -344,10 +343,10 @@ public class ClippingTextField implements TextField {
} }
protected void paintHighlights(Graphics g, Highlight[] highlights) { protected void paintHighlights(Graphics g, Highlight[] highlights) {
for (int i = 0; i < highlights.length; i++) { for (Highlight highlight : highlights) {
int startCol = Math.max(highlights[i].getStart(), 0); int startCol = Math.max(highlight.getStart(), 0);
int endCol = Math.min(highlights[i].getEnd(), getString().length()); int endCol = Math.min(highlight.getEnd(), getString().length());
Color c = highlights[i].getColor(); Color c = highlight.getColor();
if (endCol >= startCol) { if (endCol >= startCol) {
int start = findX(startCol); int start = findX(startCol);
int end = findX(endCol + 1); int end = findX(endCol + 1);
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -142,8 +141,8 @@ public class ReverseClippingTextField implements TextField {
int x = findX(col) + textStartX; int x = findX(col) + textStartX;
return new Rectangle(x, -textElement.getHeightAbove(), 2, textElement.getHeightAbove() + return new Rectangle(x, -textElement.getHeightAbove(), 2,
textElement.getHeightBelow()); textElement.getHeightAbove() + textElement.getHeightBelow());
} }
/** /**
@@ -336,8 +335,7 @@ public class ReverseClippingTextField implements TextField {
private void paintDots(Graphics g, int x) { private void paintDots(Graphics g, int x) {
int pos = 1; // skip one pixel int pos = 1; // skip one pixel
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
if (pos < DOT_DOT_DOT_WIDTH - 2) { // don't paint too close to next if (pos < DOT_DOT_DOT_WIDTH - 2) { // don't paint too close to next field
// field.
g.drawRect(x + pos, -2, 1, 1); g.drawRect(x + pos, -2, 1, 1);
pos += 4; // add in size of dot and padding pos += 4; // add in size of dot and padding
} }
@@ -349,14 +347,14 @@ public class ReverseClippingTextField implements TextField {
if (cursorLoc != null) { if (cursorLoc != null) {
cursorTextOffset = screenLocationToTextOffset(cursorLoc.row(), cursorLoc.col()); cursorTextOffset = screenLocationToTextOffset(cursorLoc.row(), cursorLoc.col());
} }
paintHighlights(g, hlFactory.getHighlights(getString(), cursorTextOffset)); paintHighlights(g, hlFactory.getHighlights(this, getString(), cursorTextOffset));
} }
protected void paintHighlights(Graphics g, Highlight[] highlights) { protected void paintHighlights(Graphics g, Highlight[] highlights) {
for (int i = 0; i < highlights.length; i++) { for (Highlight highlight : highlights) {
int startCol = Math.max(highlights[i].getStart() - startingCharIndex, 0); int startCol = Math.max(highlight.getStart() - startingCharIndex, 0);
int endCol = Math.min(highlights[i].getEnd() - startingCharIndex, getString().length()); int endCol = Math.min(highlight.getEnd() - startingCharIndex, getString().length());
Color c = highlights[i].getColor(); Color c = highlight.getColor();
if (endCol >= startCol) { if (endCol >= startCol) {
int start = findX(startCol); int start = findX(startCol);
int end = findX(endCol + 1); int end = findX(endCol + 1);
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -202,7 +201,7 @@ public class SimpleTextField implements Field {
public void paint(JComponent c, Graphics g, PaintContext context, public void paint(JComponent c, Graphics g, PaintContext context,
FieldBackgroundColorManager colorManager, RowColLocation cursorLoc, int rowHeight) { FieldBackgroundColorManager colorManager, RowColLocation cursorLoc, int rowHeight) {
paintSelection(g, colorManager, 0); paintSelection(g, colorManager, 0);
paintHighlights(g, hlFactory.getHighlights(text, -1)); paintHighlights(g, hlFactory.getHighlights(this, text, -1));
g.setFont(metrics.getFont()); g.setFont(metrics.getFont());
if (foregroundColor == null) { if (foregroundColor == null) {
foregroundColor = context.getForeground(); foregroundColor = context.getForeground();
@@ -226,10 +225,10 @@ public class SimpleTextField implements Field {
} }
protected void paintHighlights(Graphics g, Highlight[] highlights) { protected void paintHighlights(Graphics g, Highlight[] highlights) {
for (int i = 0; i < highlights.length; i++) { for (Highlight highlight : highlights) {
int startCol = Math.max(highlights[i].getStart(), 0); int startCol = Math.max(highlight.getStart(), 0);
int endCol = Math.min(highlights[i].getEnd(), text.length()); int endCol = Math.min(highlight.getEnd(), text.length());
Color c = highlights[i].getColor(); Color c = highlight.getColor();
if (endCol >= startCol) { if (endCol >= startCol) {
int start = findX(startCol); int start = findX(startCol);
int end = findX(endCol + 1); int end = findX(endCol + 1);
@@ -268,7 +268,7 @@ public class VerticalLayoutTextField implements TextField {
cursorRow = cursorLoc.row(); cursorRow = cursorLoc.row();
} }
Highlight[] highlights = hlFactory.getHighlights(getText(), cursorTextOffset); Highlight[] highlights = hlFactory.getHighlights(this, getText(), cursorTextOffset);
int columns = 0; int columns = 0;
int n = subFields.size(); int n = subFields.size();
Color fieldBackgroundColor = colorManager.getBackgroundColor(); Color fieldBackgroundColor = colorManager.getBackgroundColor();
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -17,8 +16,6 @@
package docking.widgets.fieldpanel.internal; package docking.widgets.fieldpanel.internal;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
@@ -33,11 +30,8 @@ import docking.widgets.indexedscrollpane.IndexedScrollPane;
public class TestBigLayoutModel implements LayoutModel { public class TestBigLayoutModel implements LayoutModel {
private static final Highlight[] NO_HIGHLIGHTS = new Highlight[0]; private static final Highlight[] NO_HIGHLIGHTS = new Highlight[0];
private static final HighlightFactory hlFactory = new HighlightFactory() { private static final HighlightFactory hlFactory =
public Highlight[] getHighlights(String text, int cursorTextOffset) { (field, text, cursorTextOffset) -> NO_HIGHLIGHTS;
return NO_HIGHLIGHTS;
}
};
ArrayList<LayoutModelListener> listeners = new ArrayList<LayoutModelListener>(); ArrayList<LayoutModelListener> listeners = new ArrayList<LayoutModelListener>();
FontMetrics fm; FontMetrics fm;
@@ -86,15 +80,13 @@ public class TestBigLayoutModel implements LayoutModel {
if (index.compareTo(numIndexes) >= 0) { if (index.compareTo(numIndexes) >= 0) {
return null; return null;
} }
String text = String text = name + ": This is line " + index +
name + ": This is line " + index + " More text to make line longer abcdefghijklmnopqrstuvwxyzabcdefghijk";
" More text to make line longer abcdefghijklmnopqrstuvwxyzabcdefghijk";
FieldElement fe1 = new TextFieldElement(new AttributedString(text, Color.BLACK, fm), 0, 0); FieldElement fe1 = new TextFieldElement(new AttributedString(text, Color.BLACK, fm), 0, 0);
FieldElement fe2 = FieldElement fe2 =
new TextFieldElement(new AttributedString("More text", Color.BLACK, fm), 0, 0); new TextFieldElement(new AttributedString("More text", Color.BLACK, fm), 0, 0);
SingleRowLayout layout = SingleRowLayout layout = new SingleRowLayout(new ClippingTextField(20, 300, fe1, hlFactory),
new SingleRowLayout(new ClippingTextField(20, 300, fe1, hlFactory), new ClippingTextField(330, 100, fe2, hlFactory));
new ClippingTextField(330, 100, fe2, hlFactory));
if (index.intValue() >= startBigSizes && index.intValue() <= endBigSizes) { if (index.intValue() >= startBigSizes && index.intValue() <= endBigSizes) {
layout.insertSpaceAbove(30); layout.insertSpaceAbove(30);
@@ -143,12 +135,7 @@ public class TestBigLayoutModel implements LayoutModel {
contentPane.setLayout(new BorderLayout()); contentPane.setLayout(new BorderLayout());
contentPane.add(scrollPanel); contentPane.add(scrollPanel);
JButton button = new JButton("Hit Me"); JButton button = new JButton("Hit Me");
button.addActionListener(new ActionListener() { button.addActionListener(e -> model.updateData(1000, 2000));
@Override
public void actionPerformed(ActionEvent e) {
model.updateData(1000, 2000);
}
});
contentPane.add(button, BorderLayout.SOUTH); contentPane.add(button, BorderLayout.SOUTH);
frame.pack(); frame.pack();
frame.setVisible(true); frame.setVisible(true);
@@ -1,6 +1,5 @@
/* ### /* ###
* IP: GHIDRA * IP: GHIDRA
* REVIEWED: YES
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,13 +15,18 @@
*/ */
package docking.widgets.fieldpanel.support; package docking.widgets.fieldpanel.support;
import docking.widgets.fieldpanel.field.Field;
public interface HighlightFactory { public interface HighlightFactory {
/** /**
* Returns the highlights for the given text. * Returns the highlights for the given text
* @param text the text to be considered for highlighting. *
* @param field the field that is requesting the highlight
* @param text the text to be considered for highlighting
* @param cursorTextOffset the position in the given text of the cursor. A -1 indicates the * @param cursorTextOffset the position in the given text of the cursor. A -1 indicates the
* cursor is not in this field. * cursor is not in this field.
* @return an array of highlights to be rendered. * @return an array of highlights to be rendered
*/ */
public Highlight[] getHighlights(String text, int cursorTextOffset); public Highlight[] getHighlights(Field field, String text, int cursorTextOffset);
} }
@@ -133,7 +133,7 @@ public class GTableWidget<T> extends JPanel {
listener.itemPicked(gFilterTable.getSelectedRowObject()); listener.itemPicked(gFilterTable.getSelectedRowObject());
} }
public void setItemPickListner(TableItemPickedListener<T> listener) { public void setItemPickListener(TableItemPickedListener<T> listener) {
this.listener = listener; this.listener = listener;
} }
@@ -867,7 +867,7 @@ public class GTree extends JPanel implements BusyListener {
tree.getModel().addTreeModelListener(listener); tree.getModel().addTreeModelListener(listener);
} }
public void removeGTModelListner(TreeModelListener listener) { public void removeGTModelListener(TreeModelListener listener) {
tree.getModel().removeTreeModelListener(listener); tree.getModel().removeTreeModelListener(listener);
} }

Some files were not shown because too many files have changed in this diff Show More