view src/codeview/coff.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.coff;

import std.file;
import std.stdio;
import std.c.string;

import util;
import codeview.parser;
import codeview.codeview;

import win32.winnt;

/**************************************************************************************************
    Type indeces for COFF debug sections.
**************************************************************************************************/
enum DebugType
{
	UNKNOWN = 0,
	COFF,
	CODEVIEW
}

/**************************************************************************************************
    Represents a COFF section.
**************************************************************************************************/
class Section
{
	PIMAGE_SECTION_HEADER	header;
	ubyte[]					data;

	this(PIMAGE_SECTION_HEADER csh, DataReader dr)
	{
		uint old_cursor = dr.cursor;
		header = csh;
		assert(dr.data.length>=csh.PointerToRawData+csh.SizeOfRawData);
		data = dr.data[csh.PointerToRawData .. csh.PointerToRawData+csh.SizeOfRawData];
		dr.seek(old_cursor);
	}
}

/**************************************************************************************************
    Represents a COFF executable image file.
**************************************************************************************************/
class COFFImage
{
	private PIMAGE_FILE_HEADER		header;
	private PIMAGE_OPTIONAL_HEADER	opt_header;
	private IMAGE_DEBUG_DIRECTORY[]	debug_dirs;
	Section[]		        sections;
	CodeView[]				codeview_data;

	size_t					codebase,
							code_section_index;

	string					name;

    size_t imageSize()
    {
        assert(opt_header !is null);
        return opt_header.SizeOfImage;
    }

    size_t imageBase()
    {
        assert(opt_header !is null);
        return opt_header.ImageBase;
    }

	CodeView codeView()
	{
	    if ( codeview_data.length == 0 )
            return null;
		return codeview_data[0];
	}

	void load(string filename)
	{
		load(cast(ubyte[])read(filename), filename);
	}

    /**********************************************************************************************
        Very dirty, preliminary COFF image reader to extract codeview data
    **********************************************************************************************/
	void load(ubyte[] data, string filename=null)
	{
		DataReader dr = new DataReader(data);

		dr.seek(0x3c);
		uint pe_offset;
		dr.read(pe_offset);

		uint pe_sig;
		dr.seek(pe_offset);
		dr.read(pe_sig);
		if ( pe_sig != 0x4550 )
            throw new Exception("File is not an COFF PE executable");

		ubyte[] buf;
		assert ( IMAGE_FILE_HEADER.sizeof == 5*4);
		dr.readA(buf, IMAGE_FILE_HEADER.sizeof);
		header = cast(PIMAGE_FILE_HEADER)buf.ptr;
		if ( header.Machine != 0x14c )
            throw new Exception("Unsupported machine Id in COFF file");
		uint headersize = header.SizeOfOptionalHeader;

		assert(headersize>=IMAGE_OPTIONAL_HEADER.sizeof);
		headersize -= IMAGE_OPTIONAL_HEADER.sizeof;
		dr.readA(buf, IMAGE_OPTIONAL_HEADER.sizeof);
		opt_header = cast(PIMAGE_OPTIONAL_HEADER)buf.ptr;
		if ( opt_header.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC )
            throw new Exception("Unknown optional header magic");

		assert ( IMAGE_SIZEOF_SHORT_NAME+8*4 == IMAGE_SECTION_HEADER.sizeof);
		for ( int i = 0; i < header.NumberOfSections; ++i )
		{
			dr.readA(buf, IMAGE_SECTION_HEADER.sizeof);
			sections ~= new Section(cast(PIMAGE_SECTION_HEADER)buf.ptr, dr);
		}

		dr.seek(fileOffsetFromRVA(opt_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress));
		assert ( IMAGE_DEBUG_DIRECTORY.sizeof == 7*4);
		dr.readA(debug_dirs, opt_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size/IMAGE_DEBUG_DIRECTORY.sizeof);

		foreach ( IMAGE_DEBUG_DIRECTORY dd; debug_dirs )
		{
			if ( dd.Type == DebugType.CODEVIEW )
            {
				auto cv = CodeViewParser.parse(this, dr.data[dd.PointerToRawData..dd.PointerToRawData+dd.SizeOfData]);
                if ( cv !is null )
                    codeview_data ~= cv;
            }
			else {
				debug DbgIO.println("Unsupported COFF Debug Information format 0x%x", dd.Type);
			}
		}

        if ( opt_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size > 0 )
        {
            dr.seek(fileOffsetFromRVA(opt_header.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress));
            assert ( IMAGE_EXPORT_DIRECTORY.sizeof == 10*4 );
            dr.readA(buf, IMAGE_EXPORT_DIRECTORY.sizeof);
            PIMAGE_EXPORT_DIRECTORY export_dir = cast(PIMAGE_EXPORT_DIRECTORY)buf.ptr;
            dr.seek(fileOffsetFromRVA(export_dir.Name));

            char c;
            for ( dr.read(c); c != '\0'; dr.read(c) )
                name ~= c;
        }
        if ( name is null && filename !is null )
            name = filename;
        if ( codeview_data.length > 0 )
        {
            if ( name !is null )
                DbgIO.println("Loading symbols from %s", name is null?filename:name);
            else
                DbgIO.println("Loading symbols");
        }
        else
        {
            if ( name !is null )
                DbgIO.println("No symbols available from %s", name is null?filename:name);
            else
                DbgIO.println("No symbols available");
        }
    }

    /**********************************************************************************************
        Calculates the file offset from a given virtual address.
    **********************************************************************************************/
	private uint fileOffsetFromRVA(uint rva)
	{
		foreach ( Section s; sections )
		{
			with ( *s.header )
			{
				if( rva >= VirtualAddress && rva <= VirtualAddress+Misc.VirtualSize )
					return rva-VirtualAddress+PointerToRawData;
			}
		}
		return 0;
	}

    /**********************************************************************************************
        Calculates the virtual base address where code from this image is loaded to.
    **********************************************************************************************/
	size_t getCodeBase()
	{
		if ( codebase == 0 )
		{
			assert ( opt_header !is null );
			Section s = findSection(0x20000000, code_section_index);
			assert ( s !is null );
			codebase = opt_header.ImageBase + s.header.VirtualAddress;
		}
		return codebase;
	}

    /**********************************************************************************************
        Calculates the base address for the given section.
    **********************************************************************************************/
	uint getSectionBase(uint section)
	{
		if ( section > sections.length )
			return opt_header.ImageBase;
		return opt_header.ImageBase + sections[section-1].header.VirtualAddress;
	}

    /**********************************************************************************************
        Finds a section with the given characteristics.
    **********************************************************************************************/
	private Section findSection(uint characteristics, out uint index)
	{
		foreach( int i, s; sections )
		{
			if( (s.header.Characteristics & characteristics) == characteristics ) {
				index = i;
				return s;
			}
		}
		return null;
	}
}