blob: 0dc6d91193e62edab5070a370da6525bdfdf3329 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010 Nokia and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.internal.arm;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.debug.edc.MemoryUtils;
import org.eclipse.cdt.debug.edc.internal.PersistentCache;
import org.eclipse.cdt.debug.edc.services.IEDCDMContext;
import org.eclipse.cdt.debug.edc.services.IEDCExecutionDMC;
import org.eclipse.cdt.debug.edc.services.IEDCMemory;
import org.eclipse.cdt.debug.edc.services.IEDCModuleDMContext;
import org.eclipse.cdt.debug.edc.services.IEDCModules;
import org.eclipse.cdt.debug.edc.services.IEDCSymbols;
import org.eclipse.cdt.debug.edc.services.Registers;
import org.eclipse.cdt.debug.edc.services.Stack;
import org.eclipse.cdt.debug.edc.symbols.IEDCSymbolReader;
import org.eclipse.cdt.debug.edc.symbols.IFunctionScope;
import org.eclipse.cdt.debug.edc.symbols.ILineEntry;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.Addr64;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.debug.core.model.MemoryByte;
public class ARMStack extends Stack {
/** IModuleDMContext attribute: list of IAddress which are known or suspected to be thumb */
public static final String THUMB_ADDRESSES = "thumbAddresses";
private static final int MAX_FRAMES = 30;
private static final String FUNCTION_START_ADDRESS_CACHE = "_function_start_address";
private static final String FUNCTION_END_ADDRESS_CACHE = "_function_end_address";
/**
* Container for spilled registers.
*/
private class ARMSpilledRegisters {
/**
* General purpose registers R0-R12. Note that the addresses are the
* memory location where the register values are stored in the stack.
*/
public IAddress registers[] = new IAddress[13];
/**
* Stack pointer (R13)
*/
public IAddress SP;
/**
* Link register (R14)
*/
public IAddress LR;
/**
* Address of the link register on the stack
*/
public IAddress LRAddress;
/**
* Address where prolog is finished
*/
public IAddress firstInstructionAfterProlog;
public boolean isValid() {
// as long as the LR and SP are set then we're OK
return LR != null && SP != null;
}
public Map<Integer, BigInteger> getPreservedRegisters() {
Map<Integer, BigInteger> map = new HashMap<Integer, BigInteger>();
for (int i = 0; i < registers.length; i++) {
if (registers[i] != null) {
map.put(i, registers[i].getValue());
}
}
return map;
}
public IAddress fillLRFromStack(IEDCExecutionDMC context) {
// get the real value of the link register
IEDCMemory memoryService = getService(IEDCMemory.class);
ArrayList<MemoryByte> byteArray = new ArrayList<MemoryByte>(4);
IStatus status = memoryService.getMemory(context, LRAddress, byteArray, 4, 1);
if (!status.isOK()) {
return null;
}
// check each byte
for (int i = 0; i < byteArray.size(); i++) {
if (!byteArray.get(i).isReadable())
return null;
}
LR = new Addr64(MemoryUtils.convertByteArrayToUnsignedLong(byteArray.toArray(new MemoryByte[4]),
MemoryUtils.LITTLE_ENDIAN));
return LR;
}
}
public ARMStack(DsfSession session) {
super(session, new String[] { ARMStack.class.getName() });
}
@Override
protected List<EdcStackFrame> computeStackFrames(IEDCExecutionDMC context, int startIndex, int endIndex) throws CoreException {
ArrayList<EdcStackFrame> frames = new ArrayList<EdcStackFrame>();
Registers registersService = getService(Registers.class);
IAddress pcValue = new Addr64(registersService.getRegisterValue(context, ARMRegisters.PC), 16);
// get the SP and LR values based on the processor mode
String spName, lrName;
BigInteger cpsrValue = new BigInteger(registersService.getRegisterValue(context, ARMRegisters.CPSR), 16);
switch (cpsrValue.and(BigInteger.valueOf(0x0000001FL)).intValue()) {
case 0x11:
spName = ARMRegisters.SP_fiq;
lrName = ARMRegisters.LR_fiq;
break;
case 0x12:
spName = ARMRegisters.SP_irq;
lrName = ARMRegisters.LR_irq;
break;
case 0x13:
spName = ARMRegisters.SP_svc;
lrName = ARMRegisters.LR_svc;
break;
case 0x17:
spName = ARMRegisters.SP_abt;
lrName = ARMRegisters.LR_abt;
break;
case 0x1B:
spName = ARMRegisters.SP_und;
lrName = ARMRegisters.LR_und;
break;
case 0x1F:
spName = ARMRegisters.SP_sys;
lrName = ARMRegisters.LR_sys;
break;
case 0x10:
default:
spName = ARMRegisters.SP;
lrName = ARMRegisters.LR;
break;
}
IAddress spValue = new Addr64(registersService.getRegisterValue(context, spName), 16);
IAddress lrValue = new Addr64(registersService.getRegisterValue(context, lrName), 16);
int frameCount = 0;
HashMap<String, Object> properties = null;
if (endIndex == ALL_FRAMES)
endIndex = MAX_FRAMES;
Set<IAddress> thumbAddresses = null;
IEDCSymbols symbols = getService(IEDCSymbols.class);
PersistentCache armPluginCache = ARMPlugin.getDefault().getCache();
do {
IAddress functionStartAddress = null;
IAddress functionEndAddress = null;
String moduleName = "Unknown";
// see if the PC is in an executable that we know about
IEDCModuleDMContext module = getModule(context, pcValue);
if (module != null) {
moduleName = module.getName();
{ @SuppressWarnings("unchecked")
Set<IAddress> s = (Set<IAddress>)module.getProperty(THUMB_ADDRESSES);
thumbAddresses = s;
}
if (thumbAddresses == null) {
thumbAddresses = new TreeSet<IAddress>();
module.setProperty(THUMB_ADDRESSES, thumbAddresses);
}
IEDCSymbolReader reader = module.getSymbolReader();
if (reader != null && reader.getSymbolFile() != null)
{
String symbolFileOSString = reader.getSymbolFile().toOSString();
long modDate = reader.getModificationDate();
functionStartAddress
= getFunctionStartAddress(pcValue, context, symbols, armPluginCache,
module, symbolFileOSString, modDate);
if (frameCount == 0) {
functionEndAddress
= getFunctionEndAddress(pcValue, context, symbols, armPluginCache,
module, symbolFileOSString, modDate);
}
}
} else {
// null module; hang thumb addresses off process context
IDMContext[] parents = context.getParents();
if (parents != null) {
IEDCDMContext rootProcessDMC = null;
for (int i = parents.length-1 ; i >= 0; i--) {
if (parents[i] instanceof IEDCDMContext && parents[i] instanceof IProcessDMContext) {
rootProcessDMC = (IEDCDMContext) parents[i];
break;
}
}
if (rootProcessDMC != null) {
{ @SuppressWarnings("unchecked")
Set<IAddress> s = (Set<IAddress>) rootProcessDMC.getProperty(THUMB_ADDRESSES);
thumbAddresses = s;
}
if (thumbAddresses == null) {
thumbAddresses = new TreeSet<IAddress>();
rootProcessDMC.setProperty(THUMB_ADDRESSES, thumbAddresses);
}
}
}
}
boolean thumbMode
= ((TargetEnvironmentARM)getTargetEnvironmentService()).isThumbMode(context, pcValue,
frameCount == 0);
// mask off the thumb bit
pcValue = new Addr64(pcValue.getValue().clearBit(0));
if (thumbAddresses != null && thumbMode)
thumbAddresses.add(pcValue);
// add this frame
long baseAddress = functionStartAddress == null
? pcValue.getValue().longValue()
: functionStartAddress.getValue().longValue();
properties = new HashMap<String, Object>();
properties.put(StackFrameDMC.LEVEL_INDEX, frameCount);
properties.put(StackFrameDMC.BASE_ADDR, baseAddress);
properties.put(StackFrameDMC.INSTRUCTION_PTR_ADDR, pcValue.getValue().longValue());
properties.put(StackFrameDMC.MODULE_NAME, moduleName);
frames.add(new EdcStackFrame(properties));
if (functionStartAddress == null) {
// either module at address not known or we don't have symbols
// for it, so try to find the prolog by parsing the preceding
// instructions
functionStartAddress = findProlog(context, pcValue, thumbMode);
}
if (functionStartAddress != null) {
ARMSpilledRegisters spilledRegs = null;
/*
* This first test is for epilog; when instruction stepping
* (or hitting a breakpoint) in thumb epilog, the PC could
* be in the middle of a multi-instr mod to the SP.
*
* The test case looks like this:
* add sp,#0xXXX
* add sp,#0xXXX
* pop (rX,RX,...,pc)
*
* so, instead of relying on parsed prolog, parse the epilog
* to find out where it's popping the PC from on the stack,
* and re-adjust the live SP and other preserved registers along the way.
*/
if (frameCount == 0 && thumbMode && functionEndAddress != null && module != null) {
ISymbolDMContext sym_dmc = DMContexts.getAncestorOfType(context, ISymbolDMContext.class);
final List<ILineEntry> code
= symbols.getLineEntriesForAddressRange(sym_dmc, functionStartAddress, functionEndAddress);
IAddress lastAddrBeforeEpilog
= (code.size() == 0 ? null : module.toRuntimeAddress(code.get(code.size()-1).getLowAddress()));
if (lastAddrBeforeEpilog != null && pcValue.compareTo(lastAddrBeforeEpilog) > 0) {
spilledRegs = parseThumbEpilog(context, pcValue, spValue, functionEndAddress);
}
}
// if spilledRegs is still null, thumb epilog parsing
// didn't occur. the prolog still needs to be parsed
// to figure out where the LR was stored on the stack
if (spilledRegs == null)
spilledRegs = parseProlog(context, functionStartAddress, pcValue, spValue, lrValue, thumbMode);
// parsing prolog also failed ... run away, run away!
if (spilledRegs == null)
break;
// remember spilled registers in case debug info is missing
properties.put(StackFrameDMC.PRESERVED_REGISTERS, spilledRegs.getPreservedRegisters());
// identify whether we're not yet inside a new frame
if (frameCount == 0 && (spilledRegs.firstInstructionAfterProlog == null
|| pcValue.compareTo(spilledRegs.firstInstructionAfterProlog) < 0)) {
properties.put(StackFrameDMC.IN_PROLOGUE, true);
}
pcValue = spilledRegs.LR;
spValue = spilledRegs.SP;
} else if (frameCount == 0) {
// still don't know where the prolog is for sure. there may not be
// one at all. assume that the currnet SP and LR are correct since we're
// at the first frame.
pcValue = lrValue;
} else if (spName.compareTo(ARMRegisters.SP) != 0) {
// if we're not in user mode, we're probably handling an exception.
// try crawling from user mode sp/lr since we've gone as far as we
// can go on the kernel side
spValue = new Addr64(registersService.getRegisterValue(context, ARMRegisters.SP), 16);
pcValue = new Addr64(registersService.getRegisterValue(context, ARMRegisters.LR), 16);
} else {
// still don't know where the prolog is so no way to tell where
// the LR is saved on the stack (if at all)
break;
}
if (pcValue.isZero())
break;
frameCount++;
// keep going until we reach the maximum number of frames
} while (frameCount < endIndex);
if (properties != null) {
properties.put(StackFrameDMC.ROOT_FRAME, true);
}
return frames;
}
@SuppressWarnings("unchecked")
synchronized private IAddress getFunctionEndAddress(IAddress pcValue,
IEDCExecutionDMC context, IEDCSymbols symbols, PersistentCache pCache,
IEDCModuleDMContext module, String keyPrefix, long modDate) {
// Check the persistent cache
String key = keyPrefix + FUNCTION_END_ADDRESS_CACHE;
Map<IAddress, IAddress> cachedMapping = pCache.getCachedData(key, Map.class, modDate);
IAddress addr = getRuntimeAddressFromCache(pcValue, module, cachedMapping);
if (addr == null) {
IFunctionScope scope = getNonInlineFunctionAtAddress(context, pcValue, symbols);
if (scope != null) {
addr = getAndCacheRuntimeAddress(pcValue, scope.getHighAddress(), key,
cachedMapping, pCache, module, modDate);
}
}
return addr;
}
@SuppressWarnings("unchecked")
synchronized private IAddress getFunctionStartAddress(IAddress pcValue,
IEDCExecutionDMC context, IEDCSymbols symbols, PersistentCache pCache,
IEDCModuleDMContext module, String keyPrefix, long modDate) {
// Check the persistent cache
String key = keyPrefix + FUNCTION_START_ADDRESS_CACHE;
Map<IAddress, IAddress> cachedMapping = pCache.getCachedData(key, Map.class, modDate);
IAddress addr = getRuntimeAddressFromCache(pcValue, module, cachedMapping);
if (addr == null) {
IFunctionScope scope = getNonInlineFunctionAtAddress(context, pcValue, symbols);
if (scope != null) {
addr = getAndCacheRuntimeAddress(pcValue, scope.getLowAddress(), key,
cachedMapping, pCache, module, modDate);
}
}
return addr;
}
synchronized private IAddress getAndCacheRuntimeAddress(IAddress pcValueRuntimeAddress,
IAddress mapLinkAddress, String cacheKey, Map<IAddress, IAddress> cachedMapping,
PersistentCache armPluginCache, IEDCModuleDMContext module, long modDate) {
// put it in the cache
if (cachedMapping == null)
cachedMapping = new HashMap<IAddress, IAddress>();
cachedMapping.put(module.toLinkAddress(pcValueRuntimeAddress), mapLinkAddress);
armPluginCache.putCachedData(cacheKey, (Serializable) cachedMapping, modDate);
return module.toRuntimeAddress(mapLinkAddress);
}
protected IEDCModuleDMContext getModule(IEDCExecutionDMC context, IAddress address) {
IEDCModules modules = getService(IEDCModules.class);
return modules.getModuleByAddress(context.getSymbolDMContext(), address);
}
private static IFunctionScope getNonInlineFunctionAtAddress(
IEDCExecutionDMC context, IAddress pcValue, IEDCSymbols symbols) {
IFunctionScope scope = symbols.getFunctionAtAddress(context.getSymbolDMContext(), pcValue);
while (scope != null && scope.getParent() instanceof IFunctionScope)
scope = (IFunctionScope) scope.getParent();
return scope;
}
private static IAddress getRuntimeAddressFromCache(IAddress pcValueRuntimeAddress,
IEDCModuleDMContext module, Map<IAddress, IAddress> mapping) {
if (mapping != null)
{
IAddress mapLinkAddress = mapping.get(module.toLinkAddress(pcValueRuntimeAddress));
if (mapLinkAddress != null)
return module.toRuntimeAddress(mapLinkAddress);
}
return null;
}
private IAddress findProlog(IEDCExecutionDMC context, IAddress pcValue, boolean thumbMode) {
// read memory back from the PC so we can parse the instructions
IEDCMemory memoryService = getService(IEDCMemory.class);
int instructionSize = thumbMode ? 2 : 4;
long bytesToRead = 250 * instructionSize; // max 200 instructions
// for cases where the PC is small, only read back to 0x0
if (bytesToRead > pcValue.getValue().longValue()) {
bytesToRead = pcValue.getValue().longValue();
}
ArrayList<MemoryByte> byteArray = new ArrayList<MemoryByte>();
IStatus status = memoryService.getMemory(context, pcValue.add(-bytesToRead).add(instructionSize), byteArray, (int)bytesToRead, 1);
if (!status.isOK()) {
return null;
}
// check each byte
for (int i = 0; i < byteArray.size(); i++) {
if (!byteArray.get(i).isReadable())
return null;
}
List<BigInteger> instructions = new ArrayList<BigInteger>();
int index = 0;
while (index < bytesToRead) {
instructions.add(MemoryUtils.convertByteArrayToUnsignedLong(byteArray.subList(index, index + instructionSize)
.toArray(new MemoryByte[instructionSize]), MemoryUtils.LITTLE_ENDIAN));
index += instructionSize;
}
return thumbMode ? findThumbProlog(pcValue, instructions) : findArmProlog(pcValue, instructions);
}
private IAddress findArmProlog(IAddress pcValue, List<BigInteger> instructions) {
IAddress prologAddress = pcValue;
// parse backwards
for (int i = instructions.size() - 1; i >= 0; i--) {
BigInteger instruction = instructions.get(i);
// look for a stmfd instruction that saves the LR on the stack
if (isStmfdInstruction(instruction) && instruction.testBit(14)) {
return prologAddress;
}
// look for a str instruction that saves the LR on the stack
if (isStrLrToStackInstruction(instruction)) {
return prologAddress;
}
// special handling of SWI
if (isSWIInstruction(instruction)) {
return prologAddress;
}
// special handling of NOP
if (instruction.longValue() == 0xE1A00000L) {
return prologAddress;
}
// bail if we find an epilog (mov pc, lr)
if (instruction.longValue() == 0x0E1A0F0EL) {
break;
}
prologAddress = prologAddress.add(-4);
}
return null;
}
private IAddress findThumbProlog(IAddress pcValue, List<BigInteger> instructions) {
IAddress prologAddress = pcValue;
// parse backwards
for (int i = instructions.size() - 1; i >= 0; i--) {
BigInteger instruction = instructions.get(i);
if (isPushInstruction(instruction)) {
return prologAddress;
}
// bail if we find an epilog (bx lr)
if ((instruction.intValue() & 0xFFF8L) == 0x4770L) {
break;
}
prologAddress = prologAddress.add(-2);
}
return null;
}
private ARMSpilledRegisters parseProlog(IEDCExecutionDMC context, IAddress prologAddress, IAddress pcValue,
IAddress spValue, IAddress lrValue, boolean thumbMode) throws CoreException {
// read memory from the prolog address to the pc, or 20 bytes, whichever
// is less
IEDCMemory memoryService = getService(IEDCMemory.class);
ArrayList<MemoryByte> byteArray = new ArrayList<MemoryByte>();
int bytesToRead = prologAddress.distanceTo(pcValue).min(BigInteger.valueOf(20)).intValue();
if (bytesToRead > 0) {
// the PC is not at the start of the prolog, so parse from the start
// of the prolog to the PC, or 20 bytes, whichever is less.
IStatus status = memoryService.getMemory(context, prologAddress, byteArray, bytesToRead, 1);
if (!status.isOK()) {
return null;
}
// check each byte
for (int i = 0; i < byteArray.size(); i++) {
if (!byteArray.get(i).isReadable())
return null;
}
List<BigInteger> instructions = new ArrayList<BigInteger>();
int index = 0;
while (index < bytesToRead) {
if (thumbMode) {
instructions.add(MemoryUtils.convertByteArrayToUnsignedLong(byteArray.subList(index, index + 2)
.toArray(new MemoryByte[2]), MemoryUtils.LITTLE_ENDIAN));
index += 2;
} else {
instructions.add(MemoryUtils.convertByteArrayToUnsignedLong(byteArray.subList(index, index + 4)
.toArray(new MemoryByte[4]), MemoryUtils.LITTLE_ENDIAN));
index += 4;
}
}
// look for prolog instructions. if found, figure out the LR and SP
// values and return
ARMSpilledRegisters spilledRegs
= thumbMode ? parseThumbProlog(context, instructions, spValue, prologAddress)
: parseArmProlog(context, instructions, spValue, lrValue, prologAddress);
if (spilledRegs != null) {
return spilledRegs;
}
}
// we're either at the start of the prolog, or there is no prolog for
// this function (leaf function), so just use the real LR and SP
ARMSpilledRegisters spilledRegs = new ARMSpilledRegisters();
spilledRegs.SP = spValue;
spilledRegs.LR = lrValue;
if (pcValue.equals(spilledRegs.LR)) {
// prevent recursive loop
return null;
}
return spilledRegs;
}
private ARMSpilledRegisters parseArmProlog(IEDCExecutionDMC context, List<BigInteger> instructions,
IAddress spValue, IAddress lrValue, IAddress pcAddress) throws CoreException {
ARMSpilledRegisters spilledRegs = new ARMSpilledRegisters();
IAddress currentSP = spValue;
for (BigInteger instruction : instructions) {
// point to PC of next instruction
pcAddress = pcAddress.add(4);
if (isStmfdInstruction(instruction)) {
// figure out how many registers are being stored
BigInteger regBits = instruction.and(BigInteger.valueOf(0x0000FFFFL));
int regCount = 0;
for (int i = 0; i <= 15; i++) {
if (regBits.testBit(i)) {
regCount++;
}
}
// save off R0-R12 if needed
IAddress registerLocation = currentSP;
for (int i = 0; i < spilledRegs.registers.length; i++) {
if (regBits.testBit(i)) {
spilledRegs.registers[i] = registerLocation;
registerLocation = registerLocation.add(4);
} else {
// if it's already been pushed then update its location
if (spilledRegs.registers[i] != null) {
spilledRegs.registers[i] = spilledRegs.registers[i].add(4 * regCount);
}
}
}
// save off the stack pointer register if needed
if (instruction.testBit(13)) {
spilledRegs.SP = registerLocation;
} else {
// if it's already been pushed then update its location
if (spilledRegs.SP != null) {
spilledRegs.SP = spilledRegs.SP.add(4 * regCount);
}
}
// save off the link register if needed
if (instruction.testBit(14)) {
spilledRegs.LRAddress = registerLocation;
} else {
// if it's already been pushed then update its location
if (spilledRegs.LRAddress != null) {
spilledRegs.LRAddress = spilledRegs.LRAddress.add(4 * regCount);
}
}
if (instruction.testBit(21)) {
// write back enabled (STM(1) only) - update the SP
// if the stack pointer has already been updated, just
// adjust it here
if (spilledRegs.SP != null) {
spilledRegs.SP = spilledRegs.SP.add(4 * regCount);
} else {
// the previous SP is 4 times the number of saved
// registers away from the current SP
spilledRegs.SP = currentSP.add(4 * regCount);
}
}
spilledRegs.firstInstructionAfterProlog = pcAddress;
} else if ((instruction.longValue() & 0x01E00000L) == 0x00400000L
&& (instruction.longValue() & 0x0C000000L) == 0x00000000L) {
// sub instruction - does it modify the SP?
if ((instruction.longValue() & 0x0000F000L) == 0x0000D000L) {
Integer shifter_operand = null;
// bit 25 is the I bit for immediate shift
if (instruction.testBit(25)) {
int immed_8 = instruction.and(BigInteger.valueOf(0x000000FFL)).intValue();
int rotate_imm = instruction.and(BigInteger.valueOf(0x00000F00L)).shiftRight(7).intValue(); // rotate_imm * 2
// shifter_operand = immed_8 Rotate_Right (rotate_imm * 2)
shifter_operand = immed_8 >> rotate_imm | immed_8 << (32 - rotate_imm);
} else {
// TODO register operand, but doesn't seem to be used by
// any compilers for prologs
// shifter_operand = ;
}
if (shifter_operand != null) {
// update the spilled registers
for (int i = 0; i < spilledRegs.registers.length; i++) {
IAddress address = spilledRegs.registers[i];
if (address != null) {
// register was spilled so update its location
spilledRegs.registers[i] = address.add(shifter_operand);
}
}
// update the LR and SP as well
if (spilledRegs.LRAddress != null) {
spilledRegs.LRAddress = spilledRegs.LRAddress.add(shifter_operand);
} else {
// SP is modified, but LR is not spilled
if (spilledRegs.LR == null) {
// set it to the real LR so we can spill the SP
spilledRegs.LR = lrValue;
}
}
if (spilledRegs.SP != null) {
spilledRegs.SP = spilledRegs.SP.add(shifter_operand);
} else {
spilledRegs.SP = currentSP.add(shifter_operand);
}
}
}
spilledRegs.firstInstructionAfterProlog = pcAddress;
} else if (isSWIInstruction(instruction)) {
// get the user mode LR and SP. note that the ones we've already
// read are in supervisor mode since we're in an exception
Registers registersService = getService(Registers.class);
spilledRegs.SP = new Addr64(registersService.getRegisterValue(context, ARMRegisters.SP), 16);
spilledRegs.LR = new Addr64(registersService.getRegisterValue(context, ARMRegisters.LR), 16);
spilledRegs.firstInstructionAfterProlog = pcAddress;
} else if (isStrLrToStackInstruction(instruction)) {
// get the location where the LR is stored and set it
getLRAddrOnStack(context, instruction.intValue(), spValue, spilledRegs);
}
}
if (spilledRegs.LRAddress != null && spilledRegs.LR == null) {
spilledRegs.fillLRFromStack(context);
}
if (spilledRegs.isValid()) {
return spilledRegs;
}
return null;
}
/**
* @param context
* @param pcValue
* @param spValue
* @return
*/
private ARMSpilledRegisters parseThumbEpilog(final IEDCExecutionDMC context, IAddress pcValue,
IAddress spValue, final IAddress functionEndAddress) {
if (context == null || pcValue == null || spValue == null)
throw new IllegalArgumentException("null argument passed to parseThumbProlog");
IEDCMemory memoryService = getService(IEDCMemory.class);
// get the instruction before the PC to see if SP is already changed
pcValue = pcValue.add(-2);
int bytesToRead = (functionEndAddress != null) ? pcValue.distanceTo(functionEndAddress).intValue() : 16;
bytesToRead -= bytesToRead % 2; // get only an even amount
ArrayList<MemoryByte> byteArray = new ArrayList<MemoryByte>(bytesToRead);
IStatus status = memoryService.getMemory(context, pcValue, byteArray, bytesToRead, 1);
if (!status.isOK()) {
return null;
}
// don't bother parsing rest of epilog if SP hasn't changed, yet.
// just return null and rely on caller to parse current epilog
BigInteger priorInstruction
= MemoryUtils.convertByteArrayToUnsignedLong(
byteArray.subList(0, 2).toArray(new MemoryByte[2]),
MemoryUtils.LITTLE_ENDIAN);
if (!(isAdd7ChangesSP(priorInstruction) || isSub4ChangesSP(priorInstruction)
|| isAdd4WithSPDestination(priorInstruction)))
return null;
List<BigInteger> instructions = new ArrayList<BigInteger>();
// check each byte
for (int i = 2; i < byteArray.size(); i++) {
if (!byteArray.get(i).isReadable())
break;
if (i % 2 == 0)
instructions.add(
MemoryUtils.convertByteArrayToUnsignedLong(
byteArray.subList(i, i + 2).toArray(new MemoryByte[2]),
MemoryUtils.LITTLE_ENDIAN));
}
ARMSpilledRegisters spilledRegs = new ARMSpilledRegisters();
for (BigInteger instruction : instructions) {
if (isPopInstruction(instruction)) {
// pop instruction. figure out how many registers were being stored
BigInteger regBits = instruction.and(BigInteger.valueOf(0x01FFL));
// save off R0-R7 if needed
for (int i = 0; i <= 7; i++) {
if (regBits.testBit(i)) {
spilledRegs.registers[i] = spValue;
spValue = spValue.add(4);
}
}
// the location of the pushed LR, popped into PC
if (instruction.testBit(8)) {
spilledRegs.LRAddress = spValue;
spValue = spValue.add(4);
}
} else if (isAdd7ChangesSP(instruction) || isSub4ChangesSP(instruction)) {
BigInteger immed_7 = instruction.and(BigInteger.valueOf(0x007FL)).shiftLeft(2);
if (isSub4ChangesSP(instruction))
immed_7 = immed_7.negate();
spValue = spValue.add(immed_7);
} else {
return null;
}
}
spilledRegs.SP = spValue;
if (spilledRegs.LRAddress != null) {
spilledRegs.fillLRFromStack(context);
}
if (spilledRegs.isValid()) {
return spilledRegs;
}
return null;
}
private ARMSpilledRegisters parseThumbProlog(IEDCExecutionDMC context, List<BigInteger> instructions,
IAddress spValue, IAddress prologAddress) {
ARMSpilledRegisters spilledRegs = new ARMSpilledRegisters();
IAddress currentSP = spValue;
IAddress pcAddress = prologAddress;
for (BigInteger instruction : instructions) {
// point to next instruction
pcAddress = pcAddress.add(2);
if (isPushInstruction(instruction)) {
// push instruction. figure out how many registers are being
// stored
BigInteger regBits = instruction.and(BigInteger.valueOf(0x01FFL));
int regCount = 0;
for (int i = 0; i <= 8; i++) {
if (regBits.testBit(i)) {
regCount++;
}
}
// save off R0-R7 if needed
IAddress registerLocation = currentSP;
for (int i = 0; i <= 7; i++) {
if (regBits.testBit(i)) {
spilledRegs.registers[i] = registerLocation;
registerLocation = registerLocation.add(4);
} else {
// if it's already been pushed then update its location
if (spilledRegs.registers[i] != null) {
spilledRegs.registers[i] = spilledRegs.registers[i].add(4 * regCount);
}
}
}
// save off the link register if needed
if (instruction.testBit(8)) {
spilledRegs.LRAddress = registerLocation;
} else {
// if it's already been pushed then update its location
if (spilledRegs.LRAddress != null) {
spilledRegs.LRAddress = spilledRegs.LRAddress.add(4 * regCount);
}
}
// if the stack pointer has already been updated, just adjust it
// here
if (spilledRegs.SP != null) {
spilledRegs.SP = spilledRegs.SP.add(4 * regCount);
} else {
// the previous SP is 4 times the number of saved registers
// away from the current SP
spilledRegs.SP = currentSP.add(4 * regCount);
}
spilledRegs.firstInstructionAfterProlog = pcAddress;
} else if (isSub4ChangesSP(instruction) || isAdd7ChangesSP(instruction)) {
BigInteger immed_7 = instruction.and(BigInteger.valueOf(0x007FL)).shiftLeft(2);
if (isAdd7ChangesSP(instruction))
immed_7 = immed_7.negate();
for (int i = 0; i < spilledRegs.registers.length; i++) {
IAddress address = spilledRegs.registers[i];
if (address != null) {
// register was spilled so update its location
spilledRegs.registers[i] = address.add(immed_7);
}
}
// update the LR and SP as well
if (spilledRegs.LRAddress != null) {
spilledRegs.LRAddress = spilledRegs.LRAddress.add(immed_7);
}
if (spilledRegs.SP != null) {
spilledRegs.SP = spilledRegs.SP.add(immed_7);
}
spilledRegs.firstInstructionAfterProlog = pcAddress;
} else if (isAdd4WithSPDestination(instruction)) {
// add (4) with the SP as the destination register
// get the source register number
int sourceReg = instruction.shiftRight(3).and(BigInteger.valueOf(0x000F)).intValue();
/*
Here's an example of a prolog that uses an add to the SP with another register
push {r4,r7,lr}
cpy r7,sp
ldr r4,[pc,#320]
add sp,r4
Note that is could technically use any instruction(s) to fill the value
of r4 before doing the add, but then we'd have to support all Thumb instructions.
This is the only known signature of this case, so we'll only look for an LDR (3)
instruction with the source of the add as the destination. Other instructions
can be added on a case by case basis as needed.
*/
IAddress instAddr = prologAddress;
for (BigInteger inst : instructions) {
// look for an LDR (3)
if ((inst.intValue() & 0xF800L) == 0x4800L) {
// is the destination register the right one?
if (inst.and(BigInteger.valueOf(0x0700L)).shiftRight(8).intValue() == sourceReg) {
int immed_8 = inst.and(BigInteger.valueOf(0x00FF)).intValue();
// calculate the address - (PC & 0xFFFFFFFC) + (immed_8 * 4)
IAddress dataAddr = new Addr64(instAddr.getValue().and(BigInteger.valueOf(0xFFFFFFFCL)));
dataAddr = dataAddr.add(immed_8 * 4);
// not sure why as the docs don't say anything about it, but in practice the
// data is really in (PC & 0xFFFFFFFC) + (immed_8 * 4) + 4. I validated this
// by stepping over the ldr instruction and checking the value of the destination
// register.
dataAddr = dataAddr.add(4);
IEDCMemory memoryService = getService(IEDCMemory.class);
ArrayList<MemoryByte> byteArray = new ArrayList<MemoryByte>(4);
IStatus status = memoryService.getMemory(context, dataAddr, byteArray, 4, 1);
boolean validRead = status.isOK();
if (validRead) {
// check each byte
for (int i = 0; i < byteArray.size(); i++) {
if (!byteArray.get(i).isReadable()) {
validRead = false;
break;
}
}
}
if (validRead) {
// note that we must treat this as a signed int
int sourceRegValue = MemoryUtils.convertByteArrayToInt(byteArray.toArray(new MemoryByte[4]), MemoryUtils.LITTLE_ENDIAN);
// update the spilled registers
for (int i = 0; i < spilledRegs.registers.length; i++) {
IAddress address = spilledRegs.registers[i];
if (address != null) {
// register was spilled so update its location
spilledRegs.registers[i] = address.add(-sourceRegValue);
}
}
// update the LR and SP as well
if (spilledRegs.LRAddress != null) {
spilledRegs.LRAddress = spilledRegs.LRAddress.add(-sourceRegValue);
}
if (spilledRegs.SP != null) {
spilledRegs.SP = spilledRegs.SP.add(-sourceRegValue);
}
}
spilledRegs.firstInstructionAfterProlog = instAddr.add(2);
break;
}
}
instAddr = instAddr.add(2);
}
}
}
if (spilledRegs.LRAddress != null && spilledRegs.LR == null) {
spilledRegs.fillLRFromStack(context);
}
if (spilledRegs.isValid()) {
return spilledRegs;
}
return null;
}
private boolean isAdd4WithSPDestination(BigInteger instruction) {
return (instruction.intValue() & 0xFF87L) == 0x4485L;
}
private boolean isAdd7ChangesSP(BigInteger instruction) {
return (instruction.intValue() & 0xFF80L) == 0xB000L;
}
private boolean isPopInstruction(BigInteger instruction) {
return (instruction.intValue() & 0xFE00L) == 0xBC00L;
}
private boolean isPushInstruction(BigInteger instruction) {
return (instruction.intValue() & 0xFE00L) == 0xB400L;
}
private boolean isStmfdInstruction(BigInteger instruction) {
if ((instruction.longValue() & 0x0F800000L) == 0x09000000L) {
// stmfd instruction. is the SP the destination?
if ((instruction.longValue() & 0x000F0000L) == 0x000D0000L) {
return true;
}
}
return false;
}
private boolean isSub4ChangesSP(BigInteger instruction) {
return (instruction.intValue() & 0xFF80L) == 0xB080L;
}
private boolean isStrLrToStackInstruction(BigInteger instruction) {
// is this a str instruction with LR the source and SP the destination?
// TODO: handle case where SP is in the Rm field of the str instruction
if ( ((instruction.longValue() & 0x04000000L) == 0x04000000L)
&& ((instruction.longValue() & 0x08500000L) == 0x0L)
&& ((instruction.longValue() & 0x0000F000L) == 0x0000E000L)
&& ((instruction.longValue() & 0x000F0000L) == 0x000D0000L)) {
return true;
}
return false;
}
private boolean isSWIInstruction(BigInteger instruction) {
return (instruction.longValue() & 0xFF000000L) == 0xEF000000L;
}
// Get LR value from str instruction with an SP-relative destination
private void getLRAddrOnStack(IEDCExecutionDMC context, int instruction, IAddress spValue, ARMSpilledRegisters spilledRegs) {
int regOffset = (instruction >> 25) & 1;
int addOffset = (instruction >> 23) & 1;
int updateSP = (instruction >> 21) & 1;
// TODO: handle offset in register (non-immediate) cases
IAddress lrAddress = new Addr64(spValue.getValue());
if (regOffset == 0) {
// offset is add/subtract 12-bit immediate, not in register
// Note: the code assumes the str condition passes
if (updateSP == 1) {
// SP contains the address of LR +/- the offset, so adjust it
if (addOffset == 1) {
spValue = spValue.add(-(instruction & 0xfff));
} else {
spValue = spValue.add(instruction & 0xfff);
}
} else {
if (addOffset == 1) {
lrAddress = lrAddress.add(instruction & 0xfff);
} else {
lrAddress = lrAddress.add(-(instruction & 0xfff));
}
}
}
spilledRegs.LRAddress = lrAddress;
spilledRegs.SP = new Addr64(spValue.getValue());
}
}