Added feature to show file offsets in address hover in listing

This commit is contained in:
ghidravore 2019-07-18 15:50:59 -04:00
parent 3eafdaae37
commit 57e2171dd4
24 changed files with 357 additions and 75 deletions

View file

@ -465,7 +465,8 @@
relationship between the hovered address and the base of memory and the containing memory
block. For addresses in functions, the function offset is also shown; for addresses within
a complex data (structure, array, etc.), the offset from the base of that data is
shown.</P>
shown. Also, if the byte value for the address can be traced back to the original imported
file, then the filename and offset for that location is displayed</P>
</BLOCKQUOTE>
</BLOCKQUOTE><!-- Function Name Hover Section-->

View file

@ -15,8 +15,7 @@
*/
package ghidra.app.plugin.core.codebrowser.hover;
import static ghidra.util.HTMLUtilities.bold;
import static ghidra.util.HTMLUtilities.italic;
import static ghidra.util.HTMLUtilities.*;
import javax.swing.JComponent;
@ -26,6 +25,7 @@ import ghidra.GhidraOptions;
import ghidra.app.plugin.core.hover.AbstractConfigurableHover;
import ghidra.framework.options.Options;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.model.address.*;
import ghidra.program.model.data.Structure;
import ghidra.program.model.listing.*;
@ -33,6 +33,7 @@ import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.util.AddressFieldLocation;
import ghidra.program.util.ProgramLocation;
import ghidra.util.HTMLUtilities;
import ghidra.util.StringUtilities;
/**
* A hover service to show tool tip text for hovering over a program address in the listing.
@ -44,6 +45,7 @@ import ghidra.util.HTMLUtilities;
public class ProgramAddressRelationshipListingHover extends AbstractConfigurableHover
implements ListingHoverService {
private static final int MAX_FILENAME_SIZE = 40;
private static final String NAME = "Address Display";
private static final String DESCRIPTION =
"Shows the relationship between the hovered address and the base of memory " +
@ -98,6 +100,7 @@ public class ProgramAddressRelationshipListingHover extends AbstractConfigurable
addFunctionInfo(program, loc, sb);
addDataInfo(program, loc, sb);
addByteSourceInfo(program, loc, sb);
return createTooltipComponent(sb.toString());
}
@ -139,6 +142,21 @@ public class ProgramAddressRelationshipListingHover extends AbstractConfigurable
appendTableRow(sb, dataDescr, name, dataOffset);
}
private void addByteSourceInfo(Program program, Address loc, StringBuilder sb) {
AddressSourceInfo addressSourceInfo = program.getMemory().getAddressSourceInfo(loc);
if (addressSourceInfo == null) {
return;
}
if (addressSourceInfo.getFileName() == null) {
return;
}
String filename = StringUtilities.trim(addressSourceInfo.getFileName(), MAX_FILENAME_SIZE);
long fileOffset = addressSourceInfo.getFileOffset();
String dataDescr = "Byte Source Offset";
appendTableRow(sb, dataDescr, "File: " + filename, fileOffset);
}
private void addFunctionInfo(Program program, Address loc, StringBuilder sb) {
Function function = program.getFunctionManager().getFunctionContaining(loc);
if (function != null) {

View file

@ -33,7 +33,7 @@ import docking.widgets.table.AbstractSortedTableModel;
import ghidra.framework.model.DomainFile;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.database.mem.MemoryBlockSourceInfo;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
@ -504,7 +504,7 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
case SOURCE:
if ((block.getType() == MemoryBlockType.BIT_MAPPED) ||
(block.getType() == MemoryBlockType.BYTE_MAPPED)) {
SourceInfo info = block.getSourceInfos().get(0);
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
return info.getMappedRange().get().getMinAddress().toString();
}
return block.getSourceName();
@ -522,8 +522,8 @@ class MemoryMapModel extends AbstractSortedTableModel<MemoryBlock> {
return null;
}
private String getByteSourceDescription(List<SourceInfo> sourceInfos) {
List<SourceInfo> limited = sourceInfos.size() < 5 ? sourceInfos : sourceInfos.subList(0, 4);
private String getByteSourceDescription(List<MemoryBlockSourceInfo> sourceInfos) {
List<MemoryBlockSourceInfo> limited = sourceInfos.size() < 5 ? sourceInfos : sourceInfos.subList(0, 4);
//@formatter:off
String description = limited

View file

@ -23,7 +23,7 @@ import org.xml.sax.SAXParseException;
import ghidra.app.util.MemoryBlockUtil;
import ghidra.app.util.importer.MessageLog;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.database.mem.MemoryBlockSourceInfo;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
@ -295,7 +295,7 @@ class MemoryMapXmlMgr {
if (block.getType() == MemoryBlockType.BIT_MAPPED) {
// bit mapped blocks can only have one sub-block
SourceInfo info = block.getSourceInfos().get(0);
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
attrs.addAttribute("SOURCE_ADDRESS",
info.getMappedRange().get().getMinAddress().toString());
writer.startElement("BIT_MAPPED", attrs);
@ -303,7 +303,7 @@ class MemoryMapXmlMgr {
}
else if (block.getType() == MemoryBlockType.BYTE_MAPPED) {
// byte mapped blocks can only have one sub-block
SourceInfo info = block.getSourceInfos().get(0);
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
attrs.addAttribute("SOURCE_ADDRESS",
info.getMappedRange().get().getMinAddress().toString());
writer.startElement("BYTE_MAPPED", attrs);

View file

@ -35,7 +35,7 @@ import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
import ghidra.app.plugin.core.navigation.NavigationHistoryPlugin;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.database.mem.MemoryBlockSourceInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
@ -560,7 +560,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
for (MemoryBlock memBlock : blocks) {
if (memBlock.getSourceName().equals(sources[i]) &&
memBlock.getType() == MemoryBlockType.BIT_MAPPED) {
SourceInfo info = memBlock.getSourceInfos().get(0);
MemoryBlockSourceInfo info = memBlock.getSourceInfos().get(0);
Address addr = info.getMappedRange().get().getMinAddress();
assertEquals(addr.toString(), model.getValueAt(i, MemoryMapModel.SOURCE));
@ -605,7 +605,7 @@ public class MemoryMapProvider1Test extends AbstractGhidraHeadedIntegrationTest
for (MemoryBlock memBlock : blocks) {
if (memBlock.getSourceName().equals(sources[idx]) &&
memBlock.getType() == MemoryBlockType.BIT_MAPPED) {
SourceInfo info = memBlock.getSourceInfos().get(0);
MemoryBlockSourceInfo info = memBlock.getSourceInfos().get(0);
Address addr = info.getMappedRange().get().getMinAddress();
assertEquals(addr.toString(), model.getValueAt(i, MemoryMapModel.SOURCE));
doAssert = false;

View file

@ -879,7 +879,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(MemoryBlockType.BIT_MAPPED, bitBlock.getType());
SourceInfo info = bitBlock.getSourceInfos().get(0);
MemoryBlockSourceInfo info = bitBlock.getSourceInfos().get(0);
assertEquals(new AddressRangeImpl(addr(0xf00), addr(0x10ff)), info.getMappedRange().get());
AddressSet expectedInitializedSet = new AddressSet();
expectedInitializedSet.add(addr(0), addr(0xfff));
@ -895,7 +895,7 @@ public class MemoryManagerTest extends AbstractGhidraHeadedIntegrationTest {
assertEquals(MemoryBlockType.BYTE_MAPPED, byteBlock.getType());
SourceInfo info = byteBlock.getSourceInfos().get(0);
MemoryBlockSourceInfo info = byteBlock.getSourceInfos().get(0);
assertEquals(new AddressRangeImpl(addr(0xf00), addr(0x10ff)), info.getMappedRange().get());
AddressSet expectedInitializedSet = new AddressSet();
expectedInitializedSet.add(addr(0), addr(0xfff));

View file

@ -22,7 +22,7 @@ import org.junit.*;
import generic.test.AbstractGenericTest;
import ghidra.framework.cmd.Command;
import ghidra.program.database.ProgramBuilder;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.database.mem.MemoryBlockSourceInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*;
import ghidra.program.model.mem.MemoryBlock;
@ -110,7 +110,7 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNotNull(block);
SourceInfo info = block.getSourceInfos().get(0);
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
assertEquals(getX08Addr(0), info.getMappedRange().get().getMinAddress());
assertEquals(MemoryBlockType.BIT_MAPPED, block.getType());
}
@ -124,7 +124,7 @@ public class AddMemoryBlockCmdTest extends AbstractGenericTest {
MemoryBlock block = x08.getMemory().getBlock(addr);
assertNotNull(block);
SourceInfo info = block.getSourceInfos().get(0);
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
assertEquals(getX08Addr(0), info.getMappedRange().get().getMinAddress());
assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());

View file

@ -20,6 +20,7 @@ import java.io.InputStream;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
@ -56,6 +57,11 @@ class MyTestMemory extends AddressSet implements Memory {
throw new UnsupportedOperationException();
}
@Override
public AddressSourceInfo getAddressSourceInfo(Address address) {
throw new UnsupportedOperationException();
}
@Override
public boolean isBigEndian() {
return false;

View file

@ -18,7 +18,7 @@ package ghidra.app.plugin.core.checksums;
import java.io.InputStream;
import java.util.List;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.database.mem.MemoryBlockSourceInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.*;
@ -195,7 +195,7 @@ class MyTestMemoryBlock implements MemoryBlock {
}
@Override
public List<SourceInfo> getSourceInfos() {
public List<MemoryBlockSourceInfo> getSourceInfos() {
throw new UnsupportedOperationException();
}
}

View file

@ -20,6 +20,7 @@ import java.io.InputStream;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
@ -348,4 +349,9 @@ public class MemoryTestDummy extends AddressSet implements Memory {
MemoryConflictException, AddressOverflowException {
throw new UnsupportedOperationException();
}
@Override
public AddressSourceInfo getAddressSourceInfo(Address address) {
throw new UnsupportedOperationException();
}
}

View file

@ -20,7 +20,7 @@ import java.util.Arrays;
import ghidra.pcode.error.LowlevelError;
import ghidra.pcode.memstate.MemoryFaultHandler;
import ghidra.pcode.memstate.MemoryPage;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.database.mem.MemoryBlockSourceInfo;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
@ -48,7 +48,7 @@ public class ProgramLoadImage {
}
private AddressSetView addMappedInitializedMemory(MemoryBlock mappedBlock) {
SourceInfo sourceInfo = mappedBlock.getSourceInfos().get(0); // mapped block has exactly 1 mapped source
MemoryBlockSourceInfo sourceInfo = mappedBlock.getSourceInfos().get(0); // mapped block has exactly 1 mapped source
if (!sourceInfo.getMappedRange().isPresent()) {
throw new AssertException("Mapped block did not have mapped range!");
}

View file

@ -20,7 +20,7 @@ import java.util.Arrays;
import ghidra.pcode.error.LowlevelError;
import ghidra.pcode.memstate.MemoryFaultHandler;
import ghidra.pcode.memstate.MemoryPage;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.database.mem.MemoryBlockSourceInfo;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
@ -52,7 +52,7 @@ public class ProgramMappedMemory {
private AddressSetView addMappedInitializedMemory(MemoryBlock mappedBlock) {
AddressSet modifiedSet = new AddressSet(initializedAddressSet);
SourceInfo sourceInfo = mappedBlock.getSourceInfos().get(0); // mapped block has exactly 1 mapped source
MemoryBlockSourceInfo sourceInfo = mappedBlock.getSourceInfos().get(0); // mapped block has exactly 1 mapped source
if (!sourceInfo.getMappedRange().isPresent()) {
throw new AssertException("Mapped block did not have mapped range!");
}

View file

@ -0,0 +1,138 @@
/* ###
* 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.database.mem;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.mem.Memory;
import ghidra.program.model.mem.MemoryBlock;
/**
* Provides information about the source of a byte value at an address including the file it
* came from, the offset into that file, and the original value of that byte.
*/
public class AddressSourceInfo {
private Address address;
private MemoryBlock block;
private FileBytes fileBytes;
private MemoryBlockSourceInfo sourceInfo;
private AddressSourceInfo mappedInfo;
private Memory memory;
public AddressSourceInfo(Memory memory, Address address, MemoryBlock block) {
this.memory = memory;
this.address = address;
this.block = block;
sourceInfo = getContainingInfo();
fileBytes = sourceInfo.getFileBytes().orElse(null);
}
/**
* Returns the address for which this object provides byte source information.
* @return the address for which this object provides byte source information.
*/
public Address getAddress() {
return address;
}
/**
* Returns the offset into the originally imported file that provided the byte value for the
* associated address or -1 if there is no source information for this location.
* @return the offset into the originally imported file that provided the byte value for the
* associated address.
*/
public long getFileOffset() {
if (mappedInfo != null) {
return mappedInfo.getFileOffset();
}
if (fileBytes != null) {
return sourceInfo.getFileBytesOffset(address) + fileBytes.getFileOffset();
}
return -1;
}
/**
* Returns the filename of the originally imported file that provided the byte value for the
* associated address or null if there is no source information for this location.
* @return the filename of the originally imported file that provided the byte value for the
* associated address or null if there is no source information for this location.
*/
public String getFileName() {
if (mappedInfo != null) {
return mappedInfo.getFileName();
}
if (fileBytes != null) {
return fileBytes.getFilename();
}
return null;
}
/**
* Returns the original byte value from the imported file that provided the byte value for the
* associated address or 0 if there is no source information for this location.
* @return the original byte value from the imported file that provided the byte value for the
* associated address or 0 if there is no source information for this location.
* @throws IOException if an io error occurs reading the program database.
*/
public byte getOriginalValue() throws IOException {
if (mappedInfo != null) {
return mappedInfo.getOriginalValue();
}
if (fileBytes != null) {
return fileBytes.getOriginalByte(getFileOffset());
}
return 0;
}
/**
* Returns the {@link MemoryBlockSourceInfo} for the region surround this info's location.
* @return the {@link MemoryBlockSourceInfo} for the region surround this info's location.
*/
public MemoryBlockSourceInfo getMemoryBlockSourceInfo() {
return sourceInfo;
}
private MemoryBlockSourceInfo getContainingInfo() {
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
for (MemoryBlockSourceInfo info : sourceInfos) {
if (info.contains(address)) {
Optional<AddressRange> mappedRangeOptional = info.getMappedRange();
if (mappedRangeOptional.isPresent()) {
mappedInfo = getMappedSourceInfo(info, mappedRangeOptional.get());
}
return info;
}
}
return null;
}
private AddressSourceInfo getMappedSourceInfo(MemoryBlockSourceInfo info, AddressRange addressRange) {
Address mappedAddress =
addressRange.getMinAddress().add(address.subtract(info.getMinAddress()));
MemoryBlock mappedBlock = memory.getBlock(mappedAddress);
if (mappedBlock == null) {
return null;
}
return new AddressSourceInfo(memory, mappedAddress, mappedBlock);
}
}

View file

@ -113,11 +113,7 @@ class FileBytesSubMemoryBlock extends SubMemoryBlock {
protected String getDescription() {
String fileName = fileBytes.getFilename();
if (fileBytes.getFileOffset()> 0) {
fileName = "[" + fileName + " + 0x" + Long.toHexString(fileBytes.getFileOffset()) + "]";
}
String hexString = Long.toHexString(fileBytesOffset);
String hexString = Long.toHexString(fileBytesOffset + fileBytes.getFileOffset());
return "File: " + fileName + ": 0x" + hexString;
}

View file

@ -665,8 +665,8 @@ public class MemoryBlockDB implements MemoryBlock {
}
@Override
public List<SourceInfo> getSourceInfos() {
List<SourceInfo> infos = new ArrayList<>(subBlocks.size());
public List<MemoryBlockSourceInfo> getSourceInfos() {
List<MemoryBlockSourceInfo> infos = new ArrayList<>(subBlocks.size());
for (SubMemoryBlock subBlock : subBlocks) {
infos.add(subBlock.getSourceInfo(this));
}

View file

@ -24,12 +24,12 @@ import ghidra.program.model.mem.MemoryBlock;
/**
* Class for describing the source of bytes for a memory block.
*/
public class SourceInfo {
public class MemoryBlockSourceInfo {
final MemoryBlock block;
final SubMemoryBlock subBlock;
private final MemoryBlock block;
private final SubMemoryBlock subBlock;
SourceInfo(MemoryBlock block, SubMemoryBlock subBlock) {
MemoryBlockSourceInfo(MemoryBlock block, SubMemoryBlock subBlock) {
this.block = block;
this.subBlock = subBlock;
}
@ -97,6 +97,26 @@ public class SourceInfo {
return -1;
}
/**
* Returns the offset into the {@link FileBytes} object for the given address or
* -1 if this SourceInfo does not have an associated {@link FileBytes} or the address doesn't
* belong to this SourceInfo.
*
* @param address the address for which to get an offset into the {@link FileBytes} object.
* @return the offset into the {@link FileBytes} object for the given address.
*/
public long getFileBytesOffset(Address address) {
if (!contains(address)) {
return -1;
}
if (subBlock instanceof FileBytesSubMemoryBlock) {
long blockOffset = address.subtract(getMinAddress());
long subBlockOffset = blockOffset - subBlock.startingOffset;
return ((FileBytesSubMemoryBlock) subBlock).getFileBytesOffset() + subBlockOffset;
}
return -1;
}
/**
* Returns an {@link Optional} {@link AddressRange} for the mapped addresses if this is mapped
* memory block (bit mapped or byte mapped). Otherwise, the Optional is empty.
@ -114,4 +134,21 @@ public class SourceInfo {
}
return Optional.empty();
}
/**
* Returns the containing Memory Block
* @return the containing Memory Block
*/
public MemoryBlock getMemoryBlock() {
return block;
}
/**
* Returns true if this SourceInfo object applies to the given address;
* @param address the address to test if this is its SourceInfo
* @return true if this SourceInfo object applies to the given address;
*/
public boolean contains(Address address) {
return address.compareTo(getMinAddress()) >= 0 && address.compareTo(getMaxAddress()) <= 0;
}
}

View file

@ -662,7 +662,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
try {
Address overlayAddr = null;
if (block.isMapped()) {
SourceInfo info = block.getSourceInfos().get(0);
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
overlayAddr = info.getMappedRange().get().getMinAddress();
}
MemoryBlockDB newBlock =
@ -1838,9 +1838,9 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
*/
private AddressSet getMappedIntersection(MemoryBlock block, AddressSet set) {
AddressSet mappedIntersection = new AddressSet();
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
// mapped blocks can only ever have one sourceInfo
SourceInfo info = sourceInfos.get(0);
MemoryBlockSourceInfo info = sourceInfos.get(0);
AddressRange range = info.getMappedRange().get();
AddressSet resolvedIntersection = set.intersect(new AddressSet(range));
for (AddressRange resolvedRange : resolvedIntersection) {
@ -1855,7 +1855,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
private AddressRange getMappedRange(MemoryBlock mappedBlock, AddressRange resolvedRange) {
Address start, end;
SourceInfo info = mappedBlock.getSourceInfos().get(0);
MemoryBlockSourceInfo info = mappedBlock.getSourceInfos().get(0);
long startOffset =
resolvedRange.getMinAddress().subtract(info.getMappedRange().get().getMinAddress());
boolean isBitMapped = mappedBlock.getType() == MemoryBlockType.BIT_MAPPED;
@ -1989,6 +1989,14 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
return addrSet.findFirstAddressInCommon(set);
}
@Override
public AddressSourceInfo getAddressSourceInfo(Address address) {
MemoryBlock block = getBlock(address);
if (block != null) {
return new AddressSourceInfo(this, address, block);
}
return null;
}
private void checkBlockSize(long newBlockLength, boolean initialized) {
if (newBlockLength > MAX_BLOCK_SIZE) {
throw new IllegalStateException(
@ -2109,4 +2117,5 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
}
}
}

View file

@ -116,7 +116,7 @@ abstract class MemoryMapDBAdapter {
Address mappedAddress = null;
if (block.isMapped()) {
SourceInfo info = block.getSourceInfos().get(0);
MemoryBlockSourceInfo info = block.getSourceInfos().get(0);
mappedAddress = info.getMappedRange().get().getMinAddress();
}
newBlock =

View file

@ -189,12 +189,12 @@ abstract class SubMemoryBlock {
protected abstract MemoryBlockType getType();
/**
* Returns the {@link SourceInfo} object for this SubMemoryBlock
* Returns the {@link MemoryBlockSourceInfo} object for this SubMemoryBlock
* @param block the {@link MemoryBlock} that this block belongs to.
* @return the {@link SourceInfo} object for this SubMemoryBlock
* @return the {@link MemoryBlockSourceInfo} object for this SubMemoryBlock
*/
protected final SourceInfo getSourceInfo(MemoryBlock block) {
return new SourceInfo(block, this);
protected final MemoryBlockSourceInfo getSourceInfo(MemoryBlock block) {
return new MemoryBlockSourceInfo(block, this);
}
/**

View file

@ -20,6 +20,7 @@ import java.io.InputStream;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
@ -748,4 +749,11 @@ public interface Memory extends AddressSetView {
* @throws IOException if there was an error updating the database.
*/
public boolean deleteFileBytes(FileBytes fileBytes) throws IOException;
/**
* Returns information ({@link AddressSourceInfo}) about the byte source at the given address.
* @param address the address to query.
* @return information ({@link AddressSourceInfo}) about the byte source at the given address.
*/
public AddressSourceInfo getAddressSourceInfo(Address address);
}

View file

@ -20,7 +20,7 @@ import java.io.Serializable;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.database.mem.MemoryBlockSourceInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException;
@ -264,13 +264,13 @@ public interface MemoryBlock extends Serializable, Comparable<MemoryBlock> {
public boolean isLoaded();
/**
* Returns a list of {@link SourceInfo} objects for this block. A block may consist of
* Returns a list of {@link MemoryBlockSourceInfo} objects for this block. A block may consist of
* multiple sequences of bytes from different sources. Each such source of bytes is described
* by its respective SourceInfo object. Blocks may have multiple sources after two or more
* memory blocks have been joined together and the underlying byte sources can't be joined.
* @return a list of SourceInfo objects, one for each different source of bytes in this block.
*/
public List<SourceInfo> getSourceInfos();
public List<MemoryBlockSourceInfo> getSourceInfos();
/**
* Determine if the specified address is contained within the reserved EXTERNAL block.

View file

@ -19,7 +19,7 @@ import java.io.InputStream;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.database.mem.MemoryBlockSourceInfo;
import ghidra.program.model.address.Address;
import ghidra.util.exception.DuplicateNameException;
@ -191,7 +191,7 @@ public class MemoryBlockStub implements MemoryBlock {
}
@Override
public List<SourceInfo> getSourceInfos() {
public List<MemoryBlockSourceInfo> getSourceInfos() {
throw new UnsupportedOperationException();
}

View file

@ -21,6 +21,7 @@ import java.util.Iterator;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.AddressSourceInfo;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
@ -489,4 +490,9 @@ public class MemoryStub implements Memory {
MemoryConflictException, AddressOverflowException {
throw new UnsupportedOperationException();
}
@Override
public AddressSourceInfo getAddressSourceInfo(Address address) {
throw new UnsupportedOperationException();
}
}

View file

@ -96,10 +96,10 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertNull(block.getSourceName());
assertEquals(MemoryBlock.READ, block.getPermissions());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
MemoryBlockSourceInfo info = sourceInfos.get(0);
assertEquals(10, info.getLength());
assertEquals(addr(0), info.getMinAddress());
assertEquals(addr(9), info.getMaxAddress());
@ -117,9 +117,9 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(addr(0), block.getStart());
assertEquals(addr(9), block.getEnd());
assertEquals(MemoryBlockType.DEFAULT, block.getType());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
MemoryBlockSourceInfo info = sourceInfos.get(0);
assertEquals(10, info.getLength());
assertEquals(addr(0), info.getMinAddress());
assertEquals(addr(9), info.getMaxAddress());
@ -143,9 +143,9 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(9, block.getEnd().getOffset());
assertTrue(block.getStart().getAddressSpace().isOverlaySpace());
assertEquals(MemoryBlockType.OVERLAY, block.getType());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
MemoryBlockSourceInfo info = sourceInfos.get(0);
assertEquals(10, info.getLength());
try {
block.getByte(block.getStart());
@ -168,9 +168,9 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(9, block.getEnd().getOffset());
assertTrue(block.getStart().getAddressSpace().isOverlaySpace());
assertEquals(MemoryBlockType.OVERLAY, block.getType());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
MemoryBlockSourceInfo info = sourceInfos.get(0);
assertEquals(10, info.getLength());
for (int i = 0; i < 10; i++) {
assertEquals(1, block.getByte(block.getStart().add(i)));
@ -194,10 +194,10 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertNull(block.getSourceName());
assertEquals(MemoryBlock.READ, block.getPermissions());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
MemoryBlockSourceInfo info = sourceInfos.get(0);
assertEquals(20, info.getLength());
assertEquals(new AddressRangeImpl(addr(40), addr(59)), info.getMappedRange().get());
@ -230,10 +230,10 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertNull(block.getSourceName());
assertEquals(MemoryBlock.READ, block.getPermissions());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
MemoryBlockSourceInfo info = sourceInfos.get(0);
assertEquals(16, info.getLength());
assertEquals(new AddressRangeImpl(addr(49), addr(50)), info.getMappedRange().get());
@ -266,10 +266,10 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertNull(block.getSourceName());
assertEquals(MemoryBlock.READ, block.getPermissions());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
MemoryBlockSourceInfo info = sourceInfos.get(0);
assertEquals(50, info.getLength());
assertEquals(addr(100), info.getMinAddress());
assertEquals(addr(149), info.getMaxAddress());
@ -324,9 +324,9 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(1, mem.getBlocks().length);
assertEquals(20, block.getSize());
assertEquals(addr(10), block.getStart());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
MemoryBlockSourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
byte[] bytes = new byte[30];
@ -346,9 +346,9 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(1, mem.getBlocks().length);
assertEquals(20, block.getSize());
assertEquals(addr(10), block.getStart());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(2, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
MemoryBlockSourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
assertEquals(10, sourceInfo.getLength());
@ -370,14 +370,14 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(1, mem.getBlocks().length);
assertEquals(20, block.getSize());
assertEquals(addr(10), block.getStart());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(2, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
MemoryBlockSourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
assertEquals(10, sourceInfo.getLength());
SourceInfo sourceInfo2 = sourceInfos.get(1);
MemoryBlockSourceInfo sourceInfo2 = sourceInfos.get(1);
assertEquals(10, sourceInfo2.getLength());
}
@ -392,10 +392,10 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(1, mem.getBlocks().length);
assertEquals(20, block.getSize());
assertEquals(addr(10), block.getStart());
List<SourceInfo> sourceInfos = block.getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(2, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
MemoryBlockSourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes1, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
assertEquals(10, sourceInfo.getLength());
@ -421,9 +421,9 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(addr(10), blocks[0].getStart());
assertEquals(addr(30), blocks[1].getStart());
List<SourceInfo> sourceInfos = blocks[0].getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = blocks[0].getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
MemoryBlockSourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
@ -479,7 +479,7 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(1, blocks.length);
assertEquals(addr(0), blocks[0].getStart());
assertEquals(40, blocks[0].getSize());
List<SourceInfo> sourceInfos = blocks[0].getSourceInfos();
List<MemoryBlockSourceInfo> sourceInfos = blocks[0].getSourceInfos();
assertEquals(1, sourceInfos.size()); // make sure the sub blocks were merged
}
@ -825,6 +825,63 @@ public class MemBlockDBTest extends AbstractGenericTest {
assertEquals(2, range.getSize());
assertEquals(0, range.getOffset());
}
@Test
public void testAddressSourceInfoForFileBytesBlock() throws Exception {
FileBytes fileBytes = createFileBytes();
mem.createInitializedBlock("block", addr(100), fileBytes, 10, 50, false);
AddressSourceInfo info = mem.getAddressSourceInfo(addr(100));
assertEquals(addr(100), info.getAddress());
assertEquals("test", info.getFileName());
assertEquals(10, info.getFileOffset());
assertEquals(10, info.getOriginalValue());
info = mem.getAddressSourceInfo(addr(110));
assertEquals(addr(110), info.getAddress());
assertEquals("test", info.getFileName());
assertEquals(20, info.getFileOffset());
assertEquals(20, info.getOriginalValue());
}
@Test
public void testAddressSourceInfoForBufferBlock() throws Exception {
mem.createInitializedBlock("test", addr(0), 10, (byte) 1, TaskMonitor.DUMMY, false);
AddressSourceInfo info = mem.getAddressSourceInfo(addr(0));
assertEquals(addr(0), info.getAddress());
assertNull(info.getFileName());
assertEquals(-1, info.getFileOffset());
assertEquals(0, info.getOriginalValue());
}
@Test
public void testAddressSourceInfoForUnitialized() throws Exception {
mem.createUninitializedBlock("test", addr(0), 10, false);
AddressSourceInfo info = mem.getAddressSourceInfo(addr(0));
assertEquals(addr(0), info.getAddress());
assertNull(info.getFileName());
assertEquals(-1, info.getFileOffset());
assertEquals(0, info.getOriginalValue());
}
@Test
public void testAddressSourceInfoForMappedBlock() throws Exception {
FileBytes fileBytes = createFileBytes();
mem.createInitializedBlock("block", addr(0), fileBytes, 10, 50, false);
mem.createByteMappedBlock("mapped", addr(1000), addr(0), 20);
AddressSourceInfo info = mem.getAddressSourceInfo(addr(1000));
assertEquals(addr(1000), info.getAddress());
assertEquals("test", info.getFileName());
assertEquals(10, info.getFileOffset());
assertEquals(10, info.getOriginalValue());
}
private MemoryBlock createFileBytesBlock(FileBytes fileBytes, Address addr, int offset,
int length) throws Exception {
return mem.createInitializedBlock("test" + addr.toString(), addr, fileBytes, offset, length,