xref: /xnu-11215.81.4/tools/lldbmacros/kext.py (revision d4514f0bc1d3f944c22d92e68b646ac3fb40d452)
1from collections import namedtuple
2from functools import cached_property
3import os
4import io
5from typing import Any, Generator
6import core
7from uuid import UUID
8
9from core.cvalue import (
10    unsigned,
11    signed,
12    addressof
13)
14from core.caching import (
15    cache_dynamically,
16    LazyTarget,
17)
18from core.io import SBProcessRawIO
19from macho import MachOSegment, MemMachO, VisualMachoMap
20
21from xnu import (
22    IterateLinkedList,
23    lldb_alias,
24    lldb_command,
25    lldb_run_command,
26    lldb_type_summary,
27    kern,
28    Cast,
29    header,
30    GetLongestMatchOption,
31    debuglog,
32    dsymForUUID,
33    addDSYM,
34    loadDSYM,
35    ArgumentError,
36    ArgumentStringToInt,
37    GetObjectAtIndexFromArray,
38    ResolveFSPath,
39    uuid_regex,
40    GetLLDBThreadForKernelThread
41)
42
43import kmemory
44import macho
45import lldb
46import concurrent.futures
47
48
49#
50# Summary of information available about a kext.
51#
52#   uuid     - UUID of the object
53#   vmaddr   - VA of the text segment
54#   name     - Name of the kext
55#   address  - Kext address
56#   segments - Mach-O segments (if available)
57#   summary  - OSKextLoadedSummary
58#   kmod     - kmod_info_t
59class KextSummary:
60    def __init__(self, uuid: str, vmaddr, name: str, address: int, segments: list[MachOSegment], summary: core.value):
61        self.uuid = uuid
62        self.vmaddr = vmaddr
63        self.name = name
64        self.address = address
65        self.segments = segments
66        self.summary = summary
67
68    @cached_property
69    def kmod(self):
70        try:
71            kmod = GetKmodWithAddr(unsigned(self.address))
72        except ValueError:
73            kmod = None
74
75        return kmod
76
77# Segment helpers
78
79
80def text_segment(segments):
81    """ Return TEXT segment if present in the list of first one.
82        segments: List of MachOSegment.
83    """
84
85    text_segments = {
86        s.name: s
87        for s in segments
88        if s.name in ('__TEXT_EXEC', '__TEXT')
89    }
90
91    # Pick text segment based on our prefered order.
92    for name in ['__TEXT_EXEC', '__TEXT']:
93        if name in text_segments:
94            return text_segments[name]
95
96    return segments[0]
97
98
99def seg_contains(segments, addr):
100    """ Returns generator of all segments that contains given address. """
101
102    return (
103        s for s in segments
104        if s.vmaddr <= addr < (s.vmaddr + s.vmsize)
105    )
106
107
108def sec_contains(sections, addr):
109    """ Returns generator of all sections that contains given address. """
110
111    return (
112        s for s in sections
113        if s.addr <= addr < (s.addr + s.size)
114    )
115
116def sbsec_contains(target, sbsections, addr):
117    """ Returns generator of all SBSections that contains given address. """
118
119    return (
120        s for s in sbsections
121        if s.GetLoadAddress(target) <= addr < s.GetLoadAddress(target) + s.GetByteSize()
122    )
123
124
125# Summary helpers
126
127def LoadMachO(address, size):
128    """ Parses Mach-O headers in given VA range.
129
130        return: MemMachO instance.
131    """
132
133    process = LazyTarget.GetProcess()
134    procio = SBProcessRawIO(process, address, size)
135    bufio = io.BufferedRandom(procio)
136    return macho.MemMachO(bufio)
137
138
139def IterateKextSummaries(target) -> Generator[KextSummary, Any, None]:
140    """ Generator walking over all kext summaries. """
141
142    hdr   = target.chkFindFirstGlobalVariable('gLoadedKextSummaries').Dereference()
143    arr   = hdr.GetValueForExpressionPath('.summaries[0]')
144    total = hdr.xGetIntegerByName('numSummaries')
145
146    for kext in (core.value(e.AddressOf()) for e in arr.xIterSiblings(0, total)):
147        # Load Mach-O segments/sections.
148        mobj = LoadMachO(unsigned(kext.address), unsigned(kext.size))
149
150        # Construct kext summary.
151        yield KextSummary(
152            uuid=GetUUIDSummary(kext.uuid),
153            vmaddr=text_segment(mobj.segments).vmaddr,
154            name=str(kext.name),
155            address=unsigned(kext.address),
156            segments=mobj.segments,
157            summary=kext
158        )
159
160
161@cache_dynamically
162def GetAllKextSummaries(target=None) -> list[KextSummary]:
163    """ Return all kext summaries. (cached) """
164
165    return list(IterateKextSummaries(target))
166
167
168def FindKextSummary(kmod_addr):
169    """ Returns summary for given kmod_info_t. """
170
171    for mod in GetAllKextSummaries():
172        if mod.address == kmod_addr or mod.vmaddr == kmod_addr:
173            return mod
174
175    return None
176
177
178# Keep this around until DiskImages2 migrate over to new methods above.
179def GetKextLoadInformation(addr=0, show_progress=False):
180    """ Original wrapper kept for backwards compatibility. """
181    if addr:
182        return [FindKextSummary(addr)]
183    else:
184        return GetAllKextSummaries()
185
186
187@lldb_command('showkextmacho')
188def ShowKextMachO(cmd_args=[]):
189    """ Show visual Mach-O layout.
190
191        Syntax: (lldb) showkextmacho <name of a kext>
192    """
193    if len(cmd_args) != 1:
194        raise ArgumentError("kext name is missing")
195
196    for kext in GetAllKextSummaries():
197
198        # Skip not matching kexts.
199        if kext.name.find(cmd_args[0]) == -1:
200            continue
201
202        # Load Mach-O segments/sections.
203        mobj = LoadMachO(unsigned(kext.kmod.address), unsigned(kext.kmod.size))
204
205        p = VisualMachoMap(kext.name)
206        p.printMachoMap(mobj)
207        print(" \n")
208
209
210_UNKNOWN_UUID = "........-....-....-....-............"
211
212
213@lldb_type_summary(['uuid_t'])
214@header("")
215def GetUUIDSummary(uuid):
216    """ returns a UUID string in form CA50DA4C-CA10-3246-B8DC-93542489AA26
217
218        uuid - Address of a memory where UUID is stored.
219    """
220
221    err = lldb.SBError()
222    addr = unsigned(addressof(uuid))
223    data = LazyTarget.GetProcess().ReadMemory(addr, 16, err)
224
225    if not err.Success():
226        return _UNKNOWN_UUID
227
228    return str(UUID(bytes=data)).upper()
229
230
231@lldb_type_summary(['kmod_info_t *'])
232@header((
233    "{0: <20s} {1: <20s} {2: <20s} {3: >3s} {4: >5s} {5: <20s} {6: <20s} "
234    "{7: >20s} {8: <30s}"
235).format('kmod_info', 'address', 'size', 'id', 'refs', 'TEXT exec', 'size',
236         'version', 'name'))
237def GetKextSummary(kmod):
238    """ returns a string representation of kext information """
239    if not kmod:
240        return "kmod info missing"
241
242    format_string = (
243        "{mod: <#020x} {mod.address: <#020x} {mod.size: <#020x} "
244        "{mod.id: >3d} {mod.reference_count: >5d} {seg.vmaddr: <#020x} "
245        "{seg.vmsize: <#020x} {mod.version: >20s} {mod.name: <30s}"
246    )
247
248    # Try to obtain text segment from kext summary
249    summary = FindKextSummary(unsigned(kmod.address))
250    if summary:
251        seg = text_segment(summary.segments)
252    else:
253        # Fake text segment for pseudo kexts.
254        seg = MachOSegment('__TEXT', kmod.address, kmod.size, 0, kmod.size, [])
255
256    return format_string.format(mod=kmod, seg=seg)
257
258
259def GetKmodWithAddr(addr):
260    """ Go through kmod list and find one with begin_addr as addr.
261        returns: None if not found else a cvalue of type kmod.
262    """
263
264    for kmod in IterateLinkedList(kern.globals.kmod, 'next'):
265        if addr == unsigned(kmod.address):
266            return kmod
267
268    return None
269
270
271@lldb_command('showkmodaddr')
272def ShowKmodAddr(cmd_args=[]):
273    """ Given an address, print the offset and name for the kmod containing it
274        Syntax: (lldb) showkmodaddr <addr>
275    """
276    if len(cmd_args) < 1:
277        raise ArgumentError("Insufficient arguments")
278
279    addr = ArgumentStringToInt(cmd_args[0])
280
281    # Find first summary/segment pair that covers given address.
282    sumseg = (
283        (m, next(seg_contains(m.segments, addr), None))
284        for m in GetAllKextSummaries()
285    )
286
287    print(GetKextSummary.header)
288    for ksum, segment in (t for t in sumseg if t[1] is not None):
289        summary = GetKextSummary(ksum.kmod)
290        print(summary + " segment: {} offset = {:#0x}".format(
291            segment.name, (addr - segment.vmaddr)))
292
293    return True
294
295
296def GetOSKextVersion(version_num):
297    """ returns a string of format 1.2.3x from the version_num
298        params: version_num - int
299        return: str
300    """
301    if version_num == -1:
302        return "invalid"
303
304    (MAJ_MULT, MIN_MULT) = (1000000000000, 100000000)
305    (REV_MULT, STAGE_MULT) = (10000, 1000)
306
307    version = version_num
308
309    vers_major = version // MAJ_MULT
310    version = version - (vers_major * MAJ_MULT)
311
312    vers_minor = version // MIN_MULT
313    version = version - (vers_minor * MIN_MULT)
314
315    vers_revision = version // REV_MULT
316    version = version - (vers_revision * REV_MULT)
317
318    vers_stage = version // STAGE_MULT
319    version = version - (vers_stage * STAGE_MULT)
320
321    vers_stage_level = version
322
323    out_str = "%d.%d" % (vers_major, vers_minor)
324    if vers_revision > 0:
325        out_str += ".%d" % vers_revision
326    if vers_stage == 1:
327        out_str += "d%d" % vers_stage_level
328    if vers_stage == 3:
329        out_str += "a%d" % vers_stage_level
330    if vers_stage == 5:
331        out_str += "b%d" % vers_stage_level
332    if vers_stage == 6:
333        out_str += "fc%d" % vers_stage_level
334
335    return out_str
336
337
338def FindKmodNameForAddr(addr):
339    """ Given an address, return the name of the kext containing that address.
340    """
341
342    names = (
343        mod.kmod.name
344        for mod in GetAllKextSummaries()
345        if (any(seg_contains(mod.segments, unsigned(addr))))
346    )
347
348    return next(names, None)
349
350
351@lldb_command('showallkmods')
352def ShowAllKexts(cmd_args=None):
353    """ Display a summary listing of all loaded kexts (alias: showallkmods) """
354
355    print("{: <36s} ".format("UUID") + GetKextSummary.header)
356
357    for kmod in IterateLinkedList(kern.globals.kmod, 'next'):
358        sum = FindKextSummary(unsigned(kmod.address))
359
360        if sum:
361            _ksummary = GetKextSummary(sum.kmod)
362            uuid = sum.uuid
363        else:
364            _ksummary = GetKextSummary(kmod)
365            uuid = _UNKNOWN_UUID
366
367        print(uuid + " " + _ksummary)
368
369
370@lldb_command('showallknownkmods')
371def ShowAllKnownKexts(cmd_args=None):
372    """ Display a summary listing of all kexts known in the system.
373        This is particularly useful to find if some kext was unloaded
374        before this crash'ed state.
375    """
376    kext_ptr = kern.globals.sKextsByID
377    kext_count = unsigned(kext_ptr.count)
378
379    print("%d kexts in sKextsByID:" % kext_count)
380    print("{0: <20s} {1: <20s} {2: >5s} {3: >20s} {4: <30s}".format('OSKEXT *', 'load_addr', 'id', 'version', 'name'))
381    format_string = "{0: <#020x} {1: <20s} {2: >5s} {3: >20s} {4: <30s}"
382
383    for kext_dict in (GetObjectAtIndexFromArray(kext_ptr.dictionary, i)
384                      for i in range(kext_count)):
385
386        kext_name = str(kext_dict.key.string)
387        osk = Cast(kext_dict.value, 'OSKext *')
388
389        load_addr = "------"
390        id = "--"
391
392        if int(osk.flags.loaded):
393            load_addr = "{0: <#020x}".format(osk.kmod_info)
394            id = "{0: >5d}".format(osk.loadTag)
395
396        version_num = signed(osk.version)
397        version = GetOSKextVersion(version_num)
398        print(format_string.format(osk, load_addr, id, version, kext_name))
399
400
401def FetchDSYM(kinfo):
402    """ Obtains and adds dSYM based on kext summary. """
403
404    # No op for built-in modules.
405    kernel_uuid = str(kern.globals.kernel_uuid_string)
406    if kernel_uuid == kinfo.uuid:
407        print("(built-in)")
408        return
409
410    # Obtain and load binary from dSYM.
411    print("Fetching dSYM for %s" % kinfo.uuid)
412    info = dsymForUUID(kinfo.uuid)
413    if info and 'DBGSymbolRichExecutable' in info:
414        print("Adding dSYM (%s) for %s" % (kinfo.uuid, info['DBGSymbolRichExecutable']))
415        addDSYM(kinfo.uuid, info)
416        loadDSYM(kinfo.uuid, kinfo.vmaddr, kinfo.segments)
417    else:
418        print("Failed to get symbol info for %s" % kinfo.uuid)
419
420
421def AddKextSymsByFile(filename, slide):
422    """ Add kext based on file name and slide. """
423    sections = None
424
425    filespec = lldb.SBFileSpec(filename, False)
426    print("target modules add \"{:s}\"".format(filename))
427    print(lldb_run_command("target modules add \"{:s}\"".format(filename)))
428
429    loaded_module = LazyTarget.GetTarget().FindModule(filespec)
430    if loaded_module.IsValid():
431        uuid_str = loaded_module.GetUUIDString()
432        debuglog("added module {:s} with uuid {:s}".format(filename, uuid_str))
433
434        if slide is None:
435            for k in GetAllKextSummaries():
436                debuglog(k.uuid)
437                if k.uuid.lower() == uuid_str.lower():
438                    slide = k.vmaddr
439                    sections = k.segments
440                    debuglog("found the slide {:#0x} for uuid {:s}".format(k.vmaddr, k.uuid))
441    if slide is None:
442        raise ArgumentError("Unable to find load address for module described at {:s} ".format(filename))
443
444    if not sections:
445        cmd_str = "target modules load --file \"{:s}\" --slide {:s}".format(filename, str(slide))
446        debuglog(cmd_str)
447    else:
448        cmd_str = "target modules load --file \"{:s}\"".format(filename)
449        for s in sections:
450            cmd_str += " {:s} {:#0x} ".format(s.name, s.vmaddr)
451        debuglog(cmd_str)
452
453    lldb.debugger.HandleCommand(cmd_str)
454
455    kern.symbolicator = None
456    return True
457
458
459def AddKextSymsByName(kextname, all=False):
460    """ Add kext based on longest name match"""
461
462    kexts = GetLongestMatchOption(kextname, [x.name for x in GetAllKextSummaries()], True)
463    if not kexts:
464        print("No matching kext found.")
465        return False
466
467    if len(kexts) != 1 and not all:
468        print("Ambiguous match for name: {:s}".format(kextname))
469        if len(kexts) > 0:
470            print("Options are:\n\t" + "\n\t".join(kexts))
471        return False
472
473    # Load all matching dSYMs
474    for sum in GetAllKextSummaries():
475        if sum.name in kexts:
476            debuglog("matched the kext to name {:s} "
477                     "and uuid {:s}".format(sum.name, sum.uuid))
478            FetchDSYM(sum)
479
480    kern.symbolicator = None
481    return True
482
483
484def AddKextByAddress(addr: str):
485    """ Given an address, load the kext which contains that address """
486
487    match = (
488        (kinfo, seg_contains(kinfo.segments, addr))
489        for kinfo in GetAllKextSummaries()
490        if any(seg_contains(kinfo.segments, addr))
491    )
492
493    # Load all kexts which contain given address.
494    print(GetKextSummary.header)
495    for kinfo, segs in match:
496        for s in segs:
497            print(f"{GetKextSummary(kinfo.kmod)} segment: {s.name} offset = {(addr - s.vmaddr):0x}")
498            FetchDSYM(kinfo)
499
500
501def AddKextByThread(addr: str):
502    """ Given a thread, load all kexts needed to symbolicate its backtrace """
503
504    thread_value = kern.GetValueFromAddress(addr, "thread_t")
505    thread_lldb_SBThread = GetLLDBThreadForKernelThread(thread_value)
506
507    kexts_needed = dict()
508    printed_header = False
509    for frame in thread_lldb_SBThread.frames:
510        if not frame.name:
511            frame_addr = frame.GetPC()
512
513            match = (
514                (kinfo, seg_contains(kinfo.segments, frame_addr))
515                for kinfo in GetAllKextSummaries()
516                if any(seg_contains(kinfo.segments, frame_addr))
517            )
518
519            if match and not printed_header:
520                print(GetKextSummary.header)
521                printed_header = True
522
523            for kinfo, segs in match:
524                for s in segs:
525                    print(f"{GetKextSummary(kinfo.kmod)} segment: {s.name} offset = {(frame_addr - s.vmaddr):0x}")
526                    kexts_needed[kinfo.uuid] = kinfo
527
528    print(f"Fetching {len(kexts_needed)} dSyms")
529    pool = concurrent.futures.ThreadPoolExecutor()
530    for kinfo in kexts_needed.values():
531        pool.submit(FetchDSYM, kinfo)
532    pool.shutdown(wait=True)
533
534
535def AddKextByUUID(uuid: str):
536    """ Loads the dSym for a specific UUID, or all dSym """
537
538    kernel_uuid = str(kern.globals.kernel_uuid_string).lower()
539    load_all_kexts = (uuid == "all")
540    if not load_all_kexts and len(uuid_regex.findall(uuid)) == 0:
541        raise ArgumentError("Unknown argument {:s}".format(uuid))
542
543    pool = concurrent.futures.ThreadPoolExecutor()
544    for sum in GetAllKextSummaries():
545        cur_uuid = sum.uuid.lower()
546        if load_all_kexts or (uuid == cur_uuid):
547            if kernel_uuid != cur_uuid:
548                pool.submit(FetchDSYM, sum)
549    pool.shutdown(wait=True)
550
551    kern.symbolicator = None
552
553
554@lldb_command('addkext', 'AF:T:N:')
555def AddKextSyms(cmd_args=[], cmd_options={}):
556    """ Add kext symbols into lldb.
557        This command finds symbols for a uuid and load the required executable
558        Usage:
559            addkext <uuid> : Load one kext based on uuid. eg. (lldb)addkext 4DD2344C0-4A81-3EAB-BDCF-FEAFED9EB73E
560            addkext -F <abs/path/to/executable> : Load kext with executable
561            addkext -F <abs/path/to/executable> <load_address> : Load kext with executable at specified load address
562            addkext -N <name> : Load one kext that matches the name provided. eg. (lldb) addkext -N corecrypto
563            addkext -N <name> -A: Load all kext that matches the name provided. eg. to load all kext with Apple in name do (lldb) addkext -N Apple -A
564            addkext -T <thread>: Given a thread, load all kexts needed to symbolicate its backtrace
565            addkext all    : Will load all the kext symbols - SLOW
566    """
567
568    # Load kext by file name.
569    if "-F" in cmd_options:
570        exec_path = cmd_options["-F"]
571        exec_full_path = ResolveFSPath(exec_path)
572        if not os.path.exists(exec_full_path):
573            raise ArgumentError("Unable to resolve {:s}".format(exec_path))
574
575        if not os.path.isfile(exec_full_path):
576            raise ArgumentError(
577                """Path is {:s} not a filepath.
578                Please check that path points to executable.
579                For ex. path/to/Symbols/IOUSBFamily.kext/Contents/PlugIns/AppleUSBHub.kext/Contents/MacOS/AppleUSBHub.
580                Note: LLDB does not support adding kext based on directory paths like gdb used to.""".format(exec_path))
581
582        slide_value = None
583        if cmd_args:
584            slide_value = cmd_args[0]
585            debuglog("loading slide value from user input {:s}".format(cmd_args[0]))
586
587        return AddKextSymsByFile(exec_full_path, slide_value)
588
589    # Load kext by name.
590    if "-N" in cmd_options:
591        kext_name = cmd_options["-N"]
592        return AddKextSymsByName(kext_name, "-A" in cmd_options)
593
594    # Load all kexts needed to symbolicate a thread's backtrace
595    if "-T" in cmd_options:
596        return AddKextByThread(cmd_options["-T"])
597
598    # Load kexts by UUID or "all"
599    if len(cmd_args) < 1:
600        raise ArgumentError("No arguments specified.")
601
602    uuid = cmd_args[0].lower()
603    return AddKextByUUID(uuid)
604
605
606@lldb_command('addkextaddr')
607def AddKextAddr(cmd_args=[]):
608    """ Given an address, load the kext which contains that address
609        Syntax: (lldb) addkextaddr <addr>
610    """
611    if len(cmd_args) < 1:
612        raise ArgumentError("Insufficient arguments")
613
614    addr = ArgumentStringToInt(cmd_args[0])
615    AddKextByAddress(addr)
616
617
618class KextMemoryObject(kmemory.MemoryObject):
619    """ Describes an object landing in some kext """
620
621    MO_KIND = "kext mach-o"
622
623    def __init__(self, kmem, address, kinfo):
624        super().__init__(kmem, address)
625        self.kinfo = kinfo
626        self.target = kmem.target
627
628    @property
629    def object_range(self):
630        seg = next(seg_contains(self.kinfo.segments, self.address))
631        sec = next(sec_contains(seg.sections, self.address), None)
632        if sec:
633            return kmemory.MemoryRange(sec.addr, sec.addr + sec.size)
634        return kmemory.MemoryRange(seg.vmaddr, seg.vmaddr + seg.vmsize)
635
636    def find_mod_seg_sect(self):
637        target  = self.target
638        address = self.address
639
640        return next((
641            (module, segment, next(sbsec_contains(target, segment, address), None))
642            for module in target.module_iter()
643            for segment in sbsec_contains(target, module.section_iter(), address)
644        ), (None, None, None))
645
646    def describe(self, verbose=False):
647        from lldb.utils.symbolication import Symbolicator
648
649        addr    = self.address
650        kinfo   = self.kinfo
651
652        sbmod, sbseg, sbsec = self.find_mod_seg_sect()
653        if sbmod is None:
654            FetchDSYM(kinfo)
655            print()
656            sbmod, sbseg, sbsec = self.find_mod_seg_sect()
657
658        syms   = Symbolicator.InitWithSBTarget(self.target).symbolicate(addr)
659        sym    = next(iter(syms)) if syms else None
660
661        if not sbseg:
662            # not really an SBSection but we only need to pretty print 'name'
663            # which both have, yay duck typing
664            sbseg = next(seg_contains(kinfo.segments, addr), None)
665
666        fmt  = "Kext Symbol Info\n"
667        fmt += " kext                 : {kinfo.name} ({kinfo.uuid})\n"
668        fmt += " module               : {sbmod.file.basename}\n" if sbmod else ""
669        fmt += " section              : {sbseg.name} {sbsec.name}\n" if sbsec else \
670               " segment              : {sbseg.name}\n" if sbseg else ""
671        fmt += " symbol               : {sym!s}\n" if sym else ""
672
673        print(fmt.format(kinfo=kinfo, sbmod=sbmod, sbseg=sbseg, sbsec=sbsec, sym=sym))
674
675
676class MainBinaryMemoryObject(kmemory.MemoryObject):
677    """ Describes an object landing in the main kernel binary """
678
679    MO_KIND = "kernel mach-o"
680
681    def __init__(self, kmem, address, section):
682        super().__init__(kmem, address)
683        self.section = section
684        self.target = kmem.target
685
686    def _subsection(self):
687        return next(sbsec_contains(self.target, self.section, self.address), None)
688
689    @property
690    def object_range(self):
691        target  = self.target
692        section = self._subsection() or self.section
693        addr    = section.GetLoadAddress(target)
694        size    = section.GetByteSize()
695        return kmemory.MemoryRange(addr, addr + size)
696
697    @property
698    def module(self):
699        return self.target.GetModuleAtIndex(0).GetFileSpec().GetFilename()
700
701    @property
702    def uuid(self):
703        return self.target.GetModuleAtIndex(0).GetUUIDString()
704
705    def describe(self, verbose=False):
706        from lldb.utils.symbolication import Symbolicator
707
708        subsec  = self._subsection()
709        syms    = Symbolicator.InitWithSBTarget(self.target).symbolicate(self.address)
710        sym     = next(iter(syms)) if syms else None
711
712        fmt  = "Symbol Info\n"
713        fmt += " module               : {mo.module}\n"
714        fmt += " uuid                 : {mo.uuid}\n"
715        fmt += " section              : {mo.section.name} {subsec.name}\n" if subsec else ""
716        fmt += " segment              : {mo.section.name}\n" if not subsec else ""
717        fmt += " symbol               : {sym}\n" if sym else ""
718
719        print(fmt.format(mo=self, subsec=subsec, sym=sym))
720
721
722@kmemory.whatis_provider
723class KextWhatisProvider(kmemory.WhatisProvider):
724    """ Kext ranges whatis provider """
725
726    COST = 100
727
728    def claims(self, address):
729        target  = self.target
730        mainmod = target.GetModuleAtIndex(0)
731
732        #
733        # TODO: surely the kexts can provide a better range check
734        #
735
736        return any(
737            sbsec_contains(target, mainmod.section_iter(), address)
738        ) or any(
739            any(seg_contains(kinfo.segments, address))
740            for kinfo in GetAllKextSummaries()
741        )
742
743    def lookup(self, address):
744        target  = self.target
745        mainmod = target.GetModuleAtIndex(0)
746
747        section = next(sbsec_contains(target, mainmod.section_iter(), address), None)
748
749        if section:
750            return MainBinaryMemoryObject(self.kmem, address, section)
751
752        return KextMemoryObject(self.kmem, address, next(
753            kinfo
754            for kinfo in GetAllKextSummaries()
755            if any(seg_contains(kinfo.segments, address))
756        ))
757
758
759# Aliases for backward compatibility.
760
761lldb_alias('showkmod', 'showkmodaddr')
762lldb_alias('showkext', 'showkmodaddr')
763lldb_alias('showkextaddr', 'showkmodaddr')
764lldb_alias('showallkexts', 'showallkmods')
765