| /******************************************************************************* |
| * 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(); |
| } |
| |
| } |