efi_lldb.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044
  1. #!/usr/bin/python3
  2. '''
  3. Copyright (c) Apple Inc. 2021
  4. SPDX-License-Identifier: BSD-2-Clause-Patent
  5. Example usage:
  6. OvmfPkg/build.sh qemu -gdb tcp::9000
  7. lldb -o "gdb-remote localhost:9000" -o "command script import efi_lldb.py"
  8. '''
  9. import optparse
  10. import shlex
  11. import subprocess
  12. import uuid
  13. import sys
  14. import os
  15. from pathlib import Path
  16. from efi_debugging import EfiDevicePath, EfiConfigurationTable, EfiTpl
  17. from efi_debugging import EfiHob, GuidNames, EfiStatusClass, EfiBootMode
  18. from efi_debugging import PeTeImage, patch_ctypes
  19. try:
  20. # Just try for LLDB in case PYTHONPATH is already correctly setup
  21. import lldb
  22. except ImportError:
  23. try:
  24. env = os.environ.copy()
  25. env['LLDB_DEFAULT_PYTHON_VERSION'] = str(sys.version_info.major)
  26. lldb_python_path = subprocess.check_output(
  27. ["xcrun", "lldb", "-P"], env=env).decode("utf-8").strip()
  28. sys.path.append(lldb_python_path)
  29. import lldb
  30. except ValueError:
  31. print("Couldn't find LLDB.framework from lldb -P")
  32. print("PYTHONPATH should match the currently selected lldb")
  33. sys.exit(-1)
  34. class LldbFileObject(object):
  35. '''
  36. Class that fakes out file object to abstract lldb from the generic code.
  37. For lldb this is memory so we don't have a concept of the end of the file.
  38. '''
  39. def __init__(self, process):
  40. # _exe_ctx is lldb.SBExecutionContext
  41. self._process = process
  42. self._offset = 0
  43. self._SBError = lldb.SBError()
  44. def tell(self):
  45. return self._offset
  46. def read(self, size=-1):
  47. if size == -1:
  48. # arbitrary default size
  49. size = 0x1000000
  50. data = self._process.ReadMemory(self._offset, size, self._SBError)
  51. if self._SBError.fail:
  52. raise MemoryError(
  53. f'lldb could not read memory 0x{size:x} '
  54. f' bytes from 0x{self._offset:08x}')
  55. else:
  56. return data
  57. def readable(self):
  58. return True
  59. def seek(self, offset, whence=0):
  60. if whence == 0:
  61. self._offset = offset
  62. elif whence == 1:
  63. self._offset += offset
  64. else:
  65. # whence == 2 is seek from end
  66. raise NotImplementedError
  67. def seekable(self):
  68. return True
  69. def write(self, data):
  70. result = self._process.WriteMemory(self._offset, data, self._SBError)
  71. if self._SBError.fail:
  72. raise MemoryError(
  73. f'lldb could not write memory to 0x{self._offset:08x}')
  74. return result
  75. def writable(self):
  76. return True
  77. def truncate(self, size=None):
  78. raise NotImplementedError
  79. def flush(self):
  80. raise NotImplementedError
  81. def fileno(self):
  82. raise NotImplementedError
  83. class EfiSymbols:
  84. """
  85. Class to manage EFI Symbols
  86. You need to pass file, and exe_ctx to load symbols.
  87. You can print(EfiSymbols()) to see the currently loaded symbols
  88. """
  89. loaded = {}
  90. stride = None
  91. range = None
  92. verbose = False
  93. def __init__(self, target=None):
  94. if target:
  95. EfiSymbols.target = target
  96. EfiSymbols._file = LldbFileObject(target.process)
  97. @ classmethod
  98. def __str__(cls):
  99. return ''.join(f'{pecoff}\n' for (pecoff, _) in cls.loaded.values())
  100. @ classmethod
  101. def configure_search(cls, stride, range, verbose=False):
  102. cls.stride = stride
  103. cls.range = range
  104. cls.verbose = verbose
  105. @ classmethod
  106. def clear(cls):
  107. cls.loaded = {}
  108. @ classmethod
  109. def add_symbols_for_pecoff(cls, pecoff):
  110. '''Tell lldb the location of the .text and .data sections.'''
  111. if pecoff.LoadAddress in cls.loaded:
  112. return 'Already Loaded: '
  113. module = cls.target.AddModule(None, None, str(pecoff.CodeViewUuid))
  114. if not module:
  115. module = cls.target.AddModule(pecoff.CodeViewPdb,
  116. None,
  117. str(pecoff.CodeViewUuid))
  118. if module.IsValid():
  119. SBError = cls.target.SetModuleLoadAddress(
  120. module, pecoff.LoadAddress + pecoff.TeAdjust)
  121. if SBError.success:
  122. cls.loaded[pecoff.LoadAddress] = (pecoff, module)
  123. return ''
  124. return 'Symbols NOT FOUND: '
  125. @ classmethod
  126. def address_to_symbols(cls, address, reprobe=False):
  127. '''
  128. Given an address search backwards for a PE/COFF (or TE) header
  129. and load symbols. Return a status string.
  130. '''
  131. if not isinstance(address, int):
  132. address = int(address)
  133. pecoff, _ = cls.address_in_loaded_pecoff(address)
  134. if not reprobe and pecoff is not None:
  135. # skip the probe of the remote
  136. return f'{pecoff} is already loaded'
  137. pecoff = PeTeImage(cls._file, None)
  138. if pecoff.pcToPeCoff(address, cls.stride, cls.range):
  139. res = cls.add_symbols_for_pecoff(pecoff)
  140. return f'{res}{pecoff}'
  141. else:
  142. return f'0x{address:08x} not in a PE/COFF (or TE) image'
  143. @ classmethod
  144. def address_in_loaded_pecoff(cls, address):
  145. if not isinstance(address, int):
  146. address = int(address)
  147. for (pecoff, module) in cls.loaded.values():
  148. if (address >= pecoff.LoadAddress and
  149. address <= pecoff.EndLoadAddress):
  150. return pecoff, module
  151. return None, None
  152. @ classmethod
  153. def unload_symbols(cls, address):
  154. pecoff, module = cls.address_in_loaded_pecoff(address)
  155. if module:
  156. name = str(module)
  157. cls.target.ClearModuleLoadAddress(module)
  158. cls.target.RemoveModule(module)
  159. del cls.loaded[pecoff.LoadAddress]
  160. return f'{name:s} was unloaded'
  161. return f'0x{address:x} was not in a loaded image'
  162. def arg_to_address(frame, arg):
  163. ''' convert an lldb command arg into a memory address (addr_t)'''
  164. if arg is None:
  165. return None
  166. arg_str = arg if isinstance(arg, str) else str(arg)
  167. SBValue = frame.EvaluateExpression(arg_str)
  168. if SBValue.error.fail:
  169. return arg
  170. if (SBValue.TypeIsPointerType() or
  171. SBValue.value_type == lldb.eValueTypeRegister or
  172. SBValue.value_type == lldb.eValueTypeRegisterSet or
  173. SBValue.value_type == lldb.eValueTypeConstResult):
  174. try:
  175. addr = SBValue.GetValueAsAddress()
  176. except ValueError:
  177. addr = SBValue.unsigned
  178. else:
  179. try:
  180. addr = SBValue.address_of.GetValueAsAddress()
  181. except ValueError:
  182. addr = SBValue.address_of.unsigned
  183. return addr
  184. def arg_to_data(frame, arg):
  185. ''' convert an lldb command arg into a data vale (uint32_t/uint64_t)'''
  186. if not isinstance(arg, str):
  187. arg_str = str(str)
  188. SBValue = frame.EvaluateExpression(arg_str)
  189. return SBValue.unsigned
  190. class EfiDevicePathCommand:
  191. def create_options(self):
  192. ''' standard lldb command help/options parser'''
  193. usage = "usage: %prog [options]"
  194. description = '''Command that can EFI Config Tables
  195. '''
  196. # Pass add_help_option = False, since this keeps the command in line
  197. # with lldb commands, and we wire up "help command" to work by
  198. # providing the long & short help methods below.
  199. self.parser = optparse.OptionParser(
  200. description=description,
  201. prog='devicepath',
  202. usage=usage,
  203. add_help_option=False)
  204. self.parser.add_option(
  205. '-v',
  206. '--verbose',
  207. action='store_true',
  208. dest='verbose',
  209. help='hex dump extra data',
  210. default=False)
  211. self.parser.add_option(
  212. '-n',
  213. '--node',
  214. action='store_true',
  215. dest='node',
  216. help='dump a single device path node',
  217. default=False)
  218. self.parser.add_option(
  219. '-h',
  220. '--help',
  221. action='store_true',
  222. dest='help',
  223. help='Show help for the command',
  224. default=False)
  225. def get_short_help(self):
  226. '''standard lldb function method'''
  227. return "Display EFI Tables"
  228. def get_long_help(self):
  229. '''standard lldb function method'''
  230. return self.help_string
  231. def __init__(self, debugger, internal_dict):
  232. '''standard lldb function method'''
  233. self.create_options()
  234. self.help_string = self.parser.format_help()
  235. def __call__(self, debugger, command, exe_ctx, result):
  236. '''standard lldb function method'''
  237. # Use the Shell Lexer to properly parse up command options just like a
  238. # shell would
  239. command_args = shlex.split(command)
  240. try:
  241. (options, args) = self.parser.parse_args(command_args)
  242. dev_list = []
  243. for arg in args:
  244. dev_list.append(arg_to_address(exe_ctx.frame, arg))
  245. except ValueError:
  246. # if you don't handle exceptions, passing an incorrect argument
  247. # to the OptionParser will cause LLDB to exit (courtesy of
  248. # OptParse dealing with argument errors by throwing SystemExit)
  249. result.SetError("option parsing failed")
  250. return
  251. if options.help:
  252. self.parser.print_help()
  253. return
  254. file = LldbFileObject(exe_ctx.process)
  255. for dev_addr in dev_list:
  256. if options.node:
  257. print(EfiDevicePath(file).device_path_node_str(
  258. dev_addr, options.verbose))
  259. else:
  260. device_path = EfiDevicePath(file, dev_addr, options.verbose)
  261. if device_path.valid():
  262. print(device_path)
  263. class EfiHobCommand:
  264. def create_options(self):
  265. ''' standard lldb command help/options parser'''
  266. usage = "usage: %prog [options]"
  267. description = '''Command that can EFI dump EFI HOBs'''
  268. # Pass add_help_option = False, since this keeps the command in line
  269. # with lldb commands, and we wire up "help command" to work by
  270. # providing the long & short help methods below.
  271. self.parser = optparse.OptionParser(
  272. description=description,
  273. prog='table',
  274. usage=usage,
  275. add_help_option=False)
  276. self.parser.add_option(
  277. '-a',
  278. '--address',
  279. type="int",
  280. dest='address',
  281. help='Parse HOBs from address',
  282. default=None)
  283. self.parser.add_option(
  284. '-t',
  285. '--type',
  286. type="int",
  287. dest='type',
  288. help='Only dump HOBS of his type',
  289. default=None)
  290. self.parser.add_option(
  291. '-v',
  292. '--verbose',
  293. action='store_true',
  294. dest='verbose',
  295. help='hex dump extra data',
  296. default=False)
  297. self.parser.add_option(
  298. '-h',
  299. '--help',
  300. action='store_true',
  301. dest='help',
  302. help='Show help for the command',
  303. default=False)
  304. def get_short_help(self):
  305. '''standard lldb function method'''
  306. return "Display EFI Hobs"
  307. def get_long_help(self):
  308. '''standard lldb function method'''
  309. return self.help_string
  310. def __init__(self, debugger, internal_dict):
  311. '''standard lldb function method'''
  312. self.create_options()
  313. self.help_string = self.parser.format_help()
  314. def __call__(self, debugger, command, exe_ctx, result):
  315. '''standard lldb function method'''
  316. # Use the Shell Lexer to properly parse up command options just like a
  317. # shell would
  318. command_args = shlex.split(command)
  319. try:
  320. (options, _) = self.parser.parse_args(command_args)
  321. except ValueError:
  322. # if you don't handle exceptions, passing an incorrect argument
  323. # to the OptionParser will cause LLDB to exit (courtesy of
  324. # OptParse dealing with argument errors by throwing SystemExit)
  325. result.SetError("option parsing failed")
  326. return
  327. if options.help:
  328. self.parser.print_help()
  329. return
  330. address = arg_to_address(exe_ctx.frame, options.address)
  331. file = LldbFileObject(exe_ctx.process)
  332. hob = EfiHob(file, address, options.verbose).get_hob_by_type(
  333. options.type)
  334. print(hob)
  335. class EfiTableCommand:
  336. def create_options(self):
  337. ''' standard lldb command help/options parser'''
  338. usage = "usage: %prog [options]"
  339. description = '''Command that can display EFI Config Tables
  340. '''
  341. # Pass add_help_option = False, since this keeps the command in line
  342. # with lldb commands, and we wire up "help command" to work by
  343. # providing the long & short help methods below.
  344. self.parser = optparse.OptionParser(
  345. description=description,
  346. prog='table',
  347. usage=usage,
  348. add_help_option=False)
  349. self.parser.add_option(
  350. '-h',
  351. '--help',
  352. action='store_true',
  353. dest='help',
  354. help='Show help for the command',
  355. default=False)
  356. def get_short_help(self):
  357. '''standard lldb function method'''
  358. return "Display EFI Tables"
  359. def get_long_help(self):
  360. '''standard lldb function method'''
  361. return self.help_string
  362. def __init__(self, debugger, internal_dict):
  363. '''standard lldb function method'''
  364. self.create_options()
  365. self.help_string = self.parser.format_help()
  366. def __call__(self, debugger, command, exe_ctx, result):
  367. '''standard lldb function method'''
  368. # Use the Shell Lexer to properly parse up command options just like a
  369. # shell would
  370. command_args = shlex.split(command)
  371. try:
  372. (options, _) = self.parser.parse_args(command_args)
  373. except ValueError:
  374. # if you don't handle exceptions, passing an incorrect argument
  375. # to the OptionParser will cause LLDB to exit (courtesy of
  376. # OptParse dealing with argument errors by throwing SystemExit)
  377. result.SetError("option parsing failed")
  378. return
  379. if options.help:
  380. self.parser.print_help()
  381. return
  382. gST = exe_ctx.target.FindFirstGlobalVariable('gST')
  383. if gST.error.fail:
  384. print('Error: This command requires symbols for gST to be loaded')
  385. return
  386. file = LldbFileObject(exe_ctx.process)
  387. table = EfiConfigurationTable(file, gST.unsigned)
  388. if table:
  389. print(table, '\n')
  390. class EfiGuidCommand:
  391. def create_options(self):
  392. ''' standard lldb command help/options parser'''
  393. usage = "usage: %prog [options]"
  394. description = '''
  395. Command that can display all EFI GUID's or give info on a
  396. specific GUID's
  397. '''
  398. self.parser = optparse.OptionParser(
  399. description=description,
  400. prog='guid',
  401. usage=usage,
  402. add_help_option=False)
  403. self.parser.add_option(
  404. '-n',
  405. '--new',
  406. action='store_true',
  407. dest='new',
  408. help='Generate a new GUID',
  409. default=False)
  410. self.parser.add_option(
  411. '-v',
  412. '--verbose',
  413. action='store_true',
  414. dest='verbose',
  415. help='Also display GUID C structure values',
  416. default=False)
  417. self.parser.add_option(
  418. '-h',
  419. '--help',
  420. action='store_true',
  421. dest='help',
  422. help='Show help for the command',
  423. default=False)
  424. def get_short_help(self):
  425. '''standard lldb function method'''
  426. return "Display EFI GUID's"
  427. def get_long_help(self):
  428. '''standard lldb function method'''
  429. return self.help_string
  430. def __init__(self, debugger, internal_dict):
  431. '''standard lldb function method'''
  432. self.create_options()
  433. self.help_string = self.parser.format_help()
  434. def __call__(self, debugger, command, exe_ctx, result):
  435. '''standard lldb function method'''
  436. # Use the Shell Lexer to properly parse up command options just like a
  437. # shell would
  438. command_args = shlex.split(command)
  439. try:
  440. (options, args) = self.parser.parse_args(command_args)
  441. if len(args) >= 1:
  442. # guid { 0x414e6bdd, 0xe47b, 0x47cc,
  443. # { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }}
  444. # this generates multiple args
  445. arg = ' '.join(args)
  446. except ValueError:
  447. # if you don't handle exceptions, passing an incorrect argument
  448. # to the OptionParser will cause LLDB to exit (courtesy of
  449. # OptParse dealing with argument errors by throwing SystemExit)
  450. result.SetError("option parsing failed")
  451. return
  452. if options.help:
  453. self.parser.print_help()
  454. return
  455. if options.new:
  456. guid = uuid.uuid4()
  457. print(str(guid).upper())
  458. print(GuidNames.to_c_guid(guid))
  459. return
  460. if len(args) > 0:
  461. if GuidNames.is_guid_str(arg):
  462. # guid 05AD34BA-6F02-4214-952E-4DA0398E2BB9
  463. key = arg.lower()
  464. name = GuidNames.to_name(key)
  465. elif GuidNames.is_c_guid(arg):
  466. # guid { 0x414e6bdd, 0xe47b, 0x47cc,
  467. # { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }}
  468. key = GuidNames.from_c_guid(arg)
  469. name = GuidNames.to_name(key)
  470. else:
  471. # guid gEfiDxeServicesTableGuid
  472. name = arg
  473. try:
  474. key = GuidNames.to_guid(name)
  475. name = GuidNames.to_name(key)
  476. except ValueError:
  477. return
  478. extra = f'{GuidNames.to_c_guid(key)}: ' if options.verbose else ''
  479. print(f'{key}: {extra}{name}')
  480. else:
  481. for key, value in GuidNames._dict_.items():
  482. if options.verbose:
  483. extra = f'{GuidNames.to_c_guid(key)}: '
  484. else:
  485. extra = ''
  486. print(f'{key}: {extra}{value}')
  487. class EfiSymbolicateCommand(object):
  488. '''Class to abstract an lldb command'''
  489. def create_options(self):
  490. ''' standard lldb command help/options parser'''
  491. usage = "usage: %prog [options]"
  492. description = '''Command that can load EFI PE/COFF and TE image
  493. symbols. If you are having trouble in PEI try adding --pei.
  494. '''
  495. # Pass add_help_option = False, since this keeps the command in line
  496. # with lldb commands, and we wire up "help command" to work by
  497. # providing the long & short help methods below.
  498. self.parser = optparse.OptionParser(
  499. description=description,
  500. prog='efi_symbols',
  501. usage=usage,
  502. add_help_option=False)
  503. self.parser.add_option(
  504. '-a',
  505. '--address',
  506. type="int",
  507. dest='address',
  508. help='Load symbols for image at address',
  509. default=None)
  510. self.parser.add_option(
  511. '-f',
  512. '--frame',
  513. action='store_true',
  514. dest='frame',
  515. help='Load symbols for current stack frame',
  516. default=False)
  517. self.parser.add_option(
  518. '-p',
  519. '--pc',
  520. action='store_true',
  521. dest='pc',
  522. help='Load symbols for pc',
  523. default=False)
  524. self.parser.add_option(
  525. '--pei',
  526. action='store_true',
  527. dest='pei',
  528. help='Load symbols for PEI (searches every 4 bytes)',
  529. default=False)
  530. self.parser.add_option(
  531. '-e',
  532. '--extended',
  533. action='store_true',
  534. dest='extended',
  535. help='Try to load all symbols based on config tables.',
  536. default=False)
  537. self.parser.add_option(
  538. '-r',
  539. '--range',
  540. type="long",
  541. dest='range',
  542. help='How far to search backward for start of PE/COFF Image',
  543. default=None)
  544. self.parser.add_option(
  545. '-s',
  546. '--stride',
  547. type="long",
  548. dest='stride',
  549. help='Boundary to search for PE/COFF header',
  550. default=None)
  551. self.parser.add_option(
  552. '-t',
  553. '--thread',
  554. action='store_true',
  555. dest='thread',
  556. help='Load symbols for the frames of all threads',
  557. default=False)
  558. self.parser.add_option(
  559. '-h',
  560. '--help',
  561. action='store_true',
  562. dest='help',
  563. help='Show help for the command',
  564. default=False)
  565. def get_short_help(self):
  566. '''standard lldb function method'''
  567. return (
  568. "Load symbols based on an address that is part of"
  569. " a PE/COFF EFI image.")
  570. def get_long_help(self):
  571. '''standard lldb function method'''
  572. return self.help_string
  573. def __init__(self, debugger, unused):
  574. '''standard lldb function method'''
  575. self.create_options()
  576. self.help_string = self.parser.format_help()
  577. def lldb_print(self, lldb_str):
  578. # capture command out like an lldb command
  579. self.result.PutCString(lldb_str)
  580. # flush the output right away
  581. self.result.SetImmediateOutputFile(
  582. self.exe_ctx.target.debugger.GetOutputFile())
  583. def __call__(self, debugger, command, exe_ctx, result):
  584. '''standard lldb function method'''
  585. # Use the Shell Lexer to properly parse up command options just like a
  586. # shell would
  587. command_args = shlex.split(command)
  588. try:
  589. (options, _) = self.parser.parse_args(command_args)
  590. except ValueError:
  591. # if you don't handle exceptions, passing an incorrect argument
  592. # to the OptionParser will cause LLDB to exit (courtesy of
  593. # OptParse dealing with argument errors by throwing SystemExit)
  594. result.SetError("option parsing failed")
  595. return
  596. if options.help:
  597. self.parser.print_help()
  598. return
  599. file = LldbFileObject(exe_ctx.process)
  600. efi_symbols = EfiSymbols(exe_ctx.target)
  601. self.result = result
  602. self.exe_ctx = exe_ctx
  603. if options.pei:
  604. # XIP code ends up on a 4 byte boundary.
  605. options.stride = 4
  606. options.range = 0x100000
  607. efi_symbols.configure_search(options.stride, options.range)
  608. if not options.pc and options.address is None:
  609. # default to
  610. options.frame = True
  611. if options.frame:
  612. if not exe_ctx.frame.IsValid():
  613. result.SetError("invalid frame")
  614. return
  615. threads = exe_ctx.process.threads if options.thread else [
  616. exe_ctx.thread]
  617. for thread in threads:
  618. for frame in thread:
  619. res = efi_symbols.address_to_symbols(frame.pc)
  620. self.lldb_print(res)
  621. else:
  622. if options.address is not None:
  623. address = options.address
  624. elif options.pc:
  625. try:
  626. address = exe_ctx.thread.GetSelectedFrame().pc
  627. except ValueError:
  628. result.SetError("invalid pc")
  629. return
  630. else:
  631. address = 0
  632. res = efi_symbols.address_to_symbols(address.pc)
  633. print(res)
  634. if options.extended:
  635. gST = exe_ctx.target.FindFirstGlobalVariable('gST')
  636. if gST.error.fail:
  637. print('Error: This command requires symbols to be loaded')
  638. else:
  639. table = EfiConfigurationTable(file, gST.unsigned)
  640. for address, _ in table.DebugImageInfo():
  641. res = efi_symbols.address_to_symbols(address)
  642. self.lldb_print(res)
  643. # keep trying module file names until we find a GUID xref file
  644. for m in exe_ctx.target.modules:
  645. if GuidNames.add_build_guid_file(str(m.file)):
  646. break
  647. def CHAR16_TypeSummary(valobj, internal_dict):
  648. '''
  649. Display CHAR16 as a String in the debugger.
  650. Note: utf-8 is returned as that is the value for the debugger.
  651. '''
  652. SBError = lldb.SBError()
  653. Str = ''
  654. if valobj.TypeIsPointerType():
  655. if valobj.GetValueAsUnsigned() == 0:
  656. return "NULL"
  657. # CHAR16 * max string size 1024
  658. for i in range(1024):
  659. Char = valobj.GetPointeeData(i, 1).GetUnsignedInt16(SBError, 0)
  660. if SBError.fail or Char == 0:
  661. break
  662. Str += chr(Char)
  663. return 'L"' + Str + '"'
  664. if valobj.num_children == 0:
  665. # CHAR16
  666. return "L'" + chr(valobj.unsigned) + "'"
  667. else:
  668. # CHAR16 []
  669. for i in range(valobj.num_children):
  670. Char = valobj.GetChildAtIndex(i).data.GetUnsignedInt16(SBError, 0)
  671. if Char == 0:
  672. break
  673. Str += chr(Char)
  674. return 'L"' + Str + '"'
  675. return Str
  676. def CHAR8_TypeSummary(valobj, internal_dict):
  677. '''
  678. Display CHAR8 as a String in the debugger.
  679. Note: utf-8 is returned as that is the value for the debugger.
  680. '''
  681. SBError = lldb.SBError()
  682. Str = ''
  683. if valobj.TypeIsPointerType():
  684. if valobj.GetValueAsUnsigned() == 0:
  685. return "NULL"
  686. # CHAR8 * max string size 1024
  687. for i in range(1024):
  688. Char = valobj.GetPointeeData(i, 1).GetUnsignedInt8(SBError, 0)
  689. if SBError.fail or Char == 0:
  690. break
  691. Str += chr(Char)
  692. Str = '"' + Str + '"'
  693. return Str
  694. if valobj.num_children == 0:
  695. # CHAR8
  696. return "'" + chr(valobj.unsigned) + "'"
  697. else:
  698. # CHAR8 []
  699. for i in range(valobj.num_children):
  700. Char = valobj.GetChildAtIndex(i).data.GetUnsignedInt8(SBError, 0)
  701. if SBError.fail or Char == 0:
  702. break
  703. Str += chr(Char)
  704. return '"' + Str + '"'
  705. return Str
  706. def EFI_STATUS_TypeSummary(valobj, internal_dict):
  707. if valobj.TypeIsPointerType():
  708. return ''
  709. return str(EfiStatusClass(valobj.unsigned))
  710. def EFI_TPL_TypeSummary(valobj, internal_dict):
  711. if valobj.TypeIsPointerType():
  712. return ''
  713. return str(EfiTpl(valobj.unsigned))
  714. def EFI_GUID_TypeSummary(valobj, internal_dict):
  715. if valobj.TypeIsPointerType():
  716. return ''
  717. return str(GuidNames(bytes(valobj.data.uint8)))
  718. def EFI_BOOT_MODE_TypeSummary(valobj, internal_dict):
  719. if valobj.TypeIsPointerType():
  720. return ''
  721. '''Return #define name for EFI_BOOT_MODE'''
  722. return str(EfiBootMode(valobj.unsigned))
  723. def lldb_type_formaters(debugger, mod_name):
  724. '''Teach lldb about EFI types'''
  725. category = debugger.GetDefaultCategory()
  726. FormatBool = lldb.SBTypeFormat(lldb.eFormatBoolean)
  727. category.AddTypeFormat(lldb.SBTypeNameSpecifier("BOOLEAN"), FormatBool)
  728. FormatHex = lldb.SBTypeFormat(lldb.eFormatHex)
  729. category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINT64"), FormatHex)
  730. category.AddTypeFormat(lldb.SBTypeNameSpecifier("INT64"), FormatHex)
  731. category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINT32"), FormatHex)
  732. category.AddTypeFormat(lldb.SBTypeNameSpecifier("INT32"), FormatHex)
  733. category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINT16"), FormatHex)
  734. category.AddTypeFormat(lldb.SBTypeNameSpecifier("INT16"), FormatHex)
  735. category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINT8"), FormatHex)
  736. category.AddTypeFormat(lldb.SBTypeNameSpecifier("INT8"), FormatHex)
  737. category.AddTypeFormat(lldb.SBTypeNameSpecifier("UINTN"), FormatHex)
  738. category.AddTypeFormat(lldb.SBTypeNameSpecifier("INTN"), FormatHex)
  739. category.AddTypeFormat(lldb.SBTypeNameSpecifier("CHAR8"), FormatHex)
  740. category.AddTypeFormat(lldb.SBTypeNameSpecifier("CHAR16"), FormatHex)
  741. category.AddTypeFormat(lldb.SBTypeNameSpecifier(
  742. "EFI_PHYSICAL_ADDRESS"), FormatHex)
  743. category.AddTypeFormat(lldb.SBTypeNameSpecifier(
  744. "PHYSICAL_ADDRESS"), FormatHex)
  745. category.AddTypeFormat(lldb.SBTypeNameSpecifier("EFI_LBA"), FormatHex)
  746. category.AddTypeFormat(
  747. lldb.SBTypeNameSpecifier("EFI_BOOT_MODE"), FormatHex)
  748. category.AddTypeFormat(lldb.SBTypeNameSpecifier(
  749. "EFI_FV_FILETYPE"), FormatHex)
  750. #
  751. # Smart type printing for EFI
  752. #
  753. debugger.HandleCommand(
  754. f'type summary add GUID - -python-function '
  755. f'{mod_name}.EFI_GUID_TypeSummary')
  756. debugger.HandleCommand(
  757. f'type summary add EFI_GUID --python-function '
  758. f'{mod_name}.EFI_GUID_TypeSummary')
  759. debugger.HandleCommand(
  760. f'type summary add EFI_STATUS --python-function '
  761. f'{mod_name}.EFI_STATUS_TypeSummary')
  762. debugger.HandleCommand(
  763. f'type summary add EFI_TPL - -python-function '
  764. f'{mod_name}.EFI_TPL_TypeSummary')
  765. debugger.HandleCommand(
  766. f'type summary add EFI_BOOT_MODE --python-function '
  767. f'{mod_name}.EFI_BOOT_MODE_TypeSummary')
  768. debugger.HandleCommand(
  769. f'type summary add CHAR16 --python-function '
  770. f'{mod_name}.CHAR16_TypeSummary')
  771. # W605 this is the correct escape sequence for the lldb command
  772. debugger.HandleCommand(
  773. f'type summary add --regex "CHAR16 \[[0-9]+\]" ' # noqa: W605
  774. f'--python-function {mod_name}.CHAR16_TypeSummary')
  775. debugger.HandleCommand(
  776. f'type summary add CHAR8 --python-function '
  777. f'{mod_name}.CHAR8_TypeSummary')
  778. # W605 this is the correct escape sequence for the lldb command
  779. debugger.HandleCommand(
  780. f'type summary add --regex "CHAR8 \[[0-9]+\]" ' # noqa: W605
  781. f'--python-function {mod_name}.CHAR8_TypeSummary')
  782. class LldbWorkaround:
  783. needed = True
  784. @classmethod
  785. def activate(cls):
  786. if cls.needed:
  787. lldb.debugger.HandleCommand("process handle SIGALRM -n false")
  788. cls.needed = False
  789. def LoadEmulatorEfiSymbols(frame, bp_loc, internal_dict):
  790. #
  791. # This is an lldb breakpoint script, and assumes the breakpoint is on a
  792. # function with the same prototype as SecGdbScriptBreak(). The
  793. # argument names are important as lldb looks them up.
  794. #
  795. # VOID
  796. # SecGdbScriptBreak (
  797. # char *FileName,
  798. # int FileNameLength,
  799. # long unsigned int LoadAddress,
  800. # int AddSymbolFlag
  801. # )
  802. # {
  803. # return;
  804. # }
  805. #
  806. # When the emulator loads a PE/COFF image, it calls the stub function with
  807. # the filename of the symbol file, the length of the FileName, the
  808. # load address and a flag to indicate if this is a load or unload operation
  809. #
  810. LldbWorkaround().activate()
  811. symbols = EfiSymbols(frame.thread.process.target)
  812. LoadAddress = frame.FindVariable("LoadAddress").unsigned
  813. if frame.FindVariable("AddSymbolFlag").unsigned == 1:
  814. res = symbols.address_to_symbols(LoadAddress)
  815. else:
  816. res = symbols.unload_symbols(LoadAddress)
  817. print(res)
  818. # make breakpoint command continue
  819. return False
  820. def __lldb_init_module(debugger, internal_dict):
  821. '''
  822. This initializer is being run from LLDB in the embedded command interpreter
  823. '''
  824. mod_name = Path(__file__).stem
  825. lldb_type_formaters(debugger, mod_name)
  826. # Add any commands contained in this module to LLDB
  827. debugger.HandleCommand(
  828. f'command script add -c {mod_name}.EfiSymbolicateCommand efi_symbols')
  829. debugger.HandleCommand(
  830. f'command script add -c {mod_name}.EfiGuidCommand guid')
  831. debugger.HandleCommand(
  832. f'command script add -c {mod_name}.EfiTableCommand table')
  833. debugger.HandleCommand(
  834. f'command script add -c {mod_name}.EfiHobCommand hob')
  835. debugger.HandleCommand(
  836. f'command script add -c {mod_name}.EfiDevicePathCommand devicepath')
  837. print('EFI specific commands have been installed.')
  838. # patch the ctypes c_void_p values if the debuggers OS and EFI have
  839. # different ideas on the size of the debug.
  840. try:
  841. patch_ctypes(debugger.GetSelectedTarget().addr_size)
  842. except ValueError:
  843. # incase the script is imported and the debugger has not target
  844. # defaults to sizeof(UINTN) == sizeof(UINT64)
  845. patch_ctypes()
  846. try:
  847. target = debugger.GetSelectedTarget()
  848. if target.FindFunctions('SecGdbScriptBreak').symbols:
  849. breakpoint = target.BreakpointCreateByName('SecGdbScriptBreak')
  850. # Set the emulator breakpoints, if we are in the emulator
  851. cmd = 'breakpoint command add -s python -F '
  852. cmd += f'efi_lldb.LoadEmulatorEfiSymbols {breakpoint.GetID()}'
  853. debugger.HandleCommand(cmd)
  854. print('Type r to run emulator.')
  855. else:
  856. raise ValueError("No Emulator Symbols")
  857. except ValueError:
  858. # default action when the script is imported
  859. debugger.HandleCommand("efi_symbols --frame --extended")
  860. debugger.HandleCommand("register read")
  861. debugger.HandleCommand("bt all")
  862. if __name__ == '__main__':
  863. pass