Skip to content

Commit

Permalink
GP-4855: Adding new fields and copy specials for various types of add…
Browse files Browse the repository at this point in the history
…ress offsets
  • Loading branch information
ryanmkurtz committed Aug 22, 2024
1 parent 2c3a815 commit fb6f853
Show file tree
Hide file tree
Showing 14 changed files with 647 additions and 409 deletions.
2 changes: 1 addition & 1 deletion Ghidra/Features/Base/data/base.listing.theme.properties
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ color.fg.listing.block.start = color.palette.indigo
color.fg.listing.bytes.alignment = color.palette.gray
color.fg.listing.bytes.entropy = color.fg
color.fg.listing.disassembly.external = color.fg
color.fg.listing.file.offset = color.fg
color.fg.listing.field.offset = color.fg

color.fg.listing.instruction.parallel = color.palette.blue
color.fg.listing.mask.bits = color.palette.navy
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -584,18 +584,64 @@ <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
file, or file offset tracking is not supported by the binary file's importer, no offset is
shown.</P>
<P><B>Show Name -</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
<P><B>Use Hex -</B> Option to display the file offset in hexadecimal rather than
decimal.</P>
<P><IMG src="help/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="Function_Offset_Field"></A>Function Offset Field</H3>

<BLOCKQUOTE>
<P>The Function Offset field shows the function name and function offset of the
the given address. If the address is not in a function, no offset is shown.</P>
<P><B>Show Name -</B> Option to prefix the function offset with the function name.</P>
<P><B>Use Hex -</B> Option to display the function offset in hexadecimal rather than
decimal.</P>
<P><IMG src="help/shared/note.png" border="0">The Function 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="Imagebase_Offset_Field"></A>Imagebase Offset Field</H3>

<BLOCKQUOTE>
<P>The Imagebase Offset field shows the imagebase offset of the given address. If the
address and the imagebase are in different address spaces, no offset is shown.</P>
<P><B>Show Name -</B> Option to prefix the imagebase offset with "imagebase".</P>
<P><B>Use Hex -</B> Option to display the imagebase offset in hexadecimal rather than
decimal.</P>
<P><IMG src="help/shared/note.png" border="0">The Imagebase 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="MemoryBlock_Offset_Field"></A>MemoryBlock Offset Field</H3>

<BLOCKQUOTE>
<P>The MemoryBlock Offset field shows the memory block name and memory block offset of the
given address.</P>
<P><B>Show Name -</B> Option to prefix the memory block offset with the memory block
name.</P>
<P><B>Use Hex -</B> Option to display the memory block offset in hexadecimal rather than
decimal.</P>
<P><IMG src="help/shared/note.png" border="0">The MemoryBlock 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>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
* 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.
Expand Down Expand Up @@ -42,7 +42,6 @@
import ghidra.app.services.ClipboardContentProviderService;
import ghidra.app.util.*;
import ghidra.app.util.viewer.listingpanel.ListingModel;
import ghidra.framework.cmd.Command;
import ghidra.framework.model.DomainFile;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.mem.AddressSourceInfo;
Expand All @@ -51,6 +50,7 @@
import ghidra.program.model.address.*;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.symbol.*;
import ghidra.program.util.*;
import ghidra.util.Msg;
Expand All @@ -70,6 +70,12 @@ public class CodeBrowserClipboardProvider extends ByteCopier
new ClipboardType(DataFlavor.stringFlavor, "Address w/ Offset");
public static final ClipboardType BYTE_SOURCE_OFFSET_TYPE =
new ClipboardType(DataFlavor.stringFlavor, "Byte Source Offset");
public static final ClipboardType FUNCTION_OFFSET_TYPE =
new ClipboardType(DataFlavor.stringFlavor, "Function Offset");
public static final ClipboardType IMAGEBASE_OFFSET_TYPE =
new ClipboardType(DataFlavor.stringFlavor, "Imagebase Offset");
public static final ClipboardType BLOCK_OFFSET_TYPE =
new ClipboardType(DataFlavor.stringFlavor, "Memory Block Offset");
public static final ClipboardType CODE_TEXT_TYPE =
new ClipboardType(DataFlavor.stringFlavor, "Formatted Code");
public static final ClipboardType LABELS_COMMENTS_TYPE =
Expand Down Expand Up @@ -100,6 +106,9 @@ private static List<ClipboardType> createCopyTypesList() {
list.add(ADDRESS_TEXT_TYPE);
list.add(ADDRESS_TEXT_WITH_OFFSET_TYPE);
list.add(BYTE_SOURCE_OFFSET_TYPE);
list.add(BLOCK_OFFSET_TYPE);
list.add(FUNCTION_OFFSET_TYPE);
list.add(IMAGEBASE_OFFSET_TYPE);

return list;
}
Expand Down Expand Up @@ -230,6 +239,15 @@ else if (copyType == ADDRESS_TEXT_WITH_OFFSET_TYPE) {
else if (copyType == BYTE_SOURCE_OFFSET_TYPE) {
return copyByteSourceOffset(monitor);
}
else if (copyType == BLOCK_OFFSET_TYPE) {
return copyBlockSourceOffset(monitor);
}
else if (copyType == FUNCTION_OFFSET_TYPE) {
return copyFunctionSourceOffset(monitor);
}
else if (copyType == IMAGEBASE_OFFSET_TYPE) {
return copyImagebaseSourceOffset(monitor);
}
else if (copyType == CODE_TEXT_TYPE) {
return copyCode(monitor);
}
Expand Down Expand Up @@ -409,6 +427,48 @@ private Transferable copyByteSourceOffset(TaskMonitor monitor) {
return createStringTransferable(String.join("\n", strings));
}

private Transferable copyBlockSourceOffset(TaskMonitor monitor) {
AddressSetView addrs = getSelectedAddresses();
Memory mem = currentProgram.getMemory();
List<String> strings = new ArrayList<>();
AddressIterator addresses = addrs.getAddresses(true);
while (addresses.hasNext() && !monitor.isCancelled()) {
Address addr = addresses.next();
MemoryBlock block = mem.getBlock(addr);
strings.add(
block != null ? "%x".formatted(addr.subtract(block.getStart())) : "<NO_OFFSET>");
}
return createStringTransferable(String.join("\n", strings));
}

private Transferable copyFunctionSourceOffset(TaskMonitor monitor) {
AddressSetView addrs = getSelectedAddresses();
Listing listing = currentProgram.getListing();
List<String> strings = new ArrayList<>();
AddressIterator addresses = addrs.getAddresses(true);
while (addresses.hasNext() && !monitor.isCancelled()) {
Address addr = addresses.next();
Function function = listing.getFunctionContaining(addr);
strings.add(function != null ? "%x".formatted(addr.subtract(function.getEntryPoint()))
: "<NO_OFFSET>");
}
return createStringTransferable(String.join("\n", strings));
}

private Transferable copyImagebaseSourceOffset(TaskMonitor monitor) {
AddressSetView addrs = getSelectedAddresses();
List<String> strings = new ArrayList<>();
AddressIterator addresses = addrs.getAddresses(true);
while (addresses.hasNext() && !monitor.isCancelled()) {
Address addr = addresses.next();
Address imagebase = currentProgram.getImageBase();
strings.add(
addr.hasSameAddressSpace(imagebase) ? "%x".formatted(addr.subtract(imagebase))
: "<NO_OFFSET>");
}
return createStringTransferable(String.join("\n", strings));
}

protected Transferable copyCode(TaskMonitor monitor) {

AddressSetView addressSet = getSelectedAddresses();
Expand Down Expand Up @@ -480,8 +540,8 @@ private boolean pasteLabelsComments(Transferable pasteData, boolean pasteLabels,
List<?> list =
(List<?>) pasteData.getTransferData(CodeUnitInfoTransferable.localDataTypeFlavor);
List<CodeUnitInfo> infos = CollectionUtils.asList(list, CodeUnitInfo.class);
Command cmd = new CodeUnitInfoPasteCmd(currentLocation.getAddress(), infos, pasteLabels,
pasteComments);
CodeUnitInfoPasteCmd cmd = new CodeUnitInfoPasteCmd(currentLocation.getAddress(), infos,
pasteLabels, pasteComments);
return tool.execute(cmd, currentProgram);
}
catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/* ###
* 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.math.BigInteger;

import docking.widgets.fieldpanel.field.*;
import docking.widgets.fieldpanel.support.FieldLocation;
import generic.theme.GColor;
import ghidra.app.util.ListingHighlightProvider;
import ghidra.app.util.viewer.format.FieldFormatModel;
import ghidra.app.util.viewer.proxy.ProxyObj;
import ghidra.framework.options.Options;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Data;
import ghidra.program.util.*;
import ghidra.util.HelpLocation;

/**
* Generates Offset fields
*/
public abstract class AbstractOffsetFieldFactory extends FieldFactory {

public static final GColor COLOR = new GColor("color.fg.listing.field.offset");

private static final String SHOW_NAME = "Show Name";
private static final String USE_HEX = "Use Hex";
private static final boolean DEFAULT_SHOW_NAME = false;
private static final boolean DEFAULT_USE_HEX = true;

protected boolean showName;
protected boolean useHex;

protected String fieldName;
protected String groupTitle;


/**
* Creates a new {@link AbstractOffsetFieldFactory}
*
* @param offsetDescription A description of the offset
*/
public AbstractOffsetFieldFactory(String offsetDescription) {
super(offsetDescription + " Offset");
}

/**
* Creates a new {@link AbstractOffsetFieldFactory}
*
* @param offsetDescription A description of the field offset
* @param nameDescription A description of the name that can get prepended to the field offset
* @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.
*/
protected AbstractOffsetFieldFactory(String offsetDescription, String nameDescription,
FieldFormatModel model, ListingHighlightProvider hlProvider, Options displayOptions,
Options fieldOptions) {
super(offsetDescription + " Offset", model, hlProvider, displayOptions, fieldOptions);
fieldName = offsetDescription + " Offset";
groupTitle = offsetDescription + " Offset Field";
initOptions(fieldOptions, offsetDescription, nameDescription);
}

/**
* Gets the offset value
*
* @param codeUnit The {@link CodeUnit}
* @return The offset value
*/
public abstract String getOffsetValue(CodeUnit codeUnit);

/**
* Gets the {@link OffsetFieldType offset type}
*
* @return the {@link OffsetFieldType offset type}
*/
public abstract OffsetFieldType getOffsetFieldType();

private void initOptions(Options fieldOptions, String offsetDescription,
String nameDescription) {
HelpLocation helpLoc =
new HelpLocation("CodeBrowserPlugin", offsetDescription + "_Offset_Field");
fieldOptions.getOptions(groupTitle).setOptionsHelpLocation(helpLoc);

fieldOptions.registerOption(getFullOptionName(SHOW_NAME), DEFAULT_SHOW_NAME, helpLoc,
"Prepends the %s name to the %s offset in the offset field."
.formatted(nameDescription.toLowerCase(), offsetDescription));
fieldOptions.registerOption(getFullOptionName(USE_HEX), DEFAULT_USE_HEX, helpLoc,
"Toggles displaying offsets in hexadecimal/decimal in the offset field.");

showName = fieldOptions.getBoolean(getFullOptionName(SHOW_NAME), DEFAULT_SHOW_NAME);
useHex = fieldOptions.getBoolean(getFullOptionName(USE_HEX), DEFAULT_USE_HEX);
}

@Override
public ListingField getField(ProxyObj<?> proxy, int varWidth) {
Object obj = proxy.getObject();
if (!enabled || !(obj instanceof CodeUnit)) {
return null;
}
FieldElement fieldElement = new TextFieldElement(
new AttributedString(getOffsetValue((CodeUnit) obj), COLOR, getMetrics()), 0, 0);
ListingTextField listingTextField = ListingTextField.createSingleLineTextField(this, proxy,
fieldElement, startX + varWidth, width, hlProvider);
listingTextField.setPrimary(true);
return listingTextField;
}

@Override
public void fieldOptionsChanged(Options options, String optionsName, Object oldValue,
Object newValue) {
if (optionsName.equals(getFullOptionName(SHOW_NAME))) {
showName = ((Boolean) newValue).booleanValue();
model.update();
}
else if (optionsName.equals(getFullOptionName(USE_HEX))) {
useHex = ((Boolean) newValue).booleanValue();
model.update();
}
}

@Override
public FieldLocation getFieldLocation(ListingField lf, BigInteger index, int fieldNum,
ProgramLocation loc) {
if (loc instanceof OffsetFieldLocation offsetLoc &&
offsetLoc.getType().equals(getOffsetFieldType())) {
Object obj = lf.getProxy().getObject();

if (obj instanceof CodeUnit && hasSamePath(lf, offsetLoc)) {
return new FieldLocation(index, fieldNum, 0, offsetLoc.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 OffsetFieldLocation(cu.getProgram(), addr, cpath, col, getOffsetFieldType());
}

@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);
}

/**
* Gets the full option name, which includes the group and options delimiter
*
* @param shortName The short option name (no group or options delimiter)
* @return The full option name, which includes the group and options delimiter
*/
private String getFullOptionName(String shortName) {
return groupTitle + Options.DELIMITER + shortName;
}
}
Loading

0 comments on commit fb6f853

Please sign in to comment.