""" 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