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