xref: /llvm-project/lldb/examples/python/memory.py (revision 2238dcc39358353cac21df75c3c3286ab20b8f53)
1#!/usr/bin/env python
2
3# ----------------------------------------------------------------------
4# Be sure to add the python path that points to the LLDB shared library.
5#
6# # To use this in the embedded python interpreter using "lldb" just
7# import it with the full path using the "command script import"
8# command
9#   (lldb) command script import /path/to/cmdtemplate.py
10# ----------------------------------------------------------------------
11
12import platform
13import os
14import re
15import sys
16import subprocess
17
18try:
19    # Just try for LLDB in case PYTHONPATH is already correctly setup
20    import lldb
21except ImportError:
22    lldb_python_dirs = list()
23    # lldb is not in the PYTHONPATH, try some defaults for the current platform
24    platform_system = platform.system()
25    if platform_system == "Darwin":
26        # On Darwin, try the currently selected Xcode directory
27        xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True)
28        if xcode_dir:
29            lldb_python_dirs.append(
30                os.path.realpath(
31                    xcode_dir + "/../SharedFrameworks/LLDB.framework/Resources/Python"
32                )
33            )
34            lldb_python_dirs.append(
35                xcode_dir + "/Library/PrivateFrameworks/LLDB.framework/Resources/Python"
36            )
37        lldb_python_dirs.append(
38            "/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python"
39        )
40    success = False
41    for lldb_python_dir in lldb_python_dirs:
42        if os.path.exists(lldb_python_dir):
43            if not (sys.path.__contains__(lldb_python_dir)):
44                sys.path.append(lldb_python_dir)
45                try:
46                    import lldb
47                except ImportError:
48                    pass
49                else:
50                    print('imported lldb from: "%s"' % (lldb_python_dir))
51                    success = True
52                    break
53    if not success:
54        print(
55            "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
56        )
57        sys.exit(1)
58
59import optparse
60import shlex
61import string
62import struct
63import time
64
65
66def append_data_callback(option, opt_str, value, parser):
67    if opt_str == "--uint8":
68        int8 = int(value, 0)
69        parser.values.data += struct.pack("1B", int8)
70    if opt_str == "--uint16":
71        int16 = int(value, 0)
72        parser.values.data += struct.pack("1H", int16)
73    if opt_str == "--uint32":
74        int32 = int(value, 0)
75        parser.values.data += struct.pack("1I", int32)
76    if opt_str == "--uint64":
77        int64 = int(value, 0)
78        parser.values.data += struct.pack("1Q", int64)
79    if opt_str == "--int8":
80        int8 = int(value, 0)
81        parser.values.data += struct.pack("1b", int8)
82    if opt_str == "--int16":
83        int16 = int(value, 0)
84        parser.values.data += struct.pack("1h", int16)
85    if opt_str == "--int32":
86        int32 = int(value, 0)
87        parser.values.data += struct.pack("1i", int32)
88    if opt_str == "--int64":
89        int64 = int(value, 0)
90        parser.values.data += struct.pack("1q", int64)
91
92
93def create_memfind_options():
94    usage = "usage: %prog [options] STARTADDR [ENDADDR]"
95    description = """This command can find data in a specified address range.
96Options are used to specify the data that is to be looked for and the options
97can be specified multiple times to look for longer streams of data.
98"""
99    parser = optparse.OptionParser(description=description, prog="memfind", usage=usage)
100    parser.add_option(
101        "-s",
102        "--size",
103        type="int",
104        metavar="BYTESIZE",
105        dest="size",
106        help="Specify the byte size to search.",
107        default=0,
108    )
109    parser.add_option(
110        "--int8",
111        action="callback",
112        callback=append_data_callback,
113        type="string",
114        metavar="INT",
115        dest="data",
116        help="Specify a 8 bit signed integer value to search for in memory.",
117        default="",
118    )
119    parser.add_option(
120        "--int16",
121        action="callback",
122        callback=append_data_callback,
123        type="string",
124        metavar="INT",
125        dest="data",
126        help="Specify a 16 bit signed integer value to search for in memory.",
127        default="",
128    )
129    parser.add_option(
130        "--int32",
131        action="callback",
132        callback=append_data_callback,
133        type="string",
134        metavar="INT",
135        dest="data",
136        help="Specify a 32 bit signed integer value to search for in memory.",
137        default="",
138    )
139    parser.add_option(
140        "--int64",
141        action="callback",
142        callback=append_data_callback,
143        type="string",
144        metavar="INT",
145        dest="data",
146        help="Specify a 64 bit signed integer value to search for in memory.",
147        default="",
148    )
149    parser.add_option(
150        "--uint8",
151        action="callback",
152        callback=append_data_callback,
153        type="string",
154        metavar="INT",
155        dest="data",
156        help="Specify a 8 bit unsigned integer value to search for in memory.",
157        default="",
158    )
159    parser.add_option(
160        "--uint16",
161        action="callback",
162        callback=append_data_callback,
163        type="string",
164        metavar="INT",
165        dest="data",
166        help="Specify a 16 bit unsigned integer value to search for in memory.",
167        default="",
168    )
169    parser.add_option(
170        "--uint32",
171        action="callback",
172        callback=append_data_callback,
173        type="string",
174        metavar="INT",
175        dest="data",
176        help="Specify a 32 bit unsigned integer value to search for in memory.",
177        default="",
178    )
179    parser.add_option(
180        "--uint64",
181        action="callback",
182        callback=append_data_callback,
183        type="string",
184        metavar="INT",
185        dest="data",
186        help="Specify a 64 bit unsigned integer value to search for in memory.",
187        default="",
188    )
189    return parser
190
191
192def memfind_command(debugger, command, result, dict):
193    # Use the Shell Lexer to properly parse up command options just like a
194    # shell would
195    command_args = shlex.split(command)
196    parser = create_memfind_options()
197    (options, args) = parser.parse_args(command_args)
198    # try:
199    #     (options, args) = parser.parse_args(command_args)
200    # except:
201    #     # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit
202    #     # (courtesy of OptParse dealing with argument errors by throwing SystemExit)
203    #     result.SetStatus (lldb.eReturnStatusFailed)
204    #     print >>result, "error: option parsing failed" # returning a string is the same as returning an error whose description is the string
205    #     return
206    memfind(debugger.GetSelectedTarget(), options, args, result)
207
208
209def print_error(str, show_usage, result):
210    print(str, file=result)
211    if show_usage:
212        print(create_memfind_options().format_help(), file=result)
213
214
215def memfind(target, options, args, result):
216    num_args = len(args)
217    start_addr = 0
218    if num_args == 1:
219        if options.size > 0:
220            print_error(
221                "error: --size must be specified if there is no ENDADDR argument",
222                True,
223                result,
224            )
225            return
226        start_addr = int(args[0], 0)
227    elif num_args == 2:
228        if options.size != 0:
229            print_error(
230                "error: --size can't be specified with an ENDADDR argument",
231                True,
232                result,
233            )
234            return
235        start_addr = int(args[0], 0)
236        end_addr = int(args[1], 0)
237        if start_addr >= end_addr:
238            print_error(
239                "error: inavlid memory range [%#x - %#x)" % (start_addr, end_addr),
240                True,
241                result,
242            )
243            return
244        options.size = end_addr - start_addr
245    else:
246        print_error("error: memfind takes 1 or 2 arguments", True, result)
247        return
248
249    if not options.data:
250        print("error: no data specified to search for", file=result)
251        return
252
253    if not target:
254        print("error: invalid target", file=result)
255        return
256    process = target.process
257    if not process:
258        print("error: invalid process", file=result)
259        return
260
261    error = lldb.SBError()
262    bytes = process.ReadMemory(start_addr, options.size, error)
263    if error.Success():
264        num_matches = 0
265        print(
266            "Searching memory range [%#x - %#x) for" % (start_addr, end_addr),
267            end=" ",
268            file=result,
269        )
270        for byte in options.data:
271            print("%2.2x" % ord(byte), end=" ", file=result)
272        print(file=result)
273
274        match_index = string.find(bytes, options.data)
275        while match_index != -1:
276            num_matches = num_matches + 1
277            print(
278                "%#x: %#x + %u" % (start_addr + match_index, start_addr, match_index),
279                file=result,
280            )
281            match_index = string.find(bytes, options.data, match_index + 1)
282
283        if num_matches == 0:
284            print("error: no matches found", file=result)
285    else:
286        print("error: %s" % (error.GetCString()), file=result)
287
288
289if __name__ == "__main__":
290    print(
291        "error: this script is designed to be used within the embedded script interpreter in LLDB"
292    )
293
294
295def __lldb_init_module(debugger, internal_dict):
296    memfind_command.__doc__ = create_memfind_options().format_help()
297    debugger.HandleCommand("command script add -o -f memory.memfind_command memfind")
298    print('"memfind" command installed, use the "--help" option for detailed help')
299