GT-2845: Storing original program bytes in the program database.

This commit is contained in:
ghidravore 2019-05-06 12:44:51 -04:00 committed by Ryan Kurtz
parent d95fd43762
commit 0792417979
73 changed files with 7129 additions and 2575 deletions

View file

@ -20,10 +20,12 @@ 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.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
// Derived from ProgramMappedMemory
public class ProgramLoadImage {
@ -37,23 +39,23 @@ public class ProgramLoadImage {
Memory memory = program.getMemory();
initializedAddressSet = memory.getLoadedAndInitializedAddressSet();
for (MemoryBlock block : memory.getBlocks()) {
if (!block.isInitialized() && (block instanceof MappedMemoryBlock)) {
initializedAddressSet = addMappedInitializedMemory((MappedMemoryBlock) block);
if (!block.isInitialized() && block.isMapped()) {
initializedAddressSet = addMappedInitializedMemory(block);
}
}
this.faultHandler = faultHandler;
// TODO: consider adding program consumer (would require proper dispose)
}
private AddressSetView addMappedInitializedMemory(MappedMemoryBlock mappedBlock) {
long size = mappedBlock.getSize();
if (size <= 0) {
// TODO: can't handle massive mapped blocks
return initializedAddressSet;
private AddressSetView addMappedInitializedMemory(MemoryBlock mappedBlock) {
SourceInfo 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!");
}
AddressRange mappedRange = sourceInfo.getMappedRange().get();
Address mapStart = mappedRange.getMinAddress();
Address mapEnd = mappedRange.getMaxAddress();
AddressSet modifiedSet = new AddressSet(initializedAddressSet);
Address mapStart = mappedBlock.getOverlayedMinAddress();
Address mapEnd = mapStart.add(size - 1);
AddressSet mappedAreas = initializedAddressSet.intersectRange(mapStart, mapEnd);
for (AddressRange range : mappedAreas) {
Address start = mappedBlock.getStart().add(range.getMinAddress().subtract(mapStart));

View file

@ -20,10 +20,12 @@ 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.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.util.Msg;
import ghidra.util.exception.AssertException;
public class ProgramMappedMemory {
@ -39,8 +41,8 @@ public class ProgramMappedMemory {
initializedAddressSet = memory.getLoadedAndInitializedAddressSet();
for (MemoryBlock block : memory.getBlocks()) {
if (!block.isInitialized() && (block instanceof MappedMemoryBlock)) {
initializedAddressSet = addMappedInitializedMemory((MappedMemoryBlock) block);
if (!block.isInitialized() && block.isMapped()) {
initializedAddressSet = addMappedInitializedMemory(block);
}
}
@ -48,15 +50,15 @@ public class ProgramMappedMemory {
this.faultHandler = faultHandler;
}
private AddressSetView addMappedInitializedMemory(MappedMemoryBlock mappedBlock) {
long size = mappedBlock.getSize();
if (size <= 0) {
// TODO: can't handle massive mapped blocks
return initializedAddressSet;
}
private AddressSetView addMappedInitializedMemory(MemoryBlock mappedBlock) {
AddressSet modifiedSet = new AddressSet(initializedAddressSet);
Address mapStart = mappedBlock.getOverlayedMinAddress();
Address mapEnd = mapStart.add(size - 1);
SourceInfo 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!");
}
AddressRange mappedRange = sourceInfo.getMappedRange().get();
Address mapStart = mappedRange.getMinAddress();
Address mapEnd = mappedRange.getMaxAddress();
AddressSet mappedAreas = initializedAddressSet.intersectRange(mapStart, mapEnd);
for (AddressRange range : mappedAreas) {
Address start = mappedBlock.getStart().add(range.getMinAddress().subtract(mapStart));

View file

@ -199,8 +199,8 @@ public class AddressMapDB implements AddressMap {
* @throws IOException thrown if a dabase io error occurs.
* @throws VersionException if the database version does not match the expected version.
*/
public AddressMapDB(DBHandle handle, int openMode, AddressFactory factory,
long baseImageOffset, TaskMonitor monitor) throws IOException, VersionException {
public AddressMapDB(DBHandle handle, int openMode, AddressFactory factory, long baseImageOffset,
TaskMonitor monitor) throws IOException, VersionException {
this.readOnly = (openMode == DBConstants.READ_ONLY);
this.addrFactory = factory;
this.baseImageOffset = baseImageOffset;
@ -240,7 +240,8 @@ public class AddressMapDB implements AddressMap {
max = max < 0 ? MAX_OFFSET : Math.min(max, MAX_OFFSET);
// Avoid use of add which fails for overlay addresses which have restricted min/max offsets
long off = sortedBaseStartAddrs[i].getOffset() | max;
sortedBaseEndAddrs[i] = sortedBaseStartAddrs[i].getAddressSpace().getAddressInThisSpaceOnly(off);
sortedBaseEndAddrs[i] =
sortedBaseStartAddrs[i].getAddressSpace().getAddressInThisSpaceOnly(off);
}
if (rebuildAddrToIndexMap) {
addrToIndexMap.clear();
@ -402,13 +403,14 @@ public class AddressMapDB implements AddressMap {
Integer tIndex = addrToIndexMap.get(tBase);
if (tIndex != null) {
return tIndex;
} else if (indexOperation == INDEX_MATCH) {
}
else if (indexOperation == INDEX_MATCH) {
return Integer.MIN_VALUE;
}
int search =
normalize ? Arrays.binarySearch(sortedBaseStartAddrs, addr,
normalizingAddressComparator) : Arrays.binarySearch(sortedBaseStartAddrs, addr);
int search = normalize
? Arrays.binarySearch(sortedBaseStartAddrs, addr, normalizingAddressComparator)
: Arrays.binarySearch(sortedBaseStartAddrs, addr);
if (search < 0) {
search = -search - 2;
@ -448,7 +450,8 @@ public class AddressMapDB implements AddressMap {
// Create new base without modifying database
Address[] newBaseAddrs = new Address[baseAddrs.length + 1];
System.arraycopy(baseAddrs, 0, newBaseAddrs, 0, baseAddrs.length);
newBaseAddrs[index] = addr.getAddressSpace().getAddressInThisSpaceOnly(normalizedBaseOffset);
newBaseAddrs[index] =
addr.getAddressSpace().getAddressInThisSpaceOnly(normalizedBaseOffset);
baseAddrs = newBaseAddrs;
}
else {
@ -466,8 +469,8 @@ public class AddressMapDB implements AddressMap {
void checkAddressSpace(AddressSpace addrSpace) {
AddressSpace[] spaces = addrFactory.getPhysicalSpaces();
for (int i = 0; i < spaces.length; i++) {
if (addrSpace.equals(spaces[i])) {
for (AddressSpace space : spaces) {
if (addrSpace.equals(space)) {
return;
}
}
@ -578,8 +581,8 @@ public class AddressMapDB implements AddressMap {
}
catch (AddressOutOfBoundsException e) {
// Recover bad stack address as best we can (used to be a common 32-bit stack space)
return new OldGenericNamespaceAddress(stackSpace, truncateStackOffset(
offset, stackSpace), nameSpaceID);
return new OldGenericNamespaceAddress(stackSpace,
truncateStackOffset(offset, stackSpace), nameSpaceID);
}
}
try {
@ -834,7 +837,6 @@ public class AddressMapDB implements AddressMap {
}
}
/**
* Create all memory base segments within the specified range.
* NOTE: minAddress and maxAddress must have the same address space!
@ -899,13 +901,11 @@ public class AddressMapDB implements AddressMap {
// Try optimized single range approach first
long maxKey;
long minKey =
absolute ? encodeAbsolute(normalizedStart, INDEX_MATCH) : encodeRelative(
normalizedStart, true, INDEX_MATCH);
long minKey = absolute ? encodeAbsolute(normalizedStart, INDEX_MATCH)
: encodeRelative(normalizedStart, true, INDEX_MATCH);
if (minKey != INVALID_ADDRESS_KEY) {
maxKey =
absolute ? encodeAbsolute(normalizedEnd, INDEX_MATCH) : encodeRelative(
normalizedEnd, true, INDEX_MATCH);
maxKey = absolute ? encodeAbsolute(normalizedEnd, INDEX_MATCH)
: encodeRelative(normalizedEnd, true, INDEX_MATCH);
if (maxKey != INVALID_ADDRESS_KEY && (minKey & BASE_MASK) == (maxKey & BASE_MASK)) {
keyRangeList.add(new KeyRange(minKey, maxKey));
return;
@ -926,12 +926,10 @@ public class AddressMapDB implements AddressMap {
Address addr2 = min(normalizedEnd, sortedBaseEndAddrs[index]);
if (addr1.compareTo(addr2) <= 0) {
// Collapse range where minKey and maxKey fall within existing base segments
minKey =
absolute ? encodeAbsolute(addr1, INDEX_MATCH_OR_NEXT) : encodeRelative(addr1,
true, INDEX_MATCH_OR_NEXT);
maxKey =
absolute ? encodeAbsolute(addr2, INDEX_MATCH_OR_PREVIOUS) : encodeRelative(
addr2, true, INDEX_MATCH_OR_PREVIOUS);
minKey = absolute ? encodeAbsolute(addr1, INDEX_MATCH_OR_NEXT)
: encodeRelative(addr1, true, INDEX_MATCH_OR_NEXT);
maxKey = absolute ? encodeAbsolute(addr2, INDEX_MATCH_OR_PREVIOUS)
: encodeRelative(addr2, true, INDEX_MATCH_OR_PREVIOUS);
if (minKey != INVALID_ADDRESS_KEY && maxKey != INVALID_ADDRESS_KEY) {
keyRangeList.add(new KeyRange(minKey, maxKey));
}

View file

@ -0,0 +1,49 @@
/* ###
* 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 ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryBlock;
public class BitMappedByteSourceRange extends ByteSourceRange {
public BitMappedByteSourceRange(MemoryBlock block, Address start, long sourceId, long offset,
long size) {
super(block, start, size, sourceId, offset);
}
@Override
public Address getEnd() {
return getStart().add(size * 8 - 1);
}
@Override
public ByteSourceRange intersect(ByteSourceRange range) {
if (sourceId != range.sourceId) {
return null;
}
long maxOffset = Math.max(byteSourceOffset, range.byteSourceOffset);
long minEndOffset =
Math.min(byteSourceOffset + size - 1, range.byteSourceOffset + range.size - 1);
if (maxOffset > minEndOffset) {
return null;
}
long sourceSize = minEndOffset - maxOffset + 1;
return new BitMappedByteSourceRange(block, start.add((maxOffset - byteSourceOffset) / 8),
sourceId, maxOffset, sourceSize);
}
}

View file

@ -0,0 +1,228 @@
/* ###
* 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 db.Record;
import ghidra.program.database.map.AddressMapDB;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.*;
/**
* Class for handling bit mapped memory sub blocks
*/
class BitMappedSubMemoryBlock extends SubMemoryBlock {
private final MemoryMapDB memMap;
private final Address mappedAddress;
private boolean ioPending;
BitMappedSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
super(adapter, record);
this.memMap = adapter.getMemoryMap();
AddressMapDB addressMap = memMap.getAddressMap();
mappedAddress = addressMap.decodeAddress(
record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL), false);
}
@Override
public boolean isInitialized() {
return false;
}
@Override
public byte getByte(long offset) throws MemoryAccessException, IOException {
if (ioPending) {
throw new MemoryAccessException("Cyclic Access");
}
try {
ioPending = true;
return getBitOverlayByte(offset);
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
}
finally {
ioPending = false;
}
}
public AddressRange getMappedRange() {
Address endMappedAddress = mappedAddress.add((length - 1) / 8);
return new AddressRangeImpl(mappedAddress, endMappedAddress);
}
@Override
public int getBytes(long offset, byte[] b, int off, int len)
throws MemoryAccessException, IOException {
if (ioPending) {
new MemoryAccessException("Cyclic Access");
}
try {
ioPending = true;
len = (int) Math.min(len, length - offset);
for (int i = 0; i < len; i++) {
b[i + off] = getBitOverlayByte(offset++);
}
return len;
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
}
finally {
ioPending = false;
}
}
@Override
public void putByte(long offset, byte b) throws MemoryAccessException, IOException {
try {
if (ioPending) {
new MemoryAccessException("Cyclic Access");
}
ioPending = true;
doPutByte(mappedAddress.addNoWrap(offset / 8), (int) (offset % 8), b);
}
catch (AddressOverflowException e) {
new MemoryAccessException("No memory at address");
}
finally {
ioPending = false;
}
}
@Override
public int putBytes(long offset, byte[] b, int off, int len)
throws MemoryAccessException, IOException {
try {
if (ioPending) {
new MemoryAccessException("Cyclic Access");
}
ioPending = true;
len = (int) Math.min(len, length - offset);
for (int i = 0; i < len; i++) {
doPutByte(mappedAddress.addNoWrap(offset / 8), (int) (offset % 8), b[off + i]);
offset++;
}
return len;
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
}
finally {
ioPending = false;
}
}
private byte getBitOverlayByte(long blockOffset)
throws AddressOverflowException, MemoryAccessException {
Address otherAddr = mappedAddress.addNoWrap(blockOffset / 8);
byte b = memMap.getByte(otherAddr);
return (byte) ((b >> (blockOffset % 8)) & 0x01);
}
private void doPutByte(Address addr, int bitIndex, byte b) throws MemoryAccessException {
ioPending = true;
byte value = memMap.getByte(addr);
int mask = 1 << (bitIndex % 8);
if (b == 0) {
value &= ~mask;
}
else {
value |= mask;
}
memMap.setByte(addr, value);
}
@Override
protected boolean join(SubMemoryBlock sub2) {
return false;
}
@Override
protected boolean isMapped() {
return true;
}
@Override
protected MemoryBlockType getType() {
return MemoryBlockType.BIT_MAPPED;
}
@Override
protected SubMemoryBlock split(long offset) {
throw new UnsupportedOperationException();
}
@Override
protected String getDescription() {
return "Bit Mapped: " + mappedAddress;
}
@Override
protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
long memBlockOffset,
long size) {
ByteSourceRangeList result = new ByteSourceRangeList();
// Since mapped blocks are mapped onto other memory blocks, find those blocks and
// handle each one separately
// converts to byte space since 8 bytes in this block's space maps to 1 byte in real memory
Address startMappedAddress = mappedAddress.add(memBlockOffset / 8);
Address endMappedAddress = mappedAddress.add((memBlockOffset + size - 1) / 8);
List<MemoryBlockDB> blocks = memMap.getBlocks(startMappedAddress, endMappedAddress);
// for each block, get its ByteSourceSet and then translate that set back into this block's
// addresses
for (MemoryBlockDB mappedBlock : blocks) {
Address startInBlock = max(mappedBlock.getStart(), startMappedAddress);
Address endInBlock = min(mappedBlock.getEnd(), endMappedAddress);
long blockSize = endInBlock.subtract(startInBlock) + 1;
ByteSourceRangeList ranges =
mappedBlock.getByteSourceRangeList(startInBlock, blockSize);
for (ByteSourceRange bsRange : ranges) {
result.add(translate(block, bsRange, start, memBlockOffset, size));
}
}
return result;
}
// translates the ByteSourceRange back to addresse
private ByteSourceRange translate(MemoryBlock block, ByteSourceRange bsRange, Address start,
long offset,
long bitLength) {
Address startMappedAddress = mappedAddress.add(offset / 8);
Address normalizedStart = start.subtract(offset % 8);
long mappedOffsetFromStart = bsRange.getStart().subtract(startMappedAddress);
long offsetFromStart = mappedOffsetFromStart * 8;
Address startAddress = normalizedStart.add(offsetFromStart);
return new BitMappedByteSourceRange(block, startAddress, bsRange.getSourceId(),
bsRange.getOffset(), bsRange.getSize());
}
Address min(Address a1, Address a2) {
return a1.compareTo(a2) <= 0 ? a1 : a2;
}
Address max(Address a1, Address a2) {
return a1.compareTo(a2) >= 0 ? a1 : a2;
}
}

View file

@ -0,0 +1,129 @@
/* ###
* 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 db.DBBuffer;
import db.Record;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.*;
/**
* Implementation of SubMemoryBlock for blocks that store bytes in their own private database
* buffers
*/
class BufferSubMemoryBlock extends SubMemoryBlock {
final DBBuffer buf;
BufferSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) throws IOException {
super(adapter, record);
int bufferID = record.getIntValue(MemoryMapDBAdapter.SUB_SOURCE_ID_COL);
buf = adapter.getBuffer(bufferID);
}
@Override
public boolean isInitialized() {
return true;
}
@Override
public byte getByte(long offset) throws IOException {
return buf.getByte((int) (offset - startingOffset));
}
@Override
public int getBytes(long offset, byte[] b, int off, int len) throws IOException {
len = Math.min(len, (int) (length - (offset - startingOffset)));
buf.get((int) (offset - startingOffset), b, off, len);
return len;
}
@Override
public void putByte(long offset, byte b) throws IOException {
buf.putByte((int) (offset - startingOffset), b);
}
@Override
public int putBytes(long offset, byte[] b, int off, int len) throws IOException {
len = Math.min(len, (int) (length - offset - startingOffset));
buf.put((int) (offset - startingOffset), b, off, len);
return len;
}
@Override
public void delete() throws IOException {
buf.delete();
super.delete();
}
@Override
protected boolean join(SubMemoryBlock block) throws IOException {
if (!(block instanceof BufferSubMemoryBlock)) {
return false;
}
BufferSubMemoryBlock other = (BufferSubMemoryBlock) block;
if (other.length + length > Memory.GBYTE) {
return false;
}
buf.append(other.buf);
setLength(length + other.length);
adapter.deleteSubBlock(other.record.getKey());
return true;
}
long getKey() {
return record.getKey();
}
@Override
protected MemoryBlockType getType() {
return MemoryBlockType.DEFAULT;
}
@Override
protected SubMemoryBlock split(long memBlockOffset) throws IOException {
// convert from offset in block to offset in this sub block
int offset = (int) (memBlockOffset - startingOffset);
long newLength = length - offset;
length = offset;
record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
adapter.updateSubBlockRecord(record);
DBBuffer split = buf.split(offset);
Record newSubRecord = adapter.createSubBlockRecord(0, 0, newLength,
MemoryMapDBAdapter.SUB_TYPE_BUFFER, split.getId(), 0);
return new BufferSubMemoryBlock(adapter, newSubRecord);
}
@Override
protected String getDescription() {
return "";
}
@Override
protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
long memBlockOffset,
long size) {
long sourceId = -buf.getId(); // buffers use negative id values; FileBytes use positive id values.
ByteSourceRange bsRange =
new ByteSourceRange(block, start, size, sourceId, memBlockOffset - startingOffset);
return new ByteSourceRangeList(bsRange);
}
}

View file

@ -0,0 +1,204 @@
/* ###
* 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 db.Record;
import ghidra.program.database.map.AddressMapDB;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.*;
/**
* Class for handling byte mapped memory sub blocks
*/
class ByteMappedSubMemoryBlock extends SubMemoryBlock {
private final MemoryMapDB memMap;
private final Address mappedAddress;
private boolean ioPending;
ByteMappedSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
super(adapter, record);
this.memMap = adapter.getMemoryMap();
AddressMapDB addressMap = memMap.getAddressMap();
mappedAddress = addressMap.decodeAddress(
record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL), false);
}
@Override
public boolean isInitialized() {
return false;
}
@Override
public byte getByte(long offset) throws MemoryAccessException, IOException {
if (ioPending) {
new MemoryAccessException("Cyclic Access");
}
try {
ioPending = true;
return memMap.getByte(mappedAddress.addNoWrap(offset - startingOffset));
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
}
finally {
ioPending = false;
}
}
@Override
public int getBytes(long offset, byte[] b, int off, int len)
throws MemoryAccessException, IOException {
if (ioPending) {
new MemoryAccessException("Cyclic Access");
}
try {
ioPending = true;
len = (int) Math.min(len, length - (offset - startingOffset));
return memMap.getBytes(mappedAddress.addNoWrap(offset), b, off, len);
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
}
finally {
ioPending = false;
}
}
@Override
public void putByte(long offset, byte b) throws MemoryAccessException, IOException {
try {
if (ioPending) {
new MemoryAccessException("Cyclic Access");
}
ioPending = true;
memMap.setByte(mappedAddress.addNoWrap(offset - startingOffset), b);
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
}
finally {
ioPending = false;
}
}
@Override
public int putBytes(long offset, byte[] b, int off, int len)
throws MemoryAccessException, IOException {
try {
if (ioPending) {
new MemoryAccessException("Cyclic Access");
}
ioPending = true;
len = (int) Math.min(len, length - (offset - startingOffset));
memMap.setBytes(mappedAddress.addNoWrap(offset - startingOffset), b, off, len);
return len;
}
catch (AddressOverflowException e) {
throw new MemoryAccessException("No memory at address");
}
finally {
ioPending = false;
}
}
public AddressRange getMappedRange() {
Address endMappedAddress = mappedAddress.add(length - 1);
return new AddressRangeImpl(mappedAddress, endMappedAddress);
}
@Override
protected boolean join(SubMemoryBlock sub2) {
return false;
}
@Override
protected boolean isMapped() {
return true;
}
@Override
protected MemoryBlockType getType() {
return MemoryBlockType.BYTE_MAPPED;
}
@Override
protected SubMemoryBlock split(long memBlockOffset) throws IOException {
// convert from offset in block to offset in this sub block
int offset = (int) (memBlockOffset - startingOffset);
long newLength = length - offset;
length = offset;
record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
adapter.updateSubBlockRecord(record);
Address newAddr = mappedAddress.add(offset);
AddressMapDB addressMap = adapter.getMemoryMap().getAddressMap();
long encodedAddr = addressMap.getKey(newAddr, true);
Record newSubRecord = adapter.createSubBlockRecord(0, 0, newLength,
MemoryMapDBAdapter.SUB_TYPE_BYTE_MAPPED, 0, encodedAddr);
return new ByteMappedSubMemoryBlock(adapter, newSubRecord);
}
@Override
protected String getDescription() {
return "Byte Mapped: " + mappedAddress;
}
@Override
protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
long offset, long size) {
ByteSourceRangeList result = new ByteSourceRangeList();
long relativeOffset = offset - startingOffset;
Address startAddress = mappedAddress.add(relativeOffset);
Address endAddress = startAddress.add(size - 1);
List<MemoryBlockDB> blocks = memMap.getBlocks(startAddress, endAddress);
for (MemoryBlockDB mappedBlock : blocks) {
Address startInBlock = max(mappedBlock.getStart(), startAddress);
Address endInBlock = min(mappedBlock.getEnd(), endAddress);
AddressRange blockRange = new AddressRangeImpl(startInBlock, endInBlock);
ByteSourceRangeList ranges =
mappedBlock.getByteSourceRangeList(startInBlock, blockRange.getLength());
for (ByteSourceRange bsRange : ranges) {
result.add(translate(block, bsRange, start, relativeOffset));
}
}
return result;
}
private ByteSourceRange translate(MemoryBlock block, ByteSourceRange bsRange, Address addr,
long relativeOffset) {
Address mappedStart = bsRange.getStart();
long offset = mappedStart.subtract(mappedAddress);
Address start = addr.add(offset - relativeOffset);
return new ByteSourceRange(block, start, bsRange.getSize(), bsRange.getSourceId(),
bsRange.getOffset());
}
Address min(Address a1, Address a2) {
return a1.compareTo(a2) <= 0 ? a1 : a2;
}
Address max(Address a1, Address a2) {
return a1.compareTo(a2) >= 0 ? a1 : a2;
}
}

View file

@ -0,0 +1,126 @@
/* ###
* 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 ghidra.program.model.address.Address;
import ghidra.program.model.mem.MemoryBlock;
public class ByteSourceRange {
protected final Address start;
protected final long size;
protected final long sourceId;
protected final long byteSourceOffset;
protected MemoryBlock block;
public ByteSourceRange(MemoryBlock block, Address start, long size, long sourceId,
long offset) {
this.block = block;
this.start = start;
this.size = size;
this.sourceId = sourceId;
this.byteSourceOffset = offset;
}
public Address getStart() {
return start;
}
public Address getEnd() {
return start.add(size - 1);
}
public long getSize() {
return size;
}
public long getSourceId() {
return sourceId;
}
public long getOffset() {
return byteSourceOffset;
}
public ByteSourceRange intersect(ByteSourceRange range) {
if (sourceId != range.sourceId) {
return null;
}
long maxOffset = Math.max(byteSourceOffset, range.byteSourceOffset);
long minEndOffset =
Math.min(byteSourceOffset + size - 1, range.byteSourceOffset + range.size - 1);
if (maxOffset > minEndOffset) {
return null;
}
return new ByteSourceRange(block, start.add(maxOffset - byteSourceOffset),
minEndOffset - maxOffset + 1, sourceId, maxOffset);
}
public MemoryBlock getMemoryBlock() {
return block;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (byteSourceOffset ^ (byteSourceOffset >>> 32));
result = prime * result + (int) (size ^ (size >>> 32));
result = prime * result + (int) (sourceId ^ (sourceId >>> 32));
result = prime * result + ((start == null) ? 0 : start.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ByteSourceRange other = (ByteSourceRange) obj;
if (block == null) {
if (other.block != null) {
return false;
}
}
else if (!block.equals(other.block)) {
return false;
}
if (byteSourceOffset != other.byteSourceOffset) {
return false;
}
if (size != other.size) {
return false;
}
if (sourceId != other.sourceId) {
return false;
}
if (start == null) {
if (other.start != null) {
return false;
}
}
else if (!start.equals(other.start)) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,220 @@
/* ###
* 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.util.*;
import ghidra.program.model.mem.MemoryBlock;
public class ByteSourceRangeList implements Iterable<ByteSourceRange> {
List<ByteSourceRange> ranges;
public ByteSourceRangeList(ByteSourceRange bsRange) {
this();
ranges.add(bsRange);
}
public ByteSourceRangeList() {
ranges = new ArrayList<>();
}
@Override
public Iterator<ByteSourceRange> iterator() {
return ranges.iterator();
}
public void add(ByteSourceRange range) {
if (range != null) {
ranges.add(range);
}
}
public void add(ByteSourceRangeList byteSourceList) {
ranges.addAll(byteSourceList.ranges);
}
public int getRangeCount() {
return ranges.size();
}
public ByteSourceRange get(int i) {
return ranges.get(i);
}
public boolean isEmpty() {
return ranges.isEmpty();
}
public Set<MemoryBlock> getOverlappingBlocks() {
List<BlockRangeEntry> entries = new ArrayList<>();
for (ByteSourceRange range : ranges) {
entries.add(new BlockRangeStart(this, range));
entries.add(new BlockRangeEnd(this, range));
}
Collections.sort(entries);
return findOverlappingBlocks(entries);
}
public ByteSourceRangeList intersect(ByteSourceRangeList rangeList) {
List<BlockRangeEntry> entries = new ArrayList<>();
for (ByteSourceRange range : ranges) {
entries.add(new BlockRangeStart(this, range));
entries.add(new BlockRangeEnd(this, range));
}
for (ByteSourceRange range : rangeList) {
entries.add(new BlockRangeStart(rangeList, range));
entries.add(new BlockRangeEnd(rangeList, range));
}
Collections.sort(entries);
return getIntersectingRanges(entries);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((ranges == null) ? 0 : ranges.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ByteSourceRangeList other = (ByteSourceRangeList) obj;
if (ranges == null) {
if (other.ranges != null) {
return false;
}
}
else if (!ranges.equals(other.ranges)) {
return false;
}
return true;
}
private ByteSourceRangeList getIntersectingRanges(List<BlockRangeEntry> entries) {
ByteSourceRangeList result = new ByteSourceRangeList();
Set<ByteSourceRange> currentSet = new HashSet<>();
for (BlockRangeEntry entry : entries) {
if (entry.isStart()) {
currentSet.add(entry.range);
}
else {
currentSet.remove(entry.range);
addIntersections(result, entry, currentSet);
}
}
return result;
}
private void addIntersections(ByteSourceRangeList set, BlockRangeEntry entry,
Set<ByteSourceRange> currentSet) {
if (currentSet.isEmpty()) {
return;
}
for (ByteSourceRange byteSourceRange : currentSet) {
if (entry.owner == this) {
set.add(entry.range.intersect(byteSourceRange));
}
else {
set.add(byteSourceRange.intersect(entry.range));
}
}
}
private Set<MemoryBlock> findOverlappingBlocks(List<BlockRangeEntry> entries) {
Set<MemoryBlock> overlappingBlocks = new HashSet<>();
Set<ByteSourceRange> currentSet = new HashSet<>();
for (BlockRangeEntry entry : entries) {
if (entry.isStart()) {
currentSet.add(entry.range);
}
else {
currentSet.remove(entry.range);
if (!currentSet.isEmpty()) {
overlappingBlocks.add(entry.range.block);
for (ByteSourceRange byteSourceRange : currentSet) {
overlappingBlocks.add(byteSourceRange.block);
}
}
}
}
return overlappingBlocks;
}
abstract class BlockRangeEntry implements Comparable<BlockRangeEntry> {
private ByteSourceRange range;
private long sourceId;
private long offset;
private ByteSourceRangeList owner;
BlockRangeEntry(ByteSourceRangeList owner, ByteSourceRange range, long offset) {
this.owner = owner;
this.range = range;
this.offset = offset;
this.sourceId = range.getSourceId();
}
abstract boolean isStart();
@Override
public int compareTo(BlockRangeEntry o) {
if (sourceId != o.sourceId) {
return sourceId > o.sourceId ? 1 : -1;
}
if (offset == o.offset) {
return (isStart() == o.isStart()) ? 0 : (isStart() ? -1 : 1);
}
return offset > o.offset ? 1 : -1;
}
}
class BlockRangeStart extends BlockRangeEntry {
BlockRangeStart(ByteSourceRangeList owner, ByteSourceRange range) {
super(owner, range, range.getOffset());
}
@Override
boolean isStart() {
return true;
}
}
class BlockRangeEnd extends BlockRangeEntry {
BlockRangeEnd(ByteSourceRangeList owner, ByteSourceRange range) {
super(owner, range, range.getOffset() + range.size - 1);
}
@Override
boolean isStart() {
return false;
}
}
}

View file

@ -0,0 +1,362 @@
/* ###
* 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.ConcurrentModificationException;
import db.*;
/**
* FileBytes provides access to the all the byte values (both original and modified) from an
* imported file.
*/
public class FileBytes {
private final DBBuffer[] originalBuffers;
private final DBBuffer[] layeredBuffers;
private final String filename;
private final long id;
private final long fileOffset;
private final long size;
private boolean invalid = false;
private MemoryMapDB memMap;
public FileBytes(FileBytesAdapter adapter, MemoryMapDB memMap, Record record)
throws IOException {
this.memMap = memMap;
this.filename = record.getString(FileBytesAdapter.FILENAME_COL);
this.fileOffset = record.getLongValue(FileBytesAdapter.OFFSET_COL);
this.size = record.getLongValue(FileBytesAdapter.SIZE_COL);
this.id = record.getKey();
BinaryField field = (BinaryField) record.getFieldValue(FileBytesAdapter.BUF_IDS_COL);
int[] bufferIds = new BinaryCodedField(field).getIntArray();
originalBuffers = new DBBuffer[bufferIds.length];
for (int i = 0; i < bufferIds.length; i++) {
originalBuffers[i] = adapter.getBuffer(bufferIds[i]);
}
field = (BinaryField) record.getFieldValue(FileBytesAdapter.LAYERED_BUF_IDS_COL);
bufferIds = new BinaryCodedField(field).getIntArray();
layeredBuffers = new DBBuffer[bufferIds.length];
for (int i = 0; i < bufferIds.length; i++) {
layeredBuffers[i] = adapter.getBuffer(bufferIds[i], originalBuffers[i]);
}
}
/**
* Returns the name of the file that supplied the bytes.
* @return the name of the file that supplied the bytes.
*/
public String getFilename() {
return filename;
}
/**
* Returns the offset in the original file from where these bytes originated. Normally this will
* be 0, but in the case where the program is actually a piece in some other file (e.g. tar,zip),
* this will be the offset into the file corresponding to the first byte in this FileBytes object.
*
* @return the offset in the original file from where these bytes originated.
*/
public long getFileOffset() {
return fileOffset;
}
/**
* Returns the number of bytes from the original source file that are stored in the database.
* @return the number of bytes from the original source file that are stored in the database.
*/
public long getSize() {
return size;
}
/**
* Returns the (possibly modified) byte at the given offset for this file bytes object.
* @param offset the offset into the file bytes for the byte to retrieve.
* @return the (possibly modified) byte at the given offset for this file bytes object.
* @throws IOException if there is a problem reading the database.
* @throws IndexOutOfBoundsException if the given offset is invalid.
*/
public byte getModifiedByte(long offset) throws IOException {
return getByte(layeredBuffers, offset);
}
/**
* Returns the original byte value at the given offset for this file bytes object.
* @param offset the offset into the file bytes for the byte to retrieve.
* @return the original byte at the given offset for this file bytes object.
* @throws IOException if there is a problem reading the database.
* @throws IndexOutOfBoundsException if the given offset is invalid.
*/
public byte getOriginalByte(long offset) throws IOException {
return getByte(originalBuffers, offset);
}
/**
* Tries to get b.length (possibly modified) bytes from this FileBytes entry at the given offset into the file
* bytes. May return fewer bytes if the requested length is beyond the end of the file bytes.
*
* @param offset the offset into the files bytes to start.
* @param b the byte array to populate.
* @return the number of bytes actually populated.
* @throws IOException if there is an error reading from the database
*/
public int getModifiedBytes(long offset, byte[] b) throws IOException {
return getBytes(layeredBuffers, offset, b, 0, b.length);
}
/**
* Tries to get b.length original bytes from this FileBytes entry at the given offset into the file
* bytes. May return fewer bytes if the requested length is beyond the end of the file bytes.
*
* @param offset the offset into the files bytes to start.
* @param b the byte array to populate.
* @return the number of bytes actually populated.
* @throws IOException if there is an error reading from the database
*/
public int getOriginalBytes(long offset, byte[] b) throws IOException {
return getBytes(originalBuffers, offset, b, 0, b.length);
}
/**
* Tries to get length (possibly modified) bytes from the files starting at the given offset and put them
* into the given byte array at the specified offset into the byte array. May return
* fewer bytes if the requested length is beyond the end of the file bytes.
*
* @param offset the offset into the files bytes to start.
* @param b the byte array to populate.
* @param off the offset into the byte array.
* @param length the number of bytes to get.
* @return the number of bytes actually populated.
* @throws IOException if there is an error reading from the database
* @throws IndexOutOfBoundsException if the destination offset and length would exceed the
* size of the buffer b.
*/
public int getModifiedBytes(long offset, byte[] b, int off, int length) throws IOException {
return getBytes(layeredBuffers, offset, b, off, length);
}
/**
* Tries to get length (original) bytes from the files starting at the given offset and put them
* into the given byte array at the specified offset into the byte array. May return
* fewer bytes if the requested length is beyond the end of the file bytes.
*
* @param offset the offset into the files bytes to start.
* @param b the byte array to populate.
* @param off the offset into the byte array.
* @param length the number of bytes to get.
* @return the number of bytes actually populated.
* @throws IOException if there is an error reading from the database
* @throws IndexOutOfBoundsException if the destination offset and length would exceed the
* size of the buffer b.
*/
public int getOriginalBytes(long offset, byte[] b, int off, int length) throws IOException {
return getBytes(originalBuffers, offset, b, off, length);
}
void checkValid() {
if (invalid) {
throw new ConcurrentModificationException();
}
}
void invalidate() {
invalid = true;
}
long getId() {
return id;
}
/**
* Changes the byte at the given offset to the given value. Note, the
* original byte can still be accessed via {@link #getOriginalByte(long)}
* If the byte is changed more than once, only the original value is preserved.
*
* @param offset the offset into the file bytes.
* @param b the new byte value;
* @throws IOException if the write to the database fails.
*/
void putByte(long offset, byte b) throws IOException {
if (offset < 0 || offset >= size) {
throw new IndexOutOfBoundsException();
}
checkValid();
// The max buffer size will be the size of the first buffer. (If more than
// one buffer exists, then the first buffer will be the true max size. If only one buffer,
// then its actual size can be used as the max size and it won't matter.)
int maxBufferSize = layeredBuffers[0].length();
int dbBufferIndex = (int) (offset / maxBufferSize);
int localOffset = (int) (offset % maxBufferSize);
layeredBuffers[dbBufferIndex].putByte(localOffset, b);
}
/**
* Changes the bytes at the given offset to the given values. Note, the
* original bytes can still be accessed via {@link #getOriginalBytes(long, byte[])}
* If the bytes are changed more than once, only the original values are preserved.
*
* @param offset the offset into the file bytes.
* @param b a byte array with the new values to write.
* @return the number of bytes written
* @throws IOException if the write to the database fails.
*/
int putBytes(long offset, byte[] b) throws IOException {
return putBytes(offset, b, 0, b.length);
}
/**
* Changes the bytes at the given offset to the given values. Note, the
* original bytes can still be accessed via {@link #getOriginalBytes(long, byte[], int, int)}
* If the bytes are changed more than once, only the original values are preserved.
*
* @param offset the offset into the file bytes.
* @param b a byte array with the new values to write.
* @param off the offset into the byte array to get the bytes to write.
* @param length the number of bytes to write.
* @return the number of bytes written
* @throws IOException if the write to the database fails.
*/
int putBytes(long offset, byte[] b, int off, int length) throws IOException {
if (b == null) {
throw new NullPointerException();
}
else if (off < 0 || length < 0 || length > b.length - off) {
throw new IndexOutOfBoundsException();
}
else if (length == 0) {
return 0;
}
checkValid();
// adjust size if asking length is more than we have
length = (int) Math.min(length, size - offset);
if (length == 0) {
return 0;
}
// The max buffer size will be the size of the first buffer. (If more than
// one buffer exists, then the first buffer will be the true max size. If only one buffer,
// then its actual size can be used as the max size and it won't matter.)
int maxBufferSize = layeredBuffers[0].length();
long fileBytesOffset = offset;
int byteArrayOffset = off;
int n = length;
while (n > 0) {
int dbBufferIndex = (int) (fileBytesOffset / maxBufferSize);
int localOffset = (int) (fileBytesOffset % maxBufferSize);
int writeLen = Math.min(maxBufferSize - localOffset, n);
layeredBuffers[dbBufferIndex].put(localOffset, b, byteArrayOffset, writeLen);
n -= writeLen;
fileBytesOffset += writeLen;
byteArrayOffset += writeLen;
}
return length;
}
private byte getByte(DBBuffer[] buffers, long offset) throws IOException {
if (offset < 0 || offset >= size) {
throw new IndexOutOfBoundsException();
}
checkValid();
// The max buffer size will be the size of the first buffer. (If more than
// one buffer exists, then the first buffer will be the true max size. If only one buffer,
// then its actual size can be used as the max size and it won't matter.)
int maxBufferSize = buffers[0].length();
int dbBufferIndex = (int) (offset / maxBufferSize);
int localOffset = (int) (offset % maxBufferSize);
return buffers[dbBufferIndex].getByte(localOffset);
}
private int getBytes(DBBuffer[] buffers, long offset, byte[] b, int off, int length)
throws IOException {
if (off < 0 || length < 0 || length > b.length - off) {
throw new IndexOutOfBoundsException();
}
else if (length == 0) {
return 0;
}
checkValid();
// adjust size if asking length is more than we have
length = (int) Math.min(length, size - offset);
if (length == 0) {
return 0;
}
// The max buffer size will be the size of the first buffer. (If more than
// one buffer exists, then the first buffer will be the true max size. If only one buffer,
// then its actual size can be used as the max size and it won't matter.)
int maxBufferSize = buffers[0].length();
long fileBytesOffset = offset;
int byteArrayOffset = off;
int n = length;
while (n > 0) {
int dbBufferIndex = (int) (fileBytesOffset / maxBufferSize);
int localOffset = (int) (fileBytesOffset % maxBufferSize);
int readLen = Math.min(maxBufferSize - localOffset, n);
buffers[dbBufferIndex].get(localOffset, b, byteArrayOffset, readLen);
n -= readLen;
fileBytesOffset += readLen;
byteArrayOffset += readLen;
}
return length;
}
@Override
public String toString() {
return filename;
}
@Override
public int hashCode() {
return (int) id;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
FileBytes other = (FileBytes) obj;
return id == other.id;
}
MemoryMapDB getMemMap() {
return memMap;
}
}

View file

@ -0,0 +1,125 @@
/* ###
* 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.io.InputStream;
import java.util.List;
import db.*;
import ghidra.util.exception.VersionException;
import ghidra.util.task.TaskMonitor;
/**
* Database Adapter for storing and retrieving original file bytes.
*/
abstract class FileBytesAdapter {
private static final int MAX_BUF_SIZE = 1_000_000_000;
public static final int FILENAME_COL = FileBytesAdapterV0.V0_FILENAME_COL;
public static final int OFFSET_COL = FileBytesAdapterV0.V0_OFFSET_COL;
public static final int SIZE_COL = FileBytesAdapterV0.V0_SIZE_COL;
public static final int BUF_IDS_COL = FileBytesAdapterV0.V0_BUF_IDS_COL;
public static final int LAYERED_BUF_IDS_COL = FileBytesAdapterV0.V0_LAYERED_BUF_IDS_COL;
protected DBHandle handle;
private static int maxBufSize = MAX_BUF_SIZE; // shadowed so that it can be changed for testing
protected MemoryMapDB memMap;
FileBytesAdapter(DBHandle handle, MemoryMapDB memMap) {
this.handle = handle;
this.memMap = memMap;
}
static FileBytesAdapter getAdapter(DBHandle handle, int openMode, MemoryMapDB memMap,
TaskMonitor monitor) throws VersionException, IOException {
if (openMode == DBConstants.CREATE) {
return new FileBytesAdapterV0(handle, memMap, true);
}
try {
return new FileBytesAdapterV0(handle, memMap, false);
}
catch (VersionException e) {
if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
throw e;
}
FileBytesAdapter adapter = findReadOnlyAdapter(handle, memMap);
if (openMode == DBConstants.UPGRADE) {
adapter = upgrade(handle, memMap, adapter, monitor);
}
return adapter;
}
}
private static FileBytesAdapter findReadOnlyAdapter(DBHandle handle, MemoryMapDB memMap) {
return new FileBytesAdapterNoTable(handle, memMap);
}
private static FileBytesAdapter upgrade(DBHandle handle, MemoryMapDB memMap,
FileBytesAdapter oldAdapter, TaskMonitor monitor) throws VersionException, IOException {
return new FileBytesAdapterV0(handle, memMap, true);
}
abstract FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
throws IOException;
/**
* Returns a DBBuffer object for the given database buffer id
* @param bufferID the id of the first buffer in the DBBuffer.
* @return a DBBuffer object for the given database buffer id
* @throws IOException if a database IO error occurs.
*/
DBBuffer getBuffer(int bufferID) throws IOException {
if (bufferID >= 0) {
return handle.getBuffer(bufferID);
}
return null;
}
/**
* Returns a layered DBBuffer object for the given database buffer id
* @param bufferID the id of the first buffer in the DBBuffer.
* @param shadowBuffer the buffer to use for byte values unless the bytes have been
* explicitly set in this buffer.
* @return a DBBuffer object for the given database buffer id using the given shadow buffer.
* @throws IOException if a database IO error occurs.
*/
DBBuffer getBuffer(int bufferID, DBBuffer shadowBuffer) throws IOException {
if (bufferID >= 0) {
return handle.getBuffer(bufferID, shadowBuffer);
}
return null;
}
static int getMaxBufferSize() {
return maxBufSize;
}
// *** FOR TESTING PURPOSES ONLY ***
static void setMaxBufferSize(int testSize) {
maxBufSize = testSize;
}
abstract List<FileBytes> getAllFileBytes();
abstract void refresh() throws IOException;
abstract boolean deleteFileBytes(FileBytes fileBytes) throws IOException;
}

View file

@ -0,0 +1,59 @@
/* ###
* 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.InputStream;
import java.util.Collections;
import java.util.List;
import db.DBBuffer;
import db.DBHandle;
/**
* Version of the FileBytesAdapter used to access older databases for read-only and upgrade purposes.
*/
class FileBytesAdapterNoTable extends FileBytesAdapter {
public FileBytesAdapterNoTable(DBHandle handle, MemoryMapDB memMap) {
super(handle, memMap);
}
@Override
FileBytes createFileBytes(String filename, long offset, long size, InputStream is) {
throw new UnsupportedOperationException();
}
@Override
DBBuffer getBuffer(int i) {
return null;
}
@Override
List<FileBytes> getAllFileBytes() {
return Collections.emptyList();
}
@Override
void refresh() {
// do nothing
}
@Override
boolean deleteFileBytes(FileBytes fileBytes) {
return false;
}
}

View file

@ -0,0 +1,165 @@
/* ###
* 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.io.InputStream;
import java.util.*;
import db.*;
import ghidra.util.exception.VersionException;
/**
* Initial version of the FileBytesAdapter
*/
class FileBytesAdapterV0 extends FileBytesAdapter {
static final String TABLE_NAME = "File Bytes";
static final int VERSION = 0;
public static final int V0_FILENAME_COL = 0;
public static final int V0_OFFSET_COL = 1;
public static final int V0_SIZE_COL = 2;
public static final int V0_BUF_IDS_COL = 3;
public static final int V0_LAYERED_BUF_IDS_COL = 4;
static final Schema SCHEMA = new Schema(VERSION, "Key",
new Class[] { StringField.class, LongField.class, LongField.class, BinaryField.class,
BinaryField.class },
new String[] { "Filename", "Offset", "Size", "Chain Buffer IDs",
"Layered Chain Buffer IDs" });
private Table table;
private List<FileBytes> fileBytesList = new ArrayList<>();
FileBytesAdapterV0(DBHandle handle, MemoryMapDB memMap, boolean create)
throws VersionException, IOException {
super(handle, memMap);
if (create) {
table = handle.createTable(TABLE_NAME, SCHEMA);
}
else {
table = handle.getTable(TABLE_NAME);
if (table == null) {
throw new VersionException(true);
}
if (table.getSchema().getVersion() != VERSION) {
throw new VersionException(VersionException.NEWER_VERSION, false);
}
}
// load existing file bytes
RecordIterator iterator = table.iterator();
while (iterator.hasNext()) {
Record record = iterator.next();
fileBytesList.add(new FileBytes(this, memMap, record));
}
}
@Override
FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
throws IOException {
DBBuffer[] buffers = createBuffers(size, is);
DBBuffer[] layeredBuffers = createLayeredBuffers(buffers);
int[] bufIds = getIds(buffers);
int[] layeredBufIds = getIds(layeredBuffers);
Record record = SCHEMA.createRecord(table.getKey());
record.setString(V0_FILENAME_COL, filename);
record.setLongValue(V0_OFFSET_COL, offset);
record.setLongValue(V0_SIZE_COL, size);
record.setField(V0_BUF_IDS_COL, new BinaryCodedField(bufIds));
record.setField(V0_LAYERED_BUF_IDS_COL, new BinaryCodedField(layeredBufIds));
table.putRecord(record);
FileBytes fileBytes = new FileBytes(this, memMap, record);
fileBytesList.add(fileBytes);
return fileBytes;
}
@Override
List<FileBytes> getAllFileBytes() {
return fileBytesList;
}
@Override
void refresh() throws IOException {
Map<Long, FileBytes> map = new HashMap<>();
List<FileBytes> newList = new ArrayList<>();
for (FileBytes fileBytes : fileBytesList) {
map.put(fileBytes.getId(), fileBytes);
}
RecordIterator iterator = table.iterator();
while (iterator.hasNext()) {
Record record = iterator.next();
FileBytes fileBytes = map.remove(record.getKey());
if (fileBytes == null) {
fileBytes = new FileBytes(this, memMap, record);
}
newList.add(fileBytes);
}
for (FileBytes fileBytes : map.values()) {
fileBytes.invalidate();
}
fileBytesList = newList;
}
@Override
boolean deleteFileBytes(FileBytes fileBytes) throws IOException {
if (table.deleteRecord(fileBytes.getId())) {
fileBytesList.remove(fileBytes);
return true;
}
return false;
}
private int[] getIds(DBBuffer[] buffers) {
int[] ids = new int[buffers.length];
for (int i = 0; i < ids.length; i++) {
ids[i] = buffers[i].getId();
}
return ids;
}
private DBBuffer[] createLayeredBuffers(DBBuffer[] buffers) throws IOException {
DBBuffer[] layeredBuffers = new DBBuffer[buffers.length];
for (int i = 0; i < buffers.length; i++) {
layeredBuffers[i] = handle.createBuffer(buffers[i]);
}
return layeredBuffers;
}
private DBBuffer[] createBuffers(long size, InputStream is) throws IOException {
int maxBufSize = getMaxBufferSize();
int bufCount = (int) (size / maxBufSize);
int sizeLastBuf = (int) (size % maxBufSize);
if (sizeLastBuf > 0) {
bufCount++;
}
DBBuffer[] buffers = new DBBuffer[bufCount];
for (int i = 0; i < bufCount - 1; i++) {
buffers[i] = handle.createBuffer(maxBufSize);
}
buffers[bufCount - 1] = handle.createBuffer(sizeLastBuf);
for (DBBuffer buffer : buffers) {
buffer.fill(is);
}
return buffers;
}
}

View file

@ -0,0 +1,140 @@
/* ###
* 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 db.Record;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.*;
/**
* Class for handling {@link FileBytes} memory sub blocks (blocks whose bytes are backed by a FileBytes object
*/
class FileBytesSubMemoryBlock extends SubMemoryBlock {
private final FileBytes fileBytes;
private final long fileBytesOffset;
FileBytesSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) throws IOException {
super(adapter, record);
long fileBytesID = record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_ID_COL);
fileBytesOffset = record.getLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL);
fileBytes = adapter.getMemoryMap().getLayeredFileBytes(fileBytesID);
}
@Override
public boolean isInitialized() {
return true;
}
@Override
public byte getByte(long memBlockOffset) throws IOException {
return fileBytes.getModifiedByte(fileBytesOffset + memBlockOffset - startingOffset);
}
@Override
public int getBytes(long memBlockOffset, byte[] b, int off, int len) throws IOException {
return fileBytes.getModifiedBytes(fileBytesOffset + memBlockOffset - startingOffset, b, off,
len);
}
@Override
public void putByte(long memBlockOffset, byte b) throws MemoryAccessException, IOException {
fileBytes.putByte(fileBytesOffset + memBlockOffset - startingOffset, b);
}
@Override
public int putBytes(long memBlockOffset, byte[] b, int off, int len) throws IOException {
return fileBytes.putBytes(fileBytesOffset + memBlockOffset - startingOffset, b, off, len);
}
@Override
protected boolean join(SubMemoryBlock block) throws IOException {
if (!(block instanceof FileBytesSubMemoryBlock)) {
return false;
}
FileBytesSubMemoryBlock other = (FileBytesSubMemoryBlock) block;
if (fileBytes != other.fileBytes) {
return false;
}
// are the two block consecutive in the fileBytes space?
if (other.fileBytesOffset != fileBytesOffset + length) {
return false;
}
// ok we can join them
setLength(length + other.length);
adapter.deleteSubBlock(other.record.getKey());
return true;
}
public FileBytes getFileBytes() {
return fileBytes;
}
public long getFileBytesOffset() {
return fileBytesOffset;
}
@Override
protected MemoryBlockType getType() {
return MemoryBlockType.DEFAULT;
}
@Override
protected SubMemoryBlock split(long memBlockOffset) throws IOException {
// convert from offset in block to offset in this sub block
int offset = (int) (memBlockOffset - startingOffset);
long newLength = length - offset;
length = offset;
record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
adapter.updateSubBlockRecord(record);
int fileBytesID = record.getIntValue(MemoryMapDBAdapter.SUB_SOURCE_ID_COL);
Record newSubRecord = adapter.createSubBlockRecord(0, 0, newLength,
MemoryMapDBAdapter.SUB_TYPE_FILE_BYTES, fileBytesID, fileBytesOffset + offset);
return new FileBytesSubMemoryBlock(adapter, newSubRecord);
}
@Override
protected String getDescription() {
String fileName = fileBytes.getFilename();
if (fileBytes.getFileOffset()> 0) {
fileName = "[" + fileName + " + 0x" + Long.toHexString(fileBytes.getFileOffset()) + "]";
}
String hexString = Long.toHexString(fileBytesOffset);
return "File: " + fileName + ": 0x" + hexString;
}
@Override
protected boolean uses(FileBytes fb) {
return fileBytes.equals(fb);
}
@Override
protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
long memBlockOffset,
long size) {
long sourceId = fileBytes.getId();
ByteSourceRange bsRange = new ByteSourceRange(block, start, size, sourceId,
fileBytesOffset + memBlockOffset - startingOffset);
return new ByteSourceRangeList(bsRange);
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +18,8 @@ package ghidra.program.database.mem;
import java.io.IOException;
import java.io.InputStream;
import ghidra.program.model.mem.MemoryAccessException;
/**
* Maps a MemoryBlockDB into an InputStream.
*/
@ -92,7 +93,12 @@ class MemoryBlockInputStream extends InputStream {
if (index >= numBytes) {
return -1;
}
return block.getByte(index++) & 0xff;
try {
return block.getByte(index++) & 0xff;
}
catch (MemoryAccessException e) {
throw new IOException(e);
}
}
@Override
@ -104,9 +110,14 @@ class MemoryBlockInputStream extends InputStream {
if (remaining < len) {
len = (int) remaining;
}
len = block.getBytes(index, b, off, len);
index += len;
return len;
try {
len = block.getBytes(index, b, off, len);
index += len;
return len;
}
catch (MemoryAccessException e) {
throw new IOException(e);
}
}
}

View file

@ -35,7 +35,6 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.*;
import ghidra.program.util.ChangeManager;
import ghidra.util.*;
import ghidra.util.datastruct.IntObjectHashtable;
import ghidra.util.exception.*;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
@ -48,20 +47,21 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
private ProgramDB program;
private AddressMapDB addrMap;
private MemoryMapDBAdapter adapter;
private FileBytesAdapter fileBytesAdapter;
private static final DataConverter BIG_ENDIAN = BigEndianDataConverter.INSTANCE;
private static final DataConverter LITTLE_ENDIAN = LittleEndianDataConverter.INSTANCE;
private DataConverter defaultEndian;
private MemoryBlock[] blocks;// sorted list of blocks
private IntObjectHashtable<MemoryBlock> blockMap = new IntObjectHashtable<>();// maps id to MemoryBlockDB objects
private AddressSet addrSet;
private AddressSet initializedLoadedAddrSet;
private AddressSet allInitializedAddrSet;
private List<MemoryBlockDB> blocks;// sorted list of blocks
private AddressSet addrSet = new AddressSet();
private AddressSet initializedLoadedAddrSet = new AddressSet();
private AddressSet allInitializedAddrSet = new AddressSet();
private MemoryBlock lastBlock;// the last accessed block
private LiveMemoryHandler liveMemory;
Lock lock;
private Set<MemoryBlock> potentialOverlappingBlocks;
private static Comparator<Object> BLOCK_ADDRESS_COMPARATOR = (o1, o2) -> {
MemoryBlock block = (MemoryBlock) o1;
@ -84,9 +84,25 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
Lock lock, TaskMonitor monitor) throws IOException, VersionException {
this.addrMap = addrMap;
this.lock = lock;
adapter = MemoryMapDBAdapter.getAdapter(handle, openMode, this, monitor);
defaultEndian = isBigEndian ? BIG_ENDIAN : LITTLE_ENDIAN;
init(false);
adapter = MemoryMapDBAdapter.getAdapter(handle, openMode, this, monitor);
fileBytesAdapter = FileBytesAdapter.getAdapter(handle, openMode, this, monitor);
initializeBlocks();
buildAddressSets();
}
// for testing
MemoryMapDB(DBHandle handle, AddressMapDB addrMap, int openMode, boolean isBigEndian,
Lock lock) {
this.addrMap = addrMap;
this.lock = lock;
defaultEndian = isBigEndian ? BIG_ENDIAN : LITTLE_ENDIAN;
}
// for testing
void init(MemoryMapDBAdapter memoryAdapter, FileBytesAdapter bytesAdapter) {
this.adapter = memoryAdapter;
this.fileBytesAdapter = bytesAdapter;
}
/**
@ -104,55 +120,57 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
public void invalidateCache(boolean all) throws IOException {
lock.acquire();
try {
init(true);
reloadAll();
}
finally {
lock.release();
}
}
private void init(boolean reload) throws IOException {
private void buildAddressSets() {
addrSet = new AddressSet();
initializedLoadedAddrSet = new AddressSet();
allInitializedAddrSet = new AddressSet();
// we have to process the non-mapped blocks first because to process the mapped
// blocks we need the address sets for the non-mapped blocks to be complete
for (MemoryBlockDB block : blocks) {
if (!block.isMapped()) {
addBlockAddresses(block);
}
}
for (MemoryBlockDB block : blocks) {
if (block.isMapped()) {
addBlockAddresses(block);
}
}
}
private void addBlockAddresses(MemoryBlockDB block) {
AddressSet blockSet = new AddressSet(block.getStart(), block.getEnd());
addrSet = addrSet.union(blockSet);
if (block.isMapped()) {
allInitializedAddrSet =
allInitializedAddrSet.union(getMappedIntersection(block, allInitializedAddrSet));
initializedLoadedAddrSet = initializedLoadedAddrSet.union(
getMappedIntersection(block, initializedLoadedAddrSet));
}
else if (block.isInitialized()) {
allInitializedAddrSet = allInitializedAddrSet.union(blockSet);
if (block.isLoaded()) {
initializedLoadedAddrSet = initializedLoadedAddrSet.union(blockSet);
}
}
}
private void reloadAll() throws IOException {
synchronized (this) {
if (reload) {
adapter.refreshMemory();
}
// Minimize chance of accessing field variables while in flux
MemoryBlockDB[] newBlocks = adapter.getMemoryBlocks();
AddressSet newAddrSet = new AddressSet();
AddressSet newLoadedInitializedAddrSet = new AddressSet();
AddressSet newAllInitializedAddrSet = new AddressSet();
List<MappedMemoryBlock> mappedMemoryBlocks = new LinkedList<>();
IntObjectHashtable<MemoryBlock> newBlockMap = new IntObjectHashtable<>();
for (MemoryBlockDB block : newBlocks) {
newBlockMap.put(block.getID(), block);
newAddrSet.addRange(block.getStart(), block.getEnd());
if (block.isInitialized()) {
newAllInitializedAddrSet.addRange(block.getStart(), block.getEnd());
if (block.isLoaded()) {
newLoadedInitializedAddrSet.addRange(block.getStart(), block.getEnd());
}
}
if (block.isMapped() && block instanceof MappedMemoryBlock) {
mappedMemoryBlocks.add((MappedMemoryBlock) block);
}
}
// Mapped blocks are uninitialized, but ranges of them may map to initialized ranges.
// Add in these mapped initialized ranges. Don't forget to keep them up to date as
// initialized blocks that they map to come and go (this happens automatically because
// this method gets called on every add/remove).
newAllInitializedAddrSet.add(
getMappedIntersection(mappedMemoryBlocks, newAllInitializedAddrSet));
newLoadedInitializedAddrSet.add(
getMappedIntersection(mappedMemoryBlocks, newLoadedInitializedAddrSet));
lastBlock = null;
blocks = newBlocks;
addrSet = newAddrSet;
initializedLoadedAddrSet = newLoadedInitializedAddrSet;
allInitializedAddrSet = newAllInitializedAddrSet;
blockMap = newBlockMap;
fileBytesAdapter.refresh();
adapter.refreshMemory();
initializeBlocks();
buildAddressSets();
}
if (liveMemory != null) {
liveMemory.clearCache();
@ -160,6 +178,13 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
addrMap.memoryMapChanged(this);
}
private synchronized void initializeBlocks() {
List<MemoryBlockDB> newBlocks = adapter.getMemoryBlocks();
lastBlock = null;
blocks = newBlocks;
addrMap.memoryMapChanged(this);
}
public void setLanguage(Language newLanguage) {
defaultEndian = newLanguage.isBigEndian() ? BIG_ENDIAN : LITTLE_ENDIAN;
}
@ -171,7 +196,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
public void setProgram(ProgramDB program) {
this.program = program;
try {
init(true);
reloadAll();
}
catch (IOException e) {
dbError(e);
@ -233,32 +258,36 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
return initializedLoadedAddrSet;
}
void checkMemoryWrite(Address start, int length) throws MemoryAccessException {
CodeManager codeManager = program.getCodeManager();
Instruction instr = codeManager.getInstructionContaining(start);
if (instr != null) {
throw new MemoryAccessException(
"Memory change conflicts with instruction at " + instr.getMinAddress());
}
if (length > 1) {
instr = codeManager.getInstructionAfter(start);
if (instr != null) {
Address end = start.add(length - 1);
if (instr.getMinAddress().compareTo(end) <= 0) {
throw new MemoryAccessException(
"Memory change conflicts with instruction at " + instr.getMinAddress());
void checkMemoryWrite(MemoryBlockDB block, Address start, long length)
throws MemoryAccessException {
checkRangeForInstructions(start, start.add(length - 1));
Set<MemoryBlock> overlappingBlocks = getPotentialOverlappingBlocks();
ByteSourceRangeList changeingByteSource = block.getByteSourceRangeList(start, length);
if (overlappingBlocks.contains(block)) {
for (MemoryBlock b : overlappingBlocks) {
if (b.equals(block)) {
continue;
}
ByteSourceRangeList set =
((MemoryBlockDB) b).getByteSourceRangeList(b.getStart(), b.getSize());
ByteSourceRangeList intersect = set.intersect(changeingByteSource);
for (ByteSourceRange range : intersect) {
checkRangeForInstructions(range.getStart(), range.getEnd());
}
}
}
}
void checkMemoryWrite(Address addr) throws MemoryAccessException {
CodeManager codeManager = program.getCodeManager();
Instruction instr = codeManager.getInstructionContaining(addr);
if (instr != null) {
throw new MemoryAccessException(
"Memory change conflicts with instruction at " + instr.getMinAddress());
private Set<MemoryBlock> getPotentialOverlappingBlocks() {
if (potentialOverlappingBlocks == null) {
ByteSourceRangeList byteSourceList = new ByteSourceRangeList();
for (MemoryBlockDB block : blocks) {
byteSourceList.add(block.getByteSourceRangeList(block.getStart(), block.getSize()));
}
potentialOverlappingBlocks = byteSourceList.getOverlappingBlocks();
}
return potentialOverlappingBlocks;
}
/**
@ -288,15 +317,15 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
return lastBlock;
}
}
MemoryBlock[] tmpBlocks = blocks;
int index = Arrays.binarySearch(tmpBlocks, addr, BLOCK_ADDRESS_COMPARATOR);
List<MemoryBlockDB> tmpBlocks = blocks;
int index = Collections.binarySearch(tmpBlocks, addr, BLOCK_ADDRESS_COMPARATOR);
if (index >= 0) {
lastBlock = tmpBlocks[index];
lastBlock = tmpBlocks.get(index);
return lastBlock;
}
index = -index - 2;
if (index >= 0) {
MemoryBlock block = tmpBlocks[index];
MemoryBlock block = tmpBlocks.get(index);
if (block.contains(addr)) {
lastBlock = block;
return block;
@ -305,16 +334,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
return null;
}
// MemoryBlockDB getBlock(int id) {
// lock.acquire();
// try {
// return (MemoryBlockDB) blockMap.get(id);
// }
// finally {
// lock.release();
// }
// }
void fireBlockAdded(MemoryBlock newBlock) {
AddressRange range = new AddressRangeImpl(newBlock.getStart(), newBlock.getEnd());
program.getTreeManager().addMemoryBlock(newBlock.getName(), range);
@ -352,7 +371,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
newBlock);
}
void fireBlockChanged(MemoryBlockDB block) {
void fireBlockChanged(MemoryBlock block) {
if (program != null) {
program.setChanged(ChangeManager.DOCR_MEMORY_BLOCK_CHANGED, block, null);
}
@ -468,9 +487,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
checkRange(start, length);
}
try {
adapter.createInitializedBlock(name, start, is, length, MemoryBlock.READ);
init(true);
MemoryBlock newBlock = getBlockDB(start);
MemoryBlockDB newBlock =
adapter.createInitializedBlock(name, start, is, length, MemoryBlock.READ);
initializeBlocks();
addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@ -486,6 +506,57 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
finally {
lock.release();
}
}
@Override
public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
long offset, long length, boolean overlay) throws LockException, DuplicateNameException,
MemoryConflictException, AddressOverflowException {
lock.acquire();
try {
checkBlockSize(length, true);
program.checkExclusiveAccess();
checkFileBytesRange(fileBytes, offset, length);
if (overlay) {
start = createOverlaySpace(name, start, length);
}
else {
checkRange(start, length);
}
try {
MemoryBlockDB newBlock =
adapter.createFileBytesBlock(name, start, length, fileBytes, offset,
MemoryBlock.READ);
initializeBlocks();
addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
catch (IOException e) {
program.dbError(e);
}
return null;
}
finally {
lock.release();
}
}
private void checkFileBytesRange(FileBytes fileBytes, long offset, long length) {
if (length < 0) {
throw new IllegalArgumentException("Length must be >= 0, got " + length);
}
if (offset < 0 || offset >= fileBytes.getSize()) {
throw new IndexOutOfBoundsException(
"Offset must be in range [0," + length + "], got " + offset);
}
if (offset + length > fileBytes.getSize()) {
throw new IndexOutOfBoundsException(
"Specified length extends beyond file bytes length");
}
}
@ -507,10 +578,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
checkRange(start, size);
}
try {
adapter.createBlock(MemoryBlockType.DEFAULT, name, start, size, null, false,
MemoryBlock.READ);
init(true);
MemoryBlock newBlock = getBlockDB(start);
MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.DEFAULT, name, start,
size, null, false, MemoryBlock.READ);
initializeBlocks();
addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@ -534,10 +605,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
checkRange(start, length);
overlayAddress.addNoWrap((length - 1) / 8);// just to check if length fits in address space
try {
adapter.createBlock(MemoryBlockType.BIT_MAPPED, name, start, length, overlayAddress,
false, MemoryBlock.READ);
init(true);
MemoryBlock newBlock = getBlockDB(start);
MemoryBlockDB newBlock = adapter.createBlock(MemoryBlockType.BIT_MAPPED, name,
start, length, overlayAddress, false, MemoryBlock.READ);
initializeBlocks();
addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@ -561,10 +632,11 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
checkRange(start, length);
overlayAddress.addNoWrap(length - 1);// just to check if length fits in address space
try {
adapter.createBlock(MemoryBlockType.BYTE_MAPPED, name, start, length,
overlayAddress, false, MemoryBlock.READ);
init(true);
MemoryBlock newBlock = getBlockDB(start);
MemoryBlockDB newBlock =
adapter.createBlock(MemoryBlockType.BYTE_MAPPED, name, start, length,
overlayAddress, false, MemoryBlock.READ);
initializeBlocks();
addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@ -589,13 +661,15 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
try {
Address overlayAddr = null;
if (block instanceof MappedMemoryBlock) {
overlayAddr = ((MappedMemoryBlock) block).getOverlayedMinAddress();
if (block.isMapped()) {
SourceInfo info = block.getSourceInfos().get(0);
overlayAddr = info.getMappedRange().get().getMinAddress();
}
adapter.createBlock(block.getType(), name, start, length, overlayAddr,
MemoryBlockDB newBlock =
adapter.createBlock(block.getType(), name, start, length, overlayAddr,
block.isInitialized(), block.getPermissions());
init(true);
MemoryBlock newBlock = getBlockDB(start);
initializeBlocks();
addBlockAddresses(newBlock);
fireBlockAdded(newBlock);
return newBlock;
}
@ -625,7 +699,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
public MemoryBlock[] getBlocks() {
lock.acquire();
try {
return blocks.clone();
return blocks.toArray(new MemoryBlock[blocks.size()]);
}
finally {
lock.release();
@ -643,15 +717,11 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
throw new MemoryBlockException(
"Memory move operation not permitted while live memory is active");
}
Address oldStartAddr = block.getStart();
if (!(block instanceof MemoryBlockDB)) {
throw new NotFoundException("Block does not belong to this program");
}
checkBlock(block);
MemoryBlockDB memBlock = (MemoryBlockDB) block;
if (memBlock.memMap != this) {
throw new NotFoundException("Block does not belong to this program");
}
if (memBlock.getType() == MemoryBlockType.OVERLAY) {
Address oldStartAddr = block.getStart();
if (block.getType() == MemoryBlockType.OVERLAY) {
throw new IllegalArgumentException("Overlay blocks cannot be moved");
}
if (newStartAddr.getAddressSpace().isOverlaySpace()) {
@ -662,7 +732,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
// events go out that would cause screen updates;
// the code manager will be locked until the remove is done
try {
memBlock.checkValid();
Address newEndAddr = newStartAddr.addNoWrap(block.getSize() - 1);
AddressSet set = new AddressSet(addrSet);
set.deleteRange(block.getStart(), block.getEnd());
@ -672,12 +741,12 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
try {
memBlock.setStartAddress(newStartAddr);
init(false);
reloadAll();
}
catch (IOException e) {
program.dbError(e);
}
program.moveAddressRange(oldStartAddr, newStartAddr, memBlock.length, monitor);
program.moveAddressRange(oldStartAddr, newStartAddr, memBlock.getSize(), monitor);
}
finally {
program.invalidate();
@ -701,9 +770,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
throw new MemoryBlockException(
"Memory split operation not permitted while live memory is active");
}
if (!(block instanceof MemoryBlockDB)) {
throw new NotFoundException("Block does not belong to this program");
}
checkBlock(block);
MemoryBlockDB memBlock = (MemoryBlockDB) block;
if (!memBlock.contains(addr)) {
throw new IllegalArgumentException("Block must contain split address");
@ -719,7 +786,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
try {
memBlock.split(addr);
init(true);
initializeBlocks();
fireBlockSplit();
}
catch (IOException e) {
@ -738,57 +805,25 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
throws MemoryBlockException, NotFoundException, LockException {
lock.acquire();
try {
program.checkExclusiveAccess();
if (liveMemory != null) {
throw new MemoryBlockException(
"Memory join operation not permitted while live memory is active");
}
if (!(blockOne instanceof MemoryBlockDB) || !(blockTwo instanceof MemoryBlockDB)) {
throw new NotFoundException("Blocks do not belong to this program");
}
if (blockOne.getType() != blockTwo.getType()) {
throw new MemoryBlockException("Blocks of different types can not be joined");
}
if (blockOne.isInitialized() != blockTwo.isInitialized()) {
throw new MemoryBlockException(
"Both blocks must be either initialized or uninitialized");
}
if (blockOne.getType() == MemoryBlockType.OVERLAY) {
throw new IllegalArgumentException("Cannot join overlay blocks");
}
if (blockOne.getType() == MemoryBlockType.BIT_MAPPED) {
throw new IllegalArgumentException("Cannot join bit mapped blocks");
}
if (blockOne.getType() == MemoryBlockType.BYTE_MAPPED) {
throw new IllegalArgumentException("Cannot join byte mapped blocks");
}
long size1 = blockOne.getSize();
long size2 = blockTwo.getSize();
if (size1 + size2 > Integer.MAX_VALUE) {
throw new MemoryBlockException("Blocks are too large to be joined");
}
// swap if second block is before first block
if (blockOne.getStart().compareTo(blockTwo.getStart()) > 0) {
MemoryBlock tmp = blockOne;
blockOne = blockTwo;
blockTwo = tmp;
}
checkPreconditionsForJoining(blockOne, blockTwo);
MemoryBlockDB memBlock1 = (MemoryBlockDB) blockOne;
MemoryBlockDB memBlock2 = (MemoryBlockDB) blockTwo;
Address block1Addr = blockOne.getStart();
Address block2Addr = blockTwo.getStart();
memBlock1.checkValid();
memBlock2.checkValid();
if (memBlock1.memMap != this || memBlock2.memMap != this) {
throw new NotFoundException("Blocks do not belong to this program");
}
if (!(memBlock1.getEnd().isSuccessor(memBlock2.getStart()))) {
throw new MemoryBlockException("Blocks are not contiguous");
}
MemoryBlock newBlock = null;
try {
memBlock1.join(memBlock2);
init(true);
reloadAll();
newBlock = getBlockDB(block1Addr);
fireBlocksJoined(newBlock, block2Addr);
@ -804,15 +839,62 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
private void checkPreconditionsForJoining(MemoryBlock block1, MemoryBlock block2)
throws MemoryBlockException, NotFoundException, LockException {
program.checkExclusiveAccess();
if (liveMemory != null) {
throw new MemoryBlockException(
"Memory join operation not permitted while live memory is active");
}
checkBlockForJoining(block1);
checkBlockForJoining(block2);
if (block1.isInitialized() != block2.isInitialized()) {
throw new MemoryBlockException(
"Both blocks must be either initialized or uninitialized");
}
if (!(block1.getEnd().isSuccessor(block2.getStart()))) {
throw new MemoryBlockException("Blocks are not contiguous");
}
}
private void checkBlockForJoining(MemoryBlock block) {
checkBlock(block);
switch (block.getType()) {
case BIT_MAPPED:
throw new IllegalArgumentException("Cannot join bit mapped blocks");
case BYTE_MAPPED:
throw new IllegalArgumentException("Cannot join byte mapped blocks");
case OVERLAY:
throw new IllegalArgumentException("Cannot join overlay blocks");
case DEFAULT:
default:
// do nothing, these types are ok for joining
}
}
private void checkBlock(MemoryBlock block) {
if (!(block instanceof MemoryBlockDB)) {
throw new IllegalArgumentException("Blocks do not belong to this program");
}
MemoryBlockDB blockDB = (MemoryBlockDB) block;
if (blockDB.memMap != this) {
throw new IllegalArgumentException("Blocks do not belong to this program");
}
blockDB.checkValid();
}
@Override
public MemoryBlock convertToInitialized(MemoryBlock unitializedBlock, byte initialValue)
throws MemoryBlockException, NotFoundException, LockException {
lock.acquire();
try {
checkBlock(unitializedBlock);
program.checkExclusiveAccess();
if (!(unitializedBlock instanceof MemoryBlockDB)) {
throw new NotFoundException("Block does not belong to this program");
}
if (unitializedBlock.isInitialized()) {
throw new IllegalArgumentException(
"Only an Uninitialized Block may be converted to an Initialized Block");
@ -822,7 +904,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
throw new IllegalArgumentException("Block is of a type that cannot be initialized");
}
long size = unitializedBlock.getSize();
if (size > MAX_INITIALIZED_BLOCK_SIZE) {
if (size > MAX_BLOCK_SIZE) {
throw new MemoryBlockException("Block too large to initialize");
}
MemoryBlockDB memBlock = (MemoryBlockDB) unitializedBlock;
@ -852,9 +934,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
lock.acquire();
try {
program.checkExclusiveAccess();
if (!(initializedBlock instanceof MemoryBlockDB)) {
throw new NotFoundException("Block does not belong to this program");
}
checkBlock(initializedBlock);
if (!initializedBlock.isInitialized()) {
throw new IllegalArgumentException(
"Only an Initialized Block may be converted to an Uninitialized Block");
@ -1667,14 +1747,8 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
lock.acquire();
try {
program.checkExclusiveAccess();
if (!(block instanceof MemoryBlockDB)) {
throw new IllegalArgumentException("Block not in program");
}
checkBlock(block);
MemoryBlockDB memBlock = (MemoryBlockDB) block;
if (blockMap.get(memBlock.getID()) != memBlock) {
throw new IllegalArgumentException("Block not in program");
}
Address startAddress = block.getStart();
@ -1685,7 +1759,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
try {
program.deleteAddressRange(startAddress, memBlock.getEnd(), monitor);
memBlock.delete();
init(true);
reloadAll();
}
catch (IOException e) {
program.dbError(e);
@ -1758,29 +1832,32 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
* Gets the intersected set of addresses between a list of mapped memory blocks, and some other
* address set.
*
* @param mappedMemoryBlocks The mapped memory blocks to use in the intersection.
* @param otherAddrSet Some other address set to use in the intersection.
* @return The intersected set of addresses between 'mappedMemoryBlocks' and 'otherAddrSet'.
* @param block The mapped memory block to use in the intersection.
* @param set Some other address set to use in the intersection.
* @return The intersected set of addresses between 'mappedMemoryBlock' and other address set
*/
private AddressSet getMappedIntersection(List<MappedMemoryBlock> mappedMemoryBlocks,
AddressSet otherAddrSet) {
private AddressSet getMappedIntersection(MemoryBlock block, AddressSet set) {
AddressSet mappedIntersection = new AddressSet();
for (MappedMemoryBlock mappedBlock : mappedMemoryBlocks) {
AddressSet resolvedIntersection =
otherAddrSet.intersect(new AddressSet(mappedBlock.getOverlayedAddressRange()));
for (AddressRange resolvedRange : resolvedIntersection) {
mappedIntersection.add(getMappedRange(mappedBlock, resolvedRange));
}
List<SourceInfo> sourceInfos = block.getSourceInfos();
// mapped blocks can only ever have one sourceInfo
SourceInfo info = sourceInfos.get(0);
AddressRange range = info.getMappedRange().get();
AddressSet resolvedIntersection = set.intersect(new AddressSet(range));
for (AddressRange resolvedRange : resolvedIntersection) {
mappedIntersection.add(getMappedRange(block, resolvedRange));
}
return mappedIntersection;
}
private AddressRange getMappedRange(MappedMemoryBlock mappedBlock, AddressRange resolvedRange) {
/**
* Converts the given address range back from the source range back to the mapped range.
*/
private AddressRange getMappedRange(MemoryBlock mappedBlock, AddressRange resolvedRange) {
Address start, end;
SourceInfo info = mappedBlock.getSourceInfos().get(0);
long startOffset =
resolvedRange.getMinAddress().subtract(mappedBlock.getOverlayedMinAddress());
resolvedRange.getMinAddress().subtract(info.getMappedRange().get().getMinAddress());
boolean isBitMapped = mappedBlock.getType() == MemoryBlockType.BIT_MAPPED;
if (isBitMapped) {
start = mappedBlock.getStart().add(startOffset * 8);
@ -1812,7 +1889,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
public final String toString() {
lock.acquire();
try {
if (blocks == null || blocks.length == 0) {
if (blocks == null || blocks.isEmpty()) {
return "[empty]\n";
}
StringBuffer buffer = new StringBuffer();
@ -1913,11 +1990,10 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
private void checkBlockSize(long newBlockLength, boolean initialized) {
long limit = initialized ? MAX_INITIALIZED_BLOCK_SIZE : MAX_UNINITIALIZED_BLOCK_SIZE;
if (newBlockLength > limit) {
if (newBlockLength > MAX_BLOCK_SIZE) {
throw new IllegalStateException(
"New memory block NOT added: exceeds the maximum memory block byte size of " +
(limit >> GBYTE_SHIFT_FACTOR) + " GByte(s)");
MAX_BLOCK_SIZE_GB + " GByte(s)");
}
long newSize = getNumAddresses() + newBlockLength;
@ -1929,4 +2005,108 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
}
}
@Override
public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
throws IOException {
lock.acquire();
try {
return fileBytesAdapter.createFileBytes(filename, offset, size, is);
}
finally {
lock.release();
}
}
@Override
public List<FileBytes> getAllFileBytes() {
List<FileBytes> allFileBytes = fileBytesAdapter.getAllFileBytes();
return Collections.unmodifiableList(allFileBytes);
}
@Override
public boolean deleteFileBytes(FileBytes fileBytes) throws IOException {
if (fileBytes.getMemMap() != this) {
throw new IllegalArgumentException(
"Attempted to delete FileBytes that doesn't belong to this program");
}
lock.acquire();
try {
if (inUse(fileBytes)) {
return false;
}
return fileBytesAdapter.deleteFileBytes(fileBytes);
}
finally {
lock.release();
}
}
private boolean inUse(FileBytes fileBytes) {
for (MemoryBlockDB block : blocks) {
if (block.uses(fileBytes)) {
return true;
}
}
return false;
}
FileBytes getLayeredFileBytes(long fileBytesID) throws IOException {
List<FileBytes> allFileBytes = fileBytesAdapter.getAllFileBytes();
for (FileBytes layeredFileBytes : allFileBytes) {
if (layeredFileBytes.getId() == fileBytesID) {
return layeredFileBytes;
}
}
throw new IOException("No File Bytes found for ID: " + fileBytesID);
}
/**
* Returns a list of all memory blocks that contain any addresses in the given range
* @param start the start address
* @param end the end address
* @return a list of all memory blocks that contain any addresses in the given range
*/
List<MemoryBlockDB> getBlocks(Address start, Address end) {
List<MemoryBlockDB> list = new ArrayList<>();
List<MemoryBlockDB> tmpBlocks = blocks;
int index = Collections.binarySearch(tmpBlocks, start, BLOCK_ADDRESS_COMPARATOR);
if (index < 0) {
index = -index - 2;
}
if (index >= 0) {
MemoryBlockDB block = tmpBlocks.get(index);
if (block.contains(start)) {
list.add(block);
}
}
while (++index < tmpBlocks.size()) {
MemoryBlockDB block = tmpBlocks.get(index);
if (block.getStart().compareTo(end) > 0) {
break;
}
list.add(block);
}
return list;
}
void checkRangeForInstructions(Address start, Address end) throws MemoryAccessException {
CodeManager codeManager = program.getCodeManager();
Instruction instr = codeManager.getInstructionContaining(start);
if (instr != null) {
throw new MemoryAccessException(
"Memory change conflicts with instruction at " + instr.getMinAddress());
}
if (!end.equals(start)) {
instr = codeManager.getInstructionAfter(start);
if (instr != null) {
if (instr.getMinAddress().compareTo(end) <= 0) {
throw new MemoryAccessException(
"Memory change conflicts with instruction at " + instr.getMinAddress());
}
}
}
}
}

View file

@ -17,6 +17,7 @@ package ghidra.program.database.mem;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import db.*;
import ghidra.program.model.address.Address;
@ -28,41 +29,40 @@ import ghidra.util.task.TaskMonitor;
abstract class MemoryMapDBAdapter {
static final String TABLE_NAME = "Memory Blocks";
static final int CURRENT_VERSION = MemoryMapDBAdapterV3.V3_VERSION;
static final int CURRENT_VERSION = 2;
static Schema BLOCK_SCHEMA = MemoryMapDBAdapterV3.V3_BLOCK_SCHEMA;
static Schema SUB_BLOCK_SCHEMA = MemoryMapDBAdapterV3.V3_SUB_BLOCK_SCHEMA;
static Schema BLOCK_SCHEMA = new Schema(CURRENT_VERSION, "Key",
new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class,
LongField.class, ShortField.class, LongField.class, LongField.class, IntField.class,
IntField.class },
new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address",
"Block Type", "Overlay Address", "Length", "Chain Buffer ID", "Segment" });
public static final int NAME_COL = MemoryMapDBAdapterV3.V3_NAME_COL;
public static final int COMMENTS_COL = MemoryMapDBAdapterV3.V3_COMMENTS_COL;
public static final int SOURCE_COL = MemoryMapDBAdapterV3.V3_SOURCE_COL;
public static final int PERMISSIONS_COL = MemoryMapDBAdapterV3.V3_PERMISSIONS_COL;
public static final int START_ADDR_COL = MemoryMapDBAdapterV3.V3_START_ADDR_COL;
public static final int LENGTH_COL = MemoryMapDBAdapterV3.V3_LENGTH_COL;
public static final int SEGMENT_COL = MemoryMapDBAdapterV3.V3_SEGMENT_COL;
static final int NAME_COL = 0;
static final int COMMENTS_COL = 1;
static final int SOURCE_COL = 2;
static final int PERMISSIONS_COL = 3;
static final int START_ADDR_COL = 4;
static final int BLOCK_TYPE_COL = 5;
static final int OVERLAY_ADDR_COL = 6;
static final int LENGTH_COL = 7;
static final int CHAIN_BUF_COL = 8;
static final int SEGMENT_COL = 9;
public static final int SUB_PARENT_ID_COL = MemoryMapDBAdapterV3.V3_SUB_PARENT_ID_COL;
public static final int SUB_TYPE_COL = MemoryMapDBAdapterV3.V3_SUB_TYPE_COL;
public static final int SUB_LENGTH_COL = MemoryMapDBAdapterV3.V3_SUB_LENGTH_COL;
public static final int SUB_START_OFFSET_COL = MemoryMapDBAdapterV3.V3_SUB_START_OFFSET_COL;
public static final int SUB_SOURCE_ID_COL = MemoryMapDBAdapterV3.V3_SUB_SOURCE_ID_COL;
public static final int SUB_SOURCE_OFFSET_COL = MemoryMapDBAdapterV3.V3_SUB_SOURCE_OFFSET_COL;
static final int INITIALIZED = 0;
static final int UNINITIALIZED = 1;
static final int BIT_MAPPED = 2;
static final int BYTE_MAPPED = 4;
public static final byte SUB_TYPE_BIT_MAPPED = MemoryMapDBAdapterV3.V3_SUB_TYPE_BIT_MAPPED;
public static final byte SUB_TYPE_BYTE_MAPPED = MemoryMapDBAdapterV3.V3_SUB_TYPE_BYTE_MAPPED;
public static final byte SUB_TYPE_BUFFER = MemoryMapDBAdapterV3.V3_SUB_TYPE_BUFFER;
public static final byte SUB_TYPE_UNITIALIZED = MemoryMapDBAdapterV3.V3_SUB_TYPE_UNITIALIZED;
public static final byte SUB_TYPE_FILE_BYTES = MemoryMapDBAdapterV3.V3_SUB_TYPE_FILE_BYTES;
static MemoryMapDBAdapter getAdapter(DBHandle handle, int openMode, MemoryMapDB memMap,
TaskMonitor monitor) throws VersionException, IOException {
if (openMode == DBConstants.CREATE) {
return new MemoryMapDBAdapterV2(handle, memMap, true);
return new MemoryMapDBAdapterV3(handle, memMap, Memory.GBYTE, true);
}
try {
return new MemoryMapDBAdapterV2(handle, memMap, false);
return new MemoryMapDBAdapterV3(handle, memMap, Memory.GBYTE, false);
}
catch (VersionException e) {
if (!e.isUpgradable() || openMode == DBConstants.UPDATE) {
@ -78,10 +78,17 @@ abstract class MemoryMapDBAdapter {
static MemoryMapDBAdapter findReadOnlyAdapter(DBHandle handle, MemoryMapDB memMap)
throws VersionException, IOException {
try {
return new MemoryMapDBAdapterV2(handle, memMap);
}
catch (VersionException e) {
// try next oldest version
}
try {
return new MemoryMapDBAdapterV1(handle, memMap);
}
catch (VersionException e) {
// try next oldest version
}
return new MemoryMapDBAdapterV0(handle, memMap);
}
@ -91,40 +98,34 @@ abstract class MemoryMapDBAdapter {
try {
monitor.setMessage("Upgrading Memory Blocks...");
MemoryBlockDB[] blocks = oldAdapter.getMemoryBlocks();
monitor.initialize(blocks.length * 2);
List<MemoryBlockDB> blocks = oldAdapter.getMemoryBlocks();
oldAdapter.deleteTable(handle);
MemoryMapDBAdapter newAdapter = new MemoryMapDBAdapterV2(handle, memMap, true);
for (int i = 0; i < blocks.length; i++) {
MemoryBlockDB block = blocks[i];
monitor.initialize(blocks.size() * 2);
MemoryMapDBAdapter newAdapter =
new MemoryMapDBAdapterV3(handle, memMap, Memory.GBYTE, true);
for (MemoryBlockDB block : blocks) {
MemoryBlock newBlock = null;
if (blocks[i].isInitialized()) {
if (block.isInitialized()) {
DBBuffer buf = block.getBuffer();
newBlock = newAdapter.createInitializedBlock(block.getName(), block.getStart(),
buf, MemoryBlock.READ);
buf, block.getPermissions());
}
else {
Address mappedAddress = null;
MemoryBlockType type = block.getType();
if (type == MemoryBlockType.BIT_MAPPED || type == MemoryBlockType.BYTE_MAPPED) {
mappedAddress = ((MappedMemoryBlock) block).getOverlayedMinAddress();
if (block.isMapped()) {
SourceInfo info = block.getSourceInfos().get(0);
mappedAddress = info.getMappedRange().get().getMinAddress();
}
newBlock = newAdapter.createBlock(block.getType(), block.getName(),
block.getStart(), block.getSize(), mappedAddress, false, MemoryBlock.READ);
newBlock =
newAdapter.createBlock(block.getType(), block.getName(), block.getStart(),
block.getSize(), mappedAddress, false, block.getPermissions());
}
newBlock.setComment(block.getComment());
newBlock.setSourceName(block.getSourceName());
if (block.isExecute()) {
newBlock.setExecute(true);
}
if (block.isWrite()) {
newBlock.setWrite(true);
}
if (block.isVolatile()) {
newBlock.setVolatile(true);
}
}
oldAdapter.deleteTable(handle);
newAdapter.refreshMemory();
return newAdapter;
}
@ -134,20 +135,13 @@ abstract class MemoryMapDBAdapter {
}
}
static MemoryBlockDB getMemoryBlock(MemoryMapDBAdapter adapter, Record record, DBBuffer buf,
MemoryMapDB memMap) throws IOException {
int blockType = record.getShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL);
switch (blockType) {
case INITIALIZED:
case UNINITIALIZED:
return new MemoryBlockDB(adapter, record, buf, memMap);
case BIT_MAPPED:
case BYTE_MAPPED:
return new OverlayMemoryBlockDB(adapter, record, memMap);
}
throw new IllegalStateException("Bad block type");
}
/**
* Returns a DBBuffer object for the given database buffer id
* @param bufferID the id of the first buffer in the DBBuffer.
* @return the DBBuffer for the given id.
* @throws IOException if a database IO error occurs.
*/
abstract DBBuffer getBuffer(int bufferID) throws IOException;
abstract void deleteTable(DBHandle handle) throws IOException;
@ -159,8 +153,9 @@ abstract class MemoryMapDBAdapter {
/**
* Returns an array of memory blocks sorted on start Address
* @return all the memory blocks
*/
abstract MemoryBlockDB[] getMemoryBlocks();
abstract List<MemoryBlockDB> getMemoryBlocks();
/**
* Creates a new initialized block object using data provided from an
@ -208,31 +203,12 @@ abstract class MemoryMapDBAdapter {
long length, Address mappedAddress, boolean initializeBytes, int permissions)
throws AddressOverflowException, IOException;
/**
* Splits a memory block at the given offset and create a new block at the split location.
* @param block the the split.
* @param offset the offset within the block at which to split off into a new block
* @return the new memory block created.
* @throws IOException if a database IO error occurs.
*/
abstract MemoryBlockDB splitBlock(MemoryBlockDB block, long offset) throws IOException;
/**
* Combines two memory blocks into one.
* @param block1 the first block
* @param block2 the second block
* @return the block that contains the bytes of block1 and block2.
* @throws IOException if a database IO error occurs.
*/
abstract MemoryBlockDB joinBlocks(MemoryBlockDB block1, MemoryBlockDB block2)
throws IOException;
/**
* Deletes the given memory block.
* @param block the block to delete.
* @param key the key for the memory block record
* @throws IOException if a database IO error occurs.
*/
abstract void deleteMemoryBlock(MemoryBlockDB block) throws IOException;
abstract void deleteMemoryBlock(long key) throws IOException;
/**
* Updates the memory block record.
@ -245,15 +221,74 @@ abstract class MemoryMapDBAdapter {
* Creates a new DBuffer object with the given length and initial value.
* @param length block/chunk buffer length (length limited by ChainedBuffer implementation)
* @param initialValue fill value
* @return a new DBuffer object with the given length and initial value.
* @throws IOException if a database IO error occurs.
*/
abstract DBBuffer createBuffer(int length, byte initialValue) throws IOException;
/**
* Returns a DBBuffer object for the given database buffer id
* @param bufferID the id of the first buffer in the DBBuffer.
* Returns the MemoryMap that owns this adapter.
* @return the MemoryMap that owns this adapter.
*/
abstract MemoryMapDB getMemoryMap();
/**
* Deletes the sub block record for the given key.
* @param key the record id of the sub block record to delete.
* @throws IOException if a database error occurs.
*/
abstract void deleteSubBlock(long key) throws IOException;
/**
* Updates the sub memory block record.
* @param record the record to update.
* @throws IOException if a database IO error occurs.
*/
abstract DBBuffer getBuffer(int bufferID) throws IOException;
protected abstract void updateSubBlockRecord(Record record) throws IOException;
/**
* Creates a record for a new created sub block
* @param memBlockId the id of the memory block that contains this sub block
* @param startingOffset the starting offset relative to the containing memory block where this
* sub block starts
* @param length the length of this sub block
* @param subType the type of the subBlock
* @param sourceID if the type is a buffer, then this is the buffer id. If the type is file bytes,
* then this is the FileBytes id.
* @param sourceOffset if the type is file bytes, then this is the offset into the filebytes. If
* the type is mapped, then this is the encoded mapped address.
* @return the newly created record.
* @throws IOException if a database error occurs
*/
abstract Record createSubBlockRecord(long memBlockId, long startingOffset, long length,
byte subType, int sourceID, long sourceOffset) throws IOException;
/**
* Creates a new memory block.
* @param name the name of the block
* @param startAddress the start address of the block
* @param length the length of the block
* @param permissions the permissions for the block
* @param splitBlocks the list of subBlock objects that make up this block
* @return the new MemoryBlock
* @throws IOException if a database error occurs
*/
protected abstract MemoryBlockDB createBlock(String name, Address startAddress, long length,
int permissions, List<SubMemoryBlock> splitBlocks) throws IOException;
/**
* Creates a new memory block using a FileBytes
* @param name the name of the block
* @param startAddress the start address of the block
* @param length the length of the block
* @param fileBytes the {@link FileBytes} object that provides the bytes for this block
* @param offset the offset into the {@link FileBytes} object
* @param permissions the permissions for the block
* @return the new MemoryBlock
* @throws IOException if a database error occurs
* @throws AddressOverflowException if block length is too large for the underlying space
*/
protected abstract MemoryBlockDB createFileBytesBlock(String name, Address startAddress,
long length, FileBytes fileBytes, long offset, int permissions)
throws IOException, AddressOverflowException;
}

View file

@ -17,17 +17,11 @@ package ghidra.program.database.mem;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.*;
import db.DBBuffer;
import db.DBHandle;
import db.Record;
import db.RecordIterator;
import db.Table;
import db.*;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressFactory;
import ghidra.program.model.address.SegmentedAddress;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockType;
import ghidra.util.exception.VersionException;
@ -73,12 +67,10 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
// "Block Type", "Base Address",
// "Source Block ID"});
private MemoryBlockDB[] blocks;
private List<MemoryBlockDB> blocks;
private DBHandle handle;
private MemoryMapDB memMap;
/**
* @param handle
*/
MemoryMapDBAdapterV0(DBHandle handle, MemoryMapDB memMap) throws VersionException, IOException {
this(handle, memMap, VERSION);
}
@ -86,6 +78,7 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
protected MemoryMapDBAdapterV0(DBHandle handle, MemoryMapDB memMap, int expectedVersion)
throws VersionException, IOException {
this.handle = handle;
this.memMap = memMap;
AddressMap addrMap = memMap.getAddressMap();
Table table = handle.getTable(V0_TABLE_NAME);
if (table == null) {
@ -97,10 +90,11 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
", got " + versionNumber);
}
int recCount = table.getRecordCount();
blocks = new MemoryBlockDB[recCount];
blocks = new ArrayList<MemoryBlockDB>(recCount);
AddressFactory addrFactory = memMap.getAddressFactory();
int i = 0;
int key = 0;
RecordIterator it = table.iterator();
while (it.hasNext()) {
Record rec = it.next();
@ -116,130 +110,117 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
}
Address start = addrFactory.oldGetAddressFromLong(rec.getLongValue(V0_START_ADDR_COL));
long startAddr = addrMap.getKey(start, false);
long overlayAddr = rec.getLongValue(V0_BASE_ADDR_COL);
try {
Address ov = addrFactory.oldGetAddressFromLong(overlayAddr);
overlayAddr = addrMap.getKey(ov, false);
}
catch (Exception e) {
}
long length = rec.getLongValue(V0_LENGTH_COL);
long bufID = rec.getIntValue(V0_BUFFER_ID_COL);
int segment = 0;
if (expectedVersion == 1 && (start instanceof SegmentedAddress)) {
segment = rec.getIntValue(V0_SEGMENT_COL);
// ((SegmentedAddress)start).normalize(segment);
}
// Convert to new record format
Record blockRec = MemoryMapDBAdapter.BLOCK_SCHEMA.createRecord(i);
Record blockRecord = BLOCK_SCHEMA.createRecord(key);
Record subBlockRecord = SUB_BLOCK_SCHEMA.createRecord(key);
blockRec.setString(MemoryMapDBAdapter.NAME_COL, rec.getString(V0_NAME_COL));
blockRec.setString(MemoryMapDBAdapter.COMMENTS_COL, rec.getString(V0_COMMENTS_COL));
blockRec.setString(MemoryMapDBAdapter.SOURCE_COL, rec.getString(V0_SOURCE_NAME_COL));
blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, startAddr);
blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL,
rec.getShortValue(V0_TYPE_COL));
blockRec.setLongValue(MemoryMapDBAdapter.OVERLAY_ADDR_COL, overlayAddr);
blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, rec.getLongValue(V0_LENGTH_COL));
blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL,
rec.getIntValue(V0_BUFFER_ID_COL));
blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, segment);
blockRecord.setString(NAME_COL, rec.getString(V0_NAME_COL));
blockRecord.setString(COMMENTS_COL, rec.getString(V0_COMMENTS_COL));
blockRecord.setString(SOURCE_COL, rec.getString(V0_SOURCE_NAME_COL));
blockRecord.setByteValue(PERMISSIONS_COL, (byte) permissions);
blockRecord.setLongValue(START_ADDR_COL, startAddr);
blockRecord.setLongValue(LENGTH_COL, length);
blockRecord.setIntValue(SEGMENT_COL, segment);
blocks[i++] = MemoryMapDBAdapter.getMemoryBlock(this, blockRec, null, memMap);
subBlockRecord.setLongValue(SUB_PARENT_ID_COL, key);
subBlockRecord.setLongValue(SUB_LENGTH_COL, length);
subBlockRecord.setLongValue(SUB_START_OFFSET_COL, 0);
int type = rec.getShortValue(V0_TYPE_COL);
long overlayAddr = rec.getLongValue(V0_BASE_ADDR_COL);
overlayAddr = updateOverlayAddr(addrMap, addrFactory, overlayAddr, type);
SubMemoryBlock subBlock = getSubBlock(memMap, bufID, subBlockRecord, type, overlayAddr);
blocks.add(new MemoryBlockDB(this, blockRecord, Arrays.asList(subBlock)));
}
Arrays.sort(blocks);
Collections.sort(blocks);
}
private SubMemoryBlock getSubBlock(MemoryMapDB memMap, long bufID, Record record, int type,
long overlayAddr) throws IOException {
switch (type) {
case MemoryMapDBAdapterV2.BIT_MAPPED:
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BIT_MAPPED);
record.setLongValue(MemoryMapDBAdapterV2.V2_OVERLAY_ADDR_COL, overlayAddr);
return new BitMappedSubMemoryBlock(this, record);
case MemoryMapDBAdapterV2.BYTE_MAPPED:
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BYTE_MAPPED);
record.setLongValue(MemoryMapDBAdapterV2.V2_OVERLAY_ADDR_COL, overlayAddr);
return new ByteMappedSubMemoryBlock(this, record);
case MemoryMapDBAdapterV2.INITIALIZED:
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BUFFER);
record.setLongValue(SUB_SOURCE_OFFSET_COL, bufID);
return new BufferSubMemoryBlock(this, record);
case MemoryMapDBAdapterV2.UNINITIALIZED:
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_UNITIALIZED);
return new UninitializedSubMemoryBlock(this, record);
default:
throw new IOException("Unknown memory block type: " + type);
}
}
private long updateOverlayAddr(AddressMap addrMap, AddressFactory addrFactory, long overlayAddr,
int type) {
if (type == MemoryMapDBAdapterV2.BIT_MAPPED || type == MemoryMapDBAdapterV2.BYTE_MAPPED) {
Address ov = addrFactory.oldGetAddressFromLong(overlayAddr);
overlayAddr = addrMap.getKey(ov, false);
}
return overlayAddr;
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#refreshMemory()
*/
@Override
void refreshMemory() throws IOException {
// do nothing
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#getMemoryBlocks()
*/
@Override
MemoryBlockDB[] getMemoryBlocks() {
List<MemoryBlockDB> getMemoryBlocks() {
return blocks;
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#splitBlock(ghidra.program.database.mem2.MemoryBlockDB, long)
*/
@Override
MemoryBlockDB splitBlock(MemoryBlockDB block, long offset) throws IOException {
throw new UnsupportedOperationException();
}
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is,
long length, int permissions)
throws IOException {
long length, int permissions) throws IOException {
throw new UnsupportedOperationException();
}
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf,
int permissions)
throws IOException {
int permissions) throws IOException {
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#joinBlocks(ghidra.program.database.mem2.MemoryBlockDB, ghidra.program.database.mem2.MemoryBlockDB)
*/
@Override
MemoryBlockDB joinBlocks(MemoryBlockDB block1, MemoryBlockDB block2) throws IOException {
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#setBlockSize(ghidra.program.database.mem2.MemoryBlockDB, long)
*/
void setBlockSize(MemoryBlockDB block, long size) {
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteMemoryBlock(ghidra.program.database.mem2.MemoryBlockDB)
*/
@Override
void deleteMemoryBlock(MemoryBlockDB block) throws IOException {
void deleteMemoryBlock(long key) throws IOException {
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteTable(db.DBHandle)
*/
@Override
void deleteTable(DBHandle dbHandle) throws IOException {
dbHandle.deleteTable(V0_TABLE_NAME);
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#updateBlockRecord(db.Record)
*/
@Override
void updateBlockRecord(Record record) throws IOException {
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBuffer(int, byte)
*/
@Override
DBBuffer createBuffer(int length, byte initialValue) throws IOException {
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBlock(ghidra.program.model.mem.MemoryBlockType, java.lang.String, ghidra.program.model.address.Address, long, ghidra.program.model.address.Address, boolean)
*/
@Override
MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr,
long length, Address overlayAddr, boolean initializeBytes, int permissions)
@ -247,9 +228,6 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#getBuffer(int)
*/
@Override
DBBuffer getBuffer(int bufferID) throws IOException {
if (bufferID >= 0) {
@ -258,4 +236,38 @@ class MemoryMapDBAdapterV0 extends MemoryMapDBAdapter {
return null;
}
@Override
MemoryMapDB getMemoryMap() {
return null;
}
@Override
void deleteSubBlock(long key) {
throw new UnsupportedOperationException();
}
@Override
protected void updateSubBlockRecord(Record record) throws IOException {
throw new UnsupportedOperationException();
}
@Override
Record createSubBlockRecord(long memBlockId, long startingOffset, long length, byte subType,
int sourceID, long sourceOffset) throws IOException {
throw new UnsupportedOperationException();
}
@Override
protected MemoryBlockDB createBlock(String name, Address addr, long length, int permissions,
List<SubMemoryBlock> splitBlocks) {
throw new UnsupportedOperationException();
}
@Override
protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length,
FileBytes fileBytes, long offset, int permissions)
throws IOException, AddressOverflowException {
throw new UnsupportedOperationException();
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,12 +15,14 @@
*/
package ghidra.program.database.mem;
import ghidra.util.exception.VersionException;
import java.io.IOException;
import db.DBHandle;
import ghidra.util.exception.VersionException;
/**
* Adapter for version 1
*/
class MemoryMapDBAdapterV1 extends MemoryMapDBAdapterV0 {
private final static int VERSION = 1;

View file

@ -17,266 +17,180 @@ package ghidra.program.database.mem;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.*;
import db.*;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressOverflowException;
import ghidra.program.model.mem.MemoryBlockType;
import ghidra.util.exception.IOCancelledException;
import ghidra.util.exception.VersionException;
/**
* Adapter for version 2
*/
class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter {
private static final int V2_VERSION = 2;
static final String V2_TABLE_NAME = "Memory Blocks";
private static final int VERSION = CURRENT_VERSION;
static final int V2_NAME_COL = 0;
static final int V2_COMMENTS_COL = 1;
static final int V2_SOURCE_COL = 2;
static final int V2_PERMISSIONS_COL = 3;
static final int V2_START_ADDR_COL = 4;
static final int V2_BLOCK_TYPE_COL = 5;
static final int V2_OVERLAY_ADDR_COL = 6;
static final int V2_LENGTH_COL = 7;
static final int V2_CHAIN_BUF_COL = 8;
static final int V2_SEGMENT_COL = 9;
static final int INITIALIZED = 0;
static final int UNINITIALIZED = 1;
static final int BIT_MAPPED = 2;
static final int BYTE_MAPPED = 4;
private DBHandle handle;
private Table blockTable;
private MemoryMapDB memMap;
private AddressMap addrMap;
private MemoryBlockDB[] blocks = new MemoryBlockDB[0];
MemoryMapDBAdapterV2(DBHandle handle, MemoryMapDB memMap, boolean create)
private List<MemoryBlockDB> blocks = new ArrayList<>();
// The following schema definition documents the schema used in version 2No
//
// static Schema BLOCK_SCHEMA = new Schema(CURRENT_VERSION, "Key",
// new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class,
// LongField.class, ShortField.class, LongField.class, LongField.class, IntField.class,
// IntField.class },
// new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address",
// "Block Type", "Overlay Address", "Length", "Chain Buffer ID", "Segment" });
protected MemoryMapDBAdapterV2(DBHandle handle, MemoryMapDB memMap)
throws VersionException, IOException {
this.handle = handle;
this.memMap = memMap;
this.addrMap = memMap.getAddressMap();
if (create) {
blockTable = handle.createTable(TABLE_NAME, BLOCK_SCHEMA);
Table table = handle.getTable(V2_TABLE_NAME);
if (table == null) {
throw new VersionException("Memory Block table not found");
}
else {
blockTable = handle.getTable(TABLE_NAME);
if (blockTable == null) {
throw new VersionException(
handle.getTable(MemoryMapDBAdapterV0.V0_TABLE_NAME) != null);
}
if (blockTable.getSchema().getVersion() != VERSION) {
int version = blockTable.getSchema().getVersion();
throw new VersionException(version < VERSION);
}
// refreshMemory();
int versionNumber = table.getSchema().getVersion();
if (versionNumber != V2_VERSION) {
throw new VersionException(
"Memory Block table: Expected Version " + V2_VERSION + ", got " + versionNumber);
}
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#refreshMemory()
*/
@Override
void refreshMemory() throws IOException {
MemoryBlockDB[] updatedBlocks = new MemoryBlockDB[blockTable.getRecordCount()];
RecordIterator it = blockTable.iterator();
int index = 0;
int recCount = table.getRecordCount();
blocks = new ArrayList<>(recCount);
int key = 0;
RecordIterator it = table.iterator();
while (it.hasNext()) {
Record blockRec = it.next();
long key = blockRec.getKey();
for (int n = 0; n < blocks.length; n++) {
if (blocks[n] != null && blocks[n].getID() == key) {
updatedBlocks[index] = blocks[n];
updatedBlocks[index].refresh(blockRec);
blocks[n] = null;
break;
}
}
if (updatedBlocks[index] == null) {
updatedBlocks[index] =
MemoryMapDBAdapter.getMemoryBlock(this, blockRec, null, memMap);
}
++index;
Record rec = it.next();
int permissions = rec.getByteValue(V2_PERMISSIONS_COL);
long startAddr = rec.getLongValue(V2_START_ADDR_COL);
long length = rec.getLongValue(V2_LENGTH_COL);
int bufID = rec.getIntValue(V2_CHAIN_BUF_COL);
int segment = rec.getIntValue(V2_SEGMENT_COL);
Record blockRecord = BLOCK_SCHEMA.createRecord(key);
Record subBlockRecord = SUB_BLOCK_SCHEMA.createRecord(key);
blockRecord.setString(NAME_COL, rec.getString(V2_NAME_COL));
blockRecord.setString(COMMENTS_COL, rec.getString(V2_COMMENTS_COL));
blockRecord.setString(SOURCE_COL, rec.getString(V2_SOURCE_COL));
blockRecord.setByteValue(PERMISSIONS_COL, (byte) permissions);
blockRecord.setLongValue(START_ADDR_COL, startAddr);
blockRecord.setLongValue(LENGTH_COL, length);
blockRecord.setIntValue(SEGMENT_COL, segment);
subBlockRecord.setLongValue(SUB_PARENT_ID_COL, key);
subBlockRecord.setLongValue(SUB_LENGTH_COL, length);
subBlockRecord.setLongValue(SUB_START_OFFSET_COL, 0);
int type = rec.getShortValue(V2_BLOCK_TYPE_COL);
long overlayAddr = rec.getLongValue(V2_OVERLAY_ADDR_COL);
SubMemoryBlock subBlock = getSubBlock(bufID, subBlockRecord, type, overlayAddr);
blocks.add(new MemoryBlockDB(this, blockRecord, Arrays.asList(subBlock)));
}
Collections.sort(blocks);
}
private SubMemoryBlock getSubBlock(int bufID, Record record, int type, long overlayAddr)
throws IOException {
switch (type) {
case MemoryMapDBAdapterV2.BIT_MAPPED:
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BIT_MAPPED);
record.setLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL, overlayAddr);
return new BitMappedSubMemoryBlock(this, record);
case MemoryMapDBAdapterV2.BYTE_MAPPED:
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BYTE_MAPPED);
record.setLongValue(MemoryMapDBAdapter.SUB_SOURCE_OFFSET_COL, overlayAddr);
return new ByteMappedSubMemoryBlock(this, record);
case MemoryMapDBAdapterV2.INITIALIZED:
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_BUFFER);
record.setIntValue(SUB_SOURCE_ID_COL, bufID);
return new BufferSubMemoryBlock(this, record);
case MemoryMapDBAdapterV2.UNINITIALIZED:
record.setByteValue(SUB_TYPE_COL, SUB_TYPE_UNITIALIZED);
return new UninitializedSubMemoryBlock(this, record);
default:
throw new IOException("Unknown memory block type: " + type);
}
for (int i = 0; i < blocks.length; i++) {
if (blocks[i] != null) {
blocks[i].invalidate();
}
}
Arrays.sort(updatedBlocks);
blocks = updatedBlocks;
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#getMemoryBlocks()
*/
@Override
MemoryBlockDB[] getMemoryBlocks() {
List<MemoryBlockDB> getMemoryBlocks() {
return blocks;
}
private int getSegment(Address addr) {
if (addr instanceof SegmentedAddress) {
// SegmentedAddress imageBase = (SegmentedAddress)addrMap.getImageBase();
// int baseSegment = imageBase.getSegment();
return ((SegmentedAddress) addr).getSegment();
}
return 0;
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBlock(java.lang.String, ghidra.program.model.address.Address, db.DBBuffer)
*/
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf,
int permissions) throws AddressOverflowException, IOException {
// Ensure that address key has been generated for end address
Address endAddr = startAddr.addNoWrap(buf.length() - 1);
addrMap.getKey(endAddr, true);
int blockID = (int) blockTable.getKey();
Record blockRec = BLOCK_SCHEMA.createRecord(blockID);
blockRec.setString(MemoryMapDBAdapter.NAME_COL, name);
blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(startAddr, true));
blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL, (short) INITIALIZED);
blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, buf.getId());
blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, buf.length());
blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, getSegment(startAddr));
blockTable.putRecord(blockRec);
return new MemoryBlockDB(this, blockRec, buf, memMap);
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createInitializedBlock(java.lang.String, ghidra.program.model.address.Address, java.io.InputStream, long)
*/
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is,
long length, int permissions) throws AddressOverflowException, IOException {
// Ensure that address key has been generated for end address
Address endAddr = startAddr.addNoWrap(length - 1);
addrMap.getKey(endAddr, true);
int blockID = (int) blockTable.getKey();
Record blockRec = BLOCK_SCHEMA.createRecord(blockID);
blockRec.setString(MemoryMapDBAdapter.NAME_COL, name);
blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(startAddr, true));
blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL, (short) INITIALIZED);
blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, length);
blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, getSegment(startAddr));
DBBuffer buf = createBuffer(length, is);
blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, buf.getId());
blockTable.putRecord(blockRec);
return MemoryMapDBAdapter.getMemoryBlock(this, blockRec, buf, memMap);
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBlock(int, java.lang.String, ghidra.program.database.mem2.MemoryChunkDB[], ghidra.program.model.address.Address)
*/
@Override
MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr,
long length, Address mappedAddress, boolean initializeBytes, int permissions)
throws AddressOverflowException, IOException {
if (initializeBytes) {
return createInitializedBlock(name, startAddr, null, length, permissions);
}
// Ensure that address key has been generated for end address
Address endAddr = startAddr.addNoWrap(length - 1);
addrMap.getKey(endAddr, true);
int blockID = (int) blockTable.getKey();
Record blockRec = BLOCK_SCHEMA.createRecord(blockID);
blockRec.setString(MemoryMapDBAdapter.NAME_COL, name);
blockRec.setByteValue(MemoryMapDBAdapter.PERMISSIONS_COL, (byte) permissions);
blockRec.setLongValue(MemoryMapDBAdapter.START_ADDR_COL, addrMap.getKey(startAddr, true));
blockRec.setShortValue(MemoryMapDBAdapter.BLOCK_TYPE_COL,
(short) encodeBlockType(blockType));
blockRec.setLongValue(MemoryMapDBAdapter.LENGTH_COL, length);
blockRec.setIntValue(MemoryMapDBAdapter.SEGMENT_COL, getSegment(startAddr));
blockRec.setIntValue(MemoryMapDBAdapter.CHAIN_BUF_COL, -1);
if (mappedAddress != null) {
blockRec.setLongValue(MemoryMapDBAdapter.OVERLAY_ADDR_COL,
addrMap.getKey(mappedAddress, true));
}
blockTable.putRecord(blockRec);
return MemoryMapDBAdapter.getMemoryBlock(this, blockRec, null, memMap);
}
private int encodeBlockType(MemoryBlockType blockType) {
if (blockType == MemoryBlockType.BIT_MAPPED) {
return BIT_MAPPED;
}
if (blockType == MemoryBlockType.BYTE_MAPPED) {
return BYTE_MAPPED;
}
return UNINITIALIZED;
}
private DBBuffer createBuffer(long length, InputStream is) throws IOException {
DBBuffer buf = handle.createBuffer((int) length);
if (is != null) {
try {
buf.fill(is);
}
catch (IOCancelledException e) {
buf.delete();
throw e;
}
}
return buf;
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#splitBlock(ghidra.program.database.mem2.MemoryBlockDB, long)
*/
@Override
MemoryBlockDB splitBlock(MemoryBlockDB block, long offset) throws IOException {
// TODO Auto-generated method stub
return null;
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#joinBlocks(ghidra.program.database.mem2.MemoryBlockDB, ghidra.program.database.mem2.MemoryBlockDB)
*/
@Override
MemoryBlockDB joinBlocks(MemoryBlockDB block1, MemoryBlockDB block2) throws IOException {
// TODO Auto-generated method stub
return null;
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteMemoryBlock(ghidra.program.model.mem.MemoryBlock)
*/
@Override
void deleteMemoryBlock(MemoryBlockDB block) throws IOException {
blockTable.deleteRecord(block.getID());
block.invalidate();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#deleteTable(db.DBHandle)
*/
@Override
void deleteTable(DBHandle dbHandle) throws IOException {
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#updateBlockRecord(db.Record)
*/
@Override
void deleteMemoryBlock(long key) throws IOException {
throw new UnsupportedOperationException();
}
@Override
void deleteTable(DBHandle dbHandle) throws IOException {
dbHandle.deleteTable(V2_TABLE_NAME);
}
@Override
void updateBlockRecord(Record record) throws IOException {
blockTable.putRecord(record);
throw new UnsupportedOperationException();
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#createBuffer(int, byte)
*/
@Override
DBBuffer createBuffer(int length, byte initialValue) throws IOException {
DBBuffer buffer = handle.createBuffer(length);
buffer.fill(0, length - 1, initialValue);
return buffer;
throw new UnsupportedOperationException();
}
@Override
void refreshMemory() throws IOException {
// do nothing
}
/**
* @see ghidra.program.database.mem.MemoryMapDBAdapter#getBuffer(int)
*/
@Override
DBBuffer getBuffer(int bufferID) throws IOException {
if (bufferID >= 0) {
@ -285,4 +199,38 @@ class MemoryMapDBAdapterV2 extends MemoryMapDBAdapter {
return null;
}
@Override
MemoryMapDB getMemoryMap() {
return memMap;
}
@Override
void deleteSubBlock(long key) {
throw new UnsupportedOperationException();
}
@Override
protected void updateSubBlockRecord(Record record) throws IOException {
throw new UnsupportedOperationException();
}
@Override
Record createSubBlockRecord(long memBlockId, long startingOffset, long length, byte subType,
int sourceID, long sourceOffset) throws IOException {
throw new UnsupportedOperationException();
}
@Override
protected MemoryBlockDB createBlock(String name, Address addr, long length, int permissions,
List<SubMemoryBlock> splitBlocks) {
throw new UnsupportedOperationException();
}
@Override
protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length,
FileBytes fileBytes, long offset, int permissions)
throws IOException, AddressOverflowException {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,447 @@
/* ###
* 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.io.InputStream;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import db.*;
import ghidra.program.database.map.AddressMapDB;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.MemoryBlockType;
import ghidra.util.exception.*;
/**
* MemoryMap adapter for version 3.
* This version introduces the concept of sub memory blocks and FileBytes
*/
public class MemoryMapDBAdapterV3 extends MemoryMapDBAdapter {
public static final int V3_VERSION = 3;
public static final String TABLE_NAME = "Memory Blocks";
public static final String SUB_BLOCK_TABLE_NAME = "Sub Memory Blocks";
public static final int V3_NAME_COL = 0;
public static final int V3_COMMENTS_COL = 1;
public static final int V3_SOURCE_COL = 2;
public static final int V3_PERMISSIONS_COL = 3;
public static final int V3_START_ADDR_COL = 4;
public static final int V3_LENGTH_COL = 5;
public static final int V3_SEGMENT_COL = 6;
public static final int V3_SUB_PARENT_ID_COL = 0;
public static final int V3_SUB_TYPE_COL = 1;
public static final int V3_SUB_LENGTH_COL = 2;
public static final int V3_SUB_START_OFFSET_COL = 3;
public static final int V3_SUB_SOURCE_ID_COL = 4;
public static final int V3_SUB_SOURCE_OFFSET_COL = 5;
public static final byte V3_SUB_TYPE_BIT_MAPPED = 0;
public static final byte V3_SUB_TYPE_BYTE_MAPPED = 1;
public static final byte V3_SUB_TYPE_BUFFER = 2;
public static final byte V3_SUB_TYPE_UNITIALIZED = 3;
public static final byte V3_SUB_TYPE_FILE_BYTES = 4;
static Schema V3_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key",
new Class[] { StringField.class, StringField.class, StringField.class, ByteField.class,
LongField.class, LongField.class, IntField.class },
new String[] { "Name", "Comments", "Source Name", "Permissions", "Start Address", "Length",
"Segment" });
static Schema V3_SUB_BLOCK_SCHEMA = new Schema(V3_VERSION, "Key",
new Class[] { LongField.class, ByteField.class, LongField.class, LongField.class,
IntField.class, LongField.class },
new String[] { "Parent ID", "Type", "Length", "Starting Offset", "Source ID",
"Source Address/Offset" });
private DBHandle handle;
private Table memBlockTable;
private Table subBlockTable;
private MemoryMapDB memMap;
private AddressMapDB addrMap;
private List<MemoryBlockDB> memoryBlocks = new ArrayList<>();
private long maxSubBlockSize;
public MemoryMapDBAdapterV3(DBHandle handle, MemoryMapDB memMap, long maxSubBlockSize,
boolean create) throws VersionException, IOException {
this.handle = handle;
this.memMap = memMap;
this.maxSubBlockSize = maxSubBlockSize;
this.addrMap = memMap.getAddressMap();
if (create) {
memBlockTable = handle.createTable(TABLE_NAME, V3_BLOCK_SCHEMA);
subBlockTable = handle.createTable(SUB_BLOCK_TABLE_NAME, V3_SUB_BLOCK_SCHEMA);
}
else {
memBlockTable = handle.getTable(TABLE_NAME);
subBlockTable = handle.getTable(SUB_BLOCK_TABLE_NAME);
if (memBlockTable == null) {
// the table name changed going from V1 to V2
throw new VersionException(
handle.getTable(MemoryMapDBAdapterV0.V0_TABLE_NAME) != null);
}
if (subBlockTable == null || memBlockTable.getSchema().getVersion() != V3_VERSION) {
int version = memBlockTable.getSchema().getVersion();
throw new VersionException(version < V3_VERSION);
}
}
}
@Override
void deleteTable(DBHandle dbHandle) throws IOException {
throw new UnsupportedOperationException();
}
@Override
void refreshMemory() throws IOException {
Map<Long, List<SubMemoryBlock>> subBlockMap = getSubBlockMap();
Map<Long, MemoryBlockDB> blockMap = memoryBlocks.stream().collect(
Collectors.toMap(MemoryBlockDB::getID, Function.identity()));
List<MemoryBlockDB> newBlocks = new ArrayList<>();
RecordIterator it = memBlockTable.iterator();
while (it.hasNext()) {
Record record = it.next();
long key = record.getKey();
MemoryBlockDB block = blockMap.remove(key);
if (block != null) {
block.refresh(record, subBlockMap.get(key));
}
else {
block = new MemoryBlockDB(this, record, subBlockMap.get(key));
}
newBlocks.add(block);
}
for (MemoryBlockDB block : blockMap.values()) {
block.invalidate();
}
Collections.sort(newBlocks);
memoryBlocks = newBlocks;
}
@Override
List<MemoryBlockDB> getMemoryBlocks() {
return memoryBlocks;
}
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, InputStream is,
long length, int permissions) throws AddressOverflowException, IOException {
updateAddressMapForAllAddresses(startAddr, length);
List<SubMemoryBlock> subBlocks = new ArrayList<>();
try {
Record blockRecord = createMemoryBlockRecord(name, startAddr, length, permissions);
long key = blockRecord.getKey();
int numFullBlocks = (int) (length / maxSubBlockSize);
int lastSubBlockSize = (int) (length % maxSubBlockSize);
long blockOffset = 0;
for (int i = 0; i < numFullBlocks; i++) {
subBlocks.add(createBufferSubBlock(key, blockOffset, maxSubBlockSize, is));
blockOffset += maxSubBlockSize;
}
if (lastSubBlockSize > 0) {
subBlocks.add(createBufferSubBlock(key, blockOffset, lastSubBlockSize, is));
}
memBlockTable.putRecord(blockRecord);
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
memoryBlocks.add(newBlock);
Collections.sort(memoryBlocks);
return newBlock;
}
catch (IOException e) {
// clean up any created DBBufferss
for (SubMemoryBlock subMemoryBlock : subBlocks) {
BufferSubMemoryBlock bufferSubMemoryBlock = (BufferSubMemoryBlock) subMemoryBlock;
subBlockTable.deleteRecord(bufferSubMemoryBlock.getKey());
bufferSubMemoryBlock.buf.delete();
}
throw e;
}
}
@Override
MemoryBlockDB createBlock(MemoryBlockType blockType, String name, Address startAddr,
long length, Address mappedAddress, boolean initializeBytes, int permissions)
throws AddressOverflowException, IOException {
if (initializeBytes) {
return createInitializedBlock(name, startAddr, null, length, permissions);
}
else if (blockType == MemoryBlockType.BIT_MAPPED) {
return createBitMappedBlock(name, startAddr, length, mappedAddress, permissions);
}
else if (blockType == MemoryBlockType.BYTE_MAPPED) {
return createByteMappedBlock(name, startAddr, length, mappedAddress, permissions);
}
return createUnitializedBlock(name, startAddr, length, permissions);
}
@Override
MemoryBlockDB createInitializedBlock(String name, Address startAddr, DBBuffer buf,
int permissions) throws AddressOverflowException, IOException {
updateAddressMapForAllAddresses(startAddr, buf.length());
List<SubMemoryBlock> subBlocks = new ArrayList<>();
Record blockRecord = createMemoryBlockRecord(name, startAddr, buf.length(), permissions);
long key = blockRecord.getKey();
Record subRecord =
createSubBlockRecord(key, 0, buf.length(), V3_SUB_TYPE_BUFFER, buf.getId(), 0);
subBlockTable.putRecord(subRecord);
subBlocks.add(new BufferSubMemoryBlock(this, subRecord));
memBlockTable.putRecord(blockRecord);
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
memoryBlocks.add(newBlock);
Collections.sort(memoryBlocks);
return newBlock;
}
MemoryBlockDB createUnitializedBlock(String name, Address startAddress, long length,
int permissions) throws IOException, AddressOverflowException {
updateAddressMapForAllAddresses(startAddress, length);
List<SubMemoryBlock> subBlocks = new ArrayList<>();
Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
long key = blockRecord.getKey();
Record subRecord = createSubBlockRecord(key, 0, length, V3_SUB_TYPE_UNITIALIZED, 0, 0);
subBlocks.add(new UninitializedSubMemoryBlock(this, subRecord));
memBlockTable.putRecord(blockRecord);
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
memoryBlocks.add(newBlock);
Collections.sort(memoryBlocks);
return newBlock;
}
@Override
protected MemoryBlockDB createBlock(String name, Address startAddress, long length,
int permissions, List<SubMemoryBlock> splitBlocks) throws IOException {
Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
long key = blockRecord.getKey();
long startingOffset = 0;
for (SubMemoryBlock subMemoryBlock : splitBlocks) {
subMemoryBlock.setParentIdAndStartingOffset(key, startingOffset);
startingOffset += subMemoryBlock.length;
}
memBlockTable.putRecord(blockRecord);
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, splitBlocks);
int insertionIndex = Collections.binarySearch(memoryBlocks, newBlock);
if (insertionIndex >= 0) { // should not find direct hit
throw new AssertException("New memory block collides with existing block");
}
memoryBlocks.add(-insertionIndex - 1, newBlock);
return newBlock;
}
MemoryBlockDB createBitMappedBlock(String name, Address startAddress, long length,
Address mappedAddress, int permissions) throws IOException, AddressOverflowException {
return createMappedBlock(V3_SUB_TYPE_BIT_MAPPED, name, startAddress, length, mappedAddress,
permissions);
}
MemoryBlockDB createByteMappedBlock(String name, Address startAddress, long length,
Address mappedAddress, int permissions) throws IOException, AddressOverflowException {
return createMappedBlock(V3_SUB_TYPE_BYTE_MAPPED, name, startAddress, length, mappedAddress,
permissions);
}
@Override
protected MemoryBlockDB createFileBytesBlock(String name, Address startAddress, long length,
FileBytes fileBytes, long offset, int permissions)
throws IOException, AddressOverflowException {
updateAddressMapForAllAddresses(startAddress, length);
List<SubMemoryBlock> subBlocks = new ArrayList<>();
Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
long key = blockRecord.getKey();
Record subRecord = createSubBlockRecord(key, 0, length, V3_SUB_TYPE_FILE_BYTES,
(int) fileBytes.getId(), offset);
subBlocks.add(createSubBlock(subRecord));
memBlockTable.putRecord(blockRecord);
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
memoryBlocks.add(newBlock);
Collections.sort(memoryBlocks);
return newBlock;
}
private MemoryBlockDB createMappedBlock(byte type, String name, Address startAddress,
long length, Address mappedAddress, int permissions)
throws IOException, AddressOverflowException {
updateAddressMapForAllAddresses(startAddress, length);
List<SubMemoryBlock> subBlocks = new ArrayList<>();
Record blockRecord = createMemoryBlockRecord(name, startAddress, length, permissions);
long key = blockRecord.getKey();
long encoded = addrMap.getKey(mappedAddress, true);
Record subRecord = createSubBlockRecord(key, 0, length, type, 0, encoded);
subBlocks.add(createSubBlock(subRecord));
memBlockTable.putRecord(blockRecord);
MemoryBlockDB newBlock = new MemoryBlockDB(this, blockRecord, subBlocks);
memoryBlocks.add(newBlock);
Collections.sort(memoryBlocks);
return newBlock;
}
@Override
void deleteMemoryBlock(long key) throws IOException {
memBlockTable.deleteRecord(key);
}
@Override
void deleteSubBlock(long key) throws IOException {
subBlockTable.deleteRecord(key);
}
@Override
void updateBlockRecord(Record record) throws IOException {
memBlockTable.putRecord(record);
}
@Override
protected void updateSubBlockRecord(Record record) throws IOException {
subBlockTable.putRecord(record);
}
@Override
DBBuffer getBuffer(int bufferID) throws IOException {
if (bufferID >= 0) {
return handle.getBuffer(bufferID);
}
return null;
}
@Override
MemoryMapDB getMemoryMap() {
return memMap;
}
@Override
Record createSubBlockRecord(long parentKey, long startingOffset, long length, byte type,
int sourceId, long sourceOffset) throws IOException {
Record record = V3_SUB_BLOCK_SCHEMA.createRecord(subBlockTable.getKey());
record.setLongValue(V3_SUB_PARENT_ID_COL, parentKey);
record.setByteValue(V3_SUB_TYPE_COL, type);
record.setLongValue(V3_SUB_LENGTH_COL, length);
record.setLongValue(V3_SUB_START_OFFSET_COL, startingOffset);
record.setIntValue(V3_SUB_SOURCE_ID_COL, sourceId);
record.setLongValue(V3_SUB_SOURCE_OFFSET_COL, sourceOffset);
subBlockTable.putRecord(record);
return record;
}
private Record createMemoryBlockRecord(String name, Address startAddr, long length,
int permissions) {
Record record = V3_BLOCK_SCHEMA.createRecord(memBlockTable.getKey());
record.setString(V3_NAME_COL, name);
record.setLongValue(V3_START_ADDR_COL, addrMap.getKey(startAddr, true));
record.setLongValue(V3_LENGTH_COL, length);
record.setByteValue(V3_PERMISSIONS_COL, (byte) permissions);
record.setIntValue(V3_SEGMENT_COL, getSegment(startAddr));
return record;
}
private Map<Long, List<SubMemoryBlock>> getSubBlockMap() throws IOException {
List<SubMemoryBlock> subBlocks = new ArrayList<>(subBlockTable.getRecordCount());
RecordIterator it = subBlockTable.iterator();
while (it.hasNext()) {
Record record = it.next();
subBlocks.add(createSubBlock(record));
}
return subBlocks.stream().collect(Collectors.groupingBy(SubMemoryBlock::getParentBlockID));
}
private int getSegment(Address addr) {
if (addr instanceof SegmentedAddress) {
return ((SegmentedAddress) addr).getSegment();
}
return 0;
}
private void updateAddressMapForAllAddresses(Address startAddress, long length)
throws AddressOverflowException {
AddressSet set = new AddressSet(startAddress, startAddress.addNoWrap(length - 1));
addrMap.getKeyRanges(set, true);
}
private SubMemoryBlock createSubBlock(Record record) throws IOException {
byte byteValue = record.getByteValue(V3_SUB_TYPE_COL);
switch (byteValue) {
case V3_SUB_TYPE_BIT_MAPPED:
return new BitMappedSubMemoryBlock(this, record);
case V3_SUB_TYPE_BYTE_MAPPED:
return new ByteMappedSubMemoryBlock(this, record);
case V3_SUB_TYPE_BUFFER:
return new BufferSubMemoryBlock(this, record);
case V3_SUB_TYPE_UNITIALIZED:
return new UninitializedSubMemoryBlock(this, record);
case V3_SUB_TYPE_FILE_BYTES:
return new FileBytesSubMemoryBlock(this, record);
default:
throw new AssertException("Unhandled sub block type: " + byteValue);
}
}
private SubMemoryBlock createBufferSubBlock(long parentKey, long offset, long length,
InputStream is) throws IOException {
DBBuffer buffer = createBuffer(length, is);
Record record =
createSubBlockRecord(parentKey, offset, length, V3_SUB_TYPE_BUFFER, buffer.getId(), 0);
return new BufferSubMemoryBlock(this, record);
}
private DBBuffer createBuffer(long length, InputStream is) throws IOException {
DBBuffer buf = handle.createBuffer((int) length);
if (is != null) {
try {
buf.fill(is);
}
catch (IOCancelledException e) {
buf.delete();
throw e;
}
}
return buf;
}
@Override
DBBuffer createBuffer(int length, byte initialValue) throws IOException {
DBBuffer buffer = handle.createBuffer(length);
buffer.fill(0, length - 1, initialValue);
return buffer;
}
}

View file

@ -1,261 +0,0 @@
/* ###
* 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 db.Record;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.*;
/**
* Class to handle memory blocks that are "overlayed" on other blocks. In other words, this
* block just maps request for bytes from one address to another. It also handles bit
* overlay blocks, in which case bit value requests are translated into a byte/bit into another
* block.
*/
class OverlayMemoryBlockDB extends MemoryBlockDB implements MappedMemoryBlock {
// ioPending is flag used to prevent cyclic memory access. Since this memory
// block uses memory to resolve its reads and writes, we have to be careful that
// some other block that this block uses, doesn't also use this block in return.
private boolean ioPending;
private static final MemoryAccessException IOPENDING_EXCEPTION =
new MemoryAccessException("Cyclic Access");
private static final MemoryAccessException MEMORY_ACCESS_EXCEPTION =
new MemoryAccessException("No memory at address");
private Address overlayStart;
private Address overlayEnd;
private boolean bitOverlay;
/**
* Constructs a new OverlayMemoryBlockDB
* @param adapter the memory block database adapter
* @param record the record for this block
* @param memMap the memory map manager
* @param bitOverlay if true this is a bit overlay memory.
* @throws IOException if a database IO error occurs.
*/
OverlayMemoryBlockDB(MemoryMapDBAdapter adapter, Record record, MemoryMapDB memMap)
throws IOException {
super(adapter, record, null, memMap);
}
@Override
void refresh(Record lRecord) throws IOException {
super.refresh(lRecord);
this.bitOverlay = blockType == MemoryBlockType.BIT_MAPPED;
long base = record.getLongValue(MemoryMapDBAdapter.OVERLAY_ADDR_COL);
overlayStart = addrMap.decodeAddress(base);
try {
overlayEnd = overlayStart.addNoWrap((bitOverlay ? (length - 1) / 8 : (length - 1)));
}
catch (AddressOverflowException e) {
throw new RuntimeException("Overlay range extends beyond address space");
}
}
/**
* @see ghidra.program.model.mem.MappedMemoryBlock#getOverlayedMinAddress()
*/
@Override
public Address getOverlayedMinAddress() {
return overlayStart;
}
@Override
Address getOverlayAddress(long offset) {
return overlayStart.add(bitOverlay ? offset / 8 : offset);
}
private byte getBitOverlayByte(long blockOffset)
throws AddressOverflowException, MemoryAccessException {
Address otherAddr = overlayStart.addNoWrap(blockOffset / 8);
byte b = memMap.getByte(otherAddr);
return (byte) ((b >> (blockOffset % 8)) & 0x01);
}
/**
* @see ghidra.program.model.mem.MemoryBlock#getByte(ghidra.program.model.address.Address)
*/
@Override
public byte getByte(Address addr) throws MemoryAccessException {
memMap.lock.acquire();
try {
checkValid();
if (ioPending) {
throw IOPENDING_EXCEPTION;
}
ioPending = true;
long offset = getBlockOffset(addr);
if (bitOverlay) {
return getBitOverlayByte(offset);
}
return memMap.getByte(overlayStart.addNoWrap(offset));
}
catch (AddressOverflowException e) {
throw MEMORY_ACCESS_EXCEPTION;
}
finally {
ioPending = false;
memMap.lock.release();
}
}
/**
* @see ghidra.program.model.mem.MemoryBlock#getBytes(ghidra.program.model.address.Address, byte, int, int)
*/
@Override
public int getBytes(Address addr, byte[] bytes, int off, int len) throws MemoryAccessException {
memMap.lock.acquire();
try {
checkValid();
if (ioPending) {
throw IOPENDING_EXCEPTION;
}
ioPending = true;
long offset = getBlockOffset(addr);
long size = getSize();
if (len > size - (addr.subtract(startAddress))) {
len = (int) (size - addr.subtract(startAddress));
}
if (bitOverlay) {
for (int i = 0; i < len; i++) {
bytes[i + off] = getBitOverlayByte(offset++);
}
return len;
}
return memMap.getBytes(overlayStart.addNoWrap(offset), bytes, off, len);
}
catch (AddressOverflowException e) {
throw MEMORY_ACCESS_EXCEPTION;
}
finally {
ioPending = false;
memMap.lock.release();
}
}
/**
* @see ghidra.program.model.mem.MemoryBlock#putByte(ghidra.program.model.address.Address, byte)
*/
@Override
public void putByte(Address addr, byte b) throws MemoryAccessException {
memMap.lock.acquire();
try {
checkValid();
if (ioPending) {
throw IOPENDING_EXCEPTION;
}
ioPending = true;
long offset = getBlockOffset(addr);
if (bitOverlay) {
checkValid();
doPutByte(overlayStart.add(offset / 8), (int) (offset % 8), b);
}
else {
memMap.setByte(overlayStart.add(offset), b);
}
}
finally {
ioPending = false;
memMap.lock.release();
}
}
/**
* @see ghidra.program.model.mem.MemoryBlock#putBytes(ghidra.program.model.address.Address, byte, int, int)
*/
@Override
public int putBytes(Address addr, byte[] b, int off, int len) throws MemoryAccessException {
memMap.lock.acquire();
try {
checkValid();
if (ioPending) {
throw IOPENDING_EXCEPTION;
}
ioPending = true;
long offset = getBlockOffset(addr);
long size = getSize();
if (len > size - (addr.subtract(startAddress))) {
len = (int) (size - addr.subtract(startAddress));
}
if (bitOverlay) {
for (int i = 0; i < len; i++) {
doPutByte(overlayStart.add(offset / 8), (int) (offset % 8), b[off + i]);
addr = addr.add(1);
offset++;
}
}
else {
memMap.setBytes(overlayStart.add(offset), b, off, len);
}
return len;
}
finally {
ioPending = false;
memMap.lock.release();
}
}
private void doPutByte(Address addr, int bitIndex, byte b) throws MemoryAccessException {
ioPending = true;
byte value = memMap.getByte(addr);
int mask = 1 << (bitIndex % 8);
if (b == 0) {
value &= ~mask;
}
else {
value |= mask;
}
memMap.setByte(addr, value);
}
// void dataChanged(Address addr, int cnt) {
// Address endAddr = addr.addWrap(cnt);
// if (addr.compareTo(overlayEnd) > 0 || endAddr.compareTo(overlayStart) < 0) {
// return;
// }
// if (ioPending) {
// return;
// }
// try {
// ioPending = true;
// Address minAddr = addr.compareTo(overlayStart) > 0 ? addr : overlayStart;
// Address maxAddr = endAddr.compareTo(overlayEnd) < 0 ? endAddr : overlayEnd;
// Address myStartAddr =
// startAddress.add(minAddr.subtract(overlayStart) * (bitOverlay ? 8 : 1));
// Address myEndAddr =
// startAddress.add(maxAddr.subtract(overlayStart) * (bitOverlay ? 8 : 1));
// memMap.fireBytesChanged(myStartAddr, (int) myEndAddr.subtract(myStartAddr) + 1);
// }
// finally {
// ioPending = false;
// }
// }
@Override
public boolean isMapped() {
return true;
}
@Override
public AddressRange getOverlayedAddressRange() {
return new AddressRangeImpl(overlayStart, overlayEnd);
}
}

View file

@ -0,0 +1,117 @@
/* ###
* 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.util.Optional;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.mem.MemoryBlock;
/**
* Class for describing the source of bytes for a memory block.
*/
public class SourceInfo {
final MemoryBlock block;
final SubMemoryBlock subBlock;
SourceInfo(MemoryBlock block, SubMemoryBlock subBlock) {
this.block = block;
this.subBlock = subBlock;
}
/**
* Returns the length of this block byte source.
* @return the length of this block byte source.
*/
public long getLength() {
return subBlock.length;
}
/**
* Returns the start address where this byte source is mapped.
* @return the start address where this byte source is mapped.
*/
public Address getMinAddress() {
return block.getStart().add(subBlock.startingOffset);
}
/**
* Returns the end address where this byte source is mapped.
* @return the end address where this byte source is mapped.
*/
public Address getMaxAddress() {
return block.getStart().add(subBlock.startingOffset + subBlock.length - 1);
}
/**
* Returns a description of this SourceInfo object.
* @return a description of this SourceInfo object.
*/
public String getDescription() {
return subBlock.getDescription();
}
@Override
public String toString() {
return getClass().getSimpleName() + ": StartAddress = " + getMinAddress() + ", length = " +
getLength();
}
/**
* Returns an {@link Optional} {@link FileBytes} object if a FileBytes object is the byte
* source for this SourceInfo. Otherwise, the Optional will be empty.
* @return the {@link FileBytes} object if it is the byte source for this section
*/
public Optional<FileBytes> getFileBytes() {
if (subBlock instanceof FileBytesSubMemoryBlock) {
return Optional.of(((FileBytesSubMemoryBlock) subBlock).getFileBytes());
}
return Optional.empty();
}
/**
* Returns the offset into the {@link FileBytes} object where this section starts getting its bytes or
* -1 if this SourceInfo does not have an associated {@link FileBytes}
* @return the offset into the {@link FileBytes} object where this section starts getting its bytes.
*/
public long getFileBytesOffset() {
if (subBlock instanceof FileBytesSubMemoryBlock) {
return ((FileBytesSubMemoryBlock) subBlock).getFileBytesOffset();
}
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.
* @return an {@link Optional} {@link AddressRange} for the mapped addresses if this is mapped
* memory block
*/
public Optional<AddressRange> getMappedRange() {
if (subBlock instanceof BitMappedSubMemoryBlock) {
BitMappedSubMemoryBlock bitMapped = (BitMappedSubMemoryBlock) subBlock;
return Optional.of(bitMapped.getMappedRange());
}
if (subBlock instanceof ByteMappedSubMemoryBlock) {
ByteMappedSubMemoryBlock byteMapped = (ByteMappedSubMemoryBlock) subBlock;
return Optional.of(byteMapped.getMappedRange());
}
return Optional.empty();
}
}

View file

@ -0,0 +1,251 @@
/* ###
* 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 db.Record;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.program.model.mem.*;
/**
* Interface for the various types of memory block sections. They are used by a {@link MemoryBlockDB}
* to do the actual storing and fetching of the bytes that make up a MemoryBlock
*/
abstract class SubMemoryBlock {
protected final MemoryMapDBAdapter adapter;
protected final Record record;
protected long length;
protected long startingOffset;
protected SubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
this.adapter = adapter;
this.record = record;
this.startingOffset = record.getLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL);
this.length = record.getLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL);
}
/**
* Return whether this block has been initialized (has byte values)
*
* @return true if the block has associated byte values.
*/
public abstract boolean isInitialized();
/**
* Returns the id of the MemoryBlockDB object that owns this sub block.
* @return the id of the MemoryBlockDB object that owns this sub block.
*/
public final long getParentBlockID() {
return record.getLongValue(MemoryMapDBAdapter.SUB_PARENT_ID_COL);
}
/**
* Returns the starting offset for this sub block. In other words, the first byte in this sub
* block is at this starting offset relative to the containing {@link MemoryBlockDB}
*
* @return the starting offset for this sub block.
*/
public final long getStartingOffset() {
return startingOffset;
}
/**
* Returns the length of this sub block
* @return the length of this sub block
*/
public final long getLength() {
return length;
}
/**
* Returns true if the given {@link MemoryBlockDB} offset is in this sub block.
*
* @param memBlockOffset the offset relative to the containing {@link MemoryBlockDB}
* @return true if the offset is valid for this block
*/
public final boolean contains(long memBlockOffset) {
return memBlockOffset >= startingOffset && memBlockOffset < startingOffset + length;
}
/**
* Returns the byte in this sub block corresponding to the given offset relative to the containing
* {@link MemoryBlockDB}. In other words, the first byte in this sub block can be retrieved
* using an offset equal to this blocks starting offset.
*
* @param memBlockOffset the offset from the start of the containing {@link MemoryBlockDB}
* @return the byte at the given containing block offset.
* @throws MemoryAccessException if the block is uninitialized.
* @throws IOException if there is a problem reading from the database
*/
public abstract byte getByte(long memBlockOffset) throws MemoryAccessException, IOException;
/**
* Tries to get len bytes from this block at the given offset (relative to the containing
* {@link MemoryBlockDB} and put them into the given byte array at the specified offset.
* May return fewer bytes if the requested length is beyond the end of the block.
* @param memBlockOffset the offset relative to the containing {@link MemoryBlockDB}
* @param b the byte array to populate.
* @param off the offset into the byte array.
* @param len the number of bytes to get.
* @return the number of bytes actually populated.
* @throws MemoryAccessException if any of the requested bytes are
* uninitialized.
* @throws IOException if there is a problem reading from the database
* @throws IllegalArgumentException if the offset is not in this block.
*/
public abstract int getBytes(long memBlockOffset, byte[] b, int off, int len)
throws MemoryAccessException, IOException;
/**
* Stores the byte in this sub block at the given offset relative to the containing
* {@link MemoryBlockDB}. In other words, the first byte in this sub block can be targeted
* using an offset equal to this blocks starting offset.
*
* @param memBlockOffset the offset from the start of the containing {@link MemoryBlockDB}
* @param b the byte value to store at the given offset.
* @throws MemoryAccessException if the block is uninitialized
* @throws IOException if there is a problem writing to the database
* @throws IllegalArgumentException if the offset is not in this block.
*/
public abstract void putByte(long memBlockOffset, byte b)
throws MemoryAccessException, IOException;
/**
* Tries to write len bytes to this block at the given offset (relative to the containing
* {@link MemoryBlockDB} using the bytes contained in the given byte array at the specified byte
* array offset.
* May write fewer bytes if the requested length is beyond the end of the block.
*
* @param memBlockOffset the offset relative to the containing {@link MemoryBlockDB}
* @param b the byte array with the bytes to store.
* @param off the offset into the byte array.
* @param len the number of bytes to write.
* @return the number of bytes actually written
* @throws MemoryAccessException if this block is uninitialized.
* @throws IOException if there is a problem writing to the database
* @throws IllegalArgumentException if the offset is not in this block.
*/
public abstract int putBytes(long memBlockOffset, byte[] b, int off, int len)
throws MemoryAccessException, IOException;
/**
* Deletes this SumMemoryBlock
* @throws IOException if a database error occurs
*/
public void delete() throws IOException {
adapter.deleteSubBlock(record.getKey());
}
/**
* Sets the length of a subblock (Used by the split command)
* @param length the new length of the block
* @throws IOException if a database error occurs
*/
protected void setLength(long length) throws IOException {
this.length = length;
record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
adapter.updateSubBlockRecord(record);
}
/**
* Attempts to join the given SubMemoryBlock with this block if possible
*
* @param other the SubMemoryBlock to join with this one.
* @return true if the given SubMemoryBlock was successfully merged into this one
* @throws IOException if a database error occurs.
*/
protected abstract boolean join(SubMemoryBlock other) throws IOException;
/**
* Returns true if this is either a bit-mapped or byte-mapped block.
*
* @return true if this is either a bit-mapped or byte-mapped block.
*/
protected boolean isMapped() {
return false;
}
/**
* Get the {@link MemoryBlockType} for this block: TYPE_DEFAULT, TYPE_OVERLAY, TYPE_BIT_MAPPED, or TYPE_BYTE_MAPPED
*
* @return the type for this block: TYPE_DEFAULT, TYPE_OVERLAY, TYPE_BIT_MAPPED, or TYPE_BYTE_MAPPED
*/
protected abstract MemoryBlockType getType();
/**
* Returns the {@link SourceInfo} object for this SubMemoryBlock
* @param block the {@link MemoryBlock} that this block belongs to.
* @return the {@link SourceInfo} object for this SubMemoryBlock
*/
protected final SourceInfo getSourceInfo(MemoryBlock block) {
return new SourceInfo(block, this);
}
/**
* Splits this SubMemoryBlock into two memory blocks
* @param memBlockOffset the offset relative to the owning MemoryBlock (not this SubMemoryBlock)
* To get the offset relative to this SubMemoryBlock, you have to subtract this sub blocks
* starting offset.
* @return the new SubMemoryBlock that contains the back half of this block
* @throws IOException if a database error occurs.
*/
protected abstract SubMemoryBlock split(long memBlockOffset) throws IOException;
/**
* Updates this SubMemoryBlock to have a new owning MemoryBlock and offset within that block.
* This is used when splitting a block and entire sub blocks have to be moved to the new split
* block.
* @param key the id of the new owning memory block.
* @param startingOffset the starting offset of this sub block in the new block.
* @throws IOException if a database error occurs.
*/
protected void setParentIdAndStartingOffset(long key, long startingOffset) throws IOException {
record.setLongValue(MemoryMapDBAdapter.SUB_PARENT_ID_COL, key);
record.setLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL, startingOffset);
adapter.updateSubBlockRecord(record);
}
/**
* Returns a description of this SubMemoryBlock suitable to be displayed to the user.
* @return a description of this SubMemoryBlock suitable to be displayed to the user.
*/
protected abstract String getDescription();
/**
* Returns true if this subBlock uses the given fileBytes as its byte source.
* @param fileBytes the {@link FileBytes} to check for use
* @return true if this subBlock uses the given fileBytes as its byte source.
*/
protected boolean uses(FileBytes fileBytes) {
return false;
}
/**
* Gets the list of BytesSourceRanges from this sub block for the given memBlockOffset and associates
* it with the given {@link AddressRange}
* @param block the {@link MemoryBlock} that generated the BytesSourceSet.
* @param start the program address for which to get a ByteSourceSet
* @param memBlockOffset the offset from the beginning of the containing MemoryBlock.
* @param size the size of region to get byte sources
* @return the set of ByteSourceRanges which maps program addresses to byte source locations.
*/
protected abstract ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
long memBlockOffset, long size);
}

View file

@ -0,0 +1,106 @@
/* ###
* 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 db.Record;
import ghidra.program.model.address.Address;
import ghidra.program.model.mem.*;
/**
* Implementation of SubMemoryBlock for uninitialized blocks.
*/
class UninitializedSubMemoryBlock extends SubMemoryBlock {
UninitializedSubMemoryBlock(MemoryMapDBAdapter adapter, Record record) {
super(adapter, record);
startingOffset = record.getLongValue(MemoryMapDBAdapter.SUB_START_OFFSET_COL);
}
@Override
public boolean isInitialized() {
return false;
}
@Override
public byte getByte(long offset) throws MemoryAccessException {
if (offset < startingOffset || offset >= startingOffset + length) {
throw new IllegalArgumentException(
"Offset " + offset + "is out of bounds. Should be in [" + startingOffset + "," +
(startingOffset + length - 1));
}
throw new MemoryAccessException("Attempted to read from uninitialized block");
}
@Override
public int getBytes(long offset, byte[] b, int off, int len) throws MemoryAccessException {
throw new MemoryAccessException("Attempted to read from uninitialized block");
}
@Override
public void putByte(long offset, byte b) throws MemoryAccessException {
throw new MemoryAccessException("Attempted to read from uninitialized block");
}
@Override
public int putBytes(long offset, byte[] b, int off, int len) throws MemoryAccessException {
throw new MemoryAccessException("Attempted to read from uninitialized block");
}
@Override
protected boolean join(SubMemoryBlock block) throws IOException {
if (!(block instanceof UninitializedSubMemoryBlock)) {
return false;
}
setLength(length + block.length);
adapter.deleteSubBlock(block.record.getKey());
return true;
}
@Override
protected MemoryBlockType getType() {
return MemoryBlockType.DEFAULT;
}
@Override
protected SubMemoryBlock split(long memBlockOffset) throws IOException {
// convert from offset in block to offset in this sub block
long offset = memBlockOffset - startingOffset;
long newLength = length - offset;
length = offset;
record.setLongValue(MemoryMapDBAdapter.SUB_LENGTH_COL, length);
adapter.updateSubBlockRecord(record);
Record newSubRecord = adapter.createSubBlockRecord(-1, 0, newLength,
MemoryMapDBAdapter.SUB_TYPE_UNITIALIZED, 0, 0);
return new UninitializedSubMemoryBlock(adapter, newSubRecord);
}
@Override
protected String getDescription() {
return "";
}
@Override
protected ByteSourceRangeList getByteSourceRangeList(MemoryBlock block, Address start,
long memBlockOffset,
long size) {
return new ByteSourceRangeList();
}
}

View file

@ -72,7 +72,7 @@ public class AddressRangeImpl implements AddressRange, Serializable {
* @param length the length of the range.
* @exception AddressOverflowException if the length would wrap.
*/
public AddressRangeImpl(Address start, int length) throws AddressOverflowException {
public AddressRangeImpl(Address start, long length) throws AddressOverflowException {
minAddress = start;
maxAddress = start.addNoWrap(length - 1);
}

View file

@ -1,36 +0,0 @@
/* ###
* 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.model.mem;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
/**
* Additional methods for mapped memory blocks
*/
public interface MappedMemoryBlock extends MemoryBlock {
/**
* Get the address from the source block.
*/
public Address getOverlayedMinAddress();
/**
* Returns the AddressRange of the memory this block is mapped onto.
* @return the AddressRange of the memory this block is mapped onto.
*/
public AddressRange getOverlayedAddressRange();
}

View file

@ -15,10 +15,12 @@
*/
package ghidra.program.model.mem;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import db.ChainedBuffer;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.*;
@ -41,23 +43,10 @@ public interface Memory extends AddressSetView {
public static final long MAX_BINARY_SIZE = (long) MAX_BINARY_SIZE_GB << GBYTE_SHIFT_FACTOR;
/**
* Initialized blocks must be addressable by an int, 1-GByte.
* This value has been established due to limitations of the
* {@link ChainedBuffer} implementation use positive integers
* to convey length.
* The current max size of a memory block.
*/
public static final int MAX_INITIALIZED_BLOCK_SIZE_GB = 1;
public static final long MAX_INITIALIZED_BLOCK_SIZE =
(long) MAX_INITIALIZED_BLOCK_SIZE_GB << GBYTE_SHIFT_FACTOR;
/**
* Uninitialized blocks size limit, 12-GByte (limit number of 32-bit segments).
* This restriction is somewhat arbitrary but is established to prevent an excessive
* number of memory map segments ({@link #MAX_BINARY_SIZE_GB}).
*/
public static final int MAX_UNINITIALIZED_BLOCK_SIZE_GB = 12;
public static final long MAX_UNINITIALIZED_BLOCK_SIZE =
(long) MAX_UNINITIALIZED_BLOCK_SIZE_GB << GBYTE_SHIFT_FACTOR;
public static final int MAX_BLOCK_SIZE_GB = 16; // set to 16 because anything larger, ghidra bogs down
public static final long MAX_BLOCK_SIZE = (long) MAX_BLOCK_SIZE_GB << GBYTE_SHIFT_FACTOR;
/**
* Returns the program that this memory belongs to.
@ -123,9 +112,10 @@ public interface Memory extends AddressSetView {
* @param start start address of the block
* @param is source of the data used to fill the block.
* @param length the size of the block
* @param monitor task monitor
* @param overlay if true, the block will be created as an OVERLAY which means that a new
* overlay address space will be created and the block will have a starting address at the same
* offset as the given start address paramaeter, but in the new address space.
* offset as the given start address parameter, but in the new address space.
* @return new Initialized Memory Block
* @throws LockException if exclusive lock not in place (see haveLock())
* @throws MemoryConflictException if the new block overlaps with a
@ -133,6 +123,8 @@ public interface Memory extends AddressSetView {
* @throws AddressOverflowException if the start is beyond the
* address space
* @throws CancelledException user cancelled operation
* @throws DuplicateNameException if overlay is true and there is already an overlay address
* space with the same name as this memory block
*/
public MemoryBlock createInitializedBlock(String name, Address start, InputStream is,
long length, TaskMonitor monitor, boolean overlay)
@ -150,6 +142,8 @@ public interface Memory extends AddressSetView {
* overlay address space will be created and the block will have a starting address at the same
* offset as the given start address paramaeter, but in the new address space.
* @return new Initialized Memory Block
* @throws DuplicateNameException if overlay is true and there is already an overlay address
* space with the same name as this memory block
* @throws LockException if exclusive lock not in place (see haveLock())
* @throws MemoryConflictException if the new block overlaps with a
* previous block
@ -162,6 +156,30 @@ public interface Memory extends AddressSetView {
throws LockException, DuplicateNameException, MemoryConflictException,
AddressOverflowException, CancelledException;
/**
* Create an initialized memory block using bytes from a {@link FileBytes} object.
*
* @param name block name
* @param start starting address of the block
* @param fileBytes the {@link FileBytes} object to use as the underlying source of bytes.
* @param offset the offset into the FileBytes for the first byte of this memory block.
* @param size block length
* @param overlay if true, the block will be created as an OVERLAY which means that a new
* overlay address space will be created and the block will have a starting address at the same
* offset as the given start address parameter, but in the new address space.
* @return new Initialized Memory Block
* @throws LockException if exclusive lock not in place (see haveLock())
* @throws DuplicateNameException if overlay is true and there is already an overlay address
* space with the same name as this memory block
* @throws MemoryConflictException if the new block overlaps with a
* previous block
* @throws AddressOverflowException if the start is beyond the
* address space
*/
public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
long offset, long size, boolean overlay) throws LockException, DuplicateNameException,
MemoryConflictException, AddressOverflowException;
/**
* Create an uninitialized memory block and add it to this Memory.
* @param name block name
@ -176,6 +194,8 @@ public interface Memory extends AddressSetView {
* previous block
* @throws AddressOverflowException if the start is beyond the
* address space
* @throws DuplicateNameException if overlay is true and there is already an overlay address
* space with the same name as this memory block
*/
public MemoryBlock createUninitializedBlock(String name, Address start, long size,
boolean overlay) throws LockException, DuplicateNameException, MemoryConflictException,
@ -192,6 +212,10 @@ public interface Memory extends AddressSetView {
* @throws LockException if exclusive lock not in place (see haveLock())
* @throws MemoryConflictException if the new block overlaps with a
* previous block
* @throws MemoryConflictException if the new block overlaps with a
* previous block
* @throws AddressOverflowException if the start is beyond the
* address space
*/
public MemoryBlock createBitMappedBlock(String name, Address start, Address mappedAddress,
long length) throws LockException, MemoryConflictException, AddressOverflowException;
@ -695,4 +719,33 @@ public interface Memory extends AddressSetView {
*/
public void setLong(Address addr, long value, boolean bigEndian) throws MemoryAccessException;
/**
* Stores a sequence of bytes into the program. Typically, this method is used by importers
* to store the original raw program bytes.
*
* @param filename the name of the file from where the bytes originated
* @param offset the offset into the file for the first byte in the input stream.
* @param size the number of bytes to store from the input stream.
* @param is the input stream that will supply the bytes to store in the program.
* @return a FileBytes that was created to access the bytes.
* @throws IOException if there was an IOException saving the bytes to the program database.
*/
public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
throws IOException;
/**
* Returns a list of all the stored original file bytes objects
* @return a list of all the stored original file bytes objects
*/
public List<FileBytes> getAllFileBytes();
/**
* Deletes a stored sequence of file bytes. The file bytes can only be deleted if there
* are no memory block references to the file bytes.
* @param fileBytes the FileBytes for the file bytes to be deleted.
* @return true if the FileBytes was deleted. If any memory blNoocks are reference this FileBytes,
* then it will not be deleted and false will be returned.
* @throws IOException if there was an error updating the database.
*/
public boolean deleteFileBytes(FileBytes fileBytes) throws IOException;
}

View file

@ -17,8 +17,10 @@ package ghidra.program.model.mem;
import java.io.InputStream;
import java.io.Serializable;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.DuplicateNameException;
@ -136,6 +138,14 @@ public interface MemoryBlock extends Serializable, Comparable<MemoryBlock> {
*/
public void setExecute(boolean e);
/**
* Sets the read, write, execute permissions on this block
* @param read the read permission
* @param write the write permission
* @param execute the execute permission
*/
public void setPermissions(boolean read, boolean write, boolean execute);
/**
* Returns the value of the volatile property associated with this block.
* This attribute is generally associated with block of I/O regions of memory.
@ -249,9 +259,19 @@ public interface MemoryBlock extends Serializable, Comparable<MemoryBlock> {
/**
* Returns true if this memory block is a real loaded block (i.e. RAM) and not a special block
* containing file header data such as debug sections.
* @return true if this is a loaded block and not a "special" block such as a file header.
*/
public boolean isLoaded();
/**
* Returns a list of {@link SourceInfo} 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();
/**
* Determine if the specified address is contained within the reserved EXTERNAL block.
* @param address address of interest
@ -266,4 +286,5 @@ public interface MemoryBlock extends Serializable, Comparable<MemoryBlock> {
MemoryBlock block = memory.getBlock(address);
return block != null && MemoryBlock.EXTERNAL_BLOCK_NAME.equals(block.getName());
}
}

View file

@ -15,12 +15,14 @@
*/
package ghidra.program.model.mem;
import java.io.InputStream;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.SourceInfo;
import ghidra.program.model.address.Address;
import ghidra.util.exception.DuplicateNameException;
import java.io.InputStream;
/**
* MemoryBlockStub can be extended for use by tests. It throws an UnsupportedOperationException
* for all methods in the MemoryBlock interface. Any method that is needed for your test can then
@ -113,6 +115,11 @@ public class MemoryBlockStub implements MemoryBlock {
throw new UnsupportedOperationException();
}
@Override
public void setPermissions(boolean read, boolean write, boolean execute) {
throw new UnsupportedOperationException();
}
@Override
public boolean isVolatile() {
throw new UnsupportedOperationException();
@ -183,4 +190,9 @@ public class MemoryBlockStub implements MemoryBlock {
throw new UnsupportedOperationException();
}
@Override
public List<SourceInfo> getSourceInfos() {
throw new UnsupportedOperationException();
}
}

View file

@ -15,10 +15,13 @@
*/
package ghidra.program.model.mem;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import ghidra.framework.store.LockException;
import ghidra.program.database.mem.FileBytes;
import ghidra.program.model.address.*;
import ghidra.program.model.listing.Program;
import ghidra.util.exception.*;
@ -464,4 +467,26 @@ public class MemoryStub implements Memory {
throw new UnsupportedOperationException();
}
@Override
public FileBytes createFileBytes(String filename, long offset, long size, InputStream is)
throws IOException {
throw new UnsupportedOperationException();
}
@Override
public List<FileBytes> getAllFileBytes() {
throw new UnsupportedOperationException();
}
@Override
public boolean deleteFileBytes(FileBytes descriptor) {
throw new UnsupportedOperationException();
}
@Override
public MemoryBlock createInitializedBlock(String name, Address start, FileBytes fileBytes,
long offset, long size, boolean overlay) throws LockException, DuplicateNameException,
MemoryConflictException, AddressOverflowException {
throw new UnsupportedOperationException();
}
}

View file

@ -22,7 +22,6 @@ import ghidra.program.model.address.Address;
import ghidra.program.model.lang.Register;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Instruction;
import ghidra.program.model.mem.MappedMemoryBlock;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.pcode.PcodeOp;
import ghidra.program.model.pcode.Varnode;
@ -83,9 +82,9 @@ public class RefTypeFactory {
private static RefType[] memoryRefTypes = new RefType[] { RefType.INDIRECTION,
RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL,
RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP,
RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.PARAM, RefType.DATA,
RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND,
RefType.READ_WRITE, RefType.READ_WRITE_IND };
RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.PARAM,
RefType.DATA, RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE,
RefType.WRITE_IND, RefType.READ_WRITE, RefType.READ_WRITE_IND };
private static HashSet<RefType> validMemRefTypes = new HashSet<>();
static {
@ -94,20 +93,19 @@ public class RefTypeFactory {
}
}
private static RefType[] stackRefTypes = new RefType[] { RefType.DATA, RefType.READ,
RefType.WRITE, RefType.READ_WRITE };
private static RefType[] stackRefTypes =
new RefType[] { RefType.DATA, RefType.READ, RefType.WRITE, RefType.READ_WRITE };
private static RefType[] dataRefTypes = new RefType[] { RefType.DATA, RefType.PARAM, RefType.READ,
RefType.WRITE, RefType.READ_WRITE, };
private static RefType[] dataRefTypes = new RefType[] { RefType.DATA, RefType.PARAM,
RefType.READ, RefType.WRITE, RefType.READ_WRITE, };
private static RefType[] extRefTypes = new RefType[] {
// TODO: RefType.EXTERNAL_REF should be deprecated and RefType.DATA taking its place
RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL,
RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP,
RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.DATA,
RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND,
RefType.READ_WRITE, RefType.READ_WRITE_IND
};
private static RefType[] extRefTypes = new RefType[] {
// TODO: RefType.EXTERNAL_REF should be deprecated and RefType.DATA taking its place
RefType.COMPUTED_CALL, RefType.COMPUTED_JUMP, RefType.CONDITIONAL_CALL,
RefType.CONDITIONAL_JUMP, RefType.UNCONDITIONAL_CALL, RefType.UNCONDITIONAL_JUMP,
RefType.CONDITIONAL_COMPUTED_CALL, RefType.CONDITIONAL_COMPUTED_JUMP, RefType.DATA,
RefType.DATA_IND, RefType.READ, RefType.READ_IND, RefType.WRITE, RefType.WRITE_IND,
RefType.READ_WRITE, RefType.READ_WRITE_IND };
public static RefType[] getMemoryRefTypes() {
return memoryRefTypes;
@ -258,9 +256,8 @@ public class RefTypeFactory {
Varnode[] inputs = op.getInputs();
if (opCode == PcodeOp.COPY || opCode == PcodeOp.INT_ZEXT) {
if (addrs.contains(inputs[0].getAddress())) {
RefType rt =
getLoadStoreRefType(instrOps, opSeq + 1, op.getOutput().getAddress(),
refType);
RefType rt = getLoadStoreRefType(instrOps, opSeq + 1,
op.getOutput().getAddress(), refType);
if (rt == RefType.READ) {
if (refType == RefType.WRITE) {
return RefType.READ_WRITE;
@ -395,7 +392,7 @@ public class RefTypeFactory {
if (toAddr != null && toAddr.isMemoryAddress()) {
MemoryBlock block = cu.getProgram().getMemory().getBlock(toAddr);
if (block instanceof MappedMemoryBlock) {
if (block != null && block.isMapped()) {
ignoreExistingReferences = true;
speculativeFlowNotAllowed = true;
}
@ -446,8 +443,8 @@ public class RefTypeFactory {
}
if (!ignoreExistingReferences) {
Reference[] refs =
cu.getProgram().getReferenceManager().getReferencesFrom(cu.getMinAddress(), opIndex);
Reference[] refs = cu.getProgram().getReferenceManager().getReferencesFrom(
cu.getMinAddress(), opIndex);
for (Reference ref : refs) {
if (ref.getToAddress().equals(toAddr)) {
return ref.getReferenceType();

View file

@ -0,0 +1,194 @@
/* ###
* 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 static org.junit.Assert.*;
import java.util.Iterator;
import java.util.Set;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockStub;
public class ByteSourceRangeListTest extends AbstractGenericTest {
private AddressSpace space = new GenericAddressSpace("test", 64, AddressSpace.TYPE_RAM, 0);
private MemoryBlock block = new MemoryBlockStub();
@Test
public void testConstructor() {
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
ByteSourceRangeList list1 = new ByteSourceRangeList(range1);
ByteSourceRangeList list2 = new ByteSourceRangeList();
list2.add(range1);
assertTrue(list1.equals(list2));
}
@Test
public void testAdd() {
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50);
ByteSourceRangeList list1 = new ByteSourceRangeList(range1);
ByteSourceRangeList list2 = new ByteSourceRangeList(range2);
list1.add(list2);
assertEquals(2, list1.getRangeCount());
assertEquals(range1, list1.get(0));
assertEquals(range2, list1.get(1));
}
@Test
public void testIsEmpty() {
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
ByteSourceRangeList list1 = new ByteSourceRangeList();
assertTrue(list1.isEmpty());
list1.add(range1);
assertFalse(list1.isEmpty());
}
@Test
public void testAddNullRange() {
ByteSourceRange range = null;
ByteSourceRangeList list1 = new ByteSourceRangeList();
list1.add(range);
assertTrue(list1.isEmpty());
}
@Test
public void testIterator() {
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50);
ByteSourceRangeList list1 = new ByteSourceRangeList(range1);
list1.add(range2);
Iterator<ByteSourceRange> it = list1.iterator();
assertTrue(it.hasNext());
assertEquals(range1, it.next());
assertTrue(it.hasNext());
assertEquals(range2, it.next());
assertFalse(it.hasNext());
}
@Test
public void testIntersectSimple() {
ByteSourceRangeList list1 = new ByteSourceRangeList();
list1.add(new ByteSourceRange(block, addr(0), 0x100, 1, 0));
ByteSourceRangeList list2 = new ByteSourceRangeList();
list2.add(new ByteSourceRange(block, addr(0x100), 0x100, 1, 0x10));
// note that list1.intersect(list2) is not equal to list2.intersect(list1).
// The byte sources are the same but the corresponding real addresses are calling
// objects byte sources.
ByteSourceRangeList result = list1.intersect(list2);
assertEquals(1, result.getRangeCount());
ByteSourceRange range = result.get(0);
assertEquals(0xf0, range.getSize());
assertEquals(0x10, range.getOffset());
assertEquals(block, range.getMemoryBlock());
assertEquals(1, range.getSourceId());
assertEquals(addr(0x10), range.getStart());
assertEquals(addr(0xff), range.getEnd());
// now intersect from list2 perspective
result = list2.intersect(list1);
assertEquals(1, result.getRangeCount());
range = result.get(0);
assertEquals(0xf0, range.getSize());
assertEquals(0x10, range.getOffset());
assertEquals(block, range.getMemoryBlock());
assertEquals(1, range.getSourceId());
assertEquals(addr(0x100), range.getStart());
assertEquals(addr(0x1ef), range.getEnd());
}
@Test
public void testGetOverlappingBlocks() {
ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00);
MemoryBlock block1 = new MemoryBlockStub();
ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00);
// create a byte source overlap with the first block
MemoryBlock block2 = new MemoryBlockStub();
ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0x50);
ByteSourceRangeList list = new ByteSourceRangeList();
list.add(range);
list.add(range1);
list.add(range2);
Set<MemoryBlock> overlappingBlocks = list.getOverlappingBlocks();
assertEquals(2, overlappingBlocks.size());
assertTrue(overlappingBlocks.contains(block));
assertTrue(overlappingBlocks.contains(block2));
}
@Test
public void testGetOverlappingBlocksBlocksWhereBlocksAreAdjacentButDontOverlap() {
ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00);
MemoryBlock block1 = new MemoryBlockStub();
ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00);
// create a byte source overlap with the first block
MemoryBlock block2 = new MemoryBlockStub();
ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0x100);
ByteSourceRangeList list = new ByteSourceRangeList();
list.add(range);
list.add(range1);
list.add(range2);
Set<MemoryBlock> overlappingBlocks = list.getOverlappingBlocks();
assertEquals(0, overlappingBlocks.size());
}
@Test
public void testGetOverlappingBlocksBlocksWhereBlocksOverlapByExactlyOneByte() {
ByteSourceRange range = new ByteSourceRange(block, addr(0), 0x100, 1, 0x00);
MemoryBlock block1 = new MemoryBlockStub();
ByteSourceRange range1 = new ByteSourceRange(block1, addr(0x100), 0x100, 2, 0x00);
// create a byte source overlap with the first block
MemoryBlock block2 = new MemoryBlockStub();
ByteSourceRange range2 = new ByteSourceRange(block2, addr(0x200), 0x100, 1, 0xff);
ByteSourceRangeList list = new ByteSourceRangeList();
list.add(range);
list.add(range1);
list.add(range2);
Set<MemoryBlock> overlappingBlocks = list.getOverlappingBlocks();
assertEquals(2, overlappingBlocks.size());
assertTrue(overlappingBlocks.contains(block));
assertTrue(overlappingBlocks.contains(block2));
}
private Address addr(long value) {
return space.getAddress(value);
}
}

View file

@ -0,0 +1,119 @@
/* ###
* 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 static org.junit.Assert.*;
import org.junit.Test;
import generic.test.AbstractGenericTest;
import ghidra.program.model.address.*;
import ghidra.program.model.mem.MemoryBlock;
import ghidra.program.model.mem.MemoryBlockStub;
public class ByteSourceRangeTest extends AbstractGenericTest {
private AddressSpace space = new GenericAddressSpace("test", 64, AddressSpace.TYPE_RAM, 0);
private MemoryBlock block = new MemoryBlockStub();
@Test
public void testIntersectNotSameSource() {
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x10, 2, 0x50);
assertNull(range1.intersect(range2));
}
@Test
public void testIntersectOneRangeSimpleOverlap() {
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x20, 1, 0x50);
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x20, 1, 0x60);
ByteSourceRange intersect = range1.intersect(range2);
assertNotNull(intersect);
assertEquals(addr(0x10), intersect.getStart());
assertEquals(addr(0x1f), intersect.getEnd());
assertEquals(0x10, intersect.getSize());
assertEquals(1, intersect.getSourceId());
assertEquals(0x60, intersect.getOffset());
intersect = range2.intersect(range1);
assertNotNull(intersect);
assertEquals(addr(0x100), intersect.getStart());
assertEquals(addr(0x10f), intersect.getEnd());
assertEquals(0x10, intersect.getSize());
assertEquals(1, intersect.getSourceId());
assertEquals(0x60, intersect.getOffset());
}
@Test
public void testIntersectOneRangeButsAgainsAnother() {
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x20, 1, 0x50);
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x20, 2, 0x70);
assertNull(range1.intersect(range2));
assertNull(range2.intersect(range1));
}
@Test
public void testIntersectOneRangeCompletelyInAnother() {
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
ByteSourceRange range2 = new ByteSourceRange(block, addr(0x100), 0x30, 1, 0x40);
ByteSourceRange intersect = range1.intersect(range2);
assertNotNull(intersect);
assertEquals(addr(0), intersect.getStart());
assertEquals(addr(0xf), intersect.getEnd());
assertEquals(0x10, intersect.getSize());
assertEquals(1, intersect.getSourceId());
assertEquals(0x50, intersect.getOffset());
intersect = range2.intersect(range1);
assertNotNull(intersect);
assertEquals(addr(0x110), intersect.getStart());
assertEquals(addr(0x11f), intersect.getEnd());
assertEquals(0x10, intersect.getSize());
assertEquals(1, intersect.getSourceId());
assertEquals(0x50, intersect.getOffset());
}
@Test
public void testBitMappedIntersect() {
ByteSourceRange range1 = new ByteSourceRange(block, addr(0), 0x10, 1, 0x50);
ByteSourceRange range2 = new BitMappedByteSourceRange(block, addr(0x100), 1, 0x55, 2);
ByteSourceRange intersect = range1.intersect(range2);
assertNotNull(intersect);
assertEquals(addr(5), intersect.getStart());
assertEquals(addr(6), intersect.getEnd());
assertEquals(2, intersect.getSize());
assertEquals(1, intersect.getSourceId());
assertEquals(0x55, intersect.getOffset());
intersect = range2.intersect(range1);
assertNotNull(intersect);
assertEquals(addr(0x100), intersect.getStart());
assertEquals(addr(0x10f), intersect.getEnd());
assertEquals(2, intersect.getSize());
assertEquals(1, intersect.getSourceId());
assertEquals(0x55, intersect.getOffset());
}
private Address addr(long value) {
return space.getAddress(value);
}
}

View file

@ -0,0 +1,249 @@
/* ###
* 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 static org.junit.Assert.*;
import java.io.*;
import java.util.List;
import org.junit.*;
import db.*;
import db.buffers.BufferFile;
import generic.jar.ResourceFile;
import generic.test.AbstractGenericTest;
import ghidra.framework.Application;
import ghidra.framework.store.db.PrivateDatabase;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.lang.*;
import ghidra.program.model.listing.Program;
import ghidra.program.model.mem.Memory;
import ghidra.program.util.DefaultLanguageService;
import ghidra.util.task.TaskMonitor;
public class FileBytesTest extends AbstractGenericTest {
private static final int MAX_BUFFER_SIZE_FOR_TESTING = 200;
private Program program;
private Memory mem;
private int transactionID;
public FileBytesTest() {
super();
}
@Test
public void testStoreAndRetrieveFileBytes() throws IOException {
int dataSize = MAX_BUFFER_SIZE_FOR_TESTING / 2;
FileBytes fileBytes = createFileBytes("testFile", dataSize);
byte[] outBytes = new byte[200];
assertEquals("testFile", fileBytes.getFilename());
assertEquals(0L, fileBytes.getFileOffset());
assertEquals(dataSize, fileBytes.getSize());
int n = fileBytes.getOriginalBytes(0L, outBytes);
assertEquals(dataSize, n);
for (int i = 0; i < dataSize; i++) {
assertEquals("Byte[" + i + "]", i, outBytes[i]);
}
}
@Test
public void testRetrieveAfterSavingAndReopeningProgram() throws Exception {
int dataSize = MAX_BUFFER_SIZE_FOR_TESTING / 2;
FileBytes fileBytes = createFileBytes("testFile", dataSize);
byte[] outBytes = new byte[200];
saveAndRestoreProgram();
List<FileBytes> list = program.getMemory().getAllFileBytes();
fileBytes = list.get(0);
assertEquals("testFile", fileBytes.getFilename());
assertEquals(0L, fileBytes.getFileOffset());
assertEquals(dataSize, fileBytes.getSize());
int n = fileBytes.getOriginalBytes(0L, outBytes);
assertEquals(100, n);
for (int i = 0; i < dataSize; i++) {
assertEquals("Byte[" + i + "]", i, outBytes[i]);
}
}
@Test
public void testRequiresMultipleBuffers() throws Exception {
int dataSize = MAX_BUFFER_SIZE_FOR_TESTING + MAX_BUFFER_SIZE_FOR_TESTING / 2;
FileBytes fileBytes = createFileBytes("testFile", dataSize);
saveAndRestoreProgram();
byte[] outBytes = new byte[400];
List<FileBytes> list = program.getMemory().getAllFileBytes();
fileBytes = list.get(0);
assertEquals("testFile", fileBytes.getFilename());
assertEquals(0L, fileBytes.getFileOffset());
assertEquals(dataSize, fileBytes.getSize());
int n = fileBytes.getOriginalBytes(0L, outBytes);
assertEquals(dataSize, n);
for (int i = 0; i < dataSize; i++) {
assertEquals("Byte[" + i + "]", (byte) i, outBytes[i]);
}
DBBuffer[] buffers = (DBBuffer[]) getInstanceField("originalBuffers", fileBytes);
assertEquals(2, buffers.length);
assertEquals(MAX_BUFFER_SIZE_FOR_TESTING, buffers[0].length());
}
@Test
public void testCreateMultipleFileBytes() throws Exception {
createFileBytes("file1", 10);
createFileBytes("file2", 20);
createFileBytes("file3", 30);
saveAndRestoreProgram();
List<FileBytes> fileBytesList = mem.getAllFileBytes();
assertEquals(3, fileBytesList.size());
assertEquals("file1", fileBytesList.get(0).getFilename());
assertEquals(10, fileBytesList.get(0).getSize());
assertEquals("file2", fileBytesList.get(1).getFilename());
assertEquals(20, fileBytesList.get(1).getSize());
assertEquals("file3", fileBytesList.get(2).getFilename());
assertEquals(30, fileBytesList.get(2).getSize());
}
@Test
public void testDeleteFileBytesDescriptors() throws Exception {
createFileBytes("file1", 10);
createFileBytes("file2", 20);
createFileBytes("file3", 30);
saveAndRestoreProgram();
List<FileBytes> fileBytes = mem.getAllFileBytes();
mem.deleteFileBytes(fileBytes.get(1));
saveAndRestoreProgram();
List<FileBytes> fileBytesList = mem.getAllFileBytes();
assertEquals(2, fileBytesList.size());
assertEquals("file1", fileBytesList.get(0).getFilename());
assertEquals(10, fileBytesList.get(0).getSize());
assertEquals("file3", fileBytesList.get(1).getFilename());
assertEquals(30, fileBytesList.get(1).getSize());
}
@Test
public void testGetByte() throws Exception {
FileBytes fileBytes = createFileBytes("file1", 10);
assertEquals(5, fileBytes.getOriginalByte(5));
}
@Test
public void testGetLayeredByte() throws Exception {
FileBytes fileBytes = createFileBytes("file1", 10);
incrementFileBytes(fileBytes, 0, 10);
// check that the layered bytes are changed, but you can still get the originals
for (int i = 0; i < 10; i++) {
assertEquals(i, fileBytes.getOriginalByte(i));
assertEquals(i + 1, fileBytes.getModifiedByte(i));
}
}
private void incrementFileBytes(FileBytes fileBytes, int offset, int n) throws IOException {
for (int i = offset; i < offset + n; i++) {
fileBytes.putByte(i, (byte) (fileBytes.getModifiedByte(i) + 1));
}
}
@Test
public void testGetLayeredBytes() throws Exception {
FileBytes fileBytes = createFileBytes("file1", 10);
incrementFileBytes(fileBytes, 0, 10);
// check that the layered bytes are changed, but you can still get the originals
byte[] original = new byte[10];
byte[] modified = new byte[10];
fileBytes.getOriginalBytes(0, original);
fileBytes.getModifiedBytes(0, modified);
for (int i = 0; i < 10; i++) {
assertEquals(i, original[i]);
assertEquals(i + 1, modified[i]);
}
}
private FileBytes createFileBytes(String name, int size) throws IOException {
byte[] bytes = new byte[size];
for (int i = 0; i < size; i++) {
bytes[i] = (byte) i;
}
try (ByteArrayInputStream is = new ByteArrayInputStream(bytes)) {
return mem.createFileBytes(name, 0, size, is);
}
}
private void saveAndRestoreProgram() throws Exception {
program.endTransaction(transactionID, true);
PrivateDatabase privateDatabase = saveProgram(program);
program = restoreProgram(privateDatabase);
mem = program.getMemory();
transactionID = program.startTransaction("test");
}
private PrivateDatabase saveProgram(Program program) throws Exception {
File dir = createTempDirectory("program");
File dbDir = new File(dir, "program.db");
DBHandle dbh = ((ProgramDB) program).getDBHandle();
BufferFile bfile = PrivateDatabase.createDatabase(dbDir, null, dbh.getBufferSize());
dbh.saveAs(bfile, true, TaskMonitor.DUMMY);
return new PrivateDatabase(dbDir);
}
private Program restoreProgram(PrivateDatabase db) throws Exception {
DBHandle dbh = db.open(TaskMonitor.DUMMY);
return new ProgramDB(dbh, DBConstants.UPDATE, null, this);
}
@Before
public void setUp() throws Exception {
FileBytesAdapter.setMaxBufferSize(MAX_BUFFER_SIZE_FOR_TESTING);
Language language = getLanguage("Toy:BE:64:default");
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
program = new ProgramDB("Test", language, compilerSpec, this);
mem = program.getMemory();
transactionID = program.startTransaction("Test");
}
@After
public void tearDown() throws Exception {
program.endTransaction(transactionID, true);
program.release(this);
}
private Language getLanguage(String languageName) throws Exception {
ResourceFile ldefFile = Application.getModuleDataFile("Toy", "languages/toy.ldefs");
if (ldefFile != null) {
LanguageService languageService = DefaultLanguageService.getLanguageService(ldefFile);
Language language = languageService.getLanguage(new LanguageID(languageName));
return language;
}
throw new LanguageNotFoundException("Unsupported test language: " + languageName);
}
}

View file

@ -0,0 +1,857 @@
/* ###
* 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 static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;
import org.junit.*;
import db.DBConstants;
import db.DBHandle;
import generic.jar.ResourceFile;
import generic.test.AbstractGenericTest;
import ghidra.framework.Application;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.map.AddressMapDB;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.*;
import ghidra.program.model.mem.*;
import ghidra.program.util.DefaultLanguageService;
import ghidra.util.Lock;
import ghidra.util.task.TaskMonitor;
public class MemBlockDBTest extends AbstractGenericTest {
private static final long MAX_SUB_BLOCK_SIZE = 16;
private MemoryMapDB mem;
private long txID;
private DBHandle handle;
private AddressFactory addressFactory;
private ProgramDB program;
private int ptxID;
@Before
public void setUp() throws Exception {
Language language = getLanguage("Toy:BE:64:default");
CompilerSpec compilerSpec = language.getDefaultCompilerSpec();
program = new ProgramDB("Test", language, compilerSpec, this);
ptxID = program.startTransaction("test");
handle = new DBHandle();
txID = handle.startTransaction();
addressFactory = language.getAddressFactory();
AddressMapDB addrMap = (AddressMapDB) program.getAddressMap();
Lock lock = new Lock("Test");
int openMode = DBConstants.CREATE;
mem = new MemoryMapDB(handle, addrMap, openMode, true, lock);
MemoryMapDBAdapter adapter =
new MemoryMapDBAdapterV3(handle, mem, MAX_SUB_BLOCK_SIZE, true);
FileBytesAdapter fileBytesAdapter = new FileBytesAdapterV0(handle, mem, true);
mem.init(adapter, fileBytesAdapter);
mem.setProgram(program);
}
@After
public void tearDown() throws Exception {
program.endTransaction(ptxID, true);
handle.endTransaction(txID, true);
handle.close();
program.release(this);
}
@Test
public void testCreateInitializedBlock() throws Exception {
MemoryBlock block =
mem.createInitializedBlock("test", addr(0), 10, (byte) 1, TaskMonitor.DUMMY, false);
assertEquals(10, block.getSize());
assertEquals("test", block.getName());
assertEquals(addr(0), block.getStart());
assertEquals(addr(9), block.getEnd());
assertEquals(MemoryBlockType.DEFAULT, block.getType());
assertEquals(true, block.isInitialized());
assertEquals(false, block.isMapped());
assertNull(block.getComment());
assertNull(block.getSourceName());
assertEquals(MemoryBlock.READ, block.getPermissions());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
assertEquals(10, info.getLength());
assertEquals(addr(0), info.getMinAddress());
assertEquals(addr(9), info.getMaxAddress());
for (int i = 0; i < 10; i++) {
assertEquals(1, block.getByte(addr(i)));
}
}
@Test
public void testCreateUninitializedBlock() throws Exception {
MemoryBlock block = mem.createUninitializedBlock("test", addr(0), 10, false);
assertEquals(10, block.getSize());
assertEquals("test", block.getName());
assertEquals(addr(0), block.getStart());
assertEquals(addr(9), block.getEnd());
assertEquals(MemoryBlockType.DEFAULT, block.getType());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
assertEquals(10, info.getLength());
assertEquals(addr(0), info.getMinAddress());
assertEquals(addr(9), info.getMaxAddress());
try {
block.getByte(addr(0));
fail("expected exception trying to read bytes on unitialized block");
}
catch (MemoryAccessException e) {
// expected
}
}
@Test
public void testCreateUnitializedOverlayBlock() throws Exception {
MemoryBlock block = mem.createUninitializedBlock("test", addr(0), 10, true);
assertEquals(10, block.getSize());
assertEquals("test", block.getName());
assertNotEquals(addr(0), block.getStart()); // block should be in overlay space
assertEquals(0, block.getStart().getOffset());
assertEquals(9, block.getEnd().getOffset());
assertTrue(block.getStart().getAddressSpace().isOverlaySpace());
assertEquals(MemoryBlockType.OVERLAY, block.getType());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
assertEquals(10, info.getLength());
try {
block.getByte(block.getStart());
fail("expected exception trying to read bytes on unitialized block");
}
catch (MemoryAccessException e) {
// expected
}
}
@Test
public void testCreateInitializedOverlayBlock() throws Exception {
MemoryBlock block =
mem.createInitializedBlock("test", addr(0), 10, (byte) 1, TaskMonitor.DUMMY, true);
assertEquals(10, block.getSize());
assertEquals("test", block.getName());
assertNotEquals(addr(0), block.getStart()); // block should be in overlay space
assertEquals(0, block.getStart().getOffset());
assertEquals(9, block.getEnd().getOffset());
assertTrue(block.getStart().getAddressSpace().isOverlaySpace());
assertEquals(MemoryBlockType.OVERLAY, block.getType());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
assertEquals(10, info.getLength());
for (int i = 0; i < 10; i++) {
assertEquals(1, block.getByte(block.getStart().add(i)));
}
}
@Test
public void testCreateByteMappedBlock() throws Exception {
mem.createInitializedBlock("test1", addr(0), 50, (byte) 1, TaskMonitor.DUMMY, false);
mem.createUninitializedBlock("test2", addr(50), 50, false);
MemoryBlock block = mem.createByteMappedBlock("mapped", addr(1000), addr(40), 20);
assertEquals(20, block.getSize());
assertEquals("mapped", block.getName());
assertEquals(addr(1000), block.getStart());
assertEquals(addr(1019), block.getEnd());
assertEquals(MemoryBlockType.BYTE_MAPPED, block.getType());
assertEquals(false, block.isInitialized());
assertEquals(true, block.isMapped());
assertNull(block.getComment());
assertNull(block.getSourceName());
assertEquals(MemoryBlock.READ, block.getPermissions());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
assertEquals(20, info.getLength());
assertEquals(new AddressRangeImpl(addr(40), addr(59)), info.getMappedRange().get());
for (int i = 0; i < 10; i++) {
assertEquals(1, mem.getByte(block.getStart().add(i)));
}
try {
mem.getByte(block.getStart().add(10));
fail("expected exception trying to read bytes on mapped unitialized block");
}
catch (MemoryAccessException e) {
// expected
}
}
@Test
public void testCreateBitMappedBlock() throws Exception {
mem.createInitializedBlock("test1", addr(0), 50, (byte) 1, TaskMonitor.DUMMY, false);
mem.createUninitializedBlock("test2", addr(50), 50, false);
MemoryBlock block = mem.createBitMappedBlock("mapped", addr(1000), addr(49), 16);
assertEquals(16, block.getSize());
assertEquals("mapped", block.getName());
assertEquals(addr(1000), block.getStart());
assertEquals(addr(1015), block.getEnd());
assertEquals(MemoryBlockType.BIT_MAPPED, block.getType());
assertEquals(false, block.isInitialized());
assertEquals(true, block.isMapped());
assertNull(block.getComment());
assertNull(block.getSourceName());
assertEquals(MemoryBlock.READ, block.getPermissions());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
assertEquals(16, info.getLength());
assertEquals(new AddressRangeImpl(addr(49), addr(50)), info.getMappedRange().get());
assertEquals(1, mem.getByte(block.getStart()));
for (int i = 1; i < 8; i++) {
assertEquals(0, mem.getByte(block.getStart().add(i)));
}
try {
mem.getByte(block.getStart().add(8));
fail("expected exception trying to read bytes on mapped unitialized block");
}
catch (MemoryAccessException e) {
// expected
}
}
@Test
public void testCreateFileBytesBlock() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block = mem.createInitializedBlock("test", addr(100), fileBytes, 10, 50, false);
assertEquals(50, block.getSize());
assertEquals("test", block.getName());
assertEquals(addr(100), block.getStart());
assertEquals(addr(149), block.getEnd());
assertEquals(MemoryBlockType.DEFAULT, block.getType());
assertEquals(true, block.isInitialized());
assertEquals(false, block.isMapped());
assertNull(block.getComment());
assertNull(block.getSourceName());
assertEquals(MemoryBlock.READ, block.getPermissions());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo info = sourceInfos.get(0);
assertEquals(50, info.getLength());
assertEquals(addr(100), info.getMinAddress());
assertEquals(addr(149), info.getMaxAddress());
for (int i = 0; i < block.getSize(); i++) {
assertEquals(i + 10, block.getByte(addr(100 + i)));
}
}
@Test
public void testCreateFileBytesBlockOutSideRange() throws Exception {
byte[] bytes = new byte[256];
FileBytes fileBytes = mem.createFileBytes("test", 0, 100, new ByteArrayInputStream(bytes));
try {
mem.createInitializedBlock("test", addr(100), fileBytes, 10, 100, false);
fail(
"Expected create filebytes block to fail because the offset+blockLength > fileBytesLength");
}
catch (IndexOutOfBoundsException e) {
// expected
}
}
@Test
public void testInitializedBlockAcrossSubBlocks() throws Exception {
mem.createInitializedBlock("test", addr(0), 100, (byte) 1, TaskMonitor.DUMMY, false);
assertEquals(0x0101010101010101L, mem.getLong(addr(MAX_SUB_BLOCK_SIZE - 1)));
}
@Test
public void testInitializedBlockAcrossMutlitipleSubBlocks() throws Exception {
byte[] bytes = new byte[256];
for (int i = 0; i < 256; i++) {
bytes[i] = (byte) i;
}
mem.createInitializedBlock("test", addr(0), new ByteArrayInputStream(bytes), 256,
TaskMonitor.DUMMY, false);
byte[] b = new byte[100];
assertEquals(100, mem.getBytes(addr(10), b));
for (int i = 0; i < 100; i++) {
assertEquals(i + 10, b[i]);
}
}
@Test
public void testJoinFileBytes() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 10);
MemoryBlock block2 = createFileBytesBlock(fileBytes, addr(20), 35, 10);
MemoryBlock block = mem.join(block1, block2);
assertEquals(1, mem.getBlocks().length);
assertEquals(20, block.getSize());
assertEquals(addr(10), block.getStart());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
byte[] bytes = new byte[30];
assertEquals(20, block.getBytes(addr(10), bytes));
for (int i = 0; i < 20; i++) {
assertEquals(i + 25, bytes[i]);
}
}
@Test
public void testJoinNonConsecutiveFileBytes() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 10);
MemoryBlock block2 = createFileBytesBlock(fileBytes, addr(20), 70, 10);
MemoryBlock block = mem.join(block1, block2);
assertEquals(1, mem.getBlocks().length);
assertEquals(20, block.getSize());
assertEquals(addr(10), block.getStart());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(2, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
assertEquals(10, sourceInfo.getLength());
sourceInfo = sourceInfos.get(1);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(70, sourceInfo.getFileBytesOffset());
assertEquals(10, sourceInfo.getLength());
}
@Test
public void testJoinFileBytesBlockAndBufferBlock() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 10);
MemoryBlock block2 =
mem.createInitializedBlock("test", addr(20), 10, (byte) 1, TaskMonitor.DUMMY, false);
MemoryBlock block = mem.join(block1, block2);
assertEquals(1, mem.getBlocks().length);
assertEquals(20, block.getSize());
assertEquals(addr(10), block.getStart());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(2, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
assertEquals(10, sourceInfo.getLength());
SourceInfo sourceInfo2 = sourceInfos.get(1);
assertEquals(10, sourceInfo2.getLength());
}
@Test
public void testJoinBlocksFromDifferentFileBytes() throws Exception {
FileBytes fileBytes1 = createFileBytes();
FileBytes fileBytes2 = createFileBytes();
MemoryBlock block1 = createFileBytesBlock(fileBytes1, addr(10), 25, 10);
MemoryBlock block2 = createFileBytesBlock(fileBytes2, addr(20), 35, 10);
MemoryBlock block = mem.join(block1, block2);
assertEquals(1, mem.getBlocks().length);
assertEquals(20, block.getSize());
assertEquals(addr(10), block.getStart());
List<SourceInfo> sourceInfos = block.getSourceInfos();
assertEquals(2, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes1, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
assertEquals(10, sourceInfo.getLength());
sourceInfo = sourceInfos.get(1);
assertEquals(fileBytes2, sourceInfo.getFileBytes().get());
assertEquals(35, sourceInfo.getFileBytesOffset());
assertEquals(10, sourceInfo.getLength());
}
@Test
public void testSplitFileBytes() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 50);
mem.split(block1, addr(30));
MemoryBlock[] blocks = mem.getBlocks();
assertEquals(2, blocks.length);
assertEquals(20, blocks[0].getSize());
assertEquals(30, blocks[1].getSize());
assertEquals(addr(10), blocks[0].getStart());
assertEquals(addr(30), blocks[1].getStart());
List<SourceInfo> sourceInfos = blocks[0].getSourceInfos();
assertEquals(1, sourceInfos.size());
SourceInfo sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(25, sourceInfo.getFileBytesOffset());
sourceInfos = blocks[1].getSourceInfos();
assertEquals(1, sourceInfos.size());
sourceInfo = sourceInfos.get(0);
assertEquals(fileBytes, sourceInfo.getFileBytes().get());
assertEquals(45, sourceInfo.getFileBytesOffset());
}
@Test
public void testDeleteFileBytesBlock() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(10), 25, 50);
mem.removeBlock(block1, TaskMonitor.DUMMY);
assertEquals(0, mem.getBlocks().length);
}
@Test
public void testPutByteToFileBytesBlockAndGetBothChangedAndOriginalValues() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
byte[] bytes = new byte[20];
block.getBytes(addr(0), bytes);
checkBytes(bytes, 0);
block.getBytes(addr(20), bytes);
checkBytes(bytes, 20);
block.putBytes(addr(0), bytes);
block.getBytes(addr(0), bytes);
checkBytes(bytes, 20);
fileBytes.getOriginalBytes(0, bytes);
checkBytes(bytes, 0);
}
@Test
public void testSplitAndJoinUnitializedBlock() throws Exception {
MemoryBlock block = mem.createUninitializedBlock("test", addr(0), 40, false);
mem.split(block, addr(10));
MemoryBlock[] blocks = mem.getBlocks();
assertEquals(2, blocks.length);
assertEquals(addr(0), blocks[0].getStart());
assertEquals(addr(10), blocks[1].getStart());
assertEquals(10, blocks[0].getSize());
assertEquals(30, blocks[1].getSize());
assertTrue(!blocks[0].isInitialized());
assertTrue(!blocks[1].isInitialized());
mem.join(blocks[0], blocks[1]);
blocks = mem.getBlocks();
assertEquals(1, blocks.length);
assertEquals(addr(0), blocks[0].getStart());
assertEquals(40, blocks[0].getSize());
List<SourceInfo> sourceInfos = blocks[0].getSourceInfos();
assertEquals(1, sourceInfos.size()); // make sure the sub blocks were merged
}
private void checkBytes(byte[] bytes, int startingValue) {
for (int i = 0; i < bytes.length; i++) {
assertEquals(startingValue + i, bytes[i]);
}
}
@Test
public void testPutBytesToFileBytesBlockAndGetBothChangedAndOriginalValues() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
assertEquals(0, block.getByte(addr(0)));
block.putByte(addr(0), (byte) 55);
assertEquals(55, block.getByte(addr(0)));
assertEquals(0, fileBytes.getOriginalByte(0));
}
@Test
public void testSplitOnSubBlockBoundary() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 10);
MemoryBlock block2 = createFileBytesBlock(fileBytes, addr(10), 30, 10);
mem.join(block1, block2);
MemoryBlock[] blocks = mem.getBlocks();
assertEquals(1, blocks.length);
mem.split(blocks[0], addr(10));
blocks = mem.getBlocks();
assertEquals(2, blocks.length);
assertEquals(1, blocks[0].getSourceInfos().size());
assertEquals(1, blocks[1].getSourceInfos().size());
}
@Test
public void testByteMappedGetPutByte() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 10);
MemoryBlock mappedBlock = mem.createByteMappedBlock("mapped", addr(100), addr(0), 20);
assertEquals(5, mappedBlock.getByte(addr(105)));
assertEquals(5, block1.getByte(addr(5)));
mappedBlock.putByte(addr(105), (byte) 87);
assertEquals(87, block1.getByte(addr(5)));
}
@Test
public void testByteMappedGetPutBytes() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block1 = createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlock mappedBlock = mem.createByteMappedBlock("mapped", addr(100), addr(0), 20);
byte[] bytes = new byte[10];
mappedBlock.getBytes(addr(100), bytes);
checkBytes(bytes, 0);
mappedBlock.putBytes(addr(105), bytes);
block1.getBytes(addr(5), bytes);
checkBytes(bytes, 0);
}
@Test
public void testByteMappedJoin() throws Exception {
FileBytes fileBytes = createFileBytes();
createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlock mappedBlock1 = mem.createByteMappedBlock("mapped1", addr(100), addr(0), 10);
MemoryBlock mappedBlock2 = mem.createByteMappedBlock("mapped2", addr(110), addr(10), 10);
try {
mem.join(mappedBlock1, mappedBlock2);
fail("Expected exception when joining byte mapped blocks");
}
catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testByteMappedSplit() throws Exception {
FileBytes fileBytes = createFileBytes();
createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlock mappedBlock1 = mem.createByteMappedBlock("mapped1", addr(100), addr(0), 20);
mem.split(mappedBlock1, addr(110));
MemoryBlock[] blocks = mem.getBlocks();
assertEquals(3, blocks.length);
assertEquals(addr(110), blocks[2].getStart());
}
@Test
public void testBitMappedGetPutByte() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlock mappedBlock = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 20);
assertEquals(0, mappedBlock.getByte(addr(100)));
assertEquals(0, mappedBlock.getByte(addr(101)));
assertEquals(0, mappedBlock.getByte(addr(114)));
assertEquals(1, mappedBlock.getByte(addr(108)));
assertEquals(0, mappedBlock.getByte(addr(116)));
mappedBlock.putByte(addr(100), (byte) 4);
assertEquals(1, block.getByte(addr(0)));
}
@Test
public void testBitMappedGetPutBytes() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlock block = createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlock mappedBlock = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 50);
byte[] bytes = new byte[8];
mappedBlock.getBytes(addr(108), bytes);
assertEquals(1, bytes[0]);
for (int i = 1; i < 8; i++) {
assertEquals(0, bytes[i]);
}
for (int i = 0; i < 8; i++) {
bytes[i] = 1;
}
mappedBlock.putBytes(addr(100), bytes);
assertEquals(-1, block.getByte(addr(0)));
}
@Test
public void testBitMappedJoin() throws Exception {
FileBytes fileBytes = createFileBytes();
createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlock mappedBlock1 = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16);
MemoryBlock mappedBlock2 = mem.createBitMappedBlock("mapped2", addr(116), addr(2), 16);
try {
mem.join(mappedBlock1, mappedBlock2);
fail("Expected exception when joining bit mapped blocks");
}
catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testBitMappedSplit() throws Exception {
FileBytes fileBytes = createFileBytes();
createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlock mappedBlock1 = mem.createBitMappedBlock("mapped1", addr(100), addr(0), 16);
try {
mem.split(mappedBlock1, addr(108));
fail("Expected exception when joining bit mapped blocks");
}
catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void testGetByteSourceSetForFileBytesBlock() throws Exception {
FileBytes fileBytes = createFileBytes();
MemoryBlockDB block = (MemoryBlockDB) createFileBytesBlock(fileBytes, addr(0), 10, 50);
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(5), 10);
// we expect to get a single range ByteSourceSet pointing into the filebytes at offset
// 15 (10 because block was created at filebytes:10 and 5 because we start at the 5th byte
// in the block)
assertEquals(1, ranges.getRangeCount());
assertEquals(10, ranges.get(0).getSize());
assertEquals(5, ranges.get(0).getStart().getOffset());
assertEquals(14, ranges.get(0).getEnd().getOffset());
assertEquals(fileBytes.getId(), ranges.get(0).getSourceId());
assertEquals(15, ranges.get(0).getOffset());
}
@Test
public void testGetByteSourceSetForBufferBlock() throws Exception {
MemoryBlockDB block = (MemoryBlockDB) mem.createInitializedBlock("test", addr(0), 30,
(byte) 1, TaskMonitor.DUMMY, false);
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10);
// We expect to get to ranges because we made the buffer size small (16) so when we
// created a 30 size block, it had to make two separate sub blocks each with its own
// DBBuffer. The first range should contain the first 6 bytes of the requested range
// and the second buffer should contain the last 4 bytes of request range.
assertEquals(2, ranges.getRangeCount()); // we have two sublocks so two distinct ranges
assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize());
ByteSourceRange range = ranges.get(0);
assertEquals(10, range.getStart().getOffset());
assertEquals(15, range.getEnd().getOffset());
assertEquals(6, range.getSize());
assertEquals(10, range.getOffset());
range = ranges.get(1);
assertEquals(16, range.getStart().getOffset());
assertEquals(19, range.getEnd().getOffset());
assertEquals(4, range.getSize());
assertEquals(0, range.getOffset());
}
@Test
public void testGetByteSourceForUndefinedBlock() throws Exception {
MemoryBlockDB block =
(MemoryBlockDB) mem.createUninitializedBlock("test", addr(0), 30, false);
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(10), 10);
// undefined blocks have no source bytes
assertTrue(ranges.isEmpty());
}
@Test
public void testGetByteSourceForByteMappedBlock() throws Exception {
mem.createInitializedBlock("test1", addr(0), 15, (byte) 1, TaskMonitor.DUMMY, false);
mem.createUninitializedBlock("test2", addr(15), 20, false);
mem.createInitializedBlock("test3", addr(35), 15, (byte) 1, TaskMonitor.DUMMY, false);
MemoryBlockDB block =
(MemoryBlockDB) mem.createByteMappedBlock("mapped", addr(1000), addr(5), 40);
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(1005), 30);
// Uninitialized blocks don't contribute, so we should have 10 address (5 from first and last blocks each).
assertEquals(2, ranges.getRangeCount());
assertEquals(10, ranges.get(0).getSize() + ranges.get(1).getSize());
ByteSourceRange range = ranges.get(0);
assertEquals(addr(1005), range.getStart());
assertEquals(addr(1009), range.getEnd());
assertEquals(5, range.getSize());
assertEquals(10, range.getOffset());
range = ranges.get(1);
assertEquals(addr(1030), range.getStart());
assertEquals(addr(1034), range.getEnd());
assertEquals(5, range.getSize());
assertEquals(0, range.getOffset());
}
@Test
public void testGetByteSourceForBitMappedBlock() throws Exception {
FileBytes fileBytes = createFileBytes();
createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlockDB block =
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14);
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1000), 0x14);
assertEquals(1, ranges.getRangeCount());
assertEquals(3, ranges.get(0).getSize());
ByteSourceRange range = ranges.get(0);
assertEquals(addr(0x1000), range.getStart());
assertEquals(addr(0x1017), range.getEnd());
assertEquals(3, range.getSize());
assertEquals(5, range.getOffset());
}
@Test
public void testGetByteSourceForBitMappedBlockOffcutStart() throws Exception {
FileBytes fileBytes = createFileBytes();
createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlockDB block =
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x14);
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1005), 8);
assertEquals(1, ranges.getRangeCount());
assertEquals(2, ranges.get(0).getSize());
ByteSourceRange range = ranges.get(0);
assertEquals(addr(0x1000), range.getStart());
assertEquals(addr(0x100f), range.getEnd());
assertEquals(2, range.getSize());
assertEquals(5, range.getOffset());
}
@Test
public void testGetByteSourceForBitMappedBlockOffcutStartNotAtStart() throws Exception {
FileBytes fileBytes = createFileBytes();
createFileBytesBlock(fileBytes, addr(0), 0, 50);
MemoryBlockDB block =
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(5), 0x44);
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1015), 8);
assertEquals(1, ranges.getRangeCount());
assertEquals(2, ranges.get(0).getSize());
ByteSourceRange range = ranges.get(0);
assertEquals(addr(0x1010), range.getStart());
assertEquals(addr(0x101f), range.getEnd());
assertEquals(2, range.getSize());
assertEquals(7, range.getOffset());
}
@Test
public void testGetByteSourceForBitMappedBlock2() throws Exception {
mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false);
mem.createUninitializedBlock("test2", addr(0x4), 4, false);
mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false);
MemoryBlockDB block =
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40);
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1008), 0x30);
assertEquals(2, ranges.getRangeCount());
ByteSourceRange range = ranges.get(0);
assertEquals(addr(0x1008), range.getStart());
assertEquals(addr(0x100f), range.getEnd());
assertEquals(1, range.getSize());
assertEquals(3, range.getOffset());
range = ranges.get(1);
assertEquals(addr(0x1030), range.getStart());
assertEquals(addr(0x1037), range.getEnd());
assertEquals(1, range.getSize());
assertEquals(0, range.getOffset());
}
@Test
public void testGetByteSourceForBitMappedBlock2Offcut() throws Exception {
mem.createInitializedBlock("test1", addr(0), 4, (byte) 1, TaskMonitor.DUMMY, false);
mem.createUninitializedBlock("test2", addr(0x4), 4, false);
mem.createInitializedBlock("test3", addr(0x8), 4, (byte) 1, TaskMonitor.DUMMY, false);
MemoryBlockDB block =
(MemoryBlockDB) mem.createBitMappedBlock("mapped", addr(0x1000), addr(2), 0x40);
ByteSourceRangeList ranges = block.getByteSourceRangeList(addr(0x1006), 0x34);
assertEquals(2, ranges.getRangeCount());
ByteSourceRange range = ranges.get(0);
assertEquals(addr(0x1000), range.getStart());
assertEquals(addr(0x100f), range.getEnd());
assertEquals(2, range.getSize());
assertEquals(2, range.getOffset());
range = ranges.get(1);
assertEquals(addr(0x1030), range.getStart());
assertEquals(addr(0x103f), range.getEnd());
assertEquals(2, range.getSize());
assertEquals(0, range.getOffset());
}
private MemoryBlock createFileBytesBlock(FileBytes fileBytes, Address addr, int offset,
int length) throws Exception {
return mem.createInitializedBlock("test" + addr.toString(), addr, fileBytes, offset, length,
false);
}
private FileBytes createFileBytes() throws IOException {
byte[] bytes = new byte[256];
for (int i = 0; i < 256; i++) {
bytes[i] = (byte) i;
}
FileBytes fileBytes = mem.createFileBytes("test", 0, 100, new ByteArrayInputStream(bytes));
return fileBytes;
}
private Address addr(long offset) {
return addressFactory.getDefaultAddressSpace().getAddress(offset);
}
private Language getLanguage(String languageName) throws Exception {
ResourceFile ldefFile = Application.getModuleDataFile("Toy", "languages/toy.ldefs");
if (ldefFile != null) {
LanguageService languageService = DefaultLanguageService.getLanguageService(ldefFile);
Language language = languageService.getLanguage(new LanguageID(languageName));
return language;
}
throw new LanguageNotFoundException("Unsupported test language: " + languageName);
}
}