diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/DwarfReader.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/DwarfReader.java index 8b67cb2a7ea..2545386e447 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/DwarfReader.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/DwarfReader.java @@ -15,11 +15,13 @@ package org.eclipse.cdt.utils.debug.dwarf; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.eclipse.cdt.core.ISymbolReader; import org.eclipse.cdt.utils.debug.IDebugEntryRequestor; import org.eclipse.cdt.utils.elf.Elf; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; /** @@ -37,10 +39,14 @@ public class DwarfReader extends Dwarf implements ISymbolReader { DWARF_DEBUG_STR // this is optional. Some compilers don't generate it. }; - ArrayList fileList; - String[] files = null; - boolean m_parsed = false; + private Collection m_fileCollection = new ArrayList(); + private String[] m_fileNames = null; + private String m_exeFileWin32Drive; // Win32 drive of the exe file. + private boolean m_onWindows; + private boolean m_parsed = false; private int m_leb128Size = 0; + private ArrayList m_parsedLineTableOffsets = new ArrayList(); + private int m_parsedLineTableSize = 0; public DwarfReader(String file) throws IOException { super(file); @@ -73,7 +79,11 @@ public class DwarfReader extends Dwarf implements ISymbolReader { printEnabled = false; m_parsed = false; - fileList = new ArrayList(); + + Path pa = new Path(exe.getFilename()); + m_exeFileWin32Drive = pa.getDevice(); + + m_onWindows = (File.separatorChar == '\\'); } /* @@ -82,7 +92,7 @@ public class DwarfReader extends Dwarf implements ISymbolReader { */ void parseSourceInCULineInfo( String cuCompDir, // compilation directory of the CU - int cuStmtList) // offste of the CU in the line table section + int cuStmtList) // offset of the CU line table in .debug_line section { byte[] data = (byte[]) dwarfSections.get(DWARF_DEBUG_LINE); if (data != null) { @@ -91,7 +101,7 @@ public class DwarfReader extends Dwarf implements ISymbolReader { /* Read line table header: * - * total_length: 4 bytes + * total_length: 4 bytes (excluding itself) * version: 2 * prologue length: 4 * minimum_instruction_len: 1 @@ -101,6 +111,21 @@ public class DwarfReader extends Dwarf implements ISymbolReader { * opcode_base: 1 * standard_opcode_lengths: (value of opcode_base) */ + + // Remember the CU line tables we've parsed. + Integer cuOffset = new Integer(cuStmtList); + if (! m_parsedLineTableOffsets.contains(cuOffset)) { + m_parsedLineTableOffsets.add(cuOffset); + + int length = read_4_bytes(data, offset) + 4; + m_parsedLineTableSize += length + 4; + } + else { + // Compiler like ARM RVCT may produce several CUs for the + // same source files. + return; + } + // Skip the following till "opcode_base" offset = offset + 14; int opcode_base = data[offset++]; @@ -155,53 +180,213 @@ public class DwarfReader extends Dwarf implements ISymbolReader { } } + /* + * Check if there are any line tables in .debug_line section that are + * not referenced by any TAG_compile_units. If yes, add source files + * in those table entries to our "m_fileCollection". + * If the compiler/linker is fully dwarf standard compliant, that should + * not happen. But that case does exist, hence this workaround. + * .................. LWang. 08/24/07 + */ + private void getSourceFilesFromDebugLineSection() + { + byte[] data = (byte[]) dwarfSections.get(DWARF_DEBUG_LINE); + if (data == null) + return; + + int sectionSize = data.length; + int minHeaderSize = 16; + + // Check if there is data in .debug_line section that is not parsed + // yet by parseSourceInCULineInfo(). + if (m_parsedLineTableSize >= sectionSize - minHeaderSize) + return; + + // The .debug_line section contains a list of line tables + // for compile_units. We'll iterate through all line tables + // in the section. + /* + * Line table header for one compile_unit: + * + * total_length: 4 bytes (excluding itself) + * version: 2 + * prologue length: 4 + * minimum_instruction_len: 1 + * default_is_stmt: 1 + * line_base: 1 + * line_range: 1 + * opcode_base: 1 + * standard_opcode_lengths: (value of opcode_base) + */ + + int lineTableStart = 0; // offset in the .debug_line section + + try { + while (lineTableStart < sectionSize - minHeaderSize) { + int offset = lineTableStart; + + Integer currLineTableStart = new Integer(lineTableStart); + + // Read length of the line table for one compile unit + // Note the length does not including the "length" field itself. + int tableLength = read_4_bytes(data, offset); + + // Record start of next CU line table + lineTableStart += tableLength + 4; + + // According to Dwarf standard, the "tableLength" should cover the + // the whole CU line table. But some compilers (e.g. ARM RVCT 2.2) + // produce extra padding (1 to 3 bytes) beyond that in order for + // "lineTableStart" to be aligned at multiple of 4. The padding + // bytes are beyond the "tableLength" and not indicated by + // any flag, which I believe is not Dwarf2 standard compliant. + // How to determine if that type of padding exists ? + // I don't have a 100% safe way. But following hacking seems + // good enough in practice.........08/26/07 + if (lineTableStart < sectionSize - minHeaderSize && + (lineTableStart & 0x3) != 0) + { + int ltLength = read_4_bytes(data, lineTableStart); + int dwarfVer = read_2_bytes(data, lineTableStart+4); + int minInstLengh = data[lineTableStart+4+2+4]; + + boolean dataValid = + ltLength > minHeaderSize && + ltLength < 16*64*1024 && // One source file has that much line data ? + dwarfVer > 0 && dwarfVer < 4 && // ver 3 is still draft at present. + minInstLengh > 0 && minInstLengh <= 8; + + if (! dataValid) // padding exists ! + lineTableStart = (lineTableStart+3) & ~0x3; + } + + if (m_parsedLineTableOffsets.contains(currLineTableStart)) + // current line table has already been parsed, skip it. + continue; + + // Skip following fields till "opcode_base" + offset = offset + 14; + int opcode_base = data[offset++]; + offset += opcode_base - 1; + + // Read in directories. + // + ArrayList dirList = new ArrayList(); + + String str, fileName; + + // first dir should be TAG_comp_dir from CU, which we don't have here. + dirList.add(""); + + while (true) { + str = readString(data, offset); + if (str.length() == 0) + break; + dirList.add(str); + offset += str.length() + 1; + } + offset++; + + // Read file names + // + long leb128; + while (true) { + fileName = readString(data, offset); + if (fileName.length() == 0) // no more file entry + break; + offset += fileName.length() + 1; + + // dir index. Note "0" is reserved for compilation directory. + leb128 = read_unsigned_leb128(data, offset); + offset += m_leb128Size; + + addSourceFile((String) dirList.get((int) leb128), fileName); + + // Skip the followings + // + // modification time + leb128 = read_unsigned_leb128(data, offset); + offset += m_leb128Size; + + // file size in bytes + leb128 = read_unsigned_leb128(data, offset); + offset += m_leb128Size; + } + } + } catch (IOException e) { + e.printStackTrace(); + return; + } + } + public String[] getSourceFiles() { if (!m_parsed) { - parse(null); + m_fileCollection.clear(); + + getSourceFilesFromDebugInfoSection(); + + getSourceFilesFromDebugLineSection(); + m_parsed = true; - files = new String[fileList.size()]; - fileList.toArray(files); + m_fileNames = new String[m_fileCollection.size()]; + m_fileCollection.toArray(m_fileNames); } - return files; + return m_fileNames; + } + + /* + * Get source file names from compile units (CU) in .debug_info section, + * which will also search line table for the CU in .debug_line section. + * + * The file names are stored in member "m_fileCollection". + */ + private void getSourceFilesFromDebugInfoSection() { + // This will parse the data in .debug_info section which + // will call this->processCompileUnit() to get source files. + parse(null); } private void addSourceFile(String dir, String name) { if (name == null || name.length() == 0) return; + if (name.charAt(0) == '<') // don't count the entry "" from GCCE compiler return; String fullName = name; - Path pa = new Path(name); + IPath dirPa = new Path(dir); + IPath pa = new Path(name); + // Combine dir & name if needed. + if (!pa.isAbsolute() && dir.length() > 0) + pa = dirPa.append(pa); - // Check to see if the file exists, if not, append the path information from the dir info. - // On Win32, if the file name has the full path except the drive letter, - // add the driver letter. - // Otherwise if the file name is not absolute, prepend the "dir". - - if ( !pa.toFile().exists() && dir.length() > 0) - { - if (pa.isAbsolute()) - { - if (pa.getDevice() == null && dir.charAt(1) == ':') // no drive letter - fullName = dir.substring(0, 2) + name; - } - else - fullName = dir + File.separatorChar + name; + // For win32 only. + // On Windows, there are cases where the source file itself has the full path + // except the drive letter. + if (m_onWindows && pa.isAbsolute() && pa.getDevice() == null) { + // Try to get drive letter from comp_dir. + if (dirPa.getDevice() != null) + pa = pa.setDevice(dirPa.getDevice()); + else if (m_exeFileWin32Drive != null) + // No drive from Dwarf data, which is also possible with RVCT or GCCE + // compilers for ARM. A practically good solution is to assume + // drive of the exe file as the drive. Though it's not good in theory, + // it does not hurt when the assumption is wrong, as user still has the + // option to locate the file manually...03/15/07 + pa = pa.setDevice(m_exeFileWin32Drive); } // This convert the path to canonical path (but not necessarily absolute, which // is different from java.io.File.getCanonicalPath()). - pa = new Path(fullName); fullName = pa.toOSString(); - if (!fileList.contains(fullName)) - fileList.add(fullName); + if (!m_fileCollection.contains(fullName)) + m_fileCollection.add(fullName); } /** @@ -226,6 +411,8 @@ public class DwarfReader extends Dwarf implements ISymbolReader { return str; } + // Note this method modifies a data member + // long read_unsigned_leb128(byte[] data, int offset) throws IOException { /* unsigned */ long result = 0;