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