GP-1756: New File Offset field and Go To File Offset feature

This commit is contained in:
Ryan Kurtz 2022-03-01 03:44:56 -05:00
parent 855853935b
commit eb324a2b20
10 changed files with 647 additions and 39 deletions

View file

@ -557,6 +557,24 @@
only the portion of the string that is used will be displayed.</P>
</BLOCKQUOTE>
<H3><A name="File_Offset_Field"></A>File Offset Field</H3>
<BLOCKQUOTE>
<P>The File Offset field shows the filename and file offset of the original imported byte
value for the given address. If the address's byte was not derived from an imported binary
file, or file offset tracking is not supported by the binary file's importer, a value of
"N/A" is shown.</P>
<P><B>Show Filename -</B> Option to prefix the file offset with the source filename. This
is useful if more than one binary file has been imported into a program.</P>
<P><B>Show Numbers In Hex -</B> Option to display the file offset in hexadecimal rather than
decimal.</P>
<P><IMG src="../../shared/note.png" border="0">The File Offset field is disabled by default.
To enable the field, see the <A HREF="Browser_Field_Formatter.htm#Enable_Field">Enable Field
</A> section.
</P>
</BLOCKQUOTE>
<H3><A name="Format_Code"></A>Format Code</H3>
<BLOCKQUOTE>

View file

@ -39,14 +39,15 @@
<LI>The <I>Go To</I> dialog will be displayed, as shown below:</LI>
<LI>Enter either an <A href="#gotoaddress">address</A>, <A href="#gotolabel">label</A>, or
<A href="#gotoexpression">expression</A> as specified below and press "OK"</LI>
<LI>Enter either an <A href="#GoTo_Address">address</A>, <A href="#GoTo_Label">label</A>,
<A href="#GoTo_Expression">expression</A>, or <A href="#GoTo_File_Offset">file offset</A>
as specified below and press "OK"</LI>
<LI>If the address, lable, or expression is valid, the Code Browser will be repositioned to
that location and the dialog will be dismissed</LI>
<LI>If the address, label, expression, or file offset is valid, the Code Browser will be
repositioned to that location and the dialog will be dismissed</LI>
<LI>If the address, label, or expression is not valid, the dialog will display an error
message</LI>
<LI>If the address, label, expression, or file offset is not valid, the dialog will display
an error message</LI>
</OL>
</BLOCKQUOTE>
@ -56,7 +57,7 @@
<TD width="100%">
<P align="center"><IMG alt="" src="images/GoToDialog.png"></P>
<P align="center"><I><FONT size="3">Go To Address, Label, or Expression
<P align="center"><I><FONT size="3">Go To Address, Label, Expression, or File Offset
Dialag</FONT></I></P>
</TD>
</TR>
@ -67,7 +68,7 @@
<BR>
<H3><A name="gotoaddress"></A>Go To Address</H3>
<H3><A name="GoTo_Address"></A>Go To Address</H3>
<BLOCKQUOTE>
<P>Enter an address into the text area of the dialog. The value entered will be assumed to
@ -181,7 +182,7 @@
</TBODY>
</TABLE>
<H3><A name="gotolabel"></A>Go To Label</H3>
<H3><A name="GoTo_Label"></A>Go To Label</H3>
<BLOCKQUOTE>
<P>Enter the name of an existing label into the text area of the dialog.&nbsp;</P>
@ -216,14 +217,32 @@
</BLOCKQUOTE>
<H3><A name="gotoexpression"></A>Go To Expression<BR>
<H3><A name="GoTo_File_Offset"></A>Go To File Offset<BR>
</H3>
<BLOCKQUOTE>
<P>Enter <B>file:</B> followed by a file offset of the program's source file bytes (at
time of import) into the text area of the dialog. The file offset entered will be assumed
to be in decimal, unless it is preceeded by <B>0x</B>. That is, "file:0x1000" and
"file:1000" are different values.</P>
<P><I><IMG src="../../shared/note.png" border="0">Ghidra does not support storing source
file bytes for all file formats. Searching for a file offset in these programs will always
yield no results.</I></P>
<P><I><IMG src="../../shared/note.png" border="0">When the program has multiple file byte
sources and the destination address is ambiguous, a query results dialog will be displayed.
</I></P>
</BLOCKQUOTE>
<H3><A name="GoTo_Expression"></A>Go To Expression<BR>
</H3>
<BLOCKQUOTE>
<P>Enter an arithmetic expression that can include addresses, symbols, or can be relative
to the current location. All numbers are assumed to be hexadecimal. Supported operator are
"+ - * / &lt;&lt; &gt;&gt;".&nbsp; Also, parentheses are supported to control order of
expresion evaluation.<BR>
expression evaluation.<BR>
For example:<BR>
</P>
@ -247,7 +266,7 @@
<TR>
<TD><B>0x100000+(2*10)</B>
</TD>
<TD>Posiitons the cursor at address 0x100020
<TD>Positions the cursor at address 0x100020
</TD>
</TR>
<TR>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Before After
Before After

View file

@ -16,8 +16,6 @@
package ghidra.app.util.navigation;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.List;
@ -54,7 +52,8 @@ public class GoToAddressLabelDialog extends DialogComponentProvider implements G
private static final String DIALOG_TITLE = "Go To ...";
private static final String ANCHOR_NAME = "EXPRESSION";
private static final String EXPRESSION_ANCHOR_NAME = "GoTo_Expression";
private static final String FILE_OFFSET_ANCHOR_NAME = "GoTo_File_Offset";
private static final int DEFAULT_MAX_GOTO_ENTRIES = 10;
//////////////////////////////////////////////////////////////////////
@ -181,31 +180,26 @@ public class GoToAddressLabelDialog extends DialogComponentProvider implements G
gbc.gridwidth = 2;
gbc.insets = new Insets(5, 5, 5, 5);
hyperlink = new HyperlinkComponent("<html>Enter an address, label or " + "<a href=\"" +
ANCHOR_NAME + "\">expression</a>:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
DockingWindowManager.setHelpLocation(hyperlink,
new HelpLocation(HelpTopics.NAVIGATION, "gotoexpression"));
hyperlink = new HyperlinkComponent("<html>Enter an address, label, <a href=\"" +
EXPRESSION_ANCHOR_NAME + "\">expression</a>, or " +
"<a href=\"" + FILE_OFFSET_ANCHOR_NAME +
"\">file offset</a>:");
hyperlink.addHyperlinkListener(ANCHOR_NAME, new HyperlinkListener() {
@Override
public void hyperlinkUpdate(HyperlinkEvent e) {
if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
return;
}
showExpressionHelp();
HyperlinkListener hyperlinkListener = evt -> {
if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
HelpLocation loc = new HelpLocation(HelpTopics.NAVIGATION, evt.getDescription());
DockingWindowManager.getHelpService().showHelp(loc);
}
});
inner.add(hyperlink);
};
hyperlink.addHyperlinkListener(EXPRESSION_ANCHOR_NAME, hyperlinkListener);
hyperlink.addHyperlinkListener(FILE_OFFSET_ANCHOR_NAME, hyperlinkListener);
inner.add(hyperlink, gbc);
comboBox = new GhidraComboBox<>();
comboBox.setEditable(true);
comboBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
okCallback();
}
});
comboBox.addActionListener(evt -> okCallback());
gbc.insets = new Insets(2, 5, 2, 0);
gbc.gridx = 0;
gbc.gridy = 1;
@ -230,11 +224,6 @@ public class GoToAddressLabelDialog extends DialogComponentProvider implements G
return mainPanel;
}
protected void showExpressionHelp() {
DockingWindowManager.getHelpService().showHelp(hyperlink, false, hyperlink);
}
private void writeHistory(SaveState saveState) {
String[] strs = new String[history.size()];
strs = history.toArray(strs);

View file

@ -20,6 +20,8 @@ import java.util.*;
import javax.swing.SwingUtilities;
import org.apache.commons.lang3.StringUtils;
import docking.widgets.table.threaded.ThreadedTableModelListener;
import ghidra.GhidraOptions;
import ghidra.app.nav.Navigatable;
@ -48,6 +50,8 @@ import ghidra.util.table.GhidraProgramTableModel;
import ghidra.util.task.TaskMonitor;
public class GoToQuery {
private final String FILE_OFFSET_PREFIX = "file:";
private QueryData queryData;
private Address fromAddress;
private GhidraProgramTableModel<?> model;
@ -101,6 +105,9 @@ public class GoToQuery {
if (processWildCard()) {
return true;
}
if (processFileOffset()) {
return true;
}
if (processSymbolInParsedScope()) {
return true;
}
@ -346,6 +353,27 @@ public class GoToQuery {
return true;
}
private boolean processFileOffset() {
String input = queryData.getQueryString();
if (StringUtils.startsWithIgnoreCase(input, FILE_OFFSET_PREFIX)) {
try {
long offset = Long.decode(input.substring(FILE_OFFSET_PREFIX.length()));
// NOTE: Addresses are parsed via AbstractAddressSpace.parseString(String addr)
Program currentProgram = programs.iterator().next();
Memory mem = currentProgram.getMemory();
List<Address> addresses = mem.locateAddressesForFileOffset(offset);
if (addresses.size() > 0) {
goToAddresses(currentProgram, addresses.toArray(new Address[0]));
return true;
}
}
catch (NumberFormatException e) {
// fall through to return false
}
}
return false;
}
public boolean isWildCard() {
String queryInput = queryData.getQueryString();
return queryInput.indexOf(PluginConstants.ANYSUBSTRING_WILDCARD_CHAR) > -1 ||

View file

@ -0,0 +1,195 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.field;
import java.beans.PropertyEditor;
import java.math.BigInteger;
import docking.widgets.fieldpanel.field.*;
import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.app.util.HighlightProvider;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.framework.options.*;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Data;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockSourceInfo;
import ghidra.program.util.FileOffsetFieldLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HelpLocation;
import ghidra.util.exception.AssertException;
/**
* Generates File Offset fields
*/
public class FileOffsetFieldFactory extends FieldFactory {
public static final String FIELD_NAME = "File Offset";
public static final String GROUP_TITLE = "File Offset Field";
public final static String FILE_OFFSET_DISPLAY_OPTIONS_NAME =
GROUP_TITLE + Options.DELIMITER + "File Offset Display Options";
private boolean showFilename;
private boolean useHex;
private PropertyEditor fileOffsetFieldOptionsEditor =
new FileOffsetFieldOptionsPropertyEditor();
/**
* Default Constructor
*/
public FileOffsetFieldFactory() {
super(FIELD_NAME);
}
/**
* Constructor
* @param model the model that the field belongs to.
* @param hlProvider the HightLightStringProvider.
* @param displayOptions the Options for display properties.
* @param fieldOptions the Options for field specific properties.
*/
private FileOffsetFieldFactory(FieldFormatModel model, HighlightProvider hlProvider,
Options displayOptions, Options fieldOptions) {
super(FIELD_NAME, model, hlProvider, displayOptions, fieldOptions);
initOptions(fieldOptions);
}
private void initOptions(Options fieldOptions) {
HelpLocation helpLoc = new HelpLocation("CodeBrowserPlugin", "File_Offset_Field");
fieldOptions.registerOption(FILE_OFFSET_DISPLAY_OPTIONS_NAME, OptionType.CUSTOM_TYPE,
new FileOffsetFieldOptionsWrappedOption(), helpLoc,
"Adjusts the File Offset Field display", fileOffsetFieldOptionsEditor);
CustomOption customOption =
fieldOptions.getCustomOption(FILE_OFFSET_DISPLAY_OPTIONS_NAME, null);
if (!(customOption instanceof FileOffsetFieldOptionsWrappedOption)) {
throw new AssertException("Someone set an option for " +
FILE_OFFSET_DISPLAY_OPTIONS_NAME + " that is not the expected " +
FileOffsetFieldOptionsWrappedOption.class.getName() + " type.");
}
FileOffsetFieldOptionsWrappedOption fofowo =
(FileOffsetFieldOptionsWrappedOption) customOption;
showFilename = fofowo.showFilename();
useHex = fofowo.useHex();
fieldOptions.getOptions(GROUP_TITLE).setOptionsHelpLocation(helpLoc);
}
@Override
public FieldFactory newInstance(FieldFormatModel formatModel,
HighlightProvider highlightProvider, ToolOptions options, ToolOptions fieldOptions) {
return new FileOffsetFieldFactory(formatModel, highlightProvider, options, fieldOptions);
}
@Override
public void fieldOptionsChanged(Options options, String optionsName, Object oldValue,
Object newValue) {
if (optionsName.equals(FILE_OFFSET_DISPLAY_OPTIONS_NAME)) {
FileOffsetFieldOptionsWrappedOption fofowo =
(FileOffsetFieldOptionsWrappedOption) newValue;
showFilename = fofowo.showFilename();
useHex = fofowo.useHex();
model.update();
}
}
@Override
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
Object obj = proxy.getObject();
if (!enabled || !(obj instanceof CodeUnit)) {
return null;
}
CodeUnit cu = (CodeUnit) obj;
Address addr = cu.getAddress();
MemoryBlock block = cu.getProgram().getMemory().getBlock(addr);
String text = "N/A";
for (MemoryBlockSourceInfo sourceInfo : block.getSourceInfos()) {
if (sourceInfo.contains(addr)) {
if (sourceInfo.getFileBytes().isPresent()) {
FileBytes fileBytes = sourceInfo.getFileBytes().get();
long offset = sourceInfo.getFileBytesOffset(addr);
if (useHex) {
text = String.format("0x%x", offset);
}
else {
text = String.format("%d", offset);
}
if (showFilename) {
text = fileBytes.getFilename() + ":" + text;
}
break;
}
}
}
FieldElement fieldElement =
new TextFieldElement(new AttributedString(text, color, getMetrics()), 0, 0);
ListingTextField listingTextField = ListingTextField.createSingleLineTextField(this, proxy,
fieldElement, startX + varWidth, width, hlProvider);
listingTextField.setPrimary(true);
return listingTextField;
}
@Override
public FieldLocation getFieldLocation(ListingField lf, BigInteger index, int fieldNum,
ProgramLocation loc) {
if (loc instanceof FileOffsetFieldLocation) {
FileOffsetFieldLocation fileOffsetFieldLoc = (FileOffsetFieldLocation) loc;
Object obj = lf.getProxy().getObject();
if (obj instanceof CodeUnit && hasSamePath(lf, fileOffsetFieldLoc)) {
return new FieldLocation(index, fieldNum, 0, fileOffsetFieldLoc.getCharOffset());
}
}
return null;
}
@Override
public ProgramLocation getProgramLocation(int row, int col, ListingField lf) {
Object obj = lf.getProxy().getObject();
if (!(obj instanceof CodeUnit)) {
return null;
}
CodeUnit cu = (CodeUnit) obj;
Address addr = cu.getMinAddress();
int[] cpath = null;
if (cu instanceof Data) {
cpath = ((Data) cu).getComponentPath();
}
return new FileOffsetFieldLocation(cu.getProgram(), addr, cpath, col);
}
@Override
public boolean acceptsType(int category, Class<?> proxyObjectClass) {
if (!CodeUnit.class.isAssignableFrom(proxyObjectClass)) {
return false;
}
return (category == FieldFormatModel.INSTRUCTION_OR_DATA ||
category == FieldFormatModel.OPEN_DATA || category == FieldFormatModel.ARRAY);
}
}

View file

@ -0,0 +1,136 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.field;
import java.awt.Component;
import java.beans.PropertyEditorSupport;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import docking.widgets.checkbox.GCheckBox;
import docking.widgets.label.GDLabel;
import ghidra.framework.options.CustomOptionsEditor;
import ghidra.util.HTMLUtilities;
import ghidra.util.layout.PairLayout;
/**
* Provides a custom GUI layout for the File Offset field options
*/
public class FileOffsetFieldOptionsPropertyEditor extends PropertyEditorSupport
implements CustomOptionsEditor {
private static final String SHOW_FILENAME_LABEL = "Show Filename";
private static final String USE_HEX_LABEL = "Show Numbers In Hex";
private static final String[] NAMES = { SHOW_FILENAME_LABEL, USE_HEX_LABEL };
private static final String SHOW_FILENAME_TOOLTIP = HTMLUtilities.toWrappedHTML(
"Prepends the filename to the file offset in the File Offset field.", 75);
private static final String USE_HEX_TOOLTIP = HTMLUtilities.toWrappedHTML(
"Toggles displaying file offsets in hexadecimal/decimal in the File Offset field.", 75);
private static final String[] DESCRIPTIONS = { SHOW_FILENAME_TOOLTIP, USE_HEX_TOOLTIP };
private FileOffsetFieldOptionsWrappedOption option;
private Component editorComponent;
private GCheckBox showFilenameCheckbox;
private GCheckBox useHexCheckbox;
/**
* Creates a new {@link FileOffsetFieldOptionsPropertyEditor}
*/
public FileOffsetFieldOptionsPropertyEditor() {
editorComponent = buildEditor();
}
private Component buildEditor() {
// we want to have a panel with our options so that we may group them together
JPanel panel = new JPanel(new PairLayout(6, 10));
GDLabel showFilenameLabel = new GDLabel(SHOW_FILENAME_LABEL, SwingConstants.RIGHT);
showFilenameLabel.setToolTipText(SHOW_FILENAME_TOOLTIP);
panel.add(showFilenameLabel);
showFilenameCheckbox = new GCheckBox();
showFilenameCheckbox.setToolTipText(SHOW_FILENAME_TOOLTIP);
panel.add(showFilenameCheckbox);
GDLabel useHexLabel = new GDLabel(USE_HEX_LABEL, SwingConstants.RIGHT);
useHexLabel.setToolTipText(USE_HEX_TOOLTIP);
panel.add(useHexLabel);
useHexCheckbox = new GCheckBox();
useHexCheckbox.setToolTipText(USE_HEX_TOOLTIP);
panel.add(useHexCheckbox);
showFilenameCheckbox.addItemListener(evt -> firePropertyChange());
useHexCheckbox.addItemListener(evt -> firePropertyChange());
return panel;
}
@Override
public void setValue(Object value) {
if (!(value instanceof FileOffsetFieldOptionsWrappedOption)) {
return;
}
option = (FileOffsetFieldOptionsWrappedOption) value;
setLocalValues(option);
firePropertyChange();
}
private void setLocalValues(FileOffsetFieldOptionsWrappedOption option) {
if (option.showFilename() != showFilenameCheckbox.isSelected()) {
showFilenameCheckbox.setSelected(option.showFilename());
}
if (option.useHex() != useHexCheckbox.isSelected()) {
useHexCheckbox.setSelected(option.useHex());
}
}
private FileOffsetFieldOptionsWrappedOption cloneFileOffsetValues() {
FileOffsetFieldOptionsWrappedOption newOption = new FileOffsetFieldOptionsWrappedOption();
newOption.setShowFilename(showFilenameCheckbox.isSelected());
newOption.setUseHex(useHexCheckbox.isSelected());
return newOption;
}
@Override
public String[] getOptionDescriptions() {
return DESCRIPTIONS;
}
@Override
public String[] getOptionNames() {
return NAMES;
}
@Override
public Object getValue() {
return cloneFileOffsetValues();
}
@Override
public Component getCustomEditor() {
return editorComponent;
}
@Override
public boolean supportsCustomEditor() {
return true;
}
}

View file

@ -0,0 +1,115 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.util.viewer.field;
import ghidra.framework.options.CustomOption;
import ghidra.framework.options.SaveState;
/**
* An option class that allows the user to edit a related group of options pertaining to
* File Offset field display
*/
public class FileOffsetFieldOptionsWrappedOption implements CustomOption {
private static final String SHOW_FILENAME = "ShowFilename";
private static final String USE_HEX = "UseHex";
private static final boolean DEFAULT_SHOW_FILENAME = false;
private static final boolean DEFAULT_USE_HEX = true;
private boolean showFilename = DEFAULT_SHOW_FILENAME;
private boolean useHex = DEFAULT_USE_HEX;
/**
* Default constructor, required for persistence
*/
public FileOffsetFieldOptionsWrappedOption() {
}
/**
* Returns whether or not to show the filename
*
* @return True if the filename is to be shown; otherwise, false
*/
public boolean showFilename() {
return showFilename;
}
/**
* Sets whether or not to show the filename
*
* @param showFilename True to show the filename, false to hide it
*/
public void setShowFilename(boolean showFilename) {
this.showFilename = showFilename;
}
/**
* Returns whether or not to display the file offset in hexadecimal
*
* @return True if the file offset is to be displayed in hexadecimal; otherwise, false
*/
public boolean useHex() {
return useHex;
}
/**
* Sets whether or not to display the file offset in hexadecimal
*
* @param useHex True to display the file offset in hexadecimal, false for decimal
*/
public void setUseHex(boolean useHex) {
this.useHex = useHex;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof FileOffsetFieldOptionsWrappedOption)) {
return false;
}
if (this == obj) {
return true;
}
FileOffsetFieldOptionsWrappedOption otherOption = (FileOffsetFieldOptionsWrappedOption) obj;
return showFilename == otherOption.showFilename && useHex == otherOption.useHex;
}
@Override
public int hashCode() {
int prime = 31;
int result = 1;
result = prime * result + (showFilename ? 1 : 0);
result = prime * result + (useHex ? 1 : 0);
return result;
}
//==================================================================================================
//Persistence
//==================================================================================================
@Override
public void readState(SaveState saveState) {
showFilename = saveState.getBoolean(SHOW_FILENAME, showFilename);
useHex = saveState.getBoolean(USE_HEX, useHex);
}
@Override
public void writeState(SaveState saveState) {
saveState.putBoolean(SHOW_FILENAME, showFilename);
saveState.putBoolean(USE_HEX, useHex);
}
}

View file

@ -44,13 +44,16 @@ import ghidra.app.plugin.core.table.TableComponentProvider;
import ghidra.app.plugin.core.table.TableServicePlugin;
import ghidra.app.services.ProgramManager;
import ghidra.app.services.QueryData;
import ghidra.app.util.MemoryBlockUtils;
import ghidra.app.util.PluginConstants;
import ghidra.app.util.bin.ByteArrayProvider;
import ghidra.app.util.navigation.GoToAddressLabelDialog;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.PluginTool;
import ghidra.framework.plugintool.ServiceProviderStub;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.data.*;
@ -219,6 +222,65 @@ public class GoToAddressLabelPluginTest extends AbstractGhidraHeadedIntegrationT
assertEquals(addr("0x100494d"), cbPlugin.getCurrentAddress());
}
@Test
public void testGoToFileOffset() throws Exception {
loadProgram("x86");
Memory mem = program.getMemory();
//@formatter:off
// Create a 4-byte and a 1-byte memory block using a 4-byte source FileBytes (just an array
// in this case). The 2nd block's byte should share a file byte with the first block so we
// can get multiple results when doing a Go To on that file offset.
byte[] bytes =
/* Block1 |---|---|---|---| */
/* FileBytes*/ { 1, 2, 3, 4 } ;
/* Block2 |---| */
//@formatter:on
// Create FileBytes-based memory blocks
Address addr1 = addr("0x2000");
Address addr2 = addr("0x3000");
tx(program, () -> {
FileBytes fileBytes1 = MemoryBlockUtils.createFileBytes(program,
new ByteArrayProvider(program.getName() + "1", bytes), TaskMonitor.DUMMY);
FileBytes fileBytes2 = MemoryBlockUtils.createFileBytes(program,
new ByteArrayProvider(program.getName() + "2", bytes), TaskMonitor.DUMMY);
mem.createInitializedBlock("FileBytes1", addr1, fileBytes1, 0, 4, false);
mem.createInitializedBlock("FileBytes2", addr2, fileBytes2, 3, 1, false);
});
// Test decimal
setText("file:0");
showDialog();
performOkCallback();
assertEquals(addr1, cbPlugin.getCurrentAddress());
// Test hex
setText("file:0x1");
showDialog();
performOkCallback();
assertEquals(addr1.add(1), cbPlugin.getCurrentAddress());
// Test "case"
setText("FILE:2");
showDialog();
performOkCallback();
assertEquals(addr1.add(2), cbPlugin.getCurrentAddress());
// Test not found
setText("file:0x100");
showDialog();
performOkCallback();
assertNotEquals(addr1.add(0x100), cbPlugin.getCurrentAddress());
// Test multiple results
setText("file:3");
showDialog();
performOkCallback();
GhidraProgramTableModel<?> model = waitForModel();
assertEquals(2, model.getRowCount());
}
@Test
public void testScopedSymbol() throws Exception {
loadProgram("x86");

View file

@ -0,0 +1,46 @@
/* ###
* 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 ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
/**
* Provides specific information about a program location within the File Offset field
*/
public class FileOffsetFieldLocation extends CodeUnitLocation {
/**
* Creates a new {@link FileOffsetFieldLocation} for the given address
*
* @param program the program
* @param addr the address of the byte for this location
* @param componentPath the path to data, or null
* @param charOffset the position into the string representation indicating the exact
* position within the field
*/
public FileOffsetFieldLocation(Program program, Address addr, int[] componentPath,
int charOffset) {
super(program, addr, componentPath, 0, 0, charOffset);
}
/**
* Default constructor needed for restoring the field location from XML
*/
public FileOffsetFieldLocation() {
}
}