/******************************************************************************* * Copyright (c) 2013, 2014 Mentor Graphics 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: * Mentor Graphics - Initial API and implementation * John Dallaway - Add methods to get the endianness and address size (Bug 225609) * Philippe Gil (AdaCore) - Switch to c language when getting sizeof(void *) when required (Bug 421541) * Alvaro Sanchez-Leon (Ericsson AB) - [Memory] Support 16 bit addressable size (Bug 426730) *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import org.eclipse.cdt.core.IAddress; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor; import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.concurrent.Sequence; import org.eclipse.cdt.dsf.datamodel.DMContexts; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IExpressions; import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext; import org.eclipse.cdt.dsf.debug.service.IMemory; import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; import org.eclipse.cdt.dsf.mi.service.MIMemory; import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; import org.eclipse.cdt.dsf.mi.service.command.output.CLIAddressableSizeInfo; import org.eclipse.cdt.dsf.mi.service.command.output.CLIShowEndianInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIDataEvaluateExpressionInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIGDBShowLanguageInfo; import org.eclipse.cdt.dsf.mi.service.command.output.MIInfo; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.model.MemoryByte; /** * @since 4.2 */ public class GDBMemory extends MIMemory implements IGDBMemory2 { private IGDBControl fCommandControl; /** * Cache of the address sizes for each memory context. */ private Map<IMemoryDMContext, Integer> fAddressSizes = new HashMap<IMemoryDMContext, Integer>(); /** * Cache of the addressable sizes for each memory context. */ private Map<IMemoryDMContext, Integer> fAddressableSizes = new HashMap<IMemoryDMContext, Integer>(); /** * We assume the endianness is the same for all processes because GDB supports only one target. */ private Boolean fIsBigEndian; public GDBMemory(DsfSession session) { super(session); } @Override public void initialize(final RequestMonitor requestMonitor) { super.initialize(new ImmediateRequestMonitor(requestMonitor) { @Override protected void handleSuccess() { doInitialize(requestMonitor); } }); } private void doInitialize(final RequestMonitor requestMonitor) { fCommandControl = getServicesTracker().getService(IGDBControl.class); getSession().addServiceEventListener(this, null); register( new String[] { IMemory.class.getName(), MIMemory.class.getName(), IGDBMemory.class.getName(), IGDBMemory2.class.getName(), GDBMemory.class.getName(), }, new Hashtable<String, String>()); requestMonitor.done(); } @Override public void shutdown(RequestMonitor requestMonitor) { unregister(); getSession().removeServiceEventListener(this); fAddressableSizes.clear(); fAddressSizes.clear(); super.shutdown(requestMonitor); } @Override protected void readMemoryBlock(final IDMContext dmc, IAddress address, long offset, int word_size, int word_count, final DataRequestMonitor<MemoryByte[]> drm) { super.readMemoryBlock( dmc, address, offset, word_size, word_count, new DataRequestMonitor<MemoryByte[]>(ImmediateExecutor.getInstance(), drm) { @Override protected void handleSuccess() { IMemoryDMContext memDmc = DMContexts.getAncestorOfType(dmc, IMemoryDMContext.class); if (memDmc != null) { boolean bigEndian = isBigEndian(memDmc); for (MemoryByte b : getData()) { b.setBigEndian(bigEndian); b.setEndianessKnown(true); } } drm.setData(getData()); drm.done(); } }); } @Override public void initializeMemoryData(final IMemoryDMContext memContext, RequestMonitor rm) { ImmediateExecutor.getInstance().execute(new Sequence(getExecutor(), rm) { private String originalLanguage = MIGDBShowLanguageInfo.AUTO; private boolean abortLanguageSteps = false; // Need a global here as getSteps() can be called more than once. private Step[] steps = null; private void determineSteps() { ArrayList<Step> stepsList = new ArrayList<Step>(); if (fAddressSizes.get(memContext) == null) { stepsList.add( new Step() { // store original language @Override public void execute(final RequestMonitor requestMonitor) { fCommandControl.queueCommand( fCommandControl.getCommandFactory().createMIGDBShowLanguage(memContext), new ImmediateDataRequestMonitor<MIGDBShowLanguageInfo>(requestMonitor) { @Override protected void handleCompleted() { if (isSuccess()) { originalLanguage = getData().getLanguage(); } else { abortLanguageSteps = true; } requestMonitor.done(); } }); } }); stepsList.add( new Step() { // switch to c language @Override public void execute(final RequestMonitor requestMonitor) { if (abortLanguageSteps) { requestMonitor.done(); return; } fCommandControl.queueCommand( fCommandControl.getCommandFactory().createMIGDBSetLanguage(memContext, MIGDBShowLanguageInfo.C), new ImmediateDataRequestMonitor<MIInfo>(requestMonitor) { @Override protected void handleCompleted() { if (!isSuccess()) { abortLanguageSteps = true; } // Accept failure requestMonitor.done(); } }); } }); stepsList.add(new Step() { // Run this step even if the language commands where aborted, but accept failures. // Resolve Addressable and Address size @Override public void execute(final RequestMonitor requestMonitor) { //Read Minimum addressable memory size and actual address size readAddressableSize(memContext, new ImmediateDataRequestMonitor<Integer>(requestMonitor) { @Override protected void handleCompleted() { if (isSuccess()) { final Integer minAddressableInOctets = getData(); //Preserve the addressable size per context fAddressableSizes.put(memContext, minAddressableInOctets); } readAddressSize(memContext, new ImmediateDataRequestMonitor<Integer>(requestMonitor) { @Override protected void handleCompleted() { if (isSuccess()) { //Preserve the address size per context fAddressSizes.put(memContext, getData()); } // Accept failures requestMonitor.done(); } }); } }); } }); stepsList.add( new Step() { // restore original language @Override public void execute(final RequestMonitor requestMonitor) { if (abortLanguageSteps) { requestMonitor.done(); return; } fCommandControl.queueCommand( fCommandControl.getCommandFactory().createMIGDBSetLanguage(memContext, originalLanguage), new ImmediateDataRequestMonitor<MIInfo>(requestMonitor) { @Override protected void handleCompleted() { if (!isSuccess()) { // If we are unable to set the original language back things could be bad. // Let's try setting it to "auto" as a fall back. Log the situation as info. GdbPlugin.log(getStatus()); fCommandControl.queueCommand( fCommandControl.getCommandFactory().createMIGDBSetLanguage(memContext, MIGDBShowLanguageInfo.AUTO), new ImmediateDataRequestMonitor<MIInfo>(requestMonitor) { @Override protected void handleCompleted() { if (!isSuccess()) { // This error could be bad because we've changed the language to C // but are unable to switch it back. Log the error. // If the language happens to be C anyway, everything will // continue to work, which is why we don't abort the sequence // (which would cause the entire session to fail). GdbPlugin.log(getStatus()); } // Accept failure requestMonitor.done(); } }); } else { requestMonitor.done(); } } }); } }); } if (fIsBigEndian == null) { stepsList.add( new Step() { // read endianness @Override public void execute(final RequestMonitor requestMonitor) { readEndianness( memContext, new ImmediateDataRequestMonitor<Boolean>(requestMonitor) { @Override protected void handleCompleted() { if (isSuccess()) { fIsBigEndian = getData(); } // Accept failure requestMonitor.done(); } }); } }); } steps = stepsList.toArray(new Step[stepsList.size()]); } @Override public Step[] getSteps() { if (steps == null) { determineSteps(); } return steps; } }); } @DsfServiceEventHandler public void eventDispatched(IExitedDMEvent event) { if (event.getDMContext() instanceof IContainerDMContext) { IMemoryDMContext context = DMContexts.getAncestorOfType(event.getDMContext(), IMemoryDMContext.class); if (context != null) { fAddressSizes.remove(context); fAddressableSizes.remove(context); } } } @Override public int getAddressSize(IMemoryDMContext context) { Integer addressSize = fAddressSizes.get(context); return (addressSize != null) ? addressSize.intValue() : 8; } /** * @since 4.4 */ @Override public int getAddressableSize(IMemoryDMContext context) { Integer addressableSize = fAddressableSizes.get(context); return (addressableSize != null) ? addressableSize.intValue() : 1; } @Override public boolean isBigEndian(IMemoryDMContext context) { assert fIsBigEndian != null; if (fIsBigEndian == null) { GdbPlugin.log(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, "Endianness was never initialized!")); //$NON-NLS-1$ return false; } return fIsBigEndian; } /** * Address size is determined by space, in octets, used to store an address value (e.g. a pointer) on a target system. * * <p>NOTE: Implementation requires addressable memory size to be known</p> * @param memContext * @param drm * * @see IGDBMemory#getAddressSize(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext) * @see GDBMemory#readAddressableSize(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext, DataRequestMonitor) */ protected void readAddressSize(final IMemoryDMContext memContext, final DataRequestMonitor<Integer> drm) { IExpressions exprService = getServicesTracker().getService(IExpressions.class); IExpressionDMContext exprContext = exprService.createExpression(memContext, "sizeof (void*)"); //$NON-NLS-1$ CommandFactory commandFactory = fCommandControl.getCommandFactory(); fCommandControl.queueCommand( commandFactory.createMIDataEvaluateExpression(exprContext), new DataRequestMonitor<MIDataEvaluateExpressionInfo>(ImmediateExecutor.getInstance(), drm) { @Override protected void handleSuccess() { try { // 'sizeof' returns number of bytes (aka 'chars'). // Multiply with byte size in octets to get storage required to hold a pointer. Integer ptrBytes = Integer.decode(getData().getValue()); drm.setData(ptrBytes * getAddressableSize(memContext)); } catch(NumberFormatException e) { drm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, String.format("Invalid address size: %s", getData().getValue()))); //$NON-NLS-1$ } drm.done(); } }); } /** * The minimum addressable size is determined by the space used to store a "char" on a target system * This is then resolved by retrieving a hex representation of -1 casted to the size of a "char" * e.g. from GDB command line * > p/x (char)-1 * > $7 = 0xffff * * Since two hex characters are representing one octet, for the above example this method should return 2 * @since 4.4 * */ protected void readAddressableSize(IMemoryDMContext memContext, final DataRequestMonitor<Integer> drm) { //We use a CLI command here instead of the expression services, since the target may not be available //e.g. when using a remote launch. // Using MI directly is a possibility although there is no way to specify the required output format to hex. CommandFactory commandFactory = fCommandControl.getCommandFactory(); fCommandControl.queueCommand( commandFactory.createCLIAddressableSize(memContext), new DataRequestMonitor<CLIAddressableSizeInfo>(ImmediateExecutor.getInstance(), drm) { @Override protected void handleSuccess() { drm.setData(Integer.valueOf(getData().getAddressableSize())); drm.done(); } }); } protected void readEndianness(IMemoryDMContext memContext, final DataRequestMonitor<Boolean> drm) { CommandFactory commandFactory = fCommandControl.getCommandFactory(); fCommandControl.queueCommand( commandFactory.createCLIShowEndian(memContext), new DataRequestMonitor<CLIShowEndianInfo>(ImmediateExecutor.getInstance(), drm) { @Override protected void handleSuccess() { drm.setData(Boolean.valueOf(getData().isBigEndian())); drm.done(); } }); } }