xref: /xnu-10002.1.13/tools/lldbmacros/core/pointer.py (revision 1031c584a5e37aff177559b9f69dbd3c8c3fd30a)
1"""
2Custom pointer support
3
4This module provides support for special pointer types that are not native to the
5language used by the target being debugged. Such pointers may be represented as a struct
6or class (for example IOKit's shared pointers).
7
8A custom pointer class must subclass the PointerPolicy class and implement all of its
9abstract methods. The MetaPointerPolicy metaclass ensures that all known subclasses are
10registered in a global list (wherever they are located in the lldb macro sources).
11
12A client can obtain a PointerPolicy instance by calling the match method with an SBValue
13instance as an argument. The returned value is one of:
14
15    * None - the match was unsuccessful and this SBValue instance is not a pointer.
16    * Concrete instance - An instance of the concrete PointerPolicy class that will handle
17      pointer operations for the given SBValue.
18
19Concrete policy instances implement an API that allows a client to operate on a value
20like a native pointer (for example unwrapping a native pointer from a smart pointer).
21
22Example:
23
24    # Obtain an SBValue instance.
25    val = kern.global.GlobalVariable.GetSBValue()
26
27    # Try to match the pointer policy for the given value.
28    policy = PointerPolicy.match(val)
29
30    # Unwrap the pointer SBValue.
31    if policy:
32        val = policy.GetPointerSBValue(val)
33
34    ... Operate on val as usual.
35"""
36
37from __future__ import absolute_import, division, print_function
38
39from builtins import map, filter
40from operator import methodcaller
41from abc import ABCMeta, abstractmethod
42
43import six
44import lldb
45
46from .caching import cache_statically
47
48
49class MetaPointerPolicy(ABCMeta):
50    """ Register a custom pointer policy in global list. """
51
52    classes = []
53
54    def __new__(cls, clsname, bases, args):
55        newcls = super(MetaPointerPolicy, cls).__new__(cls, clsname, bases, args)
56        cls.classes.append(newcls)
57        return newcls
58
59
60class Singleton(MetaPointerPolicy):
61    """ Meta class for creation of singleton instances. """
62
63    _instances = {}
64
65    def __call__(cls, *args, **kwargs):
66        if cls not in cls._instances:
67            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
68        return cls._instances[cls]
69
70
71@six.add_metaclass(ABCMeta)
72class PointerPolicy(object):
73    """ Abstract base class common to every custom pointer policy. """
74
75    @classmethod
76    def match(cls, sbvalue):
77        """ Match pointer representation based on given SBValue. """
78        matching = filter(bool, map(methodcaller('match', sbvalue), MetaPointerPolicy.classes))
79        return next(matching, None)
80
81    @abstractmethod
82    def GetPointerSBValue(self, sbvalue):
83        """ Returns pointer value that debugger should operate on. """
84
85
86# Pointers need to have their TBI byte stripped if in use. TBI KASan,
87# for instance, tags pointers to detect improper memory accesses. Reading
88# values from such tagged pointers fails.
89#
90# Stripping the pointers requires to learn whether TBI is in use or not.
91# We do that by checking presence of 'kasan_tbi_enabled' symbol which only
92# exists on the TBI KASan variant. Since KASan is one of more TBI
93# consumers (along with PAC or Sandbox) this is not an ideal approach.
94# Inspecting respective CPU state would be more appropriate.
95
96
97@six.add_metaclass(Singleton)
98class NativePointer(PointerPolicy):
99    """ Policy for native pointers.
100
101        Strips top bits of a pointer if TBI is in use. Otherwise
102        pointer is used as-is.
103
104        Native pointers do not have any per-pointer attributes so this policy
105        can be singleton instance.
106    """
107
108    @staticmethod
109    @cache_statically
110    def isKasanTBI(target=None):
111        """ Returns true on TBI KASan targets, false otherwise. """
112        return any(target.FindGlobalVariables('kasan_tbi_enabled', 1))
113
114    @classmethod
115    def match(cls, sbvalue):
116        return cls() if sbvalue.GetType().IsPointerType() else None
117
118    def stripTBI(self, sbvalue):
119        """ Strips the TBI byte value. Since the value is not a plain value but
120            represents a value of a variable, a register or an expression the
121            conversion is performed by (re-)creating the value through expression.
122        """
123        if sbvalue.GetValueAsAddress() != sbvalue.GetValueAsUnsigned():
124            addr = sbvalue.GetValueAsAddress()
125            sbv_new = sbvalue.CreateValueFromExpression(None, '(void *)' + str(addr))
126            return sbv_new.Cast(sbvalue.GetType())
127
128        return sbvalue
129
130    def GetPointerSBValue(self, sbvalue):
131        if self.isKasanTBI():
132            return self.stripTBI(sbvalue)
133        return sbvalue
134