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