#!/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', '1', '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', '0', '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 18', ('scalar',  '<8s', symname)))
    ])))),
  ]))),
  ('Symbols', ('ptr', 'PointerToSymbolTable', ('byte-array', '18', '* NumberOfSymbols 18',  ('struct', [
    ('Name',                ('scalar',  '<8s', symname)),
    ('Value',               ('scalar',  '<L',  '%d'   )),
    ('SectionNumber',       ('scalar',  '<H',  '%d'   )),
    ('_Type',               ('scalar',  '<H',  None   )),
    ('SimpleType',          ('enum',    '& _Type 15',  '%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',
    })),                                # (Type & 0xF0) >> 4
    ('ComplexType',         ('enum',    '>> & _Type 240 4',  '%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 token == '+':
      return eval() + eval()
    if token == '-':
      return eval() - eval()
    if token == '*':
      return eval() * eval()
    if token == '/':
      return eval() / eval()
    if token == '&':
      return eval() & eval()
    if token == '|':
      return eval() | eval()
    if token == '>>':
      return eval() >> eval()
    if token == '<<':
      return eval() << eval()

    if len(token) > 1 and token[0] in ('=', '@', '<', '!', '>'):
      val = read(expr)
      assert(len(val) == 1)
      return val[0]

    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)
  elif format_type is types.NoneType:
    pass
  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]

    if name[0] != "_":
      write("%s = " % name.ljust(24))

    value = handle_element(type)

    if name[0] != "_":
      write("\n")

    Fields[name] = value
    newFields[name] = value

  dedent()
  write("}")

  return newFields

def handle_array(entry):
  start_index = entry[1]
  length = entry[2]
  element = entry[3]

  newItems = []

  write("[\n")
  indent()

  start_index = read_value(start_index)
  value = read_value(length)

  for index in xrange(value):
    write("%d = " % (index + start_index))
    value = handle_element(element)
    write("\n")
    newItems.append(value)

  dedent()
  write("]")

  return newItems

def handle_byte_array(entry):
  ent_size = entry[1]
  length = entry[2]
  element = entry[3]

  newItems = []

  write("[\n")
  indent()

  item_size = read_value(ent_size)
  value = read_value(length)
  end_of_array = Input.tell() + value

  prev_loc = Input.tell()
  index = 0
  while Input.tell() < end_of_array:
    write("%d = " % index)
    value = handle_element(element)
    write("\n")
    newItems.append(value)
    index += (Input.tell() - prev_loc) / item_size
    prev_loc = Input.tell()

  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)

if len(sys.argv) <= 1 or sys.argv[1] == '-':
  import StringIO
  Input = StringIO.StringIO(sys.stdin.read())
else:
  Input = open (sys.argv[1], "rb")

try:
  handle_element(file)
finally:
  Input.close()
  Input = None