1515bc8c1Sserge-sans-paille#!/usr/bin/env python
204bd6840SGreg Clayton
304bd6840SGreg Clayton#----------------------------------------------------------------------
404bd6840SGreg Clayton# Be sure to add the python path that points to the LLDB shared library.
504bd6840SGreg Clayton#
604bd6840SGreg Clayton# # To use this in the embedded python interpreter using "lldb" just
704bd6840SGreg Clayton# import it with the full path using the "command script import"
804bd6840SGreg Clayton# command
904bd6840SGreg Clayton#   (lldb) command script import /path/to/cmdtemplate.py
1004bd6840SGreg Clayton#----------------------------------------------------------------------
1104bd6840SGreg Clayton
12525cd59fSSerge Gueltonfrom __future__ import print_function
13525cd59fSSerge Guelton
1404bd6840SGreg Claytonimport platform
1504bd6840SGreg Claytonimport os
1604bd6840SGreg Claytonimport re
1704bd6840SGreg Claytonimport sys
181a12dd70SSerge Gueltonimport subprocess
191a12dd70SSerge Guelton
2004bd6840SGreg Claytontry:
2104bd6840SGreg Clayton    # Just try for LLDB in case PYTHONPATH is already correctly setup
2204bd6840SGreg Clayton    import lldb
2304bd6840SGreg Claytonexcept ImportError:
2404bd6840SGreg Clayton    lldb_python_dirs = list()
2504bd6840SGreg Clayton    # lldb is not in the PYTHONPATH, try some defaults for the current platform
2604bd6840SGreg Clayton    platform_system = platform.system()
2704bd6840SGreg Clayton    if platform_system == 'Darwin':
2804bd6840SGreg Clayton        # On Darwin, try the currently selected Xcode directory
29185de8eeSDavide Italiano        xcode_dir = subprocess.check_output("xcode-select --print-path", shell=True)
3004bd6840SGreg Clayton        if xcode_dir:
31b9c1b51eSKate Stone            lldb_python_dirs.append(
32b9c1b51eSKate Stone                os.path.realpath(
33b9c1b51eSKate Stone                    xcode_dir +
34b9c1b51eSKate Stone                    '/../SharedFrameworks/LLDB.framework/Resources/Python'))
35b9c1b51eSKate Stone            lldb_python_dirs.append(
36b9c1b51eSKate Stone                xcode_dir + '/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
37b9c1b51eSKate Stone        lldb_python_dirs.append(
38b9c1b51eSKate Stone            '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
3904bd6840SGreg Clayton    success = False
4004bd6840SGreg Clayton    for lldb_python_dir in lldb_python_dirs:
4104bd6840SGreg Clayton        if os.path.exists(lldb_python_dir):
4204bd6840SGreg Clayton            if not (sys.path.__contains__(lldb_python_dir)):
4304bd6840SGreg Clayton                sys.path.append(lldb_python_dir)
4404bd6840SGreg Clayton                try:
4504bd6840SGreg Clayton                    import lldb
4604bd6840SGreg Clayton                except ImportError:
4704bd6840SGreg Clayton                    pass
4804bd6840SGreg Clayton                else:
49525cd59fSSerge Guelton                    print('imported lldb from: "%s"' % (lldb_python_dir))
5004bd6840SGreg Clayton                    success = True
5104bd6840SGreg Clayton                    break
5204bd6840SGreg Clayton    if not success:
53525cd59fSSerge Guelton        print("error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly")
5404bd6840SGreg Clayton        sys.exit(1)
5504bd6840SGreg Clayton
5604bd6840SGreg Claytonimport optparse
5704bd6840SGreg Claytonimport shlex
5804bd6840SGreg Claytonimport string
5904bd6840SGreg Claytonimport struct
6004bd6840SGreg Claytonimport time
6104bd6840SGreg Clayton
62b9c1b51eSKate Stone
6304bd6840SGreg Claytondef append_data_callback(option, opt_str, value, parser):
6404bd6840SGreg Clayton    if opt_str == "--uint8":
6504bd6840SGreg Clayton        int8 = int(value, 0)
6604bd6840SGreg Clayton        parser.values.data += struct.pack('1B', int8)
6704bd6840SGreg Clayton    if opt_str == "--uint16":
6804bd6840SGreg Clayton        int16 = int(value, 0)
6904bd6840SGreg Clayton        parser.values.data += struct.pack('1H', int16)
7004bd6840SGreg Clayton    if opt_str == "--uint32":
7104bd6840SGreg Clayton        int32 = int(value, 0)
7204bd6840SGreg Clayton        parser.values.data += struct.pack('1I', int32)
7304bd6840SGreg Clayton    if opt_str == "--uint64":
7404bd6840SGreg Clayton        int64 = int(value, 0)
7504bd6840SGreg Clayton        parser.values.data += struct.pack('1Q', int64)
7604bd6840SGreg Clayton    if opt_str == "--int8":
7704bd6840SGreg Clayton        int8 = int(value, 0)
7804bd6840SGreg Clayton        parser.values.data += struct.pack('1b', int8)
7904bd6840SGreg Clayton    if opt_str == "--int16":
8004bd6840SGreg Clayton        int16 = int(value, 0)
8104bd6840SGreg Clayton        parser.values.data += struct.pack('1h', int16)
8204bd6840SGreg Clayton    if opt_str == "--int32":
8304bd6840SGreg Clayton        int32 = int(value, 0)
8404bd6840SGreg Clayton        parser.values.data += struct.pack('1i', int32)
8504bd6840SGreg Clayton    if opt_str == "--int64":
8604bd6840SGreg Clayton        int64 = int(value, 0)
8704bd6840SGreg Clayton        parser.values.data += struct.pack('1q', int64)
8804bd6840SGreg Clayton
89b9c1b51eSKate Stone
9004bd6840SGreg Claytondef create_memfind_options():
9104bd6840SGreg Clayton    usage = "usage: %prog [options] STARTADDR [ENDADDR]"
9204bd6840SGreg Clayton    description = '''This command can find data in a specified address range.
9304bd6840SGreg ClaytonOptions are used to specify the data that is to be looked for and the options
9404bd6840SGreg Claytoncan be specified multiple times to look for longer streams of data.
9504bd6840SGreg Clayton'''
96b9c1b51eSKate Stone    parser = optparse.OptionParser(
97b9c1b51eSKate Stone        description=description,
98b9c1b51eSKate Stone        prog='memfind',
99b9c1b51eSKate Stone        usage=usage)
100b9c1b51eSKate Stone    parser.add_option(
101b9c1b51eSKate Stone        '-s',
102b9c1b51eSKate Stone        '--size',
103b9c1b51eSKate Stone        type='int',
104b9c1b51eSKate Stone        metavar='BYTESIZE',
105b9c1b51eSKate Stone        dest='size',
106b9c1b51eSKate Stone        help='Specify the byte size to search.',
107b9c1b51eSKate Stone        default=0)
108b9c1b51eSKate Stone    parser.add_option(
109b9c1b51eSKate Stone        '--int8',
110b9c1b51eSKate Stone        action="callback",
111b9c1b51eSKate Stone        callback=append_data_callback,
112b9c1b51eSKate Stone        type='string',
113b9c1b51eSKate Stone        metavar='INT',
114b9c1b51eSKate Stone        dest='data',
115b9c1b51eSKate Stone        help='Specify a 8 bit signed integer value to search for in memory.',
116b9c1b51eSKate Stone        default='')
117b9c1b51eSKate Stone    parser.add_option(
118b9c1b51eSKate Stone        '--int16',
119b9c1b51eSKate Stone        action="callback",
120b9c1b51eSKate Stone        callback=append_data_callback,
121b9c1b51eSKate Stone        type='string',
122b9c1b51eSKate Stone        metavar='INT',
123b9c1b51eSKate Stone        dest='data',
124b9c1b51eSKate Stone        help='Specify a 16 bit signed integer value to search for in memory.',
125b9c1b51eSKate Stone        default='')
126b9c1b51eSKate Stone    parser.add_option(
127b9c1b51eSKate Stone        '--int32',
128b9c1b51eSKate Stone        action="callback",
129b9c1b51eSKate Stone        callback=append_data_callback,
130b9c1b51eSKate Stone        type='string',
131b9c1b51eSKate Stone        metavar='INT',
132b9c1b51eSKate Stone        dest='data',
133b9c1b51eSKate Stone        help='Specify a 32 bit signed integer value to search for in memory.',
134b9c1b51eSKate Stone        default='')
135b9c1b51eSKate Stone    parser.add_option(
136b9c1b51eSKate Stone        '--int64',
137b9c1b51eSKate Stone        action="callback",
138b9c1b51eSKate Stone        callback=append_data_callback,
139b9c1b51eSKate Stone        type='string',
140b9c1b51eSKate Stone        metavar='INT',
141b9c1b51eSKate Stone        dest='data',
142b9c1b51eSKate Stone        help='Specify a 64 bit signed integer value to search for in memory.',
143b9c1b51eSKate Stone        default='')
144b9c1b51eSKate Stone    parser.add_option(
145b9c1b51eSKate Stone        '--uint8',
146b9c1b51eSKate Stone        action="callback",
147b9c1b51eSKate Stone        callback=append_data_callback,
148b9c1b51eSKate Stone        type='string',
149b9c1b51eSKate Stone        metavar='INT',
150b9c1b51eSKate Stone        dest='data',
151b9c1b51eSKate Stone        help='Specify a 8 bit unsigned integer value to search for in memory.',
152b9c1b51eSKate Stone        default='')
153b9c1b51eSKate Stone    parser.add_option(
154b9c1b51eSKate Stone        '--uint16',
155b9c1b51eSKate Stone        action="callback",
156b9c1b51eSKate Stone        callback=append_data_callback,
157b9c1b51eSKate Stone        type='string',
158b9c1b51eSKate Stone        metavar='INT',
159b9c1b51eSKate Stone        dest='data',
160b9c1b51eSKate Stone        help='Specify a 16 bit unsigned integer value to search for in memory.',
161b9c1b51eSKate Stone        default='')
162b9c1b51eSKate Stone    parser.add_option(
163b9c1b51eSKate Stone        '--uint32',
164b9c1b51eSKate Stone        action="callback",
165b9c1b51eSKate Stone        callback=append_data_callback,
166b9c1b51eSKate Stone        type='string',
167b9c1b51eSKate Stone        metavar='INT',
168b9c1b51eSKate Stone        dest='data',
169b9c1b51eSKate Stone        help='Specify a 32 bit unsigned integer value to search for in memory.',
170b9c1b51eSKate Stone        default='')
171b9c1b51eSKate Stone    parser.add_option(
172b9c1b51eSKate Stone        '--uint64',
173b9c1b51eSKate Stone        action="callback",
174b9c1b51eSKate Stone        callback=append_data_callback,
175b9c1b51eSKate Stone        type='string',
176b9c1b51eSKate Stone        metavar='INT',
177b9c1b51eSKate Stone        dest='data',
178b9c1b51eSKate Stone        help='Specify a 64 bit unsigned integer value to search for in memory.',
179b9c1b51eSKate Stone        default='')
18004bd6840SGreg Clayton    return parser
18104bd6840SGreg Clayton
182b9c1b51eSKate Stone
18304bd6840SGreg Claytondef memfind_command(debugger, command, result, dict):
18404bd6840SGreg Clayton    # Use the Shell Lexer to properly parse up command options just like a
18504bd6840SGreg Clayton    # shell would
18604bd6840SGreg Clayton    command_args = shlex.split(command)
18704bd6840SGreg Clayton    parser = create_memfind_options()
18804bd6840SGreg Clayton    (options, args) = parser.parse_args(command_args)
18904bd6840SGreg Clayton    # try:
19004bd6840SGreg Clayton    #     (options, args) = parser.parse_args(command_args)
19104bd6840SGreg Clayton    # except:
19204bd6840SGreg Clayton    #     # if you don't handle exceptions, passing an incorrect argument to the OptionParser will cause LLDB to exit
19304bd6840SGreg Clayton    #     # (courtesy of OptParse dealing with argument errors by throwing SystemExit)
19404bd6840SGreg Clayton    #     result.SetStatus (lldb.eReturnStatusFailed)
19504bd6840SGreg Clayton    #     print >>result, "error: option parsing failed" # returning a string is the same as returning an error whose description is the string
19604bd6840SGreg Clayton    #     return
19704bd6840SGreg Clayton    memfind(debugger.GetSelectedTarget(), options, args, result)
19804bd6840SGreg Clayton
199b9c1b51eSKate Stone
20004bd6840SGreg Claytondef print_error(str, show_usage, result):
201525cd59fSSerge Guelton    print(str, file=result)
20204bd6840SGreg Clayton    if show_usage:
203525cd59fSSerge Guelton        print(create_memfind_options().format_help(), file=result)
20404bd6840SGreg Clayton
205b9c1b51eSKate Stone
20604bd6840SGreg Claytondef memfind(target, options, args, result):
20704bd6840SGreg Clayton    num_args = len(args)
20804bd6840SGreg Clayton    start_addr = 0
20904bd6840SGreg Clayton    if num_args == 1:
21004bd6840SGreg Clayton        if options.size > 0:
211b9c1b51eSKate Stone            print_error(
212b9c1b51eSKate Stone                "error: --size must be specified if there is no ENDADDR argument",
213b9c1b51eSKate Stone                True,
214b9c1b51eSKate Stone                result)
21504bd6840SGreg Clayton            return
21604bd6840SGreg Clayton        start_addr = int(args[0], 0)
21704bd6840SGreg Clayton    elif num_args == 2:
21804bd6840SGreg Clayton        if options.size != 0:
219b9c1b51eSKate Stone            print_error(
220b9c1b51eSKate Stone                "error: --size can't be specified with an ENDADDR argument",
221b9c1b51eSKate Stone                True,
222b9c1b51eSKate Stone                result)
22304bd6840SGreg Clayton            return
22404bd6840SGreg Clayton        start_addr = int(args[0], 0)
22504bd6840SGreg Clayton        end_addr = int(args[1], 0)
22604bd6840SGreg Clayton        if start_addr >= end_addr:
227b9c1b51eSKate Stone            print_error(
228b9c1b51eSKate Stone                "error: inavlid memory range [%#x - %#x)" %
229b9c1b51eSKate Stone                (start_addr, end_addr), True, result)
23004bd6840SGreg Clayton            return
23104bd6840SGreg Clayton        options.size = end_addr - start_addr
23204bd6840SGreg Clayton    else:
23304bd6840SGreg Clayton        print_error("error: memfind takes 1 or 2 arguments", True, result)
23404bd6840SGreg Clayton        return
23504bd6840SGreg Clayton
23604bd6840SGreg Clayton    if not options.data:
237525cd59fSSerge Guelton        print('error: no data specified to search for', file=result)
23804bd6840SGreg Clayton        return
23904bd6840SGreg Clayton
24004bd6840SGreg Clayton    if not target:
241525cd59fSSerge Guelton        print('error: invalid target', file=result)
24204bd6840SGreg Clayton        return
24304bd6840SGreg Clayton    process = target.process
24404bd6840SGreg Clayton    if not process:
245525cd59fSSerge Guelton        print('error: invalid process', file=result)
24604bd6840SGreg Clayton        return
24704bd6840SGreg Clayton
24804bd6840SGreg Clayton    error = lldb.SBError()
24904bd6840SGreg Clayton    bytes = process.ReadMemory(start_addr, options.size, error)
25004bd6840SGreg Clayton    if error.Success():
25104bd6840SGreg Clayton        num_matches = 0
252525cd59fSSerge Guelton        print("Searching memory range [%#x - %#x) for" % (
253525cd59fSSerge Guelton            start_addr, end_addr), end=' ', file=result)
25404bd6840SGreg Clayton        for byte in options.data:
255525cd59fSSerge Guelton            print('%2.2x' % ord(byte), end=' ', file=result)
256525cd59fSSerge Guelton        print(file=result)
25704bd6840SGreg Clayton
25804bd6840SGreg Clayton        match_index = string.find(bytes, options.data)
25904bd6840SGreg Clayton        while match_index != -1:
26004bd6840SGreg Clayton            num_matches = num_matches + 1
261525cd59fSSerge Guelton            print('%#x: %#x + %u' % (start_addr +
262525cd59fSSerge Guelton                                               match_index, start_addr, match_index), file=result)
26304bd6840SGreg Clayton            match_index = string.find(bytes, options.data, match_index + 1)
26404bd6840SGreg Clayton
26504bd6840SGreg Clayton        if num_matches == 0:
266525cd59fSSerge Guelton            print("error: no matches found", file=result)
26704bd6840SGreg Clayton    else:
268525cd59fSSerge Guelton        print('error: %s' % (error.GetCString()), file=result)
26904bd6840SGreg Clayton
27004bd6840SGreg Clayton
27104bd6840SGreg Claytonif __name__ == '__main__':
272525cd59fSSerge Guelton    print('error: this script is designed to be used within the embedded script interpreter in LLDB')
273*1441ffe6SDave Lee
274*1441ffe6SDave Leedef __lldb_init_module(debugger, internal_dict):
27504bd6840SGreg Clayton    memfind_command.__doc__ = create_memfind_options().format_help()
276*1441ffe6SDave Lee    debugger.HandleCommand(
277b9c1b51eSKate Stone        'command script add -f memory.memfind_command memfind')
278525cd59fSSerge Guelton    print('"memfind" command installed, use the "--help" option for detailed help')
279