From 12a2ba4b3fe350e40cc8f1ff7e51f66dda9cd770 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Wed, 12 Feb 2014 17:57:17 -0500 Subject: [PATCH] Add GNU debug-info support to DwarfReader/Dwarf - Add support to find separate debug info file using the .gnu_debuglink section - Add support to also find the GNU alt debug info file created by the DWZ utility - Add additional DW_FORMs to support alt data - Add support to DwarfReader to find the special note section containing the build-id and then see if it can find the debug-info with that build-id Change-Id: I0e43ba8af12396cdab4e085ad0c20fdec8c1d83e Reviewed-on: https://git.eclipse.org/r/24034 Tested-by: Hudson CI Reviewed-by: Marc-Andre Laperle Tested-by: Marc-Andre Laperle Reviewed-by: Jeff Johnston Tested-by: Jeff Johnston --- .../cdt/core/ICompileOptionsFinder.java | 30 + .../eclipse/cdt/utils/debug/dwarf/Dwarf.java | 172 ++++- .../cdt/utils/debug/dwarf/DwarfConstants.java | 86 ++- .../cdt/utils/debug/dwarf/DwarfReader.java | 628 ++++++++++++++++-- .../utils/org/eclipse/cdt/utils/elf/Elf.java | 11 +- 5 files changed, 866 insertions(+), 61 deletions(-) create mode 100644 core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ICompileOptionsFinder.java diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ICompileOptionsFinder.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ICompileOptionsFinder.java new file mode 100644 index 00000000000..2d1a4eb7b19 --- /dev/null +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/ICompileOptionsFinder.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat Inc. 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: + * Red Hat Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.core; + +/** + * A class that can find compiler options for a given file name. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @since 5.7 + */ +public interface ICompileOptionsFinder { + + /** + * Get compiler options for a given file name. + * + * @param fileName - absolute source file name + * @return a String containing the compiler options used or null. + * + */ + public String getCompileOptions(String fileName); +} 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 e54140e38eb..e2c23dab516 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 @@ -13,6 +13,7 @@ package org.eclipse.cdt.utils.debug.dwarf; +import java.io.File; import java.io.IOException; import java.lang.reflect.Array; import java.nio.ByteBuffer; @@ -30,6 +31,8 @@ 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.elf.Elf.Section; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Path; public class Dwarf { @@ -48,6 +51,11 @@ public class Dwarf { final static String DWARF_DEBUG_VARNAMES = ".debug_varnames"; //$NON-NLS-1$ final static String DWARF_DEBUG_WEAKNAMES = ".debug_weaknames"; //$NON-NLS-1$ final static String DWARF_DEBUG_MACINFO = ".debug_macinfo"; //$NON-NLS-1$ + final static String DWARF_DEBUG_MACRO = ".debug_macro"; //$NON-NLS-1$ + final static String DWARF_DEBUG_TYPES = ".debug_types"; //$NON-NLS-1$ + final static String DWARF_GNU_DEBUGLINK = ".gnu_debuglink"; //$NON-NLS-1$ + final static String DWARF_GNU_DEBUGALTLINK = ".gnu_debugaltlink"; //$NON-NLS-1$ + final static String[] DWARF_SCNNAMES = { DWARF_DEBUG_INFO, @@ -65,6 +73,14 @@ public class Dwarf { DWARF_DEBUG_WEAKNAMES, DWARF_DEBUG_MACINFO }; + final static String[] DWARF_ALT_SCNNAMES = + { + DWARF_DEBUG_INFO, + DWARF_DEBUG_TYPES, + DWARF_DEBUG_MACRO, + DWARF_DEBUG_STR, + }; + class CompilationUnitHeader { long length; short version; @@ -166,6 +182,7 @@ public class Dwarf { } Map dwarfSections = new HashMap(); + Map dwarfAltSections = new HashMap(); Map> abbreviationMaps = new HashMap>(); boolean isLE; @@ -196,15 +213,86 @@ public class Dwarf { isLE = header.e_ident[Elf.ELFhdr.EI_DATA] == Elf.ELFhdr.ELFDATA2LSB; Elf.Section[] sections = exe.getSections(); + IPath debugInfoPath = new Path(exe.getFilename()); + // 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 = readString(data); + if (debugName.length() > 0) { + // try and open the debug info from 3 separate places in order + File debugFile = null; + IPath p = debugInfoPath.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/$(EXEPATH)/$(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()); + } + } + } catch (Exception e) { + e.printStackTrace(); + CCorePlugin.log(e); + } + } + + } for (Section section : sections) { String name = section.toString(); - for (String element : DWARF_SCNNAMES) { - if (name.equals(element)) { - try { - dwarfSections.put(element, section.mapSectionData()); - } catch (Exception e) { - e.printStackTrace(); - CCorePlugin.log(e); + 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_SCNNAMES) { + 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_SCNNAMES) { + if (name.equals(element)) { + try { + dwarfSections.put(element, section.mapSectionData()); + } catch (Exception e) { + e.printStackTrace(); + CCorePlugin.log(e); + } } } } @@ -463,7 +551,8 @@ public class Dwarf { byte hasChildren = data.get(); AbbreviationEntry entry = new AbbreviationEntry(code, tag, hasChildren); - //System.out.println("\tAbrev Entry: " + code + " " + Long.toHexString(entry.tag) + " " + entry.hasChildren); + if (printEnabled) + System.out.println("\tAbrev Entry: " + code + " " + Long.toHexString(entry.tag) + " " + entry.hasChildren); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ // attributes long name = 0; @@ -474,7 +563,8 @@ public class Dwarf { if (name != 0) { entry.attributes.add(new Attribute(name, form)); } - //System.out.println("\t\t " + Long.toHexString(name) + " " + Long.toHexString(value)); + if (printEnabled) + System.out.println("\t\t " + Long.toHexString(name) + " " + Long.toHexString(form)); //$NON-NLS-1$ //$NON-NLS-2$ } while (name != 0 && form != 0); abbrevs.put(new Long(code), entry); } @@ -505,6 +595,8 @@ public class Dwarf { } } + int oldForm = 0; + Object readAttribute(int form, ByteBuffer in, CompilationUnitHeader header) throws IOException { Object obj = null; switch (form) { @@ -621,6 +713,34 @@ public class Dwarf { } break; + case DwarfConstants.DW_FORM_GNU_strp_alt : + { + long offset; + if (header.offsetSize == 8) + offset = read_8_bytes(in); + else + offset = read_4_bytes(in) & 0xffffffffL; + + ByteBuffer data = dwarfAltSections.get(DWARF_DEBUG_STR); + if (data == null) { + obj = new String(); + } else if (offset < 0 || offset > data.capacity()) { + obj = new String(); + } else { + StringBuffer sb = new StringBuffer(); + data.position((int) offset); + while (data.hasRemaining()) { + byte c = data.get(); + if (c == 0) { + break; + } + sb.append((char) c); + } + obj = sb.toString(); + } + } + break; + case DwarfConstants.DW_FORM_ref1 : obj = new Byte(in.get()); break; @@ -646,6 +766,12 @@ public class Dwarf { int f = (int) read_unsigned_leb128(in); return readAttribute(f, in, header); } + case DwarfConstants.DW_FORM_GNU_ref_alt : + if (header.offsetSize == 8) + obj = new Long(read_8_bytes(in)); + else + obj = new Long(read_4_bytes(in) & 0xffffffffL); + break; case DwarfConstants.DW_FORM_sec_offset : if (header.offsetSize == 8) obj = new Long(read_8_bytes(in)); @@ -667,9 +793,14 @@ public class Dwarf { break; default : - break; + if (printEnabled) { + System.out.println("Default for " + form); //$NON-NLS-1$ + System.out.println("Last form is " + oldForm); //$NON-NLS-1$ + } + break; } + oldForm = form; return obj; } @@ -775,6 +906,27 @@ public class Dwarf { return new Long(value); } + /** + * Read a null-ended string from the given "data" stream. + * data : IN, byte buffer + */ + String readString(ByteBuffer data) + { + String str; + + StringBuffer sb = new StringBuffer(); + while (data.hasRemaining()) { + byte c = data.get(); + if (c == 0) { + break; + } + sb.append((char) c); + } + + str = sb.toString(); + return str; + } + void processSubProgram(IDebugEntryRequestor requestor, List list) { long lowPC = 0; long highPC = 0; diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/DwarfConstants.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/DwarfConstants.java index b0ce4f2f42a..bad355d53b7 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/DwarfConstants.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/debug/dwarf/DwarfConstants.java @@ -65,6 +65,10 @@ public class DwarfConstants { public final static int DW_TAG_variant_part = 0x33; public final static int DW_TAG_variable = 0x34; public final static int DW_TAG_volatile_type = 0x35; + /** + * @since 5.7 + */ + public final static int DW_TAG_partial_unit = 0x3c; public final static int DW_TAG_lo_user = 0x4080; public final static int DW_TAG_MIPS_loop = 0x4081; public final static int DW_TAG_format_label = 0x4101; @@ -187,22 +191,42 @@ public class DwarfConstants { public final static int DW_FORM_ref8 = 0x14; public final static int DW_FORM_ref_udata = 0x15; public final static int DW_FORM_indirect = 0x16; + /** - * @since 5.6 + * @since 5.7 */ public final static int DW_FORM_sec_offset = 0x17; /** - * @since 5.6 + * @since 5.7 */ public final static int DW_FORM_exprloc = 0x18; /** - * @since 5.6 + * @since 5.7 */ public final static int DW_FORM_flag_present = 0x19; /** - * @since 5.6 + * @since 5.7 */ public final static int DW_FORM_ref_sig8 = 0x20; + /* Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission. */ + /** + * @since 5.7 + */ + public final static int DW_FORM_GNU_addr_index = 0x1f01; + /** + * @since 5.7 + */ + public final static int DW_FORM_GNU_str_index = 0x1f02; + /* Extensions for DWZ multifile. + See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open . */ + /** + * @since 5.7 + */ + public final static int DW_FORM_GNU_ref_alt = 0x1f20; + /** + * @since 5.7 + */ + public final static int DW_FORM_GNU_strp_alt = 0x1f21; /* DWARF location operation encodings. */ public final static int DW_OP_addr = 0x03; /* Constant address. */ @@ -459,6 +483,60 @@ public class DwarfConstants { public final static int DW_MACINFO_end_file = 4; public final static int DW_MACINFO_vendor_ext = 255; + /* DWARF macro type encodings. */ + /** + * @since 5.7 + */ + public final static int DW_MACRO_end = 0; + /** + * @since 5.7 + */ + public final static int DW_MACRO_define = 1; + /** + * @since 5.7 + */ + public final static int DW_MACRO_undef = 2; + /** + * @since 5.7 + */ + public final static int DW_MACRO_start_file = 3; + /** + * @since 5.7 + */ + public final static int DW_MACRO_end_file = 4; + /** + * @since 5.7 + */ + public final static int DW_MACRO_define_indirect = 5; + /** + * @since 5.7 + */ + public final static int DW_MACRO_undef_indirect = 6; + /** + * @since 5.7 + */ + public final static int DW_MACRO_transparent_include = 7; + /** + * @since 5.7 + */ + public final static int DW_MACRO_define_indirect_alt = 0x08; + /** + * @since 5.7 + */ + public final static int DW_MACRO_undef_indirect_alt = 0x09; + /** + * @since 5.7 + */ + public final static int DW_MACRO_transparent_include_alt = 0x0a; + /** + * @since 5.7 + */ + public final static int DW_MACRO_lo_user = 0xe0; + /** + * @since 5.7 + */ + public final static int DW_MACRO_hi_user = 0xff; + /* DWARF call frame instruction encodings. */ public final static int DW_CFA_advance_loc = 0x40; public final static int DW_CFA_offset = 0x80; 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 fd9ef9eaf6d..c52462e62b9 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 @@ -9,18 +9,26 @@ * 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; @@ -35,20 +43,26 @@ 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 { +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. - }; + 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 m_fileCollection = new HashSet(); + private final Map m_stmtFileMap = new HashMap(); + private final Map> m_compileOptionsMap = new HashMap>(); private String[] m_fileNames = null; private boolean m_parsed = false; + private boolean m_macros_parsed = false; private final ArrayList m_parsedLineTableOffsets = new ArrayList(); private long m_parsedLineTableSize = 0; @@ -74,22 +88,159 @@ public class DwarfReader extends Dwarf implements ISymbolReader { 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(); + 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; + StringBuffer sb = new StringBuffer(); + 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(); - 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); + 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); + } } } } @@ -411,13 +562,19 @@ public class DwarfReader extends Dwarf implements ISymbolReader { parse(null); } - private void addSourceFile(String dir, String name) + 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; + return null; if (name.charAt(0) == '<') // don't count the entry "" from GCCE compiler - return; + return null; String fullName = name; @@ -433,30 +590,11 @@ public class DwarfReader extends Dwarf implements ISymbolReader { fullName = pa.toOSString(); if (!m_fileCollection.contains(fullName)) - m_fileCollection.add(fullName); + m_fileCollection.add(fullName); + + return fullName; } - /** - * Read a null-ended string from the given "data" stream. - * data : IN, byte buffer - */ - String readString(ByteBuffer data) - { - String str; - - StringBuffer sb = new StringBuffer(); - while (data.hasRemaining()) { - byte c = data.get(); - if (c == 0) { - break; - } - sb.append((char) c); - } - - str = sb.toString(); - return str; - } - // Override parent: only handle TAG_Compile_Unit. @Override void processDebugInfoEntry(IDebugEntryRequestor requestor, AbbreviationEntry entry, List list) { @@ -502,7 +640,7 @@ public class DwarfReader extends Dwarf implements ISymbolReader { } } - addSourceFile(cuCompDir, cuName); + addSourceFileWithStmt(cuCompDir, cuName, stmtList); if (stmtList > -1) // this CU has "stmt_list" attribute parseSourceInCULineInfo(cuCompDir, stmtList); } @@ -515,4 +653,406 @@ public class DwarfReader extends Dwarf implements ISymbolReader { return getSourceFiles(); } + private class OpcodeInfo { + private int numArgs; + private final boolean offset_size_8; + ArrayList 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 fixupList = new HashSet(); + Set fixupAltList = new HashSet(); + boolean DEBUG = false; + if (data == null) + return; + + HashMap > t_macros = new HashMap>(); + HashMap > t_alt_macros = new HashMap>(); + + // 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 fixupList, String fixupMarker, boolean DEBUG, + HashMap> t_macros) { + for (String name: fixupList) { + ArrayList 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 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 fixupList, Set fixupAltList, String fixupMarker, boolean DEBUG, + HashMap> 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 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(); + 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 macros = new ArrayList(); + + 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(); + } + 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 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; + } + ArrayListmacros = m_compileOptionsMap.get(fileName); + if (macros == null) + return ""; //$NON-NLS-1$ + + StringBuffer sb = new StringBuffer(); + for (String option: macros) { + sb.append(option); + sb.append(" "); //$NON-NLS-1$ + } + return sb.toString(); + } + } diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/elf/Elf.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/elf/Elf.java index 0d0aae02fb1..89fdb815446 100644 --- a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/elf/Elf.java +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/elf/Elf.java @@ -11,12 +11,11 @@ *******************************************************************************/ package org.eclipse.cdt.utils.elf; -import java.io.EOFException; - -import static org.eclipse.cdt.internal.core.ByteUtils.makeShort; import static org.eclipse.cdt.internal.core.ByteUtils.makeInt; import static org.eclipse.cdt.internal.core.ByteUtils.makeLong; +import static org.eclipse.cdt.internal.core.ByteUtils.makeShort; +import java.io.EOFException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel.MapMode; @@ -301,6 +300,12 @@ public class Elf { public final static int SHF_WRITE = 1; public final static int SHF_ALLOC = 2; public final static int SHF_EXECINTR = 4; + + /* note_types */ + /** + * @since 5.7 + */ + public final static int NT_GNU_BUILD_ID = 3; public long sh_name; public long sh_type;