1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-22 14:12:10 +02:00

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 <marc-andre.laperle@ericsson.com>
Tested-by: Marc-Andre Laperle <marc-andre.laperle@ericsson.com>
Reviewed-by: Jeff Johnston <jjohnstn@redhat.com>
Tested-by: Jeff Johnston <jjohnstn@redhat.com>
This commit is contained in:
Jeff Johnston 2014-02-12 17:57:17 -05:00
parent f5f4b5101a
commit 12a2ba4b3f
5 changed files with 866 additions and 61 deletions

View file

@ -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);
}

View file

@ -13,6 +13,7 @@
package org.eclipse.cdt.utils.debug.dwarf; package org.eclipse.cdt.utils.debug.dwarf;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.nio.ByteBuffer; 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.debug.tools.DebugSymsRequestor;
import org.eclipse.cdt.utils.elf.Elf; import org.eclipse.cdt.utils.elf.Elf;
import org.eclipse.cdt.utils.elf.Elf.Section; import org.eclipse.cdt.utils.elf.Elf.Section;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
public class Dwarf { 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_VARNAMES = ".debug_varnames"; //$NON-NLS-1$
final static String DWARF_DEBUG_WEAKNAMES = ".debug_weaknames"; //$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_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 = final static String[] DWARF_SCNNAMES =
{ {
DWARF_DEBUG_INFO, DWARF_DEBUG_INFO,
@ -65,6 +73,14 @@ public class Dwarf {
DWARF_DEBUG_WEAKNAMES, DWARF_DEBUG_WEAKNAMES,
DWARF_DEBUG_MACINFO }; DWARF_DEBUG_MACINFO };
final static String[] DWARF_ALT_SCNNAMES =
{
DWARF_DEBUG_INFO,
DWARF_DEBUG_TYPES,
DWARF_DEBUG_MACRO,
DWARF_DEBUG_STR,
};
class CompilationUnitHeader { class CompilationUnitHeader {
long length; long length;
short version; short version;
@ -166,6 +182,7 @@ public class Dwarf {
} }
Map<String, ByteBuffer> dwarfSections = new HashMap<String, ByteBuffer>(); Map<String, ByteBuffer> dwarfSections = new HashMap<String, ByteBuffer>();
Map<String, ByteBuffer> dwarfAltSections = new HashMap<String, ByteBuffer>();
Map<Integer, Map<Long, AbbreviationEntry>> abbreviationMaps = new HashMap<Integer, Map<Long, AbbreviationEntry>>(); Map<Integer, Map<Long, AbbreviationEntry>> abbreviationMaps = new HashMap<Integer, Map<Long, AbbreviationEntry>>();
boolean isLE; boolean isLE;
@ -196,8 +213,78 @@ public class Dwarf {
isLE = header.e_ident[Elf.ELFhdr.EI_DATA] == Elf.ELFhdr.ELFDATA2LSB; isLE = header.e_ident[Elf.ELFhdr.EI_DATA] == Elf.ELFhdr.ELFDATA2LSB;
Elf.Section[] sections = exe.getSections(); 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) { for (Section section : sections) {
String name = section.toString(); 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_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) { for (String element : DWARF_SCNNAMES) {
if (name.equals(element)) { if (name.equals(element)) {
try { try {
@ -210,6 +297,7 @@ public class Dwarf {
} }
} }
} }
}
/** /**
* @since 5.1 * @since 5.1
@ -463,7 +551,8 @@ public class Dwarf {
byte hasChildren = data.get(); byte hasChildren = data.get();
AbbreviationEntry entry = new AbbreviationEntry(code, tag, hasChildren); 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 // attributes
long name = 0; long name = 0;
@ -474,7 +563,8 @@ public class Dwarf {
if (name != 0) { if (name != 0) {
entry.attributes.add(new Attribute(name, form)); 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); } while (name != 0 && form != 0);
abbrevs.put(new Long(code), entry); 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 readAttribute(int form, ByteBuffer in, CompilationUnitHeader header) throws IOException {
Object obj = null; Object obj = null;
switch (form) { switch (form) {
@ -621,6 +713,34 @@ public class Dwarf {
} }
break; 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 : case DwarfConstants.DW_FORM_ref1 :
obj = new Byte(in.get()); obj = new Byte(in.get());
break; break;
@ -646,6 +766,12 @@ public class Dwarf {
int f = (int) read_unsigned_leb128(in); int f = (int) read_unsigned_leb128(in);
return readAttribute(f, in, header); 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 : case DwarfConstants.DW_FORM_sec_offset :
if (header.offsetSize == 8) if (header.offsetSize == 8)
obj = new Long(read_8_bytes(in)); obj = new Long(read_8_bytes(in));
@ -667,9 +793,14 @@ public class Dwarf {
break; break;
default : default :
if (printEnabled) {
System.out.println("Default for " + form); //$NON-NLS-1$
System.out.println("Last form is " + oldForm); //$NON-NLS-1$
}
break; break;
} }
oldForm = form;
return obj; return obj;
} }
@ -775,6 +906,27 @@ public class Dwarf {
return new Long(value); 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<AttributeValue> list) { void processSubProgram(IDebugEntryRequestor requestor, List<AttributeValue> list) {
long lowPC = 0; long lowPC = 0;
long highPC = 0; long highPC = 0;

View file

@ -65,6 +65,10 @@ public class DwarfConstants {
public final static int DW_TAG_variant_part = 0x33; public final static int DW_TAG_variant_part = 0x33;
public final static int DW_TAG_variable = 0x34; public final static int DW_TAG_variable = 0x34;
public final static int DW_TAG_volatile_type = 0x35; 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_lo_user = 0x4080;
public final static int DW_TAG_MIPS_loop = 0x4081; public final static int DW_TAG_MIPS_loop = 0x4081;
public final static int DW_TAG_format_label = 0x4101; 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_ref8 = 0x14;
public final static int DW_FORM_ref_udata = 0x15; public final static int DW_FORM_ref_udata = 0x15;
public final static int DW_FORM_indirect = 0x16; public final static int DW_FORM_indirect = 0x16;
/** /**
* @since 5.6 * @since 5.7
*/ */
public final static int DW_FORM_sec_offset = 0x17; public final static int DW_FORM_sec_offset = 0x17;
/** /**
* @since 5.6 * @since 5.7
*/ */
public final static int DW_FORM_exprloc = 0x18; public final static int DW_FORM_exprloc = 0x18;
/** /**
* @since 5.6 * @since 5.7
*/ */
public final static int DW_FORM_flag_present = 0x19; public final static int DW_FORM_flag_present = 0x19;
/** /**
* @since 5.6 * @since 5.7
*/ */
public final static int DW_FORM_ref_sig8 = 0x20; 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. */ /* DWARF location operation encodings. */
public final static int DW_OP_addr = 0x03; /* Constant address. */ 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_end_file = 4;
public final static int DW_MACINFO_vendor_ext = 255; 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. */ /* DWARF call frame instruction encodings. */
public final static int DW_CFA_advance_loc = 0x40; public final static int DW_CFA_advance_loc = 0x40;
public final static int DW_CFA_offset = 0x80; public final static int DW_CFA_offset = 0x80;

View file

@ -9,18 +9,26 @@
* Nokia - initial API and implementation * Nokia - initial API and implementation
* Ling Wang (Nokia) bug 201000 * Ling Wang (Nokia) bug 201000
* Serge Beauchamp (Freescale Semiconductor) - Bug 421070 * Serge Beauchamp (Freescale Semiconductor) - Bug 421070
* Red Hat Inc. - add debuginfo and macro section support
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.utils.debug.dwarf; package org.eclipse.cdt.utils.debug.dwarf;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; 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.CCorePlugin;
import org.eclipse.cdt.core.ICompileOptionsFinder;
import org.eclipse.cdt.core.ISymbolReader; import org.eclipse.cdt.core.ISymbolReader;
import org.eclipse.cdt.utils.coff.Coff.SectionHeader; import org.eclipse.cdt.utils.coff.Coff.SectionHeader;
import org.eclipse.cdt.utils.coff.PE; 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 * Light-weight parser of Dwarf2 data which is intended for getting only
* source files that contribute to the given executable. * 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. // These are sections that need be parsed to get the source file list.
final static String[] DWARF_SectionsToParse = final static String[] DWARF_SectionsToParse = {
{
DWARF_DEBUG_INFO, DWARF_DEBUG_INFO,
DWARF_DEBUG_LINE, DWARF_DEBUG_LINE,
DWARF_DEBUG_ABBREV, DWARF_DEBUG_ABBREV,
DWARF_DEBUG_STR // this is optional. Some compilers don't generate it. 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 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 String[] m_fileNames = null;
private boolean m_parsed = false; private boolean m_parsed = false;
private boolean m_macros_parsed = false;
private final ArrayList<Integer> m_parsedLineTableOffsets = new ArrayList<Integer>(); private final ArrayList<Integer> m_parsedLineTableOffsets = new ArrayList<Integer>();
private long m_parsedLineTableSize = 0; private long m_parsedLineTableSize = 0;
@ -74,12 +88,148 @@ public class DwarfReader extends Dwarf implements ISymbolReader {
Elf.ELFhdr header = exe.getELFhdr(); Elf.ELFhdr header = exe.getELFhdr();
isLE = header.e_ident[Elf.ELFhdr.EI_DATA] == Elf.ELFhdr.ELFDATA2LSB; isLE = header.e_ident[Elf.ELFhdr.EI_DATA] == Elf.ELFhdr.ELFDATA2LSB;
IPath debugInfoPath = new Path(exe.getFilename());
Elf.Section[] sections = exe.getSections(); 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. // Read in sections (and only the sections) we care about.
// //
for (Section section : sections) { for (Section section : sections) {
String name = section.toString(); 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) { for (String element : DWARF_SectionsToParse) {
if (name.equals(element)) { if (name.equals(element)) {
// catch out of memory exceptions which might happen trying to // catch out of memory exceptions which might happen trying to
@ -94,6 +244,7 @@ public class DwarfReader extends Dwarf implements ISymbolReader {
} }
} }
} }
}
// Don't print during parsing. // Don't print during parsing.
printEnabled = false; printEnabled = false;
@ -411,13 +562,19 @@ public class DwarfReader extends Dwarf implements ISymbolReader {
parse(null); 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) if (name == null || name.length() == 0)
return; return null;
if (name.charAt(0) == '<') // don't count the entry "<internal>" from GCCE compiler if (name.charAt(0) == '<') // don't count the entry "<internal>" from GCCE compiler
return; return null;
String fullName = name; String fullName = name;
@ -434,27 +591,8 @@ public class DwarfReader extends Dwarf implements ISymbolReader {
if (!m_fileCollection.contains(fullName)) 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 parent: only handle TAG_Compile_Unit.
@ -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 if (stmtList > -1) // this CU has "stmt_list" attribute
parseSourceInCULineInfo(cuCompDir, stmtList); parseSourceInCULineInfo(cuCompDir, stmtList);
} }
@ -515,4 +653,406 @@ public class DwarfReader extends Dwarf implements ISymbolReader {
return getSourceFiles(); 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$
StringBuffer sb = new StringBuffer();
for (String option: macros) {
sb.append(option);
sb.append(" "); //$NON-NLS-1$
}
return sb.toString();
}
} }

View file

@ -11,12 +11,11 @@
*******************************************************************************/ *******************************************************************************/
package org.eclipse.cdt.utils.elf; 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.makeInt;
import static org.eclipse.cdt.internal.core.ByteUtils.makeLong; 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.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.FileChannel.MapMode; import java.nio.channels.FileChannel.MapMode;
@ -302,6 +301,12 @@ public class Elf {
public final static int SHF_ALLOC = 2; public final static int SHF_ALLOC = 2;
public final static int SHF_EXECINTR = 4; 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_name;
public long sh_type; public long sh_type;
public long sh_flags; public long sh_flags;