mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-1103 fixed issues related to moving memory blocks with pinned symbols in source or destination.
This commit is contained in:
parent
aa750ff7ae
commit
b937259b31
7 changed files with 435 additions and 128 deletions
|
@ -33,18 +33,25 @@ import ghidra.program.model.mem.*;
|
|||
import ghidra.program.model.symbol.*;
|
||||
import ghidra.program.model.util.AddressLabelInfo;
|
||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.exception.InvalidInputException;
|
||||
import ghidra.util.exception.NotFoundException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import ghidra.util.task.TaskMonitorAdapter;
|
||||
|
||||
public class PinnedSymbolTest extends AbstractGhidraHeadlessIntegrationTest {
|
||||
private static int EXPECTED_PROCESSOR_SYMBOLS = 9;
|
||||
private static int EXPECTED_USER_SYMBOLS = 2;
|
||||
private static int ORIGINAL_BOB_ADDRESS = 4;
|
||||
private static int ORIGINAL_FUNCTION_ADDRESS = 0xc;
|
||||
|
||||
private Program program;
|
||||
private AddressSpace space;
|
||||
private int transactionID;
|
||||
private SymbolTable symbolTable;
|
||||
|
||||
private Address originalBobAddress;
|
||||
private Address originalFunctionAddress;
|
||||
|
||||
public PinnedSymbolTest() {
|
||||
super();
|
||||
}
|
||||
|
@ -56,22 +63,29 @@ public class PinnedSymbolTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||
checkProcessorSymbolsInPlace(EXPECTED_PROCESSOR_SYMBOLS + EXPECTED_USER_SYMBOLS);
|
||||
assertNotNull(symbolTable.getPrimarySymbol(addr(4)));
|
||||
|
||||
program.setImageBase(addr(0x100), true);
|
||||
long imageBaseMove = 0x100;
|
||||
Address movedBobAddress = originalBobAddress.add(imageBaseMove);
|
||||
Address movedFunctionAddress = originalFunctionAddress.add(imageBaseMove);
|
||||
|
||||
program.setImageBase(addr(imageBaseMove), true);
|
||||
|
||||
// expect one new symbol for pinned function
|
||||
checkProcessorSymbolsInPlace(EXPECTED_PROCESSOR_SYMBOLS + EXPECTED_USER_SYMBOLS + 1);
|
||||
|
||||
// check bob symbol
|
||||
assertNotNull(symbolTable.getPrimarySymbol(addr(0x104)));
|
||||
assertEquals(0, symbolTable.getLabelHistory(addr(0x4)).length);
|
||||
assertEquals(1, symbolTable.getLabelHistory(addr(0x104)).length);
|
||||
assertNull(symbolTable.getPrimarySymbol(originalBobAddress));
|
||||
assertNotNull(symbolTable.getPrimarySymbol(movedBobAddress));
|
||||
|
||||
assertEquals(0, symbolTable.getLabelHistory(originalBobAddress).length);
|
||||
assertEquals(1, symbolTable.getLabelHistory(movedBobAddress).length);
|
||||
|
||||
// check function symbol - function should move, but pinned label should remain.
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(addr(0xc));
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(originalFunctionAddress);
|
||||
assertNotNull(symbol);
|
||||
assertEquals(SymbolType.LABEL, symbol.getSymbolType());
|
||||
assertEquals("MyFunction", symbol.getName());
|
||||
symbol = symbolTable.getPrimarySymbol(addr(0x10c));
|
||||
|
||||
symbol = symbolTable.getPrimarySymbol(movedFunctionAddress);
|
||||
assertNotNull(symbol);
|
||||
assertEquals(SymbolType.FUNCTION, symbol.getSymbolType());
|
||||
assertEquals(SourceType.DEFAULT, symbol.getSource());
|
||||
|
@ -84,25 +98,30 @@ public class PinnedSymbolTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||
MemoryBlockException, MemoryConflictException, NotFoundException {
|
||||
|
||||
checkProcessorSymbolsInPlace(EXPECTED_PROCESSOR_SYMBOLS + EXPECTED_USER_SYMBOLS);
|
||||
assertNotNull(symbolTable.getPrimarySymbol(addr(4)));
|
||||
assertNotNull(symbolTable.getPrimarySymbol(originalBobAddress));
|
||||
|
||||
long moveAmount = 0x200;
|
||||
Address movedBobAddress = originalBobAddress.add(moveAmount);
|
||||
Address movedFunctionAddress = originalFunctionAddress.add(moveAmount);
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
MemoryBlock block = memory.getBlock(addr(0));
|
||||
memory.moveBlock(block, addr(0x200), TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
memory.moveBlock(block, addr(moveAmount), TaskMonitorAdapter.DUMMY_MONITOR);
|
||||
|
||||
checkProcessorSymbolsInPlace(EXPECTED_PROCESSOR_SYMBOLS + EXPECTED_USER_SYMBOLS + 1);
|
||||
|
||||
// check bob symbol
|
||||
assertNotNull(symbolTable.getPrimarySymbol(addr(0x204)));
|
||||
assertEquals(0, symbolTable.getLabelHistory(addr(0x4)).length);
|
||||
assertEquals(1, symbolTable.getLabelHistory(addr(0x204)).length);
|
||||
assertNull(symbolTable.getPrimarySymbol(originalBobAddress));
|
||||
assertNotNull(symbolTable.getPrimarySymbol(movedBobAddress));
|
||||
assertEquals(0, symbolTable.getLabelHistory(originalBobAddress).length);
|
||||
assertEquals(1, symbolTable.getLabelHistory(movedBobAddress).length);
|
||||
|
||||
// check function symbol - function should move, but pinned label should remain.
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(addr(0xc));
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(originalFunctionAddress);
|
||||
assertNotNull(symbol);
|
||||
assertEquals(SymbolType.LABEL, symbol.getSymbolType());
|
||||
assertEquals("MyFunction", symbol.getName());
|
||||
symbol = symbolTable.getPrimarySymbol(addr(0x20c));
|
||||
symbol = symbolTable.getPrimarySymbol(movedFunctionAddress);
|
||||
assertNotNull(symbol);
|
||||
assertEquals(SymbolType.FUNCTION, symbol.getSymbolType());
|
||||
assertEquals(SourceType.DEFAULT, symbol.getSource());
|
||||
|
@ -113,7 +132,7 @@ public class PinnedSymbolTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||
public void testDeleteMemoryBlock() throws LockException {
|
||||
|
||||
checkProcessorSymbolsInPlace(EXPECTED_PROCESSOR_SYMBOLS + EXPECTED_USER_SYMBOLS);
|
||||
assertNotNull(symbolTable.getPrimarySymbol(addr(4)));
|
||||
assertNotNull(symbolTable.getPrimarySymbol(originalBobAddress));
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
MemoryBlock block = memory.getBlock(addr(0));
|
||||
|
@ -122,17 +141,106 @@ public class PinnedSymbolTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||
checkProcessorSymbolsInPlace(EXPECTED_PROCESSOR_SYMBOLS + 1);
|
||||
|
||||
// check bob symbol is gone
|
||||
assertNull(symbolTable.getPrimarySymbol(addr(4)));
|
||||
assertEquals(0, symbolTable.getLabelHistory(addr(0x4)).length);
|
||||
assertNull(symbolTable.getPrimarySymbol(originalBobAddress));
|
||||
assertEquals(0, symbolTable.getLabelHistory(originalBobAddress).length);
|
||||
|
||||
// check the pinned function symbol is now just a pinned code symbol
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(addr(0xc));
|
||||
Symbol symbol = symbolTable.getPrimarySymbol(originalFunctionAddress);
|
||||
assertNotNull(symbol);
|
||||
assertEquals(SymbolType.LABEL, symbol.getSymbolType());
|
||||
assertEquals("MyFunction", symbol.getName());
|
||||
assertTrue(symbol.isPinned());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveMemoryBlockSymbolAlreadyExistsAtDestination() throws Exception {
|
||||
long moveAmount = 0x200;
|
||||
Address movedBobAddress = originalBobAddress.add(moveAmount);
|
||||
|
||||
symbolTable.createLabel(movedBobAddress, "Joe", SourceType.USER_DEFINED);
|
||||
|
||||
Symbol[] symbolsAtOrig = symbolTable.getSymbols(originalBobAddress);
|
||||
assertTrue(symbolsAtOrig[0].isPrimary());
|
||||
|
||||
Symbol[] symbolsAtMoved = symbolTable.getSymbols(movedBobAddress);
|
||||
assertTrue(symbolsAtMoved[0].isPrimary());
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
MemoryBlock block = memory.getBlock(addr(0));
|
||||
memory.moveBlock(block, addr(moveAmount), TaskMonitor.DUMMY);
|
||||
|
||||
symbolsAtOrig = symbolTable.getSymbols(originalBobAddress);
|
||||
assertEquals(0, symbolsAtOrig.length);
|
||||
|
||||
symbolsAtMoved = symbolTable.getSymbols(movedBobAddress);
|
||||
assertEquals(2, symbolsAtMoved.length);
|
||||
|
||||
assertEquals("Joe", symbolsAtMoved[0].getName());
|
||||
assertEquals("Bob", symbolsAtMoved[1].getName());
|
||||
|
||||
assertTrue(symbolsAtMoved[0].isPrimary()); // joe should be primary
|
||||
assertTrue(!symbolsAtMoved[1].isPrimary()); // bob should not be primary
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moveBlockWithFunctionFromOnePinnedSymbolToAnother() throws Exception {
|
||||
long moveAmount = 0x100;
|
||||
Address functionAddressAfterMove = addr(ORIGINAL_FUNCTION_ADDRESS + moveAmount);
|
||||
symbolTable.createLabel(functionAddressAfterMove, "TARGET", SourceType.USER_DEFINED);
|
||||
Symbol target = symbolTable.getPrimarySymbol(functionAddressAfterMove);
|
||||
target.setPinned(true);
|
||||
|
||||
assertEquals(SymbolType.LABEL, target.getSymbolType());
|
||||
assertTrue(target.isPinned());
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
MemoryBlock block = memory.getBlock(addr(0));
|
||||
memory.moveBlock(block, addr(moveAmount), TaskMonitor.DUMMY);
|
||||
|
||||
Symbol result = symbolTable.getPrimarySymbol(functionAddressAfterMove);
|
||||
assertEquals(SymbolType.FUNCTION, result.getSymbolType());
|
||||
assertEquals("TARGET", result.getName());
|
||||
assertTrue(result.isPinned());
|
||||
|
||||
Symbol leftover = symbolTable.getPrimarySymbol(originalFunctionAddress);
|
||||
assertEquals(SymbolType.LABEL, leftover.getSymbolType());
|
||||
assertEquals("MyFunction", leftover.getName());
|
||||
assertTrue(leftover.isPinned());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPinnedStayWhenBlockMovedOnTopOfThem() throws Exception {
|
||||
Address moveToAddress = addr(0x200);
|
||||
|
||||
Symbol symbol = symbolTable.createLabel(moveToAddress, "MyPinned", SourceType.USER_DEFINED);
|
||||
symbol.setPinned(true);
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
MemoryBlock block = memory.getBlock(addr(0));
|
||||
memory.moveBlock(block, moveToAddress, TaskMonitor.DUMMY);
|
||||
|
||||
SymbolIterator symbols = symbolTable.getSymbols("MyPinned");
|
||||
Symbol s = symbols.next();
|
||||
assertEquals(moveToAddress, s.getAddress());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLabelCollision() throws Exception {
|
||||
int moveAmount = 0x100;
|
||||
Address movedBobAddress = originalBobAddress.add(moveAmount);
|
||||
symbolTable.createLabel(movedBobAddress, "Bob", SourceType.USER_DEFINED);
|
||||
|
||||
Memory memory = program.getMemory();
|
||||
MemoryBlock block = memory.getBlock(addr(0));
|
||||
memory.moveBlock(block, addr(0x100), TaskMonitor.DUMMY);
|
||||
|
||||
Symbol[] symbols = symbolTable.getSymbols(movedBobAddress);
|
||||
assertEquals(1, symbols.length);
|
||||
assertEquals("Bob", symbols[0].getName());
|
||||
}
|
||||
|
||||
private void checkProcessorSymbolsInPlace(int expectedSymbols) {
|
||||
assertEquals(expectedSymbols, symbolTable.getNumSymbols());// 8 processor symbols and 2 user defined
|
||||
assertNotNull(symbolTable.getPrimarySymbol(addr(0)));
|
||||
|
@ -160,12 +268,17 @@ public class PinnedSymbolTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||
program = new ProgramDB("z80", lang, lang.getDefaultCompilerSpec(), this);
|
||||
symbolTable = program.getSymbolTable();
|
||||
space = program.getAddressFactory().getDefaultAddressSpace();
|
||||
|
||||
originalBobAddress = addr(ORIGINAL_BOB_ADDRESS);
|
||||
originalFunctionAddress = addr(ORIGINAL_FUNCTION_ADDRESS);
|
||||
|
||||
transactionID = program.startTransaction("Test");
|
||||
createMemBlock();
|
||||
createProcessorSymbols(lang);
|
||||
createBobSymbol();
|
||||
createPinnedFunctionSymbol();
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void createProcessorSymbols(Language lang) throws InvalidInputException {
|
||||
|
@ -179,14 +292,14 @@ public class PinnedSymbolTest extends AbstractGhidraHeadlessIntegrationTest {
|
|||
}
|
||||
|
||||
private void createBobSymbol() throws InvalidInputException {
|
||||
symbolTable.createLabel(addr(4), "Bob", SourceType.USER_DEFINED);
|
||||
symbolTable.createLabel(originalBobAddress, "Bob", SourceType.USER_DEFINED);
|
||||
}
|
||||
|
||||
private void createPinnedFunctionSymbol()
|
||||
throws DuplicateNameException, InvalidInputException, OverlappingFunctionException {
|
||||
Address addr = addr(0xc);
|
||||
AddressSet set = new AddressSet(addr);
|
||||
Function fun = program.getFunctionManager().createFunction("MyFunction", addr, set,
|
||||
throws InvalidInputException, OverlappingFunctionException {
|
||||
AddressSet set = new AddressSet(originalFunctionAddress);
|
||||
Function fun = program.getFunctionManager()
|
||||
.createFunction("MyFunction", originalFunctionAddress, set,
|
||||
SourceType.USER_DEFINED);
|
||||
Symbol symbol = fun.getSymbol();
|
||||
symbol.setPinned(true);
|
||||
|
|
|
@ -134,22 +134,38 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
|||
oldAddr, addr);
|
||||
}
|
||||
|
||||
protected void move(Address oldBase, Address newBase) {
|
||||
/**
|
||||
* low level record adjustment to move a symbol. Used only when moving a memory block or
|
||||
* changing the image base.
|
||||
*
|
||||
* @param newAddress the new address for the symbol
|
||||
* @param newName the new name for the symbol (or null if the name should stay the same)
|
||||
* @param newNamespace the new namespace for the symbol (or null if it should stay the same)
|
||||
* @param newSource
|
||||
*/
|
||||
protected void moveLowLevel(Address newAddress, String newName, Namespace newNamespace,
|
||||
SourceType newSource, boolean pinned) {
|
||||
lock.acquire();
|
||||
try {
|
||||
checkDeleted();
|
||||
// check if this symbol is in the address space that was moved.
|
||||
if (!oldBase.getAddressSpace().equals(address.getAddressSpace())) {
|
||||
return;
|
||||
}
|
||||
ProgramDB program = symbolMgr.getProgram();
|
||||
Address oldAddress = address;
|
||||
long diff = address.subtract(oldBase);
|
||||
address = newBase.addWrap(diff);
|
||||
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_ADDR_COL,
|
||||
program.getAddressMap().getKey(address, true));
|
||||
symbolMgr.getAddressMap().getKey(newAddress, true));
|
||||
if (newName != null) {
|
||||
record.setString(SymbolDatabaseAdapter.SYMBOL_NAME_COL, newName);
|
||||
if (newName.length() == 0) {
|
||||
setSourceFlagBit(SourceType.DEFAULT);
|
||||
}
|
||||
}
|
||||
if (newNamespace != null) {
|
||||
record.setLongValue(SymbolDatabaseAdapter.SYMBOL_PARENT_COL, newNamespace.getID());
|
||||
}
|
||||
if (newSource != null) {
|
||||
setSourceFlagBit(newSource);
|
||||
}
|
||||
updatePinnedFlag(pinned);
|
||||
updateRecord();
|
||||
symbolMgr.moveLabelHistory(oldAddress, address);
|
||||
setInvalid();
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
|
@ -406,12 +422,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
|||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
if (record != null) {
|
||||
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
|
||||
byte clearBits = SymbolDatabaseAdapter.SYMBOL_SOURCE_BITS;
|
||||
byte setBits = (byte) newSource.ordinal();
|
||||
flags &= ~clearBits;
|
||||
flags |= setBits;
|
||||
record.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, flags);
|
||||
setSourceFlagBit(newSource);
|
||||
updateRecord();
|
||||
symbolMgr.symbolSourceChanged(this);
|
||||
}
|
||||
|
@ -421,6 +432,15 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
|||
}
|
||||
}
|
||||
|
||||
private void setSourceFlagBit(SourceType newSource) {
|
||||
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
|
||||
byte clearBits = SymbolDatabaseAdapter.SYMBOL_SOURCE_BITS;
|
||||
byte setBits = (byte) newSource.ordinal();
|
||||
flags &= ~clearBits;
|
||||
flags |= setBits;
|
||||
record.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SourceType getSource() {
|
||||
lock.acquire();
|
||||
|
@ -461,7 +481,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
|||
|
||||
@Override
|
||||
public void setPinned(boolean pinned) {
|
||||
throw new UnsupportedOperationException("Only Code Symbols may be pinned.");
|
||||
throw new UnsupportedOperationException("Only Code and Function Symbols may be pinned.");
|
||||
}
|
||||
|
||||
protected void doSetPinned(boolean pinned) {
|
||||
|
@ -472,6 +492,17 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
|||
return;
|
||||
}
|
||||
if (record != null) {
|
||||
updatePinnedFlag(pinned);
|
||||
updateRecord();
|
||||
symbolMgr.symbolAnchoredFlagChanged(this);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePinnedFlag(boolean pinned) {
|
||||
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
|
||||
if (pinned) {
|
||||
flags |= SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
|
||||
|
@ -480,13 +511,6 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
|
|||
flags &= ~SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
|
||||
}
|
||||
record.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, flags);
|
||||
updateRecord();
|
||||
symbolMgr.symbolAnchoredFlagChanged(this);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -215,19 +215,6 @@ abstract class SymbolDatabaseAdapter {
|
|||
*/
|
||||
abstract void moveAddress(Address oldAddr, Address newAddr) throws IOException;
|
||||
|
||||
/**
|
||||
* Update the addresses in all records to reflect the movement of a memory block.
|
||||
* @param fromAddr minimum address of the original block to be moved
|
||||
* @param toAddr the new minimum address after the block move
|
||||
* @param length the number of bytes in the memory block being moved
|
||||
* @param monitor progress monitor
|
||||
* @return returns the set of addresses where symbols where not moved because they were anchored
|
||||
* @throws CancelledException
|
||||
* @throws IOException
|
||||
*/
|
||||
abstract void moveAddressRange(Address fromAddr, Address toAddr, long length,
|
||||
TaskMonitor monitor) throws CancelledException, IOException;
|
||||
|
||||
/**
|
||||
* Delete all records which contain addresses within the specified range
|
||||
* @param startAddr minimum address in range
|
||||
|
|
|
@ -188,21 +188,11 @@ class SymbolDatabaseAdapterV0 extends SymbolDatabaseAdapter {
|
|||
V0_SYMBOL_ADDR_COL, addrMap, start, end, forward)));
|
||||
}
|
||||
|
||||
void deleteExternalEntries(Address start, Address end) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
void moveAddress(Address oldAddr, Address newAddr) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<Address> deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
|
|
|
@ -22,7 +22,8 @@ import java.io.IOException;
|
|||
import java.util.Set;
|
||||
|
||||
import db.*;
|
||||
import ghidra.program.database.map.*;
|
||||
import ghidra.program.database.map.AddressIndexPrimaryKeyIterator;
|
||||
import ghidra.program.database.map.AddressMap;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.symbol.*;
|
||||
|
@ -181,22 +182,11 @@ class SymbolDatabaseAdapterV1 extends SymbolDatabaseAdapter {
|
|||
return new V1ConvertedRecordIterator(symbolTable.indexIterator(V1_SYMBOL_NAME_COL));
|
||||
}
|
||||
|
||||
void deleteExternalEntries(Address start, Address end) throws IOException {
|
||||
AddressRecordDeleter.deleteRecords(symbolTable, V1_SYMBOL_ADDR_COL, addrMap, start, end,
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
void moveAddress(Address oldAddr, Address newAddr) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<Address> deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.util.Set;
|
|||
|
||||
import db.*;
|
||||
import ghidra.program.database.map.*;
|
||||
import ghidra.program.database.util.DatabaseTableUtils;
|
||||
import ghidra.program.database.util.RecordFilter;
|
||||
import ghidra.program.model.address.Address;
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
|
@ -271,10 +270,6 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
|
|||
SYMBOL_ADDR_COL, addrMap, start, end, forward));
|
||||
}
|
||||
|
||||
void deleteExternalEntries(Address start, Address end) throws IOException {
|
||||
AddressRecordDeleter.deleteRecords(symbolTable, SYMBOL_ADDR_COL, addrMap, start, end, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
void moveAddress(Address oldAddr, Address newAddr) throws IOException {
|
||||
LongField oldKey = new LongField(addrMap.getKey(oldAddr, false));
|
||||
|
@ -287,14 +282,6 @@ class SymbolDatabaseAdapterV2 extends SymbolDatabaseAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
|
||||
DatabaseTableUtils.updateIndexedAddressField(symbolTable, SYMBOL_ADDR_COL, addrMap,
|
||||
fromAddr, toAddr, length, null, monitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<Address> deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
|
||||
throws CancelledException, IOException {
|
||||
|
|
|
@ -2144,21 +2144,171 @@ public class SymbolManager implements SymbolTable, ManagerDB {
|
|||
@Override
|
||||
public void moveAddressRange(Address fromAddr, Address toAddr, long length, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
if (fromAddr.equals(toAddr)) {
|
||||
return;
|
||||
}
|
||||
Set<Address> primaryFixups = new HashSet<>();
|
||||
lock.acquire();
|
||||
try {
|
||||
invalidateCache(true);
|
||||
adapter.moveAddressRange(fromAddr, toAddr, length, monitor);
|
||||
historyAdapter.moveAddressRange(fromAddr, toAddr, length, addrMap, monitor);
|
||||
fixupPinnedSymbols(toAddr, fromAddr, toAddr, toAddr.add(length - 1));
|
||||
Address lastAddress = fromAddr.add(length-1);
|
||||
AddressRange range = new AddressRangeImpl(fromAddr, lastAddress);
|
||||
|
||||
// in order to handle overlapping ranges, need to iterate in the correct direction
|
||||
SymbolIterator symbolIterator = (fromAddr.compareTo(toAddr) > 0) ?
|
||||
getSymbolIterator(fromAddr, true) :
|
||||
getSymbolIterator(lastAddress, false);
|
||||
|
||||
for (Symbol symbol : symbolIterator) {
|
||||
if (!range.contains(symbol.getAddress())) {
|
||||
break;
|
||||
}
|
||||
catch (IOException e) {
|
||||
program.dbError(e);
|
||||
Address newAddress = toAddr.add(symbol.getAddress().subtract(fromAddr));
|
||||
|
||||
// any address that has symbols added or removed may have a corrupted primary (too many or non-existent)
|
||||
primaryFixups.add(symbol.getAddress());
|
||||
primaryFixups.add(newAddress);
|
||||
|
||||
moveSymbolForMemoryBlockMove((SymbolDB) symbol, newAddress);
|
||||
}
|
||||
// go back and make sure there is a valid primary symbol at touched addressess
|
||||
fixupPrimarySymbols(primaryFixups);
|
||||
}
|
||||
finally {
|
||||
lock.release();
|
||||
}
|
||||
}
|
||||
|
||||
// This method is specifically for moving symbols in the context of a memory block move
|
||||
// Since this is a memory block move, the destination range can not contain any functions, but
|
||||
// it may contain labels so we may have to deal with collisions. The other complication we have
|
||||
// to deal with is functions in the moved block that are currently pinned.
|
||||
private void moveSymbolForMemoryBlockMove(SymbolDB symbol, Address newAddress) {
|
||||
Address oldAddress = symbol.getAddress();
|
||||
|
||||
// If the symbol is not pinned go ahead and move it. Only wrinkle is that there may
|
||||
// be a matching symbol at the destination. In that unlikely event, remove it, but preserve
|
||||
// its pinned status
|
||||
if (!symbol.isPinned()) {
|
||||
// if we conflict with a symbol at the destination, delete it (it can't be a function)
|
||||
// and retain its pinned status if it had one.
|
||||
boolean shouldPin = deleteMatchingSymbolAndCheckPinnedStatus(symbol, newAddress);
|
||||
symbol.moveLowLevel(newAddress, null, null, null, shouldPin);
|
||||
moveLabelHistory(oldAddress, newAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
// the other complicated case is a pinned function symbol. In this case we need to move
|
||||
// the function symbol, but create a replacement label in the pinned source location. Also,
|
||||
// if there is a primary symbol at the destination, we need to remove it and set the
|
||||
// function's symbol to that name.
|
||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||
String originalName = symbol.getName();
|
||||
Namespace originalNamespace = symbol.getParentNamespace();
|
||||
SourceType originalSource = symbol.getSource();
|
||||
String newName = "";
|
||||
Namespace newNamespace = namespaceMgr.getGlobalNamespace();
|
||||
SourceType newSource = SourceType.DEFAULT;
|
||||
boolean newPinned = false;
|
||||
// see if there is a symbol at the destination
|
||||
Symbol destinationPrimary = getPrimarySymbol(newAddress);
|
||||
|
||||
// so there is a symbol at the destination, steal its name and namespace and delete it.
|
||||
if (destinationPrimary != null) {
|
||||
newName = destinationPrimary.getName();
|
||||
newNamespace = destinationPrimary.getParentNamespace(); // use the destination's namespace
|
||||
newSource = destinationPrimary.getSource();
|
||||
newPinned = destinationPrimary.isPinned();
|
||||
destinationPrimary.delete();
|
||||
}
|
||||
try {
|
||||
// so move the symbol to the new address and update namespace, source, and pinned state
|
||||
symbol.moveLowLevel(newAddress, newName, newNamespace, newSource, newPinned);
|
||||
|
||||
// create a pinned label to replace the pinned function symbol at the source.
|
||||
Symbol newSymbol =
|
||||
createLabel(oldAddress, originalName, originalNamespace, originalSource);
|
||||
newSymbol.setPinned(true);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
// can't happen - name was already valid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if there is a matching symbol at the given address and deletes it. Also returns if
|
||||
// the deleted symbol was pinned which we will preserve on the moved symbol
|
||||
private boolean deleteMatchingSymbolAndCheckPinnedStatus(SymbolDB symbol, Address address) {
|
||||
boolean isPinned = false;
|
||||
Symbol match = getSymbol(symbol.getName(), address, symbol.getParentNamespace());
|
||||
if (match != null) {
|
||||
isPinned = match.isPinned();
|
||||
match.delete();
|
||||
}
|
||||
return isPinned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to make sure there is a single valid primary symbol at each address
|
||||
* @param set the set of addresses that may have to be fixed up
|
||||
*/
|
||||
private void fixupPrimarySymbols(Set<Address> set) {
|
||||
for (Address address : set) {
|
||||
Symbol[] symbols = getSymbols(address);
|
||||
|
||||
// check if there is a valid consistent primary state amongst all the symbols at an address.
|
||||
if (hasValidPrimary(symbols)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise, set them all to non-primary, find the best symbol to make primary and
|
||||
// make that one the primary symbol
|
||||
setAllSymbolsToNonPrimary(symbols);
|
||||
Symbol bestPrimary = findBestPrimary(symbols);
|
||||
bestPrimary.setPrimary();
|
||||
}
|
||||
}
|
||||
|
||||
private void setAllSymbolsToNonPrimary(Symbol[] symbols) {
|
||||
for (Symbol symbol : symbols) {
|
||||
if (symbol instanceof CodeSymbol) {
|
||||
((CodeSymbol) symbol).setPrimary(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Symbol findBestPrimary(Symbol[] symbols) {
|
||||
// first see if see if there is a function, if so that has to be the primary symbol
|
||||
for (Symbol symbol : symbols) {
|
||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||
return symbol;
|
||||
}
|
||||
}
|
||||
|
||||
// else just pick one. This is such a rare case, it isn't worth doing something more clever
|
||||
return symbols[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the givens symbols from the same address have exactly one primary symbol amongst them
|
||||
* @param symbols the array of symbols at a an address
|
||||
* @return true if there is exactly one primary symbol at the address (also true if no symbols at address)
|
||||
*/
|
||||
private boolean hasValidPrimary(Symbol[] symbols) {
|
||||
if (symbols.length == 0) {
|
||||
return true;
|
||||
}
|
||||
if (!symbols[0].isPrimary()) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 1; i < symbols.length; i++) {
|
||||
if (symbols[i].isPrimary()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return getPrimarySymbol(symbols[0].getAddress()) == symbols[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAddressRange(Address startAddr, Address endAddr, TaskMonitor monitor)
|
||||
throws CancelledException {
|
||||
|
@ -2176,43 +2326,109 @@ public class SymbolManager implements SymbolTable, ManagerDB {
|
|||
}
|
||||
}
|
||||
|
||||
public void imageBaseChanged(Address oldBase, Address base) {
|
||||
fixupPinnedSymbols(base, oldBase, program.getMinAddress(), program.getMaxAddress());
|
||||
public void imageBaseChanged(Address oldBase, Address newBase) {
|
||||
AddressSpace space = newBase.getAddressSpace();
|
||||
fixupPinnedSymbolsAfterRebase(oldBase, newBase, space.getMinAddress(),
|
||||
space.getMaxAddress());
|
||||
}
|
||||
|
||||
private void fixupPinnedSymbols(Address currentBase, Address newBase, Address minAddr,
|
||||
private void fixupPinnedSymbolsAfterRebase(Address oldBase, Address base, Address minAddr,
|
||||
Address maxAddr) {
|
||||
List<SymbolDB> fixupSymbols = new ArrayList<>();
|
||||
|
||||
List<SymbolDB> fixupPinnedSymbols = findPinnedSymbols(minAddr, maxAddr);
|
||||
|
||||
Set<Address> primaryFixups = new HashSet<>();
|
||||
for (SymbolDB symbol : fixupPinnedSymbols) {
|
||||
Address currentAddress = symbol.getAddress();
|
||||
Address beforeBaseChangeAddress = oldBase.add(currentAddress.subtract(base));
|
||||
primaryFixups.add(currentAddress);
|
||||
primaryFixups.add(beforeBaseChangeAddress);
|
||||
|
||||
// see if there is a name collision for the pinned symbol we are about to move back
|
||||
Symbol match =
|
||||
getSymbol(symbol.getName(), beforeBaseChangeAddress, symbol.getParentNamespace());
|
||||
|
||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||
fixupPinnedFunctionSymbolAfterRebase(symbol, beforeBaseChangeAddress, match);
|
||||
}
|
||||
else {
|
||||
fixupPinnedLabelSymbolAfterRebase(symbol, beforeBaseChangeAddress, match);
|
||||
}
|
||||
}
|
||||
fixupPrimarySymbols(primaryFixups);
|
||||
}
|
||||
|
||||
private void fixupPinnedLabelSymbolAfterRebase(SymbolDB symbol, Address newAddress,
|
||||
Symbol match) {
|
||||
if (match != null) {
|
||||
match.setPinned(true);
|
||||
symbol.delete();
|
||||
}
|
||||
else {
|
||||
symbol.moveLowLevel(newAddress, null, null, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void fixupPinnedFunctionSymbolAfterRebase(SymbolDB symbol, Address newAddress,
|
||||
Symbol match) {
|
||||
// since we are a function, we are not moving the symbol, so we must either pin a
|
||||
// matching symbol at the destination or create a new pinned label at the destination.
|
||||
if (match != null) {
|
||||
match.setPinned(true);
|
||||
}
|
||||
else {
|
||||
try {
|
||||
Symbol newLabel =
|
||||
createLabel(newAddress, symbol.getName(), symbol.getParentNamespace(),
|
||||
symbol.getSource());
|
||||
newLabel.setPinned(true);
|
||||
}
|
||||
catch (InvalidInputException e) {
|
||||
// can't happen, we already know it is valid
|
||||
}
|
||||
}
|
||||
|
||||
// unpin the function symbol since it is not moving.
|
||||
symbol.setPinned(false);
|
||||
|
||||
// now, either set the function to default or find another symbol to promote.
|
||||
String newName = "";
|
||||
Namespace newNamespace = namespaceMgr.getGlobalNamespace();
|
||||
SourceType newSource = SourceType.DEFAULT;
|
||||
Symbol symbolToPromote = findSymbolToPromote(symbol.getAddress());
|
||||
if (symbolToPromote != null) {
|
||||
newName = symbolToPromote.getName();
|
||||
newNamespace = symbolToPromote.getParentNamespace();
|
||||
newSource = symbolToPromote.getSource();
|
||||
symbolToPromote.delete();
|
||||
}
|
||||
try {
|
||||
symbol.setNameAndNamespace(newName, newNamespace, newSource);
|
||||
}
|
||||
catch (DuplicateNameException | InvalidInputException | CircularDependencyException e) {
|
||||
// can't happen
|
||||
}
|
||||
}
|
||||
|
||||
private List<SymbolDB> findPinnedSymbols(Address minAddr, Address maxAddr) {
|
||||
List<SymbolDB> pinnedSymbols = new ArrayList<>();
|
||||
for (Symbol symbol : getSymbolIterator(minAddr, true)) {
|
||||
if (symbol.getAddress().compareTo(maxAddr) > 0) {
|
||||
break;
|
||||
}
|
||||
if (symbol.isPinned()) {
|
||||
fixupSymbols.add((SymbolDB) symbol);
|
||||
pinnedSymbols.add((SymbolDB) symbol);
|
||||
}
|
||||
}
|
||||
for (SymbolDB symbol : fixupSymbols) {
|
||||
if (symbol.getSymbolType() == SymbolType.FUNCTION) {
|
||||
String name = symbol.getName();
|
||||
SourceType source = symbol.getSource();
|
||||
try {
|
||||
symbol.setPinned(false);
|
||||
symbol.setName("", SourceType.DEFAULT);
|
||||
Address symbolAddr =
|
||||
newBase.addNoWrap(symbol.getAddress().subtract(currentBase));
|
||||
Symbol newSymbol = createLabel(symbolAddr, name, source);
|
||||
newSymbol.setPinned(true);
|
||||
moveLabelHistory(symbol.getAddress(), newSymbol.getAddress());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new AssertException("Should not get exception here.", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
symbol.move(currentBase, newBase);
|
||||
}
|
||||
return pinnedSymbols;
|
||||
}
|
||||
|
||||
private Symbol findSymbolToPromote(Address address) {
|
||||
Symbol[] symbols = getSymbols(address);
|
||||
if (symbols.length > 1) {
|
||||
return symbols[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void moveLabelHistory(Address oldAddress, Address address) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue