| #!/usr/bin/env python | 
 |  | 
 | import struct | 
 | import sys | 
 | import StringIO | 
 |  | 
 | import common_dump | 
 |  | 
 | class Reader: | 
 |    def __init__(self, path): | 
 |       if path == '-': | 
 |          # Snarf all the data so we can seek. | 
 |          self.file = StringIO.StringIO(sys.stdin.read()) | 
 |       else: | 
 |          self.file = open(path,'rb') | 
 |       self.isLSB = None | 
 |       self.is64Bit = None | 
 |  | 
 |       self.string_table = None | 
 |  | 
 |    def tell(self): | 
 |       return self.file.tell() | 
 |  | 
 |    def seek(self, pos): | 
 |       self.file.seek(pos) | 
 |  | 
 |    def read(self, N): | 
 |       data = self.file.read(N) | 
 |       if len(data) != N: | 
 |          raise ValueError,"Out of data!" | 
 |       return data | 
 |  | 
 |    def read8(self): | 
 |       return ord(self.read(1)) | 
 |  | 
 |    def read16(self): | 
 |       return struct.unpack('><'[self.isLSB] + 'H', self.read(2))[0] | 
 |  | 
 |    def read32(self): | 
 |       # Force to 32-bit, if possible; otherwise these might be long ints on a | 
 |       # big-endian platform. FIXME: Why??? | 
 |       Value = struct.unpack('><'[self.isLSB] + 'I', self.read(4))[0] | 
 |       return int(Value) | 
 |  | 
 |    def read64(self): | 
 |       Value = struct.unpack('><'[self.isLSB] + 'Q', self.read(8))[0] | 
 |       if Value == int(Value): | 
 |          Value = int(Value) | 
 |       return Value | 
 |  | 
 |    def registerStringTable(self, strings): | 
 |       if self.string_table is not None: | 
 |          raise ValueError,"%s: warning: multiple string tables" % sys.argv[0] | 
 |  | 
 |       self.string_table = strings | 
 |  | 
 |    def getString(self, index): | 
 |       if self.string_table is None: | 
 |          raise ValueError,"%s: warning: no string table registered" % sys.argv[0] | 
 |        | 
 |       end = self.string_table.index('\x00', index) | 
 |       return self.string_table[index:end] | 
 |  | 
 | def dumpmacho(path, opts): | 
 |    f = Reader(path) | 
 |  | 
 |    magic = f.read(4) | 
 |    if magic == '\xFE\xED\xFA\xCE': | 
 |       f.isLSB, f.is64Bit = False, False | 
 |    elif magic == '\xCE\xFA\xED\xFE': | 
 |       f.isLSB, f.is64Bit = True, False | 
 |    elif magic == '\xFE\xED\xFA\xCF': | 
 |       f.isLSB, f.is64Bit = False, True | 
 |    elif magic == '\xCF\xFA\xED\xFE': | 
 |       f.isLSB, f.is64Bit = True, True | 
 |    else: | 
 |       raise ValueError,"Not a Mach-O object file: %r (bad magic)" % path | 
 |  | 
 |    print "('cputype', %r)" % f.read32() | 
 |    print "('cpusubtype', %r)" % f.read32() | 
 |    filetype = f.read32() | 
 |    print "('filetype', %r)" % filetype | 
 |     | 
 |    numLoadCommands = f.read32() | 
 |    print "('num_load_commands', %r)" % numLoadCommands | 
 |  | 
 |    loadCommandsSize = f.read32() | 
 |    print "('load_commands_size', %r)" % loadCommandsSize | 
 |  | 
 |    print "('flag', %r)" % f.read32() | 
 |  | 
 |    if f.is64Bit: | 
 |       print "('reserved', %r)" % f.read32() | 
 |  | 
 |    start = f.tell() | 
 |  | 
 |    print "('load_commands', [" | 
 |    for i in range(numLoadCommands): | 
 |       dumpLoadCommand(f, i, opts) | 
 |    print "])" | 
 |  | 
 |    if f.tell() - start != loadCommandsSize: | 
 |       raise ValueError,"%s: warning: invalid load commands size: %r" % ( | 
 |          sys.argv[0], loadCommandsSize) | 
 |  | 
 | def dumpLoadCommand(f, i, opts): | 
 |    start = f.tell() | 
 |  | 
 |    print "  # Load Command %r" % i | 
 |    cmd = f.read32() | 
 |    print " (('command', %r)" % cmd | 
 |    cmdSize = f.read32() | 
 |    print "  ('size', %r)" % cmdSize | 
 |  | 
 |    if cmd == 1: | 
 |       dumpSegmentLoadCommand(f, opts, False) | 
 |    elif cmd == 2: | 
 |       dumpSymtabCommand(f, opts) | 
 |    elif cmd == 11: | 
 |       dumpDysymtabCommand(f, opts) | 
 |    elif cmd == 25: | 
 |       dumpSegmentLoadCommand(f, opts, True) | 
 |    elif cmd == 27: | 
 |       import uuid | 
 |       print "  ('uuid', %s)" % uuid.UUID(bytes=f.read(16)) | 
 |    else: | 
 |       print >>sys.stderr,"%s: warning: unknown load command: %r" % ( | 
 |          sys.argv[0], cmd) | 
 |       f.read(cmdSize - 8) | 
 |    print " )," | 
 |  | 
 |    if f.tell() - start != cmdSize: | 
 |       raise ValueError,"%s: warning: invalid load command size: %r" % ( | 
 |          sys.argv[0], cmdSize) | 
 |  | 
 | def dumpSegmentLoadCommand(f, opts, is64Bit): | 
 |    print "  ('segment_name', %r)" % f.read(16)  | 
 |    if is64Bit: | 
 |       print "  ('vm_addr', %r)" % f.read64() | 
 |       print "  ('vm_size', %r)" % f.read64() | 
 |       print "  ('file_offset', %r)" % f.read64() | 
 |       print "  ('file_size', %r)" % f.read64() | 
 |    else: | 
 |       print "  ('vm_addr', %r)" % f.read32() | 
 |       print "  ('vm_size', %r)" % f.read32() | 
 |       print "  ('file_offset', %r)" % f.read32() | 
 |       print "  ('file_size', %r)" % f.read32() | 
 |    print "  ('maxprot', %r)" % f.read32() | 
 |    print "  ('initprot', %r)" % f.read32() | 
 |    numSections = f.read32() | 
 |    print "  ('num_sections', %r)" % numSections | 
 |    print "  ('flags', %r)" % f.read32() | 
 |  | 
 |    print "  ('sections', [" | 
 |    for i in range(numSections): | 
 |       dumpSection(f, i, opts, is64Bit) | 
 |    print "  ])" | 
 |  | 
 | def dumpSymtabCommand(f, opts): | 
 |    symoff = f.read32() | 
 |    print "  ('symoff', %r)" % symoff | 
 |    nsyms = f.read32() | 
 |    print "  ('nsyms', %r)" % nsyms | 
 |    stroff = f.read32() | 
 |    print "  ('stroff', %r)" % stroff | 
 |    strsize = f.read32() | 
 |    print "  ('strsize', %r)" % strsize | 
 |  | 
 |    prev_pos = f.tell() | 
 |  | 
 |    f.seek(stroff) | 
 |    string_data = f.read(strsize) | 
 |    print "  ('_string_data', %r)" % string_data | 
 |  | 
 |    f.registerStringTable(string_data) | 
 |  | 
 |    f.seek(symoff) | 
 |    print "  ('_symbols', [" | 
 |    for i in range(nsyms): | 
 |       dumpNlist32(f, i, opts) | 
 |    print "  ])" | 
 |        | 
 |    f.seek(prev_pos) | 
 |  | 
 | def dumpNlist32(f, i, opts): | 
 |    print "    # Symbol %r" % i | 
 |    n_strx = f.read32() | 
 |    print "   (('n_strx', %r)" % n_strx | 
 |    n_type = f.read8() | 
 |    print "    ('n_type', %#x)" % n_type | 
 |    n_sect = f.read8() | 
 |    print "    ('n_sect', %r)" % n_sect | 
 |    n_desc = f.read16() | 
 |    print "    ('n_desc', %r)" % n_desc | 
 |    if f.is64Bit: | 
 |       n_value = f.read64() | 
 |       print "    ('n_value', %r)" % n_value | 
 |    else: | 
 |       n_value = f.read32() | 
 |       print "    ('n_value', %r)" % n_value | 
 |    print "    ('_string', %r)" % f.getString(n_strx) | 
 |    print "   )," | 
 |  | 
 | def dumpDysymtabCommand(f, opts):    | 
 |    print "  ('ilocalsym', %r)" % f.read32() | 
 |    print "  ('nlocalsym', %r)" % f.read32() | 
 |    print "  ('iextdefsym', %r)" % f.read32() | 
 |    print "  ('nextdefsym', %r)" % f.read32() | 
 |    print "  ('iundefsym', %r)" % f.read32() | 
 |    print "  ('nundefsym', %r)" % f.read32() | 
 |    print "  ('tocoff', %r)" % f.read32() | 
 |    print "  ('ntoc', %r)" % f.read32() | 
 |    print "  ('modtaboff', %r)" % f.read32() | 
 |    print "  ('nmodtab', %r)" % f.read32() | 
 |    print "  ('extrefsymoff', %r)" % f.read32() | 
 |    print "  ('nextrefsyms', %r)" % f.read32() | 
 |    indirectsymoff = f.read32() | 
 |    print "  ('indirectsymoff', %r)" % indirectsymoff | 
 |    nindirectsyms = f.read32() | 
 |    print "  ('nindirectsyms', %r)" % nindirectsyms | 
 |    print "  ('extreloff', %r)" % f.read32() | 
 |    print "  ('nextrel', %r)" % f.read32() | 
 |    print "  ('locreloff', %r)" % f.read32() | 
 |    print "  ('nlocrel', %r)" % f.read32() | 
 |  | 
 |    prev_pos = f.tell() | 
 |  | 
 |    f.seek(indirectsymoff) | 
 |    print "  ('_indirect_symbols', [" | 
 |    for i in range(nindirectsyms): | 
 |       print "    # Indirect Symbol %r" % i | 
 |       print "    (('symbol_index', %#x),)," % f.read32() | 
 |    print "  ])" | 
 |        | 
 |    f.seek(prev_pos) | 
 |  | 
 | def dumpSection(f, i, opts, is64Bit): | 
 |    print "    # Section %r" % i | 
 |    print "   (('section_name', %r)" % f.read(16) | 
 |    print "    ('segment_name', %r)" % f.read(16) | 
 |    if is64Bit: | 
 |       print "    ('address', %r)" % f.read64() | 
 |       size = f.read64() | 
 |       print "    ('size', %r)" % size | 
 |    else: | 
 |       print "    ('address', %r)" % f.read32() | 
 |       size = f.read32() | 
 |       print "    ('size', %r)" % size | 
 |    offset = f.read32() | 
 |    print "    ('offset', %r)" % offset | 
 |    print "    ('alignment', %r)" % f.read32()    | 
 |    reloc_offset = f.read32() | 
 |    print "    ('reloc_offset', %r)" % reloc_offset | 
 |    num_reloc = f.read32() | 
 |    print "    ('num_reloc', %r)" % num_reloc | 
 |    print "    ('flags', %#x)" % f.read32() | 
 |    print "    ('reserved1', %r)" % f.read32() | 
 |    print "    ('reserved2', %r)" % f.read32() | 
 |    if is64Bit: | 
 |       print "    ('reserved3', %r)" % f.read32() | 
 |    print "   )," | 
 |  | 
 |    prev_pos = f.tell() | 
 |  | 
 |    f.seek(reloc_offset) | 
 |    print "  ('_relocations', [" | 
 |    for i in range(num_reloc): | 
 |       print "    # Relocation %r" % i | 
 |       print "    (('word-0', %#x)," % f.read32() | 
 |       print "     ('word-1', %#x))," % f.read32() | 
 |    print "  ])" | 
 |  | 
 |    if opts.dumpSectionData: | 
 |       f.seek(offset) | 
 |       print "  ('_section_data', '%s')" % common_dump.dataToHex(f.read(size)) | 
 |        | 
 |    f.seek(prev_pos) | 
 |     | 
 | def main(): | 
 |     from optparse import OptionParser, OptionGroup | 
 |     parser = OptionParser("usage: %prog [options] {files}") | 
 |     parser.add_option("", "--dump-section-data", dest="dumpSectionData", | 
 |                       help="Dump the contents of sections", | 
 |                       action="store_true", default=False)     | 
 |     (opts, args) = parser.parse_args() | 
 |  | 
 |     if not args: | 
 |        args.append('-') | 
 |  | 
 |     for arg in args: | 
 |        dumpmacho(arg, opts) | 
 |  | 
 | if __name__ == '__main__': | 
 |    main() |