GP-1103 fixed issues related to moving memory blocks with pinned symbols in source or destination.

This commit is contained in:
ghidravore 2021-07-13 14:56:36 -04:00
parent aa750ff7ae
commit b937259b31
7 changed files with 435 additions and 128 deletions

View file

@ -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);

View file

@ -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,14 +492,7 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
return;
}
if (record != null) {
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
if (pinned) {
flags |= SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
}
else {
flags &= ~SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
}
record.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, flags);
updatePinnedFlag(pinned);
updateRecord();
symbolMgr.symbolAnchoredFlagChanged(this);
}
@ -489,6 +502,17 @@ public abstract class SymbolDB extends DatabaseObject implements Symbol {
}
}
private void updatePinnedFlag(boolean pinned) {
byte flags = record.getByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL);
if (pinned) {
flags |= SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
}
else {
flags &= ~SymbolDatabaseAdapter.SYMBOL_PINNED_FLAG;
}
record.setByteValue(SymbolDatabaseAdapter.SYMBOL_FLAGS_COL, flags);
}
@Override
public void setName(String newName, SourceType source)
throws DuplicateNameException, InvalidInputException {

View file

@ -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

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -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));
}
catch (IOException e) {
program.dbError(e);
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;
}
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);
}
}
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);
pinnedSymbols.add((SymbolDB) symbol);
}
}
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) {