Home | History | Annotate | Download | only in Scripts
      1 #!/usr/bin/env python
      2 
      3 import struct
      4 import sys
      5 import StringIO
      6 
      7 import common_dump
      8 
      9 class Reader:
     10    def __init__(self, path):
     11       if path == '-':
     12          # Snarf all the data so we can seek.
     13          self.file = StringIO.StringIO(sys.stdin.read())
     14       else:
     15          self.file = open(path,'rb')
     16       self.isLSB = None
     17       self.is64Bit = None
     18 
     19       self.string_table = None
     20 
     21    def tell(self):
     22       return self.file.tell()
     23 
     24    def seek(self, pos):
     25       self.file.seek(pos)
     26 
     27    def read(self, N):
     28       data = self.file.read(N)
     29       if len(data) != N:
     30          raise ValueError,"Out of data!"
     31       return data
     32 
     33    def read8(self):
     34       return ord(self.read(1))
     35 
     36    def read16(self):
     37       return struct.unpack('><'[self.isLSB] + 'H', self.read(2))[0]
     38 
     39    def read32(self):
     40       # Force to 32-bit, if possible; otherwise these might be long ints on a
     41       # big-endian platform. FIXME: Why???
     42       Value = struct.unpack('><'[self.isLSB] + 'I', self.read(4))[0]
     43       return int(Value)
     44 
     45    def read64(self):
     46       Value = struct.unpack('><'[self.isLSB] + 'Q', self.read(8))[0]
     47       if Value == int(Value):
     48          Value = int(Value)
     49       return Value
     50 
     51    def registerStringTable(self, strings):
     52       if self.string_table is not None:
     53          raise ValueError,"%s: warning: multiple string tables" % sys.argv[0]
     54 
     55       self.string_table = strings
     56 
     57    def getString(self, index):
     58       if self.string_table is None:
     59          raise ValueError,"%s: warning: no string table registered" % sys.argv[0]
     60       
     61       end = self.string_table.index('\x00', index)
     62       return self.string_table[index:end]
     63 
     64 def dumpmacho(path, opts):
     65    f = Reader(path)
     66 
     67    magic = f.read(4)
     68    if magic == '\xFE\xED\xFA\xCE':
     69       f.isLSB, f.is64Bit = False, False
     70    elif magic == '\xCE\xFA\xED\xFE':
     71       f.isLSB, f.is64Bit = True, False
     72    elif magic == '\xFE\xED\xFA\xCF':
     73       f.isLSB, f.is64Bit = False, True
     74    elif magic == '\xCF\xFA\xED\xFE':
     75       f.isLSB, f.is64Bit = True, True
     76    else:
     77       raise ValueError,"Not a Mach-O object file: %r (bad magic)" % path
     78 
     79    print "('cputype', %r)" % f.read32()
     80    print "('cpusubtype', %r)" % f.read32()
     81    filetype = f.read32()
     82    print "('filetype', %r)" % filetype
     83    
     84    numLoadCommands = f.read32()
     85    print "('num_load_commands', %r)" % numLoadCommands
     86 
     87    loadCommandsSize = f.read32()
     88    print "('load_commands_size', %r)" % loadCommandsSize
     89 
     90    print "('flag', %r)" % f.read32()
     91 
     92    if f.is64Bit:
     93       print "('reserved', %r)" % f.read32()
     94 
     95    start = f.tell()
     96 
     97    print "('load_commands', ["
     98    for i in range(numLoadCommands):
     99       dumpLoadCommand(f, i, opts)
    100    print "])"
    101 
    102    if f.tell() - start != loadCommandsSize:
    103       raise ValueError,"%s: warning: invalid load commands size: %r" % (
    104          sys.argv[0], loadCommandsSize)
    105 
    106 def dumpLoadCommand(f, i, opts):
    107    start = f.tell()
    108 
    109    print "  # Load Command %r" % i
    110    cmd = f.read32()
    111    print " (('command', %r)" % cmd
    112    cmdSize = f.read32()
    113    print "  ('size', %r)" % cmdSize
    114 
    115    if cmd == 1:
    116       dumpSegmentLoadCommand(f, opts, False)
    117    elif cmd == 2:
    118       dumpSymtabCommand(f, opts)
    119    elif cmd == 11:
    120       dumpDysymtabCommand(f, opts)
    121    elif cmd == 25:
    122       dumpSegmentLoadCommand(f, opts, True)
    123    elif cmd == 27:
    124       import uuid
    125       print "  ('uuid', %s)" % uuid.UUID(bytes=f.read(16))
    126    else:
    127       print >>sys.stderr,"%s: warning: unknown load command: %r" % (
    128          sys.argv[0], cmd)
    129       f.read(cmdSize - 8)
    130    print " ),"
    131 
    132    if f.tell() - start != cmdSize:
    133       raise ValueError,"%s: warning: invalid load command size: %r" % (
    134          sys.argv[0], cmdSize)
    135 
    136 def dumpSegmentLoadCommand(f, opts, is64Bit):
    137    print "  ('segment_name', %r)" % f.read(16) 
    138    if is64Bit:
    139       print "  ('vm_addr', %r)" % f.read64()
    140       print "  ('vm_size', %r)" % f.read64()
    141       print "  ('file_offset', %r)" % f.read64()
    142       print "  ('file_size', %r)" % f.read64()
    143    else:
    144       print "  ('vm_addr', %r)" % f.read32()
    145       print "  ('vm_size', %r)" % f.read32()
    146       print "  ('file_offset', %r)" % f.read32()
    147       print "  ('file_size', %r)" % f.read32()
    148    print "  ('maxprot', %r)" % f.read32()
    149    print "  ('initprot', %r)" % f.read32()
    150    numSections = f.read32()
    151    print "  ('num_sections', %r)" % numSections
    152    print "  ('flags', %r)" % f.read32()
    153 
    154    print "  ('sections', ["
    155    for i in range(numSections):
    156       dumpSection(f, i, opts, is64Bit)
    157    print "  ])"
    158 
    159 def dumpSymtabCommand(f, opts):
    160    symoff = f.read32()
    161    print "  ('symoff', %r)" % symoff
    162    nsyms = f.read32()
    163    print "  ('nsyms', %r)" % nsyms
    164    stroff = f.read32()
    165    print "  ('stroff', %r)" % stroff
    166    strsize = f.read32()
    167    print "  ('strsize', %r)" % strsize
    168 
    169    prev_pos = f.tell()
    170 
    171    f.seek(stroff)
    172    string_data = f.read(strsize)
    173    print "  ('_string_data', %r)" % string_data
    174 
    175    f.registerStringTable(string_data)
    176 
    177    f.seek(symoff)
    178    print "  ('_symbols', ["
    179    for i in range(nsyms):
    180       dumpNlist32(f, i, opts)
    181    print "  ])"
    182       
    183    f.seek(prev_pos)
    184 
    185 def dumpNlist32(f, i, opts):
    186    print "    # Symbol %r" % i
    187    n_strx = f.read32()
    188    print "   (('n_strx', %r)" % n_strx
    189    n_type = f.read8()
    190    print "    ('n_type', %#x)" % n_type
    191    n_sect = f.read8()
    192    print "    ('n_sect', %r)" % n_sect
    193    n_desc = f.read16()
    194    print "    ('n_desc', %r)" % n_desc
    195    if f.is64Bit:
    196       n_value = f.read64()
    197       print "    ('n_value', %r)" % n_value
    198    else:
    199       n_value = f.read32()
    200       print "    ('n_value', %r)" % n_value
    201    print "    ('_string', %r)" % f.getString(n_strx)
    202    print "   ),"
    203 
    204 def dumpDysymtabCommand(f, opts):   
    205    print "  ('ilocalsym', %r)" % f.read32()
    206    print "  ('nlocalsym', %r)" % f.read32()
    207    print "  ('iextdefsym', %r)" % f.read32()
    208    print "  ('nextdefsym', %r)" % f.read32()
    209    print "  ('iundefsym', %r)" % f.read32()
    210    print "  ('nundefsym', %r)" % f.read32()
    211    print "  ('tocoff', %r)" % f.read32()
    212    print "  ('ntoc', %r)" % f.read32()
    213    print "  ('modtaboff', %r)" % f.read32()
    214    print "  ('nmodtab', %r)" % f.read32()
    215    print "  ('extrefsymoff', %r)" % f.read32()
    216    print "  ('nextrefsyms', %r)" % f.read32()
    217    indirectsymoff = f.read32()
    218    print "  ('indirectsymoff', %r)" % indirectsymoff
    219    nindirectsyms = f.read32()
    220    print "  ('nindirectsyms', %r)" % nindirectsyms
    221    print "  ('extreloff', %r)" % f.read32()
    222    print "  ('nextrel', %r)" % f.read32()
    223    print "  ('locreloff', %r)" % f.read32()
    224    print "  ('nlocrel', %r)" % f.read32()
    225 
    226    prev_pos = f.tell()
    227 
    228    f.seek(indirectsymoff)
    229    print "  ('_indirect_symbols', ["
    230    for i in range(nindirectsyms):
    231       print "    # Indirect Symbol %r" % i
    232       print "    (('symbol_index', %#x),)," % f.read32()
    233    print "  ])"
    234       
    235    f.seek(prev_pos)
    236 
    237 def dumpSection(f, i, opts, is64Bit):
    238    print "    # Section %r" % i
    239    print "   (('section_name', %r)" % f.read(16)
    240    print "    ('segment_name', %r)" % f.read(16)
    241    if is64Bit:
    242       print "    ('address', %r)" % f.read64()
    243       size = f.read64()
    244       print "    ('size', %r)" % size
    245    else:
    246       print "    ('address', %r)" % f.read32()
    247       size = f.read32()
    248       print "    ('size', %r)" % size
    249    offset = f.read32()
    250    print "    ('offset', %r)" % offset
    251    print "    ('alignment', %r)" % f.read32()   
    252    reloc_offset = f.read32()
    253    print "    ('reloc_offset', %r)" % reloc_offset
    254    num_reloc = f.read32()
    255    print "    ('num_reloc', %r)" % num_reloc
    256    print "    ('flags', %#x)" % f.read32()
    257    print "    ('reserved1', %r)" % f.read32()
    258    print "    ('reserved2', %r)" % f.read32()
    259    if is64Bit:
    260       print "    ('reserved3', %r)" % f.read32()
    261    print "   ),"
    262 
    263    prev_pos = f.tell()
    264 
    265    f.seek(reloc_offset)
    266    print "  ('_relocations', ["
    267    for i in range(num_reloc):
    268       print "    # Relocation %r" % i
    269       print "    (('word-0', %#x)," % f.read32()
    270       print "     ('word-1', %#x))," % f.read32()
    271    print "  ])"
    272 
    273    if opts.dumpSectionData:
    274       f.seek(offset)
    275       print "  ('_section_data', '%s')" % common_dump.dataToHex(f.read(size))
    276       
    277    f.seek(prev_pos)
    278    
    279 def main():
    280     from optparse import OptionParser, OptionGroup
    281     parser = OptionParser("usage: %prog [options] {files}")
    282     parser.add_option("", "--dump-section-data", dest="dumpSectionData",
    283                       help="Dump the contents of sections",
    284                       action="store_true", default=False)    
    285     (opts, args) = parser.parse_args()
    286 
    287     if not args:
    288        args.append('-')
    289 
    290     for arg in args:
    291        dumpmacho(arg, opts)
    292 
    293 if __name__ == '__main__':
    294    main()
    295