Merge remote-tracking branch

'origin/GP-1778_ghidravore_address_range_map_iterator--SQUASHED'
This commit is contained in:
ghidra1 2022-05-03 09:01:27 -04:00
commit c0643552a7
16 changed files with 2129 additions and 722 deletions

View File

@ -0,0 +1,986 @@
/* ###
* 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.map;
import static org.junit.Assert.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.*;
import db.Field;
import db.LongField;
import db.util.ErrorHandler;
import ghidra.program.database.ProgramDB;
import ghidra.program.database.mem.MemoryMapDB;
import ghidra.program.database.util.AddressRangeMapDB;
import ghidra.program.model.address.*;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.test.TestEnv;
import ghidra.util.Lock;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
public abstract class AbstractAddressRangeMapTest extends AbstractGhidraHeadedIntegrationTest {
private TestEnv env; // needed to discover languages
private ProgramDB program;
private AddressMap addrMap;
private AddressSpace space;
private int txId;
protected static Field ONE = new LongField(1);
protected static Field TWO = new LongField(2);
protected AddressRangeMapDB map;
protected Address spaceMax;
@Before
public void setUp() throws Exception {
env = new TestEnv();
program = createProgram();
MemoryMapDB memory = (MemoryMapDB) program.getMemory();
addrMap = (AddressMap) getInstanceField("addrMap", memory);
space = program.getAddressFactory().getDefaultAddressSpace();
spaceMax = space.getMaxAddress();
ErrorHandler errHandler = e -> fail();
map = new AddressRangeMapDB(program.getDBHandle(), addrMap,
new Lock("Test"), "TEST", errHandler, LongField.INSTANCE, true);
txId = program.startTransaction("test");
}
protected abstract ProgramDB createProgram() throws IOException;
@After
public void tearDown() {
program.endTransaction(txId, false);
if (program != null) {
program.release(this);
}
addrMap = null;
env.dispose();
}
protected Address addr(long offset) {
return space.getAddress(offset);
}
@Test
public void testPaint() {
map.paintRange(addr(0x100), addr(0x200), ONE);
map.paintRange(addr(0x300), addr(0x400), TWO);
List<AddressRange> ranges = getMapRanges();
assertEquals(2, ranges.size());
assertEquals(range(addr(0x100), addr(0x200)), ranges.get(0));
assertEquals(range(addr(0x300), addr(0x400)), ranges.get(1));
checkRange(ranges.get(0), ONE);
checkRange(ranges.get(1), TWO);
}
@Test
public void testPaintOverlap() {
assertNull(map.getValue(addr(0x200)));
map.paintRange(addr(0x100), addr(0x300), ONE);
map.paintRange(addr(0x200), addr(0x400), TWO);
List<AddressRange> ranges = getMapRanges();
assertEquals(2, ranges.size());
assertEquals(range(addr(0x100), addr(0x1ff)), ranges.get(0));
assertEquals(range(addr(0x200), addr(0x400)), ranges.get(1));
checkRange(ranges.get(0), ONE, null, TWO);
checkRange(ranges.get(1), TWO, ONE, null);
}
@Test
public void testPaintCoallesceWithLowerExistingRange() {
map.paintRange(addr(0x100), addr(0x200), ONE);
map.paintRange(addr(0x200), addr(0x300), ONE);
List<AddressRange> ranges = getMapRanges();
assertEquals(1, ranges.size());
assertEquals(range(addr(0x100), addr(0x300)), ranges.get(0));
checkRange(ranges.get(0), ONE);
}
@Test
public void testPaintCoallesceWithHigherExistingRange() {
map.paintRange(addr(0x200), addr(0x300), ONE);
map.paintRange(addr(0x100), addr(0x200), ONE);
List<AddressRange> ranges = getMapRanges();
assertEquals(1, ranges.size());
assertEquals(range(addr(0x100), addr(0x300)), ranges.get(0));
checkRange(ranges.get(0), ONE);
}
@Test
public void testPaintSplitExistingRangeWithNewValue() {
map.paintRange(addr(0x100), addr(0x400), ONE);
map.paintRange(addr(0x200), addr(0x300), TWO);
List<AddressRange> ranges = getMapRanges();
assertEquals(3, ranges.size());
assertEquals(range(addr(0x100), addr(0x1ff)), ranges.get(0));
assertEquals(range(addr(0x200), addr(0x300)), ranges.get(1));
assertEquals(range(addr(0x301), addr(0x400)), ranges.get(2));
checkRange(ranges.get(0), ONE, null, TWO);
checkRange(ranges.get(1), TWO, ONE, ONE);
checkRange(ranges.get(2), ONE, TWO, null);
}
@Test
public void testPaintStartOfExitingRangeWithNewValue() {
map.paintRange(addr(0x200), addr(0x400), ONE);
map.paintRange(addr(0x100), addr(0x300), TWO);
List<AddressRange> ranges = getMapRanges();
assertEquals(2, ranges.size());
assertEquals(range(addr(0x100), addr(0x300)), ranges.get(0));
assertEquals(range(addr(0x301), addr(0x400)), ranges.get(1));
checkRange(ranges.get(0), TWO, null, ONE);
checkRange(ranges.get(1), ONE, TWO, null);
}
@Test
public void testPaintSameValueInsideExistingRange() {
map.paintRange(addr(0x100), addr(0x200), ONE);
map.paintRange(addr(0x110), addr(0x150), ONE);
List<AddressRange> ranges = getMapRanges();
assertEquals(1, ranges.size());
assertEquals(range(addr(0x100), addr(0x200)), ranges.get(0));
checkRange(ranges.get(0), ONE);
}
@Test
public void testGetValueRecordWithStartKeyGreaterThanEndKey() {
addrMap.setImageBase(addr(0x100));
// address 0 will have a high key, and address 0x200 will have a low key
map.paintRange(addr(0), addr(0x200), ONE);
checkValueNoCache(ONE, addr(0x0));
checkValueNoCache(ONE, addr(0x1));
checkValueNoCache(ONE, addr(0xff));
checkValueNoCache(ONE, addr(0x100));
checkValueNoCache(ONE, addr(0x101));
checkValueNoCache(ONE, addr(0x1ff));
checkValueNoCache(ONE, addr(0x200));
checkValueNoCache(null, addr(0x201));
checkValueNoCache(null, spaceMax);
}
@Test
public void testGetValueWithWrappingAddressRecordAndStartKeyGreaterThanEndKey() {
addrMap.setImageBase(addr(0x200));
// address 0 will have a high key, and address 0x200 will have a low key
map.paintRange(addr(0), addr(0x400), ONE);
addrMap.setImageBase(addr(0x100));
map.invalidate();
checkValueNoCache(ONE, addr(0x0));
checkValueNoCache(ONE, addr(0xff));
checkValueNoCache(ONE, addr(0x100));
checkValueNoCache(ONE, addr(0x101));
checkValueNoCache(ONE, addr(0x1ff));
checkValueNoCache(ONE, addr(0x200));
checkValueNoCache(ONE, addr(0x201));
checkValueNoCache(ONE, addr(0x2ff));
checkValueNoCache(ONE, addr(0x300));
checkValueNoCache(null, addr(0x301));
checkValueNoCache(null, addr(0).subtractWrap(0x101));
checkValueNoCache(ONE, addr(0).subtractWrap(0x100));
checkValueNoCache(ONE, addr(0).subtractWrap(0x0ff));
checkValueNoCache(ONE, spaceMax);
}
protected void checkValueNoCache(Field expectedValue, Address address) {
map.invalidate(); // clears cache
assertEquals(expectedValue, map.getValue(address));
}
@Test
public void testGetRecordCount() {
assertEquals(0, map.getRecordCount());
map.paintRange(addr(0x100), addr(0x200), ONE);
assertEquals(1, map.getRecordCount());
map.paintRange(addr(0x300), addr(0x400), ONE);
assertEquals(2, map.getRecordCount());
// paint the gap should result in only 1 record
map.paintRange(addr(0x200), addr(0x300), ONE);
assertEquals(1, map.getRecordCount());
}
@Test
public void testMoveAddressRange() throws CancelledException {
map.paintRange(addr(0x100), addr(0x300), ONE);
map.moveAddressRange(addr(0x200), addr(0x500), 0x200, TaskMonitor.DUMMY);
List<AddressRange> ranges = getMapRanges();
assertEquals(2, ranges.size());
assertEquals(range(addr(0x100), addr(0x1ff)), ranges.get(0));
assertEquals(range(addr(0x500), addr(0x600)), ranges.get(1));
checkRange(ranges.get(0), ONE);
checkRange(ranges.get(1), ONE);
}
@Test
public void testIsEmpty() {
assertTrue(map.isEmpty());
map.paintRange(addr(0x100), addr(0x900), ONE);
assertFalse(map.isEmpty());
map.clearRange(addr(0), addr(0x1000));
assertTrue(map.isEmpty());
}
@Test
public void testPaintFullRangeWithNonZeroImageBase() {
Address imageBase = addr(0x50);
addrMap.setImageBase(imageBase);
AddressRange range = range(space.getMinAddress(), space.getMaxAddress());
map.paintRange(range.getMinAddress(), range.getMaxAddress(), ONE);
assertEquals(ONE, map.getValue(addr(0)));
assertEquals(ONE, map.getValue(addr(0x200)));
assertEquals(ONE, map.getValue(space.getMaxAddress()));
List<AddressRange> ranges = getMapRanges();
assertEquals(1, ranges.size());
assertEquals(range, ranges.get(0));
}
@Test
public void testAddressRangeIteratorWithNoTable() {
AddressRangeIterator it = map.getAddressRanges();
assertFalse(it.hasNext());
}
@Test
public void testIteratorWithMultipleRangesIncludeOneThatSpansAddressBoundary() {
Address imageBase = addr(0x100);
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(spaceMax.subtract(0x500), spaceMax);
addrMap.setImageBase(imageBase);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
checkRange(range1, ONE);
checkRange(range2, ONE);
checkRange(range3, ONE);
checkRange(range4, ONE);
List<AddressRange> ranges = getMapRanges();
assertEquals(4, ranges.size());
assertEquals(range1, ranges.get(0));
assertEquals(range2, ranges.get(1));
assertEquals(range3, ranges.get(2));
assertEquals(range4, ranges.get(3));
}
@Test
public void testAddressIteratorWithStartAddressBeforeFirstRange() {
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(addr(0x500), spaceMax);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x5));
assertEquals(3, ranges.size());
assertEquals(range2, ranges.get(0));
assertEquals(range3, ranges.get(1));
assertEquals(range4, ranges.get(2));
}
@Test
public void testAddressIteratorWithStartAddressInFirstRange() {
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(addr(0x500), spaceMax);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x5));
assertEquals(4, ranges.size());
assertEquals(range(addr(0x5), range1.getMaxAddress()), ranges.get(0));
assertEquals(range2, ranges.get(1));
assertEquals(range3, ranges.get(2));
assertEquals(range4, ranges.get(3));
}
@Test
public void testAddressIteratorWithStartAddressBetweenFirstAndSecondRange() {
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(addr(0x500), spaceMax);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x15));
assertEquals(3, ranges.size());
assertEquals(range2, ranges.get(0));
assertEquals(range3, ranges.get(1));
assertEquals(range4, ranges.get(2));
}
@Test
public void testAddressIteratorWithStartAddressInSecondRange() {
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(addr(0x500), spaceMax);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x25));
assertEquals(3, ranges.size());
assertEquals(range(addr(0x25), range2.getMaxAddress()), ranges.get(0));
assertEquals(range3, ranges.get(1));
assertEquals(range4, ranges.get(2));
}
@Test
public void testAddressIteratorWithStartAddressInLastRange() {
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(addr(0x500), addr(0x1000));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x600));
assertEquals(1, ranges.size());
assertEquals(range(addr(0x600), range4.getMaxAddress()), ranges.get(0));
}
@Test
public void testAddressIteratorWithStartAddressAfterLastRange() {
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(addr(0x500), addr(0x600));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating after last range
List<AddressRange> ranges = getMapRanges(addr(0x1000));
assertEquals(0, ranges.size());
}
@Test
public void testAddressIteratorWithImageBaseStartAddressInFirstRange() {
Address imageBase = addr(0x100);
addrMap.setImageBase(imageBase);
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(spaceMax.subtract(0x500), spaceMax);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x5));
assertEquals(4, ranges.size());
assertEquals(range(addr(0x5), range1.getMaxAddress()), ranges.get(0));
assertEquals(range2, ranges.get(1));
assertEquals(range3, ranges.get(2));
assertEquals(range4, ranges.get(3));
}
@Test
public void testAddressIteratorWithImageBaseStartAddressBetweenFirstAndSecondRange() {
Address imageBase = addr(0x100);
addrMap.setImageBase(imageBase);
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(addr(0x500), spaceMax);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x15));
assertEquals(3, ranges.size());
assertEquals(range2, ranges.get(0));
assertEquals(range3, ranges.get(1));
assertEquals(range4, ranges.get(2));
}
@Test
public void testAddressIteratorWithImageBaseStartAddressInSecondRange() {
Address imageBase = addr(0x100);
addrMap.setImageBase(imageBase);
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(addr(0x500), spaceMax);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x25));
assertEquals(3, ranges.size());
assertEquals(range(addr(0x25), range2.getMaxAddress()), ranges.get(0));
assertEquals(range3, ranges.get(1));
assertEquals(range4, ranges.get(2));
}
@Test
public void testAddressIteratorWithImageBaseStartAddressInLastRange() {
Address imageBase = addr(0x100);
addrMap.setImageBase(imageBase);
AddressRange range1 = range(addr(0x0), addr(0x10));
AddressRange range2 = range(addr(0x20), addr(0x30));
AddressRange range3 = range(addr(0x100), addr(0x200));
AddressRange range4 = range(addr(0x500), spaceMax);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x600));
assertEquals(1, ranges.size());
assertEquals(range(addr(0x600), range4.getMaxAddress()), ranges.get(0));
}
@Test
public void testGetAddressIteratorWithStartAndEndBeforeFirstRange() {
AddressRange range1 = range(addr(0x100), addr(0x200));
AddressRange range2 = range(addr(0x300), addr(0x400));
AddressRange range3 = range(addr(0x600), addr(0x700));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x20), addr(0x50));
assertEquals(0, ranges.size());
}
@Test
public void testGetAddressIteratorWithStartAndEndBeforeInsideRange() {
AddressRange range1 = range(addr(0x100), addr(0x200));
AddressRange range2 = range(addr(0x300), addr(0x400));
AddressRange range3 = range(addr(0x600), addr(0x700));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x310), addr(0x390));
assertEquals(1, ranges.size());
assertEquals(range(addr(0x310), addr(0x390)), ranges.get(0));
}
@Test
public void testGetAddressIteratorWithStartInOneRangeEndInAnother() {
AddressRange range1 = range(addr(0x100), addr(0x200));
AddressRange range2 = range(addr(0x300), addr(0x400));
AddressRange range3 = range(addr(0x600), addr(0x700));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x310), addr(0x610));
assertEquals(2, ranges.size());
assertEquals(range(addr(0x310), addr(0x400)), ranges.get(0));
assertEquals(range(addr(0x600), addr(0x610)), ranges.get(1));
}
@Test
public void testGetAddressIteratorWithStartAndEndPastLastRange() {
AddressRange range1 = range(addr(0x100), addr(0x200));
AddressRange range2 = range(addr(0x300), addr(0x400));
AddressRange range3 = range(addr(0x600), addr(0x700));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x800), addr(0x900));
assertEquals(0, ranges.size());
}
@Test
public void testGetAddressIteratorWithImageBaseStartingAndEndingInSpanningRecord() {
Address imageBase = addr(0x150);
addrMap.setImageBase(imageBase);
AddressRange range1 = range(addr(0x0), addr(0x100));
AddressRange range2 = range(addr(0x200), addr(0x300));
AddressRange range3 = range(addr(0x500), addr(0x600));
AddressRange range4 = range(addr(0x800), spaceMax);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), ONE);
List<AddressRange> ranges = getMapRanges(addr(0x50), addr(0x1000));
assertEquals(4, ranges.size());
assertEquals(range(addr(0x50), range1.getMaxAddress()), ranges.get(0));
assertEquals(range2, ranges.get(1));
assertEquals(range3, ranges.get(2));
assertEquals(range(range4.getMinAddress(), addr(0x1000)), ranges.get(3));
}
@Test
public void testGetAddressIteratorWithImageBaseWithStartAndEndIncludeAll() {
Address imageBase = addr(0x250);
addrMap.setImageBase(imageBase);
AddressRange range1 = range(addr(0x100), addr(0x200));
AddressRange range2 = range(addr(0x300), addr(0x400));
AddressRange range3 = range(addr(0x600), addr(0x700));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x150), addr(0x650));
assertEquals(3, ranges.size());
assertEquals(range(addr(0x150), range1.getMaxAddress()), ranges.get(0));
assertEquals(range2, ranges.get(1));
assertEquals(range(range3.getMinAddress(), addr(0x650)), ranges.get(2));
}
@Test
public void testGetAddressIteratorWithImageBaseStartInOneRangeEndInAnother() {
AddressRange range1 = range(addr(0x100), addr(0x200));
AddressRange range2 = range(addr(0x300), addr(0x400));
AddressRange range3 = range(addr(0x600), addr(0x700));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
// try iterating starting in first range
List<AddressRange> ranges = getMapRanges(addr(0x310), addr(0x610));
assertEquals(2, ranges.size());
assertEquals(range(addr(0x310), addr(0x400)), ranges.get(0));
assertEquals(range(addr(0x600), addr(0x610)), ranges.get(1));
}
@Test
public void testGetAddressSet() {
map.paintRange(addr(0x100), addr(0x200), ONE);
map.paintRange(addr(0x300), addr(0x400), TWO);
AddressSet set = map.getAddressSet();
assertEquals(2, set.getNumAddressRanges());
assertEquals(range(addr(0x100), addr(0x200)), set.getFirstRange());
assertEquals(range(addr(0x300), addr(0x400)), set.getLastRange());
}
@Test
public void testGetAddressSetWithImageBaseSet() {
addrMap.setImageBase(addr(0x100));
map.paintRange(addr(0), spaceMax, ONE);
AddressSet set = map.getAddressSet();
assertEquals(1, set.getNumAddressRanges());
assertEquals(range(addr(0), spaceMax), set.getFirstRange());
}
@Test
public void testGetAddressRangeContaining() {
AddressRange range1 = range(addr(0x100), addr(0x200));
AddressRange range2 = range(addr(0x300), addr(0x400));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), TWO);
assertEquals(range1, map.getAddressRangeContaining(addr(0x100)));
assertEquals(range1, map.getAddressRangeContaining(addr(0x150)));
assertEquals(range1, map.getAddressRangeContaining(addr(0x200)));
assertEquals(range2, map.getAddressRangeContaining(addr(0x300)));
assertEquals(range2, map.getAddressRangeContaining(addr(0x350)));
assertEquals(range2, map.getAddressRangeContaining(addr(0x400)));
assertEquals(range(addr(0), addr(0xff)), map.getAddressRangeContaining(addr(0x0)));
assertEquals(range(addr(0x201), addr(0x2ff)), map.getAddressRangeContaining(addr(0x250)));
assertEquals(range(addr(0x401), spaceMax), map.getAddressRangeContaining(addr(0x900)));
}
@Test
public void testGetRangeContainingWithStartKeyGreaterThanEndKey() {
AddressRange range = range(addr(0), addr(0x200));
AddressRange noValueRange = range(addr(0x201), spaceMax);
addrMap.setImageBase(addr(0x100));
// address 0 will have a high key, and address 0x200 will have a low key
map.paintRange(range.getMinAddress(), range.getMaxAddress(), ONE);
assertEquals(range, map.getAddressRangeContaining(addr(0)));
assertEquals(range, map.getAddressRangeContaining(addr(0x100)));
assertEquals(range, map.getAddressRangeContaining(addr(0x200)));
assertEquals(noValueRange, map.getAddressRangeContaining(addr(0x201)));
assertEquals(noValueRange, map.getAddressRangeContaining(addr(0x500)));
assertEquals(noValueRange, map.getAddressRangeContaining(spaceMax));
}
@Test
public void testGetRangeContainingWithWrappingAddressRecordAndStartKeyGreaterThanEndKey() {
addrMap.setImageBase(addr(0x200));
// address 0 will have a high key, and address 0x200 will have a low key
map.paintRange(addr(0), addr(0x400), ONE);
addrMap.setImageBase(addr(0x100));
map.invalidate();
AddressRange rangeLow = range(addr(0), addr(0x300));
AddressRange rangeHigh = range(addr(0).subtractWrap(0x100), spaceMax);
AddressRange noValueRange = range(addr(0x301), rangeHigh.getMinAddress().subtract(1));
assertEquals(rangeLow, map.getAddressRangeContaining(addr(0)));
assertEquals(rangeLow, map.getAddressRangeContaining(addr(0x100)));
assertEquals(rangeLow, map.getAddressRangeContaining(addr(0x200)));
assertEquals(rangeLow, map.getAddressRangeContaining(addr(0x300)));
assertEquals(rangeHigh, map.getAddressRangeContaining(spaceMax));
assertEquals(rangeHigh, map.getAddressRangeContaining(rangeHigh.getMinAddress()));
assertEquals(noValueRange, map.getAddressRangeContaining(addr(0x301)));
assertEquals(noValueRange, map.getAddressRangeContaining(addr(0x500)));
assertEquals(noValueRange,
map.getAddressRangeContaining(rangeHigh.getMinAddress().subtract(1)));
}
@Test
public void testGetAddressRangeContainingWithSpanningAddressBoundary() {
map.paintRange(addr(0), addr(0x200), ONE);
// move the image base to make the above range partially wrap into upper address
addrMap.setImageBase(addr(0).subtractWrap(0x100));
// there is still currently only one record
assertEquals(1, map.getRecordCount());
List<AddressRange> ranges = getMapRanges();
assertEquals(range(addr(0), addr(0x100)), ranges.get(0));
assertEquals(range(addr(0).subtractWrap(0x100), spaceMax), ranges.get(1));
}
@Test
public void testRangeWithImageBaseMoveToInsideRange() {
map.paintRange(addr(0x10), addr(0x20), ONE);
assertNull(map.getValue(addr(0)));
assertEquals(ONE, map.getValue(addr(0x10)));
assertEquals(ONE, map.getValue(addr(0x15)));
assertEquals(ONE, map.getValue(addr(0x20)));
assertNull(map.getValue(addr(0x25)));
List<AddressRange> ranges = getMapRanges();
assertEquals(1, ranges.size());
assertEquals(range(addr(0x10), addr(0x20)), ranges.get(0));
Address imageBase = addr(0x15);
addrMap.setImageBase(imageBase);
// if the image base changes, the map has to be told so that it can clear its cache.
map.invalidate();
// now range should be from 0x25 to 0x35
assertNull(map.getValue(addr(0)));
assertNull(map.getValue(addr(0x10)));
assertNull(map.getValue(addr(0x15)));
assertNull(map.getValue(addr(0x20)));
assertEquals(ONE, map.getValue(addr(0x25)));
assertEquals(ONE, map.getValue(addr(0x30)));
assertEquals(ONE, map.getValue(addr(0x35)));
assertNull(map.getValue(addr(0x36)));
ranges = getMapRanges();
assertEquals(1, ranges.size());
// image base will cause range to be two pieces, but effectively same set of addresses
assertEquals(range(addr(0x25), addr(0x35)), ranges.get(0));
}
@Test
public void testValueRangeSet() {
AddressRange range1 = range(addr(0x100), addr(0x200));
AddressRange range2 = range(addr(0x300), addr(0x400));
AddressRange range3 = range(addr(0x500), addr(0x600));
AddressRange range4 = range(addr(0x700), addr(0x800));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), TWO);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), TWO);
AddressSet setOne = addressSet(range1, range3);
AddressSet setTwo = addressSet(range2, range4);
assertEquals(setOne, map.getAddressSet(ONE));
assertEquals(setTwo, map.getAddressSet(TWO));
}
@Test
public void testValueRangeSetWithNoGaps() {
AddressRange range1 = range(addr(0x100), addr(0x500));
AddressRange range2 = range(addr(0x200), addr(0x300));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), TWO);
AddressSet setOne = addressSet(range1).subtract(addressSet(range2));
AddressSet setTwo = addressSet(range2);
assertEquals(setOne, map.getAddressSet(ONE));
assertEquals(setTwo, map.getAddressSet(TWO));
}
@Test
public void testValueRangeSetWithImageBaseAndSpanningRecord() {
Address imageBase = addr(0x150);
addrMap.setImageBase(imageBase);
AddressRange range1 = range(addr(0x0), addr(0x100));
AddressRange range2 = range(addr(0x300), addr(0x400));
AddressRange range3 = range(addr(0x500), spaceMax);
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), TWO);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
assertEquals(addressSet(range1, range3), map.getAddressSet(ONE));
assertEquals(addressSet(range2), map.getAddressSet(TWO));
}
@Test
public void testValueRangeSetWithValueNotInMap() {
AddressRange range1 = range(addr(0x100), addr(0x500));
AddressRange range2 = range(addr(0x200), addr(0x300));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), ONE);
assertEquals(addressSet(), map.getAddressSet(TWO));
}
@Test
public void testGetAddressSetForValue() {
AddressRange range1 = range(addr(0x100), addr(0x200));
AddressRange range2 = range(addr(0x300), addr(0x400));
AddressRange range3 = range(addr(0x500), addr(0x600));
AddressRange range4 = range(addr(0x700), addr(0x800));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), TWO);
map.paintRange(range3.getMinAddress(), range3.getMaxAddress(), ONE);
map.paintRange(range4.getMinAddress(), range4.getMaxAddress(), TWO);
AddressSet addressSet = map.getAddressSet(ONE);
assertEquals(2, addressSet.getNumAddressRanges());
assertEquals(range1, addressSet.getFirstRange());
assertEquals(range3, addressSet.getLastRange());
addressSet = map.getAddressSet(TWO);
assertEquals(2, addressSet.getNumAddressRanges());
assertEquals(range2, addressSet.getFirstRange());
assertEquals(range4, addressSet.getLastRange());
}
@Test
public void testPaintAfterImageBaseChangeSplitsWrappingRecord() {
AddressRange range1 = range(addr(0x00), addr(0x200));
AddressRange range2 = range(addr(0x500), addr(0x600));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
assertEquals(1, map.getRecordCount());
assertEquals(1, getMapRanges().size());
assertEquals(range1, getMapRanges().get(0));
Address imageBase = spaceMax.subtract(0xff);
addrMap.setImageBase(imageBase);
map.invalidate();
assertEquals(1, map.getRecordCount());
assertEquals(2, getMapRanges().size());
assertEquals(range(addr(0), addr(0x100)), getMapRanges().get(0));
assertEquals(range(imageBase, spaceMax), getMapRanges().get(1));
// do another paint and see that the record got split
// any paint will do, so do a paint that effectively does nothing
map.paintRange(range2.getMinAddress(), range2.getMaxAddress(), null);
assertEquals(2, map.getRecordCount());
assertEquals(2, getMapRanges().size());
assertEquals(range(imageBase, spaceMax), getMapRanges().get(0));
assertEquals(range(addr(0), addr(0x100)), getMapRanges().get(1));
}
@Test
public void testRangeIteratorWithRestrictionsSuchThatStartRangeIsOnlyRange() {
AddressRange range1 = range(addr(0x00), addr(0x200));
map.paintRange(range1.getMinAddress(), range1.getMaxAddress(), ONE);
assertEquals(1, map.getRecordCount());
assertEquals(1, getMapRanges().size());
assertEquals(range1, getMapRanges().get(0));
Address imageBase = spaceMax.subtract(0xff);
addrMap.setImageBase(imageBase);
map.invalidate();
List<AddressRange> ranges = getMapRanges(addr(0), addr(0x500));
assertEquals(1, ranges.size());
assertEquals(range(addr(0), addr(0x100)), ranges.get(0));
}
private AddressRange range(Address start, Address end) {
return new AddressRangeImpl(start, end);
}
private List<AddressRange> getMapRanges() {
AddressRangeIterator addressRanges = map.getAddressRanges();
List<AddressRange> ranges = new ArrayList<AddressRange>();
for (AddressRange addressRange : addressRanges) {
ranges.add(addressRange);
}
return ranges;
}
private List<AddressRange> getMapRanges(Address start) {
AddressRangeIterator addressRanges = map.getAddressRanges(start);
List<AddressRange> ranges = new ArrayList<AddressRange>();
for (AddressRange addressRange : addressRanges) {
ranges.add(addressRange);
}
return ranges;
}
private List<AddressRange> getMapRanges(Address start, Address end) {
AddressRangeIterator addressRanges = map.getAddressRanges(start, end);
List<AddressRange> ranges = new ArrayList<AddressRange>();
for (AddressRange addressRange : addressRanges) {
ranges.add(addressRange);
}
return ranges;
}
private void checkRange(AddressRange range, Field value) {
checkRange(range, value, null, null);
}
private void checkRange(AddressRange range, Field value, Field valueBeforeRange,
Field valueAfterRange) {
Address start = range.getMinAddress();
Address end = range.getMaxAddress();
assertEquals("Value at range start " + range, value, map.getValue(start));
assertEquals("Value at range end " + range, value, map.getValue(end));
// if not at zero, check that value doesn't exist just before range;
if (start.compareTo(addr(0)) > 0) {
assertEquals("Value before range " + range, valueBeforeRange,
map.getValue(start.subtract(1)));
}
// if not at end, check that value doesn't exist just past range
if (end.compareTo(spaceMax) < 0) {
assertEquals("Value after range " + range, valueAfterRange, map.getValue(end.add(1)));
}
}
private AddressSet addressSet(AddressRange... ranges) {
AddressSet set = new AddressSet();
for (AddressRange range : ranges) {
set.add(range);
}
return set;
}
}

View File

@ -0,0 +1,31 @@
/* ###
* 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.map;
import java.io.IOException;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.lang.*;
public class AddressRangeMap32BitTest extends AbstractAddressRangeMapTest {
protected ProgramDB createProgram() throws IOException {
LanguageService service = getLanguageService();
Language language = service.getLanguage(new LanguageID("sparc:BE:32:default"));
return new ProgramDB("test", language, language.getDefaultCompilerSpec(), this);
}
}

View File

@ -0,0 +1,47 @@
/* ###
* 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.map;
import java.io.IOException;
import org.junit.Test;
import ghidra.program.database.ProgramDB;
import ghidra.program.model.lang.*;
public class AddressRangeMap64BitTest extends AbstractAddressRangeMapTest {
protected ProgramDB createProgram() throws IOException {
LanguageService service = getLanguageService();
Language language = service.getLanguage(new LanguageID("sparc:BE:64:default"));
return new ProgramDB("test", language, language.getDefaultCompilerSpec(), this);
}
@Test
public void testGetValue64BitSpecific() {
map.paintRange(addr(0), spaceMax, ONE);
checkValueNoCache(ONE, addr(0x0));
checkValueNoCache(ONE, addr(0x0));
// now check addresses that are in different address bases that don't exist yet
// addresses that differ in the upper 32bits are in different address basesI
checkValueNoCache(ONE, addr(0xA0000000000000L));
checkValueNoCache(ONE, addr(0xB0000000000000L));
}
}

View File

@ -30,6 +30,7 @@ import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.ProgramContext;
import ghidra.program.model.mem.Memory;
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
import ghidra.util.task.TaskMonitor;
import ghidra.util.task.TaskMonitorAdapter;
/**
@ -78,7 +79,7 @@ public class ProgramContextTest extends AbstractGhidraHeadedIntegrationTest {
int id = program.startTransaction("Test");
try {
Address start = getAddress(0);
Address start = addr(0);
try {
mem.createInitializedBlock("first", start, 100, (byte) 0,
TaskMonitorAdapter.DUMMY_MONITOR, false);
@ -91,7 +92,7 @@ public class ProgramContextTest extends AbstractGhidraHeadedIntegrationTest {
boolean didSomething = false;
Address startAddress = start;
Address endAddress = getAddress(0x30);
Address endAddress = addr(0x30);
// stick a value into each one!
BigInteger value = BigInteger.valueOf(255);
@ -157,8 +158,38 @@ public class ProgramContextTest extends AbstractGhidraHeadedIntegrationTest {
}
}
private Address getAddress(long offset) {
return space.getAddress(offset);
@Test
public void testImageBaseChange() throws Exception {
int id = program.startTransaction("Test");
Address start = addr(0x10);
Address end = addr(0x20);
mem.createInitializedBlock("first", addr(0), 0x100, (byte) 0, TaskMonitor.DUMMY, false);
ProgramContext programContext = program.getProgramContext();
Register register = programContext.getRegisters().get(0);
BigInteger value = BigInteger.valueOf(0x11);
programContext.setValue(register, addr(0x10), addr(0x20), value);
assertNull(programContext.getValue(register, start.subtract(1), true));
assertEquals(value, programContext.getValue(register, start, true));
assertEquals(value, programContext.getValue(register, end, true));
assertNull(programContext.getValue(register, end.add(1), true));
long imageOffset = 0x5;
Address imageBase = addr(imageOffset);
program.setImageBase(imageBase, true);
assertNull(programContext.getValue(register, start.add(imageOffset - 1), true));
assertEquals(value, programContext.getValue(register, start.add(imageOffset), true));
assertEquals(value, programContext.getValue(register, end.add(imageOffset), true));
assertNull(programContext.getValue(register, end.add(imageOffset + 1), true));
program.endTransaction(id, false);
}
private Address addr(long offset) {
return space.getAddress(offset);
}
}

View File

@ -200,7 +200,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
private int languageMinorVersion;
private LanguageTranslator languageUpgradeTranslator;
private Address storedImageBase; // current image base maintained by addrMap
private boolean imageBaseOverride = false;
private boolean recordChanges;
@ -1119,7 +1118,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
private void refreshImageBase() throws IOException {
long baseOffset = getStoredBaseImageOffset();
storedImageBase = addressFactory.getDefaultAddressSpace().getAddress(baseOffset);
Address storedImageBase = addressFactory.getDefaultAddressSpace().getAddress(baseOffset);
if (!imageBaseOverride) {
Address currentImageBase = getImageBase();
if (!currentImageBase.equals(storedImageBase)) {
@ -1268,7 +1267,6 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
if (commit) {
try {
dataMap.put(IMAGE_OFFSET, Long.toHexString(base.getOffset()));
storedImageBase = base;
imageBaseOverride = false;
setChanged(ChangeManager.DOCR_IMAGE_BASE_CHANGED, oldBase, base);
@ -1736,9 +1734,7 @@ public class ProgramDB extends DomainObjectAdapterDB implements Program, ChangeM
refreshName();
overlaySpaceAdapter.updateOverlaySpaces(addressFactory);
addrMap.invalidateCache();
if (!imageBaseOverride) {
refreshImageBase();
}
refreshImageBase();
for (int i = 0; i < NUM_MANAGERS; i++) {
managers[i].invalidateCache(all);
}

View File

@ -87,12 +87,19 @@ public interface AddressMap {
* never be generated. The returned key ranges will correspond
* to those key ranges which have previously been created within
* the specified address range and may represent a much smaller subset
* of addresses within the specified range.
* @param start minimum address of range
* of addresses within the specified range.
* NOTE: if the create parameter is true, the given range must not extend in the upper 32 bits
* by more than 1 segment. For example, range(0x0000000000000000 - 0x0000000100000000)
* is acceptable, but the range (0x0000000000000000 - 0x0000000200000000) is not because the
* upper 32 bits differ by 2.
* @param start the start address of the range
* @param end maximum address of range
* @param create true if a new keys may be generated, otherwise returned
* key-ranges will be limited to those already defined.
* key-ranges will be limited to those already defined. And if true, the range will be limited
* to a size of 2^32 so that at most it creates two new address bases
* @return "sorted" list of KeyRange objects
* @throws UnsupportedOperationException if the given range is so large that the upper 32 bit
* segments differ by more than 1.
*/
public List<KeyRange> getKeyRanges(Address start, Address end, boolean create);
@ -136,7 +143,8 @@ public interface AddressMap {
* key-ranges will be limited to those already defined.
* @return "sorted" list of KeyRange objects
*/
public List<KeyRange> getKeyRanges(Address start, Address end, boolean absolute, boolean create);
public List<KeyRange> getKeyRanges(Address start, Address end, boolean absolute,
boolean create);
/**
* Generates a properly ordered list of database key ranges for a

View File

@ -15,6 +15,8 @@
*/
package ghidra.program.database.map;
import static generic.util.UnsignedDataUtils.*;
import java.io.IOException;
import java.util.*;
@ -811,15 +813,16 @@ public class AddressMapDB implements AddressMap {
/**
* Create all memory base segments within the specified range.
* NOTE: minAddress and maxAddress must have the same address space!
* @param minAddress
* @param maxAddress
* @param minAddress start address of the range
* @param maxAddress last address of the range
* @param absolute if the address are absolute and not relative
*/
private void createBaseSegments(Address minAddress, Address maxAddress) {
private void createBaseSegments(Address minAddress, Address maxAddress, boolean absolute) {
long minBase;
long maxBase;
if (isInDefaultAddressSpace(minAddress)) {
if (!absolute && isInDefaultAddressSpace(minAddress)) {
minBase = getNormalizedOffset(minAddress) & BASE_MASK;
maxBase = getNormalizedOffset(maxAddress) & BASE_MASK;
}
@ -828,7 +831,15 @@ public class AddressMapDB implements AddressMap {
maxBase = maxAddress.getOffset() & BASE_MASK;
}
for (long base = minBase; base <= maxBase; base += (MAX_OFFSET + 1)) {
long numBases = (maxBase >>> 32) - (minBase >>> 32);
if (numBases > 2) {
throw new UnsupportedOperationException("Can't create address bases for a range that" +
"extends across more than two upper 32 bit segments!");
}
for (long base = minBase; unsignedLessThanOrEqual(base, maxBase); base +=
(MAX_OFFSET + 1)) {
getBaseAddressIndex(minAddress.getNewAddress(base), false, INDEX_CREATE);
}
}
@ -836,17 +847,19 @@ public class AddressMapDB implements AddressMap {
/**
* Add simple key ranges where the address range lies within a single base segment for a single space.
* NOTE: start and end addresses must have the same address space!
* @param keyRangeList
* @param start
* @param end
* @param absolute
* @param create
* @param keyRangeList the list to store key ranges into
* @param start the start address
* @param end the end address
* @param absolute true if the address are to be encoded as absolute (not relative to the
* image base
* @param create if true, this method will add new address bases that are required to
* store addresses in that database that have that address base
*/
private void addKeyRanges(List<KeyRange> keyRangeList, Address start, Address end,
boolean absolute, boolean create) {
if (start.isMemoryAddress()) {
if (create) {
createBaseSegments(start, end);
createBaseSegments(start, end, absolute);
}
Address normalizedStart = absolute ? start : getShiftedAddr(start);
Address normalizedEnd = absolute ? end : getShiftedAddr(end);

View File

@ -15,12 +15,12 @@
*/
package ghidra.program.database.register;
import java.util.*;
import ghidra.program.model.address.*;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.util.*;
/**
* Associates objects with address ranges.
*/
@ -170,7 +170,8 @@ public class AddressRangeObjectMap<T> {
T object, AddressValueRange<T> newRange, int pos) {
AddressValueRange<T> previousRange = ranges.get(pos);
if ((start.previous() == null) || (previousRange.getEnd().compareTo(start.previous()) < 0)) {
if ((start.previous() == null) ||
(previousRange.getEnd().compareTo(start.previous()) < 0)) {
return newRange; // no overlap
}
@ -469,6 +470,10 @@ public class AddressRangeObjectMap<T> {
}
return new AddressRangeImpl(min, max);
}
public void clearCache() {
lastRange = null;
}
}
/**

View File

@ -209,4 +209,9 @@ public class DatabaseRangeMapAdapter implements RangeMapAdapter {
}
}
@Override
public void invalidate() {
rangeMap.invalidate();
}
}

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.
@ -143,4 +142,9 @@ public class InMemoryRangeMapAdapter implements RangeMapAdapter {
}
rangeMap = newRangeMap;
}
@Override
public void invalidate() {
rangeMap.clearCache();
}
}

View File

@ -175,6 +175,7 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
@Override
public void invalidateCache(boolean all) throws IOException {
this.invalidateReadCache();
invalidateRegisterStores();
}
@Override
@ -525,4 +526,12 @@ public class ProgramRegisterContextDB extends AbstractStoredProgramContext imple
}
}
private void invalidateRegisterStores() {
for (RegisterValueStore store : registerValueMap.values()) {
store.invalidate();
}
for (RegisterValueStore store : defaultRegisterValueMap.values()) {
store.invalidate();
}
}
}

View File

@ -0,0 +1,281 @@
/* ###
* 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.util;
import java.io.IOException;
import java.util.*;
import db.DBRecord;
import db.RecordIterator;
import ghidra.program.database.map.AddressKeyRecordIterator;
import ghidra.program.database.map.AddressMap;
import ghidra.program.model.address.*;
import ghidra.util.Lock;
/**
* An iterator over ranges that have a defined values in the AddressRangeMapDB
* <P>
* NOTE: this iterator is complicated by the fact that there can exist a record that represents
* an address range that "wraps around" from the max address to the 0 address, where this record
* actually represents two address ranges. This is cause by changing the image base which shifts
* all records up or down. That shift can cause a record to have a wrapping range where the start
* address is larger than the end address. If such a record exists, it is found during construction
* and the lower address range is extracted from the record and is stored as a special "start range"
* that should be emitted before any other ranges in that space. The upper range of a wrapping
* record will be handled more naturally during the iteration process. When a wrapping record is
* encountered during the normal iteration, only the upper range is used and it will be in the
* correct address range ordering.
*/
class AddressRangeMapIterator implements AddressRangeIterator {
private AddressRangeMapDB rangeMap;
private AddressMap addressMap;
private Lock lock;
private int expectedModCount;
private DBRecord nextRecord;
private RecordIterator recordIterator;
// this is the range from a wrapping address record that needs to be emitted before any other
// ranges in the default space. It is discovered during construction if it exists
private AddressRange startRange;
// these will be null if iterating over all records
private Address iteratorStart;
private Address iteratorEnd;
/**
* Constructs an AddressRangeIterator over all ranges that have a defined value
* @param rangeMap the AddressRangeMapDB to iterate over
* @throws IOException if a database I/O exception occurs
*/
AddressRangeMapIterator(AddressRangeMapDB rangeMap) throws IOException {
this.rangeMap = rangeMap;
this.lock = rangeMap.getLock();
this.addressMap = rangeMap.getAddressMap();
this.expectedModCount = rangeMap.getModCount();
this.recordIterator = new AddressKeyRecordIterator(rangeMap.getTable(), addressMap);
startRange = checkForStartRangeFromWrappingAddressRecord();
}
/**
* Constructs an AddressRangeIterator over all ranges greater than the given start address
* that have a defined value. If start is in the middle of a defined range, the first
* range will be truncated to start with the given start address.
* @param rangeMap the AddressRangeMapDB to iterate over
* @param start the address where the iterator should start
* @throws IOException if a database I/O exception occurs
*/
AddressRangeMapIterator(AddressRangeMapDB rangeMap, Address start) throws IOException {
this(rangeMap, start, null);
}
/**
* Constructs an AddressRangeIterator over all ranges between the given start address and the
* given end address that have a defined value. If start is in the middle of a defined range,
* the first range returned will truncated to start with the given start address.
* If the endAddress is in the middle of a defined range, the last range will be truncated to
* end with the given end address
* @param rangeMap the AddressRangeMapDB to iterate over
* @param start the address where the iterator should start
* @param end the address where the iterator should end
* @throws IOException if a database I/O exception occurs
*/
AddressRangeMapIterator(AddressRangeMapDB rangeMap, Address start, Address end)
throws IOException {
this.rangeMap = rangeMap;
this.lock = rangeMap.getLock();
this.addressMap = rangeMap.getAddressMap();
this.expectedModCount = rangeMap.getModCount();
this.iteratorStart = start;
this.iteratorEnd = getIteratorEnd(end);
// adjust start address to start of previous range if it contains this address
// so the iterator will include it
AddressRange range = rangeMap.getAddressRangeContaining(iteratorStart);
this.recordIterator = new AddressKeyRecordIterator(rangeMap.getTable(), addressMap,
range.getMinAddress(), iteratorEnd, range.getMinAddress(), true);
startRange = trimRange(checkForStartRangeFromWrappingAddressRecord());
}
@Override
public Iterator<AddressRange> iterator() {
return this;
}
@Override
public boolean hasNext() {
lock.acquire();
try {
if (expectedModCount != rangeMap.getModCount()) {
throw new ConcurrentModificationException();
}
if (nextRecord != null) {
return true;
}
if (startRange != null) {
return true;
}
if (recordIterator != null) {
try {
return recordIterator.hasNext();
}
catch (IOException e) {
rangeMap.dbError(e);
}
}
return false;
}
finally {
lock.release();
}
}
@Override
public AddressRange next() {
lock.acquire();
try {
if (expectedModCount != rangeMap.getModCount()) {
throw new ConcurrentModificationException();
}
if (nextRecord != null) {
DBRecord record = nextRecord;
nextRecord = null;
return getAddressRange(record);
}
if (recordIterator == null || !recordIterator.hasNext()) {
if (startRange != null) {
// there are no more items in the underlying iterator, so if we have a
// discovered start range, as described in the class header, then return that
AddressRange range = startRange;
startRange = null;
return range;
}
return null;
}
return getAddressRange(recordIterator.next());
}
catch (IOException e) {
rangeMap.dbError(e);
return null;
}
finally {
lock.release();
}
}
/**
* Computes an address range for the given record. If a wrapping record (a record whose start
* address is greater than its end address, so it really represents two ranges) is encountered,
* it only returns the high range. The low range is specially found in the constructor and
* returned before any other ranges in that same address space are returned.
* @param record the record to convert to an address range
* @return the address range represented by the given record
*/
private AddressRange getAddressRange(DBRecord record) {
List<AddressRange> ranges = rangeMap.getRangesForRecord(record);
if (ranges.isEmpty()) {
return null;
}
// Normally, there is only one range for a record. If it is an address wrapping record
// caused by an image base change, then the record will produce two ranges. While iterating
// we always want the last (higher) range.
AddressRange range = ranges.get(ranges.size() - 1);
// if there is a "start range" discovered in the constructor and this is the first
// range we see in the default space (the space with image base), then we need to return
// the start range and save this record for next time.
if (startRange != null && hasSameSpace(startRange.getMinAddress(), range.getMinAddress())) {
range = startRange;
startRange = null;
// save current record into next record so we process it again next time
nextRecord = record;
}
return trimRange(range);
}
private boolean hasSameSpace(Address address1, Address address2) {
AddressSpace space1 = address1.getAddressSpace();
AddressSpace space2 = address2.getAddressSpace();
return space1.equals(space2);
}
/**
* Look for a start range that needs to be issued before any other ranges in the default
* space.
* <P>
* Because of a changed image base, it is possible that a single record can represent
* multiple ranges, as described in the class header. This is the code that will look for that
* case and correct the ordering.
*
* @return the start range that needs to be issued before any other ranges in this space.
* @throws IOException if a database I/O error occurs
*/
private AddressRange checkForStartRangeFromWrappingAddressRecord()
throws IOException {
DBRecord record = rangeMap.getAddressWrappingRecord();
if (record == null) {
return null;
}
List<AddressRange> ranges = rangeMap.getRangesForRecord(record);
// we want the lower range - the upper range will be handle later during iteration
return ranges.get(0);
}
private Address getIteratorEnd(Address end) {
if (end != null) {
return end; // non-null end was supplied, use that
}
AddressFactory factory = addressMap.getAddressFactory();
AddressSet allAddresses = factory.getAddressSet();
return allAddresses.getMaxAddress();
}
/**
* Make sure the range is within the iterator's given start and end range. This really only
* matters for the first and last range returned by the iterator, but it hard to know when
* the given range is the first or last, just just trim all returned ranges.
* @param range the range to be trimmed
* @return the trimmed address range
*/
private AddressRange trimRange(AddressRange range) {
if (range == null) {
return null;
}
// if no iterator bounds set, no trimming needed
if (iteratorStart == null) {
return range;
}
if (range.compareTo(iteratorStart) > 0 && range.compareTo(iteratorEnd) < 0) {
return range;
}
Address start = Address.max(range.getMinAddress(), iteratorStart);
Address end = Address.min(range.getMaxAddress(), iteratorEnd);
if (start.compareTo(end) <= 0) {
return new AddressRangeImpl(start, end);
}
return null;
}
}

View File

@ -243,4 +243,5 @@ public interface ProgramContext {
* @return disassembly context register value
*/
public RegisterValue getDisassemblyContext(Address address);
}

View File

@ -109,4 +109,9 @@ public interface RangeMapAdapter {
*/
public void checkWritableState();
/**
* Notification that something has changed that may affect internal caching
*/
public void invalidate();
}

View File

@ -15,14 +15,14 @@
*/
package ghidra.program.util;
import java.util.*;
import ghidra.program.model.address.*;
import ghidra.program.model.lang.Register;
import ghidra.program.model.lang.RegisterValue;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor;
import java.util.*;
/**
* This is a generalized class for storing register values over ranges. The values include mask bits
* to indicate which bits within the register are being set. The mask is stored along with the
@ -324,4 +324,11 @@ public class RegisterValueStore {
return rangeMap.getValueRangeContaining(addr);
}
/**
* Notifies that something changed, may need to invalidate any caches
*/
public void invalidate() {
rangeMap.invalidate();
}
}