blob: 16a390b96ebd5be278e2d0e447647d5001b5896e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2019 Nokia and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.coff.PE64;
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<>();
private final Map<Long, String> m_stmtFileMap = new HashMap<>();
private final Map<String, ArrayList<String>> m_compileOptionsMap = new HashMap<>();
private String[] m_fileNames = null;
private boolean m_parsed = false;
private boolean m_macros_parsed = false;
private final ArrayList<Integer> m_parsedLineTableOffsets = new ArrayList<>();
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);
}
/**
* @since 6.9
*/
public DwarfReader(PE64 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<>();
// 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 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<>();
Set<String> fixupAltList = new HashSet<>();
boolean DEBUG = false;
if (data == null)
return;
HashMap<Long, ArrayList<String>> t_macros = new HashMap<>();
HashMap<Long, ArrayList<String>> 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<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<>();
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<>();
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<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();
}
}