view src/codeview/codeview.d @ 5:496dfd8f7342 default tip

added: -repeat option for "in", "ov" -run until a line option -run until a function option -break on a function start -n is an alias for ov
author marton@basel.hu
date Sun, 17 Apr 2011 11:05:31 +0200
parents a5fb1bc967e6
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.codeview;

import std.ctype;
import std.string;
import std.math;

import util;
import container;
import codeview.coff;
import codeview.decl;

public import codeview.debuginfo;

//=================================================================================================
// classes for accessing CodeView data

abstract class Symbol
{
	SymbolIndex	symbol_index;

	this(SymbolIndex si) { symbol_index = si; }
}

class ReturnSymbol : Symbol
{
	CVReturnSymbol*	cvdata;
	ubyte[]			registers;

	this(CVReturnSymbol* cv) { super(SymbolIndex.S_RETURN); cvdata = cv; }
}

class StringWrap
{
    string str;

    this(string _str) { str = _str; }

	int opCmp(Object o)
	{
	    NamedSymbol ns = cast(NamedSymbol)o;
	    if ( ns !is null )
            return -ns.opCmp(this);

        StringWrap sw = cast(StringWrap)o;
        if ( sw is null )
            return -1;
        if ( str == sw.str )
            return 0;
	    if ( str < sw.str )
            return -1;
        return 1;
	}
}

abstract class NamedSymbol : Symbol
{
	string				mangled_name,
						name_type,
						name_notype;

    this(SymbolIndex si)    { super(si); }

	int opCmp(Object o)
	{
	    string str;
	    NamedSymbol ns = cast(NamedSymbol)o;
	    if ( ns is null )
	    {
            StringWrap sw = cast(StringWrap)o;
            if ( sw is null )
                return -1;
            str = sw.str;
        }
        else
            str = ns.name_notype;
        if ( name_notype == str )
            return 0;
	    if ( name_notype < str )
            return -1;
        return 1;
	}
}

class StackSymbol : NamedSymbol
{
	CVStackSymbol*	cvdata;
	int				size;

	this(CVStackSymbol* cv)
	{
		super(SymbolIndex.S_BPREL32);
		cvdata=cv;
	}

	uint offset() { return cvdata.offset; }
	uint cvtype() { return cvdata.type; }
}

class DataSymbol : NamedSymbol
{
	CVDataSymbol*	cvdata;
	uint			size;

	this(SymbolIndex si, CVDataSymbol* cv)
	{
		super(si);
		cvdata=cv;
	}

	uint offset() { return cvdata.offset; }
	uint cvtype() { return cvdata.type; }
}

abstract class ScopeSymbol : NamedSymbol
{
	ScopeSymbol		parent_scope;
	SymbolSet		symbols;
	uint			lfo;

	this(SymbolIndex si, uint _lfo) { super(si); lfo = _lfo; symbols = new SymbolSet; }
}

class ProcedureSymbol : ScopeSymbol
{
	CVProcedureSymbol*	cvdata;
	SymbolSet			arguments;
	ReturnSymbol		return_sym;

	this(SymbolIndex si, uint lfo, CVProcedureSymbol* cvd)
	{
		super(si,lfo);
		cvdata = cvd;
		arguments = new SymbolSet;
	}
}

class SymbolSet
{
	ProcedureSymbol[]	proc_symbols;
	StackSymbol[]		stack_symbols;
	DataSymbol[]		data_symbols;
	NamedSymbol[]		named_symbols;
	Symbol[]			symbols;

    void opCatAssign(SymbolSet s)
    {
        symbols ~= s.symbols;
        named_symbols ~= s.named_symbols;
        data_symbols ~= s.data_symbols;
        stack_symbols ~= s.stack_symbols;
        proc_symbols ~= s.proc_symbols;
    }

	void add(Symbol s)
	{
		NamedSymbol ns = cast(NamedSymbol)s;
		if ( ns is null ) {
			symbols ~= s;
			return;
		}
		named_symbols ~= ns;
		ClassInfo ci = s.classinfo;
		if ( ci == ProcedureSymbol.classinfo )
			proc_symbols ~= cast(ProcedureSymbol)s;
		else if ( ci == StackSymbol.classinfo )
			stack_symbols ~= cast(StackSymbol)s;
		else if ( ci == DataSymbol.classinfo )
			data_symbols ~= cast(DataSymbol)s;
	}

	/**********************************************************************************************
        Find procedure symbol covering the given address.
    **********************************************************************************************/
	ProcedureSymbol findProcedureSymbol(uint address)
	{
		foreach ( ps; proc_symbols )
			if ( address >= ps.cvdata.offset && address < ps.cvdata.offset+ps.cvdata.proc_length )
				return ps;
		return null;
	}

	/**********************************************************************************************
        Find data symbol covering the given address.
    **********************************************************************************************/
	DataSymbol findDataSymbol(uint address, uint segment)
	{
		foreach ( ds; data_symbols )
		{
			if ( segment > 0 && ds.cvdata.segment != segment )
				continue;
			if ( address == ds.cvdata.offset )
				return ds;
		}
		return null;
	}

	/**********************************************************************************************
        Find data symbol by name.
    **********************************************************************************************/
	DataSymbol findDataSymbol(string name)
	{
		foreach ( ds; data_symbols )
		{
			if ( ds.name_notype == name )
				return ds;
		}
		return null;
	}

        /**********************************************************************************************
        Find matching data symbols by a substring in its name.
    **********************************************************************************************/
	DataSymbol[] findDataSymbolBySubstring(string name)
	{
                DataSymbol[] found;
		foreach ( ds; data_symbols )
		{
			if ( find(ds.name_notype,name)>=0 )
				found~=ds;
		}
		return found;
	}

	/**********************************************************************************************
        Find nearest data symbol to the given address.
    **********************************************************************************************/
	DataSymbol findNearestDataSymbol(uint address, inout uint min_dist, uint segment)
	{
		DataSymbol min_ds;
		foreach ( ds; data_symbols )
		{
			if ( address < ds.cvdata.offset || ds.cvdata.segment != segment )
				continue;
			uint dist = abs(cast(int)address-cast(int)ds.cvdata.offset);
			if ( dist < min_dist ) {
				min_dist = dist;
				min_ds = ds;
			}
		}
		return min_ds;
	}
}

class Module
{
	ModuleHeader*	header;
	SegInfo[]		seginfos;
	string			name;
	ushort			pe_section;

	SymbolSet		symbols;

	SourceModule	source_module;

    CodeView        codeview;
    
	this(CodeView cv)
    {
        symbols = new SymbolSet;
        codeview = cv;
    }
}

class Location
{
    string      path;

	ScopeSymbol	scope_sym;
	DataSymbol	data_sym;
	Module		mod;
	CodeBlock   codeblock;
    CodeView    codeview;
	uint		address;

	this(uint addr, CodeView cv)
	{
        codeview = cv;
		address = addr;
	}

	this(uint addr)
	{
		address = addr;
	}

	this(string p)
	{
		path = p;
	}

    uint line()
    {
        if ( codeblock is null )
            return 0;
        return codeblock.line;
    }

	string file()
	{
	    if ( codeblock is null || codeblock.segment is null )
            return null;
		return codeblock.segment.file.name;
	}
    
    size_t getCodeBase()
    {
        return mod.codeview.image.getCodeBase;
    }

    bool bind(ImageSet images, string[] source_search_paths)
    {
        if ( path is null )
            return false;

		if ( find(path, '"') >= 0 )
			path = replace(path, "\"", "");

		string[] file_line = split(path, ":");
		if ( file_line is null || file_line.length < 2 || !isNumeric(file_line[$-1]) ) {
		    DbgIO.println("Invalid location format. Use <part of filename>:<linenumber>");
			return false;
		}

		string	file = join(file_line[0..$-1], ":"),
				line = file_line[$-1];

		if ( find(file, '/') >= 0 )
			file = replace(file, "/", "\\");
                debug DbgIO.println("searching in %s", file);
		SourceFile[] sfs = images.findSrcFiles(file);
		if ( sfs.length == 0 )
			sfs = images.findSrcFiles(file, source_search_paths);
		if ( sfs.length == 0 ) {
		    DbgIO.println("Source file \"%s\" not found", file);
			return false;
		}

        uint linenum = cast(uint)atoi(line);
        Location loc;
        foreach ( sf; sfs )
        {
            debug DbgIO.println("searching sf %s", sf.name);
            auto loc2 = images.findSrcLine(sf, linenum);
            if ( loc is null )
                loc = loc2;
            else if ( loc2 !is null && loc2.line < loc.line )
                loc = loc2;
        }
        if ( loc is null )
            DbgIO.println("Line %d in \"%s\" not found", linenum, sfs[0].name);

        scope_sym = loc.scope_sym;
        data_sym = loc.data_sym;
        mod = loc.mod;
        codeblock = loc.codeblock;
        address = loc.address;
        path = null;
        return true;
    }
}

class UserDefinedType
{
	ushort	type_index;
	string	name;
}

/**************************************************************************************************
    Represents the CodeView information of an executable image. Provides methods to browse the
    information. Objects of this class get created by CodeViewParser.parse.
**************************************************************************************************/
class CodeView : DebugInfo
{
	Module[string]	modulesByName;
	Module[]		modulesByIndex;

	SymbolSet		global_pub,
					global_sym,
					static_sym;
	UserDefinedType[]	udtypes;
	Leaf[][]		type_strings;

	Leaf[string]	UDTsByName;

	string[]		libraries,
					segnames;

	COFFImage		image;

    AVLTree!(NamedSymbol)   globalNamedSymbols;

    size_t getCodeBase()
    {
        return image.getCodeBase;
    }
    
	/**********************************************************************************************
        Find the module and segment covering the given address.
    **********************************************************************************************/
	Module findModule(uint vaddress, out uint segment)
	{
		uint address = vaddress-image.getCodeBase;
		foreach ( m; modulesByIndex )
		{
			foreach ( s; m.seginfos )
			{
				if ( address < s.offset )
					continue;
				if ( address-s.offset < s.cbSeg ) {
					segment = s.Seg;
					return m;
				}
			}
		}
		return null;
	}

	/**********************************************************************************************
        Find next source line from the given location.
    **********************************************************************************************/
	Location findNextSrcLine(Location loc)
	{
		if ( loc is null || loc.codeblock is null || loc.codeblock.segment is null )
			return null;
        Location nextloc = findLocation(loc.codeblock.end+image.getCodeBase);
        return nextloc;
	}

	/**********************************************************************************************
        Find previous source line from location that isn't covered by a source codeblock.
    **********************************************************************************************/
	Location findPrevSrcLine(Location loc)
	{
		if ( loc is null )
			return null;
        if ( loc.codeblock !is null )
            return loc;

        AVLNode!(CodeBlock) node;
        if ( !codeblocks.find(loc.address-1+image.getCodeBase, node) )
        {
            if ( node.value.start > loc.address )
            {
                if ( !node.findPrev(node) )
                    return null;
            }
        }
        Location prevloc = new Location(node.value.start, this);
        prevloc.codeblock = node.value;
		findSymbolForLocation(prevloc);
        assert(prevloc.line != 0);
        return prevloc;
	}

	/**********************************************************************************************
        Find first source line in the given file that has a line number >= line
        and a start address >= min_start.
    **********************************************************************************************/
	Location findSrcLine(SourceFile sf, uint min_line, size_t min_start=0)
	{
		if ( sf is null )
			return null;

        pragma(msg, TODO(__FILE__,__LINE__,"use binary search here"));
        foreach ( l; sf.lines )
        {
            if ( l < min_line )
                continue;
            foreach ( cb; sf.blocks_by_line[l] )
            {
                if ( cb.start < min_start || cb.end <= cb.start )
                    continue;
                Location loc = new Location(cb.start+image.getCodeBase, this);
                loc.codeblock = cb;
                findSymbolForLocation(loc);
                return loc;
            }
        }
		return null;
	}

	/**********************************************************************************************
        Find all debug information available for the given address.
    **********************************************************************************************/
	Location findLocation(uint vaddress)
	{
		Location loc = new Location(vaddress, this);
		CodeBlock cb = findCodeBlockAbs(vaddress);
        loc.codeblock = cb;
		findSymbolForLocation(loc);
		return loc;
	}

	/**********************************************************************************************
        Fill out symbol information (as opposed to source line information) in the given location.
    **********************************************************************************************/
	void findSymbolForLocation(Location loc)
	{
		ProcedureSymbol psym = findProcedureSymbol(loc.address);
		if ( psym !is null )
			loc.scope_sym = psym;
		else
		{
			DataSymbol dsym = findDataSymbol(loc.address, 2);
			if ( dsym !is null )
				loc.data_sym = dsym;
		}

		uint seg;
		Module mod = findModule(loc.address, seg);
		if ( mod !is null )
		{
			loc.mod = mod;
			if ( loc.data_sym is null )
			{
				uint	min_dist = uint.max;
				loc.data_sym = global_sym.findNearestDataSymbol(loc.address-image.getCodeBase, min_dist, image.code_section_index+1);
				if ( loc.data_sym is null )
                    loc.data_sym = global_pub.findNearestDataSymbol(loc.address-image.getCodeBase, min_dist, image.code_section_index+1);
			}
		}
	}

	/**********************************************************************************************
        Find procedure symbol covering the given address.
    **********************************************************************************************/
	ProcedureSymbol findProcedureSymbol(uint vaddress)
	{
		uint address = vaddress-image.getCodeBase;
		ProcedureSymbol ps;
		foreach ( m; modulesByIndex )
		{
			ps = m.symbols.findProcedureSymbol(address);
			if ( ps !is null )
				return ps;
		}
		ps = global_sym.findProcedureSymbol(address);
		if ( ps is null )
			ps = static_sym.findProcedureSymbol(address);
		if ( ps is null )
			ps = global_pub.findProcedureSymbol(address);
		return ps;
	}

	/**********************************************************************************************
        Find data symbol covering the given address.
    **********************************************************************************************/
	DataSymbol findDataSymbol(uint vaddress, uint segment=0)
	{
		uint address = vaddress-image.getCodeBase;
		DataSymbol ps;
		foreach ( m; modulesByIndex )
		{
			ps = m.symbols.findDataSymbol(address, segment);
			if ( ps !is null )
				break;
		}
		ps = global_sym.findDataSymbol(address, segment);
		if ( ps is null )
			ps = static_sym.findDataSymbol(address, segment);
		if ( ps is null )
			ps = global_pub.findDataSymbol(address, segment);
		return ps;
	}

	/**********************************************************************************************
        Creates a D mangled name from a CodeView symbol.
        Uses available mangled type info if available, mangles CodeView type info else.
        Returns: Symbol name with mangled type info.
    **********************************************************************************************/
	string mangle(NamedSymbol s)
	{
		// use mangled typeinfo if available
		if ( s.mangled_name !is null && s.mangled_name.length > 2 && s.mangled_name[0..2] == "_D" && isdigit(s.mangled_name[2]) )
			return s.mangled_name;

		return "_D"~mangleName(s.name_type)~mangleType(s);
	}

	/**********************************************************************************************
        Creates the type part of a D mangled name for a CodeView symbol.
    **********************************************************************************************/
	string mangleType(Symbol s)
	{
		DataSymbol ds = cast(DataSymbol)s;
		ushort	cvtype;
		if ( ds !is null )
			cvtype = ds.cvdata.type;
		else
        {
			StackSymbol ss = cast(StackSymbol)s;
			if ( ss !is null )
				cvtype = ss.cvdata.type;
			else {
				pragma(msg, TODO(__FILE__,__LINE__,"implement procedure symbol mangling"));
			}
		}
		return mangleCVtype(cvtype);
	}

	/**********************************************************************************************
        Creates the type part of a D mangled name for a CodeView type index.
    **********************************************************************************************/
	string mangleCVtype(ushort cvtype)
	{
		if ( cvtype >= 0x1000 )
		{
			uint typeindex = cvtype-0x1000;
			if ( typeindex >= type_strings.length ) {
				debug DbgIO.println("undefined complex type 0x%x largest known type 0x%x", cvtype, type_strings.length-1);
				return null;
			}
			Leaf[] typestring = type_strings[typeindex];

			while ( typestring.length > 0 )
			{
				switch ( typestring[0].leaf_index )
				{
					case LF_MODIFIER_16t:
						LeafModifer lm = cast(LeafModifer)typestring[0];
						assert ( lm !is null );
						return mangleCVtype(lm.index);
					case LF_POINTER_16t:
						LeafPointer lp = cast(LeafPointer)typestring[0];
						assert ( lp !is null );
						return "P"~mangleCVtype(lp.type);
					// do not insert new cases here
					case LF_CLASS_16t:
						LeafClassStruc lcs = cast(LeafClassStruc)typestring[0];
						assert ( lcs !is null );
						return "C"~mangleName(lcs.name);
					case LF_ARRAY_16t:
						LeafArray la = cast(LeafArray)typestring[0];
						assert ( la !is null );
						debug(cvparser) DbgIO.println("Static array elemtype 0x%x idxtype 0x%x length %d", la.elemtype, la.idxtype, la.length.getUint);
						uint    count = la.length.getUint,
                                elmsize = sizeofCV(la.elemtype);
						if ( elmsize > 0 ) {
                            count /= elmsize;
                            assert(la.length.getUint % sizeofCV(la.elemtype) == 0);
						}
						else
                            debug DbgIO.println("WARNING: elm size == 0");
						return "G"~.toString(count)~mangleCVtype(la.elemtype);
                    case LF_OEM_16t:
                        LeafDynArray lda = cast(LeafDynArray)typestring[0];
						if ( lda !is null )
                            return "A"~mangleCVtype(lda.elem_type);
                        LeafAssocArray laa = cast(LeafAssocArray)typestring[0];
						if ( laa !is null )
                            return "H"~mangleCVtype(laa.key_type)~mangleCVtype(laa.elem_type);
                        LeafDelegate ld = cast(LeafDelegate)typestring[0];
                        if ( ld !is null )
                            return "D"~mangleCVtype(ld.func_type);
                        assert(0);

					case LF_STRUCTURE_16t:
						LeafClassStruc lcs = cast(LeafClassStruc)typestring[0];
						assert ( lcs !is null );
						return "S"~mangleName(lcs.name);
					case LF_PROCEDURE_16t:
					case LF_METHODLIST_16t:
						debug(cvparser) DbgIO.println("unmangled procedure or methodlist in complex type");
						return "";
					case LF_UNION_16t:
						LeafUnion lu = cast(LeafUnion)typestring[0];
						return "S"~mangleName(lu.name);
					default:
						DbgIO.println("mangleCVtype: unsupported type leaf 0x%x", typestring[0].leaf_index);
						typestring = typestring[1..$];
				}
			}
			return null;
		}

		switch ( cvtype )
		{
			case T_NOTYPE:
				return null;
			case T_VOID:
				return "v";
			case T_PVOID: case T_PFVOID: case T_PHVOID: case T_32PVOID: case T_32PFVOID:
				return "Pv";

			case T_CHAR:
				return "g";
			case T_UCHAR:
				return "h";
			case T_RCHAR:
				return "a";
            case T_WCHAR:
                return "u";
            case T_DCHAR:
                return "w";
            case T_32PDCHAR: case T_32PFDCHAR:
                return "Pw";
			case T_PFRCHAR: case T_PHRCHAR: case T_32PRCHAR: case T_32PFRCHAR: case T_PRCHAR:
                return "Pa";
			case T_PFWCHAR: case T_PHWCHAR: case T_32PWCHAR: case T_32PFWCHAR: case T_PWCHAR:
                return "Pu";
			case T_PFCHAR: case T_PHCHAR: case T_32PCHAR: case T_32PFCHAR: case T_PCHAR:
				return "Pg";
			case T_PFUCHAR: case T_PHUCHAR: case T_32PUCHAR: case T_32PFUCHAR: case T_PUCHAR:
				return "Ph";

			case T_SHORT: case T_INT2:
				return "s";
			case T_USHORT: case T_UINT2:
				return "t";
			case T_PINT2: case T_PSHORT: case T_32PSHORT:
				return "Ps";
			case T_PUSHORT: case T_PUINT2: case T_32PUSHORT:
				return "Pt";
			case T_INT4:
			case T_LONG:
				return "i";
			case T_UINT4:
			case T_ULONG:
				return "k";
			case T_32PINT4: case T_PINT4:
				return "Pi";
			case T_32PUINT4: case T_PUINT4:
				return "Pk";
			case T_QUAD:
				return "l";
			case T_UQUAD:
				return "m";
			case T_32PQUAD: case T_PQUAD:
				return "Pl";
			case T_32PUQUAD: case T_PUQUAD:
				return "Pm";
			case T_REAL32:
				return "f";
			case T_32PREAL32: case T_PREAL32:
				return "Pf";
			case T_REAL64:
				return "d";
			case T_32PREAL64: case T_PREAL64:
				return "Pd";
			case T_REAL80:
				return "e";
			case T_PREAL80: case T_PFREAL80: case T_PHREAL80: case T_32PREAL80: case T_32PFREAL80:
				return "Pe";
			case T_BOOL08:
				return "b";
            case T_32PBOOL08: case T_32PFBOOL08:
                return "Pb";
            case T_CPLX80:
                return "c";
            case T_CPLX64:
                return "r";
            case T_CPLX32:
                return "q";
            case T_32PCPLX80: case T_32PFCPLX80:
                return "Pc";
            case T_32PCPLX64: case T_32PFCPLX64:
                return "Pr";
            case T_32PCPLX32: case T_32PFCPLX32:
                return "Pq";
			default:
				debug DbgIO.println("mangleCVtype: unknown codeview type 0x%x", cvtype);
		}
		return null;
	}


    /**************************************************************************************************
        Returns: size of demangled type
    **************************************************************************************************/
    uint sizeofMangled(string m)
    {
        assert(m.length > 0);
        switch ( m[0] )
        {
            case 'A':	return ulong.sizeof;
            case 'G':
                string n = m[1..$];
                size_t count = parseNumber(n);
                return count*sizeofMangled(n);
            case 'P':	return size_t.sizeof;
            case 'H':	return size_t.sizeof;
            case 'v':	return void.sizeof;
            case 'b':	return bit.sizeof;
            case 'x':	return bool.sizeof;
            case 'g':	return byte.sizeof;
            case 'h':	return ubyte.sizeof;
            case 's':	return short.sizeof;
            case 't':	return ushort.sizeof;
            case 'i':	return int.sizeof;
            case 'k':	return uint.sizeof;
            case 'l':	return long.sizeof;
            case 'm':	return ulong.sizeof;
            case 'f':	return float.sizeof;
            case 'd':	return double.sizeof;
            case 'e':	return real.sizeof;
            case 'o':	return ifloat.sizeof;
            case 'p':	return idouble.sizeof;
            case 'j':	return ireal.sizeof;
            case 'q':	return cfloat.sizeof;
            case 'r':	return cdouble.sizeof;
            case 'c':	return creal.sizeof;
            case 'a':	return char.sizeof;
            case 'u':	return wchar.sizeof;
            case 'w':	return dchar.sizeof;
            case 'C':
            case 'S':
                string  mangled_name = m[1..$],
                        name = demangleNameSkip(mangled_name);
                if ( name in UDTsByName ) {
                    LeafClassStruc lcs = cast(LeafClassStruc)UDTsByName[name];
                    if ( lcs !is null )
                        return lcs.length.getUint;
                    LeafUnion lu = cast(LeafUnion)UDTsByName[name];
                    if ( lu !is null )
                        return lu.length.getUint;
                }
                assert(0, "unknown struct in sizeofMangled: "~name);
            default:
                assert(0, "unknown type in sizeofMangled: "~m);
        }
        return 0;
    }


	/**********************************************************************************************
        Calculates the size of the type specified by a CodeView type index.
    **********************************************************************************************/
	uint sizeofCV(uint cvtype)
	{
		if ( cvtype >= 0x1000 )
		{
			uint typeindex = cvtype-0x1000;
			if ( typeindex >= type_strings.length ) {
				debug DbgIO.println("sizeofCV: undefined complex type 0x%x", cvtype);
				return 0;
			}
			Leaf[] typestring = type_strings[typeindex];
			while ( typestring.length > 0 )
			{
				switch ( typestring[0].leaf_index )
				{
					case LF_MODIFIER_16t:
						LeafModifer lm = cast(LeafModifer)typestring[0];
						assert ( lm !is null );
						return sizeofCV(lm.index);
					case LF_ARRAY_16t:
						LeafArray la = cast(LeafArray)typestring[0];
						assert ( la !is null );
						return la.length.getUint;
					case LF_POINTER_16t:
					case LF_CLASS_16t:
						return 4;
					case LF_STRUCTURE_16t:
						LeafClassStruc lcs = cast(LeafClassStruc)typestring[0];
						assert ( lcs !is null );
						return lcs.length.getUint;
					case LF_PROCEDURE_16t:
					case LF_METHODLIST_16t:
						debug DbgIO.println("unmangled procedure or methodlist in complex type");
						return 0;
					case LF_UNION_16t:
						LeafUnion lu = cast(LeafUnion)typestring[0];
						return lu.length.getUint;
                    case LF_OEM_16t:
                        LeafDynArray lda = cast(LeafDynArray)typestring[0];
						if ( lda !is null )
                            return size_t.sizeof*2;
                        LeafAssocArray laa = cast(LeafAssocArray)typestring[0];
						if ( laa !is null )
                            return size_t.sizeof;
                        LeafDelegate ld = cast(LeafDelegate)typestring[0];
                        if ( ld !is null )
                            return size_t.sizeof*2;
                        assert(0);

					default:
						DbgIO.println("sizeofCV: unsupported complex type leaf 0x%x", typestring[0].leaf_index);
						typestring = typestring[1..$];
				}
			}
			return 0;
		}

		switch ( cvtype )
		{
			case T_NOTYPE:
				return 0;
			case T_VOID:
			case T_CHAR:
			case T_UCHAR:
			case T_RCHAR:
			case T_BOOL08:
				return 1;
			case T_SHORT: case T_INT2:
			case T_USHORT: case T_UINT2:
            case T_WCHAR:
				return 2;
			case T_PVOID: case T_PFVOID: case T_PHVOID: case T_32PVOID: case T_32PFVOID:
			case T_DCHAR: case T_32PDCHAR: case T_32PFDCHAR:
			case T_PFCHAR: case T_PHCHAR: case T_32PCHAR: case T_32PFCHAR: case T_PCHAR:
			case T_PFUCHAR: case T_PHUCHAR: case T_32PUCHAR: case T_32PFUCHAR: case T_PUCHAR:
            case T_PRCHAR: case T_PFRCHAR: case T_PHRCHAR: case T_32PRCHAR: case T_32PFRCHAR:
            case T_PWCHAR: case T_PFWCHAR: case T_PHWCHAR: case T_32PWCHAR: case T_32PFWCHAR:
			case T_PINT2: case T_PSHORT: case T_32PSHORT:
			case T_PUSHORT: case T_PUINT2: case T_32PUSHORT:
            case T_32PBOOL08: case T_32PFBOOL08:
			case T_INT4:
			case T_UINT4:
			case T_32PINT4: case T_PINT4:
			case T_32PUINT4: case T_PUINT4:
			case T_32PQUAD: case T_PQUAD:
			case T_32PUQUAD: case T_PUQUAD:
            case T_ULONG:
			case T_REAL32:
			case T_32PREAL32: case T_PREAL32:
			case T_32PREAL64: case T_PREAL64:
			case T_PREAL80: case T_PFREAL80: case T_PHREAL80: case T_32PREAL80: case T_32PFREAL80:
            case T_CPLX32:
            case T_32PCPLX32: case T_32PFCPLX32:
            case T_32PCPLX64: case T_32PFCPLX64:
            case T_32PCPLX80: case T_32PFCPLX80:
				return 4;
			case T_QUAD:
			case T_UQUAD:
			case T_REAL64:
            case T_CPLX64:
				return 8;
            case T_CPLX80:
			case T_REAL80:
				return 10;
			default:
				debug DbgIO.println("sizeofCV: unknown codeview type 0x%x", cvtype);
		}
		return 0;
	}
}