#!/usr/bin/env python
#===-- coff-dump.py - COFF object file dump utility-------------------------===#
#
#                     The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#

#
# COFF File Definition
#

def string_table_entry (offset):
	return ('ptr', '+ + PointerToSymbolTable * NumberOfSymbols 18 %s' % offset, ('scalar', 'cstr', '%s'))

def secname(value):
	if value[0] == '/':
		return string_table_entry (value [1:].rstrip('\0'))
	else:
		return '%s'

def symname(value):
	parts = struct.unpack("<2L", value)
	if parts [0] == 0:
		return string_table_entry (parts [1])
	else:
		return '%s'

file = ('struct', [
	('MachineType', ('enum', '<H', '0x%X', {
		0x0:    'IMAGE_FILE_MACHINE_UNKNOWN',
		0x1d3:  'IMAGE_FILE_MACHINE_AM33',
		0x8664: 'IMAGE_FILE_MACHINE_AMD64',
		0x1c0:  'IMAGE_FILE_MACHINE_ARM',
		0xebc:  'IMAGE_FILE_MACHINE_EBC',
		0x14c:  'IMAGE_FILE_MACHINE_I386',
		0x200:  'IMAGE_FILE_MACHINE_IA64',
		0x904:  'IMAGE_FILE_MACHINE_M32R',
		0x266:  'IMAGE_FILE_MACHINE_MIPS16',
		0x366:  'IMAGE_FILE_MACHINE_MIPSFPU',
		0x466:  'IMAGE_FILE_MACHINE_MIPSFPU16',
		0x1f0:  'IMAGE_FILE_MACHINE_POWERPC',
		0x1f1:  'IMAGE_FILE_MACHINE_POWERPCFP',
		0x166:  'IMAGE_FILE_MACHINE_R4000',
		0x1a2:  'IMAGE_FILE_MACHINE_SH3',
		0x1a3:  'IMAGE_FILE_MACHINE_SH3DSP',
		0x1a6:  'IMAGE_FILE_MACHINE_SH4',
		0x1a8:  'IMAGE_FILE_MACHINE_SH5',
		0x1c2:  'IMAGE_FILE_MACHINE_THUMB',
		0x169:  'IMAGE_FILE_MACHINE_WCEMIPSV2',
	})),
	('NumberOfSections',     ('scalar',  '<H', '%d')),
	('TimeDateStamp',        ('scalar',  '<L', '%d')),
	('PointerToSymbolTable', ('scalar',  '<L', '0x%0X')),
	('NumberOfSymbols',      ('scalar',  '<L', '%d')),
	('SizeOfOptionalHeader', ('scalar',  '<H', '%d')),
	('Characteristics',      ('flags',   '<H', '0x%x', [
		(0x0001,      'IMAGE_FILE_RELOCS_STRIPPED',         ),
		(0x0002,      'IMAGE_FILE_EXECUTABLE_IMAGE',        ),
		(0x0004,      'IMAGE_FILE_LINE_NUMS_STRIPPED',      ),
		(0x0008,      'IMAGE_FILE_LOCAL_SYMS_STRIPPED',     ),
		(0x0010,      'IMAGE_FILE_AGGRESSIVE_WS_TRIM',      ),
		(0x0020,      'IMAGE_FILE_LARGE_ADDRESS_AWARE',     ),
		(0x0080,      'IMAGE_FILE_BYTES_REVERSED_LO',       ),
		(0x0100,      'IMAGE_FILE_32BIT_MACHINE',           ),
		(0x0200,      'IMAGE_FILE_DEBUG_STRIPPED',          ),
		(0x0400,      'IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP', ),
		(0x0800,      'IMAGE_FILE_NET_RUN_FROM_SWAP',       ),
		(0x1000,      'IMAGE_FILE_SYSTEM',                  ),
		(0x2000,      'IMAGE_FILE_DLL',                     ),
		(0x4000,      'IMAGE_FILE_UP_SYSTEM_ONLY',          ),
		(0x8000,      'IMAGE_FILE_BYTES_REVERSED_HI',       ),
	])),
	('Sections', ('array', 'NumberOfSections', ('struct', [
		('Name',                 ('scalar',  '<8s', secname)),
		('VirtualSize',          ('scalar',  '<L',  '%d'   )),
		('VirtualAddress',       ('scalar',  '<L',  '%d'   )),
		('SizeOfRawData',        ('scalar',  '<L',  '%d'   )),
		('PointerToRawData',     ('scalar',  '<L',  '0x%X' )),
		('PointerToRelocations', ('scalar',  '<L',  '0x%X' )),
		('PointerToLineNumbers', ('scalar',  '<L',  '0x%X' )),
		('NumberOfRelocations',  ('scalar',  '<H',  '%d'   )),
		('NumberOfLineNumbers',  ('scalar',  '<H',  '%d'   )),
		('Charateristics',       ('flags',   '<L',  '0x%X', [
			(0x00000008, 'IMAGE_SCN_TYPE_NO_PAD'),
			(0x00000020, 'IMAGE_SCN_CNT_CODE'),
			(0x00000040, 'IMAGE_SCN_CNT_INITIALIZED_DATA'),
			(0x00000080, 'IMAGE_SCN_CNT_UNINITIALIZED_DATA'),
			(0x00000100, 'IMAGE_SCN_LNK_OTHER'),
			(0x00000200, 'IMAGE_SCN_LNK_INFO'),
			(0x00000800, 'IMAGE_SCN_LNK_REMOVE'),
			(0x00001000, 'IMAGE_SCN_LNK_COMDAT'),
			(0x00008000, 'IMAGE_SCN_GPREL'),
			(0x00020000, 'IMAGE_SCN_MEM_PURGEABLE'),
			(0x00020000, 'IMAGE_SCN_MEM_16BIT'),
			(0x00040000, 'IMAGE_SCN_MEM_LOCKED'),
			(0x00080000, 'IMAGE_SCN_MEM_PRELOAD'),
			(0x00F00000, 'IMAGE_SCN_ALIGN', {
				0x00100000: 'IMAGE_SCN_ALIGN_1BYTES',
				0x00200000: 'IMAGE_SCN_ALIGN_2BYTES',
				0x00300000: 'IMAGE_SCN_ALIGN_4BYTES',
				0x00400000: 'IMAGE_SCN_ALIGN_8BYTES',
				0x00500000: 'IMAGE_SCN_ALIGN_16BYTES',
				0x00600000: 'IMAGE_SCN_ALIGN_32BYTES',
				0x00700000: 'IMAGE_SCN_ALIGN_64BYTES',
				0x00800000: 'IMAGE_SCN_ALIGN_128BYTES',
				0x00900000: 'IMAGE_SCN_ALIGN_256BYTES',
				0x00A00000: 'IMAGE_SCN_ALIGN_512BYTES',
				0x00B00000: 'IMAGE_SCN_ALIGN_1024BYTES',
				0x00C00000: 'IMAGE_SCN_ALIGN_2048BYTES',
				0x00D00000: 'IMAGE_SCN_ALIGN_4096BYTES',
				0x00E00000: 'IMAGE_SCN_ALIGN_8192BYTES',
			}),
			(0x01000000, 'IMAGE_SCN_LNK_NRELOC_OVFL'),
			(0x02000000, 'IMAGE_SCN_MEM_DISCARDABLE'),
			(0x04000000, 'IMAGE_SCN_MEM_NOT_CACHED'),
			(0x08000000, 'IMAGE_SCN_MEM_NOT_PAGED'),
			(0x10000000, 'IMAGE_SCN_MEM_SHARED'),
			(0x20000000, 'IMAGE_SCN_MEM_EXECUTE'),
			(0x40000000, 'IMAGE_SCN_MEM_READ'),
			(0x80000000, 'IMAGE_SCN_MEM_WRITE'),
		])),
		('SectionData', ('ptr', 'PointerToRawData', ('blob', 'SizeOfRawData'))),
		('Relocations', ('ptr', 'PointerToRelocations', ('array', 'NumberOfRelocations', ('struct', [
			('VirtualAddress',   ('scalar', '<L', '0x%X')),
			('SymbolTableIndex', ('scalar', '<L', '%d'  )),
			('Type',             ('enum', '<H', '%d', ('MachineType', {
				0x14c: {
					0x0000: 'IMAGE_REL_I386_ABSOLUTE',
					0x0001: 'IMAGE_REL_I386_DIR16',
					0x0002: 'IMAGE_REL_I386_REL16',
					0x0006: 'IMAGE_REL_I386_DIR32',
					0x0007: 'IMAGE_REL_I386_DIR32NB',
					0x0009: 'IMAGE_REL_I386_SEG12',
					0x000A: 'IMAGE_REL_I386_SECTION',
					0x000B: 'IMAGE_REL_I386_SECREL',
					0x000C: 'IMAGE_REL_I386_TOKEN',
					0x000D: 'IMAGE_REL_I386_SECREL7',
					0x0014: 'IMAGE_REL_I386_REL32',
				},
				0x8664: {
					0x0000: 'IMAGE_REL_AMD64_ABSOLUTE',
					0x0001: 'IMAGE_REL_AMD64_ADDR64',
					0x0002: 'IMAGE_REL_AMD64_ADDR32',
					0x0003: 'IMAGE_REL_AMD64_ADDR32NB',
					0x0004: 'IMAGE_REL_AMD64_REL32',
					0x0005: 'IMAGE_REL_AMD64_REL32_1',
					0x0006: 'IMAGE_REL_AMD64_REL32_2',
					0x0007: 'IMAGE_REL_AMD64_REL32_3',
					0x0008: 'IMAGE_REL_AMD64_REL32_4',
					0x0009: 'IMAGE_REL_AMD64_REL32_5',
					0x000A: 'IMAGE_REL_AMD64_SECTION',
					0x000B: 'IMAGE_REL_AMD64_SECREL',
					0x000C: 'IMAGE_REL_AMD64_SECREL7',
					0x000D: 'IMAGE_REL_AMD64_TOKEN',
					0x000E: 'IMAGE_REL_AMD64_SREL32',
					0x000F: 'IMAGE_REL_AMD64_PAIR',
					0x0010: 'IMAGE_REL_AMD64_SSPAN32',
				},
			}))),
			('SymbolName',       ('ptr', '+ PointerToSymbolTable * - SymbolTableIndex 1 18', ('scalar',  '<8s', symname)))
		])))),
	]))),
	('Symbols', ('ptr', 'PointerToSymbolTable', ('byte-array', '* NumberOfSymbols 18',  ('struct', [
		('Name',                ('scalar',  '<8s', symname)),
		('Value',               ('scalar',  '<L',  '%d'   )),
		('SectionNumber',       ('scalar',  '<H',  '%d'   )),
		('SimpleType',          ('enum',    '<B',  '%d', {
			0: 'IMAGE_SYM_TYPE_NULL',
			1: 'IMAGE_SYM_TYPE_VOID',
			2: 'IMAGE_SYM_TYPE_CHAR',
			3: 'IMAGE_SYM_TYPE_SHORT',
			4: 'IMAGE_SYM_TYPE_INT',
			5: 'IMAGE_SYM_TYPE_LONG',
			6: 'IMAGE_SYM_TYPE_FLOAT',
			7: 'IMAGE_SYM_TYPE_DOUBLE',
			8: 'IMAGE_SYM_TYPE_STRUCT',
			9: 'IMAGE_SYM_TYPE_UNION',
			10: 'IMAGE_SYM_TYPE_ENUM',
			11: 'IMAGE_SYM_TYPE_MOE',
			12: 'IMAGE_SYM_TYPE_BYTE',
			13: 'IMAGE_SYM_TYPE_WORD',
			14: 'IMAGE_SYM_TYPE_UINT',
			15: 'IMAGE_SYM_TYPE_DWORD',
		})),
		('ComplexType',         ('enum',    '<B',  '%d', {
			0: 'IMAGE_SYM_DTYPE_NULL',
			1: 'IMAGE_SYM_DTYPE_POINTER',
			2: 'IMAGE_SYM_DTYPE_FUNCTION',
			3: 'IMAGE_SYM_DTYPE_ARRAY',
		})),
		('StorageClass',        ('enum',    '<B',  '%d', {
			-1:	'IMAGE_SYM_CLASS_END_OF_FUNCTION',
			0: 'IMAGE_SYM_CLASS_NULL',
			1: 'IMAGE_SYM_CLASS_AUTOMATIC',
			2: 'IMAGE_SYM_CLASS_EXTERNAL',
			3: 'IMAGE_SYM_CLASS_STATIC',
			4: 'IMAGE_SYM_CLASS_REGISTER',
			5: 'IMAGE_SYM_CLASS_EXTERNAL_DEF',
			6: 'IMAGE_SYM_CLASS_LABEL',
			7: 'IMAGE_SYM_CLASS_UNDEFINED_LABEL',
			8: 'IMAGE_SYM_CLASS_MEMBER_OF_STRUCT',
			9: 'IMAGE_SYM_CLASS_ARGUMENT',
			10: 'IMAGE_SYM_CLASS_STRUCT_TAG',
			11: 'IMAGE_SYM_CLASS_MEMBER_OF_UNION',
			12: 'IMAGE_SYM_CLASS_UNION_TAG',
			13: 'IMAGE_SYM_CLASS_TYPE_DEFINITION',
			14: 'IMAGE_SYM_CLASS_UNDEFINED_STATIC',
			15: 'IMAGE_SYM_CLASS_ENUM_TAG',
			16: 'IMAGE_SYM_CLASS_MEMBER_OF_ENUM',
			17: 'IMAGE_SYM_CLASS_REGISTER_PARAM',
			18: 'IMAGE_SYM_CLASS_BIT_FIELD',
			100: 'IMAGE_SYM_CLASS_BLOCK',
			101: 'IMAGE_SYM_CLASS_FUNCTION',
			102: 'IMAGE_SYM_CLASS_END_OF_STRUCT',
			103: 'IMAGE_SYM_CLASS_FILE',
			104: 'IMAGE_SYM_CLASS_SECTION',
			105: 'IMAGE_SYM_CLASS_WEAK_EXTERNAL',
			107: 'IMAGE_SYM_CLASS_CLR_TOKEN',
		})),
		('NumberOfAuxSymbols',  ('scalar',  '<B',  '%d'  )),
		('AuxillaryData', ('blob', '* NumberOfAuxSymbols 18')),
	])))),
])

#
# Definition Interpreter
#

import sys, types, struct, re

Input = None
Stack = []
Fields = {}

Indent = 0
NewLine = True

def indent():
	global Indent
	Indent += 1

def dedent():
	global Indent
	Indent -= 1

def write(input):
	global NewLine
	output = ""
	
	for char in input:
		
		if NewLine:
			output += Indent * '  '
			NewLine = False
			
		output += char
		
		if char == '\n':
			NewLine = True
	
	sys.stdout.write (output)

def read(format):
	return struct.unpack (format, Input.read(struct.calcsize(format)))

def read_cstr ():
	output = ""
	while True:
		char = Input.read (1)
		if len (char) == 0:
			raise RuntimeError ("EOF while reading cstr")
		if char == '\0':
			break
		output += char
	return output

def push_pos(seek_to = None):
	Stack [0:0] = [Input.tell ()]
	if seek_to:
		Input.seek (seek_to)

def pop_pos():
	assert(len (Stack) > 0)
	Input.seek (Stack [0])
	del Stack [0]

def print_binary_data(size):
	value = ""
	while size > 0:
		if size >= 16:
			data = Input.read(16)
			size -= 16
		else:
			data = Input.read(size)
			size = 0
		value += data
		bytes = ""
		text = ""
		for index in xrange (16):
			if index < len (data):
				if index == 8:
					bytes += "- "
				ch = ord (data [index])
				bytes += "%02X " % ch
				if ch >= 0x20 and ch <= 0x7F:
					text += data [index]
				else:
					text += "."
			else:
				if index == 8:
					bytes += "  "
				bytes += "   "
		
		write ("%s|%s|\n" % (bytes, text))
	return value

idlit = re.compile ("[a-zA-Z][a-zA-Z0-9_-]*")
numlit = re.compile ("[0-9]+")

def read_value(expr):

	input = iter (expr.split ())
	
	def eval():
		
		token = input.next ()
		
		if expr == 'cstr':
			return read_cstr ()
		if expr == 'true':
			return True
		if expr == 'false':
			return False
		
		if len (token) > 1 and token [0] in ('=', '@', '<', '!', '>'):
			val = read(expr)
			assert (len (val) == 1)
			return val [0]
		
		if token == '+':
			return eval () + eval ()
		if token == '-':
			return eval () - eval ()
		if token == '*':
			return eval () * eval ()
		if token == '/':
			return eval () / eval ()
		
		if idlit.match (token):
			return Fields [token]
		if numlit.match (token):
			return int (token)
		
		raise RuntimeError ("unexpected token %s" % repr(token))
	
	value = eval ()
	
	try:
		input.next ()
	except StopIteration:
		return value
	raise RuntimeError("unexpected input at end of expression")

def write_value(format,value):
	format_type = type (format)
	if format_type is types.StringType:
		write (format%value)
	elif format_type is types.FunctionType:
		write_value (format (value), value)
	elif format_type is types.TupleType:
		Fields ['this'] = value
		handle_element (format)
	else:
		raise RuntimeError("unexpected type: %s" % repr(format_type))

def handle_scalar(entry):
	iformat = entry [1]
	oformat = entry [2]
	
	value = read_value (iformat)
	
	write_value (oformat, value)
	
	return value

def handle_enum(entry):
	iformat = entry [1]
	oformat = entry [2]
	definitions = entry [3]
	
	value = read_value (iformat)
	
	if type (definitions) is types.TupleType:
		selector = read_value (definitions [0])
		definitions = definitions [1] [selector]
	
	if value in definitions:
		description = definitions[value]
	else:
		description = "unknown"

	write ("%s (" % description)
	write_value (oformat, value)
	write (")")
	
	return value

def handle_flags(entry):
	iformat = entry [1]
	oformat = entry [2]
	definitions = entry [3]
	
	value = read_value (iformat)
	
	write_value (oformat, value)
	
	indent ()
	for entry in definitions:
		mask = entry [0]
		name = entry [1]
		if len (entry) == 3:
			map = entry [2]
			selection = value & mask
			if selection in map:
				write("\n%s" % map[selection])
			else:
				write("\n%s <%d>" % (name, selection))
		elif len (entry) == 2:
			if value & mask != 0:
				write("\n%s" % name)
	dedent ()
	
	return value

def handle_struct(entry):
	global Fields
	members = entry [1]
	
	newFields = {}
	
	write ("{\n");
	indent ()
	
	for member in members:
		name = member [0]
		type = member [1]
		
		write("%s = "%name.ljust(24))
		
		value = handle_element(type)
		
		write("\n")
		
		Fields [name] = value
		newFields [name] = value
	
	dedent ()
	write ("}")
	
	return newFields

def handle_array(entry):
	length = entry [1]
	element = entry [2]
	
	newItems = []
	
	write ("[\n")
	indent ()
	
	value = read_value (length)
	
	for index in xrange (value):
		write ("%d = "%index)
		value = handle_element(element)
		write ("\n")
		newItems.append (value)
	
	dedent ()
	write ("]")
	
	return newItems

def handle_byte_array(entry):
	length = entry [1]
	element = entry [2]
	
	newItems = []
	
	write ("[\n")
	indent ()
	
	value = read_value (length)
	end_of_array = Input.tell () + value

	index = 0
	while Input.tell () < end_of_array:
		write ("%d = "%index)
		value = handle_element(element)
		write ("\n")
		newItems.append (value)
		index += 1
	
	dedent ()
	write ("]")
	
	return newItems

def handle_ptr(entry):
	offset = entry[1]
	element = entry [2]
	
	value = None
	offset = read_value (offset)
	
	if offset != 0:
		
		push_pos (offset)
		
		value = handle_element (element)
		
		pop_pos ()
	
	else:
		write ("None")
	
	return value

def handle_blob(entry):
	length = entry [1]
	
	write ("\n")
	indent ()
	
	value = print_binary_data (read_value (length))
	
	dedent ()
	
	return value

def handle_element(entry):
	handlers = {
		'struct':      handle_struct,
		'scalar':      handle_scalar,
		'enum':        handle_enum,
		'flags':       handle_flags,
		'ptr':         handle_ptr,
		'blob':        handle_blob,
		'array':       handle_array,
		'byte-array':  handle_byte_array,
	}
	
	if not entry [0] in handlers:
		raise RuntimeError ("unexpected type '%s'" % str (entry[0]))
	
	return handlers [entry [0]] (entry)

Input = open (sys.argv [1], "rb")
try:
	handle_element (file)
finally:
	Input.close ()
	Input = None