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