From d3a8e639c8dcb6a270d3e140f4e62144203a3c7c Mon Sep 17 00:00:00 2001 From: Teodor Madan Date: Fri, 17 Jan 2014 14:22:36 +0200 Subject: [PATCH] Bug 425955: NPE in DwarfReader - Fix parsing .debug_line section in version format, part of Dwarf 4. - Refactor in a separate method reading initial length field to consistently handle 64-bit dwarf format Change-Id: I9f32862ed91540c24ce33227eeb384a5d6b141da Signed-off-by: Teodor Madan Reviewed-on: https://git.eclipse.org/r/20830 --- .../eclipse/cdt/utils/debug/dwarf/Dwarf.java | 63 ++++++++++++---- .../cdt/utils/debug/dwarf/DwarfReader.java | 71 ++++++++++++------- 2 files changed, 96 insertions(+), 38 deletions(-) diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/Dwarf.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/Dwarf.java index 068df13236a..e54140e38eb 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/Dwarf.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/Dwarf.java @@ -372,19 +372,15 @@ public class Dwarf { try { while (data.hasRemaining()) { CompilationUnitHeader header = new CompilationUnitHeader(); - header.length = read_4_bytes(data) & 0xffffffffL; - - if (header.length == 0xffffffffL) { - header.length = read_8_bytes(data); - header.offsetSize = 8; - } else if (header.length == 0) { // IRIX - header.length = read_8_bytes(data); - header.offsetSize = 8; - } else - header.offsetSize = 4; + InitialLengthValue sectionLength = readInitialLengthField(data); + header.length = sectionLength.length; + header.offsetSize = sectionLength.offsetSize; header.version = read_2_bytes(data); - header.abbreviationOffset = read_4_bytes(data); + if (header.offsetSize == 8) + header.abbreviationOffset = (int)read_8_bytes(data); + else + header.abbreviationOffset = read_4_bytes(data); header.addressSize = data.get(); if (printEnabled) { @@ -394,12 +390,13 @@ public class Dwarf { // read the abbrev section. Map abbrevs = parseDebugAbbreviation(header); - // Note "length+4" is the total size in bytes of the CU data. + // A 4-byte or 12-byte unsigned integer representing the length of the .debug_info + // contribution for that compilation unit, not including the length field itself. ByteBuffer entryBuffer = data.slice(); - entryBuffer.limit(((int) header.length) + 4 - 11); + entryBuffer.limit(((int)header.length) - (header.offsetSize == 8 ? 11 : 7)); parseDebugInfoEntry(requestor, entryBuffer, abbrevs, header); - data.position(data.position() + ((int) header.length) + 4 - 11); + data.position(data.position() + ((int)header.length) - (header.offsetSize == 8 ? 11 : 7)); if (printEnabled) System.out.println(); @@ -410,6 +407,44 @@ public class Dwarf { } } + /** + * + */ + class InitialLengthValue { + /** + * section length + */ + long length; + + /** + * section offset size in bytes. + */ + byte offsetSize; // + } + + /** + * Read section length field from the beginning of dwarf section. + * + *

See Chapter 7.4 32-Bit and 64-Bit DWARF Formats

+ * @param data - byte buffer positioned at the start of section length record + * @return section length info. Cannot be null. + * @throws IOException + */ + InitialLengthValue readInitialLengthField(ByteBuffer data) throws IOException { + InitialLengthValue info = new InitialLengthValue(); + info.length = read_4_bytes(data) & 0xffffffffL; + + if (info.length == 0xffffffffL) { + info.length = read_8_bytes(data); + info.offsetSize = 8; + } else if (info.length == 0) { // IRIX + info.length = read_8_bytes(data); + info.offsetSize = 8; + } else + info.offsetSize = 4; + return info; + } + Map parseDebugAbbreviation(CompilationUnitHeader header) throws IOException { Integer key = new Integer(header.abbreviationOffset); Map abbrevs = abbreviationMaps.get(key); 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 e784ec56d18..fd9ef9eaf6d 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 @@ -50,7 +50,7 @@ public class DwarfReader extends Dwarf implements ISymbolReader { private String[] m_fileNames = null; private boolean m_parsed = false; private final ArrayList m_parsedLineTableOffsets = new ArrayList(); - private int m_parsedLineTableSize = 0; + private long m_parsedLineTableSize = 0; public DwarfReader(String file) throws IOException { super(file); @@ -143,10 +143,11 @@ public class DwarfReader extends Dwarf implements ISymbolReader { /* Read line table header: * - * total_length: 4 bytes (excluding itself) + * total_length: 4/12 bytes (excluding itself) * version: 2 - * prologue length: 4 + * prologue length: 4/8 bytes (depending on section version) * minimum_instruction_len: 1 + * maximum_operations_per_instruction 1 - it is defined for version >= 4 * default_is_stmt: 1 * line_base: 1 * line_range: 1 @@ -156,20 +157,30 @@ public class DwarfReader extends Dwarf implements ISymbolReader { // Remember the CU line tables we've parsed. Integer cuOffset = new Integer(cuStmtList); + + boolean dwarf64Bit = false; if (! m_parsedLineTableOffsets.contains(cuOffset)) { m_parsedLineTableOffsets.add(cuOffset); - int length = read_4_bytes(data) + 4; - m_parsedLineTableSize += length + 4; + // Note the length does not including the "length" field(s) itself. + InitialLengthValue length = readInitialLengthField(data); + dwarf64Bit = length.offsetSize == 8; + m_parsedLineTableSize += length.length + (dwarf64Bit ? 12 : 4); } else { // Compiler like ARM RVCT may produce several CUs for the // same source files. return; } - + + short version = read_2_bytes(data); // Skip the following till "opcode_base" - data.position(data.position() + 10); + short skip_bytes = 8; + if (version >= 4) + skip_bytes += 1; // see maximum_operations_per_instruction + if (dwarf64Bit) + skip_bytes += 4; // see prologue length for 64-bit DWARF format + data.position(data.position() + skip_bytes); int opcode_base = data.get(); data.position(data.position() + opcode_base - 1); @@ -248,16 +259,16 @@ public class DwarfReader extends Dwarf implements ISymbolReader { /* * Line table header for one compile_unit: * - * total_length: 4 bytes (excluding itself) - * version: 2 - * prologue length: 4 + * total_length: 4/12 bytes (excluding itself) + * version: 2 + * prologue length: 4/8 bytes (depending on section version) * minimum_instruction_len: 1 - * default_is_stmt: 1 - * line_base: 1 - * line_range: 1 - * opcode_base: 1 - * standard_opcode_lengths: (value of opcode_base) - */ + * maximum_operations_per_instruction 1 - it is defined for version >= 4 + * 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 @@ -268,11 +279,15 @@ public class DwarfReader extends Dwarf implements ISymbolReader { 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); - + // Note the length does not including the "length" field(s) itself. + InitialLengthValue sectionLength = readInitialLengthField(data); + + // Record start of next CU line table - lineTableStart += tableLength + 4; + boolean dwarf64Bit = sectionLength.offsetSize == 8; + lineTableStart += (int)(sectionLength.length + (dwarf64Bit ? 12 : 4)); + + m_parsedLineTableSize += sectionLength.length + (dwarf64Bit ? 12 : 4); // According to Dwarf standard, the "tableLength" should cover the // the whole CU line table. But some compilers (e.g. ARM RVCT 2.2) @@ -289,14 +304,15 @@ public class DwarfReader extends Dwarf implements ISymbolReader { int savedPosition = data.position(); data.position(lineTableStart); - int ltLength = read_4_bytes(data); + long ltLength = dwarf64Bit ? read_8_bytes(data) : read_4_bytes(data); + int dwarfVer = read_2_bytes(data); - int minInstLengh = data.get(data.position() + 4); + int minInstLengh = data.get(data.position() + (dwarf64Bit ? 8 : 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. + dwarfVer > 0 && dwarfVer < 5 && // ver 5 is still draft at present. minInstLengh > 0 && minInstLengh <= 8; if (! dataValid) // padding exists ! @@ -309,8 +325,15 @@ public class DwarfReader extends Dwarf implements ISymbolReader { // current line table has already been parsed, skip it. continue; + short version = read_2_bytes(data); + // Skip following fields till "opcode_base" - data.position(data.position() + 10); + short skip_bytes = 8; + if (version >= 4) + skip_bytes += 1; // see maximum_operations_per_instruction + if (dwarf64Bit) + skip_bytes += 4; // see prologue length for 64-bit DWARF format + data.position(data.position() + skip_bytes); int opcode_base = data.get(); data.position(data.position() + opcode_base - 1);