| /******************************************************************************* |
| * 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.services; |
| |
| import java.math.BigInteger; |
| import java.nio.ByteBuffer; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.cdt.core.IAddress; |
| import org.eclipse.cdt.debug.edc.disassembler.CodeBufferUnderflowException; |
| import org.eclipse.cdt.debug.edc.disassembler.DisassembledInstruction; |
| import org.eclipse.cdt.debug.edc.disassembler.EDCInstruction; |
| import org.eclipse.cdt.debug.edc.disassembler.EDCInstructionFunctionInfo; |
| import org.eclipse.cdt.debug.edc.disassembler.EDCMixedInstruction; |
| import org.eclipse.cdt.debug.edc.disassembler.IDisassembledInstruction; |
| import org.eclipse.cdt.debug.edc.disassembler.IDisassembler; |
| import org.eclipse.cdt.debug.edc.internal.EDCDebugger; |
| import org.eclipse.cdt.debug.edc.internal.services.dsf.EDCServicesMessages; |
| import org.eclipse.cdt.debug.edc.launch.EDCLaunch; |
| import org.eclipse.cdt.debug.edc.symbols.ILineEntry; |
| import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.RequestMonitor; |
| import org.eclipse.cdt.dsf.datamodel.DMContexts; |
| import org.eclipse.cdt.dsf.debug.service.IDisassembly; |
| import org.eclipse.cdt.dsf.debug.service.IInstruction; |
| import org.eclipse.cdt.dsf.debug.service.IMemory; |
| import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext; |
| import org.eclipse.cdt.dsf.debug.service.IMixedInstruction; |
| import org.eclipse.cdt.dsf.debug.service.IModules; |
| import org.eclipse.cdt.dsf.debug.service.IModules.AddressRange; |
| import org.eclipse.cdt.dsf.debug.service.IModules.ISymbolDMContext; |
| import org.eclipse.cdt.dsf.service.DsfServicesTracker; |
| import org.eclipse.cdt.dsf.service.DsfSession; |
| import org.eclipse.cdt.utils.Addr64; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.debug.core.model.MemoryByte; |
| import org.eclipse.tm.tcf.services.IMemory.MemoryError; |
| |
| |
| public class Disassembly extends AbstractEDCService implements IDisassembly { |
| |
| /** |
| * @param classNames |
| * the type names the service will be registered under. See |
| * AbstractDsfService#register for details. We tack on base DSF's |
| * IDisassembly and this class to the list if not provided. |
| * @since 2.0 |
| */ |
| public Disassembly(DsfSession session, String[] classNames) { |
| super(session, |
| massageClassNames(classNames, |
| new String[] { IDisassembly.class.getName(), Disassembly.class.getName() })); |
| } |
| |
| /** |
| * @return IStatus.ERROR containing NLS string indicating disassembler not yet available |
| * @since 2.0 |
| */ |
| public static IStatus statusNoDisassembler() { |
| return new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, |
| EDCServicesMessages.Disassembly_NoDisassemblerYet, null); |
| } |
| |
| /** |
| * @param memoryAt 1st location of unreadable memory |
| * @param memoryLength length of unreadable memory |
| * @return IStatus.ERROR containing formatted message with location & length |
| */ |
| private static IStatus statusCannotReadMemory(String memoryAt, Integer memoryLength) { |
| return new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, REQUEST_FAILED, |
| cantReadMemory(memoryAt, memoryLength.toString()), null); |
| } |
| |
| /** |
| * string for use in both status for error return and in |
| * pseudo-instruction used in error recovery mode for disassembly view |
| * @param memoryAt 1st location of unreadable memory |
| * @param memoryLength length of unreadable memory (incoming null will "unknown" for length) |
| * @return formatted message with location & length |
| */ |
| private static String cantReadMemory(String memoryAt, String memoryLength) { |
| memoryLength = (memoryLength == null) ? "unknown" : memoryLength; |
| return MessageFormat.format(EDCServicesMessages.Disassembly_CannotReadMemoryAt, |
| memoryAt, memoryLength); |
| } |
| |
| /** |
| * check each byte of incoming MemoryByte[] array to see if readable; |
| * fills the RequestMonitor status in case of unreadable bytes. |
| * @param memBytes data to translate |
| * @param start address of first byte of memBytes |
| * @param rm with which to set status in case of error |
| * @return code buffer translated from incoming memByte array, null if any are unreadable |
| * @since 2.0 |
| */ |
| public static ByteBuffer translateMemoryBytes(MemoryByte[] memBytes, |
| IAddress start, RequestMonitor rm) { |
| byte[] bytes = new byte[memBytes.length]; |
| for (int i = 0; i < memBytes.length; i++) { |
| // check each byte |
| if (!memBytes[i].isReadable()) { |
| rm.setStatus(statusCannotReadMemory(start.add(i).toHexAddressString(), |
| memBytes.length-i)); |
| rm.done(); |
| return null; |
| } |
| bytes[i] = memBytes[i].getValue(); |
| } |
| return ByteBuffer.wrap(bytes); |
| } |
| |
| /** |
| * return a buffer of instruction code whose readability matches the |
| * first byte of the data for as long as all such bytes are the same |
| * @param memBytes data to translate |
| * @return a code buffer either full of readable bytes |
| * or empty representing the unreadable region |
| */ |
| @SuppressWarnings("null") // memByte can't be null if memByteReadable is true |
| private ByteBuffer translateMemoryBytes(List<MemoryByte> memBytes) { |
| int count = 0; |
| byte[] bytes = new byte[memBytes.size()]; |
| MemoryByte firstByte = memBytes.get(0); |
| boolean firstIsReadable = firstByte != null && firstByte.isReadable(); |
| for (MemoryByte memByte : memBytes) { |
| boolean memByteReadable = memByte != null && memByte.isReadable(); |
| // check each byte |
| if (memByteReadable != firstIsReadable) |
| break; |
| bytes[count++] = memByteReadable ? memByte.getValue() : 0; |
| } |
| |
| return ByteBuffer.wrap(bytes, 0, count); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.math.BigInteger, java.math.BigInteger, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) |
| */ |
| public void getInstructions(final IDisassemblyDMContext context, BigInteger startAddress, BigInteger endAddress, |
| final DataRequestMonitor<IInstruction[]> drm) { |
| |
| // FIXME: ignoring null startAddress and null endAddress semantics |
| |
| ITargetEnvironment env = getTargetEnvironmentService(); |
| final IDisassembler disassembler = (env != null) ? env.getDisassembler() : null; |
| if (disassembler == null) { |
| drm.setStatus(statusNoDisassembler()); |
| drm.done(); |
| return; |
| } |
| |
| DsfServicesTracker services = getServicesTracker(); |
| if (services == null) // could be null if async invoked as or after debug session ends |
| return; |
| |
| IMemory memoryService = services.getService(IMemory.class); |
| if (memoryService == null) // could be null if async invoked as or after debug session ends |
| return; |
| |
| final int size = endAddress.intValue() - startAddress.intValue() + 16; |
| |
| final IMemoryDMContext mem_dmc = DMContexts.getAncestorOfType(context, IMemoryDMContext.class); |
| final IAddress start = new Addr64(startAddress); |
| |
| memoryService.getMemory(mem_dmc, start, 0, 1, size, |
| new DataRequestMonitor<MemoryByte[]>(getExecutor(), drm) { |
| /** |
| * overridden to create a non-error status plus pseudoInstruction data-set |
| * in the DRM requested by DisassemblyBackendDsf, where a DRM.status of |
| * ERROR is turned into an "invalid" block in its document map. Such |
| * blocks are repeatedly re-requested ... causing scrolling oddities & performance issues. |
| * @see failedMemoryDsfPseudoInstructions |
| */ |
| @Override |
| protected void handleError() { |
| IStatus s = getStatus(); |
| Throwable e = s.getException(); |
| if (e instanceof MemoryError && s.getMessage().contains("Fail to read memory")) { |
| drm.setData(failedMemoryDsfPseudoInstructions(start, size, s.getMessage())); |
| drm.done(); |
| } else { |
| super.handleError(); |
| } |
| |
| } |
| |
| @Override |
| protected void handleSuccess() { |
| List<MemoryByte> memBytes = Arrays.asList(getData()); |
| Map<String, Object> options = new HashMap<String, Object>(); |
| try { |
| ArrayList<IInstruction> instrs |
| = fillDisassemblyViewInstructions(memBytes, start, context, |
| disassembler, options); |
| drm.setData(instrs.toArray(new IInstruction[instrs.size()])); |
| } catch (CoreException e) { |
| drm.setStatus(e.getStatus()); |
| } |
| drm.done(); |
| } |
| }); |
| |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.lang.String, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) |
| */ |
| public void getInstructions(final IDisassemblyDMContext context, String filename, int linenum, final int lines, |
| final DataRequestMonitor<IInstruction[]> drm) { |
| |
| // FIXME: ignoring "lines" semantics |
| |
| IModules modulesService = getServicesTracker().getService(IModules.class); |
| |
| ISymbolDMContext sym_dmc = DMContexts.getAncestorOfType(context, ISymbolDMContext.class); |
| |
| filename = EDCLaunch.getLaunchForSession(getSession().getId()).getCompilationPath(filename); |
| |
| modulesService.calcAddressInfo(sym_dmc, filename, linenum, 0, |
| new DataRequestMonitor<AddressRange[]>(getExecutor(), drm) { |
| @Override |
| protected void handleSuccess() { |
| AddressRange[] addr_ranges = getData(); |
| |
| IAddress start = addr_ranges[0].getStartAddress(); |
| IAddress end = start.add(lines * 4); // kind of arbitrary end |
| // address hint. |
| |
| getInstructions(context, start.getValue(), end.getValue(), drm); |
| } |
| }); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getMixedInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.math.BigInteger, java.math.BigInteger, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) |
| */ |
| public void getMixedInstructions(final IDisassemblyDMContext context, BigInteger startAddress, BigInteger endAddress, |
| final DataRequestMonitor<IMixedInstruction[]> drm) { |
| |
| // FIXME: ignoring null startAddress and null endAddress semantics |
| |
| DsfServicesTracker services = getServicesTracker(); |
| if (services == null) // could be null if async invoked as or after debug session ends |
| return; |
| |
| IEDCSymbols symbolsService = services.getService(IEDCSymbols.class); |
| if (symbolsService == null) // could be null if async invoked as or after debug session ends |
| return; |
| |
| IEDCModules modulesService = services.getService(IEDCModules.class); |
| if (modulesService == null) // could be null if async invoked as or after debug session ends |
| return; |
| |
| final ISymbolDMContext sym_dmc = DMContexts.getAncestorOfType(context, ISymbolDMContext.class); |
| |
| // These are absolute runtime addresses. |
| final IAddress end = new Addr64(endAddress); |
| final IAddress start |
| = getStartAddressForLineEntryContainingAddress(symbolsService, modulesService, sym_dmc, |
| new Addr64(startAddress), end); |
| |
| ILineEntry startEntry = symbolsService.getLineEntryForAddress(sym_dmc, start); |
| |
| if (startEntry == null) { |
| // startAddress has no source |
| getInstructions(context, startAddress, endAddress, |
| new DataRequestMonitor<IInstruction[]>(getExecutor(), drm) { |
| @Override |
| protected void handleSuccess() { |
| IMixedInstruction[] ret = new IMixedInstruction[1]; |
| ret[0] = new EDCMixedInstruction("unknown", 0, getData()); //$NON-NLS-1$ |
| drm.setData(ret); |
| drm.done(); |
| } |
| }); |
| } else { // there is source for start address. |
| |
| final IEDCModuleDMContext module = modulesService.getModuleByAddress(sym_dmc, start); |
| final List<ILineEntry> codeLines = symbolsService.getLineEntriesForAddressRange(sym_dmc, start, end); |
| |
| getInstructions(context, startAddress, endAddress, |
| new DataRequestMonitor<IInstruction[]>(getExecutor(), drm) { |
| @Override |
| protected void handleSuccess() { |
| List<IMixedInstruction> mixedInstructions = new ArrayList<IMixedInstruction>(); |
| |
| mixSource(mixedInstructions, null, module, codeLines, getData()); |
| |
| drm.setData(mixedInstructions.toArray(new IMixedInstruction[mixedInstructions.size()])); |
| drm.done(); |
| } |
| }); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.cdt.dsf.debug.service.IDisassembly#getMixedInstructions(org.eclipse.cdt.dsf.debug.service.IDisassembly.IDisassemblyDMContext, java.lang.String, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor) |
| */ |
| public void getMixedInstructions(final IDisassemblyDMContext context, String filename, final int linenum, |
| final int lines, final DataRequestMonitor<IMixedInstruction[]> drm) { |
| |
| // FIXME: ignoring "lines" semantics |
| |
| final ITargetEnvironment env = getTargetEnvironmentService(); |
| final IDisassembler disassembler = (env != null) ? env.getDisassembler() : null; |
| if (disassembler == null) { |
| drm.setStatus(statusNoDisassembler()); |
| drm.done(); |
| return; |
| } |
| |
| IModules modulesService = getServicesTracker().getService(IModules.class); |
| |
| ISymbolDMContext sym_dmc = DMContexts.getAncestorOfType(context, ISymbolDMContext.class); |
| |
| filename = EDCLaunch.getLaunchForSession(getSession().getId()).getCompilationPath(filename); |
| |
| modulesService.calcAddressInfo(sym_dmc, filename, linenum, 0, |
| new DataRequestMonitor<AddressRange[]>(getExecutor(), drm) { |
| @Override |
| protected void handleSuccess() { |
| AddressRange[] addr_ranges = getData(); |
| |
| IAddress start = addr_ranges[0].getStartAddress(); |
| IAddress end = start.add(lines * 4); // kind of arbitrary end address hint. |
| getMixedInstructions(context, start.getValue(), end.getValue(), drm); |
| } |
| }); |
| } |
| |
| private static EDCInstruction pseudoInstruction(IAddress address, int size, String pseudoMnemonic) { |
| DisassembledInstruction pseudoInstruction = new DisassembledInstruction(); |
| pseudoInstruction.setAddress(address); |
| pseudoInstruction.setSize(size); |
| pseudoInstruction.setMnemonics(pseudoMnemonic); |
| return new EDCInstruction(pseudoInstruction); |
| } |
| |
| /** |
| * used in failedMemoryDsfPseudoInstructions() below as chunk boundary for |
| * each of the pseudoInstructions in a larger chunk of retrieved memory. |
| */ |
| private static final int asmFence = 0x20; |
| |
| /** |
| * this utility function creates pseudo-mnemonics indicating failed memory |
| * read. it was refactored from memoryService.getMemory().handleError() so |
| * that it could be utilized by subclass override getInstructions() methods |
| * making the same memoryService.getMemory() call. |
| * <p> |
| * <i>background:</i> |
| * <p> |
| * as of 2010.oct.01, EDC memoryService no longer caches blocks of memory that |
| * cannot be read (a correct change, given that this was blocking the caching |
| * of good memory on the boundaries of such blocks, causing other problems). |
| * <p> |
| * when this change was made, Disassembly#fillDisassemblyViewInstructions() |
| * stopped getting reached through memoryService.getMemory().handleSuccess() . |
| * therefore, actual bad blocks of memory were not getting filled with pseudo |
| * mnemonics indicating bad memory . in other words, when "Fail to read memory" |
| * errors from TCF caused invocation of memoryService.getMemory().handleFailure() |
| * ... and thus eventually also handleError() ... the result was that |
| * DsfBackendDisassembly would fill the DisassemblyDocument with "invalid" |
| * sections based upon address but no size. it's algorithm then later attempts |
| * to fill any missing/invalid sections corresponding with the document. this |
| * results in a visual anomaly where "Unable to retrieve disassembly" would be |
| * populated in the DisassemblyView one block at a time, until there would be a |
| * large, mostly useless portion of the document view populated with the same |
| * message repeated once for every byte the user had attempted to scroll to. |
| * <p> |
| * the handleError() override implementations that call this utility function |
| * solve the problem whereby DisassemblyBackendDsf interprets DRM.status as |
| * "invalid" sections in its the DisassemblyDocument it is associated with. |
| * <p> |
| * the point of this re-factored code is to create "fences" on regular |
| * boundaries so that chunks of failed memory always get placed on similar |
| * boundaries, thus drastically ameliorating the occurrence of small "invalid" |
| * chunks in the DisassemblyDocument map between chunks of pseudoInstructions |
| * that DisassemblyBackendDsf considers "valid". |
| * <p> |
| * in user terms, this means that scrolling in the view is more consistent |
| * and even, with better performance thanks to fewer attempts to re-retrieve |
| * memory for small "invalid" sections in its map at the boundaries of |
| * previously inserted pseudo-instructions. |
| * |
| * @param size size of the chunk to break up |
| * @param start location of memory chunk |
| * @param msg message from the target agent |
| * @return array containing 1 or more pseudo-instructions, mostly on asmFence boundaries |
| * @since 2.0 |
| */ |
| protected static IInstruction[] failedMemoryDsfPseudoInstructions( |
| IAddress start, final int size, String msg) { |
| ArrayList<IInstruction> pseudoInstr = new ArrayList<IInstruction>(); |
| int offset = 0, chunkSize = Math.min(size, asmFence - start.getValue().intValue() % asmFence); |
| do { |
| pseudoInstr.add(pseudoInstruction(start.add(offset), chunkSize, |
| msg + "..[length=" + chunkSize + ']')); |
| offset += chunkSize; |
| chunkSize = Math.min(asmFence, size-offset); |
| } while (offset < size); |
| return pseudoInstr.toArray(new IInstruction[pseudoInstr.size()]); |
| } |
| |
| /** |
| * Creates the array of instructions to be used to fill the disassembly view. |
| * for a range containing any unreadable instructions, it will create a |
| * fake instruction consisting of the address, the entire unreadable range, |
| * and a message to fill in the mnemonics section about the unreadable range. |
| * @param memBytes the buffer containing the bytes to be disassembled |
| * @param start starting address corresponding to the buffer |
| * @param context context for disassembly |
| * @param disassembler the disassembler object to use |
| * @param options to be passed to the disassembleInstructions() call |
| * @return array of IInstruction |
| * @throws CoreException can be thrown by disassembleInstructions(). |
| * @since 2.0 |
| */ |
| protected ArrayList<IInstruction> fillDisassemblyViewInstructions( |
| final List<MemoryByte> memBytes, final IAddress start, |
| final IDisassemblyDMContext context, final IDisassembler disassembler, |
| Map<String, Object> options) |
| throws CoreException { |
| ArrayList<IInstruction> ret = new ArrayList<IInstruction>(); |
| for (int offset = 0, last = memBytes.size(); offset < last ;) { |
| ByteBuffer codeBuf = translateMemoryBytes(memBytes.subList(offset, last)); |
| int codeBufSize = codeBuf.limit(); |
| IAddress block = start.add(offset); |
| MemoryByte firstByte = memBytes.get(offset); |
| if (firstByte != null && firstByte.isReadable()) { |
| try { |
| List<IDisassembledInstruction> insts |
| = disassembler.disassembleInstructions(block, block.add(codeBufSize), |
| codeBuf, options, context); |
| if (insts.size() == 0) |
| break; |
| for (int i = 0; i < insts.size(); i++) { |
| ret.add(new EDCInstruction(insts.get(i))); |
| offset += insts.get(i).getSize(); |
| } |
| } catch (CodeBufferUnderflowException e) { |
| if (offset == 0 && codeBufSize == last) { |
| // nothing in entire block can be disassembled; |
| // at least tell the Disassembly view code this much |
| ret.add(pseudoInstruction(start, codeBufSize, |
| "Buffer Underflow during disassembly")); //$NON-NLS-1$ |
| } |
| offset += codeBufSize; |
| } |
| } else { // this will only occur when the target supports partial bad blocks |
| ret.add(pseudoInstruction(block, codeBufSize, |
| cantReadMemory(block.toHexAddressString(), |
| ((Integer)codeBufSize).toString()))); |
| offset += codeBufSize; |
| } |
| } |
| return ret; |
| } |
| |
| /** |
| * whereas the default implementation of getMixedInstructions() gets and |
| * processes all the disassembly in one pass, some variants are known to need |
| * to override the default implementation to break the retrieval into chunks. |
| * <p> |
| * this portion of the mixing is still the same within those chunks, though, |
| * and so has been extracted and protected for use by extending variant classes. |
| * @param mixedInstructions the growing list of instructions being processed |
| * @param wholeFunctionName name for whole function; if null, will be calculated per line |
| * @param module the module for this block of code |
| * @param codeLines list of ILineEntry containing info for mixing |
| * @param instructions the instructions to be mixed with the source |
| * @since 2.0 |
| */ |
| protected void mixSource(List<IMixedInstruction> mixedInstructions, |
| final EDCInstructionFunctionInfo wholeBlockInfo, |
| final IEDCModuleDMContext module, |
| final List<ILineEntry> codeLines, |
| final IInstruction[] instructions) { |
| |
| List<IInstruction> instsForLine = new ArrayList<IInstruction>(); |
| |
| IPath filePath = null; |
| String osString = null; |
| |
| EDCInstructionFunctionInfo functionInfo = wholeBlockInfo; |
| int k = 0, instsCnt = instructions.length, lineCnt = codeLines.size(); |
| for (int i = 0; i < lineCnt && k < instsCnt; i++) { |
| // Now map the instructions to source lines to generate |
| // MixedInstructions. |
| instsForLine.clear(); |
| ILineEntry line = codeLines.get(i); |
| |
| if (wholeBlockInfo == null && module != null) { |
| functionInfo = new EDCInstructionFunctionInfo(module, line); |
| } |
| |
| while (k < instsCnt) { |
| EDCInstruction inst = (EDCInstruction)instructions[k]; |
| IAddress linkAddress = module.toLinkAddress(new Addr64(inst.getAdress())); |
| if (linkAddress.compareTo(line.getHighAddress()) >= 0) |
| break; |
| |
| if (functionInfo != null) { |
| inst.setFunctionName(functionInfo.getFunctionName()); |
| IAddress functionBase = functionInfo.getFunctionStartAddress(); |
| if (functionBase != null) { |
| inst.setOffset(functionBase.distanceTo(linkAddress).intValue()); |
| } |
| } |
| instsForLine.add(inst); |
| k++; |
| } |
| if (line.getFilePath() != filePath) { |
| filePath = line.getFilePath(); |
| osString = (filePath != null) ? filePath.toOSString() : null; |
| } |
| mixedInstructions.add(new EDCMixedInstruction(osString, line.getLineNumber(), |
| instsForLine.toArray(new IInstruction[instsForLine.size()]))); |
| } |
| } |
| |
| /** |
| * disassembly utility function to find the first address for a line-entry |
| * for an address contained by that line-entry |
| * @param symbolsService |
| * @param modulesService |
| * @param sym_dmc symbol context used to retrieve LineEntry for address |
| * @param initialStartAddress the address of interest |
| * @param endAddress the last address of a range to search |
| * @return the first address of an associated LineEntry if it can be found, else the initialStartAddress |
| * @since 2.0 |
| */ |
| protected static IAddress getStartAddressForLineEntryContainingAddress(final IEDCSymbols symbolsService, |
| final IEDCModules modulesService, final ISymbolDMContext sym_dmc, final IAddress initialStartAddress, |
| final IAddress endAddress) { |
| assert symbolsService != null && modulesService != null; |
| if (symbolsService == null || modulesService == null) |
| return initialStartAddress; |
| |
| if (sym_dmc != null) { |
| // in case the caller requested a start that falls somewhere other than the |
| // boundary of an instruction, back up to that boundary for the first instruction |
| if (symbolsService.getLineEntryForAddress(sym_dmc, initialStartAddress) != null) { |
| IEDCModuleDMContext module = modulesService.getModuleByAddress(sym_dmc, initialStartAddress); |
| if (module != null) { |
| List<ILineEntry> allLines |
| = symbolsService.getLineEntriesForAddressRange(sym_dmc, initialStartAddress, endAddress); |
| if (allLines != null && !allLines.isEmpty()) |
| { |
| return module.toRuntimeAddress(allLines.get(0).getLowAddress()); |
| } |
| } |
| } |
| } |
| return initialStartAddress; |
| } |
| } |