GP-5196 Adding support for expressions and symbol names in address fields
|
@ -398,6 +398,7 @@ src/main/help/help/topics/MemoryMapPlugin/images/MemoryMap.png||GHIDRA||||END|
|
|||
src/main/help/help/topics/MemoryMapPlugin/images/MoveMemory.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/MemoryMapPlugin/images/SetImageBaseDialog.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/MemoryMapPlugin/images/SplitMemoryBlock.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/Misc/AddressExpressions.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/Misc/Appendix.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/Misc/Welcome_to_Ghidra_Help.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/Navigation/Navigation.htm||GHIDRA||||END|
|
||||
|
|
|
@ -196,6 +196,8 @@
|
|||
<!-- Formerly The Byte Viewer -->
|
||||
|
||||
<tocdef id="Program Annotation" text="Program Annotation" target="help/topics/ProgramManagerPlugin/Program_Annotation.htm">
|
||||
<tocdef id="Address Expressions" sortgroup="a" text="Address Expressions" target="help/topics/Misc/AddressExpressions.htm" />
|
||||
|
||||
<tocdef id="Auto Analysis" sortgroup="a" text="Auto Analysis" target="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm" >
|
||||
<tocdef id="Forcing Analysis" sortgroup="a" text="Forcing Analysis" target="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm#Auto_Analyze" />
|
||||
<tocdef id="Auto Analysis Options Panel" sortgroup="b" text="Auto Analysis Options Panel" target="help/topics/AutoAnalysisPlugin/AutoAnalysis.htm#Auto_Analysis_Option" />
|
||||
|
|
|
@ -55,8 +55,9 @@
|
|||
|
||||
<LI>Select the <B>User</B> radio button.</LI>
|
||||
|
||||
<LI>Enter an address, or click in the Code Browser at the address of the new
|
||||
fallthrough.</LI>
|
||||
<LI>Enter an address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>), or click in
|
||||
the Code Browser at the address of the new fallthrough.</LI>
|
||||
|
||||
<LI>Select the <B>Apply</B> button to change the fallthrough and leave the dialog intact;
|
||||
select the OK button to change the fallthrough and dismiss the dialog.</LI>
|
||||
|
|
|
@ -243,8 +243,10 @@
|
|||
|
||||
<P><B><I>Block Name</I></B> - Enter the name of the new memory block.</P>
|
||||
|
||||
<P><B><I>Start Addr</I></B> - Enter the start address of the new memory block. If the
|
||||
program language defines multiple address spaces, the address space must also be
|
||||
<P><B><I>Start Address</I></B> - Enter the start address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>)
|
||||
of the new memory block. If
|
||||
the program language defines multiple address spaces, the address space must also be
|
||||
specified. The address space selection will not appear if only one is defined.
|
||||
If creating an overlay memory block within an existing overlay address space that
|
||||
space should be selected. A block within the default address space may not span across
|
||||
|
@ -378,11 +380,14 @@
|
|||
|
||||
<P><B><I>Length -</I></B> Length of the memory block to be moved (not editable).</P>
|
||||
|
||||
<P><B><I>New Start Address -</I></B> Enter the NEW starting address for the block.
|
||||
<P><B><I>New Start Address -</I></B> Enter the new starting address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>)
|
||||
for the block.
|
||||
The NEW ending address will be computed.</P>
|
||||
|
||||
<P><B><I>New End Address - </I></B> Enter the NEW ending address for the block. The
|
||||
NEW starting address will be computed.</P>
|
||||
<P><B><I>New End Address - </I></B> Enter the new ending address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>)
|
||||
for the block. The NEW starting address will be computed.</P>
|
||||
|
||||
<P><I><IMG src="help/shared/note.png" border="0"> You cannot move a block under the
|
||||
following conditions:</I></P>
|
||||
|
@ -404,11 +409,15 @@
|
|||
enter the split point:</P>
|
||||
|
||||
<UL>
|
||||
<LI>Enter an end address for the first block (block to split), or</LI>
|
||||
<LI>Enter an end address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>)
|
||||
for the first block (block to split), or</LI>
|
||||
|
||||
<LI>Enter a length for the first block (block to split), or</LI>
|
||||
|
||||
<LI>Enter a start address for the second block (new block), or</LI>
|
||||
<LI>Enter a start address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>)
|
||||
for the second block (new block), or</LI>
|
||||
|
||||
<LI>Enter a length for the second block (new block).</LI>
|
||||
</UL>
|
||||
|
@ -482,7 +491,9 @@
|
|||
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><B><I>New Start Address -</I></B> A new start address can be entered here. It must
|
||||
<P><B><I>New Start Address -</I></B> A new start address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>)
|
||||
can be entered here. It must
|
||||
be before the current start address.</P>
|
||||
|
||||
<P><I><B>End Address -</B></I> Displays the end address of the block (not editable).</P>
|
||||
|
@ -521,7 +532,9 @@
|
|||
<P><I><B>Start Address -</B></I> Displays the start address of the block (not
|
||||
editable).</P>
|
||||
|
||||
<P><B><I>New End Address -</I></B> A new end address can be entered here. It must be after
|
||||
<P><B><I>New End Address -</I></B> A new end address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>)
|
||||
can be entered here. It must be after
|
||||
the current end address.</P>
|
||||
|
||||
<P><I><B>Block Length -</B></I> Displays the length of the block. A new value can be
|
||||
|
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 8.4 KiB |
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,175 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META name="generator" content=
|
||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||
|
||||
<TITLE>Address Expressions</TITLE>
|
||||
<LINK rel="stylesheet" type="text/css" href="help/shared/DefaultStyle.css">
|
||||
</HEAD>
|
||||
|
||||
<BODY>
|
||||
<H1><A name="Address_Expressions"></A>Address Expressions<BR>
|
||||
</H1>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>An address expression is an arithmetic expression that is entered into an address input
|
||||
field and can include symbol names or memory block names that when evaluated results in a
|
||||
program address.</P>
|
||||
|
||||
<H2>Operands</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Operands can be either a number or a name that evaluates to an address.</P>
|
||||
|
||||
<H3>Names</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>If the operand is a name, then an attempt will be made to find a unique label or
|
||||
function name in the current program for that name. If that fails, then memory blocks
|
||||
will be searched looking for a memory block with that name. In either case, the associated
|
||||
address for that label, function, or memory block will be used in evaluating the
|
||||
expression.</P>
|
||||
|
||||
<P>Generally, symbols (addresses) must be the left operand for a binary operator and will
|
||||
generate a address as the result (maintaining the address space). The one exception is
|
||||
you can subtract two addresses and the result is a number.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Numbers</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Numeric operands will be evaluated as either a hex number or decimal number, depending
|
||||
on the <B>Hex/Decimal Mode</B> of the input field.</P>
|
||||
|
||||
<P>When in hex mode, undecorated numbers will be interpreted as if they are hex values.
|
||||
(so "100" would evaluate to the number 256). If in decimal mode, hex numbers can still be
|
||||
entered by prefixing them with "0x".</P>
|
||||
|
||||
<P>For fields that support either mode, the current mode will be displayed as "hint text"
|
||||
in the lower right corner of the field. The mode can be toggled between decimal and hex
|
||||
by pressing <CTRL> M.</P>
|
||||
|
||||
<P>When in hex mode, there is no corresponding prefix to use to specify a number as being
|
||||
decimal. So if you want to have a mixed mode expression, use decimal mode and use the
|
||||
"0x" prefix for any hex numbers in the expression.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Operators</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Most standard operators are supported, but not all operators are supported for all
|
||||
operands. Also order of operands is important when mixing numbers and addresses. For
|
||||
example,a number can be added to an address, but an address can't be added to a number.</P>
|
||||
|
||||
<P>Operator precedence is the standard precedence defined by the "C" programming
|
||||
language.</P>
|
||||
|
||||
<H3>Math Operators</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Supported math operators are "+ - * /". These operators generate either a number or
|
||||
address result depending on the operands.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Logical and Relational Operators</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Supported logical operators are "< > <= >= == != || && and !".
|
||||
These operators generator a numeric value of 0 or 1.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Bit Operators</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Supported bit operators are <<, >>, &, |, ^, and ~. These operators
|
||||
generate either a number or address result depending on the operands.</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3>Groups Operators</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Parenthesis can be used to group sub-expressions to control the order of
|
||||
operations</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Result</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>The result of the expression is always an address or a number. If the result is a
|
||||
number, it is converted to an address using the selected address space (in the case where
|
||||
there are multiple possible address spaces, a combo is shown for choosing the desired
|
||||
address space, otherwise the default address space is used)</P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Examples</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<TABLE width=95% border="1">
|
||||
<TR>
|
||||
<TH valign="top" bgcolor="#c0c0c0">Expression<BR>
|
||||
</TH>
|
||||
|
||||
<TH valign="top" bgcolor="#c0c0c0">Result<BR>
|
||||
</TH>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top"><B>ENTRY+10</B></TD>
|
||||
|
||||
<TD valign="top">Address 0x10 higher than the symbol "ENTRY"</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top"><B>10+ENTRY</B></TD>
|
||||
|
||||
<TD valign="top">Error (Can't add an address to a number)</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top"><B>100000+30</B></TD>
|
||||
|
||||
<TD valign="top">Address 0x100030 (hex mode)</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top"><B>0x100000+30</B></TD>
|
||||
|
||||
<TD valign="top">Address 0x100030 (hex mode)</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top"><B>0x100000+30</B></TD>
|
||||
|
||||
<TD valign="top">Address 0x10001e (decimal mode)</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top"><B>0x100000+(2*10)</B></TD>
|
||||
|
||||
<TD valign="top">Address 0x100020</TD>
|
||||
</TR>
|
||||
|
||||
<TR>
|
||||
<TD valign="top"><B>ENTRY + 1<<4</B></TD>
|
||||
|
||||
<TD valign="top">Address that is 16 higher than the symbol "ENTRY"</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD valign="top"><B>X - (X > 100) * 100</B></TD>
|
||||
|
||||
<TD valign="top">If symbol "X" address > 100, Result is 100 less than X; Otherwise X</TD>
|
||||
</TR>
|
||||
<TR>
|
||||
<TD valign="top"><B>ENTRY | FF</B></TD>
|
||||
|
||||
<TD valign="top">Address that is the symbol "ENTRY" with its last 8 bits set to FF</TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -49,7 +49,9 @@
|
|||
</LI>
|
||||
|
||||
<LI>The "Enter Base Address" field in the dialog is filled in with the first address in the
|
||||
selection. You can enter a different address as the base address.</LI>
|
||||
selection. You can enter a different address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>)
|
||||
as the base address.</LI>
|
||||
|
||||
<LI>
|
||||
The "Select Data Size" combo box has an entry for sizes 1, 2, 4, and 8. The size you select
|
||||
|
|
|
@ -1167,9 +1167,10 @@ c<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
|||
<P>[<I><B><IMG src="images/unchecked.png" alt="" valign="middle" align="middle">
|
||||
Offset</B></I>] The <I>To Address</I> entry is required for normal memory references
|
||||
and specifies the reference destination as a memory offset within a selected address
|
||||
space. The address offset entry is always
|
||||
interpretted as a unsigned hex value (i.e., the "0x" entry prefix is assumed).
|
||||
For those processors with multiple address-spaces, a pull-down is also provided
|
||||
space. Enter an address
|
||||
(or <A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>) to specify
|
||||
the referenced address.
|
||||
For those processors with multiple address-spaces, a pull-down is also provided
|
||||
allowing the address-space to be selected. Address spaces which overlay the
|
||||
</I>OTHER</I> non-loaded space are only included if the <B>Include OTHER overlay spaces</B>
|
||||
checkbox is selected.<BR>
|
||||
|
|
|
@ -124,7 +124,10 @@
|
|||
range is smaller, then value associations for address range that was trucated is
|
||||
effectively cleared. For example, in the dialog shown above, if you change the end
|
||||
address to 01001b47 and change the value to 111, then addresses 01001b33 to 01001b47
|
||||
will have the value 111 and address 01001b48 will have no value.<BR>
|
||||
will have the value 111 and address 01001b48 will have no value. When entering a
|
||||
new start or end address, you can also enter an
|
||||
<A href="help/topics/Misc/AddressExpressions.htm">Address Expression</A>.
|
||||
<BR>
|
||||
</P>
|
||||
</DIV>
|
||||
</DIV>
|
||||
|
|
|
@ -117,15 +117,14 @@ class FallThroughDialog extends DialogComponentProvider implements ChangeListene
|
|||
}
|
||||
}
|
||||
|
||||
private void addressChanged() {
|
||||
private void addressChanged(Address address) {
|
||||
if (changing) {
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable r = () -> {
|
||||
Address addr = addrField.getAddress();
|
||||
if (addr != null || addrField.getValue().length() == 0) {
|
||||
model.setCurrentFallthrough(addr);
|
||||
if (address != null || addrField.getText().length() == 0) {
|
||||
model.setCurrentFallthrough(address);
|
||||
}
|
||||
else {
|
||||
setStatusText("Invalid Address");
|
||||
|
@ -139,9 +138,7 @@ class FallThroughDialog extends DialogComponentProvider implements ChangeListene
|
|||
private JPanel create() {
|
||||
JPanel panel = new JPanel(new BorderLayout(0, 10));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
|
||||
addrField = new AddressInput();
|
||||
addrField.setAddressFactory(model.getProgram().getAddressFactory());
|
||||
addrField.addChangeListener(e -> addressChanged());
|
||||
addrField = new AddressInput(model.getProgram(), this::addressChanged);
|
||||
addrField.addActionListener(e -> model.setCurrentFallthrough(addrField.getAddress()));
|
||||
panel.add(createHomePanel(), BorderLayout.NORTH);
|
||||
panel.add(createAddressPanel(), BorderLayout.CENTER);
|
||||
|
|
|
@ -148,8 +148,9 @@ class VarnodeLocationCellEditor extends AbstractCellEditor
|
|||
}
|
||||
|
||||
private Component createAddressEditor(VarnodeInfo varnode) {
|
||||
addressInput = new AddressInput(BorderFactory.createEmptyBorder());
|
||||
addressInput.setAddressFactory(program.getAddressFactory());
|
||||
addressInput = new AddressInput(program);
|
||||
addressInput.setComponentBorders(BorderFactory.createEmptyBorder());
|
||||
|
||||
Address address = varnode.getAddress();
|
||||
if (address != null) {
|
||||
addressInput.setAddress(address);
|
||||
|
|
|
@ -35,7 +35,6 @@ import ghidra.app.util.HelpTopics;
|
|||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.database.mem.FileBytes;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlockType;
|
||||
|
@ -69,7 +68,6 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
private JCheckBox overlayCB;
|
||||
private RegisterField initialValueField;
|
||||
private JLabel initialValueLabel;
|
||||
private AddressFactory addrFactory;
|
||||
private AddressInput baseAddrField; // used for Bit and Byte mapped blocks
|
||||
private IntegerTextField schemeDestByteCountField; // used for Byte mapped blocks
|
||||
private IntegerTextField schemeSrcByteCountField; // used for Byte mapped blocks
|
||||
|
@ -134,12 +132,12 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
}
|
||||
|
||||
private Component buildBasicInfoPanel() {
|
||||
JPanel panel = new JPanel(new PairLayout(4, 10, 150));
|
||||
JPanel panel = new JPanel(new PairLayout(5, 10, 150));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(5, 7, 4, 5));
|
||||
|
||||
panel.add(new GLabel("Block Name:", SwingConstants.RIGHT));
|
||||
panel.add(buildNameField());
|
||||
panel.add(new GLabel("Start Addr:", SwingConstants.RIGHT));
|
||||
panel.add(new GLabel("Start Address:", SwingConstants.RIGHT));
|
||||
panel.add(buildAddressField());
|
||||
panel.add(new GLabel("Length:", SwingConstants.RIGHT));
|
||||
panel.add(buildLengthField());
|
||||
|
@ -422,26 +420,16 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
model.setFileBytes((FileBytes) fileBytesComboBox.getSelectedItem());
|
||||
}
|
||||
|
||||
private void addrChanged() {
|
||||
Address addr = null;
|
||||
try {
|
||||
addr = addrField.getAddress();
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// just let it be null
|
||||
}
|
||||
model.setStartAddress(addr);
|
||||
private void addressChanged(Address address) {
|
||||
model.setStartAddress(address);
|
||||
}
|
||||
|
||||
private void baseAddressChanged() {
|
||||
Address addr = null;
|
||||
try {
|
||||
addr = baseAddrField.getAddress();
|
||||
private void addressError(String errorMessage) {
|
||||
model.setAddressError(errorMessage);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// just let it be null
|
||||
}
|
||||
model.setBaseAddress(addr);
|
||||
|
||||
private void baseAddressChanged(Address address) {
|
||||
model.setBaseAddress(address);
|
||||
}
|
||||
|
||||
private void schemeSrcByteCountChanged() {
|
||||
|
@ -476,12 +464,11 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
}
|
||||
|
||||
private JPanel buildMappedPanel() {
|
||||
Program program = model.getProgram();
|
||||
JPanel panel = new JPanel(new PairLayout());
|
||||
|
||||
baseAddrField = new AddressInput();
|
||||
baseAddrField.setAddressFactory(addrFactory);
|
||||
baseAddrField = new AddressInput(program, this::baseAddressChanged);
|
||||
baseAddrField.setName("Source Addr");
|
||||
baseAddrField.addChangeListener(ev -> baseAddressChanged());
|
||||
baseAddrField.setAccessibleName("Source Address");
|
||||
|
||||
JPanel schemePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
|
@ -504,7 +491,6 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
schemePanel.add(new GLabel(" : "));
|
||||
schemePanel.add(schemeSrcByteCountField.getComponent());
|
||||
|
||||
Program program = model.getProgram();
|
||||
Address minAddr = program.getMinAddress();
|
||||
if (minAddr == null) {
|
||||
minAddr = program.getAddressFactory().getDefaultAddressSpace().getAddress(0);
|
||||
|
@ -561,12 +547,12 @@ class AddBlockDialog extends DialogComponentProvider implements ChangeListener {
|
|||
}
|
||||
|
||||
private Component buildAddressField() {
|
||||
addrField = new AddressInput();
|
||||
Program program = model.getProgram();
|
||||
addrField = new AddressInput(program, this::addressChanged);
|
||||
addrField.setAddressErrorConsumer(this::addressError);
|
||||
addrField.setAddressSpaceFilter(AddressInput.ALL_MEMORY_SPACES);
|
||||
addrField.setName("Start Addr");
|
||||
addrField.setAccessibleName("Memory Block Start Address");
|
||||
addrFactory = model.getProgram().getAddressFactory();
|
||||
addrField.setAddressFactory(addrFactory, AddressInput.INCLUDE_ALL_MEMORY_SPACES);
|
||||
addrField.addChangeListener(ev -> addrChanged());
|
||||
return addrField;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,6 +59,7 @@ class AddBlockModel {
|
|||
private String comment;
|
||||
private FileBytes fileBytes;
|
||||
private long fileBytesOffset = -1;
|
||||
private String addressErrorMessage;
|
||||
|
||||
enum InitializedType {
|
||||
UNINITIALIZED, INITIALIZED_FROM_VALUE, INITIALIZED_FROM_FILE_BYTES;
|
||||
|
@ -90,6 +91,14 @@ class AddBlockModel {
|
|||
|
||||
void setStartAddress(Address addr) {
|
||||
startAddr = addr;
|
||||
addressErrorMessage = null;
|
||||
validateInfo();
|
||||
listener.stateChanged(null);
|
||||
}
|
||||
|
||||
void setAddressError(String errorMessage) {
|
||||
startAddr = null;
|
||||
addressErrorMessage = errorMessage;
|
||||
validateInfo();
|
||||
listener.stateChanged(null);
|
||||
}
|
||||
|
@ -432,6 +441,9 @@ class AddBlockModel {
|
|||
return true;
|
||||
}
|
||||
message = "Please enter a valid Start Address";
|
||||
if (addressErrorMessage != null) {
|
||||
message = "Invalid Address: " + addressErrorMessage;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.memory;
|
|||
import java.awt.BorderLayout;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
|
@ -30,7 +31,7 @@ import ghidra.app.util.AddressInput;
|
|||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
@ -49,7 +50,6 @@ class ExpandBlockDialog extends DialogComponentProvider implements ChangeListene
|
|||
private final static String EXPAND_UP_TITLE = "Expand Block Up";
|
||||
private final static String EXPAND_DOWN_TITLE = "Expand Block Down";
|
||||
private int dialogType;
|
||||
private AddressFactory addrFactory;
|
||||
private AddressInput startAddressInput;
|
||||
private AddressInput endAddressInput;
|
||||
private JTextField startField;
|
||||
|
@ -66,7 +66,7 @@ class ExpandBlockDialog extends DialogComponentProvider implements ChangeListene
|
|||
* @param af
|
||||
* @param dialogType
|
||||
*/
|
||||
ExpandBlockDialog(PluginTool tool, ExpandBlockModel model, MemoryBlock block, AddressFactory af,
|
||||
ExpandBlockDialog(PluginTool tool, ExpandBlockModel model, MemoryBlock block, Program program,
|
||||
int dialogType) {
|
||||
super(dialogType == EXPAND_UP ? EXPAND_UP_TITLE : EXPAND_DOWN_TITLE, true);
|
||||
this.tool = tool;
|
||||
|
@ -74,9 +74,8 @@ class ExpandBlockDialog extends DialogComponentProvider implements ChangeListene
|
|||
this.dialogType = dialogType;
|
||||
setHelpLocation(new HelpLocation(HelpTopics.MEMORY_MAP,
|
||||
dialogType == EXPAND_UP ? EXPAND_UP_TITLE : EXPAND_DOWN_TITLE));
|
||||
addrFactory = af;
|
||||
model.setChangeListener(this);
|
||||
addWorkPanel(create(block));
|
||||
addWorkPanel(create(block, program));
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
setOkEnabled(false);
|
||||
|
@ -109,16 +108,16 @@ class ExpandBlockDialog extends DialogComponentProvider implements ChangeListene
|
|||
* Create the main work panel.
|
||||
* @return JPanel
|
||||
*/
|
||||
private JPanel create(MemoryBlock block) {
|
||||
private JPanel create(MemoryBlock block, Program program) {
|
||||
JPanel panel = new JPanel(new PairLayout(5, 5, 150));
|
||||
startAddressInput = new AddressInput();
|
||||
AddressChangeListener listener = new AddressChangeListener();
|
||||
|
||||
startAddressInput = new AddressInput(program, listener);
|
||||
startAddressInput.setName("NewStartAddress");
|
||||
startAddressInput.setAddressFactory(addrFactory);
|
||||
startAddressInput.setAccessibleName("New Start Address");
|
||||
|
||||
endAddressInput = new AddressInput();
|
||||
endAddressInput = new AddressInput(program, listener);
|
||||
endAddressInput.setName("EndAddress");
|
||||
endAddressInput.setAddressFactory(addrFactory);
|
||||
endAddressInput.setAccessibleName("New End Address");
|
||||
|
||||
Address start = block.getStart();
|
||||
|
@ -161,9 +160,6 @@ class ExpandBlockDialog extends DialogComponentProvider implements ChangeListene
|
|||
}
|
||||
|
||||
private void addListeners() {
|
||||
|
||||
startAddressInput.addChangeListener(new AddressChangeListener());
|
||||
endAddressInput.addChangeListener(new AddressChangeListener());
|
||||
lengthField.setChangeListener(new LengthChangeListener());
|
||||
|
||||
ActionListener al = e -> setStatusText("");
|
||||
|
@ -206,10 +202,10 @@ class ExpandBlockDialog extends DialogComponentProvider implements ChangeListene
|
|||
* Listener on the AddressInput field; update length field when the
|
||||
* address input field changes.
|
||||
*/
|
||||
private class AddressChangeListener implements ChangeListener {
|
||||
private class AddressChangeListener implements Consumer<Address> {
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent event) {
|
||||
public void accept(Address address) {
|
||||
if (isChanging) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -690,7 +690,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
"OTHER overlay blocks can not be split.");
|
||||
}
|
||||
else {
|
||||
SplitBlockDialog d = new SplitBlockDialog(plugin, block, program.getAddressFactory());
|
||||
SplitBlockDialog d = new SplitBlockDialog(plugin, block, program);
|
||||
tool.showDialog(d, this);
|
||||
}
|
||||
}
|
||||
|
@ -711,7 +711,7 @@ class MemoryMapProvider extends ComponentProviderAdapter {
|
|||
}
|
||||
|
||||
ExpandBlockDialog dialog =
|
||||
new ExpandBlockDialog(tool, model, block, program.getAddressFactory(), dialogType);
|
||||
new ExpandBlockDialog(tool, model, block, program, dialogType);
|
||||
model.initialize(block);
|
||||
dialog.dispose();
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import ghidra.app.util.AddressInput;
|
|||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
@ -82,9 +82,9 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
|
|||
setOkEnabled(false);
|
||||
changing = true;
|
||||
if (!isVisible()) {
|
||||
AddressFactory factory = model.getAddressFactory();
|
||||
newStartField.setAddressFactory(factory);
|
||||
newEndField.setAddressFactory(factory);
|
||||
Program program = model.getProgram();
|
||||
newStartField.setProgram(program);
|
||||
newEndField.setProgram(program);
|
||||
}
|
||||
Address newStart = model.getNewStartAddress();
|
||||
if (newStart != null) {
|
||||
|
@ -136,8 +136,8 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
|
|||
}
|
||||
|
||||
private JPanel buildMainPanel() {
|
||||
JPanel panel = new JPanel(new PairLayout(5, 20, 150));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||
JPanel panel = new JPanel(new PairLayout(2, 10, 150));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
|
||||
blockNameLabel = new GDLabel(".text");
|
||||
blockNameLabel.setName("blockName"); // name components for junits
|
||||
|
||||
|
@ -150,15 +150,12 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
|
|||
lengthLabel = new GDLabel("4096 (0x1000)");
|
||||
lengthLabel.setName("length");
|
||||
|
||||
newStartField = new AddressInput();
|
||||
newStartField = new AddressInput(model.getProgram(), this::startChanged);
|
||||
newStartField.setName("newStart");
|
||||
|
||||
newEndField = new AddressInput();
|
||||
newEndField = new AddressInput(model.getProgram(), this::endChanged);
|
||||
newEndField.setName("newEnd");
|
||||
|
||||
newStartField.addChangeListener(e -> startChanged());
|
||||
newEndField.addChangeListener(e -> endChanged());
|
||||
|
||||
panel.add(new GLabel("Name:", SwingConstants.RIGHT));
|
||||
panel.add(blockNameLabel);
|
||||
panel.add(new GLabel("Start Address:", SwingConstants.RIGHT));
|
||||
|
@ -174,13 +171,12 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
|
|||
return panel;
|
||||
}
|
||||
|
||||
private void startChanged() {
|
||||
private void startChanged(Address address) {
|
||||
if (changing) {
|
||||
return;
|
||||
}
|
||||
Address newStart = newStartField.getAddress();
|
||||
if (newStart != null) {
|
||||
model.setNewStartAddress(newStart);
|
||||
if (address != null) {
|
||||
model.setNewStartAddress(address);
|
||||
}
|
||||
else {
|
||||
setStatusText("Invalid Address");
|
||||
|
@ -188,13 +184,12 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
|
|||
}
|
||||
}
|
||||
|
||||
private void endChanged() {
|
||||
private void endChanged(Address address) {
|
||||
if (changing) {
|
||||
return;
|
||||
}
|
||||
Address newEnd = newEndField.getAddress();
|
||||
if (newEnd != null) {
|
||||
model.setNewEndAddress(newEnd);
|
||||
if (address != null) {
|
||||
model.setNewEndAddress(address);
|
||||
}
|
||||
else {
|
||||
setStatusText("Invalid Address");
|
||||
|
|
|
@ -18,7 +18,8 @@ package ghidra.app.plugin.core.memory;
|
|||
import ghidra.app.cmd.memory.MoveBlockListener;
|
||||
import ghidra.app.cmd.memory.MoveBlockTask;
|
||||
import ghidra.framework.model.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
|
||||
|
@ -183,8 +184,8 @@ class MoveBlockModel implements DomainObjectListener {
|
|||
program = null;
|
||||
}
|
||||
|
||||
AddressFactory getAddressFactory() {
|
||||
return program.getAddressFactory();
|
||||
Program getProgram() {
|
||||
return program;
|
||||
}
|
||||
|
||||
private Address getEndAddress(Address start) {
|
||||
|
|
|
@ -27,7 +27,9 @@ import docking.widgets.label.GLabel;
|
|||
import ghidra.app.plugin.core.misc.RegisterField;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
@ -50,16 +52,14 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
private JTextField blockTwoEndField;
|
||||
private RegisterField blockTwoLengthField;
|
||||
private MemoryBlock block;
|
||||
private AddressFactory addrFactory;
|
||||
private MemoryMapPlugin plugin;
|
||||
|
||||
SplitBlockDialog(MemoryMapPlugin plugin, MemoryBlock block, AddressFactory af) {
|
||||
SplitBlockDialog(MemoryMapPlugin plugin, MemoryBlock block, Program program) {
|
||||
super("Split Block");
|
||||
this.plugin = plugin;
|
||||
this.block = block;
|
||||
addrFactory = af;
|
||||
setHelpLocation(new HelpLocation(HelpTopics.MEMORY_MAP, "Split Block"));
|
||||
addWorkPanel(create());
|
||||
addWorkPanel(create(program));
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
setOkEnabled(false);
|
||||
|
@ -89,8 +89,8 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
* Create the work panel.
|
||||
* @return JPanel
|
||||
*/
|
||||
private JPanel create() {
|
||||
JPanel panelOne = new JPanel(new PairLayout(5, 5, 150));
|
||||
private JPanel create(Program program) {
|
||||
JPanel panelOne = new JPanel(new PairLayout(5, 10, 150));
|
||||
panelOne.setBorder(BorderFactory.createTitledBorder("Block to Split"));
|
||||
blockOneNameField = new JTextField(10);
|
||||
blockOneNameField.setName("BlockOneName");
|
||||
|
@ -99,7 +99,7 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
blockOneStartField.setName("BlockOneStart");
|
||||
blockOneStartField.getAccessibleContext().setAccessibleName("Address of Block To Split");
|
||||
|
||||
blockOneEnd = new AddressInput();
|
||||
blockOneEnd = new AddressInput(program, this::blockOneEndChanged);
|
||||
blockOneEnd.setName("BlockOneEnd");
|
||||
blockOneEnd.setAccessibleName("New Block End Adddress");
|
||||
|
||||
|
@ -121,7 +121,7 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
blockTwoNameField = new JTextField(10);
|
||||
blockTwoNameField.setName("BlockTwoName");
|
||||
blockTwoNameField.getAccessibleContext().setAccessibleName("Name of New Block");
|
||||
blockTwoStart = new AddressInput();
|
||||
blockTwoStart = new AddressInput(program, this::blockTwoStartChanged);
|
||||
blockTwoStart.setName("BlockTwoStart");
|
||||
blockTwoStart.setAccessibleName("New Block Start Address");
|
||||
blockTwoEndField = new JTextField(10);
|
||||
|
@ -141,11 +141,11 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
panelTwo.add(blockTwoLengthField);
|
||||
|
||||
JPanel mainPanel = new JPanel();
|
||||
mainPanel.setBorder(BorderFactory.createEmptyBorder(15, 20, 15, 20));
|
||||
BoxLayout bl = new BoxLayout(mainPanel, BoxLayout.Y_AXIS);
|
||||
mainPanel.setLayout(bl);
|
||||
mainPanel.add(Box.createVerticalStrut(5));
|
||||
mainPanel.add(panelOne);
|
||||
mainPanel.add(Box.createVerticalStrut(10));
|
||||
mainPanel.add(Box.createVerticalStrut(20));
|
||||
mainPanel.add(panelTwo);
|
||||
|
||||
return mainPanel;
|
||||
|
@ -166,7 +166,6 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
blockOneStartField.setText(startAddr.toString());
|
||||
blockOneStartField.setEnabled(false);
|
||||
|
||||
blockOneEnd.setAddressFactory(addrFactory);
|
||||
blockOneEnd.setAddress(endAddr);
|
||||
blockOneEnd.setAddressSpaceEditable(false);
|
||||
|
||||
|
@ -174,7 +173,6 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
|
||||
blockTwoNameField.setText(name + ".split");
|
||||
|
||||
blockTwoStart.setAddressFactory(addrFactory);
|
||||
blockTwoStart.setAddress(startAddr);
|
||||
blockTwoStart.setAddressSpaceEditable(false);
|
||||
|
||||
|
@ -189,8 +187,6 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
|
||||
blockOneLengthField.setChangeListener(new LengthChangeListener(blockOneLengthField));
|
||||
blockTwoLengthField.setChangeListener(new LengthChangeListener(blockTwoLengthField));
|
||||
blockOneEnd.addChangeListener(new AddressChangeListener(blockOneEnd));
|
||||
blockTwoStart.addChangeListener(new AddressChangeListener(blockTwoStart));
|
||||
|
||||
ActionListener al = e -> setStatusText("");
|
||||
blockOneLengthField.addActionListener(al);
|
||||
|
@ -307,55 +303,27 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener on the AddressInput fields; update other fields when either
|
||||
* of these fields change.
|
||||
*/
|
||||
private class AddressChangeListener implements ChangeListener {
|
||||
|
||||
AddressInput source;
|
||||
|
||||
public AddressChangeListener(AddressInput source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent event) {
|
||||
private void blockOneEndChanged(Address end) {
|
||||
setStatusText("");
|
||||
boolean ok = false;
|
||||
if (source == blockOneEnd) {
|
||||
ok = blockOneEndChanged();
|
||||
}
|
||||
else if (source == blockTwoStart) {
|
||||
ok = blockTwoStartChanged();
|
||||
}
|
||||
setOkEnabled(ok);
|
||||
boolean isValid = checkBlockOneEndAddress(end);
|
||||
setOkEnabled(isValid);
|
||||
}
|
||||
|
||||
private Address getAddress() throws InvalidInputException {
|
||||
|
||||
AddressInput field = source;
|
||||
Address addr = field.getAddress();
|
||||
if (addr == null && field.hasInput()) {
|
||||
throw new InvalidInputException();
|
||||
}
|
||||
return addr;
|
||||
private void blockTwoStartChanged(Address start) {
|
||||
setStatusText("");
|
||||
boolean isValid = checkBlockTwoStartChanged(start);
|
||||
setOkEnabled(isValid);
|
||||
}
|
||||
|
||||
private boolean blockOneEndChanged() {
|
||||
Address start = block.getStart();
|
||||
Address end = null;
|
||||
try {
|
||||
end = getAddress();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
private boolean checkBlockOneEndAddress(Address end) {
|
||||
|
||||
if (end == null) {
|
||||
setStatusText("Invalid Address");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (end == null) {
|
||||
return false;
|
||||
}
|
||||
Address start = block.getStart();
|
||||
|
||||
if (end.compareTo(start) < 0) {
|
||||
setStatusText("End address must be greater than start");
|
||||
return false;
|
||||
|
@ -393,14 +361,11 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private boolean blockTwoStartChanged() {
|
||||
Address start = null;
|
||||
try {
|
||||
start = getAddress();
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
private boolean checkBlockTwoStartChanged(Address start) {
|
||||
if (start == null) {
|
||||
setStatusText("Invalid Address");
|
||||
return false;
|
||||
}
|
||||
|
@ -435,7 +400,4 @@ class SplitBlockDialog extends DialogComponentProvider {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -231,7 +231,7 @@ class EditExternalReferencePanel extends EditReferencePanel {
|
|||
updateExtLibPath();
|
||||
|
||||
extLabel.setText(extLoc.getLabel());
|
||||
extAddr.setAddressFactory(program.getAddressFactory());
|
||||
extAddr.setProgram(program);
|
||||
Address addr = extLoc.getAddress();
|
||||
if (addr != null) {
|
||||
extAddr.setAddress(addr);
|
||||
|
@ -260,7 +260,7 @@ class EditExternalReferencePanel extends EditReferencePanel {
|
|||
extLibPath.setText(null);
|
||||
|
||||
extLabel.setText(null);
|
||||
extAddr.setAddressFactory(program.getAddressFactory());
|
||||
extAddr.setProgram(program);
|
||||
extAddr.clear();
|
||||
|
||||
extLibName.requestFocus();
|
||||
|
|
|
@ -191,7 +191,8 @@ class EditMemoryReferencePanel extends EditReferencePanel {
|
|||
}
|
||||
|
||||
private void initializeToAddressField(Address toAddr) {
|
||||
toAddressField.setAddressFactory(fromCodeUnit.getProgram().getAddressFactory(), (s) -> {
|
||||
Program program = fromCodeUnit.getProgram();
|
||||
toAddressField.setProgram(program, (s) -> {
|
||||
if (s.isLoadedMemorySpace()) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ import java.awt.event.ActionEvent;
|
|||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.checkbox.GCheckBox;
|
||||
|
@ -30,7 +28,7 @@ import ghidra.app.util.AddressInput;
|
|||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
@ -44,7 +42,6 @@ import ghidra.util.layout.PairLayout;
|
|||
public class OffsetTableDialog extends DialogComponentProvider {
|
||||
|
||||
private AddressInput addrInput;
|
||||
private AddressFactory addrFactory;
|
||||
private JComboBox<String> comboBox;
|
||||
private Address defaultAddress;
|
||||
private JCheckBox signedCheckBox;
|
||||
|
@ -52,15 +49,13 @@ public class OffsetTableDialog extends DialogComponentProvider {
|
|||
|
||||
/**
|
||||
* Construct a new dialog
|
||||
* @param parent parent of this dialog
|
||||
* @param defaultAddress address to put in the address field as a default
|
||||
* @param addrFactory address factory required by AddressInput object
|
||||
* @param program the program
|
||||
*/
|
||||
OffsetTableDialog(Address defaultAddress, AddressFactory addrFactory) {
|
||||
OffsetTableDialog(Address defaultAddress, Program program) {
|
||||
super("Create Offset References", true);
|
||||
this.defaultAddress = defaultAddress;
|
||||
this.addrFactory = addrFactory;
|
||||
addWorkPanel(buildMainPanel());
|
||||
addWorkPanel(buildMainPanel(program));
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
setHelpLocation(new HelpLocation(HelpTopics.REFERENCES, "Create_Offset_References"));
|
||||
|
@ -128,23 +123,16 @@ public class OffsetTableDialog extends DialogComponentProvider {
|
|||
signedCheckBox.setSelected(isSigned);
|
||||
}
|
||||
|
||||
private JPanel buildMainPanel() {
|
||||
private JPanel buildMainPanel(Program program) {
|
||||
JPanel panel = new JPanel(new PairLayout(10, 5));
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20));
|
||||
addrInput = new AddressInput();
|
||||
addrInput = new AddressInput(program, a -> clearStatusText());
|
||||
addrInput.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
okCallback();
|
||||
}
|
||||
});
|
||||
addrInput.addChangeListener(new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
clearStatusText();
|
||||
}
|
||||
});
|
||||
addrInput.setAddressFactory(addrFactory);
|
||||
addrInput.setAddress(defaultAddress);
|
||||
|
||||
panel.add(new GLabel("Enter Base Address:", SwingConstants.RIGHT));
|
||||
|
|
|
@ -101,10 +101,10 @@ public class OffsetTablePlugin extends Plugin {
|
|||
true);
|
||||
return;
|
||||
}
|
||||
AddressFactory addressFactory = context.getProgram().getAddressFactory();
|
||||
Program program = context.getProgram();
|
||||
Address minAddress = context.getSelection().getMinAddress();
|
||||
|
||||
OffsetTableDialog dialog = new OffsetTableDialog(minAddress, addressFactory);
|
||||
OffsetTableDialog dialog = new OffsetTableDialog(minAddress, program);
|
||||
dialog.setSelectedSize(lastSelectedSize);
|
||||
dialog.setSigned(lastSigned);
|
||||
try {
|
||||
|
|
|
@ -16,17 +16,18 @@
|
|||
package ghidra.app.plugin.core.register;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.widgets.label.GLabel;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.app.util.bean.FixedBitSizeValueField;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.lang.Register;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.MessageType;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
@ -39,9 +40,9 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
|||
private boolean wasCancelled = true;
|
||||
|
||||
EditRegisterValueDialog(Register register, Address start, Address end, BigInteger value,
|
||||
AddressFactory factory) {
|
||||
Program program) {
|
||||
super("Edit Register Value Range");
|
||||
addWorkPanel(buildWorkPanel(register, start, end, value, factory));
|
||||
addWorkPanel(buildWorkPanel(register, start, end, value, program));
|
||||
|
||||
addOKButton();
|
||||
addCancelButton();
|
||||
|
@ -49,24 +50,15 @@ class EditRegisterValueDialog extends DialogComponentProvider {
|
|||
}
|
||||
|
||||
private JComponent buildWorkPanel(Register register, Address start, Address end,
|
||||
BigInteger value, AddressFactory factory) {
|
||||
BigInteger value, Program program) {
|
||||
|
||||
JTextField registerField =
|
||||
new JTextField(register.getName() + " (" + register.getBitLength() + ")");
|
||||
registerField.setEditable(false);
|
||||
|
||||
startAddrField = new AddressInput();
|
||||
endAddrField = new AddressInput();
|
||||
ChangeListener changeListener = new ChangeListener() {
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
updateOk();
|
||||
}
|
||||
};
|
||||
startAddrField.setAddressFactory(factory);
|
||||
endAddrField.setAddressFactory(factory);
|
||||
startAddrField.addChangeListener(changeListener);
|
||||
endAddrField.addChangeListener(changeListener);
|
||||
Consumer<Address> addressChangeListener = a -> updateOk();
|
||||
startAddrField = new AddressInput(program, addressChangeListener);
|
||||
endAddrField = new AddressInput(program, addressChangeListener);
|
||||
|
||||
registerValueField = new FixedBitSizeValueField(register.getBitLength(), true, false);
|
||||
startAddrField.setAddress(start);
|
||||
|
|
|
@ -89,7 +89,7 @@ class RegisterValuesPanel extends JPanel {
|
|||
Address end = range.getEndAddress();
|
||||
BigInteger value = range.getValue();
|
||||
EditRegisterValueDialog dialog = new EditRegisterValueDialog(selectedRegister, start, end,
|
||||
value, currentProgram.getAddressFactory());
|
||||
value, currentProgram);
|
||||
tool.showDialog(dialog, this);
|
||||
|
||||
if (!dialog.wasCancelled()) {
|
||||
|
|
|
@ -300,7 +300,7 @@ class EditExternalLocationPanel extends JPanel {
|
|||
if (extOriginalLabelTextField != null) {
|
||||
extOriginalLabelTextField.setText(startingOriginalName);
|
||||
}
|
||||
extAddressInputWidget.setAddressFactory(program.getAddressFactory());
|
||||
extAddressInputWidget.setProgram(program);
|
||||
if (startingLocationAddress != null) {
|
||||
extAddressInputWidget.setAddress(startingLocationAddress);
|
||||
}
|
||||
|
|
|
@ -2630,7 +2630,7 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
|||
Address choice = doAsk(Integer.class, title, message, existingValue, lastValue -> {
|
||||
|
||||
AskAddrDialog dialog =
|
||||
new AskAddrDialog(title, message, currentProgram.getAddressFactory(), lastValue);
|
||||
new AskAddrDialog(title, message, currentProgram, lastValue);
|
||||
if (dialog.isCanceled()) {
|
||||
throw new CancelledException();
|
||||
}
|
||||
|
|
|
@ -16,34 +16,44 @@
|
|||
package ghidra.app.util;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FontMetrics;
|
||||
import java.awt.CardLayout;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import docking.widgets.combobox.GComboBox;
|
||||
import docking.widgets.table.FocusableEditor;
|
||||
import docking.widgets.textfield.HexDecimalModeTextField;
|
||||
import generic.expressions.ExpressionException;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.AddressEvaluator;
|
||||
import utility.function.Dummy;
|
||||
|
||||
/**
|
||||
* Panel for user input of addresses. Handles case with multiple address
|
||||
* spaces.
|
||||
* Input field for entering address or address expression. Handles multiple address
|
||||
* spaces and supports both hex and decimal number modes for evaluating numbers.
|
||||
*/
|
||||
public class AddressInput extends JPanel implements FocusableEditor {
|
||||
private JTextField textField;
|
||||
private JComboBox<AddressSpace> combo;
|
||||
public final static Predicate<AddressSpace> ALL_MEMORY_SPACES = s -> s.isMemorySpace();
|
||||
public final static Predicate<AddressSpace> LOADED_MEMORY_SPACES = s -> s.isLoadedMemorySpace();
|
||||
|
||||
private HexDecimalModeTextField textField;
|
||||
private AddressSpaceField addressSpaceField;
|
||||
AddressEvaluator addressEvaluator;
|
||||
private Predicate<AddressSpace> addressSpaceFilter = LOADED_MEMORY_SPACES;
|
||||
private Consumer<Address> addressChangedConsumer;
|
||||
private Consumer<String> addressErrorConsumer = Dummy.consumer();
|
||||
private boolean comboAdded;
|
||||
private AddressFactory addrFactory;
|
||||
private ChangeListener changeListener;
|
||||
private boolean updatingAddress;
|
||||
private boolean updateSpaceField;
|
||||
private boolean stateChanging;
|
||||
private JTextField spaceField;
|
||||
private boolean assumeHex = true;
|
||||
private boolean notificationsEnabled = true;
|
||||
|
||||
private static final Comparator<AddressSpace> ADDRESS_SPACE_SORT_COMPARATOR = (s1, s2) -> {
|
||||
if (s1.isOverlaySpace()) {
|
||||
|
@ -58,65 +68,132 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||
};
|
||||
|
||||
/**
|
||||
* Constructor for AddressInput.
|
||||
* @param border border around each subcomponent (combo/text fields).
|
||||
* Constructs an AddressInput field with no specified program or address.
|
||||
*/
|
||||
public AddressInput(Border border) {
|
||||
this();
|
||||
combo.setBorder(border);
|
||||
public AddressInput() {
|
||||
this(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AddressInput field with a consumer to be called when the address field's
|
||||
* value changes.
|
||||
* @param addressChangedConsumer the consumer to be called when the value in the address field
|
||||
* changes
|
||||
*/
|
||||
public AddressInput(Consumer<Address> addressChangedConsumer) {
|
||||
this(null, null, addressChangedConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AddressInput field and initialized with a program.
|
||||
* @param program the program used to evaluate the entered address expression.
|
||||
*/
|
||||
public AddressInput(Program program) {
|
||||
this(program, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AddressInput field and initialized with an address factory.
|
||||
* @param factory the address factory used to evaluate the entered address expression.
|
||||
*/
|
||||
public AddressInput(AddressFactory factory) {
|
||||
this(null, factory, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AddressInput field with a consumer to be notified when the address field
|
||||
* changes and initialized with a program.
|
||||
* @param program the program used to evaluate the entered address expression.
|
||||
* @param addressChangedConsumer the consumer to be called when the value in the address field
|
||||
* changes
|
||||
*/
|
||||
public AddressInput(Program program, Consumer<Address> addressChangedConsumer) {
|
||||
this(program, null, addressChangedConsumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an AddressInput field with a consumer to be notified when the address field
|
||||
* changes and initialized with an address factory.
|
||||
* @param factory the address factory used to evaluate the entered address expression.
|
||||
* @param addressChangedConsumer the consumer to be called when the value in the address field
|
||||
* changes
|
||||
*/
|
||||
public AddressInput(AddressFactory factory, Consumer<Address> addressChangedConsumer) {
|
||||
this(null, factory, addressChangedConsumer);
|
||||
}
|
||||
|
||||
private AddressInput(Program program, AddressFactory factory,
|
||||
Consumer<Address> addressChangedConsumer) {
|
||||
this.addressChangedConsumer = Dummy.ifNull(addressChangedConsumer);
|
||||
buildComponent();
|
||||
if (program != null) {
|
||||
setProgram(program);
|
||||
}
|
||||
else if (factory != null) {
|
||||
setAddressFactory(factory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a filter predicate to determine which address spaces should be selectable by the user.
|
||||
* If after filtering only one space is remaining, the address space portion of the address
|
||||
* input field will not be shown.
|
||||
* @param spaceFilter the predicate for filtering selectable address spaces.
|
||||
*/
|
||||
public void setAddressSpaceFilter(Predicate<AddressSpace> spaceFilter) {
|
||||
this.addressSpaceFilter = spaceFilter;
|
||||
updateAddressSpaceCombo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text in the expression input textfield.
|
||||
* @param text the text to initialize the input textfield
|
||||
*/
|
||||
public void setText(String text) {
|
||||
textField.setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to set the internal borders for use in specialized use cases such as a table field
|
||||
* editor.
|
||||
* @param border the border to use for the internal components that make up this input field
|
||||
*/
|
||||
public void setComponentBorders(Border border) {
|
||||
addressSpaceField.setComponentsBorder(border);
|
||||
textField.setBorder(border);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for AddressInput.
|
||||
* Sets the hex/decimal mode for this field. When in hex mode, all numbers are assumed to be
|
||||
* hexadecimal values. When in decimal mode, all numbers are assumed to be decimal numbers
|
||||
* unless prefixed with "0x".
|
||||
* @param hexMode true to assume numbers are hexadecimal.
|
||||
*/
|
||||
public AddressInput() {
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
textField = new JTextField(10);
|
||||
textField.setName("JTextField");//for JUnits...
|
||||
combo = new GComboBox<>();
|
||||
combo.setName("JComboBox");//for JUnits...
|
||||
combo.getAccessibleContext().setAccessibleName("Address Space");
|
||||
add(textField, BorderLayout.CENTER);
|
||||
//add(combo, BorderLayout.WEST);
|
||||
comboAdded = false;
|
||||
|
||||
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
stateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
stateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
stateChanged();
|
||||
}
|
||||
});
|
||||
|
||||
combo.addActionListener(ev -> stateChanged());
|
||||
public void setAssumeHex(boolean hexMode) {
|
||||
textField.setHexMode(hexMode);
|
||||
hexModeChanged(hexMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the field to display the given address
|
||||
* @param address the new address to display
|
||||
*/
|
||||
public void setAddress(Address addr) {
|
||||
if (stateChanging) {
|
||||
// called while we are in doing a state changed notification
|
||||
public void setAddress(Address address) {
|
||||
if (address.equals(getAddress())) {
|
||||
return;
|
||||
}
|
||||
updatingAddress = true;
|
||||
textField.setText(addr.toString(false));
|
||||
combo.setSelectedItem(addr.getAddressSpace());
|
||||
updatingAddress = false;
|
||||
if (updateSpaceField) {
|
||||
updateSpaceField = false;
|
||||
spaceField.setText(addr.getAddressSpace().getName());
|
||||
notificationsEnabled = false;
|
||||
try {
|
||||
String addressString = address.toString(false);
|
||||
addressString = removeLeadingZeros(addressString);
|
||||
if (!assumeHex) {
|
||||
addressString = "0x" + addressString;
|
||||
}
|
||||
textField.setText(addressString);
|
||||
addressSpaceField.setAddressSpace(address.getAddressSpace());
|
||||
}
|
||||
finally {
|
||||
notificationsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,42 +201,53 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||
* Returns the address in the field or null if the address can't
|
||||
* be parsed.
|
||||
* @return The address for the current value in the text field
|
||||
* @throws ExpressionException if expression can not be evaluated to a valid address.
|
||||
*
|
||||
* @throws NullPointerException if AddressFactory has not been set.
|
||||
*/
|
||||
public Address getAddress() {
|
||||
String addrStr = textField.getText();
|
||||
|
||||
AddressSpace space = getAddressSpace();
|
||||
try {
|
||||
return space.getAddress(addrStr);
|
||||
public Address getAddressWithExceptions() throws ExpressionException {
|
||||
String addrExpression = textField.getText();
|
||||
if (addrExpression.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
catch (AddressFormatException e) {
|
||||
|
||||
return addressEvaluator.parseAsAddress(addrExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current address the field evaluates to or null if the text does not evaluate to
|
||||
* a valid, unique address.
|
||||
* @return the current address the field evalutes to or null if the text does not evalute to
|
||||
* a valid unique address.
|
||||
*/
|
||||
public Address getAddress() {
|
||||
try {
|
||||
return getAddressWithExceptions();
|
||||
}
|
||||
catch (ExpressionException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the address space selected in the combobox or in the input text itself
|
||||
* if specified (eg: "register:1"). If the address space is not specified; returns the
|
||||
* default space.
|
||||
* Returns the address space selected in the combobox the default address space if the
|
||||
* comboBox is not being shown.
|
||||
*
|
||||
* @throws NullPointerException if AddressFactory has not been set.
|
||||
* @return the selected address space, or the default address space if no combo added, or
|
||||
* null if no program is set.
|
||||
*/
|
||||
public AddressSpace getAddressSpace() {
|
||||
if (comboAdded) {
|
||||
return (AddressSpace) combo.getSelectedItem();
|
||||
}
|
||||
return addrFactory.getDefaultAddressSpace();
|
||||
return addressSpaceField.getAddressSpace();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the Address input field contains text.
|
||||
* The getAddress() method will return null if text is not
|
||||
* a valid address.
|
||||
* @return true if the address field is not blank
|
||||
*/
|
||||
public boolean hasInput() {
|
||||
return textField.getText().length() != 0;
|
||||
return !textField.getText().isBlank();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -167,96 +255,48 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||
* @return the text in this field
|
||||
*/
|
||||
public String getText() {
|
||||
return textField.getText();
|
||||
}
|
||||
|
||||
public AddressFactory getAddressFactory() {
|
||||
return addrFactory;
|
||||
return textField.getText().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Address Space predicate which includes all loaded memory spaces.
|
||||
* See {@link AddressSpace#isLoadedMemorySpace()}.
|
||||
* Intended for use with {@link #setAddressFactory(AddressFactory, Predicate)}.
|
||||
*/
|
||||
public final static Predicate<AddressSpace> INCLUDE_LOADED_MEMORY_SPACES = (s) -> {
|
||||
return s.isLoadedMemorySpace();
|
||||
};
|
||||
|
||||
/**
|
||||
* Address Space predicate which includes all memory spaces, including the
|
||||
* {@link AddressSpace#OTHER_SPACE} and all overlay spaces.
|
||||
* Intended for use with {@link #setAddressFactory(AddressFactory, Predicate)}.
|
||||
*/
|
||||
public final static Predicate<AddressSpace> INCLUDE_ALL_MEMORY_SPACES = (s) -> {
|
||||
return s.isMemorySpace();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the address factory to be used to parse addresses. Also
|
||||
* used to set the combo box with the list of valid address spaces
|
||||
* if there is more than one space. Only loaded memory spaces
|
||||
* Set the program to be used to parse addresses and expressions and also
|
||||
* to determine the list of valid address spaces. Only loaded memory spaces
|
||||
* will be allowed (see {@link AddressSpace#isLoadedMemorySpace()}).
|
||||
* @param factory address factory to use
|
||||
* @param program the program to use to resolve address expressions
|
||||
*/
|
||||
public void setProgram(Program program) {
|
||||
addressEvaluator = new AddressEvaluator(program, assumeHex);
|
||||
updateAddressSpaceCombo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the program and the address space filter at the same time. This avoid some weird
|
||||
* intermediate results if the are set separately.
|
||||
* @param program the program to use to parse addresses and expressions.
|
||||
* @param addessSpaceFilter the predicate to determine which address spaces are user selectable
|
||||
*/
|
||||
public void setProgram(Program program, Predicate<AddressSpace> addessSpaceFilter) {
|
||||
this.addressSpaceFilter = addessSpaceFilter;
|
||||
setProgram(program);
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy method for setting the address factory to be used to parse address. Should only be
|
||||
* used when a program is not readily available.
|
||||
* @param factory the address factory to be used to parse addresses.
|
||||
*/
|
||||
public void setAddressFactory(AddressFactory factory) {
|
||||
setAddressFactory(factory, INCLUDE_LOADED_MEMORY_SPACES);
|
||||
addressEvaluator = new AddressEvaluator(factory, assumeHex);
|
||||
updateAddressSpaceCombo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address factory to be used to parse addresses. Also used to set the combo box
|
||||
* with the list of valid address spaces if there is more than one space. The specified
|
||||
* predicate will be used to determine if an address space should be included.
|
||||
* @param factory address factory to use
|
||||
* @param predicate callback used to determine if an address space should be included for selection
|
||||
* Sets a consumer to be notified when the address input field changes, but can't be parsed
|
||||
* into a valid address.
|
||||
* @param addressErrorConsumer the consumer to be notified for bad address input
|
||||
*/
|
||||
public void setAddressFactory(AddressFactory factory, Predicate<AddressSpace> predicate) {
|
||||
this.addrFactory = factory;
|
||||
AddressSpace[] spaces = factory.getAddressSpaces();
|
||||
|
||||
Arrays.sort(spaces, ADDRESS_SPACE_SORT_COMPARATOR);
|
||||
DefaultComboBoxModel<AddressSpace> model = new DefaultComboBoxModel<>();
|
||||
combo.setModel(model);
|
||||
|
||||
FontMetrics fm = combo.getFontMetrics(combo.getFont());
|
||||
int width = 0;
|
||||
for (AddressSpace space : spaces) {
|
||||
if (!predicate.test(space)) {
|
||||
continue;
|
||||
}
|
||||
String s = space.toString();
|
||||
width = Math.max(width, fm.stringWidth(s));
|
||||
|
||||
model.addElement(space);
|
||||
}
|
||||
|
||||
// // Commented out the following 2 lines since they were causing the Combo to only
|
||||
// // display "..." in some cases instead of the actual address space name.
|
||||
// Dimension d = combo.getPreferredSize();
|
||||
// combo.setPreferredSize(new Dimension(width + 30, d.height));
|
||||
|
||||
if (model.getSize() > 1) {
|
||||
if (!comboAdded) {
|
||||
add(combo, BorderLayout.WEST);
|
||||
comboAdded = true;
|
||||
}
|
||||
}
|
||||
else if (comboAdded) {
|
||||
remove(combo);
|
||||
comboAdded = false;
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected combo box item
|
||||
* to the default address space.
|
||||
*/
|
||||
public void selectDefaultAddressSpace() {
|
||||
if (addrFactory != null) {
|
||||
AddressSpace space = addrFactory.getDefaultAddressSpace();
|
||||
combo.setSelectedItem(space);
|
||||
}
|
||||
public void setAddressErrorConsumer(Consumer<String> addressErrorConsumer) {
|
||||
this.addressErrorConsumer = addressErrorConsumer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,32 +313,15 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||
textField.selectAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the offset part of the address field.
|
||||
* @return String
|
||||
*/
|
||||
public String getValue() {
|
||||
return textField.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the offset part of the address offset field without changing address space.
|
||||
* NOTE: This method is intended for test use only and mimicks user input.
|
||||
* @param value the offset value string
|
||||
*/
|
||||
public void setValue(String value) {
|
||||
textField.setText(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the address space and offset.
|
||||
* NOTE: Unlike {@link #setAddress(Address)} this method is intended for test use only
|
||||
* and mimicks user input with {@link #stateChanged()} notification.
|
||||
* and mimics user input with address changed notification
|
||||
* @param addr the address value
|
||||
*/
|
||||
public void setValue(Address addr) {
|
||||
public void simulateAddressChanged(Address addr) {
|
||||
setAddress(addr);
|
||||
stateChanged();
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -318,27 +341,7 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||
* @param state false means that the combo box should not be editable
|
||||
*/
|
||||
public void setAddressSpaceEditable(boolean state) {
|
||||
if (!state && comboAdded) {
|
||||
AddressSpace selectedSpace = (AddressSpace) combo.getSelectedItem();
|
||||
String spaceName = selectedSpace != null ? selectedSpace.getName() + ":" : " ";
|
||||
spaceField = new JTextField(spaceName);
|
||||
|
||||
spaceField.setEnabled(false);
|
||||
remove(combo);
|
||||
add(spaceField, BorderLayout.WEST);
|
||||
if (textField.getText().length() == 0) {
|
||||
updateSpaceField = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a change listener that will be notified anytime this address value
|
||||
* in this panel changes
|
||||
* @param listener the change listener to be notified.
|
||||
*/
|
||||
public void addChangeListener(ChangeListener listener) {
|
||||
changeListener = listener;
|
||||
addressSpaceField.setEditable(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -352,20 +355,17 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||
|
||||
/**
|
||||
* Removes the action listener from the list to be notified.
|
||||
* @param listener
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
public void removeActionListener(ActionListener listener) {
|
||||
this.textField.removeActionListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.awt.Component#setEnabled(boolean)
|
||||
*/
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
textField.setEnabled(enabled);
|
||||
combo.setEnabled(enabled);
|
||||
addressSpaceField.setEnabled(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -377,59 +377,204 @@ public class AddressInput extends JPanel implements FocusableEditor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set the text field to be editable according to the state param.
|
||||
* Set the text field to be editable or not.
|
||||
* @param b true if the address input field can be edited
|
||||
*/
|
||||
public void setEditable(boolean state) {
|
||||
textField.setEditable(state);
|
||||
public void setEditable(boolean b) {
|
||||
textField.setEditable(b);
|
||||
addressSpaceField.setEditable(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the address input field is editable.
|
||||
* @return true if the address input field is editable.
|
||||
*/
|
||||
public boolean isEditable() {
|
||||
return textField.isEditable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusEditor() {
|
||||
if (comboAdded) {
|
||||
combo.requestFocusInWindow();
|
||||
if (addressSpaceField.getSpaceCount() > 1 && addressSpaceField.isEnabled()) {
|
||||
addressSpaceField.requestFocusInWindow();
|
||||
}
|
||||
else {
|
||||
textField.requestFocusInWindow();
|
||||
}
|
||||
}
|
||||
|
||||
private void stateChanged() {
|
||||
if (changeListener != null && !updatingAddress && !stateChanging) {
|
||||
stateChanging = true;
|
||||
changeListener.stateChanged(null);
|
||||
stateChanging = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void showAddressSpaceCombo(boolean showCombo) {
|
||||
if (showCombo) {
|
||||
if (!comboAdded) {
|
||||
add(combo, BorderLayout.WEST);
|
||||
comboAdded = true;
|
||||
}
|
||||
}
|
||||
else if (comboAdded) {
|
||||
remove(combo);
|
||||
comboAdded = false;
|
||||
}
|
||||
revalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFocus() {
|
||||
textField.requestFocus();
|
||||
}
|
||||
|
||||
protected JTextField getAddressTextField() {
|
||||
return textField;
|
||||
private void buildComponent() {
|
||||
setLayout(new BorderLayout());
|
||||
textField = new HexDecimalModeTextField(10, b -> hexModeChanged(b));
|
||||
textField.setHexMode(true);
|
||||
textField.setName("JTextField");//for JUnits...
|
||||
addressSpaceField = new AddressSpaceField();
|
||||
add(textField, BorderLayout.CENTER);
|
||||
comboAdded = false;
|
||||
|
||||
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
protected JTextField getAddressSpaceTextField() {
|
||||
return spaceField;
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
notifyAddressChanged();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void hexModeChanged(boolean hexMode) {
|
||||
this.assumeHex = hexMode;
|
||||
addressEvaluator.setAssumeHex(hexMode);
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
private String removeLeadingZeros(String addressString) {
|
||||
// if it has a colon, then is is a segmented address, don't mess with it.
|
||||
if (addressString.indexOf(":") >= 0) {
|
||||
return addressString;
|
||||
}
|
||||
for (int i = 0; i < addressString.length(); i++) {
|
||||
if (addressString.charAt(i) != '0') {
|
||||
return addressString.substring(i);
|
||||
}
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
|
||||
private void updateAddressSpaceCombo() {
|
||||
notificationsEnabled = false;
|
||||
try {
|
||||
addressSpaceField.updateAddressSpaces(addressEvaluator.getAddressFactory());
|
||||
}
|
||||
finally {
|
||||
notificationsEnabled = true;
|
||||
}
|
||||
addRemoveAdressSpaceField();
|
||||
}
|
||||
|
||||
private void addRemoveAdressSpaceField() {
|
||||
remove(addressSpaceField);
|
||||
if (addressSpaceField.getSpaceCount() > 1) {
|
||||
add(addressSpaceField, BorderLayout.WEST);
|
||||
}
|
||||
revalidate();
|
||||
}
|
||||
|
||||
private void notifyAddressChanged() {
|
||||
if (notificationsEnabled) {
|
||||
try {
|
||||
Address address = getAddressWithExceptions();
|
||||
addressChangedConsumer.accept(address);
|
||||
}
|
||||
catch (ExpressionException e) {
|
||||
addressChangedConsumer.accept(null);
|
||||
addressErrorConsumer.accept(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AddressSpaceField extends JPanel {
|
||||
private JComboBox<AddressSpace> combo;
|
||||
private JTextField uneditableSpaceField;
|
||||
private CardLayout layout;
|
||||
private boolean editable = true;
|
||||
|
||||
private AddressSpaceField() {
|
||||
layout = new CardLayout();
|
||||
setLayout(layout);
|
||||
|
||||
combo = new GComboBox<>();
|
||||
combo.setName("JComboBox");//for JUnits...
|
||||
combo.getAccessibleContext().setAccessibleName("Address Space");
|
||||
combo.addActionListener(ev -> addressSpaceChanged());
|
||||
add(combo, "combo");
|
||||
|
||||
uneditableSpaceField = new JTextField("");
|
||||
uneditableSpaceField.setEnabled(false);
|
||||
add(uneditableSpaceField, "text");
|
||||
}
|
||||
|
||||
private void addressSpaceChanged() {
|
||||
AddressSpace space = (AddressSpace) combo.getSelectedItem();
|
||||
addressEvaluator.setPreferredAddressSpace(space);
|
||||
notifyAddressChanged();
|
||||
}
|
||||
|
||||
private void setEditable(boolean state) {
|
||||
this.editable = state;
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
private void updateLayout() {
|
||||
boolean showCombo = isEnabled() && editable;
|
||||
layout.show(this, showCombo ? "combo" : "text");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
updateLayout();
|
||||
}
|
||||
|
||||
private int getSpaceCount() {
|
||||
return combo.getModel().getSize();
|
||||
}
|
||||
|
||||
private void updateAddressSpaces(AddressFactory addressFactory) {
|
||||
ComboBoxModel<AddressSpace> model = createAddressSpaceModel(addressFactory);
|
||||
combo.setModel(model);
|
||||
AddressSpace defaultAddressSpace = addressFactory.getDefaultAddressSpace();
|
||||
if (addressSpaceFilter.test(defaultAddressSpace)) {
|
||||
setAddressSpace(defaultAddressSpace);
|
||||
}
|
||||
else {
|
||||
setAddressSpace(model.getElementAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
private AddressSpace getAddressSpace() {
|
||||
return (AddressSpace) combo.getSelectedItem();
|
||||
}
|
||||
|
||||
private void setAddressSpace(AddressSpace addressSpace) {
|
||||
combo.setSelectedItem(addressSpace);
|
||||
String name = addressSpace.getName();
|
||||
uneditableSpaceField.setText(name);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void setComponentsBorder(Border border) {
|
||||
combo.setBorder(border);
|
||||
uneditableSpaceField.setBorder(border);
|
||||
}
|
||||
|
||||
private ComboBoxModel<AddressSpace> createAddressSpaceModel(AddressFactory factory) {
|
||||
AddressSpace[] spaces = factory.getAddressSpaces();
|
||||
|
||||
Arrays.sort(spaces, ADDRESS_SPACE_SORT_COMPARATOR);
|
||||
DefaultComboBoxModel<AddressSpace> model = new DefaultComboBoxModel<>();
|
||||
|
||||
for (AddressSpace space : spaces) {
|
||||
if (!addressSpaceFilter.test(space)) {
|
||||
continue;
|
||||
}
|
||||
model.addElement(space);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,10 +67,7 @@ public class AddressSetEditorPanel extends JPanel {
|
|||
JLabel minLabel = new GDLabel("Min:");
|
||||
minLabel.setToolTipText("Enter minimum address to add or remove");
|
||||
minAddressPanel.add(minLabel, BorderLayout.WEST);
|
||||
minAddressField = new AddressInput();
|
||||
minAddressField.setAddressFactory(addressFactory);
|
||||
ChangeListener listener = e -> validateAddRemoveButton();
|
||||
minAddressField.addChangeListener(listener);
|
||||
minAddressField = new AddressInput(addressFactory, a -> validateAddRemoveButton());
|
||||
minAddressPanel.add(minAddressField, BorderLayout.CENTER);
|
||||
|
||||
JPanel maxAddressPanel = new JPanel();
|
||||
|
@ -78,9 +75,7 @@ public class AddressSetEditorPanel extends JPanel {
|
|||
JLabel maxLabel = new GDLabel("Max:");
|
||||
maxLabel.setToolTipText("Enter maximum address to add or remove");
|
||||
maxAddressPanel.add(maxLabel, BorderLayout.WEST);
|
||||
maxAddressField = new AddressInput();
|
||||
maxAddressField.setAddressFactory(addressFactory);
|
||||
maxAddressField.addChangeListener(listener);
|
||||
maxAddressField = new AddressInput(addressFactory, a -> validateAddRemoveButton());
|
||||
maxAddressPanel.add(maxAddressField, BorderLayout.CENTER);
|
||||
maxAddressPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 0, 0));
|
||||
addRangeButton = new GButton(ADD_ICON);
|
||||
|
|
|
@ -338,7 +338,7 @@ public class OptionsEditorPanel extends JPanel {
|
|||
return null;
|
||||
}
|
||||
AddressFactory addressFactory = addressFactoryService.getAddressFactory();
|
||||
AddressInput addressInput = new AddressInput();
|
||||
AddressInput addressInput = new AddressInput(a -> option.setValue(a));
|
||||
addressInput.setName(option.getName());
|
||||
Address addr = (Address) option.getValue();
|
||||
if (addr == null && addressFactory != null) {
|
||||
|
@ -347,7 +347,6 @@ public class OptionsEditorPanel extends JPanel {
|
|||
}
|
||||
addressInput.setAddressFactory(addressFactory);
|
||||
addressInput.setAddress(addr);
|
||||
addressInput.addChangeListener(e -> option.setValue(addressInput.getAddress()));// addressInput.addActionListener(e -> option.setValue(addressInput.getAddress()));
|
||||
return addressInput;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,9 +18,9 @@ package ghidra.app.util.cparser.CPP;
|
|||
|
||||
import java.util.*;
|
||||
|
||||
import generic.expressions.ExpressionEvaluator;
|
||||
import ghidra.app.util.cparser.CPP.PreProcessor.PPToken;
|
||||
import ghidra.program.model.data.*;
|
||||
import ghidra.program.util.AddressEvaluator;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
/**
|
||||
|
@ -288,7 +288,6 @@ public class DefineTable {
|
|||
sublist.addAll(initialList);
|
||||
}
|
||||
|
||||
|
||||
// don't replace an infinite number of times. Fail safe for possible ininite loop
|
||||
while (pos < buf.length() && replaceCount < ARBITRARY_MAX_REPLACEMENTS) {
|
||||
// clear list of used macros when move past replacement area
|
||||
|
@ -353,7 +352,8 @@ public class DefineTable {
|
|||
return true;
|
||||
}
|
||||
|
||||
int replace(StringBuffer buf, String currKey, int fromIndex, ArrayList<String> sublist, boolean initialList) {
|
||||
int replace(StringBuffer buf, String currKey, int fromIndex, ArrayList<String> sublist,
|
||||
boolean initialList) {
|
||||
String replacementString = null;
|
||||
|
||||
if (sublist == null) {
|
||||
|
@ -455,7 +455,7 @@ public class DefineTable {
|
|||
// Handle "..." varargs
|
||||
// if last argument is ellipsis, then is varargs, replace the rest of the params
|
||||
String curArgName = argv.elementAt(index).image;
|
||||
if (index == argv.size()-1 && VARARG_ELLIPSIS.equals(curArgName)) {
|
||||
if (index == argv.size() - 1 && VARARG_ELLIPSIS.equals(curArgName)) {
|
||||
isVarArg = true;
|
||||
// Replace __VA_ARGS__ with the rest of params
|
||||
curArgName = "__VA_ARGS__";
|
||||
|
@ -550,22 +550,23 @@ public class DefineTable {
|
|||
return;
|
||||
}
|
||||
|
||||
int lparen = buf.indexOf("(", optIdx+1);
|
||||
int lparen = buf.indexOf("(", optIdx + 1);
|
||||
if (lparen < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int rparen = buf.indexOf(")",lparen+1);
|
||||
int rparen = buf.indexOf(")", lparen + 1);
|
||||
if (rparen < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get in between string.
|
||||
String replarg = buf.substring(lparen+1, rparen);
|
||||
String replarg = buf.substring(lparen + 1, rparen);
|
||||
if (hadVarArgs) {
|
||||
buf.replace(optIdx, rparen+1, replarg);
|
||||
} else {
|
||||
buf.replace(optIdx, rparen+1, "");
|
||||
buf.replace(optIdx, rparen + 1, replarg);
|
||||
}
|
||||
else {
|
||||
buf.replace(optIdx, rparen + 1, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -720,7 +721,7 @@ public class DefineTable {
|
|||
|
||||
if (lvalue == null) {
|
||||
try {
|
||||
lvalue = AddressEvaluator.evaluateToLong(strValue);
|
||||
lvalue = ExpressionEvaluator.evaluateToLong(strValue);
|
||||
}
|
||||
catch (Exception exc) {
|
||||
// ignore didn't parse well
|
||||
|
@ -738,7 +739,8 @@ public class DefineTable {
|
|||
dtMgr.endTransaction(transactionID, true);
|
||||
}
|
||||
|
||||
public void populateDefineEquate(DataTypeManager openDTMgrs[], DataTypeManager dtMgr, String category, String prefix, String defName, long value) {
|
||||
public void populateDefineEquate(DataTypeManager openDTMgrs[], DataTypeManager dtMgr,
|
||||
String category, String prefix, String defName, long value) {
|
||||
String enumName = prefix + defName;
|
||||
|
||||
// Start the Enum at 8, then resize to fit the value
|
||||
|
|
|
@ -25,19 +25,17 @@ import docking.DockingWindowManager;
|
|||
import docking.widgets.label.GLabel;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
|
||||
public class AskAddrDialog extends DialogComponentProvider {
|
||||
private boolean isCanceled;
|
||||
private AddressInput addrInput;
|
||||
|
||||
public AskAddrDialog(final String title, final String message, AddressFactory af,
|
||||
public AskAddrDialog(final String title, final String message, Program program,
|
||||
Address lastAddr) {
|
||||
super(title, true, true, true, false);
|
||||
|
||||
addrInput = new AddressInput();
|
||||
addrInput.setAddressFactory(af);
|
||||
addrInput.selectDefaultAddressSpace();
|
||||
addrInput = new AddressInput(program);
|
||||
if (lastAddr != null) {
|
||||
addrInput.setAddress(lastAddr);
|
||||
}
|
||||
|
|
|
@ -251,7 +251,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> {
|
||||
addrField.setValue("0x200");
|
||||
addrField.setText("0x200");
|
||||
nameField.setText(".test");
|
||||
lengthField.setText("0x100");
|
||||
commentField.setText("this is a block test");
|
||||
|
@ -319,7 +319,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> {
|
||||
addrField.setValue("0x01001200");
|
||||
addrField.setText("0x01001200");
|
||||
nameField.setText(".test");
|
||||
lengthField.setText("0x100");
|
||||
commentField.setText("this is a block test");
|
||||
|
@ -354,7 +354,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> {
|
||||
addrField.setValue("xxxxx");
|
||||
addrField.setText("xxxxx");
|
||||
nameField.setText(".test");
|
||||
lengthField.setText("0x100");
|
||||
commentField.setText("this is a block test");
|
||||
|
@ -364,7 +364,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
assertFalse(okButton.isEnabled());
|
||||
|
||||
String msg = findLabelStr(d.getComponent(), "statusLabel");
|
||||
assertEquals("Please enter a valid Start Address", msg);
|
||||
assertEquals("Invalid Address: Could not evaluate token \"xxxxx\"", msg);
|
||||
assertFalse(okButton.isEnabled());
|
||||
close(d);
|
||||
}
|
||||
|
@ -768,7 +768,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
assertFalse(okButton.isEnabled());
|
||||
String msg = findLabelStr(d.getComponent(), "statusLabel");
|
||||
assertEquals("Please enter a source address for the bit block", msg);
|
||||
runSwing(() -> addrField.setValue("01001000"));
|
||||
runSwing(() -> addrField.setText("01001000"));
|
||||
}
|
||||
else {
|
||||
assertTrue(okButton.isEnabled());
|
||||
|
@ -844,7 +844,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
assertFalse(okButton.isEnabled());
|
||||
String msg = findLabelStr(d.getComponent(), "statusLabel");
|
||||
assertEquals("Please enter a source address for the bit block", msg);
|
||||
runSwing(() -> addrField.setValue("01001000"));
|
||||
runSwing(() -> addrField.setText("01001000"));
|
||||
}
|
||||
else {
|
||||
assertTrue(okButton.isEnabled());
|
||||
|
@ -1040,7 +1040,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
// move the block to 0x300
|
||||
Address newStart = startAddr.getAddressSpace().getAddressInThisSpaceOnly(0x300);
|
||||
Address newEnd = startAddr.getAddressSpace().getAddressInThisSpaceOnly(0x3ff);
|
||||
runSwing(() -> startField.setValue(newStart));
|
||||
runSwing(() -> startField.simulateAddressChanged(newStart));
|
||||
assertEquals(newEnd, endField.getAddress());
|
||||
assertTrue(okButton.isEnabled());
|
||||
|
||||
|
@ -1110,7 +1110,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
assertFalse(okButton.isEnabled());
|
||||
|
||||
// enter an invalid address
|
||||
runSwing(() -> startField.setValue(getAddr(0x0300).toString() + "gggg"));
|
||||
runSwing(() -> startField.setText(getAddr(0x0300).toString() + "gggg"));
|
||||
assertFalse(okButton.isEnabled());
|
||||
String msg = findLabelStr(d.getComponent(), "statusLabel");
|
||||
assertEquals("Invalid Address", msg);
|
||||
|
@ -1168,7 +1168,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
assertFalse(okButton.isEnabled());
|
||||
|
||||
// enter an invalid address
|
||||
runSwing(() -> endField.setValue(getAddr(0x0300).toString() + "gggg"));
|
||||
runSwing(() -> endField.setText(getAddr(0x0300).toString() + "gggg"));
|
||||
assertFalse(okButton.isEnabled());
|
||||
String msg = findLabelStr(d.getComponent(), "statusLabel");
|
||||
assertEquals("Invalid Address", msg);
|
||||
|
@ -1227,8 +1227,8 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
|
||||
// enter an invalid address
|
||||
runSwing(() -> {
|
||||
startField.setValue(getAddr(0x1000).toString());
|
||||
endField.setValue(getAddr(0x10).toString());
|
||||
startField.setText(getAddr(0x1000).toString());
|
||||
endField.setText(getAddr(0x10).toString());
|
||||
});
|
||||
assertFalse(okButton.isEnabled());
|
||||
String msg = findLabelStr(d.getComponent(), "statusLabel");
|
||||
|
@ -1287,7 +1287,7 @@ public class MemoryMapProvider2Test extends AbstractGhidraHeadedIntegrationTest
|
|||
assertFalse(okButton.isEnabled());
|
||||
|
||||
// enter an invalid address
|
||||
runSwing(() -> startField.setValue("00000000"));
|
||||
runSwing(() -> startField.setText("00000000"));
|
||||
assertFalse(okButton.isEnabled());
|
||||
String msg = findLabelStr(d.getComponent(), "statusLabel");
|
||||
assertEquals("Block is already at 00000000", msg);
|
||||
|
|
|
@ -231,7 +231,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest
|
|||
(RegisterField) findComponentByName(d.getComponent(), "BlockTwoLength");
|
||||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> blockOneEnd.setValue("01003000"));
|
||||
runSwing(() -> blockOneEnd.setText("01003000"));
|
||||
assertEquals(0x2001, blockOneLength.getValue().longValue());
|
||||
assertEquals(getAddr(0x01003001), blockTwoStart.getAddress());
|
||||
assertEquals("010075ff", blockTwoEnd.getText());
|
||||
|
@ -277,7 +277,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest
|
|||
(RegisterField) findComponentByName(d.getComponent(), "BlockTwoLength");
|
||||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> blockTwoStart.setValue("01003000"));
|
||||
runSwing(() -> blockTwoStart.setText("01003000"));
|
||||
assertEquals(0x2000, blockOneLength.getValue().longValue());
|
||||
assertEquals(getAddr(0x01002fff), blockOneEnd.getAddress());
|
||||
assertEquals("010075ff", blockTwoEnd.getText());
|
||||
|
@ -357,7 +357,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest
|
|||
(AddressInput) findComponentByName(d.getComponent(), "BlockOneEnd");
|
||||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> blockOneEnd.setValue("01000"));
|
||||
runSwing(() -> blockOneEnd.setText("01000"));
|
||||
assertFalse(okButton.isEnabled());
|
||||
assertEquals("End address must be greater than start",
|
||||
findLabelStr(d.getComponent(), "statusLabel"));
|
||||
|
@ -378,7 +378,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest
|
|||
(AddressInput) findComponentByName(d.getComponent(), "BlockTwoStart");
|
||||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> blockTwoStart.setValue("01000"));
|
||||
runSwing(() -> blockTwoStart.setText("01000"));
|
||||
assertFalse(okButton.isEnabled());
|
||||
assertEquals("Start address must be greater than original block start (01001000)",
|
||||
findLabelStr(d.getComponent(), "statusLabel"));
|
||||
|
@ -531,7 +531,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest
|
|||
RegisterField length = (RegisterField) findComponentByName(d.getComponent(), "BlockLength");
|
||||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> start.setValue("00002000"));
|
||||
runSwing(() -> start.setText("00002000"));
|
||||
assertEquals("0x1005600", length.getText());
|
||||
|
||||
assertTrue(okButton.isEnabled());
|
||||
|
@ -579,7 +579,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest
|
|||
assertNotNull(length);
|
||||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> start.setValue("01201000"));
|
||||
runSwing(() -> start.setText("01201000"));
|
||||
assertFalse(okButton.isEnabled());
|
||||
assertEquals("Start must be less than 01001000",
|
||||
findLabelStr(d.getComponent(), "statusLabel"));
|
||||
|
@ -681,7 +681,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest
|
|||
JTextField end = (JTextField) findComponentByName(d.getComponent(), "EndAddress");
|
||||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> start.setValue("01008000"));
|
||||
runSwing(() -> start.setText("01008000"));
|
||||
assertEquals("0100f3ff", end.getText());
|
||||
assertTrue(okButton.isEnabled());
|
||||
|
||||
|
@ -740,7 +740,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest
|
|||
RegisterField length = (RegisterField) findComponentByName(d.getComponent(), "BlockLength");
|
||||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> end.setValue("01007700"));
|
||||
runSwing(() -> end.setText("01007700"));
|
||||
assertEquals("0x6701", length.getText());
|
||||
assertTrue(okButton.isEnabled());
|
||||
|
||||
|
@ -798,7 +798,7 @@ public class MemoryMapProvider3Test extends AbstractGhidraHeadedIntegrationTest
|
|||
AddressInput end = (AddressInput) findComponentByName(d.getComponent(), "EndAddress");
|
||||
JButton okButton = findButton(d.getComponent(), "OK");
|
||||
|
||||
runSwing(() -> end.setValue("01007000"));
|
||||
runSwing(() -> end.setText("01007000"));
|
||||
assertFalse(okButton.isEnabled());
|
||||
assertEquals("End must be greater than 010075ff",
|
||||
findLabelStr(d.getComponent(), "statusLabel"));
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
/* ###
|
||||
* 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 static org.junit.Assert.*;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
|
||||
import org.junit.*;
|
||||
|
||||
import ghidra.program.database.*;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
|
||||
public class AddressInputTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
|
||||
private JFrame frame;
|
||||
private AddressInput field;
|
||||
private ProgramDB programOneSpace;
|
||||
private ProgramDB programMultiSpaces;
|
||||
private volatile Address changedAddress;
|
||||
private volatile String errorMessage;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
programOneSpace = createDefaultProgram("oneSpace", ProgramBuilder._TOY, this);
|
||||
programMultiSpaces = createDefaultProgram("mulitSpaces", ProgramBuilder._8051, this);
|
||||
field = new AddressInput(programOneSpace, this::addressChanged);
|
||||
field.setAddressErrorConsumer(this::addressError);
|
||||
frame = new JFrame("Test");
|
||||
frame.getContentPane().add(field);
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
frame.setVisible(false);
|
||||
programOneSpace.release(this);
|
||||
programMultiSpaces.release(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultState() {
|
||||
assertTrue(getText().isBlank());
|
||||
assertNull(getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexOrDecimalMode() {
|
||||
setText("100"); // should have defaulted to hex mode
|
||||
assertEquals(addr(0x100), getAddress());
|
||||
|
||||
setHexMode(false);
|
||||
assertEquals(addr(100), field.getAddress());
|
||||
|
||||
setHexMode(true);
|
||||
assertEquals(addr(0x100), field.getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwitchingBetweenOneAndMultiSpaces() {
|
||||
assertEquals(1, field.getComponentCount());
|
||||
setProgram(programMultiSpaces);
|
||||
assertEquals(2, field.getComponentCount());
|
||||
setProgram(programOneSpace);
|
||||
assertEquals(1, field.getComponentCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetAddress() {
|
||||
setAddress(addr(0x100));
|
||||
assertEquals("100", getText());
|
||||
setHexMode(false);
|
||||
setAddress(addr(0x100));
|
||||
assertEquals("0x100", getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettingAddressChangesResultingSpace() {
|
||||
setProgram(programMultiSpaces);
|
||||
setText("100");
|
||||
Address a = getAddress();
|
||||
assertEquals("CODE:0100", a.toString(true));
|
||||
Address newAddress = addr(programMultiSpaces, "EXTMEM", 0x20);
|
||||
setAddress(newAddress);
|
||||
setText("100");
|
||||
a = getAddress();
|
||||
assertEquals("EXTMEM:0100", a.toString(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAddressWithBadExpression() {
|
||||
setText("100+ (");
|
||||
assertNull(getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorksWithJustAddressFactory() {
|
||||
setAddressFactory(programMultiSpaces.getAddressFactory());
|
||||
setText("100");
|
||||
assertEquals(addr(programMultiSpaces, 0x100), getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSelectedAddressSpace() {
|
||||
setProgram(programMultiSpaces);
|
||||
ProgramAddressFactory factory = programMultiSpaces.getAddressFactory();
|
||||
AddressSpace codeSpace = factory.getAddressSpace("CODE");
|
||||
AddressSpace extmemSpace = factory.getAddressSpace("EXTMEM");
|
||||
|
||||
assertEquals(codeSpace, getAddressSpaceInField());
|
||||
setAddress(addr(programMultiSpaces, "EXTMEM", 100));
|
||||
assertEquals(extmemSpace, getAddressSpaceInField());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpaceFilter() {
|
||||
setProgram(programMultiSpaces);
|
||||
setSpaceFilter(s -> s.getName().equals("EXTMEM"));
|
||||
assertEquals(1, field.getComponentCount());
|
||||
setText("100");
|
||||
assertEquals(addr(programMultiSpaces, "EXTMEM", 0x100), getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddressChangeConsumer() {
|
||||
setText("200");
|
||||
assertEquals(addr(0x200), changedAddress);
|
||||
setText("300");
|
||||
assertEquals(addr(0x300), changedAddress);
|
||||
setText("lkjlkj");
|
||||
assertNull(changedAddress);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddressErrorConsmer() {
|
||||
errorMessage = null;
|
||||
setText("200");
|
||||
assertNull(errorMessage);
|
||||
|
||||
setText("xyz");
|
||||
assertEquals("Could not evaluate token \"xyz\"", errorMessage);
|
||||
}
|
||||
|
||||
private Address addr(long offset) {
|
||||
return addr(programOneSpace, offset);
|
||||
}
|
||||
|
||||
private Address addr(ProgramDB p, long offset) {
|
||||
return p.getAddressFactory().getDefaultAddressSpace().getAddress(offset);
|
||||
}
|
||||
|
||||
private Address addr(ProgramDB p, String spaceName, long offset) {
|
||||
AddressSpace space = p.getAddressFactory().getAddressSpace(spaceName);
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
private void setProgram(Program p) {
|
||||
runSwing(() -> field.setProgram(p));
|
||||
}
|
||||
|
||||
private void setAddressFactory(AddressFactory factory) {
|
||||
runSwing(() -> field.setAddressFactory(factory));
|
||||
}
|
||||
|
||||
private void setSpaceFilter(Predicate<AddressSpace> filter) {
|
||||
runSwing(() -> field.setAddressSpaceFilter(filter));
|
||||
}
|
||||
|
||||
private void setText(String value) {
|
||||
runSwing(() -> field.setText(value));
|
||||
}
|
||||
|
||||
private String getText() {
|
||||
return runSwing(() -> field.getText());
|
||||
}
|
||||
|
||||
private void setAddress(Address a) {
|
||||
runSwing(() -> field.setAddress(a));
|
||||
}
|
||||
|
||||
private Address getAddress() {
|
||||
return runSwing(() -> field.getAddress());
|
||||
}
|
||||
|
||||
private AddressSpace getAddressSpaceInField() {
|
||||
return runSwing(() -> field.getAddressSpace());
|
||||
}
|
||||
|
||||
private void setHexMode(boolean hexMode) {
|
||||
runSwing(() -> field.setAssumeHex(hexMode));
|
||||
waitForSwing();
|
||||
}
|
||||
|
||||
private void addressChanged(Address address) {
|
||||
this.changedAddress = address;
|
||||
}
|
||||
|
||||
private void addressError(String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
}
|
|
@ -21,7 +21,6 @@ import org.junit.Test;
|
|||
|
||||
import docking.widgets.values.AbstractValue;
|
||||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.features.base.values.AddressValue;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
|
||||
|
@ -144,7 +143,7 @@ public class AddressValueTest extends AbstractValueIntegrationTest {
|
|||
protected void setTextOnAddressInput(AbstractValue<?> nameValue, String text) {
|
||||
runSwing(() -> {
|
||||
AddressInput addressInput = (AddressInput) nameValue.getComponent();
|
||||
addressInput.setValue(text);
|
||||
addressInput.setText(text);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public class ValuesMapDialogParseErrorTest extends AbstractValueIntegrationTest
|
|||
protected void setTextOnAddressInput(AbstractValue<?> nameValue, String text) {
|
||||
runSwing(() -> {
|
||||
AddressInput addressInput = (AddressInput) nameValue.getComponent();
|
||||
addressInput.setValue(text);
|
||||
addressInput.setText(text);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,103 +17,146 @@ package ghidra.program.util;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.program.database.ProgramBuilder;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.SourceType;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* TODO To change the template for this generated type comment go to
|
||||
* Window - Preferences - Java - Code Style - Code Templates
|
||||
*/
|
||||
public class AddressEvaluatorTest extends AbstractGhidraHeadedIntegrationTest {
|
||||
public class AddressEvaluatorTest extends AbstractGenericTest {
|
||||
|
||||
AddressFactory addrFactory;
|
||||
private Program program;
|
||||
private ProgramBuilder builder;
|
||||
private Symbol entry;
|
||||
private Symbol entryInFoo;
|
||||
|
||||
public AddressEvaluatorTest() {
|
||||
super();
|
||||
}
|
||||
|
||||
private Address addr(String address) {
|
||||
return addrFactory.getAddress(address);
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
builder = new ProgramBuilder("Test", ProgramBuilder._TOY_LE, this);
|
||||
builder.createMemory("TestBlock", "0x100", 100);
|
||||
program = builder.getProgram();
|
||||
entry = builder.createLabel("0x100", "entry");
|
||||
builder.createNamespace("foo");
|
||||
entryInFoo = builder.createLabel("0x103", "entry", "foo");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
program.release(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEval() throws Exception {
|
||||
Program p = createDefaultProgram("Test", ProgramBuilder._TOY_LE, this);
|
||||
addrFactory = p.getAddressFactory();
|
||||
int txId = p.startTransaction("Test");
|
||||
try {
|
||||
assertEquals(addr("0x19"), AddressEvaluator.evaluate(p, "(2+3)*5"));
|
||||
assertEquals(addr("0x11"), AddressEvaluator.evaluate(p, "2+3*5"));
|
||||
assertEquals(addr("0x11"), AddressEvaluator.evaluate(p, "2+(3*5)"));
|
||||
assertEquals(addr("0x11"), AddressEvaluator.evaluate(p, "(2+3*5)"));
|
||||
assertEquals(addr("0x16"), AddressEvaluator.evaluate(p, "0x11+5"));
|
||||
assertEquals(addr("0x02"), AddressEvaluator.evaluate(p, "2-1+1"));
|
||||
assertEquals(addr("0x5"), AddressEvaluator.evaluate(p, "5"));
|
||||
assertEquals(addr("0x3"), AddressEvaluator.evaluate(p, "0-5+8"));
|
||||
assertEquals(addr("0x3"), AddressEvaluator.evaluate(p, "-5+8"));
|
||||
assertEquals(addr("0xfffffffB"), AddressEvaluator.evaluate(p, "-5"));
|
||||
assertEquals(addr("0x11"), AddressEvaluator.evaluate(p, "3+(5+(3*2)+(3))"));
|
||||
assertEquals(addr("0xff00"), AddressEvaluator.evaluate(p, "0xffff ^ 0xff"));
|
||||
assertEquals(addr("0x123f"), AddressEvaluator.evaluate(p, "0xffff & 0x123f"));
|
||||
assertEquals(addr("0x1234"), AddressEvaluator.evaluate(p, "0x1200 | 0x0034"));
|
||||
assertEquals(addr("0xffffffff"), AddressEvaluator.evaluate(p, "~ 0x0"));
|
||||
assertEquals(addr("0x1201"), AddressEvaluator.evaluate(p, "0x1200 | ~(0xfffffffe)"));
|
||||
assertEquals(addr("0x480"), AddressEvaluator.evaluate(p, "0x1200 >> 2"));
|
||||
assertEquals(addr("0x1200"), AddressEvaluator.evaluate(p, "0x480 << 2"));
|
||||
|
||||
assertEquals(addr("0x1"), AddressEvaluator.evaluate(p, "(((0x1 | 0x2) & 0x2) == 0x2)"));
|
||||
assertEquals(addr("0x0"), AddressEvaluator.evaluate(p, "(((0x1 | 0x2) & 0x2) == 0x1)"));
|
||||
assertEquals(addr("0x0"), AddressEvaluator.evaluate(p, "(((0x1 | 0x2) & 0x2) == 0x1)"));
|
||||
|
||||
assertEquals(addr("0x1"), AddressEvaluator.evaluate(p, "(((0x1 | 0x2) & 0x2) >= 0x1)"));
|
||||
assertEquals(addr("0x0"), AddressEvaluator.evaluate(p, "(((0x1 | 0x2) & 0x2) <= 0x1)"));
|
||||
|
||||
assertEquals(addr("0x4"), AddressEvaluator.evaluate(p, "(4ul)"));
|
||||
assertEquals(addr("0x4"), AddressEvaluator.evaluate(p, "(4UL)"));
|
||||
assertEquals(addr("0x4"), AddressEvaluator.evaluate(p, "( 4l )"));
|
||||
assertEquals(addr("0x4"), AddressEvaluator.evaluate(p, "(4L)"));
|
||||
assertEquals(addr("0x4"), AddressEvaluator.evaluate(p, "(4u )"));
|
||||
assertEquals(addr("0x4"), AddressEvaluator.evaluate(p, "( 4U)"));
|
||||
|
||||
assertEquals(null, AddressEvaluator.evaluate(p, "( 4P)"));
|
||||
|
||||
Symbol s = p.getSymbolTable().createLabel(addr("0x100"), "entry", SourceType.IMPORTED);
|
||||
Address a = s.getAddress();
|
||||
a = a.add(10);
|
||||
assertEquals(a, AddressEvaluator.evaluate(p, "entry+5*2"));
|
||||
assertEquals(addr("0x101"), AddressEvaluator.evaluate(p, "entry + (entry == 0x100)"));
|
||||
assertEquals(addr("0x500"), AddressEvaluator.evaluate(p,
|
||||
"entry + (entry == 0x100) * 0x400 + (entry < 0x100) * 0x500"));
|
||||
assertEquals(addr("0x600"), AddressEvaluator.evaluate(p,
|
||||
"entry + (entry > 0x100) * 0x400 + (entry <= 0x100) * 0x500"));
|
||||
public void testLongValueExpression() {
|
||||
assertEval(addr("0x19"), "(2+3)*5");
|
||||
assertEval(addr("0x11"), "2+3*5");
|
||||
assertEval(addr("0x11"), "2+(3*5)");
|
||||
assertEval(addr("0x3"), "0-5+8");
|
||||
assertEval(addr("0x3"), "-5+8");
|
||||
assertEval(addr("0xfffffffB"), "-5");
|
||||
assertEval(addr("0x11"), "3+(5+(3*2)+(3))");
|
||||
}
|
||||
finally {
|
||||
p.endTransaction(txId, true);
|
||||
p.release(this);
|
||||
|
||||
@Test
|
||||
public void testAssumesHex() {
|
||||
assertEval(addr("0x30"), "20 + 10");
|
||||
assertEval(addr("0xf1"), "e1+10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptsHexPrefix() {
|
||||
assertEval(addr("0x16"), "0x11+5");
|
||||
assertEval(addr("0x35"), "20+0x15");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitWiseExpressions() {
|
||||
|
||||
assertEval(addr("0xff00"), "0xffff ^ 0xff");
|
||||
assertEval(addr("0x123f"), "0xffff & 0x123f");
|
||||
assertEval(addr("0x1234"), "0x1200 | 0x0034");
|
||||
assertEval(addr("0xffffffff"), "~ 0x0");
|
||||
assertEval(addr("0x1201"), "0x1200 | ~(0xfffffffe)");
|
||||
assertEval(addr("0x480"), "0x1200 >> 2");
|
||||
assertEval(addr("0x1200"), "0x480 << 2");
|
||||
assertEval(addr("0x103"), "0x100 | 0x1 | ~(~0x2)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogicalExpressions() {
|
||||
assertEval(addr("0x1"), "(((0x1 | 0x2) & 0x2) == 0x2)");
|
||||
assertEval(addr("0x0"), "(((0x1 | 0x2) & 0x2) == 0x1)");
|
||||
assertEval(addr("0x0"), "(((0x1 | 0x2) & 0x2) == 0x1)");
|
||||
|
||||
assertEval(addr("0x1"), "(((0x1 | 0x2) & 0x2) >= 0x1)");
|
||||
assertEval(addr("0x0"), "(((0x1 | 0x2) & 0x2) <= 0x1)");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAlternateNumberDecorations() {
|
||||
assertEval(addr("0x4"), "(4ul)");
|
||||
assertEval(addr("0x4"), "(4UL)");
|
||||
assertEval(addr("0x4"), "( 4l )");
|
||||
assertEval(addr("0x4"), "(4L)");
|
||||
assertEval(addr("0x4"), "(4u )");
|
||||
assertEval(addr("0x4"), "( 4U)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidInput() {
|
||||
assertEval(null, "( 4P)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShift() {
|
||||
assertEval(addr("0x80"), "0x100 >> 1");
|
||||
assertEval(addr("0x400"), "0x100 << 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSymbolLookup() {
|
||||
assertEval(entry.getAddress(), "entry");
|
||||
assertEval(entry.getAddress().add(10), "entry+5*2");
|
||||
assertEval(addr("0x101"), "entry + (entry == 0x100)");
|
||||
assertEval(addr("0x500"), "entry + (entry == 0x100) * 0x400 + (entry < 0x100) * 0x500");
|
||||
assertEval(addr("0x600"), "entry + (entry > 0x100) * 0x400 + (entry <= 0x100) * 0x500");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSymbolInNamespaceLookup() {
|
||||
assertEval(entryInFoo.getAddress(), "foo::entry");
|
||||
assertEval(entryInFoo.getAddress().add(10), "foo::entry+5*2");
|
||||
assertEval(null, "bar::entry");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSymbolShift() {
|
||||
assertEval(addr("0x80"), "entry >> 1");
|
||||
assertEval(addr("0x400"), "entry << 2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemoryBlockOffset() {
|
||||
assertEval(addr("0x110"), "TestBlock+10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiAddrSpace() throws Exception {
|
||||
Program p = createDefaultProgram("Test", ProgramBuilder._TOY_LE, this);
|
||||
addrFactory = p.getAddressFactory();
|
||||
try {
|
||||
assertEquals(addr("0x19"), AddressEvaluator.evaluate(p, "(2+3)*5"));
|
||||
assertEquals(addr("0x11"), AddressEvaluator.evaluate(p, "2+3*5"));
|
||||
assertEquals(addr("0x11"), AddressEvaluator.evaluate(p, "2+(3*5)"));
|
||||
assertEquals(addr("RAM:15"), AddressEvaluator.evaluate(p, "RAM:2 + 0x13"));
|
||||
assertEval(addr("0x15"), "ram:2 + 0x13");
|
||||
assertEval(addr("register:0x15"), "register:2 + 0x13");
|
||||
}
|
||||
finally {
|
||||
p.release(this);
|
||||
|
||||
private void assertEval(Address addr, String input) {
|
||||
assertEquals(addr, AddressEvaluator.evaluate(program, input));
|
||||
}
|
||||
|
||||
private Address addr(String address) {
|
||||
return program.getAddressFactory().getAddress(address);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,12 +81,11 @@ public class ByteViewerOptionsDialog extends DialogComponentProvider
|
|||
if (provider instanceof ProgramByteViewerComponentProvider) {
|
||||
Program program = ((ProgramByteViewerComponentProvider) provider).getProgram();
|
||||
if (program != null) {
|
||||
addressInputField = new AddressInput();
|
||||
addressInputField.setAddressFactory(program.getAddressFactory());
|
||||
addressInputField.showAddressSpaceCombo(false);
|
||||
addressInputField.setAddress(getAlignmentAddress());
|
||||
Address alignment = getAlignmentAddress();
|
||||
addressInputField = new AddressInput(program, a -> update());
|
||||
addressInputField.setAddressSpaceFilter(s -> s == alignment.getAddressSpace());
|
||||
addressInputField.setAddress(alignment);
|
||||
panel.add(addressInputField);
|
||||
addressInputField.addChangeListener(this);
|
||||
addressInputField.setAccessibleName("Alignment Address");
|
||||
}
|
||||
}
|
||||
|
@ -214,13 +213,13 @@ public class ByteViewerOptionsDialog extends DialogComponentProvider
|
|||
}
|
||||
|
||||
private boolean hasValidFieldValues() {
|
||||
if (addressInputField.getValue().length() == 0) {
|
||||
if (addressInputField.getText().length() == 0) {
|
||||
setStatusText("Enter an alignment address");
|
||||
return false;
|
||||
}
|
||||
Address alignmentAddress = addressInputField.getAddress();
|
||||
if (alignmentAddress == null) {
|
||||
setStatusText("Invalid alignment address:" + addressInputField.getValue());
|
||||
setStatusText("Invalid alignment address:" + addressInputField.getText());
|
||||
return false;
|
||||
}
|
||||
BigInteger bytesPerLine = bytesPerLineField.getValue();
|
||||
|
|
|
@ -553,7 +553,7 @@ public class ByteViewerPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
// current offset is 0
|
||||
assertEquals(program.getMinAddress(), ai.getAddress());
|
||||
|
||||
runSwing(() -> ai.setValue("0100100b"));
|
||||
runSwing(() -> ai.setText("0100100b"));
|
||||
pressButtonByText(d.getComponent(), "OK");
|
||||
// verify that offset label on the plugin shows '5'
|
||||
assertEquals(5, plugin.getProvider().getOffset());
|
||||
|
@ -587,7 +587,7 @@ public class ByteViewerPlugin1Test extends AbstractGhidraHeadedIntegrationTest {
|
|||
// current offset is 0
|
||||
assertEquals(program.getMinAddress(), ai.getAddress());
|
||||
|
||||
runSwing(() -> ai.setValue("0000:0c06"));
|
||||
runSwing(() -> ai.setText("0000:0c06"));
|
||||
pressButtonByText(d.getComponent(), "OK");
|
||||
|
||||
assertEquals(10, plugin.getProvider().getOffset());
|
||||
|
|
|
@ -55,8 +55,7 @@ public class EditableListingAddress extends DisplayableListingAddress implements
|
|||
private void buildPanel() {
|
||||
setLayout(new PairLayout(5, 5, 50));
|
||||
|
||||
addressField = new AddressInput();
|
||||
addressField.setAddressFactory(program.getAddressFactory());
|
||||
addressField = new AddressInput(program);
|
||||
if (address != null) {
|
||||
addressField.setAddress(address);
|
||||
}
|
||||
|
@ -78,7 +77,7 @@ public class EditableListingAddress extends DisplayableListingAddress implements
|
|||
Address selectedAddress = addressField.getAddress();
|
||||
if (selectedAddress == null) {
|
||||
throw new InvalidInputException(
|
||||
"\"" + addressField.getValue() + "\" is not a valid address.");
|
||||
"\"" + addressField.getText() + "\" is not a valid address.");
|
||||
}
|
||||
if (!program.getMemory().contains(selectedAddress)) {
|
||||
throw new InvalidInputException(
|
||||
|
|
|
@ -24,7 +24,6 @@ import docking.widgets.label.GDLabel;
|
|||
import ghidra.app.util.AddressInput;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressFactory;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
@ -32,7 +31,6 @@ import ghidra.util.layout.PairLayout;
|
|||
public class AddRemoveAddressRangeDialog extends DialogComponentProvider {
|
||||
|
||||
private Program program;
|
||||
private AddressFactory addressFactory;
|
||||
private AddressRangeListener listener;
|
||||
|
||||
private JPanel addressRangePanel;
|
||||
|
@ -45,7 +43,6 @@ public class AddRemoveAddressRangeDialog extends DialogComponentProvider {
|
|||
AddressRangeListener listener) {
|
||||
super(programIndicator + " Address Range", true, true, true, false);
|
||||
this.program = program;
|
||||
addressFactory = program.getAddressFactory();
|
||||
this.listener = listener;
|
||||
setHelpLocation(new HelpLocation(HelpTopics.LABEL, "AddEditDialog"));
|
||||
addWorkPanel(createAddressRangePanel());
|
||||
|
@ -68,8 +65,7 @@ public class AddRemoveAddressRangeDialog extends DialogComponentProvider {
|
|||
minLabel.setToolTipText("Enter minimum address to add or remove");
|
||||
addressRangePanel.add(minLabel);
|
||||
|
||||
minAddressField = new AddressInput();
|
||||
minAddressField.setAddressFactory(addressFactory);
|
||||
minAddressField = new AddressInput(program);
|
||||
Dimension minPreferredSize = getPreferredSize();
|
||||
minPreferredSize.width = 200;
|
||||
minAddressField.setPreferredSize(minPreferredSize);
|
||||
|
@ -79,8 +75,7 @@ public class AddRemoveAddressRangeDialog extends DialogComponentProvider {
|
|||
maxLabel.setToolTipText("Enter maximum address to add or remove");
|
||||
addressRangePanel.add(maxLabel);
|
||||
|
||||
maxAddressField = new AddressInput();
|
||||
maxAddressField.setAddressFactory(addressFactory);
|
||||
maxAddressField = new AddressInput(program);
|
||||
Dimension maxPreferredSize = getPreferredSize();
|
||||
maxPreferredSize.width = 200;
|
||||
minAddressField.setPreferredSize(maxPreferredSize);
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/* ###
|
||||
* 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 docking.widgets.textfield;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ToolTipManager;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.util.GraphicsUtils;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import generic.theme.Gui;
|
||||
|
||||
/**
|
||||
* Overrides the JTextField mainly to allow hint painting for the current radix mode.
|
||||
*/
|
||||
public class HexDecimalModeTextField extends JTextField {
|
||||
|
||||
private static final String FONT_ID = "font.input.hint";
|
||||
private int hintWidth;
|
||||
private boolean isHexMode;
|
||||
private boolean showNumbericDecoration = true;
|
||||
|
||||
public HexDecimalModeTextField(int columns, Consumer<Boolean> modeConsumer) {
|
||||
super(columns);
|
||||
|
||||
FontMetrics fontMetrics = getFontMetrics(Gui.getFont(FONT_ID));
|
||||
String mode = isHexMode ? "Hex" : "Dec";
|
||||
hintWidth = fontMetrics.stringWidth(mode);
|
||||
|
||||
addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_M && DockingUtils.isControlModifier(e)) {
|
||||
isHexMode = !isHexMode;
|
||||
modeConsumer.accept(isHexMode);
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// make sure tooltips will be activated
|
||||
ToolTipManager.sharedInstance().registerComponent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(MouseEvent event) {
|
||||
|
||||
int hintStart = getBounds().width - hintWidth;
|
||||
if (event.getX() > hintStart) {
|
||||
String key = DockingUtils.CONTROL_KEY_NAME;
|
||||
return "Press '" + key + "-M' to toggle Hex or Decimal Mode";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setHexMode(boolean hexMode) {
|
||||
this.isHexMode = hexMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on or off the faded text that displays the field's radix mode (hex or decimal).
|
||||
*
|
||||
* @param show true to show the radix mode.
|
||||
*/
|
||||
public void setShowNumberMode(boolean show) {
|
||||
this.showNumbericDecoration = show;
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
if (!showNumbericDecoration) {
|
||||
return;
|
||||
}
|
||||
|
||||
Font savedFont = g.getFont();
|
||||
g.setFont(Gui.getFont(FONT_ID));
|
||||
g.setColor(Messages.HINT);
|
||||
|
||||
Dimension size = getSize();
|
||||
Insets insets = getInsets();
|
||||
int x;
|
||||
if (getHorizontalAlignment() == RIGHT) {
|
||||
x = insets.left;
|
||||
}
|
||||
else {
|
||||
x = size.width - insets.right - hintWidth;
|
||||
}
|
||||
int y = size.height - insets.bottom - 1;
|
||||
String mode = isHexMode ? "Hex" : "Dec";
|
||||
GraphicsUtils.drawString(this, g, mode, x, y);
|
||||
g.setFont(savedFont);
|
||||
}
|
||||
|
||||
}
|
|
@ -15,20 +15,16 @@
|
|||
*/
|
||||
package docking.widgets.textfield;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.util.GraphicsUtils;
|
||||
import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
/**
|
||||
|
@ -63,12 +59,11 @@ import ghidra.util.SystemUtilities;
|
|||
*/
|
||||
|
||||
public class IntegerTextField {
|
||||
private JTextField textField;
|
||||
private HexDecimalModeTextField textField;
|
||||
|
||||
private boolean isHexMode = false;
|
||||
private boolean allowsNegative = true;
|
||||
private boolean allowsHexPrefix = true;
|
||||
private boolean showNumbericDecoration = true;
|
||||
private BigInteger maxValue;
|
||||
private BigInteger minValue;
|
||||
|
||||
|
@ -109,9 +104,13 @@ public class IntegerTextField {
|
|||
* @param initialValue the initial value
|
||||
*/
|
||||
public IntegerTextField(int columns, BigInteger initialValue) {
|
||||
textField = new MyTextField(columns);
|
||||
textField = new HexDecimalModeTextField(columns, b -> textFieldHexModeChanged(b));
|
||||
|
||||
AbstractDocument document = (AbstractDocument) textField.getDocument();
|
||||
document.setDocumentFilter(new HexDecimalDocumentFilter());
|
||||
setValue(initialValue);
|
||||
textField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
|
||||
document.addDocumentListener(new DocumentListener() {
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
|
@ -266,8 +265,7 @@ public class IntegerTextField {
|
|||
* @param show true to show the radix mode.
|
||||
*/
|
||||
public void setShowNumberMode(boolean show) {
|
||||
this.showNumbericDecoration = show;
|
||||
textField.repaint();
|
||||
textField.setShowNumberMode(show);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -278,9 +276,15 @@ public class IntegerTextField {
|
|||
* the current value from decimal to hex.
|
||||
*/
|
||||
public void setHexMode() {
|
||||
BigInteger currentValue = getValue();
|
||||
isHexMode = true;
|
||||
updateTextField(currentValue);
|
||||
BigInteger value = getValue();
|
||||
setHexMode(true);
|
||||
setValue(value);
|
||||
|
||||
}
|
||||
|
||||
private void setHexMode(boolean hexMode) {
|
||||
this.isHexMode = hexMode;
|
||||
textField.setHexMode(hexMode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -291,9 +295,9 @@ public class IntegerTextField {
|
|||
* current value from hex to decimal.
|
||||
*/
|
||||
public void setDecimalMode() {
|
||||
BigInteger currentValue = getValue();
|
||||
isHexMode = false;
|
||||
updateTextField(currentValue);
|
||||
BigInteger value = getValue();
|
||||
setHexMode(false);
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -477,6 +481,12 @@ public class IntegerTextField {
|
|||
textField.setHorizontalAlignment(alignment);
|
||||
}
|
||||
|
||||
private void textFieldHexModeChanged(boolean hexMode) {
|
||||
BigInteger value = getValue();
|
||||
this.isHexMode = hexMode;
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
private String computeTextForValue(BigInteger value) {
|
||||
if (value == null) {
|
||||
return "";
|
||||
|
@ -525,15 +535,6 @@ public class IntegerTextField {
|
|||
}
|
||||
}
|
||||
|
||||
private void toggleMode() {
|
||||
if (isHexMode) {
|
||||
setDecimalMode();
|
||||
}
|
||||
else {
|
||||
setHexMode();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean passesMaxCheck(BigInteger value) {
|
||||
if (value == null) {
|
||||
return true;
|
||||
|
@ -648,7 +649,7 @@ public class IntegerTextField {
|
|||
if (isValidPrefix(valueString)) {
|
||||
// When the input is valid, update the hex mode to match how the text was parsed.
|
||||
// See parseAsHex variable comment above.
|
||||
isHexMode = parseAsHex;
|
||||
setHexMode(parseAsHex);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -661,7 +662,7 @@ public class IntegerTextField {
|
|||
if (passesMaxCheck(value) && passesMinCheck(value)) {
|
||||
// When the input is valid, update the hex mode to match how the text was parsed.
|
||||
// See parseAsHex variable comment above.
|
||||
isHexMode = parseAsHex;
|
||||
setHexMode(parseAsHex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -692,76 +693,4 @@ public class IntegerTextField {
|
|||
// so we don't allow negatives
|
||||
return value.signum() < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the JTextField mainly to allow hint painting for the current radix mode.
|
||||
*/
|
||||
private class MyTextField extends JTextField {
|
||||
|
||||
private static final String FONT_ID = "font.input.hint";
|
||||
private int hintWidth;
|
||||
|
||||
public MyTextField(int columns) {
|
||||
super(columns);
|
||||
|
||||
FontMetrics fontMetrics = getFontMetrics(Gui.getFont(FONT_ID));
|
||||
String mode = isHexMode ? "Hex" : "Dec";
|
||||
hintWidth = fontMetrics.stringWidth(mode);
|
||||
|
||||
AbstractDocument document = (AbstractDocument) getDocument();
|
||||
document.setDocumentFilter(new HexDecimalDocumentFilter());
|
||||
|
||||
addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
if (e.getKeyCode() == KeyEvent.VK_M && DockingUtils.isControlModifier(e)) {
|
||||
toggleMode();
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// make sure tooltips will be activated
|
||||
ToolTipManager.sharedInstance().registerComponent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(MouseEvent event) {
|
||||
|
||||
int hintStart = getBounds().width - hintWidth;
|
||||
if (event.getX() > hintStart) {
|
||||
String key = DockingUtils.CONTROL_KEY_NAME;
|
||||
return "Press '" + key + "-M' to toggle Hex or Decimal Mode";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
if (!showNumbericDecoration) {
|
||||
return;
|
||||
}
|
||||
|
||||
Font savedFont = g.getFont();
|
||||
g.setFont(Gui.getFont(FONT_ID));
|
||||
g.setColor(Messages.HINT);
|
||||
|
||||
Dimension size = getSize();
|
||||
Insets insets = getInsets();
|
||||
int x;
|
||||
if (getHorizontalAlignment() == RIGHT) {
|
||||
x = insets.left;
|
||||
}
|
||||
else {
|
||||
x = size.width - insets.right - hintWidth;
|
||||
}
|
||||
int y = size.height - insets.bottom - 1;
|
||||
String mode = isHexMode ? "Hex" : "Dec";
|
||||
GraphicsUtils.drawString(this, g, mode, x, y);
|
||||
g.setFont(savedFont);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/* ###
|
||||
* 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 generic.expressions;
|
||||
|
||||
/**
|
||||
* Base marker interface for {@link ExpressionGrouper}, {@link ExpressionOperator},
|
||||
* and {@link ExpressionValue}
|
||||
*/
|
||||
public interface ExpressionElement {
|
||||
// marker interface
|
||||
}
|
|
@ -0,0 +1,509 @@
|
|||
/* ###
|
||||
* 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 generic.expressions;
|
||||
|
||||
import static generic.expressions.ExpressionGrouper.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.util.NumericUtilities;
|
||||
|
||||
/**
|
||||
* Class for evaluating numeric expressions. See
|
||||
* {@link ExpressionOperator} for the full list of supported operators. All values are interpreted
|
||||
* as longs. Optionally, an ExpressionEvalualuator can be constructed with a symbol evaluator that
|
||||
* will be called on any string that can't be evaluated as an operator or number.
|
||||
* <P>
|
||||
* ExpressionEvaluators can operate in either decimal or hex mode. If in hex mode, all numbers are
|
||||
* assumed to be hexadecimal values. In decimal mode, numbers are assumed to be decimal values, but
|
||||
* hexadecimal values can still be specified by prefixing them with "0x".
|
||||
* <P>
|
||||
* There are also two convenience static methods that can be called to evaluate expressions. These
|
||||
* methods will either return a Long value as the result or null if there was an error evaluating
|
||||
* the expression. To get error messages related to parsing the expression, instantiate an
|
||||
* ExpressionEvaluator and call {@link #parse(String)} which will throw a
|
||||
* {@link ExpressionException} when the expression can't be evaluated.
|
||||
*/
|
||||
public class ExpressionEvaluator {
|
||||
private static final String TOKEN_CHARS = "+-*/()<>|^&~ =!";
|
||||
|
||||
private boolean assumeHex = false;
|
||||
|
||||
private Function<String, ExpressionValue> evaluator;
|
||||
|
||||
/**
|
||||
* Evaluates the given input as a Long value. This call assumes all numbers are decimal unless
|
||||
* prefixed with a "0x".
|
||||
* @param input the expression to be parsed into a Long value
|
||||
* @return the resulting Long value or null if the expression could not be evaluated.
|
||||
*/
|
||||
public static Long evaluateToLong(String input) {
|
||||
return evaluateToLong(input, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given input as a long value.
|
||||
* @param input the expression to be parsed into a Long value
|
||||
* @param assumeHex if true, numbers will be assumed to be hexadecimal values.
|
||||
* @return the resulting Long value or null if the expression could not be evaluated.
|
||||
*/
|
||||
public static Long evaluateToLong(String input, boolean assumeHex) {
|
||||
ExpressionEvaluator evaluator = new ExpressionEvaluator(assumeHex);
|
||||
try {
|
||||
return evaluator.parseAsLong(input);
|
||||
}
|
||||
catch (ExpressionException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an ExpressionEvaluator in decimal mode.
|
||||
*/
|
||||
public ExpressionEvaluator() {
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an ExpressionEvaluator in either decimal or hex mode.
|
||||
* @param assumeHex if true, the evaluator will assume all values are hexadecimal.
|
||||
*/
|
||||
public ExpressionEvaluator(boolean assumeHex) {
|
||||
this(assumeHex, s -> null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an ExpressionEvaluator in decimal mode with a given symbol evaluator.
|
||||
* @param evaluator A function that can convert a string token into a value (Must be Long
|
||||
* ExpressionValues, unless this is being called by a subclass that can handle other types
|
||||
* of operand values)
|
||||
*/
|
||||
public ExpressionEvaluator(Function<String, ExpressionValue> evaluator) {
|
||||
this(false, evaluator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an ExpressionEvaluator in either decimal or hex mode with a given symbol
|
||||
* evaluator.
|
||||
* @param assumeHex if true, the evaluator will assume all values are hexadecimal.
|
||||
* @param evaluator A function that can convert a string token into a value (Must be Long
|
||||
* ExpressionValues, unless this is being called by a subclass that can handle other types
|
||||
* of operand values)
|
||||
*/
|
||||
public ExpressionEvaluator(boolean assumeHex, Function<String, ExpressionValue> evaluator) {
|
||||
this.assumeHex = assumeHex;
|
||||
this.evaluator = Objects.requireNonNull(evaluator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given expression input, expecting the result to be long value.
|
||||
* @param input the expression string
|
||||
* @return the long value result.
|
||||
* @throws ExpressionException if the expression could not be evaluated to a long value.
|
||||
*/
|
||||
public long parseAsLong(String input) throws ExpressionException {
|
||||
ExpressionValue expressionValue = parse(input);
|
||||
if (expressionValue instanceof LongExpressionValue longValue) {
|
||||
return longValue.getLongValue();
|
||||
}
|
||||
throw new ExpressionException("Expression did not evalute to a long! Got a " +
|
||||
expressionValue.getClass() + " instead.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the hex/decimal mode.
|
||||
* @param b if true, all numbers will be assumed to be hexadecimal
|
||||
*/
|
||||
public void setAssumeHex(boolean b) {
|
||||
this.assumeHex = b;
|
||||
}
|
||||
|
||||
protected ExpressionValue parse(String input) throws ExpressionException {
|
||||
return this.parse(input, null);
|
||||
}
|
||||
|
||||
protected ExpressionValue parse(String input, ExpressionValue initial)
|
||||
throws ExpressionException {
|
||||
List<ExpressionElement> list = new ArrayList<>();
|
||||
|
||||
// if there is a given initial value (used for relative expressions), add it to the
|
||||
// sequential list of valid expression elements.
|
||||
if (initial != null) {
|
||||
list.add(initial);
|
||||
}
|
||||
|
||||
// convert the text input into a list of valid expression elements
|
||||
parseToList(input, list);
|
||||
|
||||
// evaluate the list of expression elements in operator precedence order.
|
||||
return eval(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the input string into a list of valid elements. When this method completes, the list
|
||||
* will contain only valid operators, valid operand values, or group operators.
|
||||
* @param input the input string to be parsed.
|
||||
* @param list the list to populate with valid elements.
|
||||
* @throws ExpressionException if any part of the input string can't be parsed into a valid
|
||||
* expression element.
|
||||
*/
|
||||
private void parseToList(String input, List<ExpressionElement> list)
|
||||
throws ExpressionException {
|
||||
|
||||
LookAheadTokenizer parser = new LookAheadTokenizer(input);
|
||||
while (parser.hasMoreTokens()) {
|
||||
String token = parser.getCurrentToken();
|
||||
|
||||
if (token.isBlank()) {
|
||||
parser.advance(1);
|
||||
}
|
||||
else if (processGroupToken(list, token)) {
|
||||
parser.advance(1);
|
||||
}
|
||||
else if (processOperator(list, token, parser.getNextToken())) {
|
||||
ExpressionOperator op = getLastOperator(list);
|
||||
parser.advance(op.size());
|
||||
}
|
||||
else if (processNumber(list, token)) {
|
||||
parser.advance(1);
|
||||
}
|
||||
else if (processSymbol(list, token)) {
|
||||
parser.advance(1);
|
||||
}
|
||||
else {
|
||||
throw new ExpressionException("Could not evaluate token \"" + token + "\"");
|
||||
}
|
||||
}
|
||||
if (list.isEmpty()) {
|
||||
throw new ExpressionException("Expression is empty. Nothing to parse!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates a list of valid expression elements into a single final value.
|
||||
* @param list the list of valid expression elements.
|
||||
* @return the final value the expression evaluates to
|
||||
* @throws ExpressionException if sequence of expression elements is not in a valid order such
|
||||
* that it evaluates to a single value. (such as two values not being separated by an operator)
|
||||
*/
|
||||
private ExpressionValue eval(List<ExpressionElement> list) throws ExpressionException {
|
||||
// first evaluate any sub-lists grouped by parenthesis
|
||||
processGroups(list);
|
||||
|
||||
// next process any unary operators
|
||||
processUnaryOperators(list);
|
||||
|
||||
// final process binary operators in operator precedence order.
|
||||
processBinaryOperators(list);
|
||||
|
||||
// if everything evaluated properly, there should only be one item left in the list
|
||||
if (list.size() != 1) {
|
||||
String result = list.stream().map(Object::toString).collect(Collectors.joining(" "));
|
||||
throw new ExpressionException("Parse failed! Stopped at \"" + result + "\"");
|
||||
}
|
||||
|
||||
ExpressionElement element = list.get(0);
|
||||
if (element instanceof ExpressionValue ev) {
|
||||
return ev;
|
||||
}
|
||||
throw new ExpressionException("Parse failed to evaluate to a value! Stopped at " + element);
|
||||
}
|
||||
|
||||
private void processBinaryOperators(List<ExpressionElement> list) throws ExpressionException {
|
||||
List<Set<ExpressionOperator>> ops = ExpressionOperator.getBinaryOperatorsByPrecedence();
|
||||
|
||||
// Each set in the list contains operators at the same precedence, so they all need
|
||||
// to be processed at the same time so that they are processed left to right (which
|
||||
// corresponds to the list order)
|
||||
for (Set<ExpressionOperator> set : ops) {
|
||||
processBinaryOperators(list, set);
|
||||
}
|
||||
}
|
||||
|
||||
private void processBinaryOperators(List<ExpressionElement> list,
|
||||
Set<ExpressionOperator> operators) throws ExpressionException {
|
||||
// can't have a valid binary operator at index 0, so start looking at index 1
|
||||
int operatorIndex = findValidBinaryOperator(list, operators, 1);
|
||||
while (operatorIndex >= 0) {
|
||||
// we can safely cast here because we checked in the findValidBinaryOperator method
|
||||
ExpressionOperator operator = (ExpressionOperator) list.get(operatorIndex);
|
||||
ExpressionValue value1 = (ExpressionValue) list.get(operatorIndex - 1);
|
||||
ExpressionValue value2 = (ExpressionValue) list.get(operatorIndex + 1);
|
||||
ExpressionValue newValue = value1.applyBinaryOperator(operator, value2);
|
||||
list.set(operatorIndex - 1, newValue);
|
||||
list.subList(operatorIndex, operatorIndex + 2).clear();
|
||||
|
||||
// After the operator completed, the list has been changed and the sequence
|
||||
// "value operator value" has been replace with the resulting value of the operation.
|
||||
|
||||
// Now look for the next operator in the current set of operators we are evaluating
|
||||
operatorIndex = findValidBinaryOperator(list, operators, operatorIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int findValidBinaryOperator(List<ExpressionElement> list,
|
||||
Set<ExpressionOperator> operators, int startIndex) {
|
||||
|
||||
// can't have a valid binary operator at the last index, so stop 1 before last
|
||||
for (int i = startIndex; i < list.size() - 1; i++) {
|
||||
if (operators.contains(list.get(i))) {
|
||||
// make sure the elements before and after the operator are value types so
|
||||
// that the caller can just cast them as values. If they are not, then
|
||||
// this operator won't be evaluated and at the end the evaluate process, the list
|
||||
// won't be reduced to just one element.
|
||||
if (list.get(i - 1) instanceof ExpressionValue &
|
||||
list.get(i + 1) instanceof ExpressionValue) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void processUnaryOperators(List<ExpressionElement> list) throws ExpressionException {
|
||||
|
||||
int unaryOperatorIndex = findValidUnaryOperator(list);
|
||||
while (unaryOperatorIndex >= 0) {
|
||||
ExpressionOperator operator = (ExpressionOperator) list.get(unaryOperatorIndex);
|
||||
ExpressionValue value = (ExpressionValue) list.get(unaryOperatorIndex + 1);
|
||||
ExpressionValue newValue = value.applyUnaryOperator(operator);
|
||||
list.remove(unaryOperatorIndex);
|
||||
list.set(unaryOperatorIndex, newValue);
|
||||
|
||||
unaryOperatorIndex = findValidUnaryOperator(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int findValidUnaryOperator(List<ExpressionElement> list) {
|
||||
// stop 1 before end since you can't end in a valid unary operator
|
||||
|
||||
for (int i = 0; i < list.size() - 1; i++) {
|
||||
// check any element in the list if it is a unary operator
|
||||
if (list.get(i) instanceof ExpressionOperator op) {
|
||||
if (!op.isUnary()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// make sure the next element is a value so the the caller can cast without fear
|
||||
if (list.get(i + 1) instanceof ExpressionValue) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively for groups (sublists surrounded by parenthesis) and process the sub list of
|
||||
* elements in the group before processing the outer list. As each group is evaluated, the start
|
||||
* paren operator, the end paren operator and all the tokens in between are replaced by the
|
||||
* single value the group evaluated to.
|
||||
* @param list the list to look for grouped sub-lists.
|
||||
* @throws ExpressionException if a exception occurs processing a sub list
|
||||
*/
|
||||
private void processGroups(List<ExpressionElement> list) throws ExpressionException {
|
||||
int groupStart = findGroupStart(list);
|
||||
while (groupStart >= 0) {
|
||||
int groupEndIndex = findGroupEnd(list, groupStart);
|
||||
if (groupEndIndex < 0) {
|
||||
throw new ExpressionException("Missing end parenthesis!");
|
||||
}
|
||||
ExpressionValue value = eval(list.subList(groupStart + 1, groupEndIndex));
|
||||
|
||||
// After evaluating, everything between the parens will be replaced by the result. So
|
||||
// replace the left paren with the results and clear the next 2 entries.
|
||||
list.set(groupStart, value);
|
||||
list.subList(groupStart + 1, groupStart + 3).clear();
|
||||
|
||||
groupStart = findGroupStart(list);
|
||||
}
|
||||
}
|
||||
|
||||
private int findGroupStart(List<ExpressionElement> list) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (list.get(i) == LEFT_PAREN) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int findGroupEnd(List<ExpressionElement> list, int groupStart) {
|
||||
int depth = 1;
|
||||
for (int i = groupStart + 1; i < list.size(); i++) {
|
||||
Object obj = list.get(i);
|
||||
if (obj == LEFT_PAREN) {
|
||||
depth++;
|
||||
}
|
||||
else if (obj == RIGHT_PAREN) {
|
||||
if (--depth == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// A tokenizer that keeps track of one future token. This is so the parser can handle operator
|
||||
// chars that can ether be an operator by itself or part of a 2 char operator(i.e. "<", "=",
|
||||
// "<=", "<<", "==")
|
||||
private class LookAheadTokenizer {
|
||||
private StringTokenizer tokenizer;
|
||||
private String currentToken;
|
||||
private String nextToken;
|
||||
|
||||
LookAheadTokenizer(String input) {
|
||||
tokenizer = new StringTokenizer(input, TOKEN_CHARS, true);
|
||||
currentToken = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
|
||||
nextToken = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
|
||||
}
|
||||
|
||||
public boolean hasMoreTokens() {
|
||||
return currentToken != null;
|
||||
}
|
||||
|
||||
public String getCurrentToken() {
|
||||
return currentToken;
|
||||
}
|
||||
|
||||
public String getNextToken() {
|
||||
return nextToken;
|
||||
}
|
||||
|
||||
public void advance(int count) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
currentToken = nextToken;
|
||||
nextToken = tokenizer.hasMoreTokens() ? tokenizer.nextToken() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ExpressionOperator getLastOperator(List<ExpressionElement> list) {
|
||||
ExpressionElement lastElement = list.get(list.size() - 1);
|
||||
return (ExpressionOperator) lastElement;
|
||||
}
|
||||
|
||||
private boolean processSymbol(List<ExpressionElement> list, String token) {
|
||||
ExpressionValue value = evaluateSymbol(token);
|
||||
if (value != null) {
|
||||
list.add(value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected ExpressionValue evaluateSymbol(String token) {
|
||||
return evaluator.apply(token);
|
||||
}
|
||||
|
||||
private boolean processOperator(List<ExpressionElement> list, String token, String nextToken) {
|
||||
boolean preferBinary = shouldPreferBinaryOp(list);
|
||||
ExpressionOperator op = ExpressionOperator.getOperator(token, nextToken, preferBinary);
|
||||
if (op != null) {
|
||||
list.add(op);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processGroupToken(List<ExpressionElement> list, String token) {
|
||||
if (token.equals("(")) {
|
||||
list.add(LEFT_PAREN);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (token.equals(")")) {
|
||||
list.add(RIGHT_PAREN);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldPreferBinaryOp(List<ExpressionElement> list) {
|
||||
if (list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
ExpressionElement lastElement = list.get(list.size() - 1);
|
||||
if (lastElement instanceof ExpressionValue) {
|
||||
return true;
|
||||
}
|
||||
if (lastElement instanceof ExpressionOperator) {
|
||||
return false;
|
||||
}
|
||||
if (lastElement == ExpressionGrouper.LEFT_PAREN) {
|
||||
return false;
|
||||
}
|
||||
if (lastElement == ExpressionGrouper.RIGHT_PAREN) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processNumber(List<ExpressionElement> list, String token) {
|
||||
int radix = 10;
|
||||
|
||||
if (assumeHex && processAsHexNumber(list, token)) {
|
||||
return true;
|
||||
}
|
||||
token = toLowerAndRemoveEndNumberDecorators(token);
|
||||
if (token.startsWith("0x")) {
|
||||
radix = 16;
|
||||
token = token.substring(2);
|
||||
}
|
||||
|
||||
try {
|
||||
long value = (radix == 10) ? NumericUtilities.parseLong(token)
|
||||
: NumericUtilities.parseHexLong(token);
|
||||
|
||||
list.add(new LongExpressionValue(value));
|
||||
return true;
|
||||
}
|
||||
catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String toLowerAndRemoveEndNumberDecorators(String token) {
|
||||
token = token.toLowerCase();
|
||||
if (token.endsWith("ull") || token.endsWith("llu")) {
|
||||
token = token.substring(0, token.length() - 3);
|
||||
}
|
||||
else if (token.endsWith("ul") || token.endsWith("lu") || token.endsWith("ll")) {
|
||||
token = token.substring(0, token.length() - 2);
|
||||
}
|
||||
else if (token.endsWith("l") || token.endsWith("u")) {
|
||||
token = token.substring(0, token.length() - 1);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
// parses values as a hex value (e.g. parsing "10" returns 16 instead of 10)
|
||||
private boolean processAsHexNumber(List<ExpressionElement> list, String token) {
|
||||
try {
|
||||
long value = NumericUtilities.parseHexLong(token);
|
||||
list.add(new LongExpressionValue(value));
|
||||
return true;
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/* ###
|
||||
* 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 generic.expressions;
|
||||
|
||||
/**
|
||||
* Exception thrown when using an {@link ExpressionEvaluator}
|
||||
*/
|
||||
public class ExpressionException extends Exception {
|
||||
public ExpressionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/* ###
|
||||
* 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 generic.expressions;
|
||||
|
||||
/**
|
||||
* Grouping {@link ExpressionElement}s
|
||||
*/
|
||||
public enum ExpressionGrouper implements ExpressionElement {
|
||||
LEFT_PAREN, RIGHT_PAREN;
|
||||
}
|
|
@ -0,0 +1,172 @@
|
|||
/* ###
|
||||
* 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 generic.expressions;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.collections4.map.LazyMap;
|
||||
|
||||
/**
|
||||
* Enum of support operators for the {@link ExpressionEvaluator}
|
||||
*/
|
||||
public enum ExpressionOperator implements ExpressionElement {
|
||||
// unary
|
||||
BITWISE_NOT("~", OpType.UNARY, 1),
|
||||
LOGICAL_NOT("!", OpType.UNARY, 1),
|
||||
UNARY_PLUS("+", OpType.UNARY, 1),
|
||||
UNARY_MINUS("-", OpType.UNARY, 1),
|
||||
|
||||
// multiplicative
|
||||
MULTIPLY("*", OpType.BINARY, 2),
|
||||
DIVIDE("/", OpType.BINARY, 2),
|
||||
|
||||
// additive
|
||||
ADD("+", OpType.BINARY, 3),
|
||||
SUBTRACT("-", OpType.BINARY, 3),
|
||||
|
||||
// shift
|
||||
SHIFT_LEFT("<<", OpType.BINARY, 4),
|
||||
SHIFT_RIGHT(">>", OpType.BINARY, 4),
|
||||
|
||||
// relational
|
||||
LESS_THAN("<", OpType.BINARY, 5),
|
||||
GREATER_THAN(">", OpType.BINARY, 5),
|
||||
LESS_THAN_OR_EQUAL("<=", OpType.BINARY, 5),
|
||||
GREATER_THAN_OR_EQUAL(">=", OpType.BINARY, 5),
|
||||
|
||||
// equality
|
||||
EQUALS("==", OpType.BINARY, 6),
|
||||
NOT_EQUALS("!=", OpType.BINARY, 6),
|
||||
|
||||
// bitwise
|
||||
BITWISE_AND("&", OpType.BINARY, 7),
|
||||
BITWISE_XOR("^", OpType.BINARY, 8),
|
||||
BITWISE_OR("|", OpType.BINARY, 9),
|
||||
|
||||
// logical
|
||||
LOGICAL_AND("&&", OpType.BINARY, 10),
|
||||
LOGICAL_OR("||", OpType.BINARY, 11);
|
||||
|
||||
public static List<Set<ExpressionOperator>> binaryOperatorsByPrecedence;
|
||||
|
||||
private String name;
|
||||
private OpType type;
|
||||
private int precedence;
|
||||
|
||||
private ExpressionOperator(String name, OpType type, int precedence) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.precedence = precedence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all the binary operators in precedence order, organized into sets where
|
||||
* each set contains all the operators of the same precedence.
|
||||
* @return a list of all the binary operators in precedence order, organized into sets where
|
||||
* each set contains all the operators of the same precedence.
|
||||
*/
|
||||
public static List<Set<ExpressionOperator>> getBinaryOperatorsByPrecedence() {
|
||||
if (binaryOperatorsByPrecedence == null) {
|
||||
binaryOperatorsByPrecedence = buildOperatorsByPrecedenceList();
|
||||
}
|
||||
return binaryOperatorsByPrecedence;
|
||||
}
|
||||
|
||||
private static List<Set<ExpressionOperator>> buildOperatorsByPrecedenceList() {
|
||||
ExpressionOperator[] values = values();
|
||||
LazyMap<Integer, HashSet<ExpressionOperator>> map =
|
||||
LazyMap.lazyMap(new TreeMap<>(), k -> new HashSet<>());
|
||||
|
||||
for (ExpressionOperator op : values) {
|
||||
if (op.isBinary()) {
|
||||
map.get(op.precedence).add(op);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(map.values());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the operator for the given token and look ahead token and if we are expecting to find
|
||||
* a binary operator. This method first tries merging the tokens looking for a double char
|
||||
* operator first.
|
||||
* @param token the first token
|
||||
* @param lookahead1 the next token that may or may not be part of this operand
|
||||
* @param preferBinary if we are expecting a binary operator (the previous expression element
|
||||
* was an operand value). We need this to know if the token '-' is the unary operator or the
|
||||
* binary operator. If the token before was an operator, then we expect a unary operator. If
|
||||
* the previous was a value, then we expect a binary operator.
|
||||
* @return the operator that matches the given tokens and expected type
|
||||
*/
|
||||
public static ExpressionOperator getOperator(String token, String lookahead1,
|
||||
boolean preferBinary) {
|
||||
|
||||
if (lookahead1 != null) {
|
||||
String doubleToken = token + lookahead1;
|
||||
ExpressionOperator operator = findOperator(doubleToken, preferBinary);
|
||||
if (operator != null) {
|
||||
return operator;
|
||||
}
|
||||
}
|
||||
|
||||
return findOperator(token, preferBinary);
|
||||
}
|
||||
|
||||
private static ExpressionOperator findOperator(String tokens, boolean expectBinary) {
|
||||
for (ExpressionOperator operator : values()) {
|
||||
if (operator.name.equals(tokens)) {
|
||||
if (operator.isBinary() == expectBinary) {
|
||||
return operator;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of chars in the operator
|
||||
* @return the number of chars in the operator
|
||||
*/
|
||||
public int size() {
|
||||
return name.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the operator is a unary operator.
|
||||
* @return if the operator is a unary operator.
|
||||
*/
|
||||
public boolean isUnary() {
|
||||
return type == OpType.UNARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the operator is a binary operator.
|
||||
* @return if the operator is a binary operator.
|
||||
*/
|
||||
public boolean isBinary() {
|
||||
return type == OpType.BINARY;
|
||||
}
|
||||
|
||||
private enum OpType {
|
||||
UNARY, BINARY
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/* ###
|
||||
* 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 generic.expressions;
|
||||
|
||||
/**
|
||||
* Operand types use by the {@link ExpressionEvaluator} must implement this interface.
|
||||
*/
|
||||
public interface ExpressionValue extends ExpressionElement {
|
||||
|
||||
/**
|
||||
* Method called to apply a unary operator to this value.
|
||||
* @param operator the operator being applied
|
||||
* @return the new value after the operator is applied to this value
|
||||
* @throws ExpressionException if the operator is not applicable for this value
|
||||
*/
|
||||
public ExpressionValue applyUnaryOperator(ExpressionOperator operator) throws ExpressionException;
|
||||
|
||||
/**
|
||||
* Method called to apply a binary operator to this value.
|
||||
* @param operator the binary operator being applied.
|
||||
* @param value the other value to combine with this value by the operator
|
||||
* @return the new value after the operator is applied to this value
|
||||
* @throws ExpressionException if the operator is not applicable for this value or the the other
|
||||
* value is not applicable for this operand and operator
|
||||
*/
|
||||
public ExpressionValue applyBinaryOperator(ExpressionOperator operator, ExpressionValue value)
|
||||
throws ExpressionException;
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/* ###
|
||||
* 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 generic.expressions;
|
||||
|
||||
/**
|
||||
* Long operand values. See {@link ExpressionValue}. Defines supported operators and other
|
||||
* operands for expression values that are long values.
|
||||
*/
|
||||
public class LongExpressionValue implements ExpressionValue {
|
||||
|
||||
private final long value;
|
||||
|
||||
public LongExpressionValue(long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public long getLongValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return Long.toString(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionValue applyUnaryOperator(ExpressionOperator operator) throws ExpressionException {
|
||||
switch (operator) {
|
||||
case BITWISE_NOT:
|
||||
return new LongExpressionValue(~value);
|
||||
case LOGICAL_NOT:
|
||||
return new LongExpressionValue(value == 0 ? 1 : 0);
|
||||
case UNARY_MINUS:
|
||||
return new LongExpressionValue(-value);
|
||||
case UNARY_PLUS:
|
||||
return this;
|
||||
default:
|
||||
throw new ExpressionException(
|
||||
"Unary Operator " + operator + " not supported by Long values!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionValue applyBinaryOperator(ExpressionOperator operator, ExpressionValue operand)
|
||||
throws ExpressionException {
|
||||
if (!(operand instanceof LongExpressionValue longOperand)) {
|
||||
throw new ExpressionException("Unsupported operand type for Long: " + value);
|
||||
}
|
||||
long otherValue = longOperand.value;
|
||||
|
||||
switch (operator) {
|
||||
case BITWISE_AND:
|
||||
return new LongExpressionValue(value & otherValue);
|
||||
case BITWISE_OR:
|
||||
return new LongExpressionValue(value | otherValue);
|
||||
case BITWISE_XOR:
|
||||
return new LongExpressionValue(value ^ otherValue);
|
||||
case DIVIDE:
|
||||
return new LongExpressionValue(value / otherValue);
|
||||
case EQUALS:
|
||||
return new LongExpressionValue(value == otherValue ? 1 : 0);
|
||||
case GREATER_THAN:
|
||||
return new LongExpressionValue(value > otherValue ? 1 : 0);
|
||||
case GREATER_THAN_OR_EQUAL:
|
||||
return new LongExpressionValue(value >= otherValue ? 1 : 0);
|
||||
case SHIFT_LEFT:
|
||||
return new LongExpressionValue(value << otherValue);
|
||||
case LESS_THAN:
|
||||
return new LongExpressionValue(value < otherValue ? 1 : 0);
|
||||
case LESS_THAN_OR_EQUAL:
|
||||
return new LongExpressionValue(value <= otherValue ? 1 : 0);
|
||||
case LOGICAL_AND:
|
||||
int b1 = value == 0 ? 0 : 1;
|
||||
int b2 = otherValue == 0 ? 0 : 1;
|
||||
return new LongExpressionValue(b1 & b2);
|
||||
case LOGICAL_OR:
|
||||
b1 = value == 0 ? 0 : 1;
|
||||
b2 = otherValue == 0 ? 0 : 1;
|
||||
return new LongExpressionValue(b1 | b2);
|
||||
case SUBTRACT:
|
||||
return new LongExpressionValue(value - otherValue);
|
||||
case NOT_EQUALS:
|
||||
return new LongExpressionValue(value == otherValue ? 0 : 1);
|
||||
case ADD:
|
||||
return new LongExpressionValue(value + otherValue);
|
||||
case SHIFT_RIGHT:
|
||||
return new LongExpressionValue(value >> otherValue);
|
||||
case MULTIPLY:
|
||||
return new LongExpressionValue(value * otherValue);
|
||||
default:
|
||||
throw new ExpressionException(
|
||||
"Binary Operator \"" + operator + "\" not supported by Long values!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
/* ###
|
||||
* 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 generic.expressions;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ExpressionEvaluatorTest {
|
||||
|
||||
@Test
|
||||
public void testUnaryPlus() {
|
||||
assertEval(100, "+100");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnaryMinus() {
|
||||
assertEval(-100, "-100");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAdd() {
|
||||
assertEval(100, "20+10 + 70");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtract() {
|
||||
assertEval(5, "20-10-5");
|
||||
assertEval(5, "20-5-10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiply() {
|
||||
assertEval(42, "2*3*7");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDivide() {
|
||||
assertEval(1, "8/4/2");
|
||||
assertEval(1, "5/4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShiftLeft() {
|
||||
assertEval(4, "16>>2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShiftRight() {
|
||||
assertEval(8, "1<<3");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitWiseNot() {
|
||||
assertEval(-1, "~0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogicalNot() {
|
||||
assertEval(0, "!1");
|
||||
assertEval(1, "!0");
|
||||
assertEval(0, "!124");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreaterThan() {
|
||||
assertEval(1, "8>5");
|
||||
assertEval(0, "5>8");
|
||||
assertEval(0, "8>8");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLessThan() {
|
||||
assertEval(0, "8<5");
|
||||
assertEval(1, "5<8");
|
||||
assertEval(0, "8<8");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGreaterThanOrEqual() {
|
||||
assertEval(1, "8>=5");
|
||||
assertEval(0, "5>=8");
|
||||
assertEval(1, "8>=8");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLessThanOrEqual() {
|
||||
assertEval(0, "8<=5");
|
||||
assertEval(1, "5<=8");
|
||||
assertEval(1, "8<=8");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSubtractAssociatesLeftToRight() {
|
||||
assertEval(110, "100+30-10-10");
|
||||
assertEval(110, "100-10-10+30");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiplyDivide() {
|
||||
assertEval(100, "10*30/3");
|
||||
assertEval(90, "10/3*30");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitwiseAnd() {
|
||||
assertEval(0x4, "0xffff & 0x4");
|
||||
assertEval(0x0, "0x4 & 0x2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitwiseOr() {
|
||||
assertEval(0x6, "0x2 | 0x4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBitwiseXor() {
|
||||
assertEval(0x2, "0x3 ^ 0x1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogicalAnd() {
|
||||
assertEval(1, "0xffff && 0x4");
|
||||
assertEval(0, "0x4 && 0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogicalOr() {
|
||||
assertEval(1, "0x2 || 0x4");
|
||||
assertEval(0, "0 || 0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMixedPrecedence() {
|
||||
assertEval(23, "10+3*5-8/4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGrouping() {
|
||||
assertEval(42, "6*(3+4)");
|
||||
assertEval(16, "1 << (8/2)");
|
||||
assertEval(-1, "~(-1+1)");
|
||||
assertEval(13, "1+(3 * (7+1)/2)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStackedUnaryOperators() {
|
||||
assertEval(-1, "~~~0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidSyntax() {
|
||||
assertEvalNull("5 5");
|
||||
assertEvalNull("+");
|
||||
assertEvalNull("<< 5");
|
||||
assertEvalNull("5 +");
|
||||
assertEvalNull("(3+2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMixedValues() {
|
||||
assertEval(26, "10+0x10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHexOnly() {
|
||||
assertEvalHexOnly(22, "10+6");
|
||||
}
|
||||
|
||||
private void assertEval(long expected, String expression) {
|
||||
long result = ExpressionEvaluator.evaluateToLong(expression);
|
||||
assertEquals(expected, result);
|
||||
|
||||
}
|
||||
|
||||
private void assertEvalHexOnly(long expected, String expression) {
|
||||
ExpressionEvaluator evaluator = new ExpressionEvaluator(true);
|
||||
long result;
|
||||
try {
|
||||
result = evaluator.parseAsLong(expression);
|
||||
assertEquals(expected, result);
|
||||
}
|
||||
catch (ExpressionException e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void assertEvalNull(String expression) {
|
||||
assertNull(ExpressionEvaluator.evaluateToLong(expression));
|
||||
|
||||
}
|
||||
}
|
|
@ -15,656 +15,270 @@
|
|||
*/
|
||||
package ghidra.program.util;
|
||||
|
||||
import java.util.*;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
|
||||
import generic.expressions.*;
|
||||
import ghidra.app.util.NamespaceUtils;
|
||||
import ghidra.app.util.SymbolPath;
|
||||
import ghidra.program.model.address.*;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.model.symbol.Symbol;
|
||||
import ghidra.program.model.symbol.SymbolTable;
|
||||
import ghidra.util.NumericUtilities;
|
||||
import ghidra.program.model.mem.Memory;
|
||||
import ghidra.program.model.mem.MemoryBlock;
|
||||
import ghidra.program.model.symbol.*;
|
||||
|
||||
/**
|
||||
* The <CODE>AddressEvaluator</CODE> class provides a way to evaluate a string
|
||||
* that represents an address and resolve it to an address for a particular program.
|
||||
* Class for evaluating expressions as an Address. See
|
||||
* {@link ExpressionOperator} for the full list of supported operators. All values are interpreted
|
||||
* as longs or symbols that resolve to an address.
|
||||
* <P>
|
||||
* ExpressionEvaluators can operate in either decimal or hex mode. If in hex mode, all numbers are
|
||||
* assumed to be hexadecimal values. In decimal mode, numbers are assumed to be decimal values, but
|
||||
* hexadecimal values can still be specified by prefixing them with "0x".
|
||||
* <P>
|
||||
* There are also two convenience static methods that can be called to evaluate address expressions.
|
||||
* These methods will either return an Address as the result or null if there was an error
|
||||
* evaluating the expression. To get error messages related to parsing the expression, instantiate
|
||||
* an AddressEvaluator and call {@link #parseAsAddress(String)} which will throw a
|
||||
* {@link ExpressionException} when the expression can't be evaluated.
|
||||
*/
|
||||
public class AddressEvaluator {
|
||||
public class AddressEvaluator extends ExpressionEvaluator {
|
||||
|
||||
private static final String TOKEN_CHARS = "+-*/()<>|^&~ =";
|
||||
private Reference<Program> programReference;
|
||||
private AddressFactory addressFactory;
|
||||
private AddressSpace preferredSpace;
|
||||
|
||||
/**
|
||||
* Gets a legitimate address for the specified program as indicated by the string.
|
||||
* Gets a valid address for the specified program as indicated by the input expression.
|
||||
* @param p the program to use for determining the address.
|
||||
* @param baseAddr the base address to use for relative addressing.
|
||||
* @param s string representation of the address desired.
|
||||
* @param inputExpression string representation of the address desired.
|
||||
* @return the address. Otherwise, return null if the string fails to evaluate
|
||||
* to a unique legitimate address.
|
||||
*/
|
||||
public static Address evaluate(Program p, Address baseAddr, String s) {
|
||||
|
||||
AddressFactory af = p.getAddressFactory();
|
||||
SymbolTable st = p.getSymbolTable();
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
if (baseAddr != null) {
|
||||
list.add(baseAddr);
|
||||
}
|
||||
if (!parseToList(s, af, st, list)) {
|
||||
return null;
|
||||
}
|
||||
Object obj = eval(list);
|
||||
if (obj instanceof Address) {
|
||||
return (Address) obj;
|
||||
}
|
||||
else if (obj instanceof Long) {
|
||||
try {
|
||||
return af.getDefaultAddressSpace().getAddress(((Long) obj).longValue(), true);
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Long evaluateToLong(String s) {
|
||||
List<Object> list = new ArrayList<Object>();
|
||||
|
||||
if (!parseToList(s, null, null, list)) {
|
||||
return null;
|
||||
}
|
||||
Object obj = eval(list);
|
||||
if (obj instanceof Address) {
|
||||
return ((Address) obj).getOffset();
|
||||
}
|
||||
else if (obj instanceof Long) {
|
||||
return (Long) obj;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected static boolean parseToList(String s, AddressFactory af, SymbolTable st,
|
||||
List<Object> list) {
|
||||
StringTokenizer parser = new StringTokenizer(s, TOKEN_CHARS, true);
|
||||
String lookahead = null;
|
||||
while (lookahead != null || parser.hasMoreTokens()) {
|
||||
String tok = null;
|
||||
if (lookahead != null) {
|
||||
tok = lookahead;
|
||||
lookahead = null;
|
||||
}
|
||||
else {
|
||||
tok = parser.nextToken();
|
||||
}
|
||||
|
||||
if (tok.equals(" ")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// = must be followed by =, others can be followed
|
||||
if ("=!<>|&".contains(tok)) {
|
||||
lookahead = parser.nextToken();
|
||||
tok = checkDoubleToken(tok, lookahead);
|
||||
// if tok is now longer, consumed lookahead
|
||||
if (tok.length() > 1) {
|
||||
lookahead = null;
|
||||
}
|
||||
}
|
||||
Object obj = Operator.getOperator(tok);
|
||||
if (obj == null) {
|
||||
obj = getValueObject(st, af, tok);
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
list.add(obj);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String checkDoubleToken(String tok, String lookahead) {
|
||||
switch (tok) {
|
||||
case "=":
|
||||
if (lookahead.equals("=")) {
|
||||
return "==";
|
||||
}
|
||||
break;
|
||||
|
||||
case "<":
|
||||
if (lookahead.equals("=")) {
|
||||
return "<=";
|
||||
}
|
||||
if (lookahead.equals("<")) {
|
||||
return "<<";
|
||||
}
|
||||
break;
|
||||
|
||||
case ">":
|
||||
if (lookahead.equals("=")) {
|
||||
return ">=";
|
||||
}
|
||||
if (lookahead.equals(">")) {
|
||||
return ">>";
|
||||
}
|
||||
break;
|
||||
|
||||
case "!":
|
||||
if (lookahead.equals("=")) {
|
||||
return "!=";
|
||||
}
|
||||
break;
|
||||
|
||||
case "|":
|
||||
if (lookahead.equals("|")) {
|
||||
return "||";
|
||||
}
|
||||
break;
|
||||
|
||||
case "&":
|
||||
if (lookahead.equals("&")) {
|
||||
return "&&";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return tok;
|
||||
public static Address evaluate(Program p, String inputExpression) {
|
||||
return evaluate(p, null, inputExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a legitimate address for the specified program as indicated by the string.
|
||||
* Gets a valid address for the specified program as indicated by the input expression.
|
||||
* @param p the program to use for determining the address.
|
||||
* @param s string representation of the address desired.
|
||||
* @param baseAddr the base address to use for relative addressing.
|
||||
* @param inputExpression string representation of the address desired.
|
||||
* @return the address. Otherwise, return null if the string fails to evaluate
|
||||
* to a legitimate address.
|
||||
* to a unique legitimate address.
|
||||
*/
|
||||
public static Address evaluate(Program p, String s) {
|
||||
return evaluate(p, null, s);
|
||||
public static Address evaluate(Program p, Address baseAddr, String inputExpression) {
|
||||
AddressEvaluator evaluator = new AddressEvaluator(p, true);
|
||||
try {
|
||||
return evaluator.parseAsAddress(inputExpression);
|
||||
}
|
||||
catch (ExpressionException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for creating an Address object from a byte array. The Address object may or may not
|
||||
* be a legitimate Address in the program's address space. This method is meant to provide a way of
|
||||
* creating an Address object from a sequence of bytes that can be used for additional tests and
|
||||
* comparisons.
|
||||
*
|
||||
* @param p - program being analyzed.
|
||||
* @param addrBytes - byte array to use containing the values the address will be constructed from.
|
||||
* @return - Address object constructed from the addrBytes array. Returns null if the program is null,
|
||||
* addrBytes is null, or the length of addrBytes does not match the default Pointer size or does not contain
|
||||
* a valid offset.
|
||||
*
|
||||
* Constructs an AddressEvalutor for the given program and in the specified hex/decimal mode.
|
||||
* @param program the program to use to evaluate expressions into valid addresses.
|
||||
* @param assumeHex if true, all numeric values are assumed to be hexadecimal numbers.
|
||||
*/
|
||||
public static Address evaluate(Program p, byte[] addrBytes) {
|
||||
|
||||
boolean isBigEndian = p.getMemory().isBigEndian();
|
||||
|
||||
int ptrSize = p.getDefaultPointerSize();
|
||||
int index = 0;
|
||||
long offset = 0;
|
||||
|
||||
// Make sure correct # of bytes were passed
|
||||
if (addrBytes == null || addrBytes.length != ptrSize) {
|
||||
return null;
|
||||
public AddressEvaluator(Program program, boolean assumeHex) {
|
||||
this(program, null, assumeHex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure we account for endianness of the program.
|
||||
* Computing the number of bits to shift the current byte value
|
||||
* is different for Little vs. Big Endian. Need to multiply by
|
||||
* 8 to shift in 1-byte increments.
|
||||
/**
|
||||
* Constructs an AdddressEvaluator without a full program. This version will not be able to
|
||||
* evaluate symbol or memory block names. This is mostly for backwards compatibility.
|
||||
* @param factory the address factory for creating addresses
|
||||
* @param assumeHex if true, all numeric values are assumed to be hexadecimal numbers.
|
||||
*/
|
||||
if (isBigEndian) {
|
||||
index = 0;
|
||||
while (index < addrBytes.length) {
|
||||
offset += (addrBytes[index] & 0xff) << ((addrBytes.length - index - 1) * 8);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Program is LittleEndian
|
||||
index = addrBytes.length - 1;
|
||||
while (index >= 0) {
|
||||
offset += ((addrBytes[index] & 0xff) << (index * 8));
|
||||
index--;
|
||||
}
|
||||
public AddressEvaluator(AddressFactory factory, boolean assumeHex) {
|
||||
this(factory, null, assumeHex);
|
||||
}
|
||||
|
||||
AddressSpace space = p.getAddressFactory().getDefaultAddressSpace();
|
||||
/**
|
||||
* Constructs an AddressEvalutor for the given program and in the specified hex/decimal mode.
|
||||
* @param program the program to use to evaluate expressions into valid addresses.
|
||||
* @param defaultSpace The address space to use when converting long values into addresses. If
|
||||
* this value is null, then the default address space will be used.
|
||||
* @param assumeHex if true, all numeric values are assumed to be hexadecimal numbers.
|
||||
*/
|
||||
public AddressEvaluator(Program program, AddressSpace defaultSpace, boolean assumeHex) {
|
||||
this(program.getAddressFactory(), defaultSpace, assumeHex);
|
||||
this.programReference = new WeakReference<>(program);
|
||||
}
|
||||
|
||||
private AddressEvaluator(AddressFactory factory, AddressSpace defaultSpace, boolean assumeHex) {
|
||||
super(assumeHex);
|
||||
this.addressFactory = factory;
|
||||
this.preferredSpace = defaultSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given input expression as an address.
|
||||
* @param input the expression to evaluate
|
||||
* @return the Address the expression evaluates to
|
||||
* @throws ExpressionException if the input expression can't be evaluated to a valid, unique
|
||||
* address.
|
||||
*/
|
||||
public Address parseAsAddress(String input) throws ExpressionException {
|
||||
return this.parseAsRelativeAddress(input, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the given input expression as a relative offset that will be added to the given
|
||||
* base address.
|
||||
* @param input the expression to evaluate as an offset
|
||||
* @param baseAddress the base address the evaluted expression will be added to to get the
|
||||
* resulting address.
|
||||
* @return the Address after the evaluated offset is added to the given base address.
|
||||
* @throws ExpressionException if the input expression can't be evaluated to a valid, unique
|
||||
* address.
|
||||
*/
|
||||
public Address parseAsRelativeAddress(String input, Address baseAddress)
|
||||
throws ExpressionException {
|
||||
ExpressionValue expressionValue = baseAddress == null ? parse(input)
|
||||
: parse(input, new AddressExpressionValue(baseAddress));
|
||||
|
||||
if (expressionValue instanceof AddressExpressionValue addressValue) {
|
||||
return validateAddressSpace(addressValue.getAddress());
|
||||
}
|
||||
if (expressionValue instanceof LongExpressionValue longValue) {
|
||||
long offset = longValue.getLongValue();
|
||||
AddressSpace space = getAddressSpace();
|
||||
try {
|
||||
return space.getAddress(offset, true);
|
||||
return space.getAddressInThisSpaceOnly(offset);
|
||||
}
|
||||
catch (AddressOutOfBoundsException e) {
|
||||
return null;
|
||||
throw new ExpressionException(e.getMessage());
|
||||
}
|
||||
}
|
||||
throw new ExpressionException("Expression did not evalute to a long! Got a " +
|
||||
expressionValue.getClass() + " instead.");
|
||||
|
||||
}
|
||||
|
||||
private static Object getValueObject(SymbolTable st, AddressFactory af, String tok) {
|
||||
|
||||
if (st == null || af == null) {
|
||||
return getValueObject(tok);
|
||||
/**
|
||||
* Returns the {@link AddressFactory} being used by this address evaluator
|
||||
* @return the {@link AddressFactory} being used by this address evaluator
|
||||
*/
|
||||
public AddressFactory getAddressFactory() {
|
||||
return addressFactory;
|
||||
}
|
||||
|
||||
try {
|
||||
return NumericUtilities.parseHexLong(tok);
|
||||
/**
|
||||
* Sets the {@link AddressSpace} to be used to convert long values into addresses.
|
||||
* @param space the address space to convert long values into addresses
|
||||
*/
|
||||
public void setPreferredAddressSpace(AddressSpace space) {
|
||||
this.preferredSpace = space;
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
Address address = af.getAddress(tok);
|
||||
if (address != null) {
|
||||
|
||||
// checks if the given address's address space is compatible with the preferred address space
|
||||
private Address validateAddressSpace(Address address) throws ExpressionException {
|
||||
if (preferredSpace == null) {
|
||||
return address;
|
||||
}
|
||||
|
||||
List<Symbol> globalSymbols = st.getLabelOrFunctionSymbols(tok, null);
|
||||
if (globalSymbols.size() == 1) {
|
||||
return globalSymbols.get(0).getAddress();
|
||||
AddressSpace space = address.getAddressSpace();
|
||||
if (space.equals(preferredSpace)) {
|
||||
return address;
|
||||
}
|
||||
return getValueObject(tok);
|
||||
if (isOverlayRelated(space, preferredSpace)) {
|
||||
return preferredSpace.getAddress(address.getOffset());
|
||||
}
|
||||
throw new ExpressionException("Selected address space is not compatible with expression!");
|
||||
}
|
||||
|
||||
private static Object getValueObject(String strValue) {
|
||||
try {
|
||||
int start = 0;
|
||||
int radix = 10;
|
||||
if (strValue.indexOf("0x") == 0) {
|
||||
start = 2;
|
||||
radix = 16;
|
||||
}
|
||||
strValue = strValue.toLowerCase();
|
||||
if (strValue.endsWith("ull") || strValue.endsWith("llu")) {
|
||||
strValue = strValue.substring(start, strValue.length() - 3);
|
||||
}
|
||||
else if (strValue.endsWith("ul") || strValue.endsWith("lu") || strValue.endsWith("ll")) {
|
||||
strValue = strValue.substring(start, strValue.length() - 2);
|
||||
}
|
||||
else if (strValue.endsWith("l") || strValue.endsWith("u")) {
|
||||
strValue = strValue.substring(start, strValue.length() - 1);
|
||||
}
|
||||
else {
|
||||
strValue = strValue.substring(start);
|
||||
private boolean isOverlayRelated(AddressSpace space1, AddressSpace space2) {
|
||||
AddressSpace base1 = getBaseSpace(space1);
|
||||
AddressSpace base2 = getBaseSpace(space2);
|
||||
return base1.equals(base2);
|
||||
}
|
||||
|
||||
return (radix == 10) ? NumericUtilities.parseLong(strValue)
|
||||
: NumericUtilities.parseHexLong(strValue);
|
||||
private AddressSpace getBaseSpace(AddressSpace space) {
|
||||
if (space instanceof OverlayAddressSpace overlaySpace) {
|
||||
return overlaySpace.getOverlayedSpace();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
// ignore
|
||||
}
|
||||
return null;
|
||||
return space;
|
||||
}
|
||||
|
||||
private static Object eval(List<Object> list) {
|
||||
|
||||
// first evaluate any grouped expressions
|
||||
boolean done = false;
|
||||
while (!done) {
|
||||
done = true;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (list.get(i) == Operator.LEFT_PAREN) {
|
||||
done = false;
|
||||
int end = findMatchingParen(list, i);
|
||||
if (end < 0) {
|
||||
return null;
|
||||
private AddressSpace getAddressSpace() {
|
||||
if (preferredSpace != null) {
|
||||
return preferredSpace;
|
||||
}
|
||||
Object obj = eval(list.subList(i + 1, end));
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
list.subList(i, i + 2).clear();
|
||||
list.set(i, obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check for leading Minus
|
||||
if (list.size() > 1 && list.get(0) == Operator.MINUS) {
|
||||
Object obj = list.get(1);
|
||||
if (obj instanceof Long) {
|
||||
obj = -((Long) obj).longValue();
|
||||
list.remove(0);
|
||||
list.set(0, obj);
|
||||
}
|
||||
}
|
||||
|
||||
//check for leading ~
|
||||
if (list.size() > 1 && list.get(0) == Operator.NOT) {
|
||||
Object obj = list.get(1);
|
||||
if (obj instanceof Long) {
|
||||
obj = ~((Long) obj).longValue();
|
||||
list.remove(0);
|
||||
list.set(0, obj);
|
||||
}
|
||||
}
|
||||
|
||||
//check for trailing leading ~
|
||||
if (list.size() > 3 && list.get(2) == Operator.NOT) {
|
||||
Object obj = list.get(3);
|
||||
if (obj instanceof Long) {
|
||||
obj = ~((Long) obj).longValue();
|
||||
list.remove(2);
|
||||
list.set(2, obj);
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate all SHIFT because they have precedence
|
||||
if (!evaluateOperator(list, Operator.RIGHTSHIFT, Operator.LEFTSHIFT)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// evaluate all TIMES because they have precedence
|
||||
if (!evaluateOperator(list, Operator.TIMES, Operator.DIVIDE)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// evaluate Plus and Minus, same precedence, but do plus then minus
|
||||
if (!evaluateOperator(list, Operator.PLUS, Operator.MINUS)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// evaluate & ^ |
|
||||
if (!evaluateOperator(list, Operator.AND, null)) {
|
||||
return null;
|
||||
}
|
||||
if (!evaluateOperator(list, Operator.XOR, null)) {
|
||||
return null;
|
||||
}
|
||||
if (!evaluateOperator(list, Operator.OR, null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!evaluateOperator(list, Operator.EQUALS, Operator.NOTEQUALS)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!evaluateOperator(list, Operator.LESS, Operator.GREATER)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!evaluateOperator(list, Operator.LESSEQUALS, Operator.GREATEREQUALS)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!evaluateOperator(list, Operator.LOG_AND, null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!evaluateOperator(list, Operator.LOG_OR, null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (list.size() != 1) {
|
||||
return null;
|
||||
}
|
||||
return list.get(0);
|
||||
}
|
||||
|
||||
private static boolean evaluateOperator(List<Object> list, Operator op1, Operator op2) {
|
||||
boolean done;
|
||||
done = false;
|
||||
while (!done) {
|
||||
done = true;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
Object obj = list.get(i);
|
||||
if (obj == op1 || obj == op2) {
|
||||
done = false;
|
||||
if (i == 0 || i == list.size() - 1) {
|
||||
return false;
|
||||
}
|
||||
Object value = computeValue(list.get(i - 1), (Operator) obj, list.get(i + 1));
|
||||
if (value == null) {
|
||||
return false;
|
||||
}
|
||||
list.subList(i, i + 2).clear();
|
||||
list.set(i - 1, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Object computeValue(Object v1, Operator op, Object v2) {
|
||||
if (op == Operator.TIMES) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() * ((Long) v2).longValue();
|
||||
}
|
||||
}
|
||||
if (op == Operator.DIVIDE) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() / ((Long) v2).longValue();
|
||||
}
|
||||
}
|
||||
else if (op == Operator.AND) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() & ((Long) v2).longValue();
|
||||
}
|
||||
}
|
||||
else if (op == Operator.XOR) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() ^ ((Long) v2).longValue();
|
||||
}
|
||||
}
|
||||
else if (op == Operator.OR) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() | ((Long) v2).longValue();
|
||||
}
|
||||
}
|
||||
else if (op == Operator.LEFTSHIFT) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() << ((Long) v2).longValue();
|
||||
}
|
||||
}
|
||||
else if (op == Operator.RIGHTSHIFT) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() >> ((Long) v2).longValue();
|
||||
}
|
||||
}
|
||||
else if (op == Operator.PLUS) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() + ((Long) v2).longValue();
|
||||
}
|
||||
else if ((v1 instanceof Address) && (v2 instanceof Long)) {
|
||||
return ((Address) v1).addWrap(((Long) v2).longValue());
|
||||
}
|
||||
else if ((v1 instanceof Long) && (v2 instanceof Address)) {
|
||||
return ((Address) v2).addWrap(((Long) v1).longValue());
|
||||
}
|
||||
}
|
||||
else if (op == Operator.NOT) {
|
||||
if (v2 instanceof Long) {
|
||||
return ~(((Long) v2).longValue());
|
||||
}
|
||||
else if (v2 instanceof Address) {
|
||||
return ((Address) v2).getNewAddress(~(((Long) v2).longValue()));
|
||||
}
|
||||
}
|
||||
else if (op == Operator.MINUS) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() - ((Long) v2).longValue();
|
||||
}
|
||||
else if ((v1 instanceof Address) && (v2 instanceof Long)) {
|
||||
return ((Address) v1).subtractWrap(((Long) v2).longValue());
|
||||
}
|
||||
else if ((v1 instanceof Address) && (v2 instanceof Address)) {
|
||||
return ((Address) v1).subtract((Address) v2);
|
||||
}
|
||||
}
|
||||
else if (op == Operator.EQUALS) {
|
||||
Long diff = getDifference(v1, v2);
|
||||
if (diff != null) {
|
||||
return diff == 0L ? 1L : 0L;
|
||||
}
|
||||
}
|
||||
else if (op == Operator.NOTEQUALS) {
|
||||
Long diff = getDifference(v1, v2);
|
||||
if (diff != null) {
|
||||
return diff != 0L ? 1L : 0L;
|
||||
}
|
||||
}
|
||||
else if (op == Operator.LESSEQUALS) {
|
||||
Long diff = getDifference(v1, v2);
|
||||
if (diff != null) {
|
||||
return diff <= 0L ? 1L : 0L;
|
||||
}
|
||||
}
|
||||
else if (op == Operator.GREATEREQUALS) {
|
||||
Long diff = getDifference(v1, v2);
|
||||
if (diff != null) {
|
||||
return diff >= 0L ? 1L : 0L;
|
||||
}
|
||||
}
|
||||
else if (op == Operator.LESS) {
|
||||
Long diff = getDifference(v1, v2);
|
||||
if (diff != null) {
|
||||
return diff < 0L ? 1L : 0L;
|
||||
}
|
||||
}
|
||||
else if (op == Operator.GREATER) {
|
||||
Long diff = getDifference(v1, v2);
|
||||
if (diff != null) {
|
||||
return diff > 0L ? 1L : 0L;
|
||||
}
|
||||
}
|
||||
else if (op == Operator.LOG_AND) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
boolean test = (((Long) v1).longValue()) != 0 && (((Long) v2).longValue()) != 0;
|
||||
return test ? 1L : 0L;
|
||||
}
|
||||
}
|
||||
else if (op == Operator.LOG_OR) {
|
||||
if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
boolean test = (((Long) v1).longValue()) != 0 || (((Long) v2).longValue()) != 0;
|
||||
return test ? 1L : 0L;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Long getDifference(Object v1, Object v2) {
|
||||
if ((v1 instanceof Address) && (v2 instanceof Long)) {
|
||||
return ((Address) v1).subtractWrap(((Long) v2).longValue()).getOffset();
|
||||
}
|
||||
else if ((v1 instanceof Address) && (v2 instanceof Address)) {
|
||||
return ((Address) v1).subtract((Address) v2);
|
||||
}
|
||||
else if ((v1 instanceof Long) && (v2 instanceof Long)) {
|
||||
return ((Long) v1).longValue() - ((Long) v2).longValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int findMatchingParen(List<Object> list, int index) {
|
||||
int depth = 1;
|
||||
for (int j = index + 1; j < list.size(); j++) {
|
||||
Object obj = list.get(j);
|
||||
if (obj == Operator.LEFT_PAREN) {
|
||||
depth++;
|
||||
}
|
||||
else if (obj == Operator.RIGHT_PAREN) {
|
||||
if (--depth == 0) {
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
class Operator {
|
||||
static Operator PLUS = new Operator("+");
|
||||
static Operator MINUS = new Operator("-");
|
||||
static Operator TIMES = new Operator("*");
|
||||
static Operator DIVIDE = new Operator("/");
|
||||
static Operator AND = new Operator("&");
|
||||
static Operator OR = new Operator("|");
|
||||
static Operator NOT = new Operator("~");
|
||||
static Operator XOR = new Operator("^");
|
||||
static Operator LEFTSHIFT = new Operator("<<");
|
||||
static Operator RIGHTSHIFT = new Operator(">>");
|
||||
static Operator LEFT_PAREN = new Operator("(");
|
||||
static Operator RIGHT_PAREN = new Operator(")");
|
||||
static Operator LOG_OR = new Operator("||");
|
||||
static Operator LOG_AND = new Operator("&&");
|
||||
static Operator EQUALS = new Operator("==");
|
||||
static Operator NOTEQUALS = new Operator("!=");
|
||||
static Operator LESS = new Operator("<");
|
||||
static Operator GREATER = new Operator(">");
|
||||
static Operator LESSEQUALS = new Operator("<=");
|
||||
static Operator GREATEREQUALS = new Operator(">=");
|
||||
|
||||
final String name;
|
||||
|
||||
private Operator(String name) {
|
||||
this.name = name;
|
||||
return addressFactory.getDefaultAddressSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name;
|
||||
protected ExpressionValue evaluateSymbol(String input) {
|
||||
Address address = addressFactory.getAddress(input);
|
||||
if (address != null) {
|
||||
return new AddressExpressionValue(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the static object implementation of an operator.
|
||||
* @param tok token string for the operator
|
||||
* @return the static operator object.
|
||||
*/
|
||||
public static Operator getOperator(String tok) {
|
||||
if (tok.equals("+")) {
|
||||
return PLUS;
|
||||
Program program = getProgram();
|
||||
if (program != null) {
|
||||
return getAddressForProgram(program, input);
|
||||
}
|
||||
if (tok.equals("&")) {
|
||||
return AND;
|
||||
|
||||
return null;
|
||||
}
|
||||
if (tok.equals("|")) {
|
||||
return OR;
|
||||
|
||||
private ExpressionValue getAddressForProgram(Program program, String input) {
|
||||
Address address = getAddressForSymbol(program, input);
|
||||
if (address == null) {
|
||||
address = getAddressFromMemoryMap(program, input);
|
||||
}
|
||||
if (tok.equals("^")) {
|
||||
return XOR;
|
||||
|
||||
return address == null ? null : new AddressExpressionValue(address);
|
||||
}
|
||||
else if (tok.equals("-")) {
|
||||
return MINUS;
|
||||
|
||||
private Address getAddressFromMemoryMap(Program program, String input) {
|
||||
Memory memory = program.getMemory();
|
||||
MemoryBlock block = memory.getBlock(input);
|
||||
if (block != null) {
|
||||
return block.getStart();
|
||||
}
|
||||
else if (tok.equals("~")) {
|
||||
return NOT;
|
||||
return null;
|
||||
}
|
||||
else if (tok.equals("*")) {
|
||||
return TIMES;
|
||||
|
||||
private Address getAddressForSymbol(Program program, String input) {
|
||||
SymbolPath symbolPath = new SymbolPath(input);
|
||||
String symbolName = symbolPath.getName();
|
||||
SymbolPath parent = symbolPath.getParent();
|
||||
|
||||
Namespace namespace = null;
|
||||
|
||||
if (parent != null) {
|
||||
namespace = getParentNamespace(program, parent);
|
||||
if (namespace == null) {
|
||||
// there was a namespace specified, but not uniquely found, so can't resolve.
|
||||
return null;
|
||||
}
|
||||
else if (tok.equals("/")) {
|
||||
return DIVIDE;
|
||||
}
|
||||
else if (tok.equals(")")) {
|
||||
return RIGHT_PAREN;
|
||||
SymbolTable symbolTable = program.getSymbolTable();
|
||||
List<Symbol> symbols = symbolTable.getLabelOrFunctionSymbols(symbolName, namespace);
|
||||
if (symbols.size() == 1) {
|
||||
return symbols.get(0).getAddress();
|
||||
}
|
||||
else if (tok.equals("(")) {
|
||||
return LEFT_PAREN;
|
||||
return null;
|
||||
}
|
||||
else if (tok.equals("<<")) {
|
||||
return LEFTSHIFT;
|
||||
|
||||
private Namespace getParentNamespace(Program program, SymbolPath path) {
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
else if (tok.equals(">>")) {
|
||||
return RIGHTSHIFT;
|
||||
List<Namespace> spaces = NamespaceUtils.getNamespaceByPath(program, null, path.getPath());
|
||||
if (spaces.size() == 1) {
|
||||
return spaces.get(0);
|
||||
}
|
||||
else if (tok.equals("==")) {
|
||||
return EQUALS;
|
||||
return null;
|
||||
}
|
||||
else if (tok.equals("!=")) {
|
||||
return NOTEQUALS;
|
||||
}
|
||||
else if (tok.equals("<")) {
|
||||
return LESS;
|
||||
}
|
||||
else if (tok.equals(">")) {
|
||||
return GREATER;
|
||||
}
|
||||
else if (tok.equals("<=")) {
|
||||
return LESSEQUALS;
|
||||
}
|
||||
else if (tok.equals(">=")) {
|
||||
return GREATEREQUALS;
|
||||
}
|
||||
else if (tok.equals("||")) {
|
||||
return LOG_OR;
|
||||
}
|
||||
else if (tok.equals("&&")) {
|
||||
return LOG_AND;
|
||||
|
||||
private Program getProgram() {
|
||||
if (programReference != null) {
|
||||
return programReference.get();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
/* ###
|
||||
* 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.program.util;
|
||||
|
||||
import generic.expressions.*;
|
||||
import ghidra.program.model.address.Address;
|
||||
|
||||
/**
|
||||
* Address operand values. See {@link ExpressionValue}. Defines supported operators and other
|
||||
* operands for expression values that are addresses.
|
||||
*/
|
||||
public class AddressExpressionValue implements ExpressionValue {
|
||||
private Address value;
|
||||
|
||||
public AddressExpressionValue(Address address) {
|
||||
this.value = address;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionValue applyUnaryOperator(ExpressionOperator operator) throws ExpressionException {
|
||||
long offset = value.getOffset();
|
||||
switch (operator) {
|
||||
case BITWISE_NOT:
|
||||
return addressExpressionOf(~offset);
|
||||
case UNARY_MINUS:
|
||||
return addressExpressionOf(-offset);
|
||||
case UNARY_PLUS:
|
||||
return this;
|
||||
default:
|
||||
throw new ExpressionException(
|
||||
"Unary Operator " + operator + " not supported by Long values!");
|
||||
}
|
||||
}
|
||||
|
||||
private AddressExpressionValue addressExpressionOf(long offset) {
|
||||
Address address = value.getNewAddress(offset);
|
||||
return new AddressExpressionValue(addressOf(offset));
|
||||
}
|
||||
|
||||
private AddressExpressionValue addressExpressionOf(Address address) {
|
||||
return new AddressExpressionValue(address);
|
||||
}
|
||||
|
||||
private Address addressOf(long offset) {
|
||||
return value.getNewAddress(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionValue applyBinaryOperator(ExpressionOperator operator, ExpressionValue operand)
|
||||
throws ExpressionException {
|
||||
|
||||
if (operand instanceof LongExpressionValue longOperand) {
|
||||
return applyBinaryOperator(operator, longOperand);
|
||||
}
|
||||
|
||||
if (operand instanceof AddressExpressionValue addressOperand) {
|
||||
return applyBinaryOperator(operator, addressOperand);
|
||||
}
|
||||
throw new ExpressionException("Unsupported operand type for Long: " + value);
|
||||
|
||||
}
|
||||
|
||||
private ExpressionValue applyBinaryOperator(ExpressionOperator operator,
|
||||
LongExpressionValue expressionValue) throws ExpressionException {
|
||||
long otherValue = expressionValue.getLongValue();
|
||||
long offset = value.getOffset();
|
||||
int compareResult = Long.compareUnsigned(offset, otherValue);
|
||||
|
||||
switch (operator) {
|
||||
case BITWISE_AND:
|
||||
return addressExpressionOf(offset & otherValue);
|
||||
case BITWISE_OR:
|
||||
return addressExpressionOf(offset | otherValue);
|
||||
case BITWISE_XOR:
|
||||
return addressExpressionOf(offset ^ otherValue);
|
||||
case DIVIDE:
|
||||
return addressExpressionOf(offset / otherValue);
|
||||
case SUBTRACT:
|
||||
return addressExpressionOf(value.subtract(otherValue));
|
||||
case ADD:
|
||||
return addressExpressionOf(value.add(otherValue));
|
||||
case MULTIPLY:
|
||||
return addressExpressionOf(offset * otherValue);
|
||||
case SHIFT_LEFT:
|
||||
return addressExpressionOf(offset << otherValue);
|
||||
case SHIFT_RIGHT:
|
||||
return addressExpressionOf(offset >> otherValue);
|
||||
case EQUALS:
|
||||
return booleanExpression(compareResult == 0);
|
||||
case GREATER_THAN:
|
||||
return booleanExpression(compareResult > 0);
|
||||
case LESS_THAN:
|
||||
return booleanExpression(compareResult < 0);
|
||||
case GREATER_THAN_OR_EQUAL:
|
||||
return booleanExpression(compareResult >= 0);
|
||||
case LESS_THAN_OR_EQUAL:
|
||||
return booleanExpression(compareResult <= 0);
|
||||
|
||||
default:
|
||||
throw new ExpressionException(
|
||||
"Binary Operator \"" + operator +
|
||||
"\" with Long operands not supported by Address values!");
|
||||
}
|
||||
}
|
||||
|
||||
private ExpressionValue booleanExpression(boolean b) {
|
||||
return new LongExpressionValue(b ? 1 : 0);
|
||||
}
|
||||
|
||||
private ExpressionValue applyBinaryOperator(ExpressionOperator operator,
|
||||
AddressExpressionValue expressionValue) throws ExpressionException {
|
||||
Address otherValue = expressionValue.getAddress();
|
||||
long otherValueOffset = otherValue.getOffset();
|
||||
long offset = value.getOffset();
|
||||
int compareResult = value.compareTo(otherValue);
|
||||
|
||||
switch (operator) {
|
||||
case BITWISE_AND:
|
||||
return new LongExpressionValue(offset & otherValueOffset);
|
||||
case BITWISE_OR:
|
||||
return new LongExpressionValue(offset | otherValueOffset);
|
||||
case BITWISE_XOR:
|
||||
return new LongExpressionValue(offset ^ otherValueOffset);
|
||||
case SUBTRACT:
|
||||
return new LongExpressionValue(value.subtract(otherValue));
|
||||
case ADD:
|
||||
return new LongExpressionValue(offset + otherValueOffset);
|
||||
case EQUALS:
|
||||
return booleanExpression(compareResult == 0);
|
||||
case GREATER_THAN:
|
||||
return booleanExpression(compareResult > 0);
|
||||
case LESS_THAN:
|
||||
return booleanExpression(compareResult < 0);
|
||||
case GREATER_THAN_OR_EQUAL:
|
||||
return booleanExpression(compareResult >= 0);
|
||||
case LESS_THAN_OR_EQUAL:
|
||||
return booleanExpression(compareResult <= 0);
|
||||
default:
|
||||
throw new ExpressionException(
|
||||
"Binary Operator \"" + operator +
|
||||
"\" with Long operands not supported by Address values!");
|
||||
}
|
||||
}
|
||||
|
||||
public Address getAddress() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -27,6 +27,7 @@ import java.util.Iterator;
|
|||
|
||||
import org.bouncycastle.util.Arrays;
|
||||
|
||||
import generic.expressions.ExpressionEvaluator;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.app.plugin.core.datamgr.util.DataTypeArchiveUtility;
|
||||
import ghidra.app.script.GhidraScript;
|
||||
|
@ -35,7 +36,6 @@ import ghidra.app.util.cparser.C.CParserUtils.CParseResults;
|
|||
import ghidra.app.util.cparser.CPP.*;
|
||||
import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.program.util.AddressEvaluator;
|
||||
import ghidra.util.Msg;
|
||||
|
||||
public class CreateAVR8GDTArchiveScript extends GhidraScript {
|
||||
|
@ -230,7 +230,7 @@ public class CreateAVR8GDTArchiveScript extends GhidraScript {
|
|||
continue;
|
||||
}
|
||||
|
||||
lvalue = AddressEvaluator.evaluateToLong(expandValue);
|
||||
lvalue = ExpressionEvaluator.evaluateToLong(expandValue);
|
||||
if (lvalue == null) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ public class MemoryMapPluginScreenShots extends GhidraScreenShotGenerator {
|
|||
DialogComponentProvider dialog = getDialog();
|
||||
GhidraComboBox<?> comboBox = (GhidraComboBox<?>) getInstanceField("comboBox", dialog);
|
||||
selectItem(comboBox, "Byte Mapped");
|
||||
|
||||
runSwing(() -> dialog.setStatusText(""));
|
||||
captureDialog();
|
||||
|
||||
drawRectangleAround(comboBox, Palette.GREEN, 10);
|
||||
|
|