Loading...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
"""
Defines a class value which encapsulates the basic lldb Scripting Bridge APIs. This provides an easy
wrapper to extract information from C based constructs.

 |------- core.value------------|
 | |--lldb Scripting Bridge--|  |
 | |    |--lldb core--|      |  |
 | |-------------------------|  |
 |------------------------------|

Use the member function GetSBValue() to access the base Scripting Bridge value.
"""

# The value class is designed to be Python 2/3 compatible. Pulling in more
# builtins classes may break it.
import numbers

import lldb
import re
from .caching import (
    cache_statically,
)
from .pointer import PointerPolicy

_CSTRING_REX = re.compile(r"((?:\s*|const\s+)\s*char(?:\s+\*|\s+[A-Za-z_0-9]*\s*\[|)\s*)", re.MULTILINE | re.DOTALL)


# pragma pylint: disable=hex-method, div-method, rdiv-method, idiv-method, oct-method, nonzero-method
class value(object):
    """A class designed to wrap lldb.SBValue() objects so the resulting object
    can be used as a variable would be in code. So if you have a Point structure
    variable in your code in the current frame named "pt", you can initialize an instance
    of this class with it:

    pt = lldb.value(lldb.frame.FindVariable("pt"))
    print pt
    print pt.x
    print pt.y

    pt = lldb.value(lldb.frame.FindVariable("rectangle_array"))
    print rectangle_array[12]
    print rectangle_array[5].origin.x
    """

    __slots__ = ('__sbval', '__ptr')

    def __init__(self, sbvalue, usePtrPolicy=True):
        # Using a double `__` means this will be hidden from getattr()
        # and can't conflict with C/C++ type field names.
        self.__sbval = sbvalue
        self.__ptr = PointerPolicy.match(sbvalue) if usePtrPolicy else None

    @property
    def sbvalue(self):
        """backward compability for the old .sbvalue property"""
        return self.GetSBValue()

    @property
    def ptrpolicy(self):
        return self.__ptr

    @ptrpolicy.setter
    def ptrpolicy(self, policy):
        self.__ptr = policy

    def __bool__(self):
        return self.__sbval.__bool__() and self._GetValueAsUnsigned() != 0

    def __nonzero__(self):
        return self.__sbval.__nonzero__() and self._GetValueAsUnsigned() != 0

    def __repr__(self):
        return self.__sbval.__str__()

    #
    # Compare operators
    #

    def __eq__(self, other):
        self_val = self._GetValueAsUnsigned()
        if isinstance(other, value):
            other_val = other._GetValueAsUnsigned()
            return self_val == other_val
        if isinstance(other, numbers.Integral):
            return int(self) == other
        raise TypeError("EQ operator is not defined for this type.")

    def __ne__(self, other):
        return not self == other

    def __lt__(self, other):
        self_val = self._GetValueAsUnsigned()
        if isinstance(other, value):
            other_val = other._GetValueAsUnsigned()
            return self_val < other_val
        if isinstance(other, numbers.Integral):
            return int(self) < int(other)
        raise TypeError("LT operator is not defined for this type")

    def __le__(self, other):
        return self < other or self == other

    def __gt__(self, other):
        return not self <= other

    def __ge__(self, other):
        return not self < other

    def __str__(self):
        global _CSTRING_REX
        sbv = self.__sbval
        type_name = sbv.GetType().GetCanonicalType().GetName()
        if len(_CSTRING_REX.findall(type_name)) > 0:
            return self._GetValueAsString()
        summary = sbv.GetSummary()
        if summary:
            return summary.strip('"')
        return sbv.__str__()

    def __getitem__(self, key):
        # Allow array access if this value has children...
        if type(key) is slice:
            _start = int(key.start)
            _end = int(key.stop)
            _step = 1
            if key.step is not None:
                _step = int(key.step)
            retval = []
            while _start < _end:
                retval.append(self[_start])
                _start += _step
            return retval
        if type(key) is value:
            key = int(key)
        if isinstance(key, numbers.Integral):
            sbv = self.__sbval
            if self.__ptr:
                sbv = self.__ptr.GetPointerSBValue(sbv)
            child_sbvalue = sbv.GetValueForExpressionPath("[%i]" % key)
            if child_sbvalue and child_sbvalue.IsValid():
                return value(child_sbvalue)
            raise IndexError("Index '%d' is out of range" % key)
        raise TypeError("Cannot fetch array item for key of type {}".format(str(type(key))))

    def __getattr__(self, name):
        sbv = self.__sbval
        if self.__ptr:
            sbv = self.__ptr.GetPointerSBValue(sbv)
        child_sbvalue = sbv.GetChildMemberWithName(name)
        if child_sbvalue and child_sbvalue.IsValid():
            return value(child_sbvalue)
        raise AttributeError("No field by name: " + name)

    def __add__(self, other):
        return int(self) + int(other)

    def __radd__(self, other):
        return int(self) + int(other)

    def __sub__(self, other):
        return int(self) - int(other)

    def __rsub__(self, other):
        return int(other) - int(self)

    def __mul__(self, other):
        return int(self) * int(other)

    def __rmul__(self, other):
        return int(self) * int(other)

    def __floordiv__(self, other):
        return int(self) // int(other)

    def __rfloordiv__(self, other):
        return int(other) // int(self)

    def __mod__(self, other):
        return int(self) % int(other)

    def __rmod__(self, other):
        return int(other) % int(self)

    def __divmod__(self, other):
        return divmod(int(self), int(other))

    def __rdivmod__(self, other):
        return divmod(int(other), int(self))

    def __pow__(self, other):
        return int(self) ** int(other)

    def __lshift__(self, other):
        return int(self) << int(other)

    def __rshift__(self, other):
        return int(self) >> int(other)

    def __and__(self, other):
        return int(self) & int(other)

    def __rand__(self, other):
        return int(other) & int(self)

    def __xor__(self, other):
        return int(self) ^ int(other)

    def __or__(self, other):
        return int(self) | int(other)

    def __truediv__(self, other):
        return int(self) / int(other)

    def __rtruediv__(self, other):
        return int(other) / int(self)

    def __iadd__(self, other):
        result = self.__add__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __isub__(self, other):
        result = self.__sub__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __imul__(self, other):
        result = self.__mul__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __idiv__(self, other):
        result = self.__div__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __itruediv__(self, other):
        result = self.__truediv__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __ifloordiv__(self, other):
        result = self.__floordiv__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __imod__(self, other):
        result = self.__mod__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __ipow__(self, other):
        result = self.__pow__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __ilshift__(self, other):
        result = self.__lshift__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __irshift__(self, other):
        result = self.__rshift__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __iand__(self, other):
        result = self.__and__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __ixor__(self, other):
        result = self.__xor__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __ior__(self, other):
        result = self.__or__(other)
        self.__sbval.SetValueFromCString(str(result))
        return result

    def __neg__(self):
        return -int(self)

    def __pos__(self):
        return +int(self)

    def __abs__(self):
        return abs(int(self))

    def __invert__(self):
        return ~int(self)

    def __complex__(self):
        return complex(int(self))

    def __int__(self):
        sbv = self.__sbval
        if self.__ptr:
            sbv = self.__ptr.GetPointerSBValue(sbv)

        flags = sbv.GetType().GetTypeFlags()
        if flags & lldb.eTypeIsPointer:
            return sbv.GetValueAsAddress()
        if not flags & lldb.eTypeIsSigned:
            return self._GetValueAsUnsigned()

        return sbv.GetValueAsSigned()

    # Python 3 conversion to int calls this.
    def __index__(self):
        return self.__int__()

    def __long__(self):
        sbv = self.__sbval
        if self.__ptr:
            sbv = self.__ptr.GetPointerSBValue(sbv)

        flags = sbv.GetType().GetTypeFlags()
        if flags & lldb.eTypeIsPointer:
            return sbv.GetValueAsAddress()
        if not flags & lldb.eTypeIsSigned:
            return self._GetValueAsUnsigned()

        return sbv.GetValueAsSigned()

    def __float__(self):
        return float(self.__sbval.GetValueAsSigned())

    # Python 2 must return native string.
    def __oct__(self):
        return '0%o' % self._GetValueAsUnsigned()

    # Python 2 must return native string.
    def __hex__(self):
        return '0x%x' % self._GetValueAsUnsigned()

    def __hash__(self):
        return hash(self.__sbval)

    def GetRawSBValue(self):
        return self.__sbval

    def GetSBValue(self):
        sbv = self.__sbval
        if self.__ptr:
            sbv = self.__ptr.GetPointerSBValue(sbv)

        return sbv

    def __getstate__(self):
        err = lldb.SBError()
        sbv = self.__sbval
        if self.__ptr:
            sbv = self.__ptr.GetPointerSBValue(sbv)
            addr = sbv.GetValueAsAddress()
            size = sbv.GetType().GetPointeeType().GetByteSize()
        else:
            addr = sbv.GetLoadAddress()
            size = sbv.GetType().GetByteSize()

        content = sbv.GetProcess().ReadMemory(addr, size, err)
        if err.fail:
            content = ''
        return content

    def _GetValueAsSigned(self):
        sbv = self.__sbval
        if self.__ptr:
            print("ERROR: You cannot get 'int' from pointer type %s, please use unsigned(obj) for such purposes." % sbv.GetType().GetDisplayTypeName())
            raise ValueError("Cannot get signed int for pointer data.")
        serr = lldb.SBError()
        retval = sbv.GetValueAsSigned(serr)
        if serr.success:
            return retval
        raise ValueError("Failed to read signed data. {} (type = {}) Error description: {}".format(
            str(sbv), sbv.GetType().GetDisplayTypeName(), serr.GetCString()))

    def _GetValueAsCast(self, dest_type):
        if not isinstance(dest_type, lldb.SBType):
            raise ValueError("Invalid type for dest_type: {}".format(type(dest_type)))
        addr = self._GetValueAsUnsigned()
        sbval = self.__sbval.target.CreateValueFromExpression("newname", "(void *)"+str(addr))
        val = value(sbval.Cast(dest_type))
        return val

    def _GetValueAsUnsigned(self):
        sbv = self.__sbval
        if self.__ptr:
            sbv = self.__ptr.GetPointerSBValue(sbv)
            return sbv.GetValueAsAddress()
        serr = lldb.SBError()
        retval = sbv.GetValueAsUnsigned(serr)
        if serr.success:
            return retval
        raise ValueError("Failed to read unsigned data. {} (type = {}) Error description: {}".format(
            str(sbv), sbv.GetType().GetDisplayTypeName(), serr.GetCString()))

    def _GetValueAsString(self, offset=0, maxlen=1024):
        sbv = self.__sbval
        serr = lldb.SBError()
        sbdata = None
        if self.__ptr:
            sbv = self.__ptr.GetPointerSBValue(sbv)
            sbdata = sbv.GetPointeeData(offset, maxlen)
        else:
            sbdata = sbv.GetData()

        retval = ''
        bytesize = sbdata.GetByteSize()
        if bytesize == 0:
            # raise ValueError('Unable to read value as string')
            return ''
        for i in range(0, bytesize):
            serr.Clear()
            ch = chr(sbdata.GetUnsignedInt8(serr, i))
            if serr.fail:
                raise ValueError("Unable to read string data: " + serr.GetCString())
            if ch == '\0':
                break
            retval += ch
        return retval

    def __format__(self, format_spec):
        # typechar is last char. see http://www.python.org/dev/peps/pep-3101/
        typechar = format_spec[-1] if len(format_spec) else ''

        if typechar in 'bcdoxX': # requires integral conversion
            return format(int(self), format_spec)

        if typechar in 'eEfFgG%': # requires float conversion
            return format(float(self), format_spec)

        if typechar in 's': # requires string conversion
            return format(str(self), format_spec)

        # 'n' or '' mean "whatever you got for me"
        flags = self.__sbval.GetType().GetTypeFlags()
        if flags & lldb.eTypeIsFloat:
            return format(float(self), format_spec)
        elif flags & lldb.eTypeIsScalar:
            return format(int(self), format_spec)
        else:
            return format(str(self), format_spec)

def unsigned(val):
    """ Helper function to get unsigned value from core.value
        params: val - value (see value class above) representation of an integer type
        returns: int which is unsigned.
        raises : ValueError if the type cannot be represented as unsigned int.
    """
    if type(val) is value:
        return int(val._GetValueAsUnsigned())
    return int(val)


def signed(val):
    """ Helper function to get signed value from core.value
        params: val - value (see value class above) representation of an integer type
        returns: int which is signed.
        raises: ValueError if the type cannot be represented as signed int.
    """
    if type(val) is value:
        return val.GetSBValue().GetValueAsSigned()
    return int(val)


def sizeof(t):
    """ Find the byte size of a type.
        params: t - str : ex 'time_spec' returns equivalent of sizeof(time_spec) in C
                t - value: ex a value object. returns size of the object
        returns: int - byte size length
    """
    if type(t) is value:
        return t.GetSBValue().GetByteSize()
    if isinstance(t, str):
        return gettype(t).GetByteSize()
    raise ValueError("Cannot get sizeof. Invalid argument")


def dereference(val):
    """ Get a dereferenced obj for a pointer type obj
        params: val - value object representing a pointer type C construct in lldb
        returns: value - value
        ex. val = dereference(ptr_obj) #python
        is same as
            obj_ptr = (int *)0x1234  #C
            val = *obj_ptr           #C
    """
    if type(val) is value:
        sbv = val.GetSBValue()
        return value(sbv.Dereference())
    raise TypeError('Cannot dereference this type.')


def wrapped(val):
    """ Get original pointer value without aplying pointer policy.
        param: val - value object representing a pointer
        returns: value - value
    """
    if isinstance(val, value):
        policy = val.ptrpolicy
        val.ptrpolicy = None
        newval = value(val.GetSBValue(), False)
        val.ptrpolicy = policy
        return newval
    raise TypeError("Cannot do wrapped for non-value type objects")


def addressof(val):
    """ Get address of a core.value object.
        params: val - value object representing a C construct in lldb
        returns: value - value object referring to 'type(val) *' type
        ex. addr = addressof(hello_obj)  #python
        is same as
           uintptr_t addr = (uintptr_t)&hello_obj  #C
    """
    if type(val) is value:
        return value(val.GetSBValue().AddressOf())
    raise TypeError("Cannot do addressof for non-value type objects")


def cast(obj, target_type):
    """ Type cast an object to another C type.
        params:
            obj - core.value  object representing some C construct in lldb
            target_type - str : ex 'char *'
                        - lldb.SBType :
    """
    dest_type = target_type
    if isinstance(target_type, str):
        dest_type = gettype(target_type)
    elif type(target_type) is value:
        dest_type = target_type.GetSBValue().GetType()

    if type(obj) is value:
        return obj._GetValueAsCast(dest_type)
    elif type(obj) is int:
        print("ERROR: You cannot cast an 'int' to %s, please use kern.GetValueFromAddress() for such purposes." % str(target_type))
    raise TypeError("object of type %s cannot be casted to %s" % (str(type(obj)), str(target_type)))


def containerof(obj, target_type, field_name):
    """ Type cast an object to another C type from a pointer to a field.
        params:
            obj - core.value  object representing some C construct in lldb
            target_type - str : ex 'struct thread'
                        - lldb.SBType :
            field_name - the field name within the target_type obj is a pointer to
    """
    addr = int(obj) - getfieldoffset(target_type, field_name)
    sbv  = obj.GetSBValue()
    sbv  = sbv.chkCreateValueFromAddress(None, addr, gettype(target_type))
    return value(sbv.AddressOf())


@cache_statically
def gettype(target_type, target=None):
    """ Returns lldb.SBType of the given target_type
        params:
            target_type - str, ex. 'char', 'uint32_t' etc
        returns:
            lldb.SBType - SBType corresponding to the given target_type
        raises:
            NameError  - Incase the type is not identified
    """

    #
    # If the type was qualified with a `struct` or `class`, ...
    # make sure we pick up the proper definition in case of clashes.
    #
    want = 0
    name = str(target_type).strip()

    if name.startswith("struct"):
        want = lldb.eTypeClassStruct
    elif name.startswith("union"):
        want = lldb.eTypeClassUnion
    elif name.startswith("class"):
        want = lldb.eTypeClassClass
    elif name.startswith("enum"):
        want = lldb.eTypeClassEnumeration
    elif name.startswith("typedef"):
        want = lldb.eTypeClassTypedef

    #
    # Now remove constness and speficiers, and pointers
    #
    tmpname  = re.sub(r'\bconst\b', '', name).strip(" ")
    tmpname  = re.sub(r'^(struct|class|union|enum|typedef) ', '', tmpname)
    basename = tmpname.rstrip(" *")
    ptrlevel = tmpname.count('*', len(basename))

    def resolve_pointee_type(t: lldb.SBType):
        while t.IsPointerType():
            t = t.GetPointeeType()
        return t

    def type_sort_heuristic(t: lldb.SBType) -> int:
        """ prioritizes types with more fields, and prefers fields with complete
        types
            params:
                t - lldb.SBType, type to score
            returns:
                int - heuristic score
        """
        # we care about the underlying type, not the pointer
        resolved_type: lldb.SBType = resolve_pointee_type(t)
        
        # heuristic score
        score = 0
        for field in resolved_type.fields:
            resolved_field_type = resolve_pointee_type(field.GetType())
            score += 3 if resolved_field_type.IsTypeComplete() else 1

        return score

    type_arr = [t for t in target.chkFindTypes(basename)]
    # After the sort, the best matching struct will be at index [0].
    # This heuristic selects a struct type with more fields (with complete types)
    # compared to ones with "opaque" members
    type_arr.sort(reverse=True, key=type_sort_heuristic)

    for tyobj in type_arr:
        if want and tyobj.GetTypeClass() != want:
            continue

        for _ in range(ptrlevel):
            tyobj = tyobj.GetPointerType()

        return tyobj

    raise NameError('Unable to find type {}'.format(target_type))


@cache_statically
def getfieldoffset(struct_type, field_name_or_path, target=None):
    """ Returns the byte offset of a field inside a given struct
        Understands anonymous unions and field names in sub-structs
        params:
            field_name_or_path  - str, name or path to the field inside the struct ex. 'ip_messages'
        returns:
            int - byte offset of the field_name inside the struct_type
    """

    return gettype(struct_type).xGetFieldOffset(field_name_or_path)


def islong(x):
    """ Returns True if a string represents a long integer, False otherwise
    """
    try:
        int(x, 16)
    except ValueError:
        try:
            int(x)
        except ValueError:
            return False
    return True


def readmemory(val):
    """ Returns a string of hex data that is referenced by the value.
        params: val - a value object.
        return: str - string of hex bytes.
        raises: TypeError if val is not a valid type
    """
    if not type(val) is value:
        raise TypeError('%s is not of type value' % str(type(val)))
    return val.__getstate__()


def getOSPtr(cpp_obj):
    """ Returns a core.value created from an intrusive_shared_ptr or itself, cpp_obj
        params: cpp_obj - core.value object representing a C construct in lldb
        return: core.value - newly created core.value or cpp_obj
    """
    child = cpp_obj.GetSBValue().GetChildAtIndex(0)
    if 'intrusive_shared_ptr' in str(child):
        return value(child.GetChildMemberWithName('ptr_'))
    return cpp_obj