view src/codeview/parser.d @ 1:4a9dcbd9e54f

-files of 0.13 beta -fixes so that it now compiles with the current dmd version
author marton@basel.hu
date Tue, 05 Apr 2011 20:44:01 +0200
parents
children
line wrap: on
line source

/*  Ddbg - Win32 Debugger for the D programming language
 *  Copyright (c) 2007 Jascha Wetzel
 *  All rights reserved. See LICENSE.TXT for details.
 */

module codeview.parser;

import codeview.codeview;
import codeview.decl;
import codeview.coff;

import util;
import container;

import std.demangle;
import std.string;

class CodeViewException : Exception
{
	this(string msg) { super(msg); }
}

class CodeViewParser
{
	const uint cv_nb09_sig = intFromStr("NB09");

static:
    /**********************************************************************************************
        Parses the given CodeView section data from the given COFF image.
    **********************************************************************************************/
	CodeView parse(COFFImage img, ubyte[] data)
	{
		if ( data.length <= 0 )
			return null;
		CodeView cv = new CodeView;

		cv.image = img;
		cv.global_pub = new SymbolSet;
		cv.global_sym = new SymbolSet;
		cv.static_sym = new SymbolSet;

		DataReader dr = new DataReader(data);
		uint sig;
		dr.read(sig);
		if ( sig != cv_nb09_sig ) {
			char[4] sigstr;
			sigstr[] = (cast(char*)&sig)[0..4];
			debug DbgIO.println("Unsupported CodeView version %s", sigstr);
			return null;
		}

		uint lfoDir;
		dr.read(lfoDir);
		dr.seek(lfoDir);

		ubyte[] buf;
		dr.readA(buf, DirHeader.sizeof);
		DirHeader* head = cast(DirHeader*)buf.ptr;
		assert(head.cbDirHeader==DirHeader.sizeof);
		assert(head.cbDirEntry==DirEntry.sizeof);

		DirEntry[] entries;
		dr.readA(entries, head.cDir);

		foreach ( DirEntry entry; entries )
		{
			ubyte[] section_data;
			dr.seek(entry.lfo);
			dr.readA(section_data, entry.cb);

			switch ( entry.subsection )
			{
				case sstModule:
					Module mod = parseModule(cv, section_data);
					assert ( entry.iMod-1 == cv.modulesByIndex.length );
					cv.modulesByIndex ~= mod;
					cv.modulesByName[mod.name] = mod;
					debug(cvsections) DbgIO.println("sstModule section \"%s\"", mod.name);
					break;
				case sstAlignSym:
					debug(cvsections) DbgIO.println("sstAlignSym section");
					assert ( entry.iMod <= cv.modulesByIndex.length );
					Module mod = cv.modulesByIndex[entry.iMod-1];
					parseSymbols(cv, section_data, mod.symbols, true, mod);
					break;
				case sstSrcModule:
					debug(cvsections) DbgIO.println("sstSrcModule section");
					assert( entry.iMod <= cv.modulesByIndex.length );
					parseSrcModule(cv, section_data, cv.modulesByIndex[entry.iMod-1]);
					break;
				case sstLibraries:
					debug(cvsections) DbgIO.println("sstLibraries section");
					parseLibraries(cv, section_data);
					break;
				case sstGlobalSym:
					debug(cvsections) DbgIO.println("sstGlobalSym section");
					parsePackedSymbols(cv, section_data, cv.global_sym);
					break;
				case sstGlobalPub:
					debug(cvsections) DbgIO.println("sstGlobalPub section");
					parsePackedSymbols(cv, section_data, cv.global_pub);
					break;
				case sstStaticSym:
					debug(cvsections) DbgIO.println("sstStaticSym section");
					parsePackedSymbols(cv, section_data, cv.static_sym);
					break;
				case sstGlobalTypes:
					debug(cvsections) DbgIO.println("sstGlobalTypes section");
					parseGlobalTypes(cv, section_data, entry.lfo);
					break;
				case sstSegMap:
					debug(cvsections) DbgIO.println("sstSegMap section");
					break;
				case sstSegName:
					debug(cvsections) DbgIO.println("sstSegName section");
					break;
				case sstFileIndex:
					debug(cvsections) DbgIO.println("sstFileIndex section");
					break;
				case sstSymbols:
				case sstTypes:
				case sstPublic:
				case sstPublicSym:
				case sstSrcLnSeg:
				case sstMPC:
				case sstPreComp:
				case sstPreCompMap:
				case sstOffsetMap16:
				case sstOffsetMap32:
					debug DbgIO.println("Unprocessed CV section 0x%x", entry.subsection);
					break;
				default:
					debug DbgIO.println("Unknown CV section 0x%x", entry.subsection);
			}
		}

		updateSymbols(cv, cv.global_pub.named_symbols);
		updateSymbols(cv, cv.global_sym.named_symbols);
		updateSymbols(cv, cv.static_sym.named_symbols);
		foreach ( m; cv.modulesByIndex )
			updateSymbols(cv, m.symbols.named_symbols);

        cv.globalNamedSymbols = new AVLTree!(NamedSymbol);
        foreach ( ns; cv.global_sym.named_symbols )
            cv.globalNamedSymbols.insert(ns);
        foreach ( ns; cv.global_pub.named_symbols )
            cv.globalNamedSymbols.insert(ns);
        foreach ( ns; cv.static_sym.named_symbols )
            cv.globalNamedSymbols.insert(ns);

        cv.updateCodeblocks();

		return cv;
	}

private:
    T min(T)(T a, T b)
    {
        return a<b?a:b;
    }

    /**********************************************************************************************
        Parse a Module from the given data.
    **********************************************************************************************/
	Module parseModule(CodeView cv, ubyte[] data)
	{
		if ( data.length <= 0 )
			throw new CodeViewException("parseModule on empty data");
		DataReader dr = new DataReader(data);
		Module mod = new Module(cv);
		ubyte[] buf;
		dr.readA(buf, ModuleHeader.sizeof);
		mod.header = cast(ModuleHeader*)buf.ptr;
		assert(mod.header.Style==intFromStr("CV"));
		dr.readA!(SegInfo)(mod.seginfos, mod.header.cSeg);
		dr.read(mod.name);
		return mod;
	}

    /**********************************************************************************************

    **********************************************************************************************/
	void parseSymbols(CodeView cv, ubyte[] data, SymbolSet symbols, bool skip_to_ssearch=false, Module mod=null)
	{
		if ( data.length <= 0 )
			throw new CodeViewException("parseSymbols on empty data");
		DataReader dr = new DataReader(data);

		uint num_sym;

		// skip until S_SSEARCH symbol is found
		while ( skip_to_ssearch && dr.available )
		{
			ushort	length, index;
			dr.read(length);
			dr.read(index);
			if ( index == SymbolIndex.S_SSEARCH ) {
				dr.relseek(-4);
				break;
			}
		}

		uint sym_off;
		bool expecting_arguments=false;
		ScopeSymbol[uint]	scope_syms;
		ScopeSymbol			parent_scope;

		while ( dr.available )
		{
			ushort	length, index;
			uint	next_symbol, symbol_start;

			void addSymbol(Symbol sym)
			{
				assert( !expecting_arguments || parent_scope !is null );
				if ( parent_scope !is null )
				{
					if ( expecting_arguments )
						(cast(ProcedureSymbol)parent_scope).arguments.add(sym);
					else
						parent_scope.symbols.add(sym);
				}
				else
					symbols.add(sym);
			}

			symbol_start = dr.cursor;
			dr.read(length);
			next_symbol = dr.cursor+length;
            if ( next_symbol > dr.data.length ) {
                debug DbgIO.println("WARNING: length %d for symbol exceeds block size %d - skipping section", length, dr.data.length);
                if ( mod !is null )
                debug DbgIO.println("in module %s", mod.name);
                return;
            }
			dr.read(index);

			ubyte[] buf;
			switch ( index )
			{
				case SymbolIndex.S_COMPILE:
					ubyte	machine,
							language;
					ushort	flags;
					dr.read(machine);
					dr.read(language);
					dr.read(flags);
					string version_string;
					dr.read(version_string);
					debug(cvsymbols) DbgIO.println("machine: 0x%x, language: %d, flags: 0x%x, version: %s", machine, language, flags, version_string);
					break;
				case SymbolIndex.S_SSEARCH:
					dr.read(sym_off);
					ushort pe_section;
					dr.read(pe_section);
					if ( mod !is null )
						mod.pe_section = pe_section;
					break;
				case SymbolIndex.S_END:
					debug(cvsymbols) DbgIO.println("S_END");
					if ( parent_scope !is null )
					{
						if ( parent_scope.parent_scope !is null )
							parent_scope = parent_scope.parent_scope;
						else
							parent_scope = null;
					}
					else {
						debug DbgIO.println("S_END with no active parent scope");
					}
					expecting_arguments = false;
					break;
				case SymbolIndex.S_ENDARG:
					expecting_arguments = false;
					break;
				case SymbolIndex.S_RETURN:
					dr.readA(buf, CVReturnSymbol.sizeof);
					ReturnSymbol rsym = new ReturnSymbol(cast(CVReturnSymbol*)buf.ptr);
					if ( rsym.cvdata.style == 1 ) {
						ubyte count;
						dr.read(count);
						dr.readA!(ubyte)(rsym.registers, count);
					}
					ProcedureSymbol psym = cast(ProcedureSymbol)parent_scope;
					assert(psym !is null);
					psym.return_sym = rsym;
					break;
				case SymbolIndex.S_LPROC32:
				case SymbolIndex.S_GPROC32:
					dr.readA(buf, CVProcedureSymbol.sizeof);
					ProcedureSymbol psym = new ProcedureSymbol(cast(SymbolIndex)index, symbol_start, cast(CVProcedureSymbol*)buf.ptr);
					dr.read(psym.mangled_name);
					psym.name_notype = demangleName(psym.mangled_name);
					psym.name_type = demangle(psym.mangled_name);
					debug(cvsymbols) DbgIO.println("PROC32 %s (%s) s:%x o:%x", psym.name_type, psym.name_notype, psym.cvdata.segment, psym.cvdata.offset);
					scope_syms[symbol_start] = psym;
					addSymbol(psym);
					assert ( parent_scope is null || psym.cvdata.pParent==parent_scope.lfo );
					if ( psym.cvdata.pParent in scope_syms )
					{
						psym.parent_scope = scope_syms[psym.cvdata.pParent];
						psym.parent_scope.symbols.add(psym);
					}
					parent_scope = psym;
					expecting_arguments = true;
					break;
				case SymbolIndex.S_BPREL32:
					dr.readA(buf, CVStackSymbol.sizeof);
					StackSymbol stsym = new StackSymbol(cast(CVStackSymbol*)buf.ptr);
					dr.read(stsym.mangled_name);
					stsym.name_notype = demangleName(stsym.mangled_name);
					stsym.name_type = demangle(stsym.mangled_name);
					debug(cvsymbols) DbgIO.println(
						"BPREL32 %s%s [%s] 0x%x o:%d", parent_scope !is null?"("~parent_scope.name_type~") ":"",
						stsym.name_type, stsym.name_notype, stsym.cvtype, cast(int)stsym.cvdata.offset
					);
					addSymbol(stsym);
					break;
				case SymbolIndex.S_LDATA32:
				case SymbolIndex.S_GDATA32:
				case SymbolIndex.S_PUB32:
					dr.readA(buf, CVDataSymbol.sizeof);
					DataSymbol dsym = new DataSymbol(cast(SymbolIndex)index, cast(CVDataSymbol*)buf.ptr);
					dr.read(dsym.mangled_name);
					dsym.name_notype = demangleName(dsym.mangled_name);
					dsym.name_type = demangle(dsym.mangled_name);
					debug(cvsymbols) DbgIO.println("[LG]DATA|PUB32 %s (%s) s:%x o:%x t:0x%x", dsym.name_type, dsym.name_notype, dsym.cvdata.segment, dsym.offset, dsym.cvtype);
					addSymbol(dsym);
					break;
				case SymbolIndex.S_ALIGN:
					debug(cvsymbols) DbgIO.println("ALIGN");
					break;
				case SymbolIndex.S_UDT:
					UserDefinedType udt = new UserDefinedType;
					dr.read(udt.type_index);
					dr.read(udt.name);
					cv.udtypes ~= udt;
					break;
                case SymbolIndex.S_PROCREF:
                case SymbolIndex.S_DATAREF:
                    break;
				case SymbolIndex.S_REGISTER:
				case SymbolIndex.S_CONSTANT:
				case SymbolIndex.S_SKIP:
				case SymbolIndex.S_CVRESERVE:
				case SymbolIndex.S_OBJNAME:
				case SymbolIndex.S_COBOLUDT:
				case SymbolIndex.S_MANYREG:
				case SymbolIndex.S_ENTRYTHIS:
				case SymbolIndex.S_BPREL16:
				case SymbolIndex.S_LDATA16:
				case SymbolIndex.S_PUB16:
				case SymbolIndex.S_LPROC16:
				case SymbolIndex.S_GPROC16:
				case SymbolIndex.S_THUNK16:
				case SymbolIndex.S_BLOCK16:
				case SymbolIndex.S_WITH16:
				case SymbolIndex.S_LABEL16:
				case SymbolIndex.S_CEXMODEL16:
				case SymbolIndex.S_VFTPATH16:
				case SymbolIndex.S_REGREL16:
				case SymbolIndex.S_GDATA16:
				case SymbolIndex.S_THUNK32:
				case SymbolIndex.S_BLOCK32:
				case SymbolIndex.S_WITH32:
				case SymbolIndex.S_LABEL32:
				case SymbolIndex.S_CEXMODEL32:
				case SymbolIndex.S_VFTPATH32:
				case SymbolIndex.S_REGREL32:
				case SymbolIndex.S_LTHREAD32:
				case SymbolIndex.S_GTHREAD32:
					debug DbgIO.println("unprocessed symbol index 0x%x", index);
					break;
				default:
					debug DbgIO.println("unknown symbol index 0x%x", index);
			}

            dr.seek(next_symbol);
		}
	}

    /**********************************************************************************************

    **********************************************************************************************/
	void parseSrcModule(CodeView cv, ubyte[] data, Module mod)
	{
		if ( data.length <= 0 )
			throw new CodeViewException("parseSrcModule on empty data");
		DataReader dr = new DataReader(data);
		ushort cFile, cSeg;
		dr.read(cFile);
		dr.read(cSeg);

		SourceModule smod = new SourceModule;
		mod.source_module = smod;
		cv.source_modules ~= smod;

        uint[]	    baseSrcFile,
                    start_end;
        ushort[]	seginds;
		dr.readA(baseSrcFile, cast(uint)cFile);
		dr.readA(start_end, cast(uint)cSeg*2);
		dr.readA(seginds, cast(uint)cSeg);
		debug(cvparser) DbgIO.println("Module %s files=%d segs=%d", mod.name, cFile, cSeg);

		foreach ( fileoffset; baseSrcFile )
		{
			dr.seek(fileoffset);
			dr.read(cSeg);
			ushort pad;
			dr.read(pad);

			SourceFile file = new SourceFile;
			file.source_module = smod;
            uint[]  baseSrcLn;
			dr.readA(baseSrcLn, cast(uint)cSeg);
			start_end = null;
			dr.readA(start_end, cast(uint)cSeg*2);
			dr.read(file.name);
            debug(cvparser) DbgIO.println("\tFile %s segs=%d", file.name, cSeg);
			smod.files ~= file;

			foreach ( int i, segoff; baseSrcLn )
			{
				dr.seek(segoff);
				SourceSegment seg = new SourceSegment;
				file.segments ~= seg;
				seg.file = file;

				seg.start  = start_end[i*2];
				seg.end    = start_end[i*2+1];

				dr.read(pad);   // segment index
				ushort cPair;
				dr.read(cPair);
                debug(cvparser) DbgIO.println("\t\tSegment %d lines=%d", file.segments.length-1, cPair);

                uint[]		offset;
                ushort[]	linenumber;
				dr.readA(offset, cast(uint)cPair);
				dr.readA(linenumber, cast(uint)cPair);

                foreach ( int j, l; linenumber ) {
                    CodeBlock cb = new CodeBlock(offset[j], l, seg);
                    file.blocks_by_line[l] ~= cb;
                    debug(cvparser)
                    {
                        bool inserted = cv.codeblocks.insert(cb);
                        if ( !inserted )
                            DbgIO.println("failed to insert block %s, already exists", cb);
                    }
                    else
                        cv.codeblocks.insert(cb);
                }
			}

			file.lines = file.blocks_by_line.keys.dup.sort;
		}
	}

    /**********************************************************************************************

    **********************************************************************************************/
	void parseLibraries(CodeView cv, ubyte[] data)
	{
		if ( data.length <= 0 )
			throw new CodeViewException("parseLibraries on empty data");
		DataReader dr = new DataReader(data);
		while ( dr.available ) {
			string lib;
			dr.read(lib);
			cv.libraries ~= lib;
		}
	}

    /**********************************************************************************************

    **********************************************************************************************/
	void parsePackedSymbols(CodeView cv, ubyte[] data, SymbolSet symbols)
	{
		if ( data.length <= 0 )
			throw new CodeViewException("parsePackedSymbols on empty data");
		DataReader dr = new DataReader(data);

		ubyte[] buf;
		dr.readA(buf, PackedSymbolsHeader.sizeof);
		PackedSymbolsHeader* gsh = cast(PackedSymbolsHeader*)buf.ptr;

		dr.readA(buf, gsh.cbSymbol);
		parseSymbols(cv, buf, symbols);
	}

    /**********************************************************************************************

    **********************************************************************************************/
	void parseGlobalTypes(CodeView cv, ubyte[] data, uint absolute_offset)
	{
		DataReader dr = new DataReader(data);
		uint	flags,
				cType;
		uint[]	offType;
		dr.read(flags);
		dr.read(cType);
		dr.readA(offType, cType);

		uint	base = dr.cursor;
		foreach ( off; offType )
		{
			dr.seek(base+off);
			ushort	len;
			dr.read(len);
			ubyte[] buf;
			dr.readA(buf, cast(uint)len);
			cv.type_strings ~= parseTypeString(cv, buf);
		}
	}

    /**********************************************************************************************

    **********************************************************************************************/
	Leaf[] parseTypeString(CodeView cv, ubyte[] data)
	{
		debug(cvdump) foreach ( b; data )
			DbgIO.print("%02x ", b);
		debug(cvdump) DbgIO.println("");
		DataReader dr = new DataReader(data);
		Leaf[] leafs;
		bool first = true;
		while ( dr.available )
		{
			ushort leaf_index;
			dr.read(leaf_index);
			Leaf l;
//			debug(cvparser) DbgIO.println("parsing 0x%x", leaf_index);
			switch ( leaf_index )
			{
				case LF_MODIFIER_16t:
					LeafModifer lm = new LeafModifer;
					l = lm;
					dr.read(lm.attribute);
					dr.read(lm.index);
					break;
				case LF_POINTER_16t:
					LeafPointer lp = new LeafPointer;
					l = lp;
					dr.read(lp.attribute);
					dr.read(lp.type);
					switch ( lp.attribute&0x1f )
					{
						case 0:
						case 10:
							break;
						default:
							debug DbgIO.println("unprocessed pointer type: 0x%x", lp.attribute);
							assert(0);
					}
					break;
				case LF_ARRAY_16t:
					LeafArray la = new LeafArray;
					l = la;
					dr.read(la.elemtype);
					dr.read(la.idxtype);
					la.length = parseNumericLeaf(dr);
					dr.read(la.name);
					debug(cvparser) DbgIO.println("LF_ARRAY: %d 0x%x", la.length.getUint, 0x1000+cv.type_strings.length);
					break;
				case LF_PROCEDURE_16t:
					LeafProcedure lp = new LeafProcedure;
					l = lp;
					dr.read(lp.rvtype);
					dr.read(lp.call);
					dr.read(lp.reserved);
					dr.read(lp.cParms);
					dr.read(lp.arglist);
					break;
				case LF_MFUNCTION_16t:
					LeafMFunction lmf = new LeafMFunction;
					l = lmf;
					dr.read(lmf.rvtype);
					dr.read(lmf._class);
					dr.read(lmf._this);
					dr.read(lmf.call);
					dr.read(lmf.reserved);
					dr.read(lmf.cParms);
					dr.read(lmf.arglist);
					dr.read(lmf.thisadjust);
					break;
				case LF_VTSHAPE:
					LeafVTShape lvts = new LeafVTShape;
					l = lvts;
					ushort count;
					dr.read(count);
					dr.readA(lvts.descriptor, cast(uint)(count+1)>>1);
					// skip rest of typestring
					dr.seek(dr.data.length);
					break;
				case LF_CLASS_16t:
				case LF_STRUCTURE_16t:
					LeafClassStruc lcs = new LeafClassStruc;
					l = lcs;
					dr.read(lcs.count);
					dr.read(lcs.field);
					dr.read(lcs.property);
					dr.read(lcs.dList);
					dr.read(lcs.vshape);
					lcs.length = parseNumericLeaf(dr);
					dr.read(lcs.name);
					lcs.type = cast(ushort)(0x1000+cv.type_strings.length);
					debug(cvparser) DbgIO.println(
						"%s 0x%x '%s' fl: 0x%x prop: 0x%x, length=0x%x", leaf_index==LF_CLASS_16t?"class":"struct",
						lcs.type, lcs.name, lcs.field, lcs.property, lcs.length.getUint
					);

					cv.UDTsByName[lcs.name] = lcs;
					break;
				case LF_ENUM_16t:
					LeafEnum le = new LeafEnum;
					l = le;
					dr.read(le.count);
					dr.read(le.type);
					dr.read(le.field);
					dr.read(le.property);
					dr.read(le.name);
					debug(cvparser) DbgIO.println(
						"enum 0x%x '%s' type: 0x%x fl: 0x%x prop: 0x%x", 0x1000+cv.type_strings.length, le.name, le.type, le.field, le.property
					);
					cv.UDTsByName[le.name] = le;
					break;
				case LF_UNION_16t:
					LeafUnion lu = new LeafUnion;
					l = lu;
					dr.read(lu.count);
					dr.read(lu.field);
					dr.read(lu.property);
					lu.length = parseNumericLeaf(dr);
					dr.read(lu.name);
					// skip rest of typestring
					dr.seek(dr.data.length);
					debug(cvparser) DbgIO.println("Union '%s' count %d fields 0x%x length 0x%x", lu.name, lu.count, lu.field, lu.length.getUint);
					cv.UDTsByName[lu.name] = lu;
					break;
                case LF_OEM_16t:
                    ushort oem;
                    dr.read(oem);
                    assert(oem == OEM_DIGITALMARS);
                    dr.read(oem);
                    ushort count;
                    dr.read(count);
                    assert(count == 2);
                    switch ( oem )
                    {
                        case D_DYN_ARRAY:
                            LeafDynArray lda = new LeafDynArray;
                            l = lda;
                            dr.read(lda.index_type);
                            dr.read(lda.elem_type);
                            debug(cvparser) DbgIO.println("Dynamic array index_type 0x%x elem_type 0x%x", lda.index_type, lda.elem_type);
                            break;
                        case D_ASSOC_ARRAY:
                            LeafAssocArray laa = new LeafAssocArray;
                            l = laa;
                            dr.read(laa.key_type);
                            dr.read(laa.elem_type);
                            debug(cvparser) DbgIO.println("Associativ array key_type 0x%x elem_type 0x%x", laa.key_type, laa.elem_type);
                            break;
                        default:
                            LeafDelegate ld = new LeafDelegate;
                            l = ld;
                            dr.read(ld.this_type);
                            dr.read(ld.func_type);
                            debug(cvparser) DbgIO.println("Delegate this_type 0x%x func_type 0x%x", ld.this_type, ld.func_type);
                            break;
                    }
                    break;

				case LF_ARGLIST_16t:
					LeafArgList lal = new LeafArgList;
					l = lal;
					dr.read(lal.argcount);
					dr.readA(lal.indeces, cast(uint)lal.argcount);
					break;
				case LF_FIELDLIST_16t:
					debug(cvparser) DbgIO.println("fieldlist 0x%x", 0x1000+cv.type_strings.length);
					LeafFieldList lfl = new LeafFieldList;
					l = lfl;
					while ( dr.available )
						lfl.fields ~= parseSubfield(dr);
					// skip rest of typestring
					dr.seek(dr.data.length);
					break;
				case LF_DERIVED_16t:
					LeafDerived ld = new LeafDerived;
					l = ld;
					ushort count;
					dr.read(count);
					dr.readA(ld.types, cast(uint)count);
					break;
				case LF_METHODLIST_16t:
					LeafMethodList lml = new LeafMethodList;
					l = lml;
					// skip rest of typestring
					dr.seek(dr.data.length);
					break;
				default:
					debug DbgIO.println("unprocessed leaf index 0x%x at 0x%x", leaf_index, dr.cursor-2);
					l = new Leaf;
			}
			assert ( l !is null );
			l.leaf_index = leaf_index;
			leafs ~= l;
		}
		return leafs;
	}

    /**********************************************************************************************

    **********************************************************************************************/
	Leaf parseSubfield(DataReader dr)
	{
		ushort leaf_index;
		dr.read(leaf_index);
		Leaf l;
		switch ( leaf_index )
		{
			case LF_BCLASS_16t:
				LeafBaseClass lbc = new LeafBaseClass;
				l = lbc;
				dr.read(lbc.type);
				dr.read(lbc.attribute);
				lbc.offset = parseNumericLeaf(dr);
				debug(cvparser) DbgIO.println("SFbclass");
				break;
			case LF_MEMBER_16t:
				LeafMember lm = new LeafMember;
				l = lm;
				dr.read(lm.type);
				dr.read(lm.attribute);
				lm.offset = parseNumericLeaf(dr);
				dr.read(lm.name);
				debug(cvparser) DbgIO.println("SFmember: %s", lm.name);
				break;
			case LF_METHOD_16t:
				LeafMethod lm = new LeafMethod;
				l = lm;
				dr.read(lm.count);
				dr.read(lm.mList);
				dr.read(lm.name);
				debug(cvparser) DbgIO.println("SFmethod: %d 0x%x %s", lm.count, lm.mList, lm.name);
				break;
			case LF_ENUMERATE:
				LeafEnumNameValue le = new LeafEnumNameValue;
				l = le;
				dr.read(le.attribute);
				le.value = parseNumericLeaf(dr);
				dr.read(le.name);
				debug(cvparser) DbgIO.println("SFenumerate: %s %d", le.name, le.value.getUint);
				break;
			case LF_NESTTYPE_16t:
				LeafNestedType ln = new LeafNestedType;
				l = ln;
				dr.read(ln.index);
				dr.read(ln.name);
				debug(cvparser) DbgIO.println("SFnesttype: %s", ln.name);
				break;
			case LF_STMEMBER_16t:
				LeafStaticDataMember ls = new LeafStaticDataMember;
				l = ls;
				dr.read(ls.type);
				dr.read(ls.attribute);
				dr.read(ls.name);
				debug(cvparser) DbgIO.println("SFstmember: %s", ls.name);
				break;
			case LF_SKIP_16t:
				dr.seek(dr.data.length);
				debug(cvparser) DbgIO.println("SFskip");
				break;
			default:
				debug DbgIO.println("unprocessed subfield index 0x%x", leaf_index);
				l = new Leaf;
		}
		if ( dr.available )
		{
			ubyte pad;
			dr.peek(pad);
			if ( pad > 0xf0 )
				dr.relseek(pad&0xf);
		}
		assert ( l !is null );
		l.leaf_index = leaf_index;
		return l;
	}

    /**********************************************************************************************

    **********************************************************************************************/
	LeafNumeric parseNumericLeaf(DataReader dr)
	{
		LeafNumeric nl = new LeafNumeric;
		dr.read(nl.leaf_index);
		if ( nl.leaf_index < 0x8000 ) {
			nl.us = cast(ushort)nl.leaf_index;
			nl.leaf_index = LF_USHORT;
		}
		else switch ( nl.leaf_index )
		{
			case LF_VARSTRING:	dr.read(nl.str);	break;
			case LF_CHAR:		dr.read(nl.c);	break;
			case LF_SHORT:		dr.read(nl.s);	break;
			case LF_USHORT:		dr.read(nl.us);	break;
			case LF_LONG:		dr.read(nl.i);	break;
			case LF_ULONG:		dr.read(nl.ui);	break;
			case LF_REAL32:		dr.read(nl.f);	break;
			case LF_REAL64:		dr.read(nl.d);	break;
			case LF_REAL80:		dr.read(nl.r);	break;
			case LF_QUADWORD:	dr.read(nl.l);	break;
			case LF_UQUADWORD:	dr.read(nl.ul);	break;
			case LF_COMPLEX32:	dr.read(nl.cf);	break;
			case LF_COMPLEX64:
			case LF_COMPLEX80:
			case LF_REAL48:
			case LF_COMPLEX128:
			case LF_REAL128:
			default:
				debug DbgIO.println("unknown leaftype %x", nl.leaf_index);
		}
		return nl;
	}

    /**********************************************************************************************
        Updates symbol size fields and mangled_names. Since both can only
        be determined fully after sstGlobalTypes have been read.
    **********************************************************************************************/
	void updateSymbols(CodeView cv, NamedSymbol[] syms)
	{
		foreach ( s; syms )
		{
			if ( s.mangled_name is null || s.mangled_name.length <= 0 )
				s.mangled_name = cv.mangle(s);

			ScopeSymbol scs = cast(ScopeSymbol)s;
			if ( scs !is null )
				updateSymbols(cv, scs.symbols.named_symbols);
			ProcedureSymbol ps = cast(ProcedureSymbol)s;
			if ( ps !is null )
				updateSymbols(cv, ps.arguments.named_symbols);

			string type = cv.mangleType(s);
			if ( type is null )
				continue;

			DataSymbol ds = cast(DataSymbol)s;
			if ( ds !is null )
				ds.size = cv.sizeofCV(ds.cvdata.type);
			else
			{
				StackSymbol ss = cast(StackSymbol)s;
				if ( ss !is null )
					ss.size = cv.sizeofCV(ss.cvdata.type);
			}
		}
	}
}