From 24e017a04a423fa53e79055d6599f1355ada9d87 Mon Sep 17 00:00:00 2001 From: Alain Magloire Date: Tue, 6 Jan 2004 21:19:57 +0000 Subject: [PATCH] First implementation of STABS debug format parsing --- core/org.eclipse.cdt.core/ChangeLog | 8 + .../eclipse/cdt/utils/stabs/StabConstant.java | 157 +++++++ .../org/eclipse/cdt/utils/stabs/Stabs.java | 399 ++++++++++++++++++ .../cdt/utils/stabs/StabsAddr2line.java | 160 +++++++ 4 files changed, 724 insertions(+) create mode 100644 core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/StabConstant.java create mode 100644 core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/Stabs.java create mode 100644 core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/StabsAddr2line.java diff --git a/core/org.eclipse.cdt.core/ChangeLog b/core/org.eclipse.cdt.core/ChangeLog index eb5a7a95668..975cb42a55f 100644 --- a/core/org.eclipse.cdt.core/ChangeLog +++ b/core/org.eclipse.cdt.core/ChangeLog @@ -1,3 +1,11 @@ +2004-01-06 Alain Magloire + + Simple draft implementation of stabs debug format parsing. + Not ready. + + * utils/org/eclipse/cdt/utils/stabs: New package implementing + Stabs debug format parsing. + 2003-12-29 Hoda Amer Content Assist Work : Moved ICompletionRequestor from core to ui diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/StabConstant.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/StabConstant.java new file mode 100644 index 00000000000..e78d2b9ca09 --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/StabConstant.java @@ -0,0 +1,157 @@ +/********************************************************************** + * Copyright (c) 2002,2003 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation +***********************************************************************/ + +package org.eclipse.cdt.utils.stabs; + +public final class StabConstant { + + // Stab Symbol Types + public final static int N_UNDF = 0x00; + public final static int N_GSYM = 0x20; + public final static int N_FNAME = 0x22; + public final static int N_FUN = 0x24; + public final static int N_STSYM = 0x26; + public final static int N_LCSYM = 0x28; + public final static int N_MAIN = 0x2a; + public final static int N_ROSYM = 0x2c; + public final static int N_PC = 0x30; + public final static int N_NSYMS = 0x32; + public final static int N_NOMAP = 0x34; + public final static int N_OBJ = 0x38; + public final static int N_OPT = 0x3c; + public final static int N_RSYM = 0x40; + public final static int N_M2C = 0x42; + public final static int N_SLINE = 0x44; + public final static int N_DSLINE = 0x46; + public final static int N_BSLINE = 0x48; + public final static int N_DEFD = 0x4a; + public final static int N_FLINE = 0x4c; + public final static int N_EHDECL = 0x50; + public final static int N_CATCH = 0x54; + public final static int N_SSYM = 0x60; + public final static int N_ENDM = 0x62; + public final static int N_SO = 0x64; + public final static int N_LSYM = 0x80; + public final static int N_BINCL = 0x82; + public final static int N_SOL = 0x84; + public final static int N_PSYM = 0xa0; + public final static int N_EINCL = 0xa2; + public final static int N_ENTRY = 0xa4; + public final static int N_LBRAC = 0xc0; + public final static int N_EXCL = 0xc2; + public final static int N_SCOPE = 0xc4; + public final static int N_RBRAC = 0xe0; + public final static int N_BCOMM = 0xe2; + public final static int N_ECOMM = 0xe4; + public final static int N_ECOML = 0xe8; + public final static int N_WITH = 0xea; + public final static int N_NBTEXT = 0xef; + public final static int N_NBDATA = 0xf2; + public final static int N_NBBSS = 0xf4; + public final static int N_NBSTS = 0xf6; + public final static int N_NBLCS = 0xf8; + + public final static int SIZE = 12; // 4 + 1 + 1 + 2 + 4 + + public static String type2String(int t) { + switch (t) { + case N_UNDF : + return "UNDF"; + case N_GSYM : + return "GSYM"; + case N_FNAME : + return "FNAME"; + case N_FUN : + return "FUN"; + case N_STSYM : + return "STSYM"; + case N_LCSYM : + return "LCSYM"; + case N_MAIN : + return "MAIN"; + case N_ROSYM : + return "ROSYM"; + case N_PC : + return "PC"; + case N_NSYMS : + return "SSYMS"; + case N_NOMAP : + return "NOMAP"; + case N_OBJ : + return "OBJ"; + case N_OPT : + return "OPT"; + case N_RSYM : + return "RSYM"; + case N_M2C : + return "M2C"; + case N_SLINE : + return "SLINE"; + case N_DSLINE : + return "DSLINE"; + case N_BSLINE : + return "BSLINE"; + case N_DEFD : + return "DEFD"; + case N_FLINE : + return "FLINE"; + case N_EHDECL : + return "EHDECL"; + case N_CATCH : + return "CATCH"; + case N_SSYM : + return "SSYM"; + case N_ENDM : + return "ENDM"; + case N_SO : + return "SO"; + case N_LSYM : + return "LSYM"; + case N_BINCL : + return "BINCL"; + case N_SOL : + return "SOL"; + case N_PSYM : + return "PSYM"; + case N_EINCL : + return "EINCL"; + case N_ENTRY : + return "ENTRY"; + case N_LBRAC : + return "LBRAC"; + case N_EXCL : + return "EXCL"; + case N_SCOPE: + return "SCOPE"; + case N_RBRAC : + return "RBRAC"; + case N_BCOMM : + return "COMM"; + case N_ECOMM : + return "ECOMM"; + case N_ECOML : + return "ECOML"; + case N_WITH : + return "WITH"; + case N_NBTEXT : + return "NBTEXT"; + case N_NBDATA : + return "NBDATA"; + case N_NBBSS : + return "NBBSS"; + case N_NBSTS : + return "NBSTS"; + case N_NBLCS : + return "NBLCS"; + } + return "" + t; + } +} diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/Stabs.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/Stabs.java new file mode 100644 index 00000000000..8c1561842d0 --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/Stabs.java @@ -0,0 +1,399 @@ +/********************************************************************** + * Copyright (c) 2002,2003 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation +***********************************************************************/ + +package org.eclipse.cdt.utils.stabs; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.cdt.utils.elf.Elf; + +public class Stabs { + + byte[] stabData; + byte[] stabstrData; + boolean isLe; + List entries; + + public class Entry { + public long addr; + public int startLine; + public String string; + + public Entry(String s) { + string = s; + } + + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("Name:").append(string).append("\n"); + buf.append("\taddress:").append(addr).append("\n"); + buf.append("\tstartLine:").append(startLine).append("\n"); + //buf.append("\tName:").append(string).append("\n"); + return buf.toString(); + } + } + + public abstract class LocatableEntry extends Entry { + public String filename; + + public LocatableEntry(String s) { + super(s); + } + } + + public class Variable extends LocatableEntry { + public int kind; + public Function function; + + public Variable(String s) { + super(s); + } + + public String toString() { + StringBuffer buf = new StringBuffer(super.toString()); + buf.append("\tkind:").append(kind).append("\n"); + buf.append("\tfilename:").append(filename).append("\n"); + buf.append("\tVariable"); + return buf.toString(); + } + } + + public class Function extends LocatableEntry { + public int endLine; + public ArrayList lines; + public ArrayList variables; + + public Function(String s) { + super(s); + variables = new ArrayList(); + lines = new ArrayList(); + } + + public String toString() { + StringBuffer buf = new StringBuffer(super.toString()); + buf.append("\tendLine:").append(endLine).append("\n"); + buf.append("\tfilename:").append(filename).append("\n"); + buf.append("\tSource code: "); + for (int i = 0; i < lines.size(); i++) { + buf.append(" ").append(lines.get(i)); + } + buf.append("\n"); + buf.append("\tVariables\n"); + for (int i = 0; i < variables.size(); i++) { + buf.append("\t\t").append(variables.get(i)).append("\n"); + } + buf.append("\tFunction"); + return buf.toString(); + } + } + + public class Include extends Entry { + int index; + + public Include(String s) { + super(s); + } + + public String toString() { + return super.toString() + "\tindex:" + index + "\n"; + } + } + + public class TypeDef extends Entry { + String type; + + public TypeDef(String s) { + super(s); + } + } + + public class TypeDefinition extends Entry { + int typeNumber; + String name; + + public TypeDefinition(String s) { + super(s); + } + } + + public class StringField extends Entry { + String name; + String symbolDescriptor; + String typeDefinition; + int typeNumber; + + public StringField(String s) { + super(s); + } + } + + public String makeString(long offset) { + StringBuffer buf = new StringBuffer(); + for (; offset < stabstrData.length; offset++) { + byte b = stabstrData[(int) offset]; + if (b == 0) { + break; + } + buf.append((char) b); + } + return buf.toString(); + } + + public Stabs(byte[] stab, byte[] stabstr, boolean le) { + stabData = stab; + stabstrData = stabstr; + isLe = le; + } + + public Entry[] getEntries() throws IOException { + if (entries == null) { + parse(); + } + Entry[] array = new Entry[entries.size()]; + entries.toArray(array); + return array; + } + + void parse() throws IOException { + + entries = new ArrayList(); + + long nstab = stabData.length / StabConstant.SIZE; + int i, offset, bracket; + int includeCount = 0; + Function currentFunction = null; + String currentFile = ""; + String holder = null; + + for (bracket = i = offset = 0; i < nstab; i++, offset += StabConstant.SIZE) { + + long stroff = 0; + int type = 0; + int other = 0; + short desc = 0; + long value = 0; + String name = new String(); + + // get the offset for the string; 4 bytes + if (isLe) { + stroff = + (((stabData[offset + 3] & 0xff) << 24) + + ((stabData[offset + 2] & 0xff) << 16) + + ((stabData[offset + 1] & 0xff) << 8) + + (stabData[offset] & 0xff)); + } else { + stroff = + (((stabData[offset] & 0xff) << 24) + + ((stabData[offset + 1] & 0xff) << 16) + + ((stabData[offset + 2] & 0xff) << 8) + + (stabData[offset + 3] & 0xff)); + } + + if (stroff > 0) { + name = makeString(stroff); + } + + // Check for continuation and if any go to the next stab + // until we find a string that is not terminated with a continuation line '\\' + // According to the spec all the other fields are duplicated so we still have the data. + // From the spec continuation line on AIX is '?' + if (name.endsWith("\\") || name.endsWith("?")) { + name = name.substring(0, name.length() - 1); + if (holder == null) { + holder = name; + } else { + holder += name; + } + continue; + } else if (holder != null) { + name = holder + name; + holder = null; + } + + // get the type; 1 byte; + type = 0xff & stabData[offset + 4]; + + // get the other + other = 0xff & stabData[offset + 5]; + + // get the desc + if (isLe) { + int a = stabData[offset + 8] << 8; + int b = stabData[offset + 7]; + int c = a + b; + //desc = (short) ((stabData[offset + 7] << 8) + stabData[offset + 6]); + desc = (short) ((stabData[offset + 8] << 8) + stabData[offset + 7]); + } else { + desc = (short) ((stabData[offset + 6] << 8) + stabData[offset + 7]); + } + + // get the value + if (isLe) { + value = + (((stabData[offset + 11] & 0xff) << 24) + + ((stabData[offset + 10] & 0xff) << 16) + + ((stabData[offset + 9] & 0xff) << 8) + + (stabData[offset + 8] & 0xff)); + } else { + value = + (((stabData[offset + 8] & 0xff) << 24) + + ((stabData[offset + 9] & 0xff) << 16) + + ((stabData[offset + 10] & 0xff) << 8) + + (stabData[offset + 11] & 0xff)); + } + + // Parse the string + switch (type) { + case StabConstant.N_GSYM : + case StabConstant.N_LSYM : + case StabConstant.N_PSYM : + Variable variable = new Variable(name); + variable.kind = type; + variable.addr = value; + variable.startLine = desc; + variable.function = currentFunction; + variable.filename = currentFile; + entries.add(variable); + if (currentFunction != null) { + currentFunction.variables.add(variable); + } + break; + case StabConstant.N_SLINE : + if (currentFunction != null) { + currentFunction.endLine = desc; + currentFunction.lines.add(new Integer(desc)); + } + break; + case StabConstant.N_FUN : + if (name.length() == 0 || desc == 0) { + currentFunction = null; + } else { + currentFunction = new Function(name); + currentFunction.addr = value; + currentFunction.startLine = desc; + currentFunction.filename = currentFile; + entries.add(currentFunction); + } + break; + case StabConstant.N_LBRAC : + bracket++; + break; + case StabConstant.N_RBRAC : + bracket--; + break; + case StabConstant.N_BINCL : + Include include = new Include(name); + include.index = includeCount++; + entries.add(include); + break; + case StabConstant.N_EINCL : + break; + case StabConstant.N_SO : + if (name.length() == 0) { + currentFile = name; + } else { + if (currentFile != null && currentFile.endsWith("/")) { + currentFile += name; + } else { + currentFile = name; + } + } + break; + } + //System.out.println(" " + i + "\t" + Stab.type2String(type) + "\t" + other + "\t\t" + + // desc + "\t" + Long.toHexString(value) + "\t" + + stroff + "\t\t" +name); + } + } + + /** + * Format: string_field = name ':' symbol-descriptor type-information type-information = type_number [ '=' ( type_description | + * type_reference )] type_number = number | '(' number ',' number ')' type_reference type_descriptor = + */ + private void parseString(String s, StringField field) { + // Some String field may contain format like: + // "foo::bar::baz:t5=*6" in that case the name is "foo::bar::baz" + int index = s.lastIndexOf(':'); + + if (index > 0) { + field.name = s.substring(0, index).trim(); + index++; + // Advance the string. + s = s.substring(index); + } else { + field.name = s; + s = new String(); + } + + // get the symbol descriptor + for (index = 0; index < s.length(); index++) { + char c = s.charAt(index); + if (!(Character.isLetter(c) || c == ':' || c == '-')) { + break; + } + } + if (index > 0) { + field.symbolDescriptor = s.substring(0, index); + index++; + // Advance the string. + s = s.substring(index); + } else { + field.symbolDescriptor = new String(); + } + + field.typeDefinition = s; + + } + + private void parseType(String s) { + int index = s.indexOf(':'); + if (index != -1) { + } + // "name:symbol-descriptor type-information" + } + + public void print() { + for (int i = 0; i < entries.size(); i++) { + Entry entry = (Entry) entries.get(i); + System.out.println(entry); + } + } + + public static void main(String[] args) { + try { + Elf.Section stab = null; + Elf.Section stabstr = null; + Elf exe = new Elf(args[0]); + Elf.Section[] sections = exe.getSections(); + for (int i = 0; i < sections.length; i++) { + String name = sections[i].toString(); + if (name.equals(".stab")) { + stab = sections[i]; + } else if (name.equals(".stabstr")) { + stabstr = sections[i]; + } + } + if (stab != null && stabstr != null) { + long nstab = stab.sh_size / StabConstant.SIZE; + System.out.println("Number of stabs" + nstab); + byte[] array = stab.loadSectionData(); + byte[] strtab = stabstr.loadSectionData(); + Stabs stabs = new Stabs(array, strtab, true); + stabs.parse(); + stabs.print(); + } + } catch (IOException e) { + e.printStackTrace(); + } + + } +} diff --git a/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/StabsAddr2line.java b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/StabsAddr2line.java new file mode 100644 index 00000000000..0e77ee3c1de --- /dev/null +++ b/core/org.eclipse.cdt.core/utils/org/eclipse/cdt/utils/stabs/StabsAddr2line.java @@ -0,0 +1,160 @@ +/********************************************************************** + * Copyright (c) 2002,2003 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + ***********************************************************************/ + +package org.eclipse.cdt.utils.stabs; + +import java.io.IOException; + +import org.eclipse.cdt.utils.elf.Elf; + +/** + * StabsAddr2ine + * + * @author alain + */ +public class StabsAddr2line { + + Stabs stabs; + long lastAddress; + Stabs.Entry entry; + + public StabsAddr2line(byte[] stab, byte[] stabstr, boolean le) throws IOException { + stabs = new Stabs(stab, stabstr, le); + } + + /* + * (non-Javadoc) + * + * @see IAddr2line#dispose() + */ + public void dispose() { + } + + /* + * (non-Javadoc) + * + * @see IAddr2line#getStartLine(long) + */ + public int getStartLine(long address) throws IOException { + if (address != lastAddress || entry == null) { + Stabs.Entry[] entries = stabs.getEntries(); + for (int i = 0; i < entries.length; i++) { + if (entries[i].addr == address) { + lastAddress = address; + entry = entries[i]; + break; + } + } + } + if (address == lastAddress && entry != null) { + return entry.startLine; + } + return 0; + } + + /* + * (non-Javadoc) + * + * @see IAddr2line#getEndLine(long) + */ + public int getEndLine(long address) throws IOException { + if (address != lastAddress || entry == null) { + Stabs.Entry[] entries = stabs.getEntries(); + for (int i = 0; i < entries.length; i++) { + if (entries[i].addr == address) { + lastAddress = address; + entry = entries[i]; + break; + } + } + } + if (address == lastAddress && entry != null) { + if (entry instanceof Stabs.Function) { + return ((Stabs.Function)entry).endLine; + } + return entry.startLine; + } + return 0; + } + + /* + * (non-Javadoc) + * + * @see IAddr2line#getFunction(long) + */ + public String getFunction(long address) throws IOException { + if (address != lastAddress || entry == null) { + Stabs.Entry[] entries = stabs.getEntries(); + for (int i = 0; i < entries.length; i++) { + if (entries[i].addr == address) { + lastAddress = address; + entry = entries[i]; + break; + } + } + } + if (address == lastAddress && entry != null) { + return entry.string; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see IAddr2line#getFileName(long) + */ + public String getFileName(long address) throws IOException { + if (address != lastAddress || entry == null) { + Stabs.Entry[] entries = stabs.getEntries(); + for (int i = 0; i < entries.length; i++) { + if (entries[i].addr == address) { + lastAddress = address; + entry = entries[i]; + break; + } + } + } + if (address == lastAddress && entry instanceof Stabs.LocatableEntry) { + return ((Stabs.LocatableEntry)entry).filename; + } + return null; + } + + public static void main(String[] args) { + try { + Elf.Section stab = null; + Elf.Section stabstr = null; + Elf exe = new Elf(args[0]); + Elf.Section[] sections = exe.getSections(); + for (int i = 0; i < sections.length; i++) { + String name = sections[i].toString(); + if (name.equals(".stab")) { + stab = sections[i]; + } else if (name.equals(".stabstr")) { + stabstr = sections[i]; + } + } + if (stab != null && stabstr != null) { + long nstab = stab.sh_size / StabConstant.SIZE; + System.out.println("Number of stabs" + nstab); + byte[] array = stab.loadSectionData(); + byte[] strtab = stabstr.loadSectionData(); + StabsAddr2line addr2line = new StabsAddr2line(array, strtab, true); + long address = Integer.decode(args[1]).longValue(); + addr2line.getStartLine(address); + } + } catch (IOException e) { + e.printStackTrace(); + } + + } +}