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