]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Scripts/efi_gdb.py
UefiCpuPkg: Fix SMM code hangs when InitPaging
[mirror_edk2.git] / BaseTools / Scripts / efi_gdb.py
CommitLineData
0d7fec9f
RC
1#!/usr/bin/python3\r
2'''\r
3Copyright 2021 (c) Apple Inc. All rights reserved.\r
4SPDX-License-Identifier: BSD-2-Clause-Patent\r
5\r
6EFI gdb commands based on efi_debugging classes.\r
7\r
8Example usage:\r
9OvmfPkg/build.sh qemu -gdb tcp::9000\r
10gdb -ex "target remote localhost:9000" -ex "source efi_gdb.py"\r
11\r
12(gdb) help efi\r
13Commands for debugging EFI. efi <cmd>\r
14\r
15List of efi subcommands:\r
16\r
17efi devicepath -- Display an EFI device path.\r
18efi guid -- Display info about EFI GUID's.\r
19efi hob -- Dump EFI HOBs. Type 'hob -h' for more info.\r
20efi symbols -- Load Symbols for EFI. Type 'efi_symbols -h' for more info.\r
21efi table -- Dump EFI System Tables. Type 'table -h' for more info.\r
22\r
23This module is coded against a generic gdb remote serial stub. It should work\r
24with QEMU, JTAG debugger, or a generic EFI gdb remote serial stub.\r
25\r
26If you are debugging with QEMU or a JTAG hardware debugger you can insert\r
27a CpuDeadLoop(); in your code, attach with gdb, and then `p Index=1` to\r
28step past. If you have a debug stub in EFI you can use CpuBreakpoint();.\r
29'''\r
30\r
31from gdb.printing import RegexpCollectionPrettyPrinter\r
32from gdb.printing import register_pretty_printer\r
33import gdb\r
34import os\r
35import sys\r
36import uuid\r
37import optparse\r
38import shlex\r
39\r
40# gdb will not import from the same path as this script.\r
41# so lets fix that for gdb...\r
42sys.path.append(os.path.dirname(os.path.abspath(__file__)))\r
43\r
44from efi_debugging import PeTeImage, patch_ctypes # noqa: E402\r
45from efi_debugging import EfiHob, GuidNames, EfiStatusClass # noqa: E402\r
46from efi_debugging import EfiBootMode, EfiDevicePath # noqa: E402\r
47from efi_debugging import EfiConfigurationTable, EfiTpl # noqa: E402\r
48\r
49\r
50class GdbFileObject(object):\r
51 '''Provide a file like object required by efi_debugging'''\r
52\r
53 def __init__(self):\r
54 self.inferior = gdb.selected_inferior()\r
55 self.offset = 0\r
56\r
57 def tell(self):\r
58 return self.offset\r
59\r
60 def read(self, size=-1):\r
61 if size == -1:\r
62 # arbitrary default size\r
63 size = 0x1000000\r
64\r
65 try:\r
66 data = self.inferior.read_memory(self.offset, size)\r
67 except MemoryError:\r
68 data = bytearray(size)\r
69 assert False\r
70 if len(data) != size:\r
71 raise MemoryError(\r
72 f'gdb could not read memory 0x{size:x}'\r
73 + f' bytes from 0x{self.offset:08x}')\r
74 else:\r
75 # convert memoryview object to a bytestring.\r
76 return data.tobytes()\r
77\r
78 def readable(self):\r
79 return True\r
80\r
81 def seek(self, offset, whence=0):\r
82 if whence == 0:\r
83 self.offset = offset\r
84 elif whence == 1:\r
85 self.offset += offset\r
86 else:\r
87 # whence == 2 is seek from end\r
88 raise NotImplementedError\r
89\r
90 def seekable(self):\r
91 return True\r
92\r
93 def write(self, data):\r
94 self.inferior.write_memory(self.offset, data)\r
95 return len(data)\r
96\r
97 def writable(self):\r
98 return True\r
99\r
100 def truncate(self, size=None):\r
101 raise NotImplementedError\r
102\r
103 def flush(self):\r
104 raise NotImplementedError\r
105\r
106 def fileno(self):\r
107 raise NotImplementedError\r
108\r
109\r
110class EfiSymbols:\r
111 """Class to manage EFI Symbols"""\r
112\r
113 loaded = {}\r
114 stride = None\r
115 range = None\r
116 verbose = False\r
117\r
118 def __init__(self, file=None):\r
119 EfiSymbols.file = file if file else GdbFileObject()\r
120\r
121 @ classmethod\r
122 def __str__(cls):\r
123 return ''.join(f'{value}\n' for value in cls.loaded.values())\r
124\r
125 @ classmethod\r
126 def configure_search(cls, stride, range=None, verbose=False):\r
127 cls.stride = stride\r
128 cls.range = range\r
129 cls.verbose = verbose\r
130\r
131 @ classmethod\r
132 def clear(cls):\r
133 cls.loaded = {}\r
134\r
135 @ classmethod\r
136 def add_symbols_for_pecoff(cls, pecoff):\r
137 '''Tell lldb the location of the .text and .data sections.'''\r
138\r
139 if pecoff.TextAddress in cls.loaded:\r
140 return 'Already Loaded: '\r
141 try:\r
142 res = 'Loading Symbols Failed:'\r
143 res = gdb.execute('add-symbol-file ' + pecoff.CodeViewPdb +\r
144 ' ' + hex(pecoff.TextAddress) +\r
145 ' -s .data ' + hex(pecoff.DataAddress),\r
146 False, True)\r
147\r
148 cls.loaded[pecoff.TextAddress] = pecoff\r
149 if cls.verbose:\r
150 print(f'\n{res:s}\n')\r
151 return ''\r
152 except gdb.error:\r
153 return res\r
154\r
155 @ classmethod\r
156 def address_to_symbols(cls, address, reprobe=False):\r
157 '''\r
158 Given an address search backwards for a PE/COFF (or TE) header\r
159 and load symbols. Return a status string.\r
160 '''\r
161 if not isinstance(address, int):\r
162 address = int(address)\r
163\r
164 pecoff = cls.address_in_loaded_pecoff(address)\r
165 if not reprobe and pecoff is not None:\r
166 # skip the probe of the remote\r
167 return f'{pecoff} is already loaded'\r
168\r
169 pecoff = PeTeImage(cls.file, None)\r
170 if pecoff.pcToPeCoff(address, cls.stride, cls.range):\r
171 res = cls.add_symbols_for_pecoff(pecoff)\r
172 return f'{res}{pecoff}'\r
173 else:\r
174 return f'0x{address:08x} not in a PE/COFF (or TE) image'\r
175\r
176 @ classmethod\r
177 def address_in_loaded_pecoff(cls, address):\r
178 if not isinstance(address, int):\r
179 address = int(address)\r
180\r
181 for value in cls.loaded.values():\r
182 if (address >= value.LoadAddress and\r
183 address <= value.EndLoadAddress):\r
184 return value\r
185\r
186 return None\r
187\r
188 @ classmethod\r
189 def unload_symbols(cls, address):\r
190 if not isinstance(address, int):\r
191 address = int(address)\r
192\r
193 pecoff = cls.address_in_loaded_pecoff(address)\r
194 try:\r
195 res = 'Unloading Symbols Failed:'\r
196 res = gdb.execute(\r
197 f'remove-symbol-file -a {hex(pecoff.TextAddress):s}',\r
198 False, True)\r
199 del cls.loaded[pecoff.LoadAddress]\r
200 return res\r
201 except gdb.error:\r
202 return res\r
203\r
204\r
205class CHAR16_PrettyPrinter(object):\r
206\r
207 def __init__(self, val):\r
208 self.val = val\r
209\r
210 def to_string(self):\r
211 if int(self.val) < 0x20:\r
212 return f"L'\\x{int(self.val):02x}'"\r
213 else:\r
214 return f"L'{chr(self.val):s}'"\r
215\r
216\r
217class EFI_TPL_PrettyPrinter(object):\r
218\r
219 def __init__(self, val):\r
220 self.val = val\r
221\r
222 def to_string(self):\r
223 return str(EfiTpl(int(self.val)))\r
224\r
225\r
226class EFI_STATUS_PrettyPrinter(object):\r
227\r
228 def __init__(self, val):\r
229 self.val = val\r
230\r
231 def to_string(self):\r
232 status = int(self.val)\r
233 return f'{str(EfiStatusClass(status)):s} (0x{status:08x})'\r
234\r
235\r
236class EFI_BOOT_MODE_PrettyPrinter(object):\r
237\r
238 def __init__(self, val):\r
239 self.val = val\r
240\r
241 def to_string(self):\r
242 return str(EfiBootMode(int(self.val)))\r
243\r
244\r
245class EFI_GUID_PrettyPrinter(object):\r
246 """Print 'EFI_GUID' as 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'"""\r
247\r
248 def __init__(self, val):\r
249 self.val = val\r
250\r
251 def to_string(self):\r
252 # if we could get a byte like object of *(unsigned char (*)[16])\r
253 # then we could just use uuid.UUID() to convert\r
254 Data1 = int(self.val['Data1'])\r
255 Data2 = int(self.val['Data2'])\r
256 Data3 = int(self.val['Data3'])\r
257 Data4 = self.val['Data4']\r
258 guid = f'{Data1:08X}-{Data2:04X}-'\r
259 guid += f'{Data3:04X}-'\r
260 guid += f'{int(Data4[0]):02X}{int(Data4[1]):02X}-'\r
261 guid += f'{int(Data4[2]):02X}{int(Data4[3]):02X}'\r
262 guid += f'{int(Data4[4]):02X}{int(Data4[5]):02X}'\r
263 guid += f'{int(Data4[6]):02X}{int(Data4[7]):02X}'\r
264 return str(GuidNames(guid))\r
265\r
266\r
267def build_pretty_printer():\r
268 # Turn off via: disable pretty-printer global EFI\r
269 pp = RegexpCollectionPrettyPrinter("EFI")\r
270 # you can also tell gdb `x/sh <address>` to print CHAR16 string\r
271 pp.add_printer('CHAR16', '^CHAR16$', CHAR16_PrettyPrinter)\r
272 pp.add_printer('EFI_BOOT_MODE', '^EFI_BOOT_MODE$',\r
273 EFI_BOOT_MODE_PrettyPrinter)\r
274 pp.add_printer('EFI_GUID', '^EFI_GUID$', EFI_GUID_PrettyPrinter)\r
275 pp.add_printer('EFI_STATUS', '^EFI_STATUS$', EFI_STATUS_PrettyPrinter)\r
276 pp.add_printer('EFI_TPL', '^EFI_TPL$', EFI_TPL_PrettyPrinter)\r
277 return pp\r
278\r
279\r
280class EfiDevicePathCmd (gdb.Command):\r
281 """Display an EFI device path. Type 'efi devicepath -h' for more info"""\r
282\r
283 def __init__(self):\r
284 super(EfiDevicePathCmd, self).__init__(\r
285 "efi devicepath", gdb.COMMAND_NONE)\r
286\r
287 self.file = GdbFileObject()\r
288\r
289 def create_options(self, arg, from_tty):\r
290 usage = "usage: %prog [options] [arg]"\r
291 description = (\r
292 "Command that can load EFI PE/COFF and TE image symbols. ")\r
293\r
294 self.parser = optparse.OptionParser(\r
295 description=description,\r
296 prog='efi devicepath',\r
297 usage=usage,\r
298 add_help_option=False)\r
299\r
300 self.parser.add_option(\r
301 '-v',\r
302 '--verbose',\r
303 action='store_true',\r
304 dest='verbose',\r
305 help='hex dump extra data',\r
306 default=False)\r
307\r
308 self.parser.add_option(\r
309 '-n',\r
310 '--node',\r
311 action='store_true',\r
312 dest='node',\r
313 help='dump a single device path node',\r
314 default=False)\r
315\r
316 self.parser.add_option(\r
317 '-h',\r
318 '--help',\r
319 action='store_true',\r
320 dest='help',\r
321 help='Show help for the command',\r
322 default=False)\r
323\r
324 return self.parser.parse_args(shlex.split(arg))\r
325\r
326 def invoke(self, arg, from_tty):\r
327 '''gdb command to dump EFI device paths'''\r
328\r
329 try:\r
330 (options, _) = self.create_options(arg, from_tty)\r
331 if options.help:\r
332 self.parser.print_help()\r
333 return\r
334\r
335 dev_addr = int(gdb.parse_and_eval(arg))\r
336 except ValueError:\r
337 print("Invalid argument!")\r
338 return\r
339\r
340 if options.node:\r
341 print(EfiDevicePath(\r
342 self.file).device_path_node_str(dev_addr,\r
343 options.verbose))\r
344 else:\r
345 device_path = EfiDevicePath(self.file, dev_addr, options.verbose)\r
346 if device_path.valid():\r
347 print(device_path)\r
348\r
349\r
350class EfiGuidCmd (gdb.Command):\r
351 """Display info about EFI GUID's. Type 'efi guid -h' for more info"""\r
352\r
353 def __init__(self):\r
354 super(EfiGuidCmd, self).__init__("efi guid",\r
355 gdb.COMMAND_NONE,\r
356 gdb.COMPLETE_EXPRESSION)\r
357 self.file = GdbFileObject()\r
358\r
359 def create_options(self, arg, from_tty):\r
360 usage = "usage: %prog [options] [arg]"\r
361 description = (\r
362 "Show EFI_GUID values and the C name of the EFI_GUID variables"\r
363 "in the C code. If symbols are loaded the Guid.xref file"\r
364 "can be processed and the complete GUID database can be shown."\r
365 "This command also suports generating new GUID's, and showing"\r
366 "the value used to initialize the C variable.")\r
367\r
368 self.parser = optparse.OptionParser(\r
369 description=description,\r
370 prog='efi guid',\r
371 usage=usage,\r
372 add_help_option=False)\r
373\r
374 self.parser.add_option(\r
375 '-n',\r
376 '--new',\r
377 action='store_true',\r
378 dest='new',\r
379 help='Generate a new GUID',\r
380 default=False)\r
381\r
382 self.parser.add_option(\r
383 '-v',\r
384 '--verbose',\r
385 action='store_true',\r
386 dest='verbose',\r
387 help='Also display GUID C structure values',\r
388 default=False)\r
389\r
390 self.parser.add_option(\r
391 '-h',\r
392 '--help',\r
393 action='store_true',\r
394 dest='help',\r
395 help='Show help for the command',\r
396 default=False)\r
397\r
398 return self.parser.parse_args(shlex.split(arg))\r
399\r
400 def invoke(self, arg, from_tty):\r
401 '''gdb command to dump EFI System Tables'''\r
402\r
403 try:\r
404 (options, args) = self.create_options(arg, from_tty)\r
405 if options.help:\r
406 self.parser.print_help()\r
407 return\r
408 if len(args) >= 1:\r
409 # guid { 0x414e6bdd, 0xe47b, 0x47cc,\r
410 # { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }}\r
411 # this generates multiple args\r
412 guid = ' '.join(args)\r
413 except ValueError:\r
414 print('bad arguments!')\r
415 return\r
416\r
417 if options.new:\r
418 guid = uuid.uuid4()\r
419 print(str(guid).upper())\r
420 print(GuidNames.to_c_guid(guid))\r
421 return\r
422\r
423 if len(args) > 0:\r
424 if GuidNames.is_guid_str(arg):\r
425 # guid 05AD34BA-6F02-4214-952E-4DA0398E2BB9\r
426 key = guid.upper()\r
427 name = GuidNames.to_name(key)\r
428 elif GuidNames.is_c_guid(arg):\r
429 # guid { 0x414e6bdd, 0xe47b, 0x47cc,\r
430 # { 0xb2, 0x44, 0xbb, 0x61, 0x02, 0x0c,0xf5, 0x16 }}\r
431 key = GuidNames.from_c_guid(arg)\r
432 name = GuidNames.to_name(key)\r
433 else:\r
434 # guid gEfiDxeServicesTableGuid\r
435 name = guid\r
436 try:\r
437 key = GuidNames.to_guid(name)\r
438 name = GuidNames.to_name(key)\r
439 except ValueError:\r
440 return\r
441\r
442 extra = f'{GuidNames.to_c_guid(key)}: ' if options.verbose else ''\r
443 print(f'{key}: {extra}{name}')\r
444\r
445 else:\r
446 for key, value in GuidNames._dict_.items():\r
447 if options.verbose:\r
448 extra = f'{GuidNames.to_c_guid(key)}: '\r
449 else:\r
450 extra = ''\r
451 print(f'{key}: {extra}{value}')\r
452\r
453\r
454class EfiHobCmd (gdb.Command):\r
455 """Dump EFI HOBs. Type 'hob -h' for more info."""\r
456\r
457 def __init__(self):\r
458 super(EfiHobCmd, self).__init__("efi hob", gdb.COMMAND_NONE)\r
459 self.file = GdbFileObject()\r
460\r
461 def create_options(self, arg, from_tty):\r
462 usage = "usage: %prog [options] [arg]"\r
463 description = (\r
464 "Command that can load EFI PE/COFF and TE image symbols. ")\r
465\r
466 self.parser = optparse.OptionParser(\r
467 description=description,\r
468 prog='efi hob',\r
469 usage=usage,\r
470 add_help_option=False)\r
471\r
472 self.parser.add_option(\r
473 '-a',\r
474 '--address',\r
475 type="int",\r
476 dest='address',\r
477 help='Parse HOBs from address',\r
478 default=None)\r
479\r
480 self.parser.add_option(\r
481 '-t',\r
482 '--type',\r
483 type="int",\r
484 dest='type',\r
485 help='Only dump HOBS of his type',\r
486 default=None)\r
487\r
488 self.parser.add_option(\r
489 '-v',\r
490 '--verbose',\r
491 action='store_true',\r
492 dest='verbose',\r
493 help='hex dump extra data',\r
494 default=False)\r
495\r
496 self.parser.add_option(\r
497 '-h',\r
498 '--help',\r
499 action='store_true',\r
500 dest='help',\r
501 help='Show help for the command',\r
502 default=False)\r
503\r
504 return self.parser.parse_args(shlex.split(arg))\r
505\r
506 def invoke(self, arg, from_tty):\r
507 '''gdb command to dump EFI System Tables'''\r
508\r
509 try:\r
510 (options, _) = self.create_options(arg, from_tty)\r
511 if options.help:\r
512 self.parser.print_help()\r
513 return\r
514 except ValueError:\r
515 print('bad arguments!')\r
516 return\r
517\r
518 if options.address:\r
519 try:\r
520 value = gdb.parse_and_eval(options.address)\r
521 address = int(value)\r
522 except ValueError:\r
523 address = None\r
524 else:\r
525 address = None\r
526\r
527 hob = EfiHob(self.file,\r
528 address,\r
529 options.verbose).get_hob_by_type(options.type)\r
530 print(hob)\r
531\r
532\r
533class EfiTablesCmd (gdb.Command):\r
534 """Dump EFI System Tables. Type 'table -h' for more info."""\r
535\r
536 def __init__(self):\r
537 super(EfiTablesCmd, self).__init__("efi table", gdb.COMMAND_NONE)\r
538\r
539 self.file = GdbFileObject()\r
540\r
541 def create_options(self, arg, from_tty):\r
542 usage = "usage: %prog [options] [arg]"\r
543 description = "Dump EFI System Tables. Requires symbols to be loaded"\r
544\r
545 self.parser = optparse.OptionParser(\r
546 description=description,\r
547 prog='efi table',\r
548 usage=usage,\r
549 add_help_option=False)\r
550\r
551 self.parser.add_option(\r
552 '-h',\r
553 '--help',\r
554 action='store_true',\r
555 dest='help',\r
556 help='Show help for the command',\r
557 default=False)\r
558\r
559 return self.parser.parse_args(shlex.split(arg))\r
560\r
561 def invoke(self, arg, from_tty):\r
562 '''gdb command to dump EFI System Tables'''\r
563\r
564 try:\r
565 (options, _) = self.create_options(arg, from_tty)\r
566 if options.help:\r
567 self.parser.print_help()\r
568 return\r
569 except ValueError:\r
570 print('bad arguments!')\r
571 return\r
572\r
573 gST = gdb.lookup_global_symbol('gST')\r
574 if gST is None:\r
575 print('Error: This command requires symbols for gST to be loaded')\r
576 return\r
577\r
578 table = EfiConfigurationTable(\r
579 self.file, int(gST.value(gdb.selected_frame())))\r
580 if table:\r
581 print(table, '\n')\r
582\r
583\r
584class EfiSymbolsCmd (gdb.Command):\r
585 """Load Symbols for EFI. Type 'efi symbols -h' for more info."""\r
586\r
587 def __init__(self):\r
588 super(EfiSymbolsCmd, self).__init__("efi symbols",\r
589 gdb.COMMAND_NONE,\r
590 gdb.COMPLETE_EXPRESSION)\r
591 self.file = GdbFileObject()\r
592 self.gST = None\r
593 self.efi_symbols = EfiSymbols(self.file)\r
594\r
595 def create_options(self, arg, from_tty):\r
596 usage = "usage: %prog [options]"\r
597 description = (\r
598 "Command that can load EFI PE/COFF and TE image symbols. "\r
599 "If you are having trouble in PEI try adding --pei. "\r
600 "Given any address search backward for the PE/COFF (or TE header) "\r
601 "and then parse the PE/COFF image to get debug info. "\r
602 "The address can come from the current pc, pc values in the "\r
603 "frame, or an address provided to the command"\r
604 "")\r
605\r
606 self.parser = optparse.OptionParser(\r
607 description=description,\r
608 prog='efi symbols',\r
609 usage=usage,\r
610 add_help_option=False)\r
611\r
612 self.parser.add_option(\r
613 '-a',\r
614 '--address',\r
615 type="str",\r
616 dest='address',\r
617 help='Load symbols for image that contains address',\r
618 default=None)\r
619\r
620 self.parser.add_option(\r
621 '-c',\r
622 '--clear',\r
623 action='store_true',\r
624 dest='clear',\r
625 help='Clear the cache of loaded images',\r
626 default=False)\r
627\r
628 self.parser.add_option(\r
629 '-f',\r
630 '--frame',\r
631 action='store_true',\r
632 dest='frame',\r
633 help='Load symbols for current stack frame',\r
634 default=False)\r
635\r
636 self.parser.add_option(\r
637 '-p',\r
638 '--pc',\r
639 action='store_true',\r
640 dest='pc',\r
641 help='Load symbols for pc',\r
642 default=False)\r
643\r
644 self.parser.add_option(\r
645 '--pei',\r
646 action='store_true',\r
647 dest='pei',\r
648 help='Load symbols for PEI (searches every 4 bytes)',\r
649 default=False)\r
650\r
651 self.parser.add_option(\r
652 '-e',\r
653 '--extended',\r
654 action='store_true',\r
655 dest='extended',\r
656 help='Try to load all symbols based on config tables',\r
657 default=False)\r
658\r
659 self.parser.add_option(\r
660 '-r',\r
661 '--range',\r
662 type="long",\r
663 dest='range',\r
664 help='How far to search backward for start of PE/COFF Image',\r
665 default=None)\r
666\r
667 self.parser.add_option(\r
668 '-s',\r
669 '--stride',\r
670 type="long",\r
671 dest='stride',\r
672 help='Boundary to search for PE/COFF header',\r
673 default=None)\r
674\r
675 self.parser.add_option(\r
676 '-t',\r
677 '--thread',\r
678 action='store_true',\r
679 dest='thread',\r
680 help='Load symbols for the frames of all threads',\r
681 default=False)\r
682\r
683 self.parser.add_option(\r
684 '-v',\r
685 '--verbose',\r
686 action='store_true',\r
687 dest='verbose',\r
688 help='Show more info on symbols loading in gdb',\r
689 default=False)\r
690\r
691 self.parser.add_option(\r
692 '-h',\r
693 '--help',\r
694 action='store_true',\r
695 dest='help',\r
696 help='Show help for the command',\r
697 default=False)\r
698\r
699 return self.parser.parse_args(shlex.split(arg))\r
700\r
701 def save_user_state(self):\r
702 self.pagination = gdb.parameter("pagination")\r
703 if self.pagination:\r
704 gdb.execute("set pagination off")\r
705\r
706 self.user_selected_thread = gdb.selected_thread()\r
707 self.user_selected_frame = gdb.selected_frame()\r
708\r
709 def restore_user_state(self):\r
710 self.user_selected_thread.switch()\r
711 self.user_selected_frame.select()\r
712\r
713 if self.pagination:\r
714 gdb.execute("set pagination on")\r
715\r
716 def canonical_address(self, address):\r
717 '''\r
718 Scrub out 48-bit non canonical addresses\r
719 Raw frames in gdb can have some funky values\r
720 '''\r
721\r
722 # Skip lowest 256 bytes to avoid interrupt frames\r
723 if address > 0xFF and address < 0x00007FFFFFFFFFFF:\r
724 return True\r
725 if address >= 0xFFFF800000000000:\r
726 return True\r
727\r
728 return False\r
729\r
730 def pc_set_for_frames(self):\r
731 '''Return a set for the PC's in the current frame'''\r
732 pc_list = []\r
733 frame = gdb.newest_frame()\r
734 while frame:\r
735 pc = int(frame.read_register('pc'))\r
736 if self.canonical_address(pc):\r
737 pc_list.append(pc)\r
738 frame = frame.older()\r
739\r
740 return set(pc_list)\r
741\r
742 def invoke(self, arg, from_tty):\r
743 '''gdb command to symbolicate all the frames from all the threads'''\r
744\r
745 try:\r
746 (options, _) = self.create_options(arg, from_tty)\r
747 if options.help:\r
748 self.parser.print_help()\r
749 return\r
750 except ValueError:\r
751 print('bad arguments!')\r
752 return\r
753\r
754 self.dont_repeat()\r
755\r
756 self.save_user_state()\r
757\r
758 if options.clear:\r
759 self.efi_symbols.clear()\r
760 return\r
761\r
762 if options.pei:\r
763 # XIP code can be 4 byte aligned in the FV\r
764 options.stride = 4\r
765 options.range = 0x100000\r
766 self.efi_symbols.configure_search(options.stride,\r
767 options.range,\r
768 options.verbose)\r
769\r
770 if options.thread:\r
771 thread_list = gdb.selected_inferior().threads()\r
772 else:\r
773 thread_list = (gdb.selected_thread(),)\r
774\r
775 address = None\r
776 if options.address:\r
777 value = gdb.parse_and_eval(options.address)\r
778 address = int(value)\r
779 elif options.pc:\r
780 address = gdb.selected_frame().pc()\r
781\r
782 if address:\r
783 res = self.efi_symbols.address_to_symbols(address)\r
784 print(res)\r
785 else:\r
786\r
787 for thread in thread_list:\r
788 thread.switch()\r
789\r
790 # You can not iterate over frames as you load symbols. Loading\r
791 # symbols changes the frames gdb can see due to inlining and\r
792 # boom. So we loop adding symbols for the current frame, and\r
793 # we test to see if new frames have shown up. If new frames\r
794 # show up we process those new frames. Thus 1st pass is the\r
795 # raw frame, and other passes are only new PC values.\r
796 NewPcSet = self.pc_set_for_frames()\r
797 while NewPcSet:\r
798 PcSet = self.pc_set_for_frames()\r
799 for pc in NewPcSet:\r
800 res = self.efi_symbols.address_to_symbols(pc)\r
801 print(res)\r
802\r
803 NewPcSet = PcSet.symmetric_difference(\r
804 self.pc_set_for_frames())\r
805\r
806 # find the EFI System tables the 1st time\r
807 if self.gST is None:\r
808 gST = gdb.lookup_global_symbol('gST')\r
809 if gST is not None:\r
810 self.gST = int(gST.value(gdb.selected_frame()))\r
811 table = EfiConfigurationTable(self.file, self.gST)\r
812 else:\r
813 table = None\r
814 else:\r
815 table = EfiConfigurationTable(self.file, self.gST)\r
816\r
817 if options.extended and table:\r
818 # load symbols from EFI System Table entry\r
819 for address, _ in table.DebugImageInfo():\r
820 res = self.efi_symbols.address_to_symbols(address)\r
821 print(res)\r
822\r
823 # sync up the GUID database from the build output\r
824 for m in gdb.objfiles():\r
825 if GuidNames.add_build_guid_file(str(m.filename)):\r
826 break\r
827\r
828 self.restore_user_state()\r
829\r
830\r
831class EfiCmd (gdb.Command):\r
832 """Commands for debugging EFI. efi <cmd>"""\r
833\r
834 def __init__(self):\r
835 super(EfiCmd, self).__init__("efi",\r
836 gdb.COMMAND_NONE,\r
837 gdb.COMPLETE_NONE,\r
838 True)\r
839\r
840 def invoke(self, arg, from_tty):\r
841 '''default to loading symbols'''\r
842 if '-h' in arg or '--help' in arg:\r
843 gdb.execute('help efi')\r
844 else:\r
845 # default to loading all symbols\r
846 gdb.execute('efi symbols --extended')\r
847\r
848\r
849class LoadEmulatorEfiSymbols(gdb.Breakpoint):\r
850 '''\r
851 breakpoint for EmulatorPkg to load symbols\r
852 Note: make sure SecGdbScriptBreak is not optimized away!\r
853 Also turn off the dlopen() flow like on macOS.\r
854 '''\r
855 def stop(self):\r
856 symbols = EfiSymbols()\r
857 # Emulator adds SizeOfHeaders so we need file alignment to search\r
858 symbols.configure_search(0x20)\r
859\r
860 frame = gdb.newest_frame()\r
861\r
862 try:\r
863 # gdb was looking at spill address, pre spill :(\r
864 LoadAddress = frame.read_register('rdx')\r
865 AddSymbolFlag = frame.read_register('rcx')\r
866 except gdb.error:\r
867 LoadAddress = frame.read_var('LoadAddress')\r
868 AddSymbolFlag = frame.read_var('AddSymbolFlag')\r
869\r
870 if AddSymbolFlag == 1:\r
871 res = symbols.address_to_symbols(LoadAddress)\r
872 else:\r
873 res = symbols.unload_symbols(LoadAddress)\r
874 print(res)\r
875\r
876 # keep running\r
877 return False\r
878\r
879\r
880# Get python backtraces to debug errors in this script\r
881gdb.execute("set python print-stack full")\r
882\r
883# tell efi_debugging how to walk data structures with pointers\r
884try:\r
885 pointer_width = gdb.lookup_type('int').pointer().sizeof\r
886except ValueError:\r
887 pointer_width = 8\r
888patch_ctypes(pointer_width)\r
889\r
890register_pretty_printer(None, build_pretty_printer(), replace=True)\r
891\r
892# gdb commands that we are adding\r
893# add `efi` prefix gdb command\r
894EfiCmd()\r
895\r
896# subcommands for `efi`\r
897EfiSymbolsCmd()\r
898EfiTablesCmd()\r
899EfiHobCmd()\r
900EfiDevicePathCmd()\r
901EfiGuidCmd()\r
902\r
903#\r
904bp = LoadEmulatorEfiSymbols('SecGdbScriptBreak', internal=True)\r
905if bp.pending:\r
906 try:\r
907 gdb.selected_frame()\r
908 # Not the emulator so do this when you attach\r
909 gdb.execute('efi symbols --frame --extended', True)\r
910 gdb.execute('bt')\r
911 # If you want to skip the above commands comment them out\r
912 pass\r
913 except gdb.error:\r
914 # If you load the script and there is no target ignore the error.\r
915 pass\r
916else:\r
917 # start the emulator\r
918 gdb.execute('run')\r