xref: /openbsd-src/gnu/llvm/lldb/examples/python/bsd.py (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1be691f3bSpatrick#!/usr/bin/env python
2061da546Spatrick
3061da546Spatrickimport cmd
4061da546Spatrickimport optparse
5061da546Spatrickimport os
6061da546Spatrickimport shlex
7061da546Spatrickimport struct
8061da546Spatrickimport sys
9061da546Spatrick
10061da546SpatrickARMAG = "!<arch>\n"
11061da546SpatrickSARMAG = 8
12061da546SpatrickARFMAG = "`\n"
13061da546SpatrickAR_EFMT1 = "#1/"
14061da546Spatrick
15061da546Spatrick
16061da546Spatrickdef memdump(src, bytes_per_line=16, address=0):
17061da546Spatrick    FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.'
18061da546Spatrick                     for x in range(256)])
19061da546Spatrick    for i in range(0, len(src), bytes_per_line):
20061da546Spatrick        s = src[i:i+bytes_per_line]
21061da546Spatrick        hex_bytes = ' '.join(["%02x" % (ord(x)) for x in s])
22061da546Spatrick        ascii = s.translate(FILTER)
23061da546Spatrick        print("%#08.8x: %-*s %s" % (address+i, bytes_per_line*3, hex_bytes,
24061da546Spatrick                                    ascii))
25061da546Spatrick
26061da546Spatrick
27061da546Spatrickclass Object(object):
28061da546Spatrick    def __init__(self, file):
29061da546Spatrick        def read_str(file, str_len):
30061da546Spatrick            return file.read(str_len).rstrip('\0 ')
31061da546Spatrick
32061da546Spatrick        def read_int(file, str_len, base):
33061da546Spatrick            return int(read_str(file, str_len), base)
34061da546Spatrick
35061da546Spatrick        self.offset = file.tell()
36061da546Spatrick        self.file = file
37061da546Spatrick        self.name = read_str(file, 16)
38061da546Spatrick        self.date = read_int(file, 12, 10)
39061da546Spatrick        self.uid = read_int(file, 6, 10)
40061da546Spatrick        self.gid = read_int(file, 6, 10)
41061da546Spatrick        self.mode = read_int(file, 8, 8)
42061da546Spatrick        self.size = read_int(file, 10, 10)
43061da546Spatrick        if file.read(2) != ARFMAG:
44061da546Spatrick            raise ValueError('invalid BSD object at offset %#08.8x' % (
45061da546Spatrick                             self.offset))
46061da546Spatrick        # If we have an extended name read it. Extended names start with
47061da546Spatrick        name_len = 0
48061da546Spatrick        if self.name.startswith(AR_EFMT1):
49061da546Spatrick            name_len = int(self.name[len(AR_EFMT1):], 10)
50061da546Spatrick            self.name = read_str(file, name_len)
51061da546Spatrick        self.obj_offset = file.tell()
52061da546Spatrick        self.obj_size = self.size - name_len
53061da546Spatrick        file.seek(self.obj_size, 1)
54061da546Spatrick
55061da546Spatrick    def dump(self, f=sys.stdout, flat=True):
56061da546Spatrick        if flat:
57061da546Spatrick            f.write('%#08.8x: %#08.8x %5u %5u %6o %#08.8x %s\n' % (self.offset,
58061da546Spatrick                    self.date, self.uid, self.gid, self.mode, self.size,
59061da546Spatrick                    self.name))
60061da546Spatrick        else:
61061da546Spatrick            f.write('%#08.8x: \n' % self.offset)
62061da546Spatrick            f.write(' name = "%s"\n' % self.name)
63061da546Spatrick            f.write(' date = %#08.8x\n' % self.date)
64061da546Spatrick            f.write('  uid = %i\n' % self.uid)
65061da546Spatrick            f.write('  gid = %i\n' % self.gid)
66061da546Spatrick            f.write(' mode = %o\n' % self.mode)
67061da546Spatrick            f.write(' size = %#08.8x\n' % (self.size))
68061da546Spatrick            self.file.seek(self.obj_offset, 0)
69061da546Spatrick            first_bytes = self.file.read(4)
70061da546Spatrick            f.write('bytes = ')
71061da546Spatrick            memdump(first_bytes)
72061da546Spatrick
73061da546Spatrick    def get_bytes(self):
74061da546Spatrick        saved_pos = self.file.tell()
75061da546Spatrick        self.file.seek(self.obj_offset, 0)
76061da546Spatrick        bytes = self.file.read(self.obj_size)
77061da546Spatrick        self.file.seek(saved_pos, 0)
78061da546Spatrick        return bytes
79061da546Spatrick
80061da546Spatrick    def save(self, path=None, overwrite=False):
81061da546Spatrick        '''
82061da546Spatrick            Save the contents of the object to disk using 'path' argument as
83061da546Spatrick            the path, or save it to the current working directory using the
84061da546Spatrick            object name.
85061da546Spatrick        '''
86061da546Spatrick
87061da546Spatrick        if path is None:
88061da546Spatrick            path = self.name
89061da546Spatrick        if not overwrite and os.path.exists(path):
90061da546Spatrick            print('error: outfile "%s" already exists' % (path))
91061da546Spatrick            return
92061da546Spatrick        print('Saving "%s" to "%s"...' % (self.name, path))
93061da546Spatrick        with open(path, 'w') as f:
94061da546Spatrick            f.write(self.get_bytes())
95061da546Spatrick
96061da546Spatrick
97061da546Spatrickclass StringTable(object):
98061da546Spatrick    def __init__(self, bytes):
99061da546Spatrick        self.bytes = bytes
100061da546Spatrick
101061da546Spatrick    def get_string(self, offset):
102061da546Spatrick        length = len(self.bytes)
103061da546Spatrick        if offset >= length:
104061da546Spatrick            return None
105061da546Spatrick        return self.bytes[offset:self.bytes.find('\0', offset)]
106061da546Spatrick
107061da546Spatrick
108061da546Spatrickclass Archive(object):
109061da546Spatrick    def __init__(self, path):
110061da546Spatrick        self.path = path
111061da546Spatrick        self.file = open(path, 'r')
112061da546Spatrick        self.objects = []
113061da546Spatrick        self.offset_to_object = {}
114061da546Spatrick        if self.file.read(SARMAG) != ARMAG:
115061da546Spatrick            print("error: file isn't a BSD archive")
116061da546Spatrick        while True:
117061da546Spatrick            try:
118061da546Spatrick                self.objects.append(Object(self.file))
119061da546Spatrick            except ValueError:
120061da546Spatrick                break
121061da546Spatrick
122061da546Spatrick    def get_object_at_offset(self, offset):
123061da546Spatrick        if offset in self.offset_to_object:
124061da546Spatrick            return self.offset_to_object[offset]
125061da546Spatrick        for obj in self.objects:
126061da546Spatrick            if obj.offset == offset:
127061da546Spatrick                self.offset_to_object[offset] = obj
128061da546Spatrick                return obj
129061da546Spatrick        return None
130061da546Spatrick
131061da546Spatrick    def find(self, name, mtime=None, f=sys.stdout):
132061da546Spatrick        '''
133061da546Spatrick            Find an object(s) by name with optional modification time. There
134061da546Spatrick            can be multple objects with the same name inside and possibly with
135061da546Spatrick            the same modification time within a BSD archive so clients must be
136061da546Spatrick            prepared to get multiple results.
137061da546Spatrick        '''
138061da546Spatrick        matches = []
139061da546Spatrick        for obj in self.objects:
140061da546Spatrick            if obj.name == name and (mtime is None or mtime == obj.date):
141061da546Spatrick                matches.append(obj)
142061da546Spatrick        return matches
143061da546Spatrick
144061da546Spatrick    @classmethod
145061da546Spatrick    def dump_header(self, f=sys.stdout):
146061da546Spatrick        f.write('            DATE       UID   GID   MODE   SIZE       NAME\n')
147061da546Spatrick        f.write('            ---------- ----- ----- ------ ---------- '
148061da546Spatrick                '--------------\n')
149061da546Spatrick
150061da546Spatrick    def get_symdef(self):
151061da546Spatrick        def get_uint32(file):
152061da546Spatrick            '''Extract a uint32_t from the current file position.'''
153061da546Spatrick            v, = struct.unpack('=I', file.read(4))
154061da546Spatrick            return v
155061da546Spatrick
156061da546Spatrick        for obj in self.objects:
157061da546Spatrick            symdef = []
158061da546Spatrick            if obj.name.startswith("__.SYMDEF"):
159061da546Spatrick                self.file.seek(obj.obj_offset, 0)
160061da546Spatrick                ranlib_byte_size = get_uint32(self.file)
161061da546Spatrick                num_ranlib_structs = ranlib_byte_size/8
162061da546Spatrick                str_offset_pairs = []
163061da546Spatrick                for _ in range(num_ranlib_structs):
164061da546Spatrick                    strx = get_uint32(self.file)
165061da546Spatrick                    offset = get_uint32(self.file)
166061da546Spatrick                    str_offset_pairs.append((strx, offset))
167061da546Spatrick                strtab_len = get_uint32(self.file)
168061da546Spatrick                strtab = StringTable(self.file.read(strtab_len))
169061da546Spatrick                for s in str_offset_pairs:
170061da546Spatrick                    symdef.append((strtab.get_string(s[0]), s[1]))
171061da546Spatrick            return symdef
172061da546Spatrick
173061da546Spatrick    def get_object_dicts(self):
174061da546Spatrick        '''
175061da546Spatrick            Returns an array of object dictionaries that contain they following
176061da546Spatrick            keys:
177061da546Spatrick                'object': the actual bsd.Object instance
178061da546Spatrick                'symdefs': an array of symbol names that the object contains
179061da546Spatrick                           as found in the "__.SYMDEF" item in the archive
180061da546Spatrick        '''
181061da546Spatrick        symdefs = self.get_symdef()
182061da546Spatrick        symdef_dict = {}
183061da546Spatrick        if symdefs:
184061da546Spatrick            for (name, offset) in symdefs:
185061da546Spatrick                if offset in symdef_dict:
186061da546Spatrick                    object_dict = symdef_dict[offset]
187061da546Spatrick                else:
188061da546Spatrick                    object_dict = {
189061da546Spatrick                        'object': self.get_object_at_offset(offset),
190061da546Spatrick                        'symdefs': []
191061da546Spatrick                    }
192061da546Spatrick                    symdef_dict[offset] = object_dict
193061da546Spatrick                object_dict['symdefs'].append(name)
194061da546Spatrick        object_dicts = []
195061da546Spatrick        for offset in sorted(symdef_dict):
196061da546Spatrick            object_dicts.append(symdef_dict[offset])
197061da546Spatrick        return object_dicts
198061da546Spatrick
199061da546Spatrick    def dump(self, f=sys.stdout, flat=True):
200061da546Spatrick        f.write('%s:\n' % self.path)
201061da546Spatrick        if flat:
202061da546Spatrick            self.dump_header(f=f)
203061da546Spatrick        for obj in self.objects:
204061da546Spatrick            obj.dump(f=f, flat=flat)
205061da546Spatrick
206061da546Spatrickclass Interactive(cmd.Cmd):
207061da546Spatrick    '''Interactive prompt for exploring contents of BSD archive files, type
208061da546Spatrick      "help" to see a list of supported commands.'''
209061da546Spatrick    image_option_parser = None
210061da546Spatrick
211061da546Spatrick    def __init__(self, archives):
212061da546Spatrick        cmd.Cmd.__init__(self)
213061da546Spatrick        self.use_rawinput = False
214061da546Spatrick        self.intro = ('Interactive  BSD archive prompt, type "help" to see a '
215061da546Spatrick                      'list of supported commands.')
216061da546Spatrick        self.archives = archives
217061da546Spatrick        self.prompt = '% '
218061da546Spatrick
219061da546Spatrick    def default(self, line):
220061da546Spatrick        '''Catch all for unknown command, which will exit the interpreter.'''
221061da546Spatrick        print("unknown command: %s" % line)
222061da546Spatrick        return True
223061da546Spatrick
224061da546Spatrick    def do_q(self, line):
225061da546Spatrick        '''Quit command'''
226061da546Spatrick        return True
227061da546Spatrick
228061da546Spatrick    def do_quit(self, line):
229061da546Spatrick        '''Quit command'''
230061da546Spatrick        return True
231061da546Spatrick
232061da546Spatrick    def do_extract(self, line):
233061da546Spatrick        args = shlex.split(line)
234061da546Spatrick        if args:
235061da546Spatrick            extracted = False
236061da546Spatrick            for object_name in args:
237061da546Spatrick                for archive in self.archives:
238061da546Spatrick                    matches = archive.find(object_name)
239061da546Spatrick                    if matches:
240061da546Spatrick                        for object in matches:
241061da546Spatrick                            object.save(overwrite=False)
242061da546Spatrick                            extracted = True
243061da546Spatrick            if not extracted:
244061da546Spatrick                print('error: no object matches "%s" in any archives' % (
245061da546Spatrick                        object_name))
246061da546Spatrick        else:
247061da546Spatrick            print('error: must specify the name of an object to extract')
248061da546Spatrick
249061da546Spatrick    def do_ls(self, line):
250061da546Spatrick        args = shlex.split(line)
251061da546Spatrick        if args:
252061da546Spatrick            for object_name in args:
253061da546Spatrick                for archive in self.archives:
254061da546Spatrick                    matches = archive.find(object_name)
255061da546Spatrick                    if matches:
256061da546Spatrick                        for object in matches:
257061da546Spatrick                            object.dump(flat=False)
258061da546Spatrick                    else:
259061da546Spatrick                        print('error: no object matches "%s" in "%s"' % (
260061da546Spatrick                                object_name, archive.path))
261061da546Spatrick        else:
262061da546Spatrick            for archive in self.archives:
263061da546Spatrick                archive.dump(flat=True)
264061da546Spatrick                print('')
265061da546Spatrick
266061da546Spatrick
267061da546Spatrick
268061da546Spatrickdef main():
269061da546Spatrick    parser = optparse.OptionParser(
270061da546Spatrick        prog='bsd',
271061da546Spatrick        description='Utility for BSD archives')
272061da546Spatrick    parser.add_option(
273061da546Spatrick        '--object',
274061da546Spatrick        type='string',
275061da546Spatrick        dest='object_name',
276061da546Spatrick        default=None,
277061da546Spatrick        help=('Specify the name of a object within the BSD archive to get '
278061da546Spatrick              'information on'))
279061da546Spatrick    parser.add_option(
280061da546Spatrick        '-s', '--symbol',
281061da546Spatrick        type='string',
282061da546Spatrick        dest='find_symbol',
283061da546Spatrick        default=None,
284061da546Spatrick        help=('Specify the name of a symbol within the BSD archive to get '
285061da546Spatrick              'information on from SYMDEF'))
286061da546Spatrick    parser.add_option(
287061da546Spatrick        '--symdef',
288061da546Spatrick        action='store_true',
289061da546Spatrick        dest='symdef',
290061da546Spatrick        default=False,
291061da546Spatrick        help=('Dump the information in the SYMDEF.'))
292061da546Spatrick    parser.add_option(
293061da546Spatrick        '-v', '--verbose',
294061da546Spatrick        action='store_true',
295061da546Spatrick        dest='verbose',
296061da546Spatrick        default=False,
297061da546Spatrick        help='Enable verbose output')
298061da546Spatrick    parser.add_option(
299061da546Spatrick        '-e', '--extract',
300061da546Spatrick        action='store_true',
301061da546Spatrick        dest='extract',
302061da546Spatrick        default=False,
303061da546Spatrick        help=('Specify this to extract the object specified with the --object '
304061da546Spatrick              'option. There must be only one object with a matching name or '
305061da546Spatrick              'the --mtime option must be specified to uniquely identify a '
306061da546Spatrick              'single object.'))
307061da546Spatrick    parser.add_option(
308061da546Spatrick        '-m', '--mtime',
309061da546Spatrick        type='int',
310061da546Spatrick        dest='mtime',
311061da546Spatrick        default=None,
312061da546Spatrick        help=('Specify the modification time of the object an object. This '
313061da546Spatrick              'option is used with either the --object or --extract options.'))
314061da546Spatrick    parser.add_option(
315061da546Spatrick        '-o', '--outfile',
316061da546Spatrick        type='string',
317061da546Spatrick        dest='outfile',
318061da546Spatrick        default=None,
319061da546Spatrick        help=('Specify a different name or path for the file to extract when '
320061da546Spatrick              'using the --extract option. If this option isn\'t specified, '
321061da546Spatrick              'then the extracted object file will be extracted into the '
322061da546Spatrick              'current working directory if a file doesn\'t already exist '
323061da546Spatrick              'with that name.'))
324061da546Spatrick    parser.add_option(
325061da546Spatrick        '-i', '--interactive',
326061da546Spatrick        action='store_true',
327061da546Spatrick        dest='interactive',
328061da546Spatrick        default=False,
329061da546Spatrick        help=('Enter an interactive shell that allows users to interactively '
330061da546Spatrick              'explore contents of .a files.'))
331061da546Spatrick
332061da546Spatrick    (options, args) = parser.parse_args(sys.argv[1:])
333061da546Spatrick
334061da546Spatrick    if options.interactive:
335061da546Spatrick        archives = []
336061da546Spatrick        for path in args:
337061da546Spatrick            archives.append(Archive(path))
338061da546Spatrick        interpreter = Interactive(archives)
339061da546Spatrick        interpreter.cmdloop()
340061da546Spatrick        return
341061da546Spatrick
342061da546Spatrick    for path in args:
343061da546Spatrick        archive = Archive(path)
344061da546Spatrick        if options.object_name:
345061da546Spatrick            print('%s:\n' % (path))
346061da546Spatrick            matches = archive.find(options.object_name, options.mtime)
347061da546Spatrick            if matches:
348061da546Spatrick                dump_all = True
349061da546Spatrick                if options.extract:
350061da546Spatrick                    if len(matches) == 1:
351061da546Spatrick                        dump_all = False
352061da546Spatrick                        matches[0].save(path=options.outfile, overwrite=False)
353061da546Spatrick                    else:
354061da546Spatrick                        print('error: multiple objects match "%s". Specify '
355061da546Spatrick                              'the modification time using --mtime.' % (
356061da546Spatrick                                options.object_name))
357061da546Spatrick                if dump_all:
358061da546Spatrick                    for obj in matches:
359061da546Spatrick                        obj.dump(flat=False)
360061da546Spatrick            else:
361061da546Spatrick                print('error: object "%s" not found in archive' % (
362061da546Spatrick                      options.object_name))
363061da546Spatrick        elif options.find_symbol:
364061da546Spatrick            symdefs = archive.get_symdef()
365061da546Spatrick            if symdefs:
366061da546Spatrick                success = False
367061da546Spatrick                for (name, offset) in symdefs:
368061da546Spatrick                    obj = archive.get_object_at_offset(offset)
369061da546Spatrick                    if name == options.find_symbol:
370061da546Spatrick                        print('Found "%s" in:' % (options.find_symbol))
371061da546Spatrick                        obj.dump(flat=False)
372061da546Spatrick                        success = True
373061da546Spatrick                if not success:
374061da546Spatrick                    print('Didn\'t find "%s" in any objects' % (
375061da546Spatrick                          options.find_symbol))
376061da546Spatrick            else:
377061da546Spatrick                print("error: no __.SYMDEF was found")
378061da546Spatrick        elif options.symdef:
379061da546Spatrick            object_dicts = archive.get_object_dicts()
380061da546Spatrick            for object_dict in object_dicts:
381061da546Spatrick                object_dict['object'].dump(flat=False)
382061da546Spatrick                print("symbols:")
383061da546Spatrick                for name in object_dict['symdefs']:
384061da546Spatrick                    print("  %s" % (name))
385061da546Spatrick        else:
386061da546Spatrick            archive.dump(flat=not options.verbose)
387061da546Spatrick
388061da546Spatrick
389061da546Spatrickif __name__ == '__main__':
390061da546Spatrick    main()
391061da546Spatrick
392061da546Spatrick
393061da546Spatrickdef print_mtime_error(result, dmap_mtime, actual_mtime):
394061da546Spatrick    print("error: modification time in debug map (%#08.8x) doesn't "
395061da546Spatrick                     "match the .o file modification time (%#08.8x)" % (
396061da546Spatrick                        dmap_mtime, actual_mtime), file=result)
397061da546Spatrick
398061da546Spatrick
399061da546Spatrickdef print_file_missing_error(result, path):
400061da546Spatrick    print("error: file \"%s\" doesn't exist" % (path), file=result)
401061da546Spatrick
402061da546Spatrick
403061da546Spatrickdef print_multiple_object_matches(result, object_name, mtime, matches):
404061da546Spatrick    print("error: multiple matches for object '%s' with with "
405061da546Spatrick                     "modification time %#08.8x:" % (object_name, mtime), file=result)
406061da546Spatrick    Archive.dump_header(f=result)
407061da546Spatrick    for match in matches:
408061da546Spatrick        match.dump(f=result, flat=True)
409061da546Spatrick
410061da546Spatrick
411061da546Spatrickdef print_archive_object_error(result, object_name, mtime, archive):
412061da546Spatrick    matches = archive.find(object_name, f=result)
413061da546Spatrick    if len(matches) > 0:
414061da546Spatrick        print("error: no objects have a modification time that "
415061da546Spatrick                         "matches %#08.8x for '%s'. Potential matches:" % (
416061da546Spatrick                            mtime, object_name), file=result)
417061da546Spatrick        Archive.dump_header(f=result)
418061da546Spatrick        for match in matches:
419061da546Spatrick            match.dump(f=result, flat=True)
420061da546Spatrick    else:
421061da546Spatrick        print("error: no object named \"%s\" found in archive:" % (
422061da546Spatrick            object_name), file=result)
423061da546Spatrick        Archive.dump_header(f=result)
424061da546Spatrick        for match in archive.objects:
425061da546Spatrick            match.dump(f=result, flat=True)
426061da546Spatrick        # archive.dump(f=result, flat=True)
427061da546Spatrick
428061da546Spatrick
429061da546Spatrickclass VerifyDebugMapCommand:
430061da546Spatrick    name = "verify-debug-map-objects"
431061da546Spatrick
432061da546Spatrick    def create_options(self):
433061da546Spatrick        usage = "usage: %prog [options]"
434061da546Spatrick        description = '''This command reports any .o files that are missing
435061da546Spatrickor whose modification times don't match in the debug map of an executable.'''
436061da546Spatrick
437061da546Spatrick        self.parser = optparse.OptionParser(
438061da546Spatrick            description=description,
439061da546Spatrick            prog=self.name,
440061da546Spatrick            usage=usage,
441061da546Spatrick            add_help_option=False)
442061da546Spatrick
443061da546Spatrick        self.parser.add_option(
444061da546Spatrick            '-e', '--errors',
445061da546Spatrick            action='store_true',
446061da546Spatrick            dest='errors',
447061da546Spatrick            default=False,
448061da546Spatrick            help="Only show errors")
449061da546Spatrick
450061da546Spatrick    def get_short_help(self):
451061da546Spatrick        return "Verify debug map object files."
452061da546Spatrick
453061da546Spatrick    def get_long_help(self):
454061da546Spatrick        return self.help_string
455061da546Spatrick
456061da546Spatrick    def __init__(self, debugger, unused):
457061da546Spatrick        self.create_options()
458061da546Spatrick        self.help_string = self.parser.format_help()
459061da546Spatrick
460061da546Spatrick    def __call__(self, debugger, command, exe_ctx, result):
461061da546Spatrick        import lldb
462061da546Spatrick        # Use the Shell Lexer to properly parse up command options just like a
463061da546Spatrick        # shell would
464061da546Spatrick        command_args = shlex.split(command)
465061da546Spatrick
466061da546Spatrick        try:
467061da546Spatrick            (options, args) = self.parser.parse_args(command_args)
468061da546Spatrick        except:
469061da546Spatrick            result.SetError("option parsing failed")
470061da546Spatrick            return
471061da546Spatrick
472061da546Spatrick        # Always get program state from the SBExecutionContext passed in
473061da546Spatrick        target = exe_ctx.GetTarget()
474061da546Spatrick        if not target.IsValid():
475061da546Spatrick            result.SetError("invalid target")
476061da546Spatrick            return
477061da546Spatrick        archives = {}
478061da546Spatrick        for module_spec in args:
479061da546Spatrick            module = target.module[module_spec]
480061da546Spatrick            if not (module and module.IsValid()):
481061da546Spatrick                result.SetError('error: invalid module specification: "%s". '
482061da546Spatrick                                'Specify the full path, basename, or UUID of '
483061da546Spatrick                                'a module ' % (module_spec))
484061da546Spatrick                return
485061da546Spatrick            num_symbols = module.GetNumSymbols()
486061da546Spatrick            num_errors = 0
487061da546Spatrick            for i in range(num_symbols):
488061da546Spatrick                symbol = module.GetSymbolAtIndex(i)
489061da546Spatrick                if symbol.GetType() != lldb.eSymbolTypeObjectFile:
490061da546Spatrick                    continue
491061da546Spatrick                path = symbol.GetName()
492061da546Spatrick                if not path:
493061da546Spatrick                    continue
494061da546Spatrick                # Extract the value of the symbol by dumping the
495061da546Spatrick                # symbol. The value is the mod time.
496061da546Spatrick                dmap_mtime = int(str(symbol).split('value = ')
497061da546Spatrick                                 [1].split(',')[0], 16)
498061da546Spatrick                if not options.errors:
499061da546Spatrick                    print('%s' % (path), file=result)
500061da546Spatrick                if os.path.exists(path):
501061da546Spatrick                    actual_mtime = int(os.stat(path).st_mtime)
502061da546Spatrick                    if dmap_mtime != actual_mtime:
503061da546Spatrick                        num_errors += 1
504061da546Spatrick                        if options.errors:
505061da546Spatrick                            print('%s' % (path), end=' ', file=result)
506061da546Spatrick                        print_mtime_error(result, dmap_mtime,
507061da546Spatrick                                          actual_mtime)
508061da546Spatrick                elif path[-1] == ')':
509061da546Spatrick                    (archive_path, object_name) = path[0:-1].split('(')
510061da546Spatrick                    if not archive_path and not object_name:
511061da546Spatrick                        num_errors += 1
512061da546Spatrick                        if options.errors:
513061da546Spatrick                            print('%s' % (path), end=' ', file=result)
514061da546Spatrick                        print_file_missing_error(path)
515061da546Spatrick                        continue
516061da546Spatrick                    if not os.path.exists(archive_path):
517061da546Spatrick                        num_errors += 1
518061da546Spatrick                        if options.errors:
519061da546Spatrick                            print('%s' % (path), end=' ', file=result)
520061da546Spatrick                        print_file_missing_error(archive_path)
521061da546Spatrick                        continue
522061da546Spatrick                    if archive_path in archives:
523061da546Spatrick                        archive = archives[archive_path]
524061da546Spatrick                    else:
525061da546Spatrick                        archive = Archive(archive_path)
526061da546Spatrick                        archives[archive_path] = archive
527061da546Spatrick                    matches = archive.find(object_name, dmap_mtime)
528061da546Spatrick                    num_matches = len(matches)
529061da546Spatrick                    if num_matches == 1:
530061da546Spatrick                        print('1 match', file=result)
531061da546Spatrick                        obj = matches[0]
532061da546Spatrick                        if obj.date != dmap_mtime:
533061da546Spatrick                            num_errors += 1
534061da546Spatrick                            if options.errors:
535061da546Spatrick                                print('%s' % (path), end=' ', file=result)
536061da546Spatrick                            print_mtime_error(result, dmap_mtime, obj.date)
537061da546Spatrick                    elif num_matches == 0:
538061da546Spatrick                        num_errors += 1
539061da546Spatrick                        if options.errors:
540061da546Spatrick                            print('%s' % (path), end=' ', file=result)
541061da546Spatrick                        print_archive_object_error(result, object_name,
542061da546Spatrick                                                   dmap_mtime, archive)
543061da546Spatrick                    elif num_matches > 1:
544061da546Spatrick                        num_errors += 1
545061da546Spatrick                        if options.errors:
546061da546Spatrick                            print('%s' % (path), end=' ', file=result)
547061da546Spatrick                        print_multiple_object_matches(result,
548061da546Spatrick                                                      object_name,
549061da546Spatrick                                                      dmap_mtime, matches)
550061da546Spatrick            if num_errors > 0:
551061da546Spatrick                print("%u errors found" % (num_errors), file=result)
552061da546Spatrick            else:
553061da546Spatrick                print("No errors detected in debug map", file=result)
554061da546Spatrick
555061da546Spatrick
556061da546Spatrickdef __lldb_init_module(debugger, dict):
557061da546Spatrick    # This initializer is being run from LLDB in the embedded command
558061da546Spatrick    # interpreter.
559061da546Spatrick    # Add any commands contained in this module to LLDB
560061da546Spatrick    debugger.HandleCommand(
561*f6aab3d8Srobert        'command script add -o -c %s.VerifyDebugMapCommand %s' % (
562061da546Spatrick            __name__, VerifyDebugMapCommand.name))
563061da546Spatrick    print('The "%s" command has been installed, type "help %s" for detailed '
564061da546Spatrick          'help.' % (VerifyDebugMapCommand.name, VerifyDebugMapCommand.name))
565