/******************************************************************************* * Copyright (c) 2007, 2016 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 * Ling Wang (Nokia) bug 201000 * Serge Beauchamp (Freescale Semiconductor) - Bug 421070 * Red Hat Inc. - add debuginfo and macro section support *******************************************************************************/ package org.eclipse.cdt.utils.debug.dwarf; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.bind.DatatypeConverter; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.ICompileOptionsFinder; import org.eclipse.cdt.core.ISymbolReader; import org.eclipse.cdt.utils.coff.Coff.SectionHeader; import org.eclipse.cdt.utils.coff.PE; import org.eclipse.cdt.utils.debug.IDebugEntryRequestor; import org.eclipse.cdt.utils.elf.Elf; import org.eclipse.cdt.utils.elf.Elf.Section; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; /** * Light-weight parser of Dwarf2 data which is intended for getting only * source files that contribute to the given executable. */ public class DwarfReader extends Dwarf implements ISymbolReader, ICompileOptionsFinder { // These are sections that need be parsed to get the source file list. final static String[] DWARF_SectionsToParse = { DWARF_DEBUG_INFO, DWARF_DEBUG_LINE, DWARF_DEBUG_ABBREV, DWARF_DEBUG_STR, // this is optional. Some compilers don't generate it. DWARF_DEBUG_MACRO, }; final static String[] DWARF_ALT_SectionsToParse = { DWARF_DEBUG_STR, DWARF_DEBUG_MACRO }; private final Collection<String> m_fileCollection = new HashSet<String>(); private final Map<Long, String> m_stmtFileMap = new HashMap<Long, String>(); private final Map<String, ArrayList<String>> m_compileOptionsMap = new HashMap<String, ArrayList<String>>(); private String[] m_fileNames = null; private boolean m_parsed = false; private boolean m_macros_parsed = false; private final ArrayList<Integer> m_parsedLineTableOffsets = new ArrayList<Integer>(); private long m_parsedLineTableSize = 0; public DwarfReader(String file) throws IOException { super(file); } public DwarfReader(Elf exe) throws IOException { super(exe); } /** * @since 5.1 */ public DwarfReader(PE exe) throws IOException { super(exe); } // Override parent. // @Override public void init(Elf exe) throws IOException { Elf.ELFhdr header = exe.getELFhdr(); isLE = header.e_ident[Elf.ELFhdr.EI_DATA] == Elf.ELFhdr.ELFDATA2LSB; IPath debugInfoPath = new Path(exe.getFilename()); Elf.Section[] sections = exe.getSections(); boolean have_build_id = false; // Look for a special GNU build-id note which means the debug data resides in a separate // file with a name based on the build-id. for (Section section : sections) { if (section.sh_type == Elf.Section.SHT_NOTE) { ByteBuffer data = section.mapSectionData(); if (data.remaining() > 12) { try { // Read .note section, looking to see if it is named "GNU" and is of GNU_BUILD_ID type @SuppressWarnings("unused") int name_sz = read_4_bytes(data); int data_sz = read_4_bytes(data); int note_type = read_4_bytes(data); String noteName = readString(data); String buildId = null; if (noteName.equals("GNU") && note_type == Elf.Section.NT_GNU_BUILD_ID) { //$NON-NLS-1$ // We have the special GNU build-id note section. Skip over the name to // a 4-byte boundary. byte[] byteArray = new byte[data_sz]; while ((data.position() & 0x3) != 0) data.get(); int i = 0; // Read in the hex bytes from the note section's data. while (data.hasRemaining() && data_sz-- > 0) { byteArray[i++] = data.get(); } // The build-id location is taken by converting the binary bytes to hex string. // The first byte is used as a directory specifier (e.g. 51/a4578fe2). String bName = DatatypeConverter.printHexBinary(byteArray).toLowerCase(); buildId = bName.substring(0, 2) + "/" + bName.substring(2) + ".debug"; //$NON-NLS-1$ //$NON-NLS-2$ // The build-id file should be in the special directory /usr/lib/debug/.build-id IPath buildIdPath = new Path("/usr/lib/debug/.build-id").append(buildId); //$NON-NLS-1$ File buildIdFile = buildIdPath.toFile(); if (buildIdFile.exists()) { // if the debug file exists from above, open it and get the section info from it Elf debugInfo = new Elf(buildIdFile.getCanonicalPath()); sections = debugInfo.getSections(); have_build_id = true; debugInfoPath = new Path(buildIdFile.getCanonicalPath()).removeLastSegments(1); break; } } } catch (Exception e) { e.printStackTrace(); CCorePlugin.log(e); } } } } if (!have_build_id) { // No build-id. Look for a .gnu_debuglink section which will have the name of the debug info file Elf.Section gnuDebugLink = exe.getSectionByName(DWARF_GNU_DEBUGLINK); if (gnuDebugLink != null) { ByteBuffer data = gnuDebugLink.mapSectionData(); if (data != null) { // we have non-empty debug info link try { // name is zero-byte terminated character string String debugName = ""; //$NON-NLS-1$ if (data.hasRemaining()) { int c; StringBuilder sb = new StringBuilder(); while ((c = data.get()) != -1) { if (c == 0) { break; } sb.append((char) c); } debugName = sb.toString(); } if (debugName.length() > 0) { // try and open the debug info from 3 separate places in order File debugFile = null; IPath exePath = new Path(exe.getFilename()); IPath p = exePath.removeLastSegments(1); // 1. try and open the file in the same directory as the executable debugFile = p.append(debugName).toFile(); if (!debugFile.exists()) { // 2. try and open the file in the .debug directory where the executable is debugFile = p.append(".debug").append(debugName).toFile(); //$NON-NLS-1$ if (!debugFile.exists()) // 3. try and open /usr/lib/debug/$(EXE_DIR)/$(DEBUGINFO_NAME) debugFile = new Path("/usr/lib/debug").append(p).append(debugName).toFile(); //$NON-NLS-1$ } if (debugFile.exists()) { // if the debug file exists from above, open it and get the section info from it Elf debugInfo = new Elf(debugFile.getCanonicalPath()); sections = debugInfo.getSections(); debugInfoPath = new Path(debugFile.getCanonicalPath()).removeLastSegments(1); } } } catch (Exception e) { e.printStackTrace(); CCorePlugin.log(e); } } } } // Read in sections (and only the sections) we care about. // for (Section section : sections) { String name = section.toString(); if (name.equals(DWARF_GNU_DEBUGALTLINK)) { ByteBuffer data = section.mapSectionData(); try { // name is zero-byte terminated character string String altInfoName = readString(data); if (altInfoName.length() > 0) { IPath altPath = new Path(altInfoName); if (!altPath.isAbsolute()) { altPath = debugInfoPath.append(altPath); } File altFile = altPath.toFile(); if (altFile.exists()) { Elf altInfo = new Elf(altFile.getCanonicalPath()); Elf.Section[] altSections = altInfo.getSections(); for (Section altSection : altSections) { String altName = altSection.toString(); for (String element : DWARF_ALT_SectionsToParse) { if (altName.equals(element)) { try { dwarfAltSections.put(element, altSection.mapSectionData()); } catch (Exception e) { e.printStackTrace(); CCorePlugin.log(e); } } } } } } } catch (Exception e) { e.printStackTrace(); CCorePlugin.log(e); } } else { for (String element : DWARF_SectionsToParse) { if (name.equals(element)) { // catch out of memory exceptions which might happen trying to // load large sections (like .debug_info). not a fix for that // problem itself, but will at least continue to load the other // sections. try { dwarfSections.put(element, section.mapSectionData()); } catch (Exception e) { CCorePlugin.log(e); } } } } } // Don't print during parsing. printEnabled = false; m_parsed = false; } @Override public void init(PE exe) throws IOException { isLE = true; SectionHeader[] sections = exe.getSectionHeaders(); for (int i = 0; i < sections.length; i++) { String name = new String(sections[i].s_name).trim(); if (name.startsWith("/")) //$NON-NLS-1$ { int stringTableOffset = Integer.parseInt(name.substring(1)); name = exe.getStringTableEntry(stringTableOffset); } for (String element : Dwarf.DWARF_SCNNAMES) { if (name.equals(element)) { try { dwarfSections.put(element, sections[i].mapSectionData()); } catch (Exception e) { CCorePlugin.log(e); } } } } // Don't print during parsing. printEnabled = false; m_parsed = false; } /* * Parse line table data of a compilation unit to get names of all source files * that contribute to the compilation unit. */ void parseSourceInCULineInfo( String cuCompDir, // compilation directory of the CU int cuStmtList) // offset of the CU line table in .debug_line section { ByteBuffer data = dwarfSections.get(DWARF_DEBUG_LINE); if (data != null) { try { data.position(cuStmtList); /* Read line table header: * * total_length: 4/12 bytes (excluding itself) * version: 2 * 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 * opcode_base: 1 * standard_opcode_lengths: (value of opcode_base) */ // Remember the CU line tables we've parsed. Integer cuOffset = Integer.valueOf(cuStmtList); boolean dwarf64Bit = false; if (! m_parsedLineTableOffsets.contains(cuOffset)) { m_parsedLineTableOffsets.add(cuOffset); // 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" 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); // Read in directories. // ArrayList<String> dirList = new ArrayList<String>(); // Put the compilation directory of the CU as the first dir dirList.add(cuCompDir); 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 = new Path(str); if(!dir.isAbsolute()) dir = new Path(cuCompDir).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); addSourceFile(dirList.get((int)leb128), fileName); // Skip the followings // // modification time leb128 = read_unsigned_leb128(data); // file size in bytes leb128 = read_unsigned_leb128(data); } } catch (IOException e) { e.printStackTrace(); } } } /* * 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() { ByteBuffer data = dwarfSections.get(DWARF_DEBUG_LINE); if (data == null) return; int sectionSize = data.capacity(); 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/12 bytes (excluding itself) * version: 2 * 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 * opcode_base: 1 * standard_opcode_lengths: (value of opcode_base) */ int lineTableStart = 0; // offset in the .debug_line section try { while (lineTableStart < sectionSize - minHeaderSize) { data.position(lineTableStart); Integer currLineTableStart = Integer.valueOf(lineTableStart); // Read length of the line table for one compile unit // Note the length does not including the "length" field(s) itself. InitialLengthValue sectionLength = readInitialLengthField(data); // Record start of next CU line table 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) // 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 savedPosition = data.position(); data.position(lineTableStart); long ltLength = dwarf64Bit ? read_8_bytes(data) : read_4_bytes(data); int dwarfVer = read_2_bytes(data); 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 < 5 && // ver 5 is still draft at present. minInstLengh > 0 && minInstLengh <= 8; if (! dataValid) // padding exists ! lineTableStart = (lineTableStart+3) & ~0x3; data.position(savedPosition); } if (m_parsedLineTableOffsets.contains(currLineTableStart)) // current line table has already been parsed, skip it. continue; short version = read_2_bytes(data); // Skip following fields till "opcode_base" 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); // Read in directories. // ArrayList<String> dirList = new ArrayList<String>(); String str, fileName; // first dir should be TAG_comp_dir from CU, which we don't have here. dirList.add(""); //$NON-NLS-1$ while (true) { str = readString(data); if (str.length() == 0) break; dirList.add(str); } // Read file names // long leb128; while (true) { fileName = readString(data); if (fileName.length() == 0) // no more file entry break; // dir index. Note "0" is reserved for compilation directory. leb128 = read_unsigned_leb128(data); addSourceFile(dirList.get((int) leb128), fileName); // Skip the followings // // modification time leb128 = read_unsigned_leb128(data); // file size in bytes leb128 = read_unsigned_leb128(data); } } } catch (IOException e) { e.printStackTrace(); return; } } @Override public String[] getSourceFiles() { if (!m_parsed) { m_fileCollection.clear(); getSourceFilesFromDebugInfoSection(); getSourceFilesFromDebugLineSection(); m_parsed = true; m_fileNames = new String[m_fileCollection.size()]; m_fileCollection.toArray(m_fileNames); } 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 String addSourceFileWithStmt(String dir, String name, int stmt) { String fullName = addSourceFile(dir, name); m_stmtFileMap.put(Long.valueOf(stmt), fullName); return fullName; } private String addSourceFile(String dir, String name) { if (name == null || name.length() == 0) return null; if (name.charAt(0) == '<') // don't count the entry "<internal>" from GCCE compiler return null; String fullName = 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); // This convert the path to canonical path (but not necessarily absolute, which // is different from java.io.File.getCanonicalPath()). fullName = pa.toOSString(); if (!m_fileCollection.contains(fullName)) m_fileCollection.add(fullName); return fullName; } // Override parent: only handle TAG_Compile_Unit. @Override void processDebugInfoEntry(IDebugEntryRequestor requestor, AbbreviationEntry entry, List<Dwarf.AttributeValue> list) { int tag = (int) entry.tag; switch (tag) { case DwarfConstants.DW_TAG_compile_unit : processCompileUnit(requestor, list); break; default: break; } } // Override parent. // Just get the file name of the CU. // Argument "requestor" is ignored. @Override void processCompileUnit(IDebugEntryRequestor requestor, List<AttributeValue> list) { String cuName, cuCompDir; int stmtList = -1; cuName = cuCompDir = ""; //$NON-NLS-1$ for (int i = 0; i < list.size(); i++) { AttributeValue av = list.get(i); try { int name = (int)av.attribute.name; switch(name) { case DwarfConstants.DW_AT_name: cuName = (String)av.value; break; case DwarfConstants.DW_AT_comp_dir: cuCompDir = (String)av.value; break; case DwarfConstants.DW_AT_stmt_list: stmtList = ((Number)av.value).intValue(); break; default: break; } } catch (ClassCastException e) { } } addSourceFileWithStmt(cuCompDir, cuName, stmtList); if (stmtList > -1) // this CU has "stmt_list" attribute parseSourceInCULineInfo(cuCompDir, stmtList); } /** * @since 5.2 */ @Override public String[] getSourceFiles(IProgressMonitor monitor) { return getSourceFiles(); } private class OpcodeInfo { private int numArgs; private final boolean offset_size_8; ArrayList<Integer> argTypes; public OpcodeInfo (boolean offset_size_8) { this.offset_size_8 = offset_size_8; } public void setNumArgs (int numArgs) { this.numArgs = numArgs; } public void addArgType(int argType) { argTypes.add(Integer.valueOf(argType)); } public void readPastEntry(ByteBuffer data) { for (int i = 0; i < numArgs; ++i) { int argType = argTypes.get(i).intValue(); switch (argType) { case DwarfConstants.DW_FORM_flag: case DwarfConstants.DW_FORM_data1: data.get(); break; case DwarfConstants.DW_FORM_data2: data.getShort(); break; case DwarfConstants.DW_FORM_data4: data.getInt(); break; case DwarfConstants.DW_FORM_data8: data.getLong(); break; case DwarfConstants.DW_FORM_sdata: case DwarfConstants.DW_FORM_udata: try { read_signed_leb128(data); } catch (IOException e) { e.printStackTrace(); } break; case DwarfConstants.DW_FORM_block: { try { long off = read_signed_leb128(data); data.position((int)(data.position() + off)); } catch (IOException e) { e.printStackTrace(); } } break; case DwarfConstants.DW_FORM_block1: { int off = data.get(); data.position(data.position() + off + 1); } break; case DwarfConstants.DW_FORM_block2: { int off = data.getShort(); data.position(data.position() + off + 2); } break; case DwarfConstants.DW_FORM_block4: { int off = data.getInt(); data.position(data.position() + off + 4); } break; case DwarfConstants.DW_FORM_string: while (data.get() != 0) { // loop until we find 0 byte } break; case DwarfConstants.DW_FORM_strp: case DwarfConstants.DW_FORM_GNU_strp_alt: case DwarfConstants.DW_FORM_GNU_ref_alt: case DwarfConstants.DW_FORM_sec_offset: if (offset_size_8) data.getLong(); else data.getInt(); break; } } } } // Convert a macro to its command line form. private String getCommandLineMacro(String macro) { String commandLineMacro = "-D" + macro; //$NON-NLS-1$ commandLineMacro = commandLineMacro.replaceFirst(" ", "="); //$NON-NLS-1$ //$NON-NLS-2$ return commandLineMacro; } // Go through regular and alt macro sections and find any macros that were set on the // compilation command line (e.g. gcc -Dflagx=1 my.c). Built-in macros and macros that // are set within files (source and include) are ignored since they can and will be // discovered by indexing. private void getCommandMacrosFromMacroSection() { ByteBuffer data = dwarfSections.get(DWARF_DEBUG_MACRO); ByteBuffer str = dwarfSections.get(DWARF_DEBUG_STR); ByteBuffer altdata = dwarfAltSections.get(DWARF_DEBUG_MACRO); ByteBuffer altstr = dwarfAltSections.get(DWARF_DEBUG_STR); Set<String> fixupList = new HashSet<String>(); Set<String> fixupAltList = new HashSet<String>(); boolean DEBUG = false; if (data == null) return; HashMap <Long, ArrayList<String>> t_macros = new HashMap<Long, ArrayList<String>>(); HashMap <Long, ArrayList<String>> t_alt_macros = new HashMap<Long, ArrayList<String>>(); // Parse the macro section, looking for command-line macros meant for compiling files (i.e. // not internal macro definitions in headers or C/C++ files. Keep track of any forward // references to fix-up later when we have a complete list of command-line macros. parseMacroSection(data, str, altstr, fixupList, fixupAltList, "=FIXUP=", DEBUG, t_macros); //$NON-NLS-1$ // Check if there is an alternate macro section. If there is, parse the alternate section, but any references // found there should be considered referring to the alternate string and macro sections. // All forward reference fix-ups should be put on the alternate fix-up list. if (altdata != null) { if (DEBUG) System.out.println("Processing Alternate Macro Section"); //$NON-NLS-1$ parseMacroSection(altdata, altstr, altstr, fixupAltList, fixupAltList, "=FIXUPALT=", DEBUG, t_alt_macros); //$NON-NLS-1$ } // Fix up all forward references from transparent includes fixupMacros(fixupList, "=FIXUP=", DEBUG, t_macros); //$NON-NLS-1$ // Fix up all forward references from transparent alt includes if (DEBUG) System.out.println("Fix up forward references in alternate macro section"); //$NON-NLS-1$ fixupMacros(fixupAltList, "=FIXUPALT=", DEBUG, t_alt_macros); //$NON-NLS-1$ } // Fix up forward references made by transparent includes now that a complete macro list has been retrieved. private void fixupMacros(Set<String> fixupList, String fixupMarker, boolean DEBUG, HashMap<Long, ArrayList<String>> t_macros) { for (String name: fixupList) { ArrayList<String> macros = m_compileOptionsMap.get(name); for (int i = 0; i < macros.size(); ++i) { String macroLine = macros.get(i); if (macroLine.startsWith(fixupMarker)) { Long offset = Long.valueOf(macroLine.substring(7)); if (DEBUG) System.out.println("Found fixup needed for offset: " + offset + " for file: " + name); //$NON-NLS-1$ //$NON-NLS-2$ ArrayList<String> insertMacros = t_macros.get(offset); if (DEBUG) System.out.println("insert macros are: " + insertMacros.toString()); //$NON-NLS-1$ macros.remove(i); macros.addAll(i, insertMacros); i += insertMacros.size(); } } m_compileOptionsMap.put(name, macros); // replace updated list } } // Parse a macro section, looking for command-line macros that are used as flags to compile source files. // Keep track of any forward references to macros not yet defined in the file. We will later // fix-up these references when we have seen all command-line macros for the file. private void parseMacroSection(ByteBuffer data, ByteBuffer str, ByteBuffer altstr, Set<String> fixupList, Set<String> fixupAltList, String fixupMarker, boolean DEBUG, HashMap<Long, ArrayList<String>> t_macros) { byte op; while (data.hasRemaining()) { try { int original_position = data.position(); int type = read_2_bytes(data); byte flags = data.get(); boolean offset_size_8; long lt_offset = -1; String fileName = null; HashMap<Integer, OpcodeInfo> opcodeInfos = null; if (DEBUG) System.out.println("type is " + type); //$NON-NLS-1$ // bottom bit 0 tells us whether we have 8 byte offsets or 4 byte offsets offset_size_8 = (flags & 0x1) == 1; if (DEBUG) System.out.println("offset size is " + (offset_size_8 ? 8 : 4)); //$NON-NLS-1$ // bit 1 indicates we have an offset from the start of .debug_line section if ((flags & 0x2) != 0) { lt_offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data)); fileName = m_stmtFileMap.get(Long.valueOf(lt_offset)); if (DEBUG) System.out.println("debug line offset is " + lt_offset); //$NON-NLS-1$ } // if bit 2 flag is on, then we have a macro entry table which may // have non-standard entry types defined which we need to know when // we come across macro entries later if ((flags & 0x4) != 0) { opcodeInfos = new HashMap<Integer, OpcodeInfo>(); int num_opcodes = data.get(); for (int i = 0; i < num_opcodes; ++i) { OpcodeInfo info = new OpcodeInfo(offset_size_8); int opcode = data.get(); long numArgs = read_unsigned_leb128(data); info.setNumArgs((int)numArgs); for (int j = 0; j < numArgs; ++j) { int argType = data.get(); info.addArgType(argType); } opcodeInfos.put(Integer.valueOf(opcode), info); } } ArrayList<String> macros = new ArrayList<String>(); boolean done = false; while (!done) { op = data.get(); switch (op) { case DwarfConstants.DW_MACRO_start_file: { long filenum; long lineno; lineno = read_signed_leb128(data); filenum = read_signed_leb128(data); // All command line macros are defined as being included before start of file if (filenum == 1 && lt_offset >= 0) { // we have a source file so add all macros defined before it with lineno 0 m_compileOptionsMap.put(fileName, macros); if (DEBUG) System.out.println("following macros found for file " + macros.toString()); //$NON-NLS-1$ macros = new ArrayList<String>(); } if (fileName != null) if (DEBUG) System.out.println(" DW_MACRO_start_file - lineno: " + lineno + " filenum: " //$NON-NLS-1$ //$NON-NLS-2$ + filenum + " " + fileName); //$NON-NLS-1$ else if (DEBUG) System.out.println(" DW_MACRO_start_file - lineno: " + lineno + " filenum: " //$NON-NLS-1$ //$NON-NLS-2$ + filenum); } break; case DwarfConstants.DW_MACRO_end_file: { if (DEBUG) System.out.println(" DW_MACRO_end_file"); //$NON-NLS-1$ } break; case DwarfConstants.DW_MACRO_define: { long lineno; String string; lineno = read_signed_leb128(data); string = readString(data); if (lineno == 0) macros.add(getCommandLineMacro(string)); if (DEBUG) System.out.println(" DW_MACRO_define - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$ + string); } break; case DwarfConstants.DW_MACRO_undef: { long lineno; String macro; lineno = read_signed_leb128(data); macro = readString(data); if (DEBUG) System.out.println(" DW_MACRO_undef - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$ + macro); } break; case DwarfConstants.DW_MACRO_define_indirect: { long lineno; long offset; lineno = read_signed_leb128(data); offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data)); str.position((int)offset); String macro = readString(str); if (lineno == 0) macros.add(getCommandLineMacro(macro)); if (DEBUG) System.out.println(" DW_MACRO_define_indirect - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$ + macro); } break; case DwarfConstants.DW_MACRO_define_indirect_alt: { long lineno; long offset; lineno = read_signed_leb128(data); offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data)); altstr.position((int)offset); String macro = readString(altstr); if (lineno == 0) macros.add(getCommandLineMacro(macro)); if (DEBUG) System.out.println(" DW_MACRO_define_indirect_alt - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$ + macro); } break; case DwarfConstants.DW_MACRO_undef_indirect: { long lineno; long offset; String macro; lineno = read_signed_leb128(data); offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data)); str.position((int)offset); macro = readString(str); if (DEBUG) System.out.println(" DW_MACRO_undef_indirect - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$ + macro); } break; case DwarfConstants.DW_MACRO_undef_indirect_alt: { long lineno; long offset; String macro; lineno = read_signed_leb128(data); offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data)); altstr.position((int)offset); macro = readString(altstr); if (DEBUG) System.out.println(" DW_MACRO_undef_indirect_alt - lineno : " + lineno + " macro : " //$NON-NLS-1$ //$NON-NLS-2$ + macro); } break; case DwarfConstants.DW_MACRO_transparent_include: { long offset; offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data)); ArrayList<String> foundMacros = t_macros.get(Long.valueOf(offset)); if (foundMacros != null) macros.addAll(foundMacros); else if (lt_offset >= 0) { macros.add(fixupMarker + offset); // leave a marker we can fix up later if (DEBUG) System.out.println("Adding fixup for offset: " + offset + " for file: " + fileName); //$NON-NLS-1$ //$NON-NLS-2$ fixupList.add(fileName); } if (DEBUG) System.out.println(" DW_MACRO_transparent_include - offset : " + offset); //$NON-NLS-1$ } break; case DwarfConstants.DW_MACRO_transparent_include_alt: { long offset; offset = (offset_size_8 ? read_8_bytes(data) : read_4_bytes(data)); if (lt_offset >= 0) { macros.add("=FIXUPALT=" + offset); // leave a marker we can fix up later //$NON-NLS-1$ if (DEBUG) System.out.println("Adding alt fixup for offset: " + offset + " for file: " + fileName); //$NON-NLS-1$ //$NON-NLS-2$ fixupAltList.add(fileName); } if (DEBUG) System.out.println(" DW_MACRO_transparent_include - offset : " + offset); //$NON-NLS-1$ } break; case DwarfConstants.DW_MACRO_end: { if (lt_offset < 0) { if (DEBUG) System.out.println("creating transparent include macros for offset: " + original_position); //$NON-NLS-1$ t_macros.put(Long.valueOf(original_position), macros); } done = true; } break; default: { if (opcodeInfos != null) { OpcodeInfo info = opcodeInfos.get(op); info.readPastEntry(data); } } break; } } } catch (IOException e) { // do nothing } } } /** * Get the set of command line flags used for a particular file name. * * @param fileName - name of file * @return string containing all macros used on command line to compile the file * * @since 5.7 */ @Override public String getCompileOptions(String fileName) { if (!m_macros_parsed) { getSourceFiles(); getCommandMacrosFromMacroSection(); m_macros_parsed = true; } ArrayList<String>macros = m_compileOptionsMap.get(fileName); if (macros == null) return ""; //$NON-NLS-1$ StringBuilder sb = new StringBuilder(); for (String option: macros) { sb.append(option); sb.append(" "); //$NON-NLS-1$ } return sb.toString(); } }