diff --git a/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowser.htm b/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowser.htm index 90f7e50942..6fa568554e 100644 --- a/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowser.htm +++ b/Ghidra/Features/Base/src/main/help/help/topics/CodeBrowserPlugin/CodeBrowser.htm @@ -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.

+ 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

diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ProgramAddressRelationshipListingHover.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ProgramAddressRelationshipListingHover.java index a7f1a313ed..55c1bb5815 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ProgramAddressRelationshipListingHover.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/codebrowser/hover/ProgramAddressRelationshipListingHover.java @@ -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) { diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java index cc0bd55c68..97dc4ad512 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/memory/MemoryMapModel.java @@ -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 { 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 { return null; } - private String getByteSourceDescription(List sourceInfos) { - List limited = sourceInfos.size() < 5 ? sourceInfos : sourceInfos.subList(0, 4); + private String getByteSourceDescription(List sourceInfos) { + List limited = sourceInfos.size() < 5 ? sourceInfos : sourceInfos.subList(0, 4); //@formatter:off String description = limited diff --git a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java index db8e3c7ba2..376082419a 100644 --- a/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java +++ b/Ghidra/Features/Base/src/main/java/ghidra/app/util/xml/MemoryMapXmlMgr.java @@ -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); diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java index fcbd105873..e27c884644 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/app/plugin/core/memory/MemoryMapProvider1Test.java @@ -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; diff --git a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java index fba2b05b5f..20433a4ee7 100644 --- a/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java +++ b/Ghidra/Features/Base/src/test.slow/java/ghidra/program/database/mem/MemoryManagerTest.java @@ -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)); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java index 5d0c317194..bab3928e42 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/cmd/memory/AddMemoryBlockCmdTest.java @@ -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()); diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java index 93f955310f..047be6da11 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemory.java @@ -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; diff --git a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java index b771d75855..56cb91887a 100644 --- a/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java +++ b/Ghidra/Features/Base/src/test/java/ghidra/app/plugin/core/checksums/MyTestMemoryBlock.java @@ -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 getSourceInfos() { + public List getSourceInfos() { throw new UnsupportedOperationException(); } } diff --git a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java index f95a2077c8..dc4f8b3d22 100644 --- a/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java +++ b/Ghidra/Features/VersionTracking/src/test/java/ghidra/feature/vt/db/MemoryTestDummy.java @@ -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(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramLoadImage.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramLoadImage.java index 5fd89101d7..12c7d09314 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramLoadImage.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramLoadImage.java @@ -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!"); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramMappedMemory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramMappedMemory.java index 2bf5f61cad..60268b20fc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramMappedMemory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/app/emulator/memory/ProgramMappedMemory.java @@ -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!"); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/AddressSourceInfo.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/AddressSourceInfo.java new file mode 100644 index 0000000000..769084cfe5 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/AddressSourceInfo.java @@ -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 sourceInfos = block.getSourceInfos(); + for (MemoryBlockSourceInfo info : sourceInfos) { + if (info.contains(address)) { + Optional 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); + } +} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java index b002ab232d..460a324be1 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/FileBytesSubMemoryBlock.java @@ -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; } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java index f039276723..bd0a8d82e4 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockDB.java @@ -665,8 +665,8 @@ public class MemoryBlockDB implements MemoryBlock { } @Override - public List getSourceInfos() { - List infos = new ArrayList<>(subBlocks.size()); + public List getSourceInfos() { + List infos = new ArrayList<>(subBlocks.size()); for (SubMemoryBlock subBlock : subBlocks) { infos.add(subBlock.getSourceInfo(this)); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SourceInfo.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockSourceInfo.java similarity index 71% rename from Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SourceInfo.java rename to Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockSourceInfo.java index 69b65f5003..6c63536d56 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SourceInfo.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryBlockSourceInfo.java @@ -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; + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java index 0e7d49dcb2..85e4e51a56 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDB.java @@ -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 sourceInfos = block.getSourceInfos(); + List 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 { } } } + } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java index 33aeb3d23b..f0cbc98124 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/MemoryMapDBAdapter.java @@ -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 = diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java index fba75aff6b..79a2a2c8c9 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/SubMemoryBlock.java @@ -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); } /** diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java index 4e90438108..35b24ef806 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/Memory.java @@ -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); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java index 01712dcc92..49a867dea5 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlock.java @@ -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 { 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 getSourceInfos(); + public List getSourceInfos(); /** * Determine if the specified address is contained within the reserved EXTERNAL block. diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java index 44a25e4f45..915e96a9ae 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryBlockStub.java @@ -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 getSourceInfos() { + public List getSourceInfos() { throw new UnsupportedOperationException(); } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java index 3b600874d5..29d4ff9a1a 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/mem/MemoryStub.java @@ -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(); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java index cad62f4e7f..6ba898fd74 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/database/mem/MemBlockDBTest.java @@ -96,10 +96,10 @@ public class MemBlockDBTest extends AbstractGenericTest { assertNull(block.getSourceName()); assertEquals(MemoryBlock.READ, block.getPermissions()); - List sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = block.getSourceInfos(); + List 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 sourceInfos = blocks[0].getSourceInfos(); + List 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 sourceInfos = blocks[0].getSourceInfos(); + List 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,