/******************************************************************************* * Copyright (c) 2000, 2009 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.utils.debug.dwarf; import java.io.IOException; import java.lang.reflect.Array; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.utils.coff.PE; import org.eclipse.cdt.utils.coff.Coff.SectionHeader; import org.eclipse.cdt.utils.debug.DebugUnknownType; import org.eclipse.cdt.utils.debug.IDebugEntryRequestor; import org.eclipse.cdt.utils.debug.tools.DebugSym; import org.eclipse.cdt.utils.debug.tools.DebugSymsRequestor; import org.eclipse.cdt.utils.elf.Elf; import org.eclipse.cdt.utils.elf.Elf.Section; public class Dwarf { /* Section names. */ final static String DWARF_DEBUG_INFO = ".debug_info"; //$NON-NLS-1$ final static String DWARF_DEBUG_ABBREV = ".debug_abbrev"; //$NON-NLS-1$ final static String DWARF_DEBUG_ARANGES = ".debug_aranges"; //$NON-NLS-1$ final static String DWARF_DEBUG_LINE = ".debug_line"; //$NON-NLS-1$ final static String DWARF_DEBUG_FRAME = ".debug_frame"; //$NON-NLS-1$ final static String DWARF_EH_FRAME = ".eh_frame"; //$NON-NLS-1$ final static String DWARF_DEBUG_LOC = ".debug_loc"; //$NON-NLS-1$ final static String DWARF_DEBUG_PUBNAMES = ".debug_pubnames"; //$NON-NLS-1$ final static String DWARF_DEBUG_STR = ".debug_str"; //$NON-NLS-1$ final static String DWARF_DEBUG_FUNCNAMES = ".debug_funcnames"; //$NON-NLS-1$ final static String DWARF_DEBUG_TYPENAMES = ".debug_typenames"; //$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_MACINFO = ".debug_macinfo"; //$NON-NLS-1$ final static String[] DWARF_SCNNAMES = { DWARF_DEBUG_INFO, DWARF_DEBUG_ABBREV, DWARF_DEBUG_ARANGES, DWARF_DEBUG_LINE, DWARF_DEBUG_FRAME, DWARF_EH_FRAME, DWARF_DEBUG_LOC, DWARF_DEBUG_PUBNAMES, DWARF_DEBUG_STR, DWARF_DEBUG_FUNCNAMES, DWARF_DEBUG_TYPENAMES, DWARF_DEBUG_VARNAMES, DWARF_DEBUG_WEAKNAMES, DWARF_DEBUG_MACINFO }; class CompilationUnitHeader { int length; short version; int abbreviationOffset; byte addressSize; @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Length: " + length).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ sb.append("Version: " + version).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ sb.append("Abbreviation: " + abbreviationOffset).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ sb.append("Address size: " + addressSize).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$ return sb.toString(); } } class AbbreviationEntry { /* unsigned */ long code; /* unsigned */ long tag; byte hasChildren; List<Attribute> attributes; AbbreviationEntry(long c, long t, byte h) { code = c; tag = t; hasChildren = h; attributes = new ArrayList<Attribute>(); } } class Attribute { /* unsigned */ long name; /* unsigned */ long form; Attribute(long n, long f) { name = n; form = f; } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append("name: " + Long.toHexString(name)); //$NON-NLS-1$ sb.append(" value: " + Long.toHexString(form)); //$NON-NLS-1$ return sb.toString(); } } class AttributeValue { Attribute attribute; Object value; AttributeValue(Attribute a, Object o) { attribute = a; value = o; } @Override public String toString() { StringBuffer sb = new StringBuffer(); sb.append(attribute.toString()).append(' '); if (value != null) { Class<? extends Object> clazz = value.getClass(); if (clazz.isArray()) { int len = Array.getLength(value); sb.append(len).append(' '); sb.append(clazz.getComponentType().toString()); sb.append(':'); for (int i = 0; i < len; i++) { byte b = Array.getByte(value, i); sb.append(' ').append(Integer.toHexString(b)); } } else { if (value instanceof Number) { Number n = (Number) value; sb.append(Long.toHexString(n.longValue())); } else if (value instanceof String) { sb.append(value); } else { sb.append(value); } } } return sb.toString(); } } class CompileUnit { long lowPC; long highPC; int stmtList; String name; int language; int macroInfo; String compDir; String producer; int identifierCase; } Map<String, ByteBuffer> dwarfSections = new HashMap<String, ByteBuffer>(); Map<Integer, Map<Long, AbbreviationEntry>> abbreviationMaps = new HashMap<Integer, Map<Long, AbbreviationEntry>>(); boolean isLE; CompileUnit currentCU; boolean printEnabled = true; public Dwarf(String file) throws IOException { Elf exe = new Elf(file); init(exe); exe.dispose(); } public Dwarf(Elf exe) throws IOException { init(exe); } /** * @since 5.1 */ public Dwarf(PE exe) throws IOException { init(exe); } public void init(Elf exe) throws IOException { Elf.ELFhdr header = exe.getELFhdr(); isLE = header.e_ident[Elf.ELFhdr.EI_DATA] == Elf.ELFhdr.ELFDATA2LSB; Elf.Section[] sections = exe.getSections(); for (Section section : sections) { String name = section.toString(); for (String element : DWARF_SCNNAMES) { if (name.equals(element)) { try { dwarfSections.put(element, section.mapSectionData()); } catch (Exception e) { e.printStackTrace(); CCorePlugin.log(e); } } } } } /** * @since 5.1 */ 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) { e.printStackTrace(); CCorePlugin.log(e); } } } } } int read_4_bytes(ByteBuffer in) throws IOException { try { byte[] bytes = new byte[4]; in.get(bytes); return read_4_bytes(bytes); } catch (Exception e) { throw new IOException(CCorePlugin.getResourceString("Util.exception.missingBytes")); //$NON-NLS-1$ } } // FIXME:This is wrong, it's signed. int read_4_bytes(byte[] bytes) throws IndexOutOfBoundsException { if (isLE) { return ( ((bytes[3] & 0xff) << 24) | ((bytes[2] & 0xff) << 16) | ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff)); } return ( ((bytes[0] & 0xff) << 24) | ((bytes[1] & 0xff) << 16) | ((bytes[2] & 0xff) << 8) | (bytes[3] & 0xff)); } long read_8_bytes(ByteBuffer in) throws IOException { try { byte[] bytes = new byte[8]; in.get(bytes); return read_8_bytes(bytes); } catch (Exception e) { throw new IOException(CCorePlugin.getResourceString("Util.exception.missingBytes")); //$NON-NLS-1$ } } // FIXME:This is wrong, for unsigned. long read_8_bytes(byte[] bytes) throws IndexOutOfBoundsException { if (isLE) { return (((bytes[7] & 0xff) << 56) | ((bytes[6] & 0xff) << 48) | ((bytes[5] & 0xff) << 40) | ((bytes[4] & 0xff) << 32) | ((bytes[3] & 0xff) << 24) | ((bytes[2] & 0xff) << 16) | ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff)); } return (((bytes[0] & 0xff) << 56) | ((bytes[1] & 0xff) << 48) | ((bytes[2] & 0xff) << 40) | ((bytes[3] & 0xff) << 32) | ((bytes[4] & 0xff) << 24) | ((bytes[5] & 0xff) << 16) | ((bytes[6] & 0xff) << 8) | (bytes[7] & 0xff)); } short read_2_bytes(ByteBuffer in) throws IOException { try { byte[] bytes = new byte[2]; in.get(bytes); return read_2_bytes(bytes); } catch (Exception e) { throw new IOException(CCorePlugin.getResourceString("Util.exception.missingBytes")); //$NON-NLS-1$ } } short read_2_bytes(byte[] bytes) throws IndexOutOfBoundsException { if (isLE) { return (short) (((bytes[1] & 0xff) << 8) + (bytes[0] & 0xff)); } return (short) (((bytes[0] & 0xff) << 8) + (bytes[1] & 0xff)); } /* unsigned */ long read_unsigned_leb128(ByteBuffer in) throws IOException { /* unsigned */ long result = 0; int shift = 0; short b; while (true) { b = in.get(); if (!in.hasRemaining()) break; //throw new IOException("no more data"); result |= ((long) (b & 0x7f) << shift); if ((b & 0x80) == 0) { break; } shift += 7; } return result; } /* unsigned */ long read_signed_leb128(ByteBuffer in) throws IOException { /* unsigned */ long result = 0; int shift = 0; int size = 32; short b; while (true) { b = in.get(); if (!in.hasRemaining()) throw new IOException(CCorePlugin.getResourceString("Util.exception.noData")); //$NON-NLS-1$ result |= ((long) (b & 0x7f) << shift); shift += 7; if ((b & 0x80) == 0) { break; } } if ((shift < size) && (b & 0x40) != 0) { result |= - (1 << shift); } return result; } public void parse(IDebugEntryRequestor requestor) { parseDebugInfo(requestor); } void parseDebugInfo(IDebugEntryRequestor requestor) { ByteBuffer data = dwarfSections.get(DWARF_DEBUG_INFO); if (data != null) { try { while (data.hasRemaining()) { CompilationUnitHeader header = new CompilationUnitHeader(); header.length = read_4_bytes(data); header.version = read_2_bytes(data); header.abbreviationOffset = read_4_bytes(data); header.addressSize = data.get(); if (printEnabled) { System.out.println("Compilation Unit @ " + Long.toHexString(data.position())); //$NON-NLS-1$ System.out.println(header); } // read the abbrev section. Map<Long, AbbreviationEntry> abbrevs = parseDebugAbbreviation(header); // Note "length+4" is the total size in bytes of the CU data. ByteBuffer entryBuffer = data.slice(); entryBuffer.limit(header.length + 4 - 11); parseDebugInfoEntry(requestor, entryBuffer, abbrevs, header); data.position(data.position() + header.length + 4 - 11); if (printEnabled) System.out.println(); } } catch (IOException e) { e.printStackTrace(); } } } Map<Long, AbbreviationEntry> parseDebugAbbreviation(CompilationUnitHeader header) throws IOException { Integer key = new Integer(header.abbreviationOffset); Map<Long, AbbreviationEntry> abbrevs = abbreviationMaps.get(key); if (abbrevs == null) { abbrevs = new HashMap<Long, AbbreviationEntry>(); abbreviationMaps.put(key, abbrevs); ByteBuffer data = dwarfSections.get(DWARF_DEBUG_ABBREV); if (data != null) { data.position(header.abbreviationOffset); while (data.remaining() > 0) { long code = read_unsigned_leb128(data); if (code == 0) { break; } long tag = read_unsigned_leb128(data); byte hasChildren = data.get(); AbbreviationEntry entry = new AbbreviationEntry(code, tag, hasChildren); //System.out.println("\tAbrev Entry: " + code + " " + Long.toHexString(entry.tag) + " " + entry.hasChildren); // attributes long name = 0; long form = 0; do { name = read_unsigned_leb128(data); form = read_unsigned_leb128(data); if (name != 0) { entry.attributes.add(new Attribute(name, form)); } //System.out.println("\t\t " + Long.toHexString(name) + " " + Long.toHexString(value)); } while (name != 0 && form != 0); abbrevs.put(new Long(code), entry); } } } return abbrevs; } void parseDebugInfoEntry(IDebugEntryRequestor requestor, ByteBuffer in, Map<Long, AbbreviationEntry> abbrevs, CompilationUnitHeader header) throws IOException { while (in.remaining() > 0) { long code = read_unsigned_leb128(in); AbbreviationEntry entry = abbrevs.get(new Long(code)); if (entry != null) { int len = entry.attributes.size(); List<AttributeValue> list = new ArrayList<AttributeValue>(len); try { for (int i = 0; i < len; i++) { Attribute attr = entry.attributes.get(i); Object obj = readAttribute((int) attr.form, in, header); list.add(new AttributeValue(attr, obj)); } } catch (IOException e) { //break; } processDebugInfoEntry(requestor, entry, list); } } } Object readAttribute(int form, ByteBuffer in, CompilationUnitHeader header) throws IOException { Object obj = null; switch (form) { case DwarfConstants.DW_FORM_addr : case DwarfConstants.DW_FORM_ref_addr : obj = readAddress(in, header); break; case DwarfConstants.DW_FORM_block : { int size = (int) read_unsigned_leb128(in); byte[] bytes = new byte[size]; in.get(bytes); obj = bytes; } break; case DwarfConstants.DW_FORM_block1 : { int size = in.get(); byte[] bytes = new byte[size]; in.get(bytes); obj = bytes; } break; case DwarfConstants.DW_FORM_block2 : { int size = read_2_bytes(in); byte[] bytes = new byte[size]; in.get(bytes); obj = bytes; } break; case DwarfConstants.DW_FORM_block4 : { int size = read_4_bytes(in); byte[] bytes = new byte[size]; in.get(bytes); obj = bytes; } break; case DwarfConstants.DW_FORM_data1 : obj = new Byte(in.get()); break; case DwarfConstants.DW_FORM_data2 : obj = new Short(read_2_bytes(in)); break; case DwarfConstants.DW_FORM_data4 : obj = new Integer(read_4_bytes(in)); break; case DwarfConstants.DW_FORM_data8 : obj = new Long(read_8_bytes(in)); break; case DwarfConstants.DW_FORM_sdata : obj = new Long(read_signed_leb128(in)); break; case DwarfConstants.DW_FORM_udata : obj = new Long(read_unsigned_leb128(in)); break; case DwarfConstants.DW_FORM_string : { int c; StringBuffer sb = new StringBuffer(); while ((c = in.get()) != -1) { if (c == 0) { break; } sb.append((char) c); } obj = sb.toString(); } break; case DwarfConstants.DW_FORM_flag : obj = new Byte(in.get()); break; case DwarfConstants.DW_FORM_strp : { int offset = read_4_bytes(in); ByteBuffer data = dwarfSections.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(offset); while (data.hasRemaining()) { byte c = data.get(); if (c == 0) { break; } sb.append((char) c); } obj = sb.toString(); } } break; case DwarfConstants.DW_FORM_ref1 : obj = new Byte(in.get()); break; case DwarfConstants.DW_FORM_ref2 : obj = new Short(read_2_bytes(in)); break; case DwarfConstants.DW_FORM_ref4 : obj = new Integer(read_4_bytes(in)); break; case DwarfConstants.DW_FORM_ref8 : obj = new Long(read_8_bytes(in)); break; case DwarfConstants.DW_FORM_ref_udata : obj = new Long(read_unsigned_leb128(in)); break; case DwarfConstants.DW_FORM_indirect : { int f = (int) read_unsigned_leb128(in); return readAttribute(f, in, header); } default : break; } return obj; } void processDebugInfoEntry(IDebugEntryRequestor requestor, AbbreviationEntry entry, List<AttributeValue> list) { int len = list.size(); int tag = (int) entry.tag; if (printEnabled) System.out.println("Abbrev Number " + entry.code); //$NON-NLS-1$ for (int i = 0; i < len; i++) { AttributeValue av = list.get(i); if (printEnabled) System.out.println(av); // We are only interrested in certain tags. switch (tag) { case DwarfConstants.DW_TAG_array_type : break; case DwarfConstants.DW_TAG_class_type : break; case DwarfConstants.DW_TAG_enumeration_type : break; case DwarfConstants.DW_TAG_formal_parameter : break; case DwarfConstants.DW_TAG_lexical_block : break; case DwarfConstants.DW_TAG_member : break; case DwarfConstants.DW_TAG_pointer_type : break; case DwarfConstants.DW_TAG_reference_type : break; case DwarfConstants.DW_TAG_compile_unit : processCompileUnit(requestor, list); break; case DwarfConstants.DW_TAG_structure_type : break; case DwarfConstants.DW_TAG_subroutine_type : break; case DwarfConstants.DW_TAG_typedef : break; case DwarfConstants.DW_TAG_union_type : break; case DwarfConstants.DW_TAG_unspecified_parameters : break; case DwarfConstants.DW_TAG_inheritance : break; case DwarfConstants.DW_TAG_ptr_to_member_type : break; case DwarfConstants.DW_TAG_with_stmt : break; case DwarfConstants.DW_TAG_base_type : break; case DwarfConstants.DW_TAG_catch_block : break; case DwarfConstants.DW_TAG_const_type : break; case DwarfConstants.DW_TAG_enumerator : break; case DwarfConstants.DW_TAG_file_type : break; case DwarfConstants.DW_TAG_friend : break; case DwarfConstants.DW_TAG_subprogram : processSubProgram(requestor, list); break; case DwarfConstants.DW_TAG_template_type_param : break; case DwarfConstants.DW_TAG_template_value_param : break; case DwarfConstants.DW_TAG_thrown_type : break; case DwarfConstants.DW_TAG_try_block : break; case DwarfConstants.DW_TAG_variable : break; case DwarfConstants.DW_TAG_volatile_type : break; } } } Long readAddress(ByteBuffer in, CompilationUnitHeader header) throws IOException { long value = 0; switch (header.addressSize) { case 2 : value = read_2_bytes(in); break; case 4 : value = read_4_bytes(in); break; case 8 : value = read_8_bytes(in); break; default : // ???? } return new Long(value); } void processSubProgram(IDebugEntryRequestor requestor, List<AttributeValue> list) { long lowPC = 0; long highPC = 0; String funcName = ""; //$NON-NLS-1$ boolean isExtern = false; 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_low_pc: lowPC = ((Number)av.value).longValue(); break; case DwarfConstants.DW_AT_high_pc: highPC = ((Number)av.value).longValue(); break; case DwarfConstants.DW_AT_name: funcName = (String)av.value; break; case DwarfConstants.DW_AT_external: isExtern = ((Number)av.value).intValue() > 0; break; } } catch (ClassCastException e) { } } requestor.enterFunction(funcName, new DebugUnknownType(""), isExtern, lowPC); //$NON-NLS-1$ requestor.exitFunction(highPC); } void processCompileUnit(IDebugEntryRequestor requestor, List<AttributeValue> list) { if (currentCU != null) { requestor.exitCompilationUnit(currentCU.highPC); } currentCU = new CompileUnit(); 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_low_pc: currentCU.lowPC = ((Number)av.value).longValue(); break; case DwarfConstants.DW_AT_high_pc: currentCU.highPC = ((Number)av.value).longValue(); break; case DwarfConstants.DW_AT_name: currentCU.name = (String)av.value; break; case DwarfConstants.DW_AT_language: currentCU.language = ((Number)av.value).intValue(); break; case DwarfConstants.DW_AT_stmt_list: currentCU.stmtList = ((Number)av.value).intValue(); break; case DwarfConstants.DW_AT_macro_info: currentCU.macroInfo = ((Number)av.value).intValue(); break; case DwarfConstants.DW_AT_comp_dir: currentCU.compDir = (String)av.value; break; case DwarfConstants.DW_AT_producer: currentCU.producer = (String)av.value; break; //case DW_AT_identifier_case: } } catch (ClassCastException e) { } } requestor.enterCompilationUnit(currentCU.name, currentCU.lowPC); } public static void main(String[] args) { try { DebugSymsRequestor symreq = new DebugSymsRequestor(); Dwarf dwarf = new Dwarf(args[0]); dwarf.parse(symreq); DebugSym[] entries = symreq.getEntries(); for (DebugSym entry : entries) { System.out.println(entry); } } catch (IOException e) { e.printStackTrace(); } } }