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