/** | |
* Copyright (c) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies). | |
* All rights reserved. | |
* This component and the accompanying materials are made available | |
* under the terms of the License "Eclipse Public License v1.0" | |
* which accompanies this distribution, and is available | |
* at the URL "http://www.eclipse.org/legal/epl-v10.html". | |
* | |
* Initial Contributors: | |
* Nokia Corporation - initial contribution. | |
* | |
* Contributors: | |
* Broadcom - Refactored ForwardTypeReference to separate top-level class | |
* - optimized Variable list | |
* | |
* Description: | |
* | |
*/ | |
package org.eclipse.cdt.debug.edc.internal.symbols.dwarf; | |
import java.io.File; | |
import java.io.FileNotFoundException; | |
import java.io.IOException; | |
import java.io.PrintStream; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.Comparator; | |
import java.util.HashMap; | |
import java.util.Iterator; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Stack; | |
import java.util.TreeMap; | |
import org.eclipse.cdt.core.CCorePlugin; | |
import org.eclipse.cdt.core.IAddress; | |
import org.eclipse.cdt.debug.edc.IStreamBuffer; | |
import org.eclipse.cdt.debug.edc.internal.EDCDebugger; | |
import org.eclipse.cdt.debug.edc.internal.EDCTrace; | |
import org.eclipse.cdt.debug.edc.internal.MemoryStreamBuffer; | |
import org.eclipse.cdt.debug.edc.internal.PathUtils; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ArrayBoundType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ArrayType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.CPPBasicType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ClassType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ConstType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.Enumeration; | |
import org.eclipse.cdt.debug.edc.internal.symbols.Enumerator; | |
import org.eclipse.cdt.debug.edc.internal.symbols.FieldType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.FunctionScope; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IArrayType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IBasicType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ICPPBasicType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ICompositeType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.IForwardTypeReference; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ISection; | |
import org.eclipse.cdt.debug.edc.internal.symbols.InheritanceType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.LexicalBlockScope; | |
import org.eclipse.cdt.debug.edc.internal.symbols.LineEntry; | |
import org.eclipse.cdt.debug.edc.internal.symbols.PointerType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.ReferenceType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.Scope; | |
import org.eclipse.cdt.debug.edc.internal.symbols.StructType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.SubroutineType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.TemplateParamType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.TypedefType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.UnionType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.VolatileType; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.AbbreviationEntry; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.Attribute; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.AttributeList; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.AttributeValue; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.CompilationUnitHeader; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfDebugInfoProvider.PublicNameInfo; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.CommonInformationEntry; | |
import org.eclipse.cdt.debug.edc.internal.symbols.dwarf.DwarfFrameRegisterProvider.FrameDescriptionEntry; | |
import org.eclipse.cdt.debug.edc.internal.symbols.files.BaseExecutableSymbolicsReader; | |
import org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglerEABI; | |
import org.eclipse.cdt.debug.edc.internal.symbols.files.UnmanglingException; | |
import org.eclipse.cdt.debug.edc.symbols.ICompileUnitScope; | |
import org.eclipse.cdt.debug.edc.symbols.IExecutableSection; | |
import org.eclipse.cdt.debug.edc.symbols.IExecutableSymbolicsReader; | |
import org.eclipse.cdt.debug.edc.symbols.IFunctionScope; | |
import org.eclipse.cdt.debug.edc.symbols.ILineEntry; | |
import org.eclipse.cdt.debug.edc.symbols.ILocationProvider; | |
import org.eclipse.cdt.debug.edc.symbols.IRangeList; | |
import org.eclipse.cdt.debug.edc.symbols.IScope; | |
import org.eclipse.cdt.debug.edc.symbols.IType; | |
import org.eclipse.cdt.debug.edc.symbols.IUnmangler; | |
import org.eclipse.cdt.debug.edc.symbols.IVariable; | |
import org.eclipse.cdt.utils.Addr32; | |
import org.eclipse.core.runtime.IPath; | |
import org.eclipse.core.runtime.IProgressMonitor; | |
import org.eclipse.core.runtime.IStatus; | |
import org.eclipse.core.runtime.NullProgressMonitor; | |
import org.eclipse.core.runtime.Status; | |
import org.eclipse.core.runtime.jobs.Job; | |
/** | |
* Handle restartable parsing of Dwarf information from a single module. | |
* This class may be instantiated multiple times to parse specific subsets | |
* of the Dwarf data. The {@link DwarfDebugInfoProvider} | |
* holds the global state of everything parsed so far. | |
*/ | |
public class DwarfInfoReader { | |
private class BaseAndScopedNames { | |
public String baseName; // e.g., "bar" | |
public String nameWithScope; // e.g., "foo::bar" | |
} | |
private BaseAndScopedNames baseAndScopedNames = new BaseAndScopedNames(); | |
// These are only for developer of the reader. | |
// | |
private static boolean DEBUG = false; | |
private static String dumpFileName = "C:\\temp\\_EDC_DwarfReaderDump.txt"; //$NON-NLS-1$ | |
// TODO 64-bit Dwarf currently unsupported | |
/* Section names. */ | |
public final static String DWARF_DEBUG_INFO = ".debug_info"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_RANGES = ".debug_ranges"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_ABBREV = ".debug_abbrev"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_ARANGES = ".debug_aranges"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_LINE = ".debug_line"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_FRAME = ".debug_frame"; //$NON-NLS-1$ | |
public final static String DWARF_EH_FRAME = ".eh_frame"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_LOC = ".debug_loc"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_PUBNAMES = ".debug_pubnames"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_STR = ".debug_str"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_FUNCNAMES = ".debug_funcnames"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_TYPENAMES = ".debug_typenames"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_VARNAMES = ".debug_varnames"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_WEAKNAMES = ".debug_weaknames"; //$NON-NLS-1$ | |
public final static String DWARF_DEBUG_MACINFO = ".debug_macinfo"; //$NON-NLS-1$ | |
private Map<Long, Collection<LocationEntry>> locationEntriesByOffset = Collections.synchronizedMap(new HashMap<Long, Collection<LocationEntry>>()); | |
// the target for all the reading | |
private DwarfDebugInfoProvider provider; | |
private IExecutableSymbolicsReader exeReader; | |
private DwarfModuleScope moduleScope; | |
private IPath symbolFilePath; | |
private IExecutableSection debugInfoSection; | |
private IExecutableSection publicNamesSection; | |
private CompilationUnitHeader currentCUHeader; | |
private Map<IType, IType> typeToParentMap = Collections.synchronizedMap(new HashMap<IType, IType>()); | |
private IType currentParentType; | |
private Scope currentParentScope; | |
private DwarfCompileUnit currentCompileUnitScope; | |
private DwarfFileHelper fileHelper; | |
private RangeList codeRanges; | |
private ICPPBasicType voidType = null; | |
private IUnmangler unmangler; | |
// comparator for sorting and searching based on compilation unit low address | |
private static Comparator<DwarfCompileUnit> sComparatorByLowAddress = new Comparator<DwarfCompileUnit>() { | |
public int compare(DwarfCompileUnit o1, DwarfCompileUnit o2) { | |
return (o1.getLowAddress().compareTo(o2.getLowAddress())); | |
}}; | |
/** | |
* Create a reader for the provider. This constructor and any methods | |
* on this reader class will incrementally update the provider. | |
* @param provider | |
*/ | |
public DwarfInfoReader(DwarfDebugInfoProvider provider) { | |
this.provider = provider; | |
exeReader = provider.getExecutableSymbolicsReader(); | |
if (exeReader instanceof BaseExecutableSymbolicsReader) | |
unmangler = ((BaseExecutableSymbolicsReader) exeReader).getUnmangler(); | |
if (unmangler == null) | |
unmangler = new UnmanglerEABI(); | |
symbolFilePath = provider.getSymbolFile(); | |
fileHelper = provider.fileHelper; | |
moduleScope = (DwarfModuleScope) provider.getModuleScope(); | |
debugInfoSection = exeReader.findExecutableSection(DWARF_DEBUG_INFO); | |
publicNamesSection = exeReader.findExecutableSection(DWARF_DEBUG_PUBNAMES); | |
codeRanges = getCodeRanges(); | |
} | |
private String unmangle(String name) { | |
if (!unmangler.isMangled(name)) | |
return name; | |
try { | |
name = unmangler.unmangle(name); | |
} catch (UnmanglingException ue) { | |
} | |
return name; | |
} | |
private String unmangleType(String name) { | |
if (!unmangler.isMangled(name)) | |
return name; | |
try { | |
name = unmangler.unmangleType(name); | |
} catch (UnmanglingException ue) { | |
} | |
return name; | |
} | |
/** | |
* @return | |
*/ | |
private RangeList getCodeRanges() { | |
RangeList codeRanges = new RangeList(); | |
for (ISection section : exeReader.getSections()) { | |
if (section.getProperties().get(ISection.PROPERTY_NAME).equals(ISection.NAME_TEXT)) { | |
long start = section.getLinkAddress().getValue().longValue(); | |
long size = section.getSize(); | |
codeRanges.addRange(start, start + size); | |
} | |
} | |
return codeRanges; | |
} | |
protected IStreamBuffer getDwarfSection(String sectionName) { | |
// the exe reader and section already handle caching this | |
IStreamBuffer buffer = null; | |
IExecutableSection section = exeReader.findExecutableSection(sectionName); | |
if (section != null) { | |
buffer = section.getBuffer(); | |
} | |
return buffer; | |
} | |
/** | |
* Parse top-level debugging information about compilation units and globally | |
* visible objects, but do not expand or gather data about other objects in | |
* compilation units. | |
*/ | |
public void parseInitial() { | |
Job parseInitialJob = new Job(DwarfMessages.DwarfInfoReader_ReadingSymbolInfo + symbolFilePath) { | |
@Override | |
protected IStatus run(IProgressMonitor monitor) { | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceInitialParseFor + symbolFilePath)); } | |
synchronized (provider) { | |
parseCUDebugInfo(monitor); | |
parsePublicNames(); | |
} | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceFinishedInitialParse)); } | |
return Status.OK_STATUS; | |
} | |
}; | |
try { | |
parseInitialJob.schedule(); | |
parseInitialJob.join(); | |
} catch (InterruptedException e) { | |
EDCDebugger.getMessageLogger().logError(null, e); | |
} | |
} | |
/** | |
* Parse all compilation units for addresses | |
* | |
* @param includeCUWithoutCode | |
* whether to parse compile units without code. For variable | |
* info, we need to look into those CUs, whereas for scope | |
* info, we don't. | |
*/ | |
public void parseForAddresses(boolean includeCUWithoutCode) { | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceAddressesParseFor + symbolFilePath)); } | |
synchronized (provider) { | |
for (DwarfCompileUnit compileUnit : provider.compileUnits) { | |
if (DEBUG) { | |
// For internal check. | |
if (compileUnit.getHighAddress().isZero()) | |
assert(compileUnit.getChildren().size() == 0); | |
else | |
assert(compileUnit.getChildren().size() >= 0); | |
} | |
if (includeCUWithoutCode || // parse every CU | |
! compileUnit.getHighAddress().isZero()) // parse only those with code. | |
{ | |
parseCompilationUnitForAddresses(compileUnit); | |
} | |
} | |
} | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceFinishedAddressesParse)); } | |
moduleScope.fixupRanges(Addr32.ZERO); | |
if (DEBUG) { | |
dumpSymbols(); | |
} | |
} | |
/** | |
* Parse compilation unit corresponding to address | |
* | |
* @param linkAddress | |
* address in a compile unit that contains code | |
*/ | |
public void parseForAddress(IAddress linkAddress) { | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceAddressParseFor + symbolFilePath)); } | |
// find compilation unit containing address, and parse it | |
synchronized (provider) { | |
DwarfCompileUnit cu = new DwarfCompileUnit(provider, null, null, linkAddress, linkAddress, null, false, null); | |
int index = Collections.binarySearch (provider.sortedCompileUnitsWithCode, cu, sComparatorByLowAddress); | |
if (index >= 0) { | |
cu = provider.sortedCompileUnitsWithCode.get(index); | |
parseCompilationUnitForAddresses(cu); | |
} else if (index < -1 && -index - 2 < provider.sortedCompileUnitsWithCode.size()) { | |
cu = provider.sortedCompileUnitsWithCode.get(-index - 2); | |
if (cu.getLowAddress().compareTo(linkAddress) <= 0 && cu.getHighAddress().compareTo(linkAddress) >= 0) | |
parseCompilationUnitForAddresses(cu); | |
} else { | |
return; | |
} | |
if (moduleScope.getLowAddress().compareTo(cu.getLowAddress()) > 0) | |
moduleScope.setLowAddress(cu.getLowAddress()); | |
if (moduleScope.getHighAddress().compareTo(cu.getHighAddress()) < 0) | |
moduleScope.setHighAddress(cu.getHighAddress()); | |
} | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceFinishedAddressParse)); } | |
} | |
/** | |
* Parse names in the .debug_pubnames section | |
*/ | |
private void parsePublicNames() { | |
if (publicNamesSection == null || debugInfoSection == null) { // no public names and/or debug info | |
return; | |
} | |
IStreamBuffer bufferPublicNames = publicNamesSection.getBuffer(); | |
if (bufferPublicNames == null) | |
return; | |
IStreamBuffer bufferDebuginfo = debugInfoSection.getBuffer(); | |
if (bufferDebuginfo == null) | |
return; | |
long fileIndex = 0; | |
long fileEndIndex = bufferPublicNames.capacity(); | |
// parse all the sets in the .debug_pubnames section | |
while (fileIndex < fileEndIndex) { | |
fileIndex = parsePublicNamesSet(bufferPublicNames, fileIndex, bufferDebuginfo); | |
} | |
} | |
/** | |
* Parse one set of global objects and functions | |
*/ | |
private long parsePublicNamesSet(IStreamBuffer bufferPublicNames, long fileIndex, IStreamBuffer bufferDebugInfo) { | |
bufferPublicNames.position(fileIndex); | |
// get the set's data length | |
int setLength = bufferPublicNames.getInt(); | |
// get the entire set | |
IStreamBuffer dataPublicNames = bufferPublicNames.wrapSubsection(setLength); | |
// get header info for set of public names | |
// skip over Dwarf version | |
dataPublicNames.position(2); | |
int debugInfoOffset = dataPublicNames.getInt(); | |
int debugInfoLength = dataPublicNames.getInt(); | |
try { | |
// read the entire compile unit | |
bufferDebugInfo.position(debugInfoOffset); | |
IStreamBuffer dataInfoBytes = bufferDebugInfo.wrapSubsection(debugInfoLength); | |
CompilationUnitHeader header = provider.debugOffsetsToCompileUnits.get(Long.valueOf(debugInfoOffset)); | |
// get stored abbrev table, or read and parse an abbrev table | |
Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(header.abbreviationOffset); | |
// read names and corresponding types | |
// ignore the 4 bytes of 0 at the end of the set | |
while (dataPublicNames.remaining() > 4) { | |
// read the object offset and name | |
int objectOffset = dataPublicNames.getInt(); | |
String name = readString(dataPublicNames); | |
// read the type of object | |
dataInfoBytes.position(objectOffset); | |
long code = read_unsigned_leb128(dataInfoBytes); | |
AbbreviationEntry entry = abbrevs.get(new Long(code)); | |
// ignore empty names, names without debug info, and | |
// compiler-generated special symbols | |
if (entry != null && name.length() > 0 && !name.startsWith("<")) { | |
// if the name is mangled, unmangle it | |
name = unmangle(name); | |
String baseName = name; | |
int baseStart = name.lastIndexOf("::"); //$NON-NLS-1$ | |
if (baseStart != -1) | |
baseName = name.substring(baseStart + 2); | |
if (entry.tag == DwarfConstants.DW_TAG_variable) { | |
List<PublicNameInfo> variables = provider.publicVariables.get(baseName); | |
if (variables == null) { | |
variables = new ArrayList<PublicNameInfo>(); | |
} | |
variables.add(new PublicNameInfo(name, header, entry.tag)); | |
provider.publicVariables.put(baseName, variables); | |
} else if (entry.tag == DwarfConstants.DW_TAG_subprogram) { | |
List<PublicNameInfo> functions = provider.publicFunctions.get(baseName); | |
if (functions == null) { | |
functions = new ArrayList<PublicNameInfo>(); | |
functions.add(new PublicNameInfo(name, header, entry.tag)); | |
} else { | |
// we don't store debug info offsets, so polymorphic functions for a compilation | |
// unit have identical PublicNameInfo fields; throw all but one away | |
ArrayList<PublicNameInfo> arrayList = (ArrayList<PublicNameInfo>)functions; | |
boolean found = false; | |
for (int i = arrayList.size() - 1; | |
!found && (i >= 0) && (arrayList.get(i).cuHeader == header); i--) | |
found = arrayList.get(i).nameWithNameSpace.equals(name); | |
if (!found) | |
functions.add(new PublicNameInfo(name, header, entry.tag)); | |
} | |
provider.publicFunctions.put(baseName, functions); | |
} | |
} | |
} | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(null, t); | |
} | |
return fileIndex + setLength + 4; | |
} | |
/** | |
* Parse all compilation units for types | |
*/ | |
public void parseForTypes() { | |
synchronized (provider) { | |
for (DwarfCompileUnit compileUnit : provider.compileUnits) { | |
parseCompilationUnitForTypes(compileUnit); | |
} | |
} | |
} | |
/** | |
* Parse compilation unit headers and top-level info in the .debug_info section | |
* @param monitor | |
*/ | |
private void parseCUDebugInfo(IProgressMonitor monitor) { | |
if (debugInfoSection == null) { // no Dwarf data. | |
return; | |
} | |
// if we haven't built the referenced files list from a quick parse yet, | |
// flag it here so we can build the file list as we parse. | |
if (provider.referencedFiles.isEmpty()) { | |
provider.buildReferencedFilesList = true; | |
} | |
IStreamBuffer buffer = debugInfoSection.getBuffer(); | |
IStreamBuffer debugStrings = getDebugStrings(); | |
boolean havePubNames = publicNamesSection != null && publicNamesSection.getBuffer() != null; | |
int totalWork = (int) (buffer.capacity() / 1024); | |
monitor.beginTask(DwarfMessages.DwarfInfoReader_ReadDebugInfo, totalWork); | |
try { | |
if (buffer != null) { | |
long fileIndex = 0; | |
long fileEndIndex = buffer.capacity(); | |
while (fileIndex < fileEndIndex) { | |
long oldIndex = fileIndex; | |
fileIndex = parseCompilationUnitForNames(buffer, fileIndex, debugStrings, fileEndIndex, havePubNames); | |
monitor.worked((int) ((fileIndex - oldIndex) / 1024)); | |
} | |
} | |
} finally { | |
monitor.done(); | |
} | |
provider.compileUnits.trimToSize(); | |
// sort by low address the list of compilation units with code | |
provider.sortedCompileUnitsWithCode.trimToSize(); | |
Collections.sort(provider.sortedCompileUnitsWithCode, sComparatorByLowAddress); | |
provider.buildReferencedFilesList = false; | |
} | |
/** | |
* Parse the compile unit quickly looking for variables that are globally visible | |
* | |
* @return offset of next compilation unit | |
*/ | |
private long parseCompilationUnitForNames(IStreamBuffer buffer, long fileIndex, IStreamBuffer debugStrings, long fileEndIndex, boolean havePubNames) { | |
buffer.position(fileIndex); | |
currentCUHeader = new CompilationUnitHeader(); | |
currentCUHeader.length = buffer.getInt(); | |
currentCUHeader.version = buffer.getShort(); | |
currentCUHeader.abbreviationOffset = buffer.getInt(); | |
currentCUHeader.addressSize = buffer.get(); | |
currentCUHeader.debugInfoOffset = (int) fileIndex; | |
/* | |
* With certain GCC-E 3.x compilers, some subset of compile unit headers can have unit | |
* lengths 4 bytes too long. Before reading compile unit data, make sure there is a | |
* valid compile unit header right after this unit's data. Adjust the length if needed. | |
* To validate, check that the DWARF version and the address size are | |
* the same as the previous compile unit's. | |
*/ | |
if (fileIndex + currentCUHeader.length + 8 < fileEndIndex) { | |
// try good case | |
short nextVersion; | |
byte nextAddrSize; | |
buffer.position(fileIndex + currentCUHeader.length + 8); // to next version | |
nextVersion = buffer.getShort(); | |
buffer.position(fileIndex + currentCUHeader.length + 14); // to next address size | |
nextAddrSize = buffer.get(); | |
if (currentCUHeader.version != nextVersion || currentCUHeader.addressSize != nextAddrSize) { | |
// try adjusting back by 4 bytes | |
buffer.position(fileIndex + currentCUHeader.length + 4); // to next version | |
nextVersion = buffer.getShort(); | |
buffer.position(fileIndex + currentCUHeader.length + 10); // to next address size | |
nextAddrSize = buffer.get(); | |
if (currentCUHeader.version == nextVersion && currentCUHeader.addressSize == nextAddrSize) { | |
// all this work to adjust the length... | |
currentCUHeader.length -= 4; | |
} | |
} | |
} | |
// now read the whole compile unit into memory. note that we're | |
// reading the whole section including the size that we already | |
// read because other code will use the offset of the buffer as | |
// the offset of the section to store things by offset (types, | |
// function declarations, etc). | |
buffer.position(fileIndex); | |
IStreamBuffer in = buffer.wrapSubsection(currentCUHeader.length + 4); | |
// skip over the header info we already read | |
in.position(11); | |
try { | |
// get stored abbrev table, or read and parse an abbrev table | |
Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(currentCUHeader.abbreviationOffset); | |
// read the compile unit's attribute list | |
long code = read_unsigned_leb128(in); | |
AbbreviationEntry entry = abbrevs.get(Long.valueOf(code)); | |
AttributeList attributeList = new AttributeList(entry, in, currentCUHeader.addressSize, debugStrings); | |
processCompileUnit(currentCUHeader, entry.hasChildren, attributeList); | |
if (!havePubNames) { | |
// record file scope variables | |
byte addressSize = currentCUHeader.addressSize; | |
while (in.remaining() > 0) { | |
code = read_unsigned_leb128(in); | |
if (code != 0) { | |
entry = abbrevs.get(Long.valueOf(code)); | |
switch (entry.tag) { | |
// record names of interest, but not other Dwarf attributes | |
case DwarfConstants.DW_TAG_variable: | |
{ | |
// get variable names at the compile unit scope level | |
parseAttributesForNames(true, baseAndScopedNames, entry, in, addressSize, debugStrings); | |
if (baseAndScopedNames.baseName != null) | |
storePublicNames(provider.publicVariables, baseAndScopedNames, currentCUHeader, (short) DwarfConstants.DW_TAG_variable); | |
break; | |
} | |
case DwarfConstants.DW_TAG_imported_declaration: // for possible namespace alias | |
case DwarfConstants.DW_TAG_namespace: | |
case DwarfConstants.DW_TAG_subprogram: | |
case DwarfConstants.DW_TAG_enumerator: | |
case DwarfConstants.DW_TAG_class_type: | |
case DwarfConstants.DW_TAG_structure_type: | |
case DwarfConstants.DW_TAG_array_type: | |
case DwarfConstants.DW_TAG_base_type: | |
case DwarfConstants.DW_TAG_enumeration_type: | |
case DwarfConstants.DW_TAG_pointer_type: | |
case DwarfConstants.DW_TAG_ptr_to_member_type: | |
case DwarfConstants.DW_TAG_subroutine_type: | |
case DwarfConstants.DW_TAG_typedef: | |
case DwarfConstants.DW_TAG_union_type: | |
case DwarfConstants.DW_TAG_access_declaration: | |
case DwarfConstants.DW_TAG_catch_block: | |
case DwarfConstants.DW_TAG_common_block: | |
case DwarfConstants.DW_TAG_common_inclusion: | |
case DwarfConstants.DW_TAG_condition: | |
case DwarfConstants.DW_TAG_const_type: | |
case DwarfConstants.DW_TAG_constant: | |
case DwarfConstants.DW_TAG_entry_point: | |
case DwarfConstants.DW_TAG_file_type: | |
case DwarfConstants.DW_TAG_formal_parameter: | |
case DwarfConstants.DW_TAG_friend: | |
case DwarfConstants.DW_TAG_imported_module: | |
case DwarfConstants.DW_TAG_inheritance: | |
case DwarfConstants.DW_TAG_inlined_subroutine: | |
case DwarfConstants.DW_TAG_interface_type: | |
case DwarfConstants.DW_TAG_label: | |
case DwarfConstants.DW_TAG_lexical_block: | |
case DwarfConstants.DW_TAG_member: | |
case DwarfConstants.DW_TAG_module: | |
case DwarfConstants.DW_TAG_namelist: | |
case DwarfConstants.DW_TAG_namelist_item: | |
case DwarfConstants.DW_TAG_packed_type: | |
case DwarfConstants.DW_TAG_reference_type: | |
case DwarfConstants.DW_TAG_restrict_type: | |
case DwarfConstants.DW_TAG_set_type: | |
case DwarfConstants.DW_TAG_shared_type: | |
case DwarfConstants.DW_TAG_string_type: | |
case DwarfConstants.DW_TAG_subrange_type: | |
case DwarfConstants.DW_TAG_template_type_param: | |
case DwarfConstants.DW_TAG_template_value_param: | |
case DwarfConstants.DW_TAG_thrown_type: | |
case DwarfConstants.DW_TAG_try_block: | |
case DwarfConstants.DW_TAG_unspecified_parameters: | |
case DwarfConstants.DW_TAG_variant: | |
case DwarfConstants.DW_TAG_variant_part: | |
case DwarfConstants.DW_TAG_volatile_type: | |
case DwarfConstants.DW_TAG_with_stmt: | |
{ | |
AttributeValue.skipAttributesToSibling(entry, in, addressSize); | |
break; | |
} | |
// case DwarfConstants.DW_TAG_compile_unit: | |
// case DwarfConstants.DW_TAG_partial_unit: | |
// case DwarfConstants.DW_TAG_unspecified_type: | |
default: | |
// skip entire entries | |
AttributeList.skipAttributes(entry, in, addressSize); | |
break; | |
} | |
} | |
} | |
} | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed1 | |
+ debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed2 + symbolFilePath, t); | |
} | |
// skip past the compile unit. note that the | |
// currentCUHeader.length does not include | |
// the size of the unit length itself | |
fileIndex += currentCUHeader.length + 4; | |
return fileIndex; | |
} | |
/** | |
* Parse attributes, returning names | |
* | |
* @param onlyExternal only return names if they have external visibility | |
* @param names array to hold up to two names | |
* @param entry debug info entry | |
* @param in buffer stream of debug info | |
* @param addressSize | |
* @param debugStrings | |
* @return DW_AT_name value in names[0], unmangled DW_AT_MIPS_linkage_name value in | |
* names[1], or nulls | |
*/ | |
private void parseAttributesForNames(boolean onlyExternal, BaseAndScopedNames baseAndScopedNames, AbbreviationEntry entry, IStreamBuffer in, | |
byte addressSize, IStreamBuffer debugStrings) { | |
String name = null; | |
baseAndScopedNames.baseName = null; | |
baseAndScopedNames.nameWithScope = null; | |
boolean isExternal = false; | |
// go through the attributes and throw away everything except the names | |
int len = entry.attributes.size(); | |
for (int i = 0; i < len; i++) { | |
Attribute attr = entry.attributes.get(i); | |
try { | |
if ( attr.tag == DwarfConstants.DW_AT_name | |
|| attr.tag == DwarfConstants.DW_AT_MIPS_linkage_name) { | |
// names should be DW_FORM_string or DW_FORM_strp | |
if (attr.form == DwarfConstants.DW_FORM_string) { | |
int c; | |
StringBuffer sb = new StringBuffer(); | |
while ((c = (in.get() & 0xff)) != -1) { | |
if (c == 0) { | |
break; | |
} | |
sb.append((char) c); | |
} | |
name = sb.toString(); | |
} else if (attr.form == DwarfConstants.DW_FORM_strp) { | |
int debugStringOffset = in.getInt(); | |
if ( debugStrings != null | |
&& debugStringOffset >= 0 | |
&& debugStringOffset < debugStrings.capacity()) { | |
debugStrings.position(debugStringOffset); | |
name = DwarfInfoReader.readString(debugStrings); | |
} | |
} | |
if (name != null) { | |
if (attr.tag == DwarfConstants.DW_AT_name) { | |
baseAndScopedNames.baseName = name; | |
baseAndScopedNames.nameWithScope = name; | |
} else { | |
if (exeReader instanceof BaseExecutableSymbolicsReader) { | |
try { | |
baseAndScopedNames.nameWithScope = unmangler.unmangle(unmangler.undecorate(name)); | |
} catch(UnmanglingException ue) { | |
} | |
} | |
} | |
name = null; | |
} | |
} else if (attr.tag == DwarfConstants.DW_AT_external) { | |
if (attr.form == DwarfConstants.DW_FORM_flag) { | |
isExternal = in.get() != 0; | |
} else { | |
AttributeValue.skipAttributeValue(attr.form, in, addressSize); | |
} | |
} else { | |
AttributeValue.skipAttributeValue(attr.form, in, addressSize); | |
} | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(null, t); | |
break; | |
} | |
} | |
// if only looking for externals, throw away internals | |
if (onlyExternal && !isExternal) { | |
baseAndScopedNames.baseName = null; | |
baseAndScopedNames.nameWithScope = null; | |
} else { | |
// if only have the scoped name, derive the base name | |
if (baseAndScopedNames.nameWithScope != null && baseAndScopedNames.baseName == null) { | |
int baseStart = baseAndScopedNames.nameWithScope.lastIndexOf("::"); //$NON-NLS-1$ | |
if (baseStart != -1) | |
baseAndScopedNames.baseName = baseAndScopedNames.nameWithScope.substring(baseStart + 2); | |
else | |
baseAndScopedNames.baseName = baseAndScopedNames.nameWithScope; | |
} | |
} | |
} | |
/** | |
* Store compilation unit level names from Dwarf .debug_info | |
* | |
* @param namesStore | |
* @param names | |
* @param offset | |
*/ | |
private void storePublicNames(Map<String, List<PublicNameInfo>> namesStore, BaseAndScopedNames baseAndScopedNames, | |
CompilationUnitHeader cuHeader, short tag) { | |
List<PublicNameInfo> currentNames = namesStore.get(baseAndScopedNames.baseName); | |
if (currentNames == null) { | |
currentNames = new ArrayList<PublicNameInfo>(); | |
namesStore.put(baseAndScopedNames.baseName, currentNames); | |
} | |
currentNames.add(new PublicNameInfo(baseAndScopedNames.nameWithScope, cuHeader, tag)); | |
} | |
private synchronized void parseCompilationUnitForAddressesPrivate(DwarfCompileUnit compileUnit, IProgressMonitor monitor) | |
{ | |
synchronized (compileUnit) { | |
if (compileUnit.isParsedForAddresses()) | |
return; | |
compileUnit.setParsedForAddresses(true); | |
CompilationUnitHeader header = compileUnit.header; | |
if (header == null) | |
return; | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().trace(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceAddressParse1 + Integer.toHexString(header.debugInfoOffset) + DwarfMessages.DwarfInfoReader_TraceAddressParse2 + header.scope.getFilePath())); } | |
IStreamBuffer buffer = debugInfoSection.getBuffer(); | |
if (buffer == null) | |
return; | |
int fileIndex = header.debugInfoOffset; | |
// read the compile unit debug info into memory | |
buffer.position(fileIndex); | |
IStreamBuffer data = buffer.wrapSubsection(header.length + 4); | |
// skip over the header, since we've already read it | |
data.position(11); // unit length + version + abbrev table offset + address size | |
currentCompileUnitScope = compileUnit; | |
currentParentScope = compileUnit; | |
registerScope(header.debugInfoOffset, compileUnit); | |
currentCUHeader = header; | |
try { | |
// get stored abbrev table, or read and parse an abbrev table | |
Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(header.abbreviationOffset); | |
parseForAddresses(data, abbrevs, header, new Stack<Scope>(), monitor); | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed1 | |
+ debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed2 + symbolFilePath, t); | |
} | |
} | |
} | |
/** | |
* Given compilation unit, parse to get variables and all children that have address ranges. | |
* | |
* @param compileUnit | |
*/ | |
public void parseCompilationUnitForAddresses(final DwarfCompileUnit compileUnit) { | |
synchronized (provider) { | |
synchronized (compileUnit) { | |
if (compileUnit.isParsedForAddresses()) | |
return; | |
} | |
parseCompilationUnitForAddressesPrivate(compileUnit, new NullProgressMonitor()); | |
} | |
} | |
synchronized public void parseCompilationUnitForTypes(DwarfCompileUnit compileUnit) { | |
synchronized (provider) { | |
synchronized(compileUnit) { | |
if (compileUnit.isParsedForTypes()) | |
return; | |
compileUnit.setParsedForTypes(true); | |
CompilationUnitHeader header = compileUnit.header; | |
if (header == null) | |
return; | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().trace(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceTypeParse1 + Integer.toHexString(header.debugInfoOffset) + DwarfMessages.DwarfInfoReader_TraceTypeParse2 + header.scope.getFilePath())); } | |
IStreamBuffer buffer = debugInfoSection.getBuffer(); | |
if (buffer == null) | |
return; | |
int fileIndex = header.debugInfoOffset; | |
// read the compile unit debug info into memory | |
buffer.position(fileIndex); | |
IStreamBuffer data = buffer.wrapSubsection(header.length + 4); | |
// skip over the header, since we've already read it | |
data.position(11); // unit length + version + abbrev table offset + address size | |
currentCompileUnitScope = compileUnit; | |
currentParentScope = compileUnit; | |
registerScope(header.debugInfoOffset, compileUnit); | |
currentCUHeader = header; | |
try { | |
// get stored abbrev table, or read and parse an abbrev table | |
Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(header.abbreviationOffset); | |
parseForTypes(data, abbrevs, header, new Stack<Scope>()); | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseTraceInfoSectionFailed1 | |
+ debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseTraceInfoSectionFailed2 + symbolFilePath, t); | |
} | |
} | |
} | |
} | |
public void quickParseDebugInfo(IProgressMonitor monitor) { | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceQuickParse + symbolFilePath)); } | |
synchronized (provider) { | |
doQuickParseDebugInfo(monitor); | |
} | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceFinishedQuickParse)); } | |
} | |
/** | |
* Does a quick parse of the .debug_info section just to get a list of | |
* referenced files from the compile units. | |
*/ | |
private void doQuickParseDebugInfo(IProgressMonitor monitor) { | |
if (debugInfoSection == null) { // no Dwarf data. | |
return; | |
} | |
// get the compile units out of the .debug_info section | |
IStreamBuffer buffer = debugInfoSection.getBuffer(); | |
if (buffer == null) | |
return; | |
try { | |
long fileIndex = 0; | |
long fileEndIndex = buffer.capacity(); | |
monitor.beginTask(DwarfMessages.DwarfInfoReader_ReadDebugInfo, (int) (fileEndIndex / 1024)); | |
buffer.position(0); | |
while (fileIndex < fileEndIndex) { | |
buffer.position(fileIndex); | |
int unit_length = buffer.getInt(); | |
short version = buffer.getShort(); | |
int debug_abbrev_offset = buffer.getInt(); | |
byte address_size = buffer.get(); | |
/* | |
* With certain GCC-E 3.x compilers, some subset of compile unit headers can have unit | |
* lengths 4 bytes too long. So before reading this unit's data, make sure there is a | |
* valid compile unit header right after this unit's data. Adjust the length if needed. | |
* To validate, check that the DWARF version and the address size are | |
* the same as the previous compile unit's. | |
*/ | |
if (fileIndex + unit_length + 8 < fileEndIndex) { | |
// try good case | |
short nextVersion; | |
byte nextAddrSize; | |
buffer.position(fileIndex + unit_length + 8); // to next version | |
nextVersion = buffer.getShort(); | |
buffer.position(fileIndex + unit_length + 14); // to next address size | |
nextAddrSize = buffer.get(); | |
if (version != nextVersion || address_size != nextAddrSize) { | |
// try adjusting back by 4 bytes | |
buffer.position(fileIndex + unit_length + 4); // to next version | |
nextVersion = buffer.getShort(); | |
buffer.position(fileIndex + unit_length + 10); // to next address size | |
nextAddrSize = buffer.get(); | |
if (version == nextVersion && address_size == nextAddrSize) { | |
unit_length -= 4; | |
} // otherwise, just let things bomb | |
} | |
} | |
buffer.position(fileIndex + 4); | |
IStreamBuffer data = buffer.wrapSubsection(unit_length); | |
data.position(7); // skip header info already read | |
// get the abbreviation entry for the compile unit | |
// find the offset to the | |
Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(debug_abbrev_offset); | |
long code = read_unsigned_leb128(data); | |
AbbreviationEntry entry = abbrevs.get(Long.valueOf(code)); | |
AttributeList attributeList = new AttributeList(entry, data, address_size, getDebugStrings()); | |
// get comp_dir and name and figure out the path | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
String compDir = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_comp_dir); | |
IPath filePath = fileHelper.normalizeFilePath(compDir, name); | |
provider.referencedFiles.add(filePath.toOSString()); | |
// do a quick parse of the line table to get any other | |
// referenced files | |
AttributeValue a = attributeList.getAttribute(DwarfConstants.DW_AT_stmt_list); | |
if (a != null) { | |
int stmtList = a.getValueAsInt(); | |
quickParseLineInfo(stmtList, compDir); | |
} | |
// skip past the compile unit. note that the unit_length does | |
// not include the size of the unit length itself | |
long oldIndex = fileIndex; | |
fileIndex += unit_length + 4; | |
monitor.worked((int) ((fileIndex - oldIndex) / 1024)); | |
} | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseSectionSourceFilesFailed1 | |
+ debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseSectionSourceFilesFailed2 + symbolFilePath, t); | |
} finally { | |
monitor.done(); | |
} | |
} | |
/** | |
* Get the .debug_strings section. | |
* @return ByteBuffer or <code>null</code> | |
*/ | |
private IStreamBuffer getDebugStrings() { | |
return getDwarfSection(DWARF_DEBUG_STR); | |
} | |
/** | |
* Does a quick parse of the .debug_line section just to get a list of | |
* referenced files from the line table. | |
*/ | |
private void quickParseLineInfo(int lineTableOffset, String compileUnitDirectory) { | |
IPath compileUnitDirectoryPath = PathUtils.createPath(compileUnitDirectory); | |
try { | |
// do a quick parse of the line table just to get referenced files | |
IStreamBuffer data = getDwarfSection(DWARF_DEBUG_LINE); | |
if (data != null) { | |
data.position(lineTableOffset); | |
/* | |
* Skip past the bytes of the header that we don't care about: | |
* unit_length (4 bytes), version (2 bytes), header_length (4 bytes), | |
* minimum_instruction_length (1 byte), default_is_stmt (1 byte), | |
* line_base (1 byte), line_range (1 byte) | |
*/ | |
data.position(data.position() + 14); | |
// we need to get this value so we can skip over | |
// standard_opcode_lengths | |
int opcode_base = data.get() & 0xff; | |
data.position(data.position() + opcode_base - 1); | |
// include_directories | |
ArrayList<String> dirList = new ArrayList<String>(); | |
// add the compilation directory of the CU as the first | |
// directory | |
dirList.add(compileUnitDirectory); | |
while (true) { | |
String str = readString(data); | |
if (str.length() == 0) | |
break; | |
// if the directory is relative, append it to the CU dir | |
IPath dir = PathUtils.createPath(str); | |
if (!dir.isAbsolute() && dir.getDevice() == null) { | |
dir = compileUnitDirectoryPath.append(str); | |
} | |
dirList.add(dir.toString()); | |
} | |
while (true) { | |
String fileName = readString(data); | |
if (fileName.length() == 0) // no more file entry | |
break; | |
// dir index | |
long leb128 = DwarfInfoReader.read_unsigned_leb128(data); | |
IPath fullPath = fileHelper.normalizeFilePath(dirList.get((int) leb128), fileName); | |
if (fullPath != null) { | |
provider.referencedFiles.add(fullPath.toOSString()); | |
} | |
// skip the modification time and file size | |
leb128 = read_unsigned_leb128(data); | |
leb128 = read_unsigned_leb128(data); | |
} | |
} | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(null, t); | |
} | |
} | |
/** | |
* Parse the line table for a given compile unit | |
* @param attributes | |
* @param fileList list for file entries | |
* @return new array of ILineEntry | |
*/ | |
public Collection<ILineEntry> parseLineTable(IScope scope, AttributeList attributes, List<IPath> fileList) { | |
synchronized (provider) { | |
List<ILineEntry> lineEntries = new ArrayList<ILineEntry>(); | |
try { | |
IStreamBuffer data = getDwarfSection(DWARF_DEBUG_LINE); | |
AttributeValue a = attributes.getAttribute(DwarfConstants.DW_AT_stmt_list); | |
if (data != null && a != null) { | |
int stmtList = a.getValueAsInt(); | |
data.position(stmtList); | |
/* | |
* Read line table header: | |
* | |
* total_length: 4 bytes (excluding itself) | |
* version: 2 | |
* prologue length: 4 | |
* minimum_instruction_len: 1 | |
* default_is_stmt: 0 or 1 | |
* line_base: 1 | |
* line_range: 1 | |
* opcode_base: 1 | |
* standard_opcode_lengths: (value of opcode_base) | |
*/ | |
// Remember the CU line tables we've parsed. | |
int length = data.getInt() + 4; | |
// Skip the following till "opcode_base" | |
@SuppressWarnings("unused") | |
int version = data.getShort(); | |
@SuppressWarnings("unused") | |
int prologue_length = data.getInt(); | |
int minimum_instruction_length = data.get() & 0xff; | |
boolean default_is_stmt = data.get() > 0; | |
int line_base = data.get(); // signed | |
int line_range = data.get() & 0xff; | |
int opcode_base = data.get() & 0xff; | |
byte[] opcodes = new byte[opcode_base - 1]; | |
data.get(opcodes); | |
// Read in directories. | |
// | |
ArrayList<String> dirList = new ArrayList<String>(); | |
// Put the compilation directory of the CU as the first dir | |
String compDir = attributes.getAttributeValueAsString(DwarfConstants.DW_AT_comp_dir); | |
dirList.add(compDir); | |
IPath compDirPath = PathUtils.createPath(compDir); | |
String str, fileName; | |
while (true) { | |
str = readString(data); | |
if (str.length() == 0) | |
break; | |
// If the directory is relative, append it to the CU dir | |
IPath dir = PathUtils.createPath(str); | |
if (!dir.isAbsolute() && dir.getDevice() == null) { | |
dir = compDirPath.append(str); | |
} | |
dirList.add(dir.toString()); | |
} | |
// Read file names | |
// | |
long leb128; | |
while (true) { | |
fileName = readString(data); | |
if (fileName.length() == 0) // no more file entry | |
break; | |
// dir index | |
leb128 = read_unsigned_leb128(data); | |
IPath fullPath = fileHelper.normalizeFilePath(dirList.get((int) leb128), fileName); | |
// add a null as a placeholder when the filename is enclosed in '<' & '>' (e.g., "<stdin>") | |
fileList.add(fullPath); | |
// Skip the following | |
// | |
// modification time | |
leb128 = read_unsigned_leb128(data); | |
// file size in bytes | |
leb128 = read_unsigned_leb128(data); | |
} | |
long info_address = 0; | |
long info_file = 1; | |
int info_line = 1; | |
int info_column = 0; | |
boolean is_stmt = default_is_stmt; | |
@SuppressWarnings("unused") | |
int info_flags = 0; | |
@SuppressWarnings("unused") | |
long info_ISA = 0; | |
long lineInfoEnd = stmtList + length; | |
while (data.position() < lineInfoEnd) { | |
byte opcodeB = data.get(); | |
int opcode = 0xFF & opcodeB; | |
if (opcode >= opcode_base) { | |
info_line += (((opcode - opcode_base) % line_range) + line_base); | |
info_address += (opcode - opcode_base) / line_range * minimum_instruction_length; | |
if (is_stmt && fileList.size() > 0) { | |
IPath path = fileList.get((int) info_file - 1); | |
// added a null as a placeholder when the filename was enclosed in '<' & '>' (e.g., "<stdin>") | |
if (path != null) | |
lineEntries.add(new LineEntry(path, info_line, info_column, new Addr32(info_address), null)); | |
} | |
info_flags &= ~(DwarfConstants.LINE_BasicBlock | DwarfConstants.LINE_PrologueEnd | DwarfConstants.LINE_EpilogueBegin); | |
} else if (opcode == 0) { | |
long op_size = read_unsigned_leb128(data); | |
long op_pos = data.position(); | |
int code = data.get() & 0xff; | |
switch (code) { | |
case DwarfConstants.DW_LNE_define_file: { | |
fileName = readString(data); | |
long dir = read_unsigned_leb128(data); | |
@SuppressWarnings("unused") | |
long modTime = read_unsigned_leb128(data); | |
@SuppressWarnings("unused") | |
long fileSize = read_unsigned_leb128(data); | |
IPath fullPath = fileHelper.normalizeFilePath(dirList.get((int) dir), fileName); | |
if (fullPath != null) { | |
fileList.add(fullPath); | |
} | |
break; | |
} | |
case DwarfConstants.DW_LNE_end_sequence: | |
info_flags |= DwarfConstants.LINE_EndSequence; | |
if (lineEntries.size() > 0) { | |
// this just marks the end of a line number | |
// program sequence. use | |
// its address to set the high address of the | |
// last line entry | |
lineEntries.get(lineEntries.size() - 1).setHighAddress(new Addr32(info_address)); | |
} | |
// it also resets the state machine | |
info_address = 0; | |
info_file = 1; | |
info_line = 1; | |
info_column = 0; | |
is_stmt = default_is_stmt; | |
info_flags = 0; | |
info_ISA = 0; | |
break; | |
case DwarfConstants.DW_LNE_set_address: | |
info_address = data.getInt(); | |
break; | |
default: | |
data.position((int) (data.position() + op_size - 1)); | |
break; | |
} | |
assert (data.position() == op_pos + op_size); | |
} else { | |
switch (opcode) { | |
case DwarfConstants.DW_LNS_copy: | |
if (is_stmt && fileList.size() > 0) { | |
lineEntries.add(new LineEntry(fileList.get((int) info_file - 1), info_line, | |
info_column, new Addr32(info_address), null)); | |
} | |
info_flags &= ~(DwarfConstants.LINE_BasicBlock | DwarfConstants.LINE_PrologueEnd | DwarfConstants.LINE_EpilogueBegin); | |
break; | |
case DwarfConstants.DW_LNS_advance_pc: | |
info_address += read_unsigned_leb128(data) * minimum_instruction_length; | |
break; | |
case DwarfConstants.DW_LNS_advance_line: | |
info_line += read_signed_leb128(data); | |
break; | |
case DwarfConstants.DW_LNS_set_file: | |
info_file = read_unsigned_leb128(data); | |
break; | |
case DwarfConstants.DW_LNS_set_column: | |
info_column = (int) read_unsigned_leb128(data); | |
break; | |
case DwarfConstants.DW_LNS_negate_stmt: | |
is_stmt = !is_stmt; | |
break; | |
case DwarfConstants.DW_LNS_set_basic_block: | |
info_flags |= DwarfConstants.LINE_BasicBlock; | |
break; | |
case DwarfConstants.DW_LNS_const_add_pc: | |
info_address += (255 - opcode_base) / line_range * minimum_instruction_length; | |
break; | |
case DwarfConstants.DW_LNS_fixed_advance_pc: | |
info_address += data.getShort(); | |
break; | |
case DwarfConstants.DW_LNS_set_prologue_end: | |
info_flags |= DwarfConstants.LINE_PrologueEnd; | |
break; | |
case DwarfConstants.DW_LNS_set_epilogue_begin: | |
info_flags |= DwarfConstants.LINE_EpilogueBegin; | |
break; | |
case DwarfConstants.DW_LNS_set_isa: | |
info_ISA = read_unsigned_leb128(data); | |
break; | |
default: | |
break; | |
} | |
} | |
} | |
} | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(null, t); | |
} | |
// sort by start address | |
Collections.sort(lineEntries); | |
// fill in the end addresses as needed | |
ILineEntry previousEntry = null; | |
for (ILineEntry line : lineEntries) { | |
if (previousEntry != null && previousEntry.getHighAddress() == null) { | |
previousEntry.setHighAddress(line.getLowAddress()); | |
} | |
previousEntry = line; | |
} | |
// the last line entry | |
if (previousEntry != null) { | |
IAddress prevHigh = previousEntry.getHighAddress(); | |
if (prevHigh == null) | |
previousEntry.setHighAddress(scope.getHighAddress()); | |
// FIXME: the following is causing JUnit tests to fail | |
// else if (prevHigh != null && prevHigh.compareTo(scope.getHighAddress()) > 0) | |
// previousEntry.setHighAddress(scope.getHighAddress()); | |
} | |
return lineEntries; | |
} | |
} | |
private void parseForAddresses(IStreamBuffer in, Map<Long, AbbreviationEntry> abbrevs, CompilationUnitHeader header, | |
Stack<Scope> nestingStack, IProgressMonitor monitor) | |
throws IOException { | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().trace(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceScopeAddressParse1 + header.scope.getName() + DwarfMessages.DwarfInfoReader_TraceScopeAddressParse2 + Long.toHexString(header.debugInfoOffset))); } | |
try { | |
long startWork = in.remaining(); | |
long lastWork = startWork; | |
int workChunk = (int)(startWork / Integer.MAX_VALUE) + 1; | |
int work = (int)(startWork / workChunk); | |
monitor.beginTask(DwarfMessages.DwarfInfoReader_TraceScopeAddressParse1 + header.scope.getName() + DwarfMessages.DwarfInfoReader_TraceScopeAddressParse2 + Long.toHexString(header.debugInfoOffset), work); | |
while (in.remaining() > 0) { | |
work = (int)((lastWork - in.remaining()) / workChunk); | |
monitor.worked(work); | |
lastWork = in.remaining(); | |
long offset = in.position() + currentCUHeader.debugInfoOffset; | |
long code = read_unsigned_leb128(in); | |
if (code != 0) { | |
AbbreviationEntry entry = abbrevs.get(new Long(code)); | |
if (entry == null) { | |
assert false; | |
continue; | |
} | |
if (entry.hasChildren) { | |
nestingStack.push(currentParentScope); | |
} | |
if (isDebugInfoEntryWithAddressRange(entry.tag)) { | |
AttributeList attributeList = new AttributeList(entry, in, header.addressSize, getDebugStrings()); | |
processDebugInfoEntry(offset, entry, attributeList, header); | |
// if we didn't create a scope for a routine or lexical block, then ignore its innards | |
switch (entry.tag) { | |
case DwarfConstants.DW_TAG_subprogram: | |
case DwarfConstants.DW_TAG_inlined_subroutine: | |
case DwarfConstants.DW_TAG_lexical_block: | |
if (entry.hasChildren && (provider.scopesByOffset.get(offset) == null)) { | |
// because some versions of GCC-E 3.x produce invalid sibling offsets, | |
// always read entry attributes, rather than skipping using siblings | |
// keep track of nesting just like in parseForTypes() | |
int nesting = 1; | |
while ((nesting > 0) && (in.remaining() > 0)) { | |
offset = in.position() + currentCUHeader.debugInfoOffset; | |
code = read_unsigned_leb128(in); | |
if (code != 0) { | |
entry = abbrevs.get(new Long(code)); | |
if (entry == null) { | |
assert false; | |
continue; | |
} | |
if (entry.hasChildren) | |
nesting++; | |
// skip the attributes we're not reading... | |
AttributeList.skipAttributes(entry, in, header.addressSize); | |
} else { | |
nesting--; | |
} | |
} | |
// nesting loop ends after reading 0 code of skipped entry | |
if (nestingStack.isEmpty()) { | |
// FIXME | |
currentParentScope = null; | |
} else { | |
currentParentScope = nestingStack.pop(); | |
} | |
} | |
break; | |
} | |
} else { | |
// skip the attributes we're not reading... | |
AttributeList.skipAttributes(entry, in, header.addressSize); | |
} | |
} else { | |
if (code == 0) { | |
if (nestingStack.isEmpty()) { | |
// FIXME | |
currentParentScope = null; | |
} else { | |
currentParentScope = nestingStack.pop(); | |
} | |
} | |
} | |
} | |
} catch (IOException e) { | |
throw e; | |
} finally { | |
monitor.done(); | |
} | |
} | |
private void parseForTypes(IStreamBuffer in, Map<Long, AbbreviationEntry> abbrevs, CompilationUnitHeader header, | |
Stack<Scope> nestingStack) | |
throws IOException { | |
if (EDCTrace.SYMBOL_READER_TRACE_ON) { EDCTrace.getTrace().trace(null, EDCTrace.fixArg(DwarfMessages.DwarfInfoReader_TraceParseTypes1 + header.scope.getName() + DwarfMessages.DwarfInfoReader_TraceParseTypes2 + Long.toHexString(header.debugInfoOffset))); } | |
Stack<IType> typeStack = new Stack<IType>(); | |
typeToParentMap.clear(); | |
currentParentScope = currentCompileUnitScope; | |
while (in.remaining() > 0) { | |
long offset = in.position() + currentCUHeader.debugInfoOffset; | |
long code = read_unsigned_leb128(in); | |
if (code != 0) { | |
AbbreviationEntry entry = abbrevs.get(new Long(code)); | |
if (entry == null) { | |
assert false; | |
continue; | |
} | |
if (entry.hasChildren) { | |
nestingStack.push(currentParentScope); | |
typeStack.push(currentParentType); | |
} | |
if (isForwardTypeTag(entry.tag) || isForwardTypeChildTag(entry.tag)) { | |
processDebugInfoEntry(offset, entry, | |
new AttributeList(entry, in, header.addressSize, getDebugStrings()), | |
header); | |
} else { | |
switch (entry.tag) { | |
case DwarfConstants.DW_TAG_subprogram: | |
case DwarfConstants.DW_TAG_inlined_subroutine: | |
case DwarfConstants.DW_TAG_lexical_block: { | |
Scope scope = provider.scopesByOffset.get(offset); // may be null | |
if (scope != null) | |
currentParentScope = scope; | |
break; | |
} | |
} | |
// skip the attributes we're not reading... | |
AttributeList.skipAttributes(entry, in, header.addressSize); | |
} | |
} else { | |
// code == 0 | |
if (nestingStack.isEmpty()) { | |
// FIXME | |
currentParentType = null; | |
currentParentScope = null; | |
} else { | |
currentParentScope = nestingStack.pop(); | |
currentParentType = typeStack.pop(); | |
} | |
} | |
} | |
} | |
/** | |
* Tell if a tag will be parsed on-demand to generate an IType, and will | |
* be accessible via provider.getType() or provider.readType(). | |
* <p> | |
* Note: DW_TAG_member is usually considered a child of struct/class/etc., but | |
* a static class variable contains a reference to it, so we must be able to | |
* locate it. | |
* @param tag | |
* @return true if type is parsed and should have a ForwardDwarfDefinition | |
*/ | |
private boolean isForwardTypeTag(short tag) { | |
switch (tag) { | |
case DwarfConstants.DW_TAG_array_type: | |
case DwarfConstants.DW_TAG_class_type: | |
case DwarfConstants.DW_TAG_enumeration_type: | |
case DwarfConstants.DW_TAG_member: | |
case DwarfConstants.DW_TAG_pointer_type: | |
case DwarfConstants.DW_TAG_reference_type: | |
case DwarfConstants.DW_TAG_structure_type: | |
case DwarfConstants.DW_TAG_subroutine_type: | |
case DwarfConstants.DW_TAG_typedef: | |
case DwarfConstants.DW_TAG_union_type: | |
//case DwarfConstants.DW_TAG_unspecified_parameters: | |
case DwarfConstants.DW_TAG_inheritance: | |
case DwarfConstants.DW_TAG_ptr_to_member_type: | |
//case DwarfConstants.DW_TAG_with_stmt: | |
case DwarfConstants.DW_TAG_base_type: | |
//case DwarfConstants.DW_TAG_catch_block: | |
case DwarfConstants.DW_TAG_const_type: | |
//case DwarfConstants.DW_TAG_enumerator: | |
//case DwarfConstants.DW_TAG_file_type: | |
//case DwarfConstants.DW_TAG_friend: | |
case DwarfConstants.DW_TAG_template_type_param: | |
//case DwarfConstants.DW_TAG_template_value_param: | |
//case DwarfConstants.DW_TAG_thrown_type: | |
//case DwarfConstants.DW_TAG_try_block: | |
case DwarfConstants.DW_TAG_volatile_type: | |
case DwarfConstants.DW_TAG_subrange_type: | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Tell if a tag is a parsed child of an IType. This should not be explicitly | |
* referenced in provider.typesByOffset or .forwardDwarfDefinitions but as | |
* children of other ForwardDwarfDefinitions parsed on demand. | |
*<p> | |
* Note: DW_TAG_member is usually considered a child of struct/class/etc., but | |
* a static class variable contains a reference to it, so we must be able to | |
* locate it. Thus, it is not listed here. | |
* @param tag | |
* @return true if component is parsed and a child of a forward definition | |
*/ | |
private boolean isForwardTypeChildTag(short tag) { | |
switch (tag) { | |
//case DwarfConstants.DW_TAG_unspecified_parameters: | |
case DwarfConstants.DW_TAG_inheritance: | |
case DwarfConstants.DW_TAG_enumerator: | |
case DwarfConstants.DW_TAG_member: | |
case DwarfConstants.DW_TAG_subrange_type: | |
//case DwarfConstants.DW_TAG_friend: | |
//case DwarfConstants.DW_TAG_template_type_param: | |
//case DwarfConstants.DW_TAG_template_value_param: | |
//case DwarfConstants.DW_TAG_thrown_type: | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Fully parse any debug info entry. | |
* @param offset | |
* @param entry | |
* @param attributeList | |
* @param header | |
* @param compositeNesting | |
*/ | |
private void processDebugInfoEntry(long offset, AbbreviationEntry entry, AttributeList attributeList, | |
CompilationUnitHeader header) { | |
//System.out.println("Handling " + entry.tag + " at " + Long.toHexString(offset)); | |
short tag = entry.tag; | |
// We are only interested in certain tags. | |
switch (tag) { | |
case DwarfConstants.DW_TAG_array_type: | |
processArrayType(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_class_type: | |
processClassType(offset, attributeList, header, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_enumeration_type: | |
processEnumType(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_formal_parameter: | |
processVariable(offset, attributeList, true); | |
break; | |
case DwarfConstants.DW_TAG_lexical_block: | |
processLexicalBlock(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_member: | |
processField(offset, attributeList, header, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_pointer_type: | |
processPointerType(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_reference_type: | |
processReferenceType(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_structure_type: | |
processStructType(offset, attributeList, header, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_subroutine_type: | |
processSubroutineType(offset, attributeList, header, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_typedef: | |
processTypeDef(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_union_type: | |
processUnionType(offset, attributeList, header, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_unspecified_parameters: | |
break; | |
case DwarfConstants.DW_TAG_inheritance: | |
processInheritance(offset, attributeList, header, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_ptr_to_member_type: | |
processPtrToMemberType(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_with_stmt: | |
break; | |
case DwarfConstants.DW_TAG_base_type: | |
processBasicType(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_catch_block: | |
break; | |
case DwarfConstants.DW_TAG_const_type: | |
processConstType(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_enumerator: | |
processEnumerator(offset, attributeList); | |
break; | |
case DwarfConstants.DW_TAG_file_type: | |
break; | |
case DwarfConstants.DW_TAG_friend: | |
break; | |
case DwarfConstants.DW_TAG_subprogram: | |
processSubprogram(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_inlined_subroutine: | |
processInlinedSubroutine(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_template_type_param: | |
processTemplateTypeParam(offset, attributeList, header, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_template_value_param: | |
break; | |
case DwarfConstants.DW_TAG_thrown_type: | |
break; | |
case DwarfConstants.DW_TAG_try_block: | |
break; | |
case DwarfConstants.DW_TAG_variable: | |
processVariable(offset, attributeList, false); | |
break; | |
case DwarfConstants.DW_TAG_volatile_type: | |
processVolatileType(offset, attributeList, entry.hasChildren); | |
break; | |
case DwarfConstants.DW_TAG_subrange_type: | |
processArrayBoundType(offset, attributeList, entry.hasChildren); | |
break; | |
} | |
} | |
/** | |
* Tell whether a tag has or may have content with an address range | |
* | |
* Note: tag DW_TAG_compile_unit was parsed in the initial parse | |
* | |
* @param tag | |
* @return | |
*/ | |
private boolean isDebugInfoEntryWithAddressRange(short tag) { | |
switch (tag) { | |
// tags allowed to have both DW_AT_low_pc and DW_AT_high_pc or DW_at_ranges | |
case DwarfConstants.DW_TAG_catch_block: | |
case DwarfConstants.DW_TAG_inlined_subroutine: | |
case DwarfConstants.DW_TAG_lexical_block: | |
case DwarfConstants.DW_TAG_module: | |
case DwarfConstants.DW_TAG_partial_unit: | |
case DwarfConstants.DW_TAG_subprogram: | |
case DwarfConstants.DW_TAG_try_block: | |
case DwarfConstants.DW_TAG_with_stmt: | |
return true; | |
// TODO: take DW_TAG_variable out of here? | |
case DwarfConstants.DW_TAG_variable: | |
case DwarfConstants.DW_TAG_formal_parameter: | |
return true; | |
} | |
return false; | |
} | |
static long readAddress(IStreamBuffer in, int addressSize) throws IOException { | |
long value = 0; | |
switch (addressSize) { | |
case 2: | |
value = in.getShort(); | |
break; | |
case 4: | |
value = in.getInt(); | |
break; | |
case 8: | |
value = in.getLong(); | |
break; | |
default: | |
// ???? | |
} | |
return value; | |
} | |
/* unsigned */ | |
static long read_unsigned_leb128(IStreamBuffer in) throws IOException { | |
/* unsigned */ | |
long result = 0; | |
int shift = 0; | |
byte b; | |
while (true) { | |
if (!in.hasRemaining()) | |
break; // throw new IOException("no more data"); | |
b = in.get(); | |
result |= ((long) (b & 0x7f) << shift); | |
if ((b & 0x80) == 0) { | |
break; | |
} | |
shift += 7; | |
} | |
return result; | |
} | |
/* signed */ | |
public static long read_signed_leb128(IStreamBuffer in) throws IOException { | |
/* unsigned */ | |
long result = 0; | |
int shift = 0; | |
int size = 32; | |
byte b; | |
while (true) { | |
if (!in.hasRemaining()) | |
throw new IOException(CCorePlugin.getResourceString("Util.exception.noData")); //$NON-NLS-1$ | |
b = in.get(); | |
result |= ((long) (b & 0x7f) << shift); | |
shift += 7; | |
if ((b & 0x80) == 0) { | |
break; | |
} | |
} | |
if ((shift < size) && (b & 0x40) != 0) { | |
result |= -(1 << shift); | |
} | |
return result; | |
} | |
/** | |
* Read a null-ended string from the given "data" stream. data : IN, byte | |
* buffer | |
*/ | |
public static String readString(IStreamBuffer data) { | |
String str; | |
StringBuilder sb = new StringBuilder(); | |
while (data.hasRemaining()) { | |
byte c = data.get(); | |
if (c == 0) { | |
break; | |
} | |
sb.append((char) c); | |
} | |
str = sb.toString(); | |
return str; | |
} | |
private Collection<LocationEntry> getLocationRecord(long offset) { | |
// first check the cache | |
Collection<LocationEntry> entries = locationEntriesByOffset.get(offset); | |
if (entries == null) { | |
// not found so try to get the entries from the offset | |
// note: some compilers generate MULTIPLE ENTRIES for the same location, | |
// and the last one tends to be more correct... use a map here when reading | |
TreeMap<IRangeList.Entry, LocationEntry> entryMap = new TreeMap<IRangeList.Entry, LocationEntry>(); | |
try { | |
IStreamBuffer data = getDwarfSection(DWARF_DEBUG_LOC); | |
if (data != null) { | |
data.position(offset); | |
boolean first = true; | |
long base = 0; | |
while (data.hasRemaining()) { | |
long lowPC = readAddress(data, currentCUHeader.addressSize); | |
long highPC = readAddress(data, currentCUHeader.addressSize); | |
if (lowPC == 0 && highPC == 0) { | |
// end of list entry | |
break; | |
} else if (first) { | |
first = false; | |
long maxaddress = currentCUHeader.addressSize == 4 ? Integer.MAX_VALUE : Long.MAX_VALUE; | |
if (lowPC == maxaddress) { | |
// base address selection entry | |
base = highPC; | |
continue; | |
} else if (currentCompileUnitScope.getRangeList() == null) { | |
// if the compilation unit has a contiguous range, no implicit base is needed | |
base = currentCompileUnitScope.getLowAddress().getValue().longValue(); | |
} | |
} | |
// location list entry | |
int numOpCodes = data.getShort(); | |
byte[] bytes = new byte[numOpCodes]; | |
data.get(bytes); | |
LocationEntry entry = new LocationEntry(lowPC + base, highPC + base, bytes); | |
entryMap.put(new IRangeList.Entry(lowPC + base, highPC + base), entry); | |
} | |
entries = entryMap.values(); | |
locationEntriesByOffset.put(offset, entries); | |
} | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(null, t); | |
} | |
} | |
return entries; | |
} | |
private Map<Long, AbbreviationEntry> parseDebugAbbreviation(int abbreviationOffset) throws IOException { | |
Integer key = Integer.valueOf(abbreviationOffset); | |
Map<Long, AbbreviationEntry> abbrevs = provider.abbreviationMaps.get(key); | |
if (abbrevs == null) { | |
abbrevs = new HashMap<Long, AbbreviationEntry>(); | |
provider.abbreviationMaps.put(key, abbrevs); | |
IStreamBuffer data = getDwarfSection(DWARF_DEBUG_ABBREV); | |
if (data != null) { | |
data.position(abbreviationOffset); | |
while (data.remaining() > 0) { | |
long code = read_unsigned_leb128(data); | |
if (code == 0) { | |
break; | |
} | |
short tag = (short) read_unsigned_leb128(data); | |
boolean hasChildren = data.get() == DwarfConstants.DW_CHILDREN_yes; | |
AbbreviationEntry entry = new AbbreviationEntry(code, tag, hasChildren); | |
// attributes | |
short name = 0; | |
byte form = 0; | |
do { | |
name = (short) read_unsigned_leb128(data); | |
form = (byte) read_unsigned_leb128(data); | |
if (name != 0) { | |
entry.attributes.add(new Attribute(name, form)); | |
} | |
} while (name != 0 && form != 0); | |
entry.attributes.trimToSize(); | |
abbrevs.put(Long.valueOf(code), entry); | |
} | |
} | |
} | |
return abbrevs; | |
} | |
private void registerType(long offset, IType type, boolean hasChildren) { | |
provider.typesByOffset.put(offset, type); | |
provider.referenceTypesByOffset.remove(offset); | |
typeToParentMap.put(type, currentParentType); | |
if (hasChildren) | |
currentParentType = type; | |
if (DEBUG) { | |
if (type != null) { | |
System.out.print(DwarfMessages.DwarfInfoReader_ReadType + type.getName()); | |
while (type.getType() != null) { | |
type = type.getType(); | |
System.out.print(" " + type.getName()); //$NON-NLS-1$ | |
} | |
System.out.println(); | |
} | |
} | |
} | |
private void registerScope(long offset, Scope scope) { | |
provider.scopesByOffset.put(offset, scope); | |
} | |
/** | |
* Read a range list referenced from a code scope. | |
* @param offset | |
* @param base the specified DW_AT_low_pc value (or 0) | |
* @return a new RangeList | |
*/ | |
public RangeList readRangeList(int offset, AttributeValue baseValue) { | |
synchronized (provider) { | |
IStreamBuffer data = getDwarfSection(DWARF_DEBUG_RANGES); | |
if (data == null) { | |
return null; | |
} | |
try { | |
data.position(offset); | |
/* | |
* Read range list entry: | |
* | |
* start: DW_FORM_addr | |
* end: DW_FORM_addr | |
* | |
* When start == all ones, it is a base address selection entry, | |
* and end is the base address. The base address does not need to | |
* be specified, and is the compialtion unit's base address by default. | |
* | |
* When start == end == 0, this is the end of the list. | |
*/ | |
RangeList list = new RangeList(); | |
//Whether the list actually got anything added - otherwise we should return null | |
boolean listAddedTo = false; | |
long base = 0; | |
long start = data.getInt(); | |
long end = data.getInt(); | |
if (start == -1) { | |
base = end; | |
start = data.getInt(); | |
end = data.getInt(); | |
} else if (baseValue != null) { | |
base = baseValue.getValueAsLong(); | |
} else if (currentCompileUnitScope != null && currentCompileUnitScope.getRangeList() == null) { | |
base = currentCompileUnitScope.getLowAddress().getValue().longValue(); | |
} | |
do { | |
if (start == 0 && end == 0) { | |
break; | |
} else if (start != end) { | |
// ignore bogus entries: GCC-E sometimes generates these buggily (for artifical non-inlined functions) | |
if (base + start >= codeRanges.getLowAddress()) { | |
list.addRange(base + start, base + end); | |
listAddedTo = true; | |
} | |
} | |
start = data.getInt(); | |
end = data.getInt(); | |
} while (true); | |
return (listAddedTo) ? list : null; | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_RangeReadFailed, t); | |
return null; | |
} | |
} | |
} | |
/** | |
* Set up the address range for a scope by using its DW_AT_low_pc/DW_AT_high_pc | |
* or DW_AT_ranges attributes, or DW_AT_stmt_list in a pinch | |
* @param attributeList | |
* @param scope | |
*/ | |
private void setupAddresses(AttributeList attributeList, Scope scope) { | |
// get the high and low pc from the attributes list | |
AttributeValue value | |
= attributeList.getAttribute(DwarfConstants.DW_AT_high_pc); | |
if (value != null) { | |
IAddress low = new Addr32(attributeList.getAttributeValueAsLong(DwarfConstants.DW_AT_low_pc)); | |
scope.setLowAddress(low); | |
IAddress high = new Addr32(attributeList.getAttributeValueAsLong(DwarfConstants.DW_AT_high_pc)); | |
IScope parent = scope.getParent(); | |
if (low.compareTo(high) <= 0) { | |
if (parent instanceof DwarfFunctionScope) { | |
IAddress parentHigh = parent.getHighAddress(); | |
if (parentHigh != null && high.compareTo(parentHigh) > 0) { | |
((Scope)parent).setHighAddress(high); | |
} | |
} | |
} else { | |
// relying on the following to confirm that this is an RVCT inline DWARF generation bug | |
if (scope instanceof DwarfFunctionScope && parent instanceof DwarfFunctionScope) { | |
high = fix_Dwarf_InlineHighAddress_Problem(scope); | |
if (high == null) | |
// at least prevent ecl.bz Bug 329324 from happening | |
high = parent.getHighAddress(); | |
// wow, RVCT, you're neat... I think you mean, point to the high PC of the parent | |
} else if (parent != null && parent.getHighAddress() != null) { | |
high = parent.getHighAddress(); | |
// may still be bogus, check again next | |
} | |
if (low.compareTo(high) > 0) { | |
scope.setLowAddress(high); | |
high = low; | |
} | |
} | |
scope.setHighAddress(high); | |
return; | |
} | |
// look for a range | |
value = attributeList.getAttribute(DwarfConstants.DW_AT_ranges); | |
if (value != null) { | |
AttributeValue baseValue = attributeList.getAttribute(DwarfConstants.DW_AT_low_pc); | |
RangeList ranges = readRangeList(value.getValueAsInt(), baseValue); | |
if (ranges != null) { | |
scope.setRangeList(ranges); | |
// if the range list high and low pc extend outside the parent's | |
// high/low range, adjust the parent (found in GCC-E) | |
if (ranges.getLowAddress() < scope.getParent().getLowAddress().getValue().longValue()) { | |
if (scope.getParent() instanceof Scope) | |
((Scope)scope.getParent()).setLowAddress(new Addr32(ranges.getLowAddress())); | |
} | |
if (ranges.getHighAddress() > scope.getParent().getHighAddress().getValue().longValue()) { | |
if (scope.getParent() instanceof Scope) | |
((Scope)scope.getParent()).setHighAddress(new Addr32(ranges.getHighAddress())); | |
} | |
return; | |
} | |
} | |
// in a CU, GCC-E may have only generated this, so we need to dig into the line table | |
if (scope instanceof ICompileUnitScope) { | |
value = attributeList.getAttribute(DwarfConstants.DW_AT_stmt_list); | |
if (value != null) { | |
RangeList ranges = new RangeList(); | |
for (ILineEntry entry : ((ICompileUnitScope) scope).getLineEntries()) { | |
// ignore (for now) entries that seem far out of range | |
if (entry.getLowAddress().getValue().longValue() >= codeRanges.getLowAddress()) { | |
ranges.addRange(entry.getLowAddress().getValue().longValue(), | |
entry.getHighAddress().getValue().longValue()); | |
} | |
} | |
scope.setRangeList(ranges); | |
scope.fixupRanges(provider.getBaseLinkAddress()); | |
return; | |
} | |
} | |
// no code, apparently | |
scope.setLowAddress(new Addr32(0)); | |
scope.setHighAddress(new Addr32(0)); | |
} | |
/** | |
* for cases where the compiler generates an incorrect high-address, | |
* the line entry provider can give information about the current and | |
* subsequent lines within an inline. | |
* <br> | |
* caveats: this is not meant to handle nested broken inlines. the | |
* algorithm assumes | |
* - an outer inline nesting another inline will use set of ranges, not a high-low | |
* - an inline function will likely be in another file | |
* - if not, it will likely be prior to the function that inlines it | |
* - and if not, it will likely be more than 32 lines after the function that inlines it | |
* @param scope | |
* @return high address if determined, or null if prerequisites for finding it aren't met. | |
*/ | |
private IAddress fix_Dwarf_InlineHighAddress_Problem(Scope scope) { | |
IAddress low = scope.getLowAddress(); | |
IAddress highest = scope.getParent().getHighAddress(); | |
Iterator<ILineEntry> lineEntries | |
= currentCompileUnitScope.getLineEntries().iterator(); | |
ILineEntry entry = null; | |
do { | |
if (lineEntries.hasNext()) | |
entry = lineEntries.next(); | |
else | |
return null; | |
if (entry == null) | |
return null; | |
} while (low.compareTo(entry.getHighAddress()) > 0); | |
IAddress high = null; | |
IPath actualPath = entry.getFilePath(), otherPath = null; | |
int actualLine = entry.getLineNumber(); | |
int thisLine = actualLine, lastLine = 0; // XXX false positive on uninitialized variable below causes needless initialization of lastLine = 0 | |
boolean jumpedBack = false, jumpedAway = false; | |
OUTER:do { | |
IAddress nextHigh = entry.getHighAddress(); | |
if (highest != null && nextHigh != null && highest.compareTo(nextHigh) < 0) { | |
nextHigh = entry.getLowAddress(); | |
if (high == null || nextHigh.compareTo(high) < 0) | |
high = nextHigh; | |
break; | |
} | |
high = nextHigh; | |
if (!jumpedAway && otherPath == null) | |
lastLine = thisLine; | |
if (high == null) | |
break OUTER; | |
do { | |
if (lineEntries.hasNext()) | |
entry = lineEntries.next(); | |
else | |
return null; | |
if (entry == null) | |
break OUTER; | |
} while (high.equals(entry.getHighAddress())); | |
if (otherPath != null) { | |
if (entry.getFilePath().equals(actualPath)) // done with nesting | |
break; | |
} else if (!entry.getFilePath().equals(actualPath)) {// easiest test for done with inline | |
otherPath = entry.getFilePath(); | |
} else { | |
thisLine = entry.getLineNumber(); | |
if (!jumpedBack && !jumpedAway) { | |
if (thisLine < actualLine) { | |
jumpedBack = true; | |
} else if (thisLine > lastLine + 24) { // XXX false positive here causes needless init of lastLine = 0 above | |
jumpedAway = true; | |
} | |
} else if (jumpedBack) { | |
if (thisLine > actualLine) // jumped back ahead; done | |
break; | |
} else if (jumpedAway) { | |
if (thisLine < actualLine | |
|| thisLine < lastLine | |
|| thisLine > lastLine + 24) | |
break; | |
} | |
} | |
} while (entry != null); | |
return high; | |
} | |
/** | |
* Process a compile unit | |
* | |
* @param attributeList | |
* @param childrenPosition | |
*/ | |
private void processCompileUnit(CompilationUnitHeader header, boolean hasChildren, AttributeList attributeList) { | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
String compDir = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_comp_dir); | |
//System.out.println("processing compile unit: " + Integer.toHexString(header.debugInfoOffset) + ": " + name); | |
IPath filePath = fileHelper.normalizeFilePath(compDir, name); | |
currentCompileUnitScope = new DwarfCompileUnit(provider, moduleScope, filePath, null, null, header, hasChildren, attributeList); | |
header.scope = currentCompileUnitScope; | |
currentParentScope = currentCompileUnitScope; | |
// Needs to be registered before setupAddresses. | |
provider.registerCompileUnitHeader(currentCUHeader.debugInfoOffset, currentCUHeader); | |
setupAddresses(attributeList, currentCompileUnitScope); | |
// some compilers (RVCT) may generate multiple compile units for the | |
// same file. | |
List<ICompileUnitScope> matchingCompileUnits = provider.compileUnitsPerFile.get(filePath); | |
if (matchingCompileUnits == null) { | |
// first one. create it now. | |
matchingCompileUnits = new ArrayList<ICompileUnitScope>(); | |
} | |
matchingCompileUnits.add(currentCompileUnitScope); | |
provider.compileUnitsPerFile.put(filePath, matchingCompileUnits); | |
provider.compileUnits.add(currentCompileUnitScope); | |
if (!currentCompileUnitScope.getHighAddress().isZero()) // has code | |
provider.sortedCompileUnitsWithCode.add(currentCompileUnitScope); | |
moduleScope.addChild(currentCompileUnitScope); | |
if (provider.buildReferencedFilesList) { | |
provider.referencedFiles.add(filePath.toOSString()); | |
// do a quick parse of the line table to get any other referenced files. | |
// note that even the full parse doesn't parse the line table information. | |
// that is calculated (and then cached) on demand | |
AttributeValue a = attributeList.getAttribute(DwarfConstants.DW_AT_stmt_list); | |
if (a != null) { | |
int stmtList = a.getValueAsInt(); | |
quickParseLineInfo(stmtList, compDir); | |
} | |
} | |
// remove unused attributes | |
attributeList.attributeMap.remove(DwarfConstants.DW_AT_name); | |
//attributeList.attributeMap.remove(DwarfConstants.DW_AT_comp_dir); // needed later | |
attributeList.attributeMap.remove(DwarfConstants.DW_AT_low_pc); | |
attributeList.attributeMap.remove(DwarfConstants.DW_AT_high_pc); | |
attributeList.attributeMap.remove(DwarfConstants.DW_AT_ranges); | |
} | |
private void processLexicalBlock(long offset, AttributeList attributeList, boolean hasChildren) { | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
if (!attributeList.hasCodeRangeAttributes()) { | |
// ignore any that don't have a valid range | |
return; | |
} | |
LexicalBlockScope lb = new LexicalBlockScope(name, currentParentScope, null, null); | |
setupAddresses(attributeList, lb); | |
currentParentScope.addChild(lb); | |
registerScope(offset, lb); | |
if (hasChildren) | |
currentParentScope = lb; | |
} | |
static class DereferencedAttributes { | |
public CompilationUnitHeader header; | |
public AttributeList attributeList; | |
public DereferencedAttributes(CompilationUnitHeader header, | |
AttributeList attributeList) { | |
this.header = header; | |
this.attributeList = attributeList; | |
} | |
} | |
/** | |
* DW_AT_abstract_origin and DW_AT_specification can refer to types in other | |
* compilation units. This will dynamically parse that CU if needed in order | |
* to get the attributes for the type. | |
* | |
* @param debugInfoOffset | |
* @return AttributeList or <code>null</code> (should not happen) | |
*/ | |
private DereferencedAttributes getDereferencedAttributes(AttributeList attributeList, short tag) { | |
CompilationUnitHeader providingCU = currentCUHeader; | |
AttributeValue derefLocation = attributeList.getAttribute(tag); | |
if (derefLocation == null) | |
return null; | |
// get the offset into the .debug_info section | |
long debugInfoOffset = derefLocation.getValueAsLong(); | |
if (derefLocation.getActualForm() == DwarfConstants.DW_FORM_ref_addr) { | |
// this is already relative to the .debug_info section | |
} else { | |
// relative to the CU | |
debugInfoOffset += providingCU.debugInfoOffset; | |
} | |
AttributeList attributes = provider.functionsByOffset.get(debugInfoOffset); | |
if (attributes == null) { | |
// dereferenced function does not exist yet | |
providingCU = provider.fetchCompileUnitHeader(debugInfoOffset); | |
attributes = provider.functionsByOffset.get(debugInfoOffset); | |
if (attributes == null) { | |
// dereferenced entry is not parsed yet, perhaps because it's | |
// later in the current compile unit (despite Dwarf 3 spec saying | |
// that's not allowed) | |
IStreamBuffer buffer = getDwarfSection(DWARF_DEBUG_INFO); | |
if (buffer != null) { | |
buffer.position(debugInfoOffset); | |
try { | |
// get stored abbrev table, or read and parse an abbrev table | |
Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(providingCU.abbreviationOffset); | |
long code = read_unsigned_leb128(buffer); | |
if (code != 0) { | |
AbbreviationEntry entry = abbrevs.get(new Long(code)); | |
if (entry != null) { | |
attributes = new AttributeList(entry, buffer, providingCU.addressSize, getDebugStrings()); | |
} | |
} | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed1 | |
+ debugInfoSection.getName() + DwarfMessages.DwarfInfoReader_ParseDebugInfoSectionFailed2 + symbolFilePath, t); | |
} | |
} | |
} | |
} else { | |
providingCU = provider.fetchCompileUnitHeader(debugInfoOffset); | |
if (providingCU == null) { | |
assert(false); | |
return null; | |
} | |
} | |
if (attributes == null) | |
return null; | |
return new DereferencedAttributes(providingCU, attributes); | |
} | |
private void processSubprogram(long offset, AttributeList attributeList, boolean hasChildren) { | |
// if it's a declaration just add to the offsets map for later lookup | |
if (attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_declaration) > 0) { | |
provider.functionsByOffset.put(offset, attributeList); | |
return; | |
} | |
// functions with no high/low pc aren't real functions. just treat them | |
// as declarations as they will be pointed to by abstract_origin from | |
// another subprogram tag | |
if (!attributeList.hasCodeRangeAttributes()) { | |
provider.functionsByOffset.put(offset, attributeList); | |
return; | |
} | |
CompilationUnitHeader otherCU = null; | |
boolean isArtificial = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_artificial) > 0; | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
if (name.length() == 0) { | |
// no name. see if we can get it from a declaration | |
DereferencedAttributes deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_abstract_origin); | |
if (deref != null) { | |
// this should either have a name or point to another | |
// declaration | |
otherCU = deref.header; | |
AttributeList attributes = deref.attributeList; | |
name = attributes.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
isArtificial |= attributes.getAttributeValueAsInt(DwarfConstants. DW_AT_artificial) > 0; | |
if (name.length() == 0) { | |
deref = getDereferencedAttributes(attributes, DwarfConstants.DW_AT_specification); | |
if (deref != null) { | |
// this should either have a name or point to another | |
// declaration | |
otherCU = deref.header; | |
attributes = deref.attributeList; | |
name = attributes.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
isArtificial |= attributes.getAttributeValueAsInt(DwarfConstants. DW_AT_artificial) > 0; | |
} | |
} | |
} | |
} | |
if (name.length() == 0) { | |
DereferencedAttributes deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_specification); | |
if (deref != null) { | |
// this should either have a name or point to another | |
// declaration | |
otherCU = deref.header; | |
AttributeList attributes = deref.attributeList; | |
name = attributes.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
} | |
} | |
if (name.length() == 0) { | |
// the name should either be an attribute of the compile unit, or be | |
// in the declaration which according to the spec will always be | |
// before its definition in the Dwarf. | |
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_SubprogramNameNotFound1 + Long.toHexString(offset) + | |
DwarfMessages.DwarfInfoReader_SubprogramNameNotFound2, null); | |
return; | |
} | |
DwarfFunctionScope function = new DwarfFunctionScope(name, currentCompileUnitScope, null, null, null); | |
setupAddresses(attributeList, function); | |
Scope originalParentScope = currentParentScope; | |
registerScope(offset, function); | |
currentParentScope = function; // needed for getLocationProvider(), etc. | |
AttributeValue frameBaseAttribute = attributeList.getAttribute(DwarfConstants.DW_AT_frame_base); | |
ILocationProvider locationProvider = getLocationProvider(frameBaseAttribute); | |
function.setLocationProvider(locationProvider); | |
// Note: we may still have cases where DW_AT_low_pc and/or DW_AT_high_pc are 0x0 | |
// (some "ignored inlined" functions in GCC). We want to keep track of their scope | |
// (though not store them in the CU), because child tag parses expect to find a parent into | |
// which to write their formal parameters and locals. | |
if (!function.getLowAddress().isZero() && !function.getHighAddress().isZero() && !isArtificial) { | |
// find the declaration location | |
int declLine = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_line); | |
int declColumn = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_column); | |
int declFileNum = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_file); | |
if (otherCU != null) | |
function.setDeclFile(otherCU.scope.getFileEntry(declFileNum)); | |
else | |
function.setDeclFileNum(declFileNum); | |
function.setDeclLine(declLine); | |
function.setDeclColumn(declColumn); | |
currentCompileUnitScope.addChild(function); | |
// keep track of all functions by name for faster lookup | |
List<IFunctionScope> functions = provider.functionsByName.get(name); | |
if (functions == null) { | |
functions = new ArrayList<IFunctionScope>(); | |
provider.functionsByName.put(name, functions); | |
} | |
functions.add(function); | |
} | |
// if the entry has no children, then restore the original parent scope | |
if (!hasChildren) | |
currentParentScope = originalParentScope; | |
} | |
/** | |
* Get the already-parsed or forward reference to a type from a DW_AT_type attribute, if present | |
* @param attributeMap the map of Long, AttributeValue from AttributeList or Object, Object from Type | |
* @return offset to referenced type or 0 if no type attribute | |
*/ | |
private IType getTypeOrReference(AttributeList attributeList, CompilationUnitHeader header) { | |
AttributeValue typeAttribute = attributeList.getAttribute(DwarfConstants.DW_AT_type); | |
if (typeAttribute == null) | |
return null; | |
return getTypeOrReference(typeAttribute, header); | |
} | |
/** | |
* Get the already-parsed or forward reference to a type from a DW_AT_type attribute, if present | |
* @param attributeMap the map of Long, AttributeValue from AttributeList or Object, Object from Type | |
* @return offset to referenced type or 0 if no type attribute | |
*/ | |
private IType getTypeOrReference(AttributeValue typeAttribute, CompilationUnitHeader header) { | |
if (typeAttribute == null) | |
return null; | |
// get the offset into the .debug_info section | |
long debugInfoOffset = typeAttribute.getValueAsLong(); | |
if (typeAttribute.getActualForm() == DwarfConstants.DW_FORM_ref_addr) { | |
// this is already relative to the .debug_info section | |
} else { | |
debugInfoOffset += header.debugInfoOffset; | |
} | |
IType type = provider.typesByOffset.get(debugInfoOffset); | |
if (type == null) { | |
// So that we only create one ForwardTypeReference per type we want to reference. | |
IForwardTypeReference refType = provider.referenceTypesByOffset.get(debugInfoOffset); | |
if (type == null) { | |
refType = new ForwardTypeReference(provider, debugInfoOffset); | |
provider.referenceTypesByOffset.put(Long.valueOf(debugInfoOffset), refType); | |
} | |
type = refType; | |
} | |
return type; | |
} | |
private void processInlinedSubroutine(long offset, AttributeList attributeList, boolean hasChildren) { | |
// functions with no high/low pc aren't real (probably an error) | |
if (!attributeList.hasCodeRangeAttributes()) { | |
return; | |
} | |
DereferencedAttributes deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_abstract_origin); | |
if (deref == null) { | |
if (attributeList.getAttribute(DwarfConstants.DW_AT_abstract_origin) != null) { | |
// TODO: GCC-E can reference forward tags (!) so we need to handle these another way | |
} else { | |
assert(false); | |
} | |
return; | |
} | |
CompilationUnitHeader otherCU = deref.header; | |
AttributeList origAttributes = deref.attributeList; | |
// this should either have a name or point to another | |
// declaration | |
String name = origAttributes.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
if (name.length() == 0) { | |
deref = getDereferencedAttributes(origAttributes, DwarfConstants.DW_AT_specification); | |
if (deref != null) { | |
// this should either have a name or point to another | |
// declaration | |
//otherCU = deref.header; | |
AttributeList declarationAttributes = deref.attributeList; | |
name = declarationAttributes.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
} | |
} | |
if (name.length() == 0) { | |
// the name should either be an attribute of the compile unit, or be | |
// in the declaration which according to the spec will always be | |
// before its definition in the Dwarf. | |
return; | |
} | |
// find the declaration location | |
int declLine = origAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_line); | |
int declColumn = origAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_column); | |
int declFileNum = origAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_file); | |
if (declFileNum == 0) { | |
assert(false); | |
return; | |
} | |
DwarfFunctionScope function = new DwarfFunctionScope(name, currentParentScope, null, null, null); | |
setupAddresses(attributeList, function); | |
function.setDeclFile(otherCU.scope.getFileEntry(declFileNum)); | |
function.setDeclLine(declLine); | |
function.setDeclColumn(declColumn); | |
currentParentScope.addChild(function); | |
registerScope(offset, function); | |
if (hasChildren) | |
currentParentScope = function; | |
// keep track of all functions by name for faster lookup | |
List<IFunctionScope> functions = provider.functionsByName.get(name); | |
if (functions == null) { | |
functions = new ArrayList<IFunctionScope>(); | |
provider.functionsByName.put(name, functions); | |
} | |
functions.add(function); | |
} | |
private void processSubroutineType(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
SubroutineType type = new SubroutineType(currentParentScope, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
registerType(offset, type, hasChildren); | |
// TODO: associate parameters with this type in child tag parse | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processClassType(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
// if the name is mangled, unmangle it | |
name = unmangleType(name); | |
// GCC may put the template parameter of an inherited class at the end of the name, | |
// so strip that off | |
// TODO: support template parameters at end of name or as separate DW_TAG_template_value_param | |
if (name.endsWith(">")) { | |
int templateStart = name.indexOf("<"); | |
if (templateStart != -1) | |
name = name.substring(0, templateStart); | |
} | |
int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size); | |
ClassType type = new ClassType(name, currentParentScope, byteSize, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
registerType(offset, type, hasChildren); | |
storeTypeByName(name, type); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processStructType(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size); | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
// if the name is mangled, unmangle it | |
name = unmangleType(name); | |
StructType type = new StructType(name, currentParentScope, byteSize, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
registerType(offset, type, hasChildren); | |
storeTypeByName(name, type); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processUnionType(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size); | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
// if the name is mangled, unmangle it | |
name = unmangleType(name); | |
UnionType type = new UnionType(name, currentParentScope, byteSize, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
registerType(offset, type, hasChildren); | |
storeTypeByName(name, type); | |
/* | |
* For an anonymous union, which has members accessible by methods in a class, ARM RVCT | |
* does not create an unnamed class member so you know the offset of the union's members. | |
* Instead, it just gives the anonymous union's type a name of the form "__C" followed | |
* by a number. And it places the union's DWARF info after all class member and inherited | |
* member DWARF info. | |
* | |
* E.g., when you have 2 named members and 2 anonymous unions in this order: | |
* 4-byte union <anonymous 1> | |
* 4-byte long long1 | |
* 4-byte union <anonymous 2> | |
* 4-byte long long2 | |
* ARM RVCT DWARF info says the class has 2 members: | |
* long1 at offset 4 | |
* long2 at offset 12 | |
* ARM RVCT DWARF info is in the order: | |
* member long1 | |
* member long2 | |
* type union <anonymous 1> | |
* type union <anonymous 2> | |
* So the rules for handling anonymous unions in RVCT DWARF are: | |
* 1st read offsets and sizes of non-anonymous members, which leaves offset holes | |
* for anonymous unions | |
* 2nd read anonymous union type info, which have compiler-generated names of | |
* "__C" following by a number, and assign unnamed members to offset holes | |
*/ | |
boolean isRVCTAnonymousUnion = false; | |
try { | |
isRVCTAnonymousUnion = name.startsWith("__C") && (name.length() > 3) && //$NON-NLS-1$ | |
(name.charAt(3) != '-') && (Long.parseLong(name.substring(3)) > -1); | |
} catch (NumberFormatException nfe) {} | |
// if needed, create an "unnamed" member field with an offset to be determined later | |
if (isRVCTAnonymousUnion && getCompositeParent(typeToParentMap.get(currentParentType)) != null) { | |
ICompositeType compositeType = getCompositeParent(typeToParentMap.get(currentParentType)); | |
// unnamed member accessibility depends on the enclosing composite's type - | |
// public for a struct or union, private for a class | |
int accessibility = ICompositeType.ACCESS_PUBLIC; | |
if (compositeType instanceof ClassType) | |
accessibility = ICompositeType.ACCESS_PRIVATE; | |
// empty field names confuse the expressions service | |
String fieldName = "$unnamed$" + (compositeType.fieldCount() + 1); //$NON-NLS-1$ | |
// since we're generating a field, give it a -1 offset. We cannot tell the real | |
// offset until we determine offsets of all previously defined members - which | |
// may include inherited types not yet read in Dwarf info | |
FieldType fieldType = new FieldType(fieldName, currentParentScope, compositeType, -1, 0, 0, | |
byteSize, accessibility, null); | |
fieldType.setType(type); | |
// add the member to the deepest nested (last added) compositeNesting | |
// member | |
compositeType.addField(fieldType); | |
registerType(offset, fieldType, false); | |
} | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processInheritance(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
ICompositeType compositeType = getCompositeParent(); | |
// The allowed attributes are DW_AT_type, DW_AT_data_member_location, | |
// and DW_AT_accessibility | |
long fieldsOffset = 0; | |
byte[] offsetBlock = attributeList.getAttributeValueAsBytes(DwarfConstants.DW_AT_data_member_location); | |
// unsigned LEB128 encoding | |
if (offsetBlock.length > 0 && offsetBlock[0] == DwarfConstants.DW_OP_plus_uconst) { | |
for (int i = 1, shift = 0; i < offsetBlock.length; i++) { | |
fieldsOffset += (offsetBlock[i] & 0x7f) << shift; | |
shift += 7; | |
} | |
} | |
// default accessibility is private | |
int accessibility = ICompositeType.ACCESS_PRIVATE; | |
if (attributeList.getAttribute(DwarfConstants.DW_AT_accessibility) != null) { | |
accessibility = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_accessibility); | |
if (accessibility == DwarfConstants.DW_ACCESS_public) | |
accessibility = ICompositeType.ACCESS_PUBLIC; | |
else if (accessibility == DwarfConstants.DW_ACCESS_private) | |
accessibility = ICompositeType.ACCESS_PRIVATE; | |
else | |
accessibility = ICompositeType.ACCESS_PROTECTED; | |
} | |
InheritanceType type = new InheritanceType(currentParentScope, accessibility, fieldsOffset, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
// add the member to the deepest nested (last added) compositeNesting | |
// member | |
if (compositeType != null) | |
compositeType.addInheritance(type); | |
registerType(offset, type, hasChildren); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processField(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
// GCC-E has fields like "_vptr.BaseClass" which will be a problem for us | |
// (since '.' is an operator); rename these here | |
name = name.replace('.', '$'); | |
int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size); | |
int bitSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_bit_size); | |
int bitOffset = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_bit_offset); | |
long fieldOffset = 0; | |
byte[] offsetBlock = attributeList.getAttributeValueAsBytes(DwarfConstants.DW_AT_data_member_location); | |
// unsigned LEB128 encoding | |
if (offsetBlock.length > 0 && offsetBlock[0] == DwarfConstants.DW_OP_plus_uconst) { | |
for (int i = 1, shift = 0; i < offsetBlock.length; i++) { | |
fieldOffset += (offsetBlock[i] & 0x7f) << shift; | |
shift += 7; | |
} | |
} | |
ICompositeType compositeType = getCompositeParent(); | |
// default accessibility depends on the composite type - | |
// public for a struct or union, private for a class | |
int accessibility = ICompositeType.ACCESS_PUBLIC; | |
if (attributeList.getAttribute(DwarfConstants.DW_AT_accessibility) != null) { | |
accessibility = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_accessibility); | |
if (accessibility == DwarfConstants.DW_ACCESS_public) | |
accessibility = ICompositeType.ACCESS_PUBLIC; | |
else if (accessibility == DwarfConstants.DW_ACCESS_private) | |
accessibility = ICompositeType.ACCESS_PRIVATE; | |
else | |
accessibility = ICompositeType.ACCESS_PROTECTED; | |
} else if (compositeType != null && compositeType instanceof ClassType) | |
accessibility = ICompositeType.ACCESS_PRIVATE; | |
// Empty fields confuse the expressions service (#10369) | |
if (name.length() == 0) { | |
if (compositeType != null) { | |
name = "$unnamed$" + (compositeType.fieldCount() + 1); //$NON-NLS-1$ | |
} else { | |
name = "$unnamed$"; //$NON-NLS-1$ | |
} | |
} | |
FieldType type = new FieldType(name, currentParentScope, compositeType, fieldOffset, bitSize, bitOffset, | |
byteSize, accessibility, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
// add the member to the deepest nested (last added) compositeNesting | |
// member | |
if (compositeType != null) | |
compositeType.addField(type); | |
registerType(offset, type, hasChildren); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processTemplateTypeParam(long offset, AttributeList attributeList, CompilationUnitHeader header, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
IType paramType = getTypeOrReference(attributeList.getAttribute(DwarfConstants.DW_AT_type), currentCUHeader); | |
TemplateParamType type = new TemplateParamType(name, paramType); | |
ICompositeType compositeType = getCompositeParent(); | |
// add the template param to the deepest nested (last added) compositeNesting | |
// member | |
if (compositeType != null) | |
compositeType.addTemplateParam(type); | |
registerType(offset, type, hasChildren); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private ICompositeType getCompositeParent() { | |
return getCompositeParent(currentParentType); | |
} | |
private ICompositeType getCompositeParent(IType parent) { | |
while (parent != null) { | |
if (parent instanceof ICompositeType) | |
return ((ICompositeType) parent); | |
parent = typeToParentMap.get(parent); | |
} | |
return null; | |
} | |
private void processArrayType(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size); | |
ArrayType type = new ArrayType(name, currentParentScope, byteSize, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
registerType(offset, type, hasChildren); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private IArrayType getArrayParent() { | |
IType parent = currentParentType; | |
while (parent != null) { | |
if (parent instanceof IArrayType) | |
return ((IArrayType) parent); | |
parent = typeToParentMap.get(parent); | |
} | |
return null; | |
} | |
private void processArrayBoundType(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
long arrayBound = 0; | |
if (attributeList.getAttribute(DwarfConstants.DW_AT_upper_bound) != null) | |
arrayBound = attributeList.getAttributeValueAsLong(DwarfConstants.DW_AT_upper_bound) + 1; | |
ArrayBoundType type = new ArrayBoundType(currentParentScope, arrayBound); | |
IArrayType array = getArrayParent(); | |
if (array == null) | |
throw new IllegalStateException(); | |
array.addBound(type); | |
registerType(offset, type, hasChildren); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processReferenceType(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size); | |
if (byteSize == 0) | |
byteSize = currentCUHeader.addressSize; | |
ReferenceType type = new ReferenceType(name, currentParentScope, byteSize, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
registerType(offset, type, hasChildren); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processPointerType(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size); | |
if (byteSize == 0) | |
byteSize = currentCUHeader.addressSize; | |
PointerType type = new PointerType(name, currentParentScope, byteSize, null); | |
type.setType(getTypeOrReferenceOrVoid(attributeList)); | |
registerType(offset, type, hasChildren); | |
storeTypeByName(name, type); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processPtrToMemberType(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
// unnamed data types don't get stored by name | |
if (name.length() == 0) | |
name = "" + offset; | |
PointerType type = new PointerType(name, currentParentScope, currentCUHeader.addressSize, null); | |
type.setType(getTypeOrReferenceOrVoid(attributeList)); | |
registerType(offset, type, hasChildren); | |
storeTypeByName(name, type); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processConstType(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
ConstType type = new ConstType(currentParentScope, null); | |
type.setType(getTypeOrReferenceOrVoid(attributeList)); | |
registerType(offset, type, hasChildren); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processVolatileType(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
VolatileType type = new VolatileType(currentParentScope, null); | |
type.setType(getTypeOrReferenceOrVoid(attributeList)); | |
registerType(offset, type, hasChildren); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
// for void pointers, GCC will produce qualifiers and pointers without types or references, | |
// so create a void to be qualified or pointed to | |
private IType getTypeOrReferenceOrVoid(AttributeList attributeList) { | |
IType typeOrReference = getTypeOrReference(attributeList, currentCUHeader); | |
if (typeOrReference != null) | |
return typeOrReference; | |
if (moduleScope != null && voidType == null) { | |
voidType = new CPPBasicType("void", moduleScope, IBasicType.t_void, 0, 0, null); | |
} | |
if (voidType != null) | |
return voidType; | |
return new CPPBasicType("void", currentParentScope, IBasicType.t_void, 0, 0, null); | |
} | |
private void processEnumType(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
// if the name is mangled, unmangle it | |
name = unmangleType(name); | |
int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size); | |
Enumeration type = new Enumeration(name, currentParentScope, byteSize, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
registerType(offset, type, hasChildren); | |
storeTypeByName(name, type); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private Enumeration getEnumerationParent() { | |
IType parent = currentParentType; | |
while (parent != null) { | |
if (parent instanceof Enumeration) | |
return ((Enumeration) parent); | |
parent = typeToParentMap.get(parent); | |
} | |
return null; | |
} | |
private void processEnumerator(long offset, AttributeList attributeList) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
if (unmangler.isMangled(name)) { | |
try { | |
name = unmangler.unmangle(name); | |
} catch (UnmanglingException ue) { | |
} | |
} | |
long value = attributeList.getAttributeValueAsSignedLong(DwarfConstants.DW_AT_const_value); | |
Enumerator enumerator = new Enumerator(name, value); | |
Enumeration enumeration = getEnumerationParent(); | |
if (enumeration == null) | |
throw new IllegalStateException(); | |
enumeration.addEnumerator(enumerator); | |
((Scope)enumeration.getScope()).addEnumerator(enumerator); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(enumerator)); } | |
} | |
private void processTypeDef(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
// if the name is mangled, unmangle it | |
name = unmangleType(name); | |
TypedefType type = new TypedefType(name, currentParentScope, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
registerType(offset, type, hasChildren); | |
storeTypeByName(name, type); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processBasicType(long offset, AttributeList attributeList, boolean hasChildren) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(offset)); } | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
int byteSize = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_byte_size); | |
int baseType = IBasicType.t_unspecified; | |
int qualifierBits = 0; | |
int encoding = attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_encoding); | |
switch (encoding) { | |
case DwarfConstants.DW_ATE_boolean: | |
baseType = ICPPBasicType.t_bool; | |
break; | |
case DwarfConstants.DW_ATE_float: | |
if (name.contains("float")) { //$NON-NLS-1$ | |
baseType = IBasicType.t_float; | |
} else if (name.contains("long double")) { //$NON-NLS-1$ | |
baseType = IBasicType.t_double; | |
qualifierBits |= ICPPBasicType.IS_LONG; | |
} else if (name.contains("double")) { //$NON-NLS-1$ | |
baseType = IBasicType.t_double; | |
} | |
break; | |
case DwarfConstants.DW_ATE_signed: | |
baseType = IBasicType.t_int; | |
qualifierBits |= ICPPBasicType.IS_SIGNED; | |
if (name.contains("short")) { //$NON-NLS-1$ | |
qualifierBits |= ICPPBasicType.IS_SHORT; | |
} else if (name.contains("long long")) { //$NON-NLS-1$ | |
qualifierBits |= ICPPBasicType.IS_LONG_LONG; | |
} else if (name.contains("long")) { //$NON-NLS-1$ | |
qualifierBits |= ICPPBasicType.IS_LONG; | |
} | |
break; | |
case DwarfConstants.DW_ATE_signed_char: | |
baseType = IBasicType.t_char; | |
qualifierBits |= ICPPBasicType.IS_SIGNED; | |
break; | |
case DwarfConstants.DW_ATE_unsigned: | |
baseType = IBasicType.t_int; | |
qualifierBits |= ICPPBasicType.IS_UNSIGNED; | |
if (name.contains("short")) { //$NON-NLS-1$ | |
qualifierBits |= ICPPBasicType.IS_SHORT; | |
} else if (name.contains("long long")) { //$NON-NLS-1$ | |
qualifierBits |= ICPPBasicType.IS_LONG_LONG; | |
} else if (name.contains("long")) { //$NON-NLS-1$ | |
qualifierBits |= ICPPBasicType.IS_LONG; | |
} | |
break; | |
case DwarfConstants.DW_ATE_unsigned_char: | |
baseType = IBasicType.t_char; | |
qualifierBits |= ICPPBasicType.IS_UNSIGNED; | |
break; | |
case DwarfConstants.DW_ATE_complex_float: | |
qualifierBits |= ICPPBasicType.IS_COMPLEX; | |
if (name.contains("float")) { //$NON-NLS-1$ | |
baseType = IBasicType.t_float; | |
} else if (name.contains("long double")) { //$NON-NLS-1$ | |
baseType = IBasicType.t_double; | |
qualifierBits |= ICPPBasicType.IS_LONG; | |
} else if (name.contains("double")) { //$NON-NLS-1$ | |
baseType = IBasicType.t_double; | |
} | |
break; | |
case DwarfConstants.DW_ATE_imaginary_float: | |
qualifierBits |= ICPPBasicType.IS_IMAGINARY; | |
if (name.contains("float")) { //$NON-NLS-1$ | |
baseType = IBasicType.t_float; | |
} else if (name.contains("long double")) { //$NON-NLS-1$ | |
baseType = IBasicType.t_double; | |
qualifierBits |= ICPPBasicType.IS_LONG; | |
} else if (name.contains("double")) { //$NON-NLS-1$ | |
baseType = IBasicType.t_double; | |
} | |
break; | |
case DwarfConstants.DW_ATE_void: | |
baseType = IBasicType.t_void; | |
break; | |
case DwarfConstants.DW_ATE_address: | |
case DwarfConstants.DW_ATE_packed_decimal: | |
case DwarfConstants.DW_ATE_numeric_string: | |
case DwarfConstants.DW_ATE_edited: | |
case DwarfConstants.DW_ATE_signed_fixed: | |
case DwarfConstants.DW_ATE_unsigned_fixed: | |
case DwarfConstants.DW_ATE_decimal_float: | |
default: | |
break; | |
} | |
// RVCT has interesting conceptions about "encoding" here. Be sure not to get confused later. | |
if (name.equals("void") && byteSize == 0) //$NON-NLS-1$ | |
baseType = IBasicType.t_void; | |
CPPBasicType type = new CPPBasicType(name, currentParentScope, baseType, qualifierBits, byteSize, null); | |
type.setType(getTypeOrReference(attributeList, currentCUHeader)); | |
registerType(offset, type, hasChildren); | |
storeTypeByName(name, type); | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(type)); } | |
} | |
private void processVariable(long offset, AttributeList attributeList, boolean isParameter) { | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArg(attributeList)); } | |
AttributeValue locationAttribute = attributeList.getAttribute(DwarfConstants.DW_AT_location); | |
ILocationProvider locationProvider = getLocationProvider(locationAttribute); | |
if (locationProvider == null) { | |
// No location means either this is a placeholder (in a subprogram declaration) | |
// or it may have been optimized out. See section | |
// 2.6 of the Dwarf3 spec. for now we're ignoring it but we may be able | |
// to show it in the view with some special decoration to indicate that | |
// it's been optimized out | |
// assume it is a forward reference if we're inside a function (formal_parameter or local)... | |
provider.functionsByOffset.put(offset, attributeList); | |
return; | |
} | |
// variables can have abstract origins with most of their contents | |
CompilationUnitHeader otherCU = currentCUHeader; | |
AttributeList otherAttributes = attributeList; | |
String name = attributeList.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
if (name.length() == 0) { | |
// no name. | |
// if there is a DW_AT_specification or DW_AT_abstract_origin attribute, use it to get variable attributes | |
DereferencedAttributes deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_specification); | |
if (deref == null) | |
deref = getDereferencedAttributes(attributeList, DwarfConstants.DW_AT_abstract_origin); | |
if (deref != null) { | |
// this should either have a name or point to another | |
// declaration | |
otherCU = deref.header; | |
otherAttributes = deref.attributeList; | |
name = otherAttributes.getAttributeValueAsString(DwarfConstants.DW_AT_name); | |
} | |
} | |
boolean global = (otherAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_external) == 1); | |
// if the name is mangled, unmangle it | |
if (name.startsWith("_Z")) { | |
name = unmangle(name); | |
} else if (global) { | |
// GCCE uses DW_AT_MIPS_linkage_name for the mangled name of an externally visible variable | |
String mangledName = otherAttributes.getAttributeValueAsString(DwarfConstants.DW_AT_MIPS_linkage_name); | |
if (unmangler.isMangled(mangledName)) { | |
try { | |
name = unmangler.unmangle(mangledName); | |
} catch (UnmanglingException ue) { | |
} | |
} | |
} | |
IType type = getTypeOrReference(otherAttributes.getAttribute(DwarfConstants.DW_AT_type), otherCU); | |
if (type != null) { | |
long startScope = attributeList.getAttributeValueAsLong(DwarfConstants.DW_AT_start_scope); | |
boolean isDeclared = otherAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_artificial) <= 0; | |
int definingFileNum = otherAttributes.getAttributeValueAsInt(DwarfConstants.DW_AT_decl_file); | |
if (definingFileNum > 0 && attributeList.getAttributeValueAsInt(DwarfConstants.DW_AT_declaration) > 0) { | |
// variable is declared here, but not defined here | |
definingFileNum = 0; | |
} | |
IPath definingFile = null; | |
// find the file it's defined in | |
if (definingFileNum > 0) { | |
// find the enclosing compile unit to get access to its list of | |
// .debug_line file names | |
IScope cuScope = currentParentScope; | |
while (cuScope != null && !(cuScope instanceof DwarfCompileUnit)) | |
cuScope = cuScope.getParent(); | |
if (cuScope != null) { | |
definingFile = ((DwarfCompileUnit) cuScope).getFileEntry(definingFileNum); | |
} | |
} | |
DwarfVariable variable = new DwarfVariable(name, | |
global ? moduleScope : currentParentScope, | |
locationProvider, | |
type, | |
isDeclared, | |
definingFile); | |
variable.setStartScope(startScope); | |
if (isParameter) { | |
if (currentParentScope instanceof FunctionScope) { | |
((FunctionScope) currentParentScope).addParameter(variable); | |
} else { | |
assert (false); | |
} | |
} else { | |
if (global) { | |
// add global variables to the module scope | |
moduleScope.addVariable(variable); | |
// AND to the CU scope | |
if (currentCompileUnitScope != null) { | |
currentCompileUnitScope.addVariable(variable); | |
} | |
} else { | |
// the parent scope could be compile unit, function or | |
// lexical block | |
currentParentScope.addVariable(variable); | |
} | |
// keep track of all variables by name for faster lookup | |
List<IVariable> variables = provider.variablesByName.get(name); | |
if (variables == null) { | |
variables = new ArrayList<IVariable>(); | |
provider.variablesByName.put(name, variables); | |
} | |
if (!variables.contains(variable)) { | |
variables.add(variable); | |
} | |
} | |
if (EDCTrace.SYMBOL_READER_VERBOSE_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(variable)); } | |
} | |
} | |
private ILocationProvider getLocationProvider(AttributeValue locationValue) { | |
if (locationValue != null) { | |
byte actualForm = locationValue.getActualForm(); | |
if (actualForm == DwarfConstants.DW_FORM_data4) { | |
// location list | |
Collection<LocationEntry> entryList = getLocationRecord(locationValue.getValueAsLong()); | |
if (entryList != null) { | |
return new LocationList(entryList.toArray(new LocationEntry[entryList.size()]), | |
exeReader.getByteOrder(), | |
currentCUHeader.addressSize, currentParentScope); | |
} | |
} else if (actualForm == DwarfConstants.DW_FORM_block | |
|| actualForm == DwarfConstants.DW_FORM_block1 | |
|| actualForm == DwarfConstants.DW_FORM_block2 | |
|| actualForm == DwarfConstants.DW_FORM_block4) { | |
// location expression | |
IStreamBuffer locationData = new MemoryStreamBuffer(locationValue.getValueAsBytes(), exeReader.getByteOrder()); | |
return new LocationExpression(locationData, | |
currentCUHeader.addressSize, | |
currentParentScope); | |
} else { | |
// should not happen according to the spec | |
assert (false); | |
} | |
} | |
return null; | |
} | |
private void dumpSymbols() { | |
if (DEBUG) { | |
PrintStream out = null; | |
try { | |
out = new PrintStream(new File(dumpFileName)); | |
} catch (FileNotFoundException e) { | |
System.out.println(DwarfMessages.DwarfInfoReader_DumpFileOpenOrCreateFailed + dumpFileName); | |
return; | |
} | |
// If to write to console | |
// PrintStream out = System.out; | |
out.println("Module - " + symbolFilePath); | |
out.println(" Variables - " + moduleScope.getVariables().size()); | |
out.println(" Compile units - " + moduleScope.getChildren().size()); | |
out.println(); | |
for (IScope cu : moduleScope.getChildren()) { | |
out.println(" Compile unit - " + cu.toString()); | |
out.println(" Variables - " + cu.getVariables().size()); | |
out.println(" Functions - " + cu.getChildren().size()); | |
out.println(); | |
for (IScope func : cu.getChildren()) { | |
out.println(" Function - " + func.toString()); | |
out.println(" Variables - " + func.getVariables().size()); | |
out.println(" Parameters - " + ((IFunctionScope) func).getParameters().size()); | |
out.println(" Lexical blocks - " + func.getChildren().size()); | |
out.println(); | |
// not accurate: can contain IFunctionScope too! | |
for (IScope block : func.getChildren()) { | |
out.println(" Lexical block - " + block.toString()); | |
out.println(" Variables - " + block.getVariables().size()); | |
out.println(); | |
} | |
} | |
} | |
out.close(); | |
} | |
} | |
public void parseForFrameIndices() { | |
synchronized (provider) { | |
if (!provider.frameDescEntries.isEmpty()) | |
return; | |
IExecutableSection frameSection = exeReader.findExecutableSection(DWARF_DEBUG_FRAME); | |
if (frameSection == null) | |
return; | |
IStreamBuffer buffer = frameSection.getBuffer(); | |
buffer.position(0); | |
int addressSize = 4; // TODO: 64-bit Dwarf | |
long cie_id = addressSize == 4 ? 0xffffffff : ~0L; | |
// in the first pass, just get a mapping of PC ranges to FDEs, | |
// so we can locate entries quickly (don't pre-parse CIEs or decompile FDE instructions yet) | |
while (buffer.position() < buffer.capacity()) { | |
try { | |
long fdePtr = buffer.position(); | |
long headerLength = readAddress(buffer, addressSize); | |
long nextPosition = buffer.position() + headerLength; | |
long ciePtr = readAddress(buffer, addressSize); | |
if (ciePtr != cie_id) { | |
long initialLocation = readAddress(buffer, addressSize); | |
long addressRange = readAddress(buffer, addressSize); | |
IStreamBuffer instructions = buffer.wrapSubsection(nextPosition - buffer.position()); | |
IRangeList.Entry entry = new IRangeList.Entry(initialLocation, initialLocation + addressRange); | |
FrameDescriptionEntry fde = new FrameDescriptionEntry(fdePtr, ciePtr, | |
entry.low, entry.high, | |
instructions, addressSize); | |
provider.frameDescEntries.put(entry, fde); | |
} | |
buffer.position(nextPosition); | |
} catch (Throwable t) { | |
EDCDebugger.getMessageLogger().logError(DwarfMessages.DwarfInfoReader_FrameIndicesReadFailed, t); | |
break; | |
} | |
} | |
} | |
} | |
/** | |
* Parse a CIE | |
* @param ciePtr | |
* @param addressSize | |
* @param framePC | |
* @return the CIE or <code>null</code> in case of error | |
*/ | |
public CommonInformationEntry parseCommonInfoEntry(Long ciePtr, int addressSize, IAddress framePC) throws IOException { | |
synchronized (provider) { | |
IExecutableSection frameSection = exeReader.findExecutableSection(DWARF_DEBUG_FRAME); | |
if (frameSection == null) | |
return null; | |
IStreamBuffer buffer = frameSection.getBuffer(); | |
buffer.position(ciePtr); | |
long headerLength = readAddress(buffer, addressSize); | |
if (headerLength > buffer.capacity()) { | |
assert(false); | |
return null; | |
} | |
long nextPosition = buffer.position() + headerLength; | |
/* cie_id = */ readAddress(buffer, addressSize); | |
byte version = buffer.get(); | |
String augmentation = readString(buffer); | |
long codeAlignmentFactor = read_unsigned_leb128(buffer); | |
long dataAlignmentFactor = read_signed_leb128(buffer); | |
int returnAddressRegister = version < 3 ? buffer.get() & 0xff : (int) read_unsigned_leb128(buffer); | |
IStreamBuffer instructions = buffer.wrapSubsection(nextPosition - buffer.position()); | |
String producer = null; | |
ICompileUnitScope cuScope = provider.getCompileUnitForAddress(framePC); | |
if (cuScope instanceof DwarfCompileUnit) | |
producer = ((DwarfCompileUnit) cuScope).getAttributeList().getAttributeValueAsString(DwarfConstants.DW_AT_producer); | |
return new CommonInformationEntry(codeAlignmentFactor, dataAlignmentFactor, | |
returnAddressRegister, version, instructions, addressSize, | |
producer, augmentation); | |
} | |
} | |
private void storeTypeByName(String name, IType type) { | |
if (name.length() == 0) | |
return; | |
// Don't store opaque types as they are not useful in user-defined | |
// type casting nor in opaque type resolution. And storing it would | |
// screw up opaque type resolution. | |
if (type instanceof ICompositeType && ((ICompositeType)type).isOpaque()) | |
return; | |
List<IType> typeList = provider.typesByName.get(name); | |
if (typeList == null) { | |
typeList = new ArrayList<IType>(); | |
// for a template, remove extra spaces and composite type names (e.g., "class") | |
if (name.indexOf('<') != -1) { | |
while (name.contains(" ")) //$NON-NLS-1$ | |
name = name.replaceAll(" ", " "); //$NON-NLS-1$ //$NON-NLS-2$ | |
name = name.replaceAll(", ", ","); //$NON-NLS-1$ //$NON-NLS-2$ | |
name = name.replaceAll("class ", ""); //$NON-NLS-1$ //$NON-NLS-2$ | |
name = name.replaceAll("struct ", ""); //$NON-NLS-1$ //$NON-NLS-2$ | |
name = name.replaceAll("union ", ""); //$NON-NLS-1$ //$NON-NLS-2$ | |
} | |
provider.typesByName.put(name, typeList); | |
} | |
typeList.add(type); | |
} | |
} |