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