xref: /llvm-project/lldb/examples/python/bsd.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1515bc8c1Sserge-sans-paille#!/usr/bin/env python
2de01668bSGreg Clayton
395c23f66SGreg Claytonimport cmd
4de01668bSGreg Claytonimport optparse
5de01668bSGreg Claytonimport os
6de01668bSGreg Claytonimport shlex
7de01668bSGreg Claytonimport struct
8de01668bSGreg Claytonimport sys
9de01668bSGreg Clayton
10de01668bSGreg ClaytonARMAG = "!<arch>\n"
11de01668bSGreg ClaytonSARMAG = 8
12de01668bSGreg ClaytonARFMAG = "`\n"
13de01668bSGreg ClaytonAR_EFMT1 = "#1/"
14de01668bSGreg Clayton
15de01668bSGreg Clayton
16de01668bSGreg Claytondef memdump(src, bytes_per_line=16, address=0):
17*2238dcc3SJonas Devlieghere    FILTER = "".join([(len(repr(chr(x))) == 3) and chr(x) or "." for x in range(256)])
18de01668bSGreg Clayton    for i in range(0, len(src), bytes_per_line):
19de01668bSGreg Clayton        s = src[i : i + bytes_per_line]
20*2238dcc3SJonas Devlieghere        hex_bytes = " ".join(["%02x" % (ord(x)) for x in s])
21de01668bSGreg Clayton        ascii = s.translate(FILTER)
22*2238dcc3SJonas Devlieghere        print("%#08.8x: %-*s %s" % (address + i, bytes_per_line * 3, hex_bytes, ascii))
23de01668bSGreg Clayton
24de01668bSGreg Clayton
25de01668bSGreg Claytonclass Object(object):
26de01668bSGreg Clayton    def __init__(self, file):
27de01668bSGreg Clayton        def read_str(file, str_len):
28*2238dcc3SJonas Devlieghere            return file.read(str_len).rstrip("\0 ")
29de01668bSGreg Clayton
30de01668bSGreg Clayton        def read_int(file, str_len, base):
31de01668bSGreg Clayton            return int(read_str(file, str_len), base)
32de01668bSGreg Clayton
33de01668bSGreg Clayton        self.offset = file.tell()
34de01668bSGreg Clayton        self.file = file
35de01668bSGreg Clayton        self.name = read_str(file, 16)
36de01668bSGreg Clayton        self.date = read_int(file, 12, 10)
37de01668bSGreg Clayton        self.uid = read_int(file, 6, 10)
38de01668bSGreg Clayton        self.gid = read_int(file, 6, 10)
39de01668bSGreg Clayton        self.mode = read_int(file, 8, 8)
40de01668bSGreg Clayton        self.size = read_int(file, 10, 10)
41de01668bSGreg Clayton        if file.read(2) != ARFMAG:
42*2238dcc3SJonas Devlieghere            raise ValueError("invalid BSD object at offset %#08.8x" % (self.offset))
43de01668bSGreg Clayton        # If we have an extended name read it. Extended names start with
44de01668bSGreg Clayton        name_len = 0
45de01668bSGreg Clayton        if self.name.startswith(AR_EFMT1):
46de01668bSGreg Clayton            name_len = int(self.name[len(AR_EFMT1) :], 10)
47de01668bSGreg Clayton            self.name = read_str(file, name_len)
48de01668bSGreg Clayton        self.obj_offset = file.tell()
49de01668bSGreg Clayton        self.obj_size = self.size - name_len
50de01668bSGreg Clayton        file.seek(self.obj_size, 1)
51de01668bSGreg Clayton
52de01668bSGreg Clayton    def dump(self, f=sys.stdout, flat=True):
53de01668bSGreg Clayton        if flat:
54*2238dcc3SJonas Devlieghere            f.write(
55*2238dcc3SJonas Devlieghere                "%#08.8x: %#08.8x %5u %5u %6o %#08.8x %s\n"
56*2238dcc3SJonas Devlieghere                % (
57*2238dcc3SJonas Devlieghere                    self.offset,
58*2238dcc3SJonas Devlieghere                    self.date,
59*2238dcc3SJonas Devlieghere                    self.uid,
60*2238dcc3SJonas Devlieghere                    self.gid,
61*2238dcc3SJonas Devlieghere                    self.mode,
62*2238dcc3SJonas Devlieghere                    self.size,
63*2238dcc3SJonas Devlieghere                    self.name,
64*2238dcc3SJonas Devlieghere                )
65*2238dcc3SJonas Devlieghere            )
66de01668bSGreg Clayton        else:
67*2238dcc3SJonas Devlieghere            f.write("%#08.8x: \n" % self.offset)
68de01668bSGreg Clayton            f.write(' name = "%s"\n' % self.name)
69*2238dcc3SJonas Devlieghere            f.write(" date = %#08.8x\n" % self.date)
70*2238dcc3SJonas Devlieghere            f.write("  uid = %i\n" % self.uid)
71*2238dcc3SJonas Devlieghere            f.write("  gid = %i\n" % self.gid)
72*2238dcc3SJonas Devlieghere            f.write(" mode = %o\n" % self.mode)
73*2238dcc3SJonas Devlieghere            f.write(" size = %#08.8x\n" % (self.size))
74de01668bSGreg Clayton            self.file.seek(self.obj_offset, 0)
75de01668bSGreg Clayton            first_bytes = self.file.read(4)
76*2238dcc3SJonas Devlieghere            f.write("bytes = ")
77de01668bSGreg Clayton            memdump(first_bytes)
78de01668bSGreg Clayton
79de01668bSGreg Clayton    def get_bytes(self):
80de01668bSGreg Clayton        saved_pos = self.file.tell()
81de01668bSGreg Clayton        self.file.seek(self.obj_offset, 0)
82de01668bSGreg Clayton        bytes = self.file.read(self.obj_size)
83de01668bSGreg Clayton        self.file.seek(saved_pos, 0)
84de01668bSGreg Clayton        return bytes
85de01668bSGreg Clayton
8695c23f66SGreg Clayton    def save(self, path=None, overwrite=False):
87*2238dcc3SJonas Devlieghere        """
8895c23f66SGreg Clayton        Save the contents of the object to disk using 'path' argument as
8995c23f66SGreg Clayton        the path, or save it to the current working directory using the
9095c23f66SGreg Clayton        object name.
91*2238dcc3SJonas Devlieghere        """
9295c23f66SGreg Clayton
9395c23f66SGreg Clayton        if path is None:
9495c23f66SGreg Clayton            path = self.name
9595c23f66SGreg Clayton        if not overwrite and os.path.exists(path):
9695c23f66SGreg Clayton            print('error: outfile "%s" already exists' % (path))
9795c23f66SGreg Clayton            return
9895c23f66SGreg Clayton        print('Saving "%s" to "%s"...' % (self.name, path))
99*2238dcc3SJonas Devlieghere        with open(path, "w") as f:
10095c23f66SGreg Clayton            f.write(self.get_bytes())
10195c23f66SGreg Clayton
102de01668bSGreg Clayton
103de01668bSGreg Claytonclass StringTable(object):
104de01668bSGreg Clayton    def __init__(self, bytes):
105de01668bSGreg Clayton        self.bytes = bytes
106de01668bSGreg Clayton
107de01668bSGreg Clayton    def get_string(self, offset):
108de01668bSGreg Clayton        length = len(self.bytes)
109de01668bSGreg Clayton        if offset >= length:
110de01668bSGreg Clayton            return None
111*2238dcc3SJonas Devlieghere        return self.bytes[offset : self.bytes.find("\0", offset)]
112de01668bSGreg Clayton
113de01668bSGreg Clayton
114de01668bSGreg Claytonclass Archive(object):
115de01668bSGreg Clayton    def __init__(self, path):
116de01668bSGreg Clayton        self.path = path
117*2238dcc3SJonas Devlieghere        self.file = open(path, "r")
118de01668bSGreg Clayton        self.objects = []
119de01668bSGreg Clayton        self.offset_to_object = {}
120de01668bSGreg Clayton        if self.file.read(SARMAG) != ARMAG:
121de01668bSGreg Clayton            print("error: file isn't a BSD archive")
122de01668bSGreg Clayton        while True:
123de01668bSGreg Clayton            try:
124de01668bSGreg Clayton                self.objects.append(Object(self.file))
125de01668bSGreg Clayton            except ValueError:
126de01668bSGreg Clayton                break
127de01668bSGreg Clayton
128de01668bSGreg Clayton    def get_object_at_offset(self, offset):
129de01668bSGreg Clayton        if offset in self.offset_to_object:
130de01668bSGreg Clayton            return self.offset_to_object[offset]
131de01668bSGreg Clayton        for obj in self.objects:
132de01668bSGreg Clayton            if obj.offset == offset:
133de01668bSGreg Clayton                self.offset_to_object[offset] = obj
134de01668bSGreg Clayton                return obj
135de01668bSGreg Clayton        return None
136de01668bSGreg Clayton
137de01668bSGreg Clayton    def find(self, name, mtime=None, f=sys.stdout):
138*2238dcc3SJonas Devlieghere        """
139de01668bSGreg Clayton        Find an object(s) by name with optional modification time. There
140de01668bSGreg Clayton        can be multple objects with the same name inside and possibly with
141de01668bSGreg Clayton        the same modification time within a BSD archive so clients must be
142de01668bSGreg Clayton        prepared to get multiple results.
143*2238dcc3SJonas Devlieghere        """
144de01668bSGreg Clayton        matches = []
145de01668bSGreg Clayton        for obj in self.objects:
146de01668bSGreg Clayton            if obj.name == name and (mtime is None or mtime == obj.date):
147de01668bSGreg Clayton                matches.append(obj)
148de01668bSGreg Clayton        return matches
149de01668bSGreg Clayton
150de01668bSGreg Clayton    @classmethod
151de01668bSGreg Clayton    def dump_header(self, f=sys.stdout):
152*2238dcc3SJonas Devlieghere        f.write("            DATE       UID   GID   MODE   SIZE       NAME\n")
153*2238dcc3SJonas Devlieghere        f.write(
154*2238dcc3SJonas Devlieghere            "            ---------- ----- ----- ------ ---------- " "--------------\n"
155*2238dcc3SJonas Devlieghere        )
156de01668bSGreg Clayton
157de01668bSGreg Clayton    def get_symdef(self):
158de01668bSGreg Clayton        def get_uint32(file):
159*2238dcc3SJonas Devlieghere            """Extract a uint32_t from the current file position."""
160*2238dcc3SJonas Devlieghere            (v,) = struct.unpack("=I", file.read(4))
161de01668bSGreg Clayton            return v
162de01668bSGreg Clayton
163de01668bSGreg Clayton        for obj in self.objects:
164de01668bSGreg Clayton            symdef = []
165de01668bSGreg Clayton            if obj.name.startswith("__.SYMDEF"):
166de01668bSGreg Clayton                self.file.seek(obj.obj_offset, 0)
167de01668bSGreg Clayton                ranlib_byte_size = get_uint32(self.file)
168de01668bSGreg Clayton                num_ranlib_structs = ranlib_byte_size / 8
169de01668bSGreg Clayton                str_offset_pairs = []
170de01668bSGreg Clayton                for _ in range(num_ranlib_structs):
171de01668bSGreg Clayton                    strx = get_uint32(self.file)
172de01668bSGreg Clayton                    offset = get_uint32(self.file)
173de01668bSGreg Clayton                    str_offset_pairs.append((strx, offset))
174de01668bSGreg Clayton                strtab_len = get_uint32(self.file)
175de01668bSGreg Clayton                strtab = StringTable(self.file.read(strtab_len))
176de01668bSGreg Clayton                for s in str_offset_pairs:
177de01668bSGreg Clayton                    symdef.append((strtab.get_string(s[0]), s[1]))
178de01668bSGreg Clayton            return symdef
179de01668bSGreg Clayton
180de01668bSGreg Clayton    def get_object_dicts(self):
181*2238dcc3SJonas Devlieghere        """
182de01668bSGreg Clayton        Returns an array of object dictionaries that contain they following
183de01668bSGreg Clayton        keys:
184de01668bSGreg Clayton            'object': the actual bsd.Object instance
185de01668bSGreg Clayton            'symdefs': an array of symbol names that the object contains
186de01668bSGreg Clayton                       as found in the "__.SYMDEF" item in the archive
187*2238dcc3SJonas Devlieghere        """
188de01668bSGreg Clayton        symdefs = self.get_symdef()
189de01668bSGreg Clayton        symdef_dict = {}
190de01668bSGreg Clayton        if symdefs:
191*2238dcc3SJonas Devlieghere            for name, offset in symdefs:
192de01668bSGreg Clayton                if offset in symdef_dict:
193de01668bSGreg Clayton                    object_dict = symdef_dict[offset]
194de01668bSGreg Clayton                else:
195de01668bSGreg Clayton                    object_dict = {
196*2238dcc3SJonas Devlieghere                        "object": self.get_object_at_offset(offset),
197*2238dcc3SJonas Devlieghere                        "symdefs": [],
198de01668bSGreg Clayton                    }
199de01668bSGreg Clayton                    symdef_dict[offset] = object_dict
200*2238dcc3SJonas Devlieghere                object_dict["symdefs"].append(name)
201de01668bSGreg Clayton        object_dicts = []
202de01668bSGreg Clayton        for offset in sorted(symdef_dict):
203de01668bSGreg Clayton            object_dicts.append(symdef_dict[offset])
204de01668bSGreg Clayton        return object_dicts
205de01668bSGreg Clayton
206de01668bSGreg Clayton    def dump(self, f=sys.stdout, flat=True):
207*2238dcc3SJonas Devlieghere        f.write("%s:\n" % self.path)
208de01668bSGreg Clayton        if flat:
209de01668bSGreg Clayton            self.dump_header(f=f)
210de01668bSGreg Clayton        for obj in self.objects:
211de01668bSGreg Clayton            obj.dump(f=f, flat=flat)
212de01668bSGreg Clayton
213*2238dcc3SJonas Devlieghere
21495c23f66SGreg Claytonclass Interactive(cmd.Cmd):
215*2238dcc3SJonas Devlieghere    """Interactive prompt for exploring contents of BSD archive files, type
216*2238dcc3SJonas Devlieghere    "help" to see a list of supported commands."""
217*2238dcc3SJonas Devlieghere
21895c23f66SGreg Clayton    image_option_parser = None
21995c23f66SGreg Clayton
22095c23f66SGreg Clayton    def __init__(self, archives):
22195c23f66SGreg Clayton        cmd.Cmd.__init__(self)
22295c23f66SGreg Clayton        self.use_rawinput = False
223*2238dcc3SJonas Devlieghere        self.intro = (
224*2238dcc3SJonas Devlieghere            'Interactive  BSD archive prompt, type "help" to see a '
225*2238dcc3SJonas Devlieghere            "list of supported commands."
226*2238dcc3SJonas Devlieghere        )
22795c23f66SGreg Clayton        self.archives = archives
228*2238dcc3SJonas Devlieghere        self.prompt = "% "
22995c23f66SGreg Clayton
23095c23f66SGreg Clayton    def default(self, line):
231*2238dcc3SJonas Devlieghere        """Catch all for unknown command, which will exit the interpreter."""
23295c23f66SGreg Clayton        print("unknown command: %s" % line)
23395c23f66SGreg Clayton        return True
23495c23f66SGreg Clayton
23595c23f66SGreg Clayton    def do_q(self, line):
236*2238dcc3SJonas Devlieghere        """Quit command"""
23795c23f66SGreg Clayton        return True
23895c23f66SGreg Clayton
23995c23f66SGreg Clayton    def do_quit(self, line):
240*2238dcc3SJonas Devlieghere        """Quit command"""
24195c23f66SGreg Clayton        return True
24295c23f66SGreg Clayton
24395c23f66SGreg Clayton    def do_extract(self, line):
24495c23f66SGreg Clayton        args = shlex.split(line)
24595c23f66SGreg Clayton        if args:
24695c23f66SGreg Clayton            extracted = False
24795c23f66SGreg Clayton            for object_name in args:
24895c23f66SGreg Clayton                for archive in self.archives:
24995c23f66SGreg Clayton                    matches = archive.find(object_name)
25095c23f66SGreg Clayton                    if matches:
25195c23f66SGreg Clayton                        for object in matches:
25295c23f66SGreg Clayton                            object.save(overwrite=False)
25395c23f66SGreg Clayton                            extracted = True
25495c23f66SGreg Clayton            if not extracted:
255*2238dcc3SJonas Devlieghere                print('error: no object matches "%s" in any archives' % (object_name))
25695c23f66SGreg Clayton        else:
257*2238dcc3SJonas Devlieghere            print("error: must specify the name of an object to extract")
25895c23f66SGreg Clayton
25995c23f66SGreg Clayton    def do_ls(self, line):
26095c23f66SGreg Clayton        args = shlex.split(line)
26195c23f66SGreg Clayton        if args:
26295c23f66SGreg Clayton            for object_name in args:
26395c23f66SGreg Clayton                for archive in self.archives:
26495c23f66SGreg Clayton                    matches = archive.find(object_name)
26595c23f66SGreg Clayton                    if matches:
26695c23f66SGreg Clayton                        for object in matches:
26795c23f66SGreg Clayton                            object.dump(flat=False)
26895c23f66SGreg Clayton                    else:
269*2238dcc3SJonas Devlieghere                        print(
270*2238dcc3SJonas Devlieghere                            'error: no object matches "%s" in "%s"'
271*2238dcc3SJonas Devlieghere                            % (object_name, archive.path)
272*2238dcc3SJonas Devlieghere                        )
27395c23f66SGreg Clayton        else:
27495c23f66SGreg Clayton            for archive in self.archives:
27595c23f66SGreg Clayton                archive.dump(flat=True)
276*2238dcc3SJonas Devlieghere                print("")
27795c23f66SGreg Clayton
278de01668bSGreg Clayton
279de01668bSGreg Claytondef main():
280*2238dcc3SJonas Devlieghere    parser = optparse.OptionParser(prog="bsd", description="Utility for BSD archives")
281de01668bSGreg Clayton    parser.add_option(
282*2238dcc3SJonas Devlieghere        "--object",
283*2238dcc3SJonas Devlieghere        type="string",
284*2238dcc3SJonas Devlieghere        dest="object_name",
285de01668bSGreg Clayton        default=None,
286*2238dcc3SJonas Devlieghere        help=(
287*2238dcc3SJonas Devlieghere            "Specify the name of a object within the BSD archive to get "
288*2238dcc3SJonas Devlieghere            "information on"
289*2238dcc3SJonas Devlieghere        ),
290*2238dcc3SJonas Devlieghere    )
291de01668bSGreg Clayton    parser.add_option(
292*2238dcc3SJonas Devlieghere        "-s",
293*2238dcc3SJonas Devlieghere        "--symbol",
294*2238dcc3SJonas Devlieghere        type="string",
295*2238dcc3SJonas Devlieghere        dest="find_symbol",
296de01668bSGreg Clayton        default=None,
297*2238dcc3SJonas Devlieghere        help=(
298*2238dcc3SJonas Devlieghere            "Specify the name of a symbol within the BSD archive to get "
299*2238dcc3SJonas Devlieghere            "information on from SYMDEF"
300*2238dcc3SJonas Devlieghere        ),
301*2238dcc3SJonas Devlieghere    )
302de01668bSGreg Clayton    parser.add_option(
303*2238dcc3SJonas Devlieghere        "--symdef",
304*2238dcc3SJonas Devlieghere        action="store_true",
305*2238dcc3SJonas Devlieghere        dest="symdef",
306de01668bSGreg Clayton        default=False,
307*2238dcc3SJonas Devlieghere        help=("Dump the information in the SYMDEF."),
308*2238dcc3SJonas Devlieghere    )
309de01668bSGreg Clayton    parser.add_option(
310*2238dcc3SJonas Devlieghere        "-v",
311*2238dcc3SJonas Devlieghere        "--verbose",
312*2238dcc3SJonas Devlieghere        action="store_true",
313*2238dcc3SJonas Devlieghere        dest="verbose",
314de01668bSGreg Clayton        default=False,
315*2238dcc3SJonas Devlieghere        help="Enable verbose output",
316*2238dcc3SJonas Devlieghere    )
317de01668bSGreg Clayton    parser.add_option(
318*2238dcc3SJonas Devlieghere        "-e",
319*2238dcc3SJonas Devlieghere        "--extract",
320*2238dcc3SJonas Devlieghere        action="store_true",
321*2238dcc3SJonas Devlieghere        dest="extract",
322de01668bSGreg Clayton        default=False,
323*2238dcc3SJonas Devlieghere        help=(
324*2238dcc3SJonas Devlieghere            "Specify this to extract the object specified with the --object "
325*2238dcc3SJonas Devlieghere            "option. There must be only one object with a matching name or "
326*2238dcc3SJonas Devlieghere            "the --mtime option must be specified to uniquely identify a "
327*2238dcc3SJonas Devlieghere            "single object."
328*2238dcc3SJonas Devlieghere        ),
329*2238dcc3SJonas Devlieghere    )
330de01668bSGreg Clayton    parser.add_option(
331*2238dcc3SJonas Devlieghere        "-m",
332*2238dcc3SJonas Devlieghere        "--mtime",
333*2238dcc3SJonas Devlieghere        type="int",
334*2238dcc3SJonas Devlieghere        dest="mtime",
335de01668bSGreg Clayton        default=None,
336*2238dcc3SJonas Devlieghere        help=(
337*2238dcc3SJonas Devlieghere            "Specify the modification time of the object an object. This "
338*2238dcc3SJonas Devlieghere            "option is used with either the --object or --extract options."
339*2238dcc3SJonas Devlieghere        ),
340*2238dcc3SJonas Devlieghere    )
341de01668bSGreg Clayton    parser.add_option(
342*2238dcc3SJonas Devlieghere        "-o",
343*2238dcc3SJonas Devlieghere        "--outfile",
344*2238dcc3SJonas Devlieghere        type="string",
345*2238dcc3SJonas Devlieghere        dest="outfile",
346de01668bSGreg Clayton        default=None,
347*2238dcc3SJonas Devlieghere        help=(
348*2238dcc3SJonas Devlieghere            "Specify a different name or path for the file to extract when "
349*2238dcc3SJonas Devlieghere            "using the --extract option. If this option isn't specified, "
350*2238dcc3SJonas Devlieghere            "then the extracted object file will be extracted into the "
351*2238dcc3SJonas Devlieghere            "current working directory if a file doesn't already exist "
352*2238dcc3SJonas Devlieghere            "with that name."
353*2238dcc3SJonas Devlieghere        ),
354*2238dcc3SJonas Devlieghere    )
35595c23f66SGreg Clayton    parser.add_option(
356*2238dcc3SJonas Devlieghere        "-i",
357*2238dcc3SJonas Devlieghere        "--interactive",
358*2238dcc3SJonas Devlieghere        action="store_true",
359*2238dcc3SJonas Devlieghere        dest="interactive",
36095c23f66SGreg Clayton        default=False,
361*2238dcc3SJonas Devlieghere        help=(
362*2238dcc3SJonas Devlieghere            "Enter an interactive shell that allows users to interactively "
363*2238dcc3SJonas Devlieghere            "explore contents of .a files."
364*2238dcc3SJonas Devlieghere        ),
365*2238dcc3SJonas Devlieghere    )
366de01668bSGreg Clayton
367de01668bSGreg Clayton    (options, args) = parser.parse_args(sys.argv[1:])
368de01668bSGreg Clayton
36995c23f66SGreg Clayton    if options.interactive:
37095c23f66SGreg Clayton        archives = []
37195c23f66SGreg Clayton        for path in args:
37295c23f66SGreg Clayton            archives.append(Archive(path))
37395c23f66SGreg Clayton        interpreter = Interactive(archives)
37495c23f66SGreg Clayton        interpreter.cmdloop()
37595c23f66SGreg Clayton        return
37695c23f66SGreg Clayton
377de01668bSGreg Clayton    for path in args:
378de01668bSGreg Clayton        archive = Archive(path)
379de01668bSGreg Clayton        if options.object_name:
380*2238dcc3SJonas Devlieghere            print("%s:\n" % (path))
381de01668bSGreg Clayton            matches = archive.find(options.object_name, options.mtime)
382de01668bSGreg Clayton            if matches:
383de01668bSGreg Clayton                dump_all = True
384de01668bSGreg Clayton                if options.extract:
385de01668bSGreg Clayton                    if len(matches) == 1:
386de01668bSGreg Clayton                        dump_all = False
38795c23f66SGreg Clayton                        matches[0].save(path=options.outfile, overwrite=False)
388de01668bSGreg Clayton                    else:
389*2238dcc3SJonas Devlieghere                        print(
390*2238dcc3SJonas Devlieghere                            'error: multiple objects match "%s". Specify '
391*2238dcc3SJonas Devlieghere                            "the modification time using --mtime."
392*2238dcc3SJonas Devlieghere                            % (options.object_name)
393*2238dcc3SJonas Devlieghere                        )
394de01668bSGreg Clayton                if dump_all:
395de01668bSGreg Clayton                    for obj in matches:
396de01668bSGreg Clayton                        obj.dump(flat=False)
397de01668bSGreg Clayton            else:
398*2238dcc3SJonas Devlieghere                print('error: object "%s" not found in archive' % (options.object_name))
399de01668bSGreg Clayton        elif options.find_symbol:
400de01668bSGreg Clayton            symdefs = archive.get_symdef()
401de01668bSGreg Clayton            if symdefs:
402de01668bSGreg Clayton                success = False
403*2238dcc3SJonas Devlieghere                for name, offset in symdefs:
404de01668bSGreg Clayton                    obj = archive.get_object_at_offset(offset)
405de01668bSGreg Clayton                    if name == options.find_symbol:
406de01668bSGreg Clayton                        print('Found "%s" in:' % (options.find_symbol))
407de01668bSGreg Clayton                        obj.dump(flat=False)
408de01668bSGreg Clayton                        success = True
409de01668bSGreg Clayton                if not success:
410*2238dcc3SJonas Devlieghere                    print('Didn\'t find "%s" in any objects' % (options.find_symbol))
411de01668bSGreg Clayton            else:
412de01668bSGreg Clayton                print("error: no __.SYMDEF was found")
413de01668bSGreg Clayton        elif options.symdef:
414de01668bSGreg Clayton            object_dicts = archive.get_object_dicts()
415de01668bSGreg Clayton            for object_dict in object_dicts:
416*2238dcc3SJonas Devlieghere                object_dict["object"].dump(flat=False)
417de01668bSGreg Clayton                print("symbols:")
418*2238dcc3SJonas Devlieghere                for name in object_dict["symdefs"]:
419de01668bSGreg Clayton                    print("  %s" % (name))
420de01668bSGreg Clayton        else:
421de01668bSGreg Clayton            archive.dump(flat=not options.verbose)
422de01668bSGreg Clayton
423de01668bSGreg Clayton
424*2238dcc3SJonas Devlieghereif __name__ == "__main__":
425de01668bSGreg Clayton    main()
426de01668bSGreg Clayton
427de01668bSGreg Clayton
428de01668bSGreg Claytondef print_mtime_error(result, dmap_mtime, actual_mtime):
429*2238dcc3SJonas Devlieghere    print(
430*2238dcc3SJonas Devlieghere        "error: modification time in debug map (%#08.8x) doesn't "
431*2238dcc3SJonas Devlieghere        "match the .o file modification time (%#08.8x)" % (dmap_mtime, actual_mtime),
432*2238dcc3SJonas Devlieghere        file=result,
433*2238dcc3SJonas Devlieghere    )
434de01668bSGreg Clayton
435de01668bSGreg Clayton
436de01668bSGreg Claytondef print_file_missing_error(result, path):
437*2238dcc3SJonas Devlieghere    print('error: file "%s" doesn\'t exist' % (path), file=result)
438de01668bSGreg Clayton
439de01668bSGreg Clayton
440de01668bSGreg Claytondef print_multiple_object_matches(result, object_name, mtime, matches):
441*2238dcc3SJonas Devlieghere    print(
442*2238dcc3SJonas Devlieghere        "error: multiple matches for object '%s' with with "
443*2238dcc3SJonas Devlieghere        "modification time %#08.8x:" % (object_name, mtime),
444*2238dcc3SJonas Devlieghere        file=result,
445*2238dcc3SJonas Devlieghere    )
446de01668bSGreg Clayton    Archive.dump_header(f=result)
447de01668bSGreg Clayton    for match in matches:
448de01668bSGreg Clayton        match.dump(f=result, flat=True)
449de01668bSGreg Clayton
450de01668bSGreg Clayton
451de01668bSGreg Claytondef print_archive_object_error(result, object_name, mtime, archive):
452de01668bSGreg Clayton    matches = archive.find(object_name, f=result)
453de01668bSGreg Clayton    if len(matches) > 0:
454*2238dcc3SJonas Devlieghere        print(
455*2238dcc3SJonas Devlieghere            "error: no objects have a modification time that "
456*2238dcc3SJonas Devlieghere            "matches %#08.8x for '%s'. Potential matches:" % (mtime, object_name),
457*2238dcc3SJonas Devlieghere            file=result,
458*2238dcc3SJonas Devlieghere        )
459de01668bSGreg Clayton        Archive.dump_header(f=result)
460de01668bSGreg Clayton        for match in matches:
461de01668bSGreg Clayton            match.dump(f=result, flat=True)
462de01668bSGreg Clayton    else:
463*2238dcc3SJonas Devlieghere        print(
464*2238dcc3SJonas Devlieghere            'error: no object named "%s" found in archive:' % (object_name), file=result
465*2238dcc3SJonas Devlieghere        )
466de01668bSGreg Clayton        Archive.dump_header(f=result)
467de01668bSGreg Clayton        for match in archive.objects:
468de01668bSGreg Clayton            match.dump(f=result, flat=True)
469de01668bSGreg Clayton        # archive.dump(f=result, flat=True)
470de01668bSGreg Clayton
471de01668bSGreg Clayton
472de01668bSGreg Claytonclass VerifyDebugMapCommand:
473de01668bSGreg Clayton    name = "verify-debug-map-objects"
474de01668bSGreg Clayton
475de01668bSGreg Clayton    def create_options(self):
476de01668bSGreg Clayton        usage = "usage: %prog [options]"
477*2238dcc3SJonas Devlieghere        description = """This command reports any .o files that are missing
478*2238dcc3SJonas Devlieghereor whose modification times don't match in the debug map of an executable."""
479de01668bSGreg Clayton
480de01668bSGreg Clayton        self.parser = optparse.OptionParser(
481*2238dcc3SJonas Devlieghere            description=description, prog=self.name, usage=usage, add_help_option=False
482*2238dcc3SJonas Devlieghere        )
483de01668bSGreg Clayton
484de01668bSGreg Clayton        self.parser.add_option(
485*2238dcc3SJonas Devlieghere            "-e",
486*2238dcc3SJonas Devlieghere            "--errors",
487*2238dcc3SJonas Devlieghere            action="store_true",
488*2238dcc3SJonas Devlieghere            dest="errors",
489de01668bSGreg Clayton            default=False,
490*2238dcc3SJonas Devlieghere            help="Only show errors",
491*2238dcc3SJonas Devlieghere        )
492de01668bSGreg Clayton
493de01668bSGreg Clayton    def get_short_help(self):
494de01668bSGreg Clayton        return "Verify debug map object files."
495de01668bSGreg Clayton
496de01668bSGreg Clayton    def get_long_help(self):
497de01668bSGreg Clayton        return self.help_string
498de01668bSGreg Clayton
499de01668bSGreg Clayton    def __init__(self, debugger, unused):
500de01668bSGreg Clayton        self.create_options()
501de01668bSGreg Clayton        self.help_string = self.parser.format_help()
502de01668bSGreg Clayton
503de01668bSGreg Clayton    def __call__(self, debugger, command, exe_ctx, result):
504de01668bSGreg Clayton        import lldb
505*2238dcc3SJonas Devlieghere
506de01668bSGreg Clayton        # Use the Shell Lexer to properly parse up command options just like a
507de01668bSGreg Clayton        # shell would
508de01668bSGreg Clayton        command_args = shlex.split(command)
509de01668bSGreg Clayton
510de01668bSGreg Clayton        try:
511de01668bSGreg Clayton            (options, args) = self.parser.parse_args(command_args)
512de01668bSGreg Clayton        except:
513de01668bSGreg Clayton            result.SetError("option parsing failed")
514de01668bSGreg Clayton            return
515de01668bSGreg Clayton
516de01668bSGreg Clayton        # Always get program state from the SBExecutionContext passed in
517de01668bSGreg Clayton        target = exe_ctx.GetTarget()
518de01668bSGreg Clayton        if not target.IsValid():
519de01668bSGreg Clayton            result.SetError("invalid target")
520de01668bSGreg Clayton            return
521de01668bSGreg Clayton        archives = {}
522de01668bSGreg Clayton        for module_spec in args:
523de01668bSGreg Clayton            module = target.module[module_spec]
524de01668bSGreg Clayton            if not (module and module.IsValid()):
525*2238dcc3SJonas Devlieghere                result.SetError(
526*2238dcc3SJonas Devlieghere                    'error: invalid module specification: "%s". '
527*2238dcc3SJonas Devlieghere                    "Specify the full path, basename, or UUID of "
528*2238dcc3SJonas Devlieghere                    "a module " % (module_spec)
529*2238dcc3SJonas Devlieghere                )
530de01668bSGreg Clayton                return
531de01668bSGreg Clayton            num_symbols = module.GetNumSymbols()
532de01668bSGreg Clayton            num_errors = 0
533de01668bSGreg Clayton            for i in range(num_symbols):
534de01668bSGreg Clayton                symbol = module.GetSymbolAtIndex(i)
535de01668bSGreg Clayton                if symbol.GetType() != lldb.eSymbolTypeObjectFile:
536de01668bSGreg Clayton                    continue
537de01668bSGreg Clayton                path = symbol.GetName()
538de01668bSGreg Clayton                if not path:
539de01668bSGreg Clayton                    continue
540de01668bSGreg Clayton                # Extract the value of the symbol by dumping the
541de01668bSGreg Clayton                # symbol. The value is the mod time.
542*2238dcc3SJonas Devlieghere                dmap_mtime = int(str(symbol).split("value = ")[1].split(",")[0], 16)
543de01668bSGreg Clayton                if not options.errors:
544*2238dcc3SJonas Devlieghere                    print("%s" % (path), file=result)
545de01668bSGreg Clayton                if os.path.exists(path):
546de01668bSGreg Clayton                    actual_mtime = int(os.stat(path).st_mtime)
547de01668bSGreg Clayton                    if dmap_mtime != actual_mtime:
548de01668bSGreg Clayton                        num_errors += 1
549de01668bSGreg Clayton                        if options.errors:
550*2238dcc3SJonas Devlieghere                            print("%s" % (path), end=" ", file=result)
551*2238dcc3SJonas Devlieghere                        print_mtime_error(result, dmap_mtime, actual_mtime)
552*2238dcc3SJonas Devlieghere                elif path[-1] == ")":
553*2238dcc3SJonas Devlieghere                    (archive_path, object_name) = path[0:-1].split("(")
554de01668bSGreg Clayton                    if not archive_path and not object_name:
555de01668bSGreg Clayton                        num_errors += 1
556de01668bSGreg Clayton                        if options.errors:
557*2238dcc3SJonas Devlieghere                            print("%s" % (path), end=" ", file=result)
558de01668bSGreg Clayton                        print_file_missing_error(path)
559de01668bSGreg Clayton                        continue
560de01668bSGreg Clayton                    if not os.path.exists(archive_path):
561de01668bSGreg Clayton                        num_errors += 1
562de01668bSGreg Clayton                        if options.errors:
563*2238dcc3SJonas Devlieghere                            print("%s" % (path), end=" ", file=result)
564de01668bSGreg Clayton                        print_file_missing_error(archive_path)
565de01668bSGreg Clayton                        continue
566de01668bSGreg Clayton                    if archive_path in archives:
567de01668bSGreg Clayton                        archive = archives[archive_path]
568de01668bSGreg Clayton                    else:
569de01668bSGreg Clayton                        archive = Archive(archive_path)
570de01668bSGreg Clayton                        archives[archive_path] = archive
571de01668bSGreg Clayton                    matches = archive.find(object_name, dmap_mtime)
572de01668bSGreg Clayton                    num_matches = len(matches)
573de01668bSGreg Clayton                    if num_matches == 1:
574*2238dcc3SJonas Devlieghere                        print("1 match", file=result)
575de01668bSGreg Clayton                        obj = matches[0]
576de01668bSGreg Clayton                        if obj.date != dmap_mtime:
577de01668bSGreg Clayton                            num_errors += 1
578de01668bSGreg Clayton                            if options.errors:
579*2238dcc3SJonas Devlieghere                                print("%s" % (path), end=" ", file=result)
580de01668bSGreg Clayton                            print_mtime_error(result, dmap_mtime, obj.date)
581de01668bSGreg Clayton                    elif num_matches == 0:
582de01668bSGreg Clayton                        num_errors += 1
583de01668bSGreg Clayton                        if options.errors:
584*2238dcc3SJonas Devlieghere                            print("%s" % (path), end=" ", file=result)
585*2238dcc3SJonas Devlieghere                        print_archive_object_error(
586*2238dcc3SJonas Devlieghere                            result, object_name, dmap_mtime, archive
587*2238dcc3SJonas Devlieghere                        )
588de01668bSGreg Clayton                    elif num_matches > 1:
589de01668bSGreg Clayton                        num_errors += 1
590de01668bSGreg Clayton                        if options.errors:
591*2238dcc3SJonas Devlieghere                            print("%s" % (path), end=" ", file=result)
592*2238dcc3SJonas Devlieghere                        print_multiple_object_matches(
593*2238dcc3SJonas Devlieghere                            result, object_name, dmap_mtime, matches
594*2238dcc3SJonas Devlieghere                        )
595de01668bSGreg Clayton            if num_errors > 0:
596525cd59fSSerge Guelton                print("%u errors found" % (num_errors), file=result)
597de01668bSGreg Clayton            else:
598525cd59fSSerge Guelton                print("No errors detected in debug map", file=result)
599de01668bSGreg Clayton
600de01668bSGreg Clayton
601de01668bSGreg Claytondef __lldb_init_module(debugger, dict):
602de01668bSGreg Clayton    # This initializer is being run from LLDB in the embedded command
603de01668bSGreg Clayton    # interpreter.
604de01668bSGreg Clayton    # Add any commands contained in this module to LLDB
605de01668bSGreg Clayton    debugger.HandleCommand(
606*2238dcc3SJonas Devlieghere        "command script add -o -c %s.VerifyDebugMapCommand %s"
607*2238dcc3SJonas Devlieghere        % (__name__, VerifyDebugMapCommand.name)
608*2238dcc3SJonas Devlieghere    )
609*2238dcc3SJonas Devlieghere    print(
610*2238dcc3SJonas Devlieghere        'The "%s" command has been installed, type "help %s" for detailed '
611*2238dcc3SJonas Devlieghere        "help." % (VerifyDebugMapCommand.name, VerifyDebugMapCommand.name)
612*2238dcc3SJonas Devlieghere    )
613