mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-17 08:00:15 +00:00
GP-1949 Revised MemoryMapDB to utilize SynchronizedAddressSet
This commit is contained in:
parent
26f921cf6e
commit
2bb57e6d5b
@ -53,7 +53,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
|
||||
private DataConverter defaultEndian;
|
||||
private List<MemoryBlockDB> blocks;// sorted list of blocks
|
||||
private AddressSet allAddrSet; // continuously updated
|
||||
private SynchronizedAddressSet allAddrSet = new SynchronizedAddressSet(); // continuously updated
|
||||
|
||||
private MemoryAddressSetView addrSetView;
|
||||
|
||||
@ -62,7 +62,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
* for public API methods.
|
||||
*/
|
||||
private class MemoryAddressSetView {
|
||||
private AddressSet all = new AddressSet();
|
||||
private AddressSet initializedAndLoaded = new AddressSet();
|
||||
private AddressSet initialized = new AddressSet();
|
||||
private AddressSet externalBlock = new AddressSet();
|
||||
@ -146,29 +145,26 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
}
|
||||
addrSetView = new MemoryAddressSetView();
|
||||
|
||||
// The allAddrSet instance is generally maintained with all memory
|
||||
// block addresses and need only be updated if currently empty.
|
||||
// Any time allAddrSet is modified addrSetView is set to null to
|
||||
// signal it becoming stale.
|
||||
boolean addToAll = allAddrSet.isEmpty();
|
||||
|
||||
// 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) {
|
||||
block.clearMappedBlockList();
|
||||
if (!block.isMapped()) {
|
||||
addBlockAddresses(block);
|
||||
addBlockAddresses(block, addToAll);
|
||||
}
|
||||
}
|
||||
// process all mapped blocks after non-mapped-blocks above
|
||||
for (MemoryBlockDB block : blocks) {
|
||||
if (block.isMapped()) {
|
||||
addBlockAddresses(block);
|
||||
addBlockAddresses(block, addToAll);
|
||||
}
|
||||
}
|
||||
|
||||
if (allAddrSet == null) {
|
||||
// An independent address set copy is maintained which may be changed
|
||||
// dynamically when adding memory blocks. This is neccessary
|
||||
// since such a changing address set may not be used to hand out
|
||||
// iterators. Any time allAddrSet is modified addrSetView is
|
||||
// set to null to signal it becoming stale.
|
||||
allAddrSet = new AddressSet(addrSetView.all);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
@ -176,21 +172,18 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the <code>addrSetView.initialized</code> and <code>addrSetView.initializedAndLoaded</code> with
|
||||
* relevant initialized addresses from the specified memory block. If block is not a
|
||||
* mapped-block and it may be a source to existing mapped-blocks then
|
||||
* <code>scanAllMappedBlocksIfNeeded</code> should be passed as <code>true</code> unless all
|
||||
* mapped blocks will be processed separately.
|
||||
* Update the <code>addrSetView</code> address sets with relevant addresses from the
|
||||
* specified memory block. In addition, allAddrSet will be updated if addToAll parameter is true.
|
||||
*
|
||||
* @param block memory block
|
||||
* @param blockMayBeMappedOnto if true and block is initialized and not a mapped block
|
||||
* all mapped blocks will be processed for possible introduction of newly initialized
|
||||
* mapped regions.
|
||||
* @param addToAll if true the allAddrSet should be updated with the specified block's address range
|
||||
*/
|
||||
private void addBlockAddresses(MemoryBlockDB block) {
|
||||
private void addBlockAddresses(MemoryBlockDB block, boolean addToAll) {
|
||||
Address start = block.getStart();
|
||||
Address end = block.getEnd();
|
||||
addrSetView.all.add(start, end);
|
||||
if (addToAll) {
|
||||
allAddrSet.add(start, end);
|
||||
}
|
||||
if (block.isExternalBlock()) {
|
||||
addrSetView.externalBlock.add(start, end);
|
||||
}
|
||||
@ -222,7 +215,7 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
synchronized (this) {
|
||||
fileBytesAdapter.refresh();
|
||||
adapter.refreshMemory();
|
||||
allAddrSet = null;
|
||||
allAddrSet = new SynchronizedAddressSet(); // buildAddressSets() will populate
|
||||
initializeBlocks();
|
||||
buildAddressSets();
|
||||
}
|
||||
@ -314,19 +307,6 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
return getLoadedAndInitializedAddressSet();
|
||||
}
|
||||
|
||||
private AddressSetView getIterableAddressSet() {
|
||||
lock.acquire();
|
||||
try {
|
||||
if (addrSetView == null) {
|
||||
buildAddressSets();
|
||||
}
|
||||
return new AddressSetViewAdapter(addrSetView.all);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSetView getAllInitializedAddressSet() {
|
||||
lock.acquire();
|
||||
@ -1786,35 +1766,17 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
|
||||
@Override
|
||||
public boolean contains(Address start, Address end) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.contains(start, end);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.contains(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(AddressSetView s) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.contains(s);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.contains(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.isEmpty();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1833,40 +1795,22 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
|
||||
@Override
|
||||
public Address getMinAddress() {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.getMinAddress();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address getMaxAddress() {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.getMaxAddress();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumAddressRanges() {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.getNumAddressRanges();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.getNumAddressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges() {
|
||||
return getIterableAddressSet().getAddressRanges();
|
||||
return allAddrSet.getAddressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1875,117 +1819,63 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(boolean startAtFront) {
|
||||
return getIterableAddressSet().getAddressRanges(startAtFront);
|
||||
public AddressRangeIterator getAddressRanges(boolean forward) {
|
||||
return allAddrSet.getAddressRanges(forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNumAddresses() {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.getNumAddresses();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.getNumAddresses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressIterator getAddresses(boolean forward) {
|
||||
return getIterableAddressSet().getAddresses(forward);
|
||||
return allAddrSet.getAddresses(forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressIterator getAddresses(Address start, boolean forward) {
|
||||
return getIterableAddressSet().getAddresses(start, forward);
|
||||
return allAddrSet.getAddresses(start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(AddressSetView set) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.intersects(set);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.intersects(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Address start, Address end) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.intersects(start, end);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.intersects(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet intersect(AddressSetView set) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.intersect(set);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.intersect(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet intersectRange(Address start, Address end) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.intersectRange(start, end);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.intersectRange(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet union(AddressSetView set) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.union(set);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.union(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet subtract(AddressSetView set) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.subtract(set);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.subtract(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressSet xor(AddressSetView set) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.xor(set);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.xor(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSameAddresses(AddressSetView set) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.hasSameAddresses(set);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.hasSameAddresses(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2238,61 +2128,37 @@ public class MemoryMapDB implements Memory, ManagerDB, LiveMemoryListener {
|
||||
|
||||
@Override
|
||||
public AddressRangeIterator getAddressRanges(Address start, boolean forward) {
|
||||
return getIterableAddressSet().getAddressRanges(start, forward);
|
||||
return allAddrSet.getAddressRanges(start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getFirstRange() {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.getFirstRange();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.getFirstRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getLastRange() {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.getLastRange();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.getLastRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange getRangeContaining(Address address) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.getRangeContaining(address);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.getRangeContaining(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator(boolean forward) {
|
||||
return getIterableAddressSet().iterator(forward);
|
||||
return allAddrSet.getAddressRanges(forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator(Address start, boolean forward) {
|
||||
return getIterableAddressSet().iterator(start, forward);
|
||||
return allAddrSet.getAddressRanges(start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address findFirstAddressInCommon(AddressSetView set) {
|
||||
lock.acquire();
|
||||
try {
|
||||
return allAddrSet.findFirstAddressInCommon(set);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
return allAddrSet.findFirstAddressInCommon(set);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,110 @@
|
||||
/* ###
|
||||
* 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.ConcurrentModificationException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
/**
|
||||
* <code>RecoverableAddressIterator</code> provides the ability to iterator over an {@link AddressSet}
|
||||
* which is getting modified concurrent with the iteration of Addresses contained within it. Do to
|
||||
* multiple levels of prefetch caching, the results returned may be stale relative to the actual
|
||||
* {@link AddressSet} at any point in time. The primary intent is to return addresses in proper order
|
||||
* and avoid throwing a {@link ConcurrentModificationException} which the standard iterators are
|
||||
* subject to.
|
||||
* <p>
|
||||
* NOTES:
|
||||
* <ol>
|
||||
* <li>The iterator methods are not symchronized but could be made so if restricted to
|
||||
* use in conjunction with the {@link SynchronizedAddressSet} where it would synchronize on
|
||||
* the set itself.</li>
|
||||
* <li>This class and {@link SynchronizedAddressSet} could be made public alongside {@link AddressSet}
|
||||
* if so desired in the future. Its current use has been limited until proven to be thread-safe
|
||||
* and useful.</li>
|
||||
* </ol>
|
||||
*/
|
||||
class RecoverableAddressIterator implements AddressIterator {
|
||||
|
||||
private AddressSetView set;
|
||||
private boolean forward;
|
||||
private AddressIterator iterator;
|
||||
private Address next;
|
||||
|
||||
/**
|
||||
* Construct iterator
|
||||
* @param set address set
|
||||
* @param start address to start iterating at in the address set or null for all addresses
|
||||
* @param forward if true address are return from lowest to highest, else from highest to lowest
|
||||
*/
|
||||
RecoverableAddressIterator(AddressSetView set, Address start, boolean forward) {
|
||||
this.set = set;
|
||||
this.forward = forward;
|
||||
initIterator(start);
|
||||
this.next = iterator.next();
|
||||
}
|
||||
|
||||
private void initIterator(Address start) {
|
||||
if (start == null) {
|
||||
iterator = set.getAddresses(forward);
|
||||
}
|
||||
else {
|
||||
iterator = set.getAddresses(start, forward);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Address> iterator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Address next() {
|
||||
Address addr = next;
|
||||
if (addr != null) {
|
||||
try {
|
||||
next = iterator.next();
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
next = recoverNext(addr);
|
||||
}
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
private Address recoverNext(Address lastAddr) {
|
||||
while (true) {
|
||||
try {
|
||||
initIterator(lastAddr);
|
||||
Address a = iterator.next();
|
||||
if (a != null && a.equals(lastAddr)) {
|
||||
a = iterator.next();
|
||||
}
|
||||
return a;
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
// set must have changed - try re-initializing again
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/* ###
|
||||
* 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.address.*;
|
||||
|
||||
/**
|
||||
* <code>RecoverableAddressRangeIterator</code> provides the ability to iterator over an {@link AddressSet}
|
||||
* which is getting modified concurrent with the iteration of {@link AddressRange}es contained within it. Do to
|
||||
* multiple levels of prefetch caching, the results returned may be stale relative to the actual
|
||||
* {@link AddressSet} at any point in time. The primary intent is to return address ranges in proper order
|
||||
* and avoid throwing a {@link ConcurrentModificationException} which the standard iterators are
|
||||
* subject to.
|
||||
* <p>
|
||||
* NOTES:
|
||||
* <ol>
|
||||
* <li>The iterator methods are not symchronized but could be made so if restricted to
|
||||
* use in conjunction with the {@link SynchronizedAddressSet} where it would synchronize on
|
||||
* the set itself.</li>
|
||||
* <li>This class and {@link SynchronizedAddressSet} could be made public alongside {@link AddressSet}
|
||||
* if so desired in the future. Its current use has been limited until proven to be thread-safe
|
||||
* and useful.</li>
|
||||
* </ol>
|
||||
*/
|
||||
class RecoverableAddressRangeIterator implements AddressRangeIterator {
|
||||
|
||||
private AddressSetView set;
|
||||
private boolean forward;
|
||||
private AddressRangeIterator iterator;
|
||||
private AddressRange next;
|
||||
|
||||
/**
|
||||
* Construct iterator
|
||||
* @param set address set
|
||||
* @param start the address the the first range should contain.
|
||||
* @param forward true iterators forward, false backwards
|
||||
*/
|
||||
RecoverableAddressRangeIterator(AddressSetView set, Address start, boolean forward) {
|
||||
this.set = set;
|
||||
this.forward = forward;
|
||||
initIterator(start);
|
||||
try {
|
||||
this.next = iterator.next();
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
this.next = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initIterator(Address start) {
|
||||
if (start == null) {
|
||||
iterator = set.getAddressRanges(forward);
|
||||
}
|
||||
else {
|
||||
iterator = set.getAddressRanges(start, forward);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AddressRange> iterator() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRange next() throws NoSuchElementException {
|
||||
AddressRange range = next;
|
||||
if (range == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
try {
|
||||
next = iterator.next();
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
next = recoverNext(range);
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
next = null;
|
||||
}
|
||||
return range;
|
||||
}
|
||||
|
||||
private AddressRange recoverNext(AddressRange lastRange) {
|
||||
while (true) {
|
||||
try {
|
||||
Address lastAddr = forward ? lastRange.getMaxAddress() : lastRange.getMinAddress();
|
||||
initIterator(lastAddr);
|
||||
AddressRange r = iterator.next();
|
||||
if (!r.intersects(lastRange)) {
|
||||
return r;
|
||||
}
|
||||
if (forward) {
|
||||
if (r.getMaxAddress().compareTo(lastAddr) > 0) {
|
||||
return new AddressRangeImpl(lastAddr.next(), r.getMaxAddress());
|
||||
}
|
||||
}
|
||||
else if (r.getMinAddress().compareTo(lastAddr) < 0) { // reverse
|
||||
return new AddressRangeImpl(r.getMinAddress(), lastAddr.previous());
|
||||
}
|
||||
return iterator.next(); // skip range and return next
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
// set must have changed - try re-initializing again
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next != null;
|
||||
}
|
||||
|
||||
}
|
@ -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.Iterator;
|
||||
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
/**
|
||||
* <code>SynchronizedAddressSet</code> provides a synchronized address set which
|
||||
* implements the {@link AddressSetView} interface. Iterators returned by this
|
||||
* implementation will recover from concurrent modification of this address set.
|
||||
* See {@link RecoverableAddressRangeIterator} and {@link RecoverableAddressIterator}.
|
||||
*/
|
||||
class SynchronizedAddressSet implements AddressSetView {
|
||||
|
||||
private AddressSet set;
|
||||
|
||||
SynchronizedAddressSet() {
|
||||
set = new AddressSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all addresses of the given AddressSet to this set.
|
||||
* @param addrSet set of addresses to add.
|
||||
* @see AddressSet#add(AddressSetView)
|
||||
*/
|
||||
synchronized void add(AddressSet addrSet) {
|
||||
set.add(addrSet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the range to this set
|
||||
* @param start the start address of the range to add
|
||||
* @param end the end address of the range to add
|
||||
* @see AddressSet#add(Address, Address)
|
||||
*/
|
||||
synchronized void add(Address start, Address end) {
|
||||
set.add(start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a range of addresses from this set
|
||||
* @param start the starting address of the range to be removed
|
||||
* @param end the ending address of the range to be removed (inclusive)
|
||||
* @see AddressSet#delete(Address, Address)
|
||||
*/
|
||||
synchronized void delete(Address start, Address end) {
|
||||
set.delete(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean contains(Address addr) {
|
||||
return set.contains(addr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean contains(Address start, Address end) {
|
||||
return set.contains(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean contains(AddressSetView addrSet) {
|
||||
return set.contains(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean isEmpty() {
|
||||
return set.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Address getMinAddress() {
|
||||
return set.getMinAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Address getMaxAddress() {
|
||||
return set.getMaxAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int getNumAddressRanges() {
|
||||
return set.getNumAddressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressRangeIterator getAddressRanges() {
|
||||
return set.getAddressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressRangeIterator getAddressRanges(boolean forward) {
|
||||
return set.getAddressRanges(forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressRangeIterator getAddressRanges(Address start, boolean forward) {
|
||||
return new RecoverableAddressRangeIterator(set, start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Iterator<AddressRange> iterator() {
|
||||
return set.getAddressRanges();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Iterator<AddressRange> iterator(boolean forward) {
|
||||
return set.getAddressRanges(forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Iterator<AddressRange> iterator(Address start, boolean forward) {
|
||||
return set.getAddressRanges(start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized long getNumAddresses() {
|
||||
return set.getNumAddresses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressIterator getAddresses(boolean forward) {
|
||||
return new RecoverableAddressIterator(set, null, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressIterator getAddresses(Address start, boolean forward) {
|
||||
return new RecoverableAddressIterator(set, start, forward);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean intersects(AddressSetView addrSet) {
|
||||
return set.intersects(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean intersects(Address start, Address end) {
|
||||
return set.intersects(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressSet intersect(AddressSetView addrSet) {
|
||||
return set.intersect(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressSet intersectRange(Address start, Address end) {
|
||||
return set.intersectRange(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressSet union(AddressSetView addrSet) {
|
||||
return set.union(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressSet subtract(AddressSetView addrSet) {
|
||||
return set.subtract(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressSet xor(AddressSetView addrSet) {
|
||||
return set.xor(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean hasSameAddresses(AddressSetView addrSet) {
|
||||
return set.hasSameAddresses(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressRange getFirstRange() {
|
||||
return set.getFirstRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressRange getLastRange() {
|
||||
return set.getLastRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized AddressRange getRangeContaining(Address address) {
|
||||
return set.getRangeContaining(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Address findFirstAddressInCommon(AddressSetView addrSet) {
|
||||
return set.findFirstAddressInCommon(addrSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int hashCode() {
|
||||
return set.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean equals(Object obj) {
|
||||
return set.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return set.toString();
|
||||
}
|
||||
|
||||
}
|
@ -1249,6 +1249,9 @@ public class AddressSet implements AddressSetView {
|
||||
@Override
|
||||
public AddressRange next() {
|
||||
RedBlackEntry<Address, Address> next = iterator.next();
|
||||
if (next == null) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return new AddressRangeImpl(next.getKey(), next.getValue());
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,174 @@
|
||||
/* ###
|
||||
* 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.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class RecoverableAddressIteratorTest extends AbstractGenericTest {
|
||||
|
||||
private AddressSpace space;
|
||||
private AddressSet set;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
space = new GenericAddressSpace("xx", 32, AddressSpace.TYPE_RAM, 0);
|
||||
|
||||
set = new AddressSet();
|
||||
set.add(range(0x100, 0x200));
|
||||
set.add(range(0x250, 0x250));
|
||||
set.add(range(0x300, 0x400));
|
||||
}
|
||||
|
||||
private Address addr(long offset) {
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
private AddressRange range(long start, long end) {
|
||||
return new AddressRangeImpl(addr(start), addr(end));
|
||||
}
|
||||
|
||||
private void assertHasAddressRange(AddressRange range, boolean forward, AddressIterator it) {
|
||||
Address nextAddr, endAddr;
|
||||
if (forward) {
|
||||
nextAddr = range.getMinAddress();
|
||||
endAddr = range.getMaxAddress();
|
||||
}
|
||||
else {
|
||||
nextAddr = range.getMaxAddress();
|
||||
endAddr = range.getMinAddress();
|
||||
}
|
||||
while (true) {
|
||||
assertEquals(nextAddr, it.next());
|
||||
if (nextAddr.equals(endAddr)) {
|
||||
break;
|
||||
}
|
||||
nextAddr = forward ? nextAddr.next() : nextAddr.previous();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1Forward() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(range(0x350, 0x500));
|
||||
|
||||
assertHasAddressRange(range(0x150, 0x200), true, it);
|
||||
|
||||
set.add(addr(0x220)); // will get skipped due to underlying iterator prefetch
|
||||
|
||||
assertHasAddressRange(range(0x250, 0x250), true, it);
|
||||
|
||||
assertHasAddressRange(range(0x300, 0x400), true, it);
|
||||
|
||||
assertHasAddressRange(range(0x401, 0x500), true, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
assertNull(it.next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Forward() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(range(0x210, 0x215));
|
||||
|
||||
assertHasAddressRange(range(0x150, 0x200), true, it);
|
||||
|
||||
set.add(range(0x220, 0x500));
|
||||
|
||||
assertHasAddressRange(range(0x210, 0x215), true, it);
|
||||
|
||||
assertHasAddressRange(range(0x220, 0x500), true, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3Forward() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(range(0x210, 0x215));
|
||||
|
||||
assertHasAddressRange(range(0x150, 0x200), true, it);
|
||||
|
||||
set.delete(range(0x220, 0x500));
|
||||
|
||||
assertHasAddressRange(range(0x210, 0x215), true, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1Reverse() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(addr(0x240));
|
||||
|
||||
assertHasAddressRange(range(0x300, 0x350), false, it);
|
||||
|
||||
set.add(range(0x50, 0x150));
|
||||
|
||||
assertHasAddressRange(range(0x250, 0x250), false, it);
|
||||
|
||||
set.add(range(0x50, 0x150));
|
||||
|
||||
assertHasAddressRange(range(0x240, 0x240), false, it);
|
||||
|
||||
assertHasAddressRange(range(0x50, 0x200), false, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Reverse() {
|
||||
|
||||
AddressIterator it = new RecoverableAddressIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
|
||||
set.add(addr(0x240));
|
||||
|
||||
assertHasAddressRange(range(0x300, 0x350), false, it);
|
||||
|
||||
set.delete(range(0x100, 0x200));
|
||||
|
||||
assertHasAddressRange(range(0x250, 0x250), false, it);
|
||||
|
||||
assertHasAddressRange(range(0x240, 0x240), false, it);
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
/* ###
|
||||
* 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.NoSuchElementException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import generic.test.AbstractGenericTest;
|
||||
import ghidra.program.model.address.*;
|
||||
|
||||
public class RecoverableAddressRangeIteratorTest extends AbstractGenericTest {
|
||||
|
||||
private AddressSpace space;
|
||||
private AddressSet set;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
space = new GenericAddressSpace("xx", 32, AddressSpace.TYPE_RAM, 0);
|
||||
|
||||
set = new AddressSet();
|
||||
set.add(range(0x100, 0x200));
|
||||
set.add(range(0x250, 0x250));
|
||||
set.add(range(0x300, 0x400));
|
||||
}
|
||||
|
||||
private Address addr(long offset) {
|
||||
return space.getAddress(offset);
|
||||
}
|
||||
|
||||
private AddressRange range(long start, long end) {
|
||||
return new AddressRangeImpl(addr(start), addr(end));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1Forward() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.add(addr(0x220)); // will get skipped due to underlying iterator prefetch
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers prefetch of range(0x300, 0x400)
|
||||
|
||||
set.add(range(0x350, 0x500)); // modifies existing RedBlackEntry node - no iterator recovery triggered
|
||||
|
||||
assertEquals(range(0x300, 0x400), it.next()); // triggers prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
try {
|
||||
it.next();
|
||||
fail("Expected NoSuchElementException");
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Forward() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.add(range(0x220, 0x400));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of partial range(0x251, 0x400)
|
||||
|
||||
assertEquals(range(0x251, 0x400), it.next()); // triggers prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3Forward() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(150), true);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.delete(range(0x220, 0x400));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test1Reverse() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x300, 0x400), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.add(addr(0x220));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of range(0x220, 0x220)
|
||||
|
||||
assertEquals(range(0x220, 0x220), it.next()); // triggers prefetch of range(0x100, 0x200)
|
||||
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test2Reverse() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x300, 0x400), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.add(range(0x220, 0x380));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of partial range(0x220, 0x24f)
|
||||
|
||||
assertEquals(range(0x220, 0x24f), it.next()); // triggers prefetch of range(0x100, 0x200)
|
||||
|
||||
assertEquals(range(0x100, 0x200), it.next()); // triggers prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test3Reverse() {
|
||||
|
||||
AddressRangeIterator it = new RecoverableAddressRangeIterator(set, addr(0x350), false);
|
||||
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(range(0x300, 0x400), it.next()); // triggers prefetch of range(0x250, 0x250)
|
||||
|
||||
set.delete(range(0x100, 0x220));
|
||||
|
||||
assertEquals(range(0x250, 0x250), it.next()); // triggers recovery prefetch of END
|
||||
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user