diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Binary.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Binary.java index 1c519df3652..9283d781aa5 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Binary.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/Binary.java @@ -12,25 +12,30 @@ package org.eclipse.cdt.internal.core.model; import java.io.BufferedReader; +import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; import java.util.Map; +import org.eclipse.cdt.core.ISymbolReader; import org.eclipse.cdt.core.IBinaryParser.IBinaryExecutable; import org.eclipse.cdt.core.IBinaryParser.IBinaryFile; import org.eclipse.cdt.core.IBinaryParser.IBinaryObject; import org.eclipse.cdt.core.IBinaryParser.IBinaryShared; import org.eclipse.cdt.core.IBinaryParser.ISymbol; import org.eclipse.cdt.core.model.CModelException; +import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.IBinary; import org.eclipse.cdt.core.model.IBuffer; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.Path; public class Binary extends Openable implements IBinary { @@ -240,24 +245,97 @@ public class Binary extends Openable implements IBinary { Map hash = new HashMap(); IBinaryObject obj = getBinaryObject(); if (obj != null) { - ISymbol[] symbols = obj.getSymbols(); - for (int i = 0; i < symbols.length; i++) { - switch (symbols[i].getType()) { - case ISymbol.FUNCTION : - addFunction(info, symbols[i], hash); - break; + // First check if we can get the list of source + // files used to build the binary from the symbol + // information. if not, fall back on information from the binary parser. + if (!addSourceFiles(info, obj, hash)) + { + ISymbol[] symbols = obj.getSymbols(); + for (int i = 0; i < symbols.length; i++) { + switch (symbols[i].getType()) { + case ISymbol.FUNCTION : + addFunction(info, symbols[i], hash); + break; - case ISymbol.VARIABLE : - addVariable(info, symbols[i], hash); - break; + case ISymbol.VARIABLE : + addVariable(info, symbols[i], hash); + break; + } } - } + } ok = true; } } return ok; } + private boolean addSourceFiles(OpenableInfo info, IBinaryObject obj, + Map hash) throws CModelException { + // Try to get the list of source files used to build the binary from the + // symbol information. + + ISymbolReader symbolreader = (ISymbolReader)obj.getAdapter(ISymbolReader.class); + if (symbolreader == null) + return false; + + String[] sourceFiles = symbolreader.getSourceFiles(); + if (sourceFiles != null && sourceFiles.length > 0) { + for (int i = 0; i < sourceFiles.length; i++) { + String filename = sourceFiles[i]; + + // Sometimes the path in the symbolics will have a different + // case than the actual file system path. Even if the file + // system is not case sensitive this will confuse the Path + // class. + // So make sure the path is canonical, otherwise breakpoints + // won't be resolved, etc.. + + File file = new File(filename); + if (file.exists()) { + try { + filename = file.getCanonicalPath(); + } catch (IOException e) { + } + } + + // See if this source file is already in the project. + // We check this to determine if we should create a TranslationUnit or ExternalTranslationUnit + IFile sourceFile = getCProject().getProject().getFile(filename); + IPath path = new Path(filename); + + IFile wkspFile = null; + if (sourceFile.exists()) + wkspFile = sourceFile; + else { + IFile[] filesInWP = ResourcesPlugin + .getWorkspace().getRoot() + .findFilesForLocation(path); + + for (int j = 0; j < filesInWP.length; j++) { + if (filesInWP[j].isAccessible()) { + wkspFile = filesInWP[j]; + break; + } + } + } + + // Create a translation unit for this file and add it as a child of the binary + String id = CoreModel.getRegistedContentTypeId(sourceFile + .getProject(), sourceFile.getName()); + + TranslationUnit tu; + if (wkspFile != null) + tu = new TranslationUnit(this, wkspFile, id); + else + tu = new ExternalTranslationUnit(this, path, id); + + info.addChild(tu); + } + return true; + } + return false; + } + private void addFunction(OpenableInfo info, ISymbol symbol, Map hash) throws CModelException { IPath filename = filename = symbol.getFilename(); BinaryFunction function = null; diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ISymbolReader.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ISymbolReader.java new file mode 100644 index 00000000000..014d0984986 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ISymbolReader.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2006 Nokia and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Nokia - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core; + +/** + * A reader that's able to decipher debug symbol formats. + * + * This initial version only returns a list of source files. + * + */ +public interface ISymbolReader { + + String[] getSourceFiles(); +} diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/CodeViewReader.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/CodeViewReader.java new file mode 100644 index 00000000000..e3f408c288c --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/CodeViewReader.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2006 Nokia and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Nokia - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.utils.coff; + +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.core.ISymbolReader; + +public class CodeViewReader implements ISymbolReader { + + RandomAccessFile file; + int cvData; + boolean isLe; + List fileList; + String[] files = null; + boolean parsed = false; + + public CodeViewReader(RandomAccessFile accessFile, int dataOffset, boolean littleEndian) { + file = accessFile; + cvData = dataOffset; + isLe = littleEndian; + + fileList = new ArrayList(); + } + + public String[] getSourceFiles() { + if (!parsed) { + try { + parse(); + } catch (IOException e) { + } + + parsed = true; + + files = new String[fileList.size()]; + for (int i = 0; i < fileList.size(); i++) { + files[i] = (String)fileList.get(i); + } + } + + return files; + } + + private int getInt(int value) { + if (isLe) { + int tmp = 0; + + for (int i = 0; i < 4; i++) { + tmp <<= 8; + tmp |= value & 0xFF; + value >>= 8; + } + + return tmp; + } else { + return value; + } + } + + private short getShort(short value) { + if (isLe) { + short tmp = value; + + tmp &= 0xFF; + tmp <<= 8; + tmp |= (value >> 8) & 0xFF; + + return tmp; + } else { + return value; + } + } + + private void parse() throws IOException { + if (cvData <= 0) + return; + + // seek to the start of the CodeView data + file.seek(cvData); + + // skip the next four bytes - signature "NB11" + file.skipBytes(4); + + // get the offset to the subsection directory + int subsectionDirOffset = getInt(file.readInt()); + + // seek to the start of the subsection directory + file.seek(cvData + subsectionDirOffset); + + // skip the header length (2) and directory entry length (2) + file.skipBytes(4); + + // loop through the directories looking for source files + int directoryCount = getInt(file.readInt()); + + // skip the rest of the header + file.skipBytes(8); + + // save the file offset to the base of the directories + long directoryOffset = file.getFilePointer(); + + for (int i = 0; i < directoryCount; i++) { + // seek to the next directory + file.seek(directoryOffset + i*12); + + // get the type of the subsection. we only care about source modules + short subsectionType = getShort(file.readShort()); + if (0x127 == subsectionType) { + + // skip the module index + file.skipBytes(2); + + // get the offset from the base address + int subsectionOffset = getInt(file.readInt()); + + // seek to the start of the source module section + file.seek(cvData + subsectionOffset); + + // get the number of source files + short fileCount = getShort(file.readShort()); + + // skip the number of segments + file.skipBytes(2); + + // save the file offset to the array of base offsets + long arrayOffset = file.getFilePointer(); + + // loop through the files and add them to our list + for (int j = 0; j < fileCount; j++) { + // seek to the correct array entry + file.seek(arrayOffset + j*4); + + // get the offset to the first entry and seek to it + int offset = getInt(file.readInt()); + file.seek(cvData + subsectionOffset + offset); + + // get the number of segments + short segments = getShort(file.readShort()); + + // now skip to the name length + file.skipBytes(2 + segments*4 + segments*8); + byte nameLength = file.readByte(); + + // now extract the filename and add it to our list + // if it's not already there + byte[] nameBuffer = new byte[nameLength]; + file.readFully(nameBuffer); + String name = new String(nameBuffer); + + if (!fileList.contains(name)) + fileList.add(name); + } + } + } + } +} diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PE.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PE.java index ee3c6b6ebe5..d7c9606dca3 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PE.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PE.java @@ -16,12 +16,14 @@ import java.io.RandomAccessFile; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.IAddressFactory; +import org.eclipse.cdt.core.ISymbolReader; import org.eclipse.cdt.utils.Addr32Factory; import org.eclipse.cdt.utils.coff.Coff.FileHeader; import org.eclipse.cdt.utils.coff.Coff.OptionalHeader; import org.eclipse.cdt.utils.coff.Coff.SectionHeader; import org.eclipse.cdt.utils.coff.Coff.Symbol; import org.eclipse.cdt.utils.coff.Exe.ExeHeader; +import org.eclipse.cdt.utils.debug.stabs.StabsReader; /** * The PE file header consists of an MS-DOS stub, the PE signalture, the COFF file Header @@ -162,9 +164,42 @@ public class PE { } } + public static class IMAGE_DEBUG_DIRECTORY { + final int DEBUGDIRSZ = 28; + public int Characteristics; + public int TimeDateStamp; + public short MajorVersion; + public short MinorVersion; + public int Type; + public int SizeOfData; + public int AddressOfRawData; + public int PointerToRawData; + + public IMAGE_DEBUG_DIRECTORY(RandomAccessFile file, long offset) throws IOException { + file.seek(offset); + byte[] dir = new byte[DEBUGDIRSZ]; + file.readFully(dir); + ReadMemoryAccess memory = new ReadMemoryAccess(dir, true); + Characteristics = memory.getInt(); + TimeDateStamp = memory.getInt(); + MajorVersion = memory.getShort(); + MinorVersion = memory.getShort(); + Type = memory.getInt(); + SizeOfData = memory.getInt(); + AddressOfRawData = memory.getInt(); + PointerToRawData = memory.getInt(); + } + } + + public static class IMAGE_DATA_DIRECTORY { + + public int VirtualAddress; + public int Size; + } + public static class NTOptionalHeader { - public final static int NTHDRSZ = 68; + public final static int NTHDRSZ = 196; public int ImageBase; // 4 bytes. public int SectionAlignment; // 4 bytes. public int FileAlignment; // 4 bytes. @@ -186,6 +221,7 @@ public class PE { public int SizeOfHeapCommit; // 4 bytes. public int LoaderFlags; // 4 bytes. public int NumberOfRvaAndSizes; // 4 bytes. + public IMAGE_DATA_DIRECTORY DataDirectory[]; public NTOptionalHeader(RandomAccessFile file) throws IOException { this(file, file.getFilePointer()); @@ -217,6 +253,13 @@ public class PE { SizeOfHeapCommit = memory.getInt(); LoaderFlags = memory.getInt(); NumberOfRvaAndSizes = memory.getInt(); + + DataDirectory = new IMAGE_DATA_DIRECTORY[NumberOfRvaAndSizes]; // 8*16=128 bytes + for (int i = 0; i < NumberOfRvaAndSizes; i++) { + DataDirectory[i] = new IMAGE_DATA_DIRECTORY(); + DataDirectory[i].VirtualAddress = memory.getInt(); + DataDirectory[i].Size = memory.getInt(); + } } public String toString() { @@ -353,6 +396,7 @@ public class PE { switch (magic) { case PEConstants.IMAGE_FILE_MACHINE_ALPHA: case PEConstants.IMAGE_FILE_MACHINE_ARM: + case PEConstants.IMAGE_FILE_MACHINE_ARM2: case PEConstants.IMAGE_FILE_MACHINE_ALPHA64: case PEConstants.IMAGE_FILE_MACHINE_I386: case PEConstants.IMAGE_FILE_MACHINE_IA64: @@ -385,6 +429,7 @@ public class PE { attrib.cpu = "alpha"; //$NON-NLS-1$ break; case PEConstants.IMAGE_FILE_MACHINE_ARM: + case PEConstants.IMAGE_FILE_MACHINE_ARM2: attrib.cpu = "arm"; //$NON-NLS-1$ break; case PEConstants.IMAGE_FILE_MACHINE_ALPHA64: @@ -600,11 +645,12 @@ public class PE { // FIXME: What is this again ? if (ntHeader != null) symbolTable[i].n_value += ntHeader.ImageBase + ntHeader.FileAlignment; - } + } } return symbolTable; } + public byte[] getStringTable() throws IOException { if (stringTable == null) { if (fileHeader.f_nsyms > 0) { @@ -691,12 +737,102 @@ public class PE { } return rfile; } - public static void main(String[] args) { + + private ISymbolReader createCodeViewReader() { + ISymbolReader symReader = null; + final int IMAGE_DIRECTORY_ENTRY_DEBUG = 6; + try { - PE pe = new PE(args[0]); - System.out.println(pe); + // the debug directory is the 6th entry + NTOptionalHeader ntHeader = getNTOptionalHeader(); + if (ntHeader.NumberOfRvaAndSizes < IMAGE_DIRECTORY_ENTRY_DEBUG) + return null; + + int debugDir = ntHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress; + if (debugDir == 0) + return null; + + int debugFormats = ntHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size / 28; + if (debugFormats == 0) + return null; + + SectionHeader[] sections = getSectionHeaders(); + + // loop through the section headers to find the .rdata section + for (int i = 0; i < sections.length; i++) { + String name = new String(sections[i].s_name).trim(); + if (name.equals(".rdata")) { + // figure out the file offset of the debug ddirectory entries + int offsetInto_rdata = debugDir - sections[i].s_vaddr; + int fileOffset = sections[i].s_scnptr + offsetInto_rdata; + RandomAccessFile accessFile = getRandomAccessFile(); + + // loop through the debug directories looking for CodeView (type 2) + for (int j = 0; j < debugFormats; j++) { + PE.IMAGE_DEBUG_DIRECTORY dir = new PE.IMAGE_DEBUG_DIRECTORY( + accessFile, fileOffset); + + if ((2 == dir.Type) && (dir.SizeOfData > 0)) { + // CodeView found, seek to actual data + int debugBase = dir.PointerToRawData; + accessFile.seek(debugBase); + + // sanity check. the first four bytes of the CodeView + // data should be "NB11" + String s2 = accessFile.readLine(); + if (s2.startsWith("NB11")) { + Attribute att = getAttribute(); + symReader = new CodeViewReader(accessFile, + debugBase, att.isLittleEndian()); + return symReader; + } + } + fileOffset += dir.DEBUGDIRSZ; + } + } + } } catch (IOException e) { e.printStackTrace(); } + + return symReader; + } + + private ISymbolReader createStabsReader() { + ISymbolReader symReader = null; + try { + SectionHeader[] sections = getSectionHeaders(); + byte[] stab = null; + byte[] stabstr = null; + + // loop through the section headers looking for stabs info + for (int i = 0; i < sections.length; i++) { + String name = new String(sections[i].s_name).trim(); + if (name.equals(".stab")) { + stab = sections[i].getRawData(); + } + if (name.equals(".stabstr")) { + stabstr = sections[i].getRawData(); + } + } + + // if we found both sections then proceed + if (stab != null && stabstr != null) { + Attribute att = getAttribute(); + symReader = new StabsReader(stab, stabstr, att.isLittleEndian()); + } + + } catch (IOException e) { + } + return symReader; + } + + public ISymbolReader getSymbolReader() { + ISymbolReader reader = null; + reader = createStabsReader(); + if (reader == null) { + reader = createCodeViewReader(); + } + return reader; } } diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PEConstants.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PEConstants.java index ffcfb5d45a0..0ca416af743 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PEConstants.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/PEConstants.java @@ -49,6 +49,9 @@ public final static int IMAGE_FILE_MACHINE_SH3 = 0x1a2; public final static int IMAGE_FILE_MACHINE_SH4 = 0x1a6; public final static int IMAGE_FILE_MACHINE_THUMB = 0x1c2; +// This is not listed in the PE docs but is generated by some GCC tools. +public final static int IMAGE_FILE_MACHINE_ARM2 = 0xa00; + /* OptionalHeader.magic */ public final static int PE32 = 0x10b; public final static int PE32PLUS = 0x20b; diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/parser/PEBinaryObject.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/parser/PEBinaryObject.java index 50f63433c6c..e7ad168169c 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/parser/PEBinaryObject.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/parser/PEBinaryObject.java @@ -19,6 +19,7 @@ import java.util.List; import org.eclipse.cdt.core.IAddressFactory; import org.eclipse.cdt.core.IBinaryParser; +import org.eclipse.cdt.core.ISymbolReader; import org.eclipse.cdt.core.IBinaryParser.IBinaryFile; import org.eclipse.cdt.core.IBinaryParser.ISymbol; import org.eclipse.cdt.utils.AR; @@ -97,6 +98,25 @@ public class PEBinaryObject extends BinaryObjectAdapter { return info; } + public Object getAdapter(Class adapter) { + if (adapter.equals(PE.class)) { + try { + if (header != null) { + return new PE(getPath().toOSString(), header.getObjectDataOffset()); + } + return new PE(getPath().toOSString()); + } catch (IOException e) { + } + } + if (adapter.equals(ISymbolReader.class)) { + PE pe = (PE)getAdapter(PE.class); + if (pe != null) { + return pe.getSymbolReader(); + } + } + return super.getAdapter(adapter); + } + protected PE getPE() throws IOException { if (header != null) { return new PE(getPath().toOSString(), header.getObjectDataOffset()); diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/parser/PEParser.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/parser/PEParser.java index 9b7337b409d..d41b75fb151 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/parser/PEParser.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/coff/parser/PEParser.java @@ -116,6 +116,7 @@ public class PEParser extends AbstractCExtension implements IBinaryParser { case PEConstants.IMAGE_FILE_MACHINE_SH3: case PEConstants.IMAGE_FILE_MACHINE_SH4: case PEConstants.IMAGE_FILE_MACHINE_THUMB: + case PEConstants.IMAGE_FILE_MACHINE_ARM2: // Ok; isBin = true; break; diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/stabs/Stabs.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/stabs/Stabs.java index cdbf05eee76..b6061b80006 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/stabs/Stabs.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/stabs/Stabs.java @@ -36,6 +36,9 @@ import org.eclipse.cdt.utils.debug.IDebugEntryRequestor; import org.eclipse.cdt.utils.debug.tools.DebugSym; import org.eclipse.cdt.utils.debug.tools.DebugSymsRequestor; import org.eclipse.cdt.utils.elf.Elf; +import org.eclipse.cdt.utils.coff.PE; +import org.eclipse.cdt.utils.coff.Coff.SectionHeader; +import org.eclipse.cdt.utils.coff.PE.Attribute; public class Stabs { @@ -58,9 +61,18 @@ public class Stabs { DebugType voidType = new DebugBaseType("void", 0, false); //$NON-NLS-1$ public Stabs(String file) throws IOException { - Elf exe = new Elf(file); - init(exe); - exe.dispose(); + + // we support Elf and PE executable formats. try Elf + // and then PE. + try { + Elf exe = new Elf(file); + init(exe); + exe.dispose(); + } catch (IOException e) { + PE exe = new PE(file); + init(exe); + exe.dispose(); + } } public Stabs(Elf exe) throws IOException { @@ -90,6 +102,26 @@ public class Stabs { } } + void init(PE exe) throws IOException { + byte[] data = null; + byte[] stabstr = null; + + SectionHeader[] sections = exe.getSectionHeaders(); + for (int i = 0; i < sections.length; i++) { + String name = new String(sections[i].s_name).trim(); + if (name.equals(".stab")) { //$NON-NLS-1$ + data = sections[i].getRawData(); + } else if (name.equals(".stabstr")) { //$NON-NLS-1$ + stabstr = sections[i].getRawData(); + } + } + + Attribute att = exe.getAttribute(); + if (data != null && stabstr != null) { + init(data, stabstr, att.isLittleEndian()); + } + } + void init(byte[] stab, byte[] stabstr, boolean le) { stabData = stab; stabstrData = stabstr; @@ -453,10 +485,11 @@ public class Stabs { case 'T' : { String infoField = sf.getTypeInformation(); - // According to the doc 't' can follow the 'T' + // According to the doc 't' can follow the 'T'. If so just + // strip the T and go again. if (infoField.length() > 0 && infoField.charAt(0) == 't') { - //String s = infoField.substring(1); - parseStabString(requestor, field, value); + String s = field.replaceFirst(":T", ":"); + parseStabString(requestor, s, value); } else { // Just register the type. String name = sf.getName(); diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/stabs/StabsReader.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/stabs/StabsReader.java new file mode 100644 index 00000000000..b6f741fb449 --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/stabs/StabsReader.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2006 Nokia and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Nokia - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.utils.debug.stabs; + +import java.io.File; +import java.util.List; +import java.util.ArrayList; + +import org.eclipse.cdt.core.ISymbolReader; + +public class StabsReader implements ISymbolReader { + + byte[] stabData; + byte[] stabstrData; + boolean isLe; + List fileList; + String[] files = null; + boolean parsed = false; + String currentFile; + + public StabsReader(byte[] data, byte[] stabstr, boolean littleEndian) { + stabData = data; + stabstrData = stabstr; + isLe = littleEndian; + + fileList = new ArrayList(); + } + + public String[] getSourceFiles() { + if (!parsed) { + parse(); + + parsed = true; + + files = new String[fileList.size()]; + for (int i = 0; i < fileList.size(); i++) { + files[i] = (String)fileList.get(i); + } + } + + return files; + } + + private String makeString(long offset) { + StringBuffer buf = new StringBuffer(); + for (; offset < stabstrData.length; offset++) { + byte b = stabstrData[(int) offset]; + if (b == 0) { + break; + } + buf.append((char) b); + } + return buf.toString(); + } + + private int read_4_bytes(byte[] bytes, int offset) { + if (isLe) { + return (((bytes[offset + 3] & 0xff) << 24) + | ((bytes[offset + 2] & 0xff) << 16) + | ((bytes[offset + 1] & 0xff) << 8) + | (bytes[offset] & 0xff)); + } + return (((bytes[offset] & 0xff) << 24) + | ((bytes[offset + 1] & 0xff) << 16) + | ((bytes[offset + 2] & 0xff) << 8) + | (bytes[offset + 3] & 0xff)); + } + + private short read_2_bytes(byte[] bytes, int offset) { + if (isLe) { + return (short) (((bytes[offset + 1] & 0xff) << 8) | (bytes[offset] & 0xff)); + } + return (short) (((bytes[offset] & 0xff) << 8) | (bytes[offset + 1] & 0xff)); + } + + private String fixUpPath(String path) { + // some compilers generate extra back slashes + path = path.replaceAll("\\\\\\\\", "\\\\"); + + // translate any cygwin drive paths, e.g. //G/System/main.cpp or /cygdrive/c/system/main.c + if (path.startsWith("/cygdrive/") && ('/' == path.charAt(11))) { + char driveLetter = path.charAt(10); + driveLetter = (Character.isLowerCase(driveLetter)) ? Character.toUpperCase(driveLetter) : driveLetter; + + StringBuffer buf = new StringBuffer(path); + buf.delete(0, 11); + buf.insert(0, driveLetter); + buf.insert(1, ':'); + + path = buf.toString(); + } + + // translate any cygwin drive paths, e.g. //G/System/main.cpp or /cygdrive/c/system/main.c + if (path.startsWith("//") && ('/' == path.charAt(3))) { + char driveLetter = path.charAt(2); + driveLetter = (Character.isLowerCase(driveLetter)) ? Character.toUpperCase(driveLetter) : driveLetter; + + StringBuffer buf = new StringBuffer(path); + buf.delete(0, 3); + buf.insert(0, driveLetter); + buf.insert(1, ':'); + + path = buf.toString(); + } + + return path; + } + + private void parse() { + long nstab = stabData.length / StabConstant.SIZE; + int i, offset; + String holder = null; + long stroff = 0; + int type = 0; + int other = 0; + short desc = 0; + long value = 0; + + for (i = offset = 0; i < nstab; i++, offset += StabConstant.SIZE) { + + // get the type; 1 byte; + type = 0xff & stabData[offset + 4]; + + // ignoring anything other than N_SO and N_SOL because these are source or + // object file entries + if (type == StabConstant.N_SO || type == StabConstant.N_SOL) { + // get the other + other = 0xff & stabData[offset + 5]; + // get the desc + desc = read_2_bytes(stabData, offset + 6); + // get the value + value = read_4_bytes(stabData, offset + 8); + + // get the offset for the string; 4 bytes + stroff = read_4_bytes(stabData, offset); + + String field; + if (stroff > 0) { + field = makeString(stroff); + } else { + field = new String(); + } + // Check for continuation and if any go to the next stab + // until we find a string that is not terminated with a + // continuation line '\\' + // According to the spec all the other fields are duplicated so we + // still have the data. + // From the spec continuation line on AIX is '?' + if (field.endsWith("\\") || field.endsWith("?")) { //$NON-NLS-1$ //$NON-NLS-2$ + field = field.substring(0, field.length() - 1); + if (holder == null) { + holder = field; + } else { + holder += field; + } + continue; + } else if (holder != null) { + field = holder + field; + holder = null; + } + parseStabEntry(field, type, other, desc, value); + } + } + } + + void parseStabEntry(String field, int type, int other, short desc, long value) { + // Parse the string + switch (type) { + case StabConstant.N_SOL : + // include file + if (field != null && field.length() > 0) { + + field = fixUpPath(field); + + if (!fileList.contains(field)) + fileList.add(field); + } + break; + + case StabConstant.N_SO : + // source file + if (field != null && field.length() > 0) { + // if it ends with "/" then the next entry will be the rest of the string. + if (field.endsWith("/")) { //$NON-NLS-1$ + currentFile = field; + } else { + if (currentFile != null) { + // if this entry is an absolute path then just throw away the existing currentFile + if (new File(field).isAbsolute()) { + currentFile = field; + } else { + currentFile += field; + } + } else { + currentFile = field; + } + + + currentFile = fixUpPath(currentFile); + + if (!fileList.contains(currentFile)) + fileList.add(currentFile); + + currentFile = null; + } + } + break; + } + } +}