1import operator
2
3from abc import ABCMeta, abstractmethod
4from core import (
5    caching,
6    gettype,
7)
8
9from .kmem import KMem, MemoryRange
10
11
12class MemoryObject(object, metaclass=ABCMeta):
13    """
14    Abstract class for any memory object resolved by Whatis
15    """
16
17    MO_KIND = None
18
19    def __init__(self, kmem, address):
20        self.kmem    = kmem
21        self.address = address
22
23    @property
24    @abstractmethod
25    def object_range(self):
26        """
27        Returns the MemoryRange for this object if any
28        """
29        pass
30
31    @abstractmethod
32    def describe(self, verbose=False):
33        """
34        Method to describe oneself for whatis
35        """
36        pass
37
38
39class UnknownMemoryObject(MemoryObject):
40    """ Fallback Memory Object for unclaimed addresses """
41
42    MO_KIND = "<unknown>"
43
44    @property
45    def object_range(self):
46        return None
47
48    def describe(self, verbose=False):
49        print("Unknown Memory Object Info")
50        print(" this address is not recognized, please implement/extend")
51        print(" a WhatisProvider to recognize it in the future")
52        print()
53
54
55class WhatisProvider(object):
56    """ Base class for Whatis Providers """
57
58    """
59    List of direct subclasses, used for resolution
60    """
61    subproviders = []
62
63    """
64    Evaluation cost of this provider
65
66    the higher the cost, the later it gets evaluated.
67    """
68
69    COST = 10
70
71    def __init__(self, target):
72        self._children = list(cls(target) for cls in self.__class__.subproviders)
73        self.kmem      = KMem.get_shared()
74        self.target    = target
75
76    @staticmethod
77    @caching.cache_statically
78    def get_shared(target=None):
79        return WhatisProvider(target)
80
81    def find_provider(self, address):
82        return next(iter(c for c in self._children if c.claims(address)), self)
83
84    def claims(self, address):
85        """
86        Returns whether this provider "claims" the address
87
88        @param address (int)
89            The addrress being considered
90        """
91
92        pass
93
94    def lookup(self, address):
95        """
96        Lookup a memory object by address
97
98        @param address (int)
99            The addrress being considered
100
101        @returns (MemoryObject)
102        """
103
104        return UnknownMemoryObject(self.kmem, address)
105
106    def describe(self, mo):
107        """
108        Describe a memory object
109
110        Providers can override this method to add more information.
111        """
112
113        print((
114            "Basic Info\n"
115            " kind                 : {0.__class__.MO_KIND}\n"
116            " address              : {0.address:#x}"
117        ).format(mo))
118
119        mem_r = mo.object_range
120        if mem_r is None:
121            print(" {:<21s}: Unknown".format("object range"))
122        else:
123            print(" {:<21s}: {r.start:#x} - {r.end:#x} ({r.size:,d} bytes)".format(
124                "object range", r = mem_r))
125            address = mo.address
126            if address != mem_r.start:
127                print(" {:<21s}: {:,d} from start, {:,d} to end".format(
128                    "offset", address - mem_r.start, mem_r.end - address))
129
130        print()
131
132
133def whatis_provider(cls):
134    """
135    Class decorator for Whatis providers
136    """
137
138    if not issubclass(cls, WhatisProvider):
139        raise TypeError("{} is not a subclass of WhatisProvider".format(cls.__name__))
140
141    cls.subproviders = []
142    base = cls.__base__
143
144    if base != object:
145        k = next((
146            k for k in ['claims', 'lookup']
147            if getattr(cls, k) == getattr(base, k)
148        ), None)
149        if k:
150            raise TypeError("{} must reimplement function '{}'".format(cls.__name__, k))
151
152        base.subproviders.append(cls)
153        base.subproviders.sort(key=operator.attrgetter('COST'))
154
155    return cls
156
157
158__all__ = [
159    whatis_provider.__name__,
160
161    MemoryObject.__name__,
162    WhatisProvider.__name__,
163]
164