3 Copyright (c) Apple Inc. 2021
4 SPDX-License-Identifier: BSD-2-Clause-Patent
6 Class that abstracts PE/COFF debug info parsing via a Python file like
7 object. You can port this code into an arbitrary debugger by invoking
8 the classes and passing in a file like object that abstracts the debugger
11 If you run this file directly it will parse the passed in PE/COFF files
13 python3 ./efi_pefcoff.py DxeCore.efi
14 IA32`<path...>/DxeCore.dll load = 0x00000000
15 EntryPoint = 0x000030d2 TextAddress = 0x00000240 DataAddress = 0x000042c0
16 .text 0x00000240 (0x04080) flags:0x60000020
17 .data 0x000042C0 (0x001C0) flags:0xC0000040
18 .reloc 0x00004480 (0x00240) flags:0x42000040
20 Note: PeCoffClass uses virtual addresses and not file offsets.
21 It needs to work when images are loaded into memory.
22 as long as virtual address map to file addresses this
23 code can process binary files.
25 Note: This file can also contain generic worker functions (like GuidNames)
26 that abstract debugger agnostic services to the debugger.
28 This file should never import debugger specific modules.
36 from ctypes
import c_char
, c_uint8
, c_uint16
, c_uint32
, c_uint64
, c_void_p
37 from ctypes
import ARRAY
, sizeof
38 from ctypes
import Structure
, LittleEndianStructure
41 # The empty LittleEndianStructure must have _fields_ assigned prior to use or
42 # sizeof(). Anything that is size UINTN may need to get adjusted.
44 # The issue is ctypes matches our local machine, not the machine we are
45 # trying to debug. Call patch_ctypes() passing in the byte width from the
46 # debugger python to make sure you are in sync.
48 # Splitting out the _field_ from the Structure (LittleEndianStructure) class
49 # allows it to be patched.
53 class EFI_LOADED_IMAGE_PROTOCOL(LittleEndianStructure
):
57 EFI_LOADED_IMAGE_PROTOCOL_fields_
= [
58 ('Revision', c_uint32
),
59 ('ParentHandle', c_void_p
),
60 ('SystemTable', c_void_p
),
61 ('DeviceHandle', c_void_p
),
62 ('FilePath', c_void_p
),
63 ('Reserved', c_void_p
),
64 ('LoadOptionsSize', c_uint32
),
65 ('LoadOptions', c_void_p
),
66 ('ImageBase', c_void_p
),
67 ('ImageSize', c_uint64
),
68 ('ImageCodeType', c_uint32
),
69 ('ImageDataType', c_uint32
),
74 class EFI_GUID(LittleEndianStructure
):
79 ('Data4', ARRAY(c_uint8
, 8))
83 class EFI_SYSTEM_TABLE_POINTER(LittleEndianStructure
):
85 ('Signature', c_uint64
),
86 ('EfiSystemTableBase', c_uint64
),
91 class EFI_DEBUG_IMAGE_INFO_NORMAL(LittleEndianStructure
):
95 EFI_DEBUG_IMAGE_INFO_NORMAL_fields_
= [
96 ('ImageInfoType', c_uint32
),
97 ('LoadedImageProtocolInstance', c_void_p
),
98 ('ImageHandle', c_void_p
)
102 class EFI_DEBUG_IMAGE_INFO(LittleEndianStructure
):
106 EFI_DEBUG_IMAGE_INFO_fields_
= [
107 ('NormalImage', c_void_p
),
111 class EFI_DEBUG_IMAGE_INFO_TABLE_HEADER(LittleEndianStructure
):
115 EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_
= [
116 ('UpdateStatus', c_uint32
),
117 ('TableSize', c_uint32
),
118 ('EfiDebugImageInfoTable', c_void_p
),
122 class EFI_TABLE_HEADER(LittleEndianStructure
):
124 ('Signature', c_uint64
),
125 ('Revision', c_uint32
),
126 ('HeaderSize', c_uint32
),
128 ('Reserved', c_uint32
),
132 class EFI_CONFIGURATION_TABLE(LittleEndianStructure
):
136 EFI_CONFIGURATION_TABLE_fields_
= [
137 ('VendorGuid', EFI_GUID
),
138 ('VendorTable', c_void_p
)
142 class EFI_SYSTEM_TABLE(LittleEndianStructure
):
146 EFI_SYSTEM_TABLE_fields_
= [
147 ('Hdr', EFI_TABLE_HEADER
),
148 ('FirmwareVendor', c_void_p
),
149 ('FirmwareRevision', c_uint32
),
150 ('ConsoleInHandle', c_void_p
),
152 ('ConsoleOutHandle', c_void_p
),
153 ('ConOut', c_void_p
),
154 ('StandardErrHandle', c_void_p
),
155 ('StdErr', c_void_p
),
156 ('RuntimeService', c_void_p
),
157 ('BootService', c_void_p
),
158 ('NumberOfTableEntries', c_void_p
),
159 ('ConfigurationTable', c_void_p
),
163 class EFI_IMAGE_DATA_DIRECTORY(LittleEndianStructure
):
165 ('VirtualAddress', c_uint32
),
170 class EFI_TE_IMAGE_HEADER(LittleEndianStructure
):
172 ('Signature', ARRAY(c_char
, 2)),
173 ('Machine', c_uint16
),
174 ('NumberOfSections', c_uint8
),
175 ('Subsystem', c_uint8
),
176 ('StrippedSize', c_uint16
),
177 ('AddressOfEntryPoint', c_uint32
),
178 ('BaseOfCode', c_uint32
),
179 ('ImageBase', c_uint64
),
180 ('DataDirectoryBaseReloc', EFI_IMAGE_DATA_DIRECTORY
),
181 ('DataDirectoryDebug', EFI_IMAGE_DATA_DIRECTORY
)
185 class EFI_IMAGE_DOS_HEADER(LittleEndianStructure
):
187 ('e_magic', c_uint16
),
188 ('e_cblp', c_uint16
),
190 ('e_crlc', c_uint16
),
191 ('e_cparhdr', c_uint16
),
192 ('e_minalloc', c_uint16
),
193 ('e_maxalloc', c_uint16
),
196 ('e_csum', c_uint16
),
199 ('e_lfarlc', c_uint16
),
200 ('e_ovno', c_uint16
),
201 ('e_res', ARRAY(c_uint16
, 4)),
202 ('e_oemid', c_uint16
),
203 ('e_oeminfo', c_uint16
),
204 ('e_res2', ARRAY(c_uint16
, 10)),
205 ('e_lfanew', c_uint16
)
209 class EFI_IMAGE_FILE_HEADER(LittleEndianStructure
):
211 ('Machine', c_uint16
),
212 ('NumberOfSections', c_uint16
),
213 ('TimeDateStamp', c_uint32
),
214 ('PointerToSymbolTable', c_uint32
),
215 ('NumberOfSymbols', c_uint32
),
216 ('SizeOfOptionalHeader', c_uint16
),
217 ('Characteristics', c_uint16
)
221 class EFI_IMAGE_OPTIONAL_HEADER32(LittleEndianStructure
):
224 ('MajorLinkerVersion', c_uint8
),
225 ('MinorLinkerVersion', c_uint8
),
226 ('SizeOfCode', c_uint32
),
227 ('SizeOfInitializedData', c_uint32
),
228 ('SizeOfUninitializedData', c_uint32
),
229 ('AddressOfEntryPoint', c_uint32
),
230 ('BaseOfCode', c_uint32
),
231 ('BaseOfData', c_uint32
),
232 ('ImageBase', c_uint32
),
233 ('SectionAlignment', c_uint32
),
234 ('FileAlignment', c_uint32
),
235 ('MajorOperatingSystemVersion', c_uint16
),
236 ('MinorOperatingSystemVersion', c_uint16
),
237 ('MajorImageVersion', c_uint16
),
238 ('MinorImageVersion', c_uint16
),
239 ('MajorSubsystemVersion', c_uint16
),
240 ('MinorSubsystemVersion', c_uint16
),
241 ('Win32VersionValue', c_uint32
),
242 ('SizeOfImage', c_uint32
),
243 ('SizeOfHeaders', c_uint32
),
244 ('CheckSum', c_uint32
),
245 ('Subsystem', c_uint16
),
246 ('DllCharacteristics', c_uint16
),
247 ('SizeOfStackReserve', c_uint32
),
248 ('SizeOfStackCommit', c_uint32
),
249 ('SizeOfHeapReserve', c_uint32
),
250 ('SizeOfHeapCommit', c_uint32
),
251 ('LoaderFlags', c_uint32
),
252 ('NumberOfRvaAndSizes', c_uint32
),
253 ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY
, 16))
257 class EFI_IMAGE_NT_HEADERS32(LittleEndianStructure
):
259 ('Signature', c_uint32
),
260 ('FileHeader', EFI_IMAGE_FILE_HEADER
),
261 ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER32
)
265 class EFI_IMAGE_OPTIONAL_HEADER64(LittleEndianStructure
):
268 ('MajorLinkerVersion', c_uint8
),
269 ('MinorLinkerVersion', c_uint8
),
270 ('SizeOfCode', c_uint32
),
271 ('SizeOfInitializedData', c_uint32
),
272 ('SizeOfUninitializedData', c_uint32
),
273 ('AddressOfEntryPoint', c_uint32
),
274 ('BaseOfCode', c_uint32
),
275 ('BaseOfData', c_uint32
),
276 ('ImageBase', c_uint32
),
277 ('SectionAlignment', c_uint32
),
278 ('FileAlignment', c_uint32
),
279 ('MajorOperatingSystemVersion', c_uint16
),
280 ('MinorOperatingSystemVersion', c_uint16
),
281 ('MajorImageVersion', c_uint16
),
282 ('MinorImageVersion', c_uint16
),
283 ('MajorSubsystemVersion', c_uint16
),
284 ('MinorSubsystemVersion', c_uint16
),
285 ('Win32VersionValue', c_uint32
),
286 ('SizeOfImage', c_uint32
),
287 ('SizeOfHeaders', c_uint32
),
288 ('CheckSum', c_uint32
),
289 ('Subsystem', c_uint16
),
290 ('DllCharacteristics', c_uint16
),
291 ('SizeOfStackReserve', c_uint64
),
292 ('SizeOfStackCommit', c_uint64
),
293 ('SizeOfHeapReserve', c_uint64
),
294 ('SizeOfHeapCommit', c_uint64
),
295 ('LoaderFlags', c_uint32
),
296 ('NumberOfRvaAndSizes', c_uint32
),
297 ('DataDirectory', ARRAY(EFI_IMAGE_DATA_DIRECTORY
, 16))
301 class EFI_IMAGE_NT_HEADERS64(LittleEndianStructure
):
303 ('Signature', c_uint32
),
304 ('FileHeader', EFI_IMAGE_FILE_HEADER
),
305 ('OptionalHeader', EFI_IMAGE_OPTIONAL_HEADER64
)
309 class EFI_IMAGE_DEBUG_DIRECTORY_ENTRY(LittleEndianStructure
):
311 ('Characteristics', c_uint32
),
312 ('TimeDateStamp', c_uint32
),
313 ('MajorVersion', c_uint16
),
314 ('MinorVersion', c_uint16
),
316 ('SizeOfData', c_uint32
),
318 ('FileOffset', c_uint32
),
322 class EFI_IMAGE_SECTION_HEADER(LittleEndianStructure
):
324 ('Name', ARRAY(c_char
, 8)),
325 ('VirtualSize', c_uint32
),
326 ('VirtualAddress', c_uint32
),
327 ('SizeOfRawData', c_uint32
),
328 ('PointerToRawData', c_uint32
),
329 ('PointerToRelocations', c_uint32
),
330 ('PointerToLinenumbers', c_uint32
),
331 ('NumberOfRelocations', c_uint16
),
332 ('NumberOfLinenumbers', c_uint16
),
333 ('Characteristics', c_uint32
),
337 EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
= 0x10b
338 EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
= 0x20b
343 image_machine_dict
= {
356 def patch_void_p_to_ctype(patch_type
, to_patch
):
357 '''Optionally patch c_void_p in the Structure._fields_'''
358 if patch_type
is None:
362 for name
, c_type
in to_patch
:
363 if type(c_type
) == type(c_void_p
):
364 result
.append((name
, c_uint32
))
366 result
.append((name
, c_type
))
370 def patch_ctypes(pointer_width
=8):
372 Pass in the pointer width of the system being debugged. If it is not
373 the same as c_void_p then patch the _fields_ with the correct type.
374 For any ctypes Structure that has a c_void_p this function needs to be
375 called prior to use or sizeof() to initialize _fields_.
378 if sizeof(c_void_p
) == pointer_width
:
380 elif pointer_width
== 16:
382 elif pointer_width
== 8:
383 patch_type
= c_uint64
384 elif pointer_width
== 4:
385 patch_type
= c_uint32
387 raise Exception(f
'ERROR: Unkown pointer_width = {pointer_width}')
389 # If you add a ctypes Structure class with a c_void_p you need to add
390 # it to this list. Note: you should use c_void_p for UINTN values.
391 EFI_LOADED_IMAGE_PROTOCOL
._fields
_ = patch_void_p_to_ctype(
392 patch_type
, EFI_LOADED_IMAGE_PROTOCOL_fields_
)
393 EFI_DEBUG_IMAGE_INFO_NORMAL
._fields
_ = patch_void_p_to_ctype(
394 patch_type
, EFI_DEBUG_IMAGE_INFO_NORMAL_fields_
)
395 EFI_DEBUG_IMAGE_INFO
._fields
_ = patch_void_p_to_ctype(
396 patch_type
, EFI_DEBUG_IMAGE_INFO_fields_
)
397 EFI_DEBUG_IMAGE_INFO_TABLE_HEADER
._fields
_ = patch_void_p_to_ctype(
398 patch_type
, EFI_DEBUG_IMAGE_INFO_TABLE_HEADER_fields_
)
399 EFI_CONFIGURATION_TABLE
._fields
_ = patch_void_p_to_ctype(
400 patch_type
, EFI_CONFIGURATION_TABLE_fields_
)
401 EFI_SYSTEM_TABLE
._fields
_ = patch_void_p_to_ctype(
402 patch_type
, EFI_SYSTEM_TABLE_fields_
)
404 # patch up anything else that needs to know pointer_width
405 EfiStatusClass(pointer_width
)
408 def ctype_to_str(ctype
, indent
='', hide_list
=[]):
410 Given a ctype object print out as a string by walking the _fields_
414 for field
in ctype
._fields
_:
415 attr
= getattr(ctype
, field
[0])
416 tname
= type(attr
).__name
__
417 if field
[0] in hide_list
:
420 result
+= indent
+ f
'{field[0]} = '
421 if tname
== 'EFI_GUID':
422 result
+= GuidNames
.to_name(GuidNames
.to_uuid(attr
)) + '\n'
423 elif issubclass(type(attr
), Structure
):
424 result
+= f
'{tname}\n' + \
425 ctype_to_str(attr
, indent
+ ' ', hide_list
)
426 elif isinstance(attr
, int):
427 result
+= f
'0x{attr:x}\n'
429 result
+= f
'{attr}\n'
434 def hexline(addr
, data
):
437 for i
in range(0, len(data
)):
438 hexstr
+= f
'{data[i]:02x} '
439 printable
+= chr(data
[i
]) if data
[i
] > 0x20 and data
[i
] < 0x7f else '.'
440 return f
'{addr:04x} {hexstr:48s} |{printable:s}|'
443 def hexdump(data
, indent
=''):
444 if not isinstance(data
, bytearray
):
445 data
= bytearray(data
)
448 for i
in range(0, len(data
), 16):
449 result
+= indent
+ hexline(i
, data
[i
:i
+16]) + '\n'
454 ''' Return string for EFI_TPL'''
456 def __init__(self
, tpl
):
461 result
= f
'{self.tpl:d}'
463 result
= "TPL_APPLICATION"
465 result
+= f
' + {self.tpl - 4:d}'
467 result
= "TPL_CALLBACK"
469 result
+= f
' + {self.tpl - 8:d}'
471 result
= "TPL_NOTIFY"
472 if self
.tpl
- 16 > 0:
473 result
+= f
' + {self.tpl - 16:d}'
475 result
= "TPL_HIGH_LEVEL"
477 result
= f
'Invalid TPL = {self.tpl:d}'
483 Class to return human readable string for EFI_BOOT_MODE
487 to_str(boot_mode, default)
488 return string for boot_mode, and return default if there is not a
492 EFI_BOOT_MODE_dict
= {
493 0x00: "BOOT_WITH_FULL_CONFIGURATION",
494 0x01: "BOOT_WITH_MINIMAL_CONFIGURATION",
495 0x02: "BOOT_ASSUMING_NO_CONFIGURATION_CHANGES",
496 0x03: "BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS",
497 0x04: "BOOT_WITH_DEFAULT_SETTINGS",
498 0x05: "BOOT_ON_S4_RESUME",
499 0x06: "BOOT_ON_S5_RESUME",
500 0x07: "BOOT_WITH_MFG_MODE_SETTINGS",
501 0x10: "BOOT_ON_S2_RESUME",
502 0x11: "BOOT_ON_S3_RESUME",
503 0x12: "BOOT_ON_FLASH_UPDATE",
504 0x20: "BOOT_IN_RECOVERY_MODE",
507 def __init__(self
, boot_mode
):
508 self
._boot
_mode
= boot_mode
511 return self
.to_str(self
._boot
_mode
)
514 def to_str(cls
, boot_mode
, default
=''):
515 return cls
.EFI_BOOT_MODE_dict
.get(boot_mode
, default
)
518 class EfiStatusClass
:
520 Class to decode EFI_STATUS to a human readable string. You need to
521 pass in pointer_width to get the corret value since the EFI_STATUS
522 code values are different based on the sizeof UINTN. The default is
528 dictionary of EFI_STATUS that has beed updated to match
533 patch_dictionary(pointer_width)
535 to_str(status, default)
539 _EFI_STATUS_UINT32_dict
= {
541 1: "Warning Unknown Glyph",
542 2: "Warning Delete Failure",
543 3: "Warning Write Failure",
544 4: "Warning Buffer Too Small",
545 5: "Warning Stale Data",
546 6: "Warngin File System",
547 (0x20000000 |
0): "Warning interrupt source pending",
548 (0x20000000 |
1): "Warning interrupt source quiesced",
550 (0x80000000 |
1): "Load Error",
551 (0x80000000 |
2): "Invalid Parameter",
552 (0x80000000 |
3): "Unsupported",
553 (0x80000000 |
4): "Bad Buffer Size",
554 (0x80000000 |
5): "Buffer Too Small",
555 (0x80000000 |
6): "Not Ready",
556 (0x80000000 |
7): "Device Error",
557 (0x80000000 |
8): "Write Protected",
558 (0x80000000 |
9): "Out of Resources",
559 (0x80000000 |
10): "Volume Corrupt",
560 (0x80000000 |
11): "Volume Full",
561 (0x80000000 |
12): "No Media",
562 (0x80000000 |
13): "Media changed",
563 (0x80000000 |
14): "Not Found",
564 (0x80000000 |
15): "Access Denied",
565 (0x80000000 |
16): "No Response",
566 (0x80000000 |
17): "No mapping",
567 (0x80000000 |
18): "Time out",
568 (0x80000000 |
19): "Not started",
569 (0x80000000 |
20): "Already started",
570 (0x80000000 |
21): "Aborted",
571 (0x80000000 |
22): "ICMP Error",
572 (0x80000000 |
23): "TFTP Error",
573 (0x80000000 |
24): "Protocol Error",
574 (0x80000000 |
25): "Incompatible Version",
575 (0x80000000 |
26): "Security Violation",
576 (0x80000000 |
27): "CRC Error",
577 (0x80000000 |
28): "End of Media",
578 (0x80000000 |
31): "End of File",
579 (0x80000000 |
32): "Invalid Language",
580 (0x80000000 |
33): "Compromised Data",
581 (0x80000000 |
35): "HTTP Error",
583 (0xA0000000 |
0): "Interrupt Pending",
586 def __init__(self
, status
=None, pointer_width
=8):
588 # this will convert to 64-bit version if needed
589 self
.patch_dictionary(pointer_width
)
592 return self
.to_str(self
.status
)
595 def to_str(cls
, status
, default
=''):
596 return cls
._dict
_.get(status
, default
)
599 def patch_dictionary(cls
, pointer_width
):
600 '''Patch UINTN upper bits like values '''
603 # only patch the class variable once
606 if pointer_width
== 4:
607 cls
._dict
= cls
._EFI
_STATUS
_UINT
32_dict
608 elif pointer_width
== 8:
609 for key
, value
in cls
._EFI
_STATUS
_UINT
32_dict
.items():
610 mask
= (key
& 0xE0000000) << 32
611 new_key
= (key
& 0x1FFFFFFF) | mask
612 cls
._dict
_[new_key
] = value
620 Class to expose the C names of EFI_GUID's. The _dict_ starts with
621 common EFI System Table entry EFI_GUID's. _dict_ can get updated with the
622 build generated Guid.xref file if a path to a module is passed
623 into add_build_guid_file(). If symbols are loaded for any module
624 in the build the path the build product should imply the
625 relative location of that builds Guid.xref file.
630 dictionary of EFI_GUID (uuid) strings to C global names
635 convert a hex UUID string or bytearray to a uuid.UUID
637 convert a UUID string to a C global constant name.
639 convert a C global constant EFI_GUID name to uuid hex string.
641 name is a hex UUID string.
642 Example: 49152E77-1ADA-4764-B7A2-7AFEFED95E8B
645 convert a uuid.UUID or UUID string to a c_guid string
648 covert a C guid string to a hex UUID string.
650 name is the C initialization value for an EFI_GUID. Example:
651 { 0x414e6bdd, 0xe47b, 0x47cc, { 0xb2, 0x44, 0xbb, 0x61,
652 0x02, 0x0c, 0xf5, 0x16 }}
654 add_build_guid_file(module_path, custom_file):
655 assume module_path is an edk2 build product and load the Guid.xref
656 file from that build to fill in _dict_. If you know the path and
657 file name of a custom Guid.xref you can pass it in as custom_file.
660 _dict_
= { # Common EFI System Table values
661 '05AD34BA-6F02-4214-952E-4DA0398E2BB9':
662 'gEfiDxeServicesTableGuid',
663 '7739F24C-93D7-11D4-9A3A-0090273FC14D':
665 '4C19049F-4137-4DD3-9C10-8B97A83FFDFA':
666 'gEfiMemoryTypeInformationGuid',
667 '49152E77-1ADA-4764-B7A2-7AFEFED95E8B':
668 'gEfiDebugImageInfoTableGuid',
669 '060CC026-4C0D-4DDA-8F41-595FEF00A502':
670 'gMemoryStatusCodeRecordGuid',
671 'EB9D2D31-2D88-11D3-9A16-0090273FC14D':
672 'gEfiSmbiosTableGuid',
673 'EB9D2D30-2D88-11D3-9A16-0090273FC14D':
674 'gEfiAcpi10TableGuid',
675 '8868E871-E4F1-11D3-BC22-0080C73C8881':
676 'gEfiAcpi20TableGuid',
681 def __init__(self
, uuid
=None, pointer_width
=8):
682 self
.uuid
= None if uuid
is None else self
.to_uuid(uuid
)
685 if self
.uuid
is None:
687 for key
, value
in GuidNames
._dict
_.items():
688 result
+= f
'{key}: {value}\n'
690 result
= self
.to_name(self
.uuid
)
695 def to_uuid(cls
, obj
):
697 return uuid
.UUID(bytes_le
=bytes(obj
))
698 except (ValueError, TypeError):
700 return uuid
.UUID(bytes_le
=obj
)
701 except (ValueError, TypeError):
702 return uuid
.UUID(obj
)
705 def to_name(cls
, uuid
):
706 if not isinstance(uuid
, str):
708 if cls
.is_c_guid(uuid
):
709 uuid
= cls
.from_c_guid(uuid
)
710 return cls
._dict
_.get(uuid
.upper(), uuid
.upper())
713 def to_guid(cls
, guid_name
):
714 for key
, value
in cls
._dict
_.items():
715 if guid_name
== value
:
721 def is_guid_str(cls
, name
):
722 if not isinstance(name
, str):
724 return name
.count('-') >= 4
727 def to_c_guid(cls
, value
):
728 if isinstance(value
, uuid
.UUID
):
731 guid
= uuid
.UUID(value
)
733 (data1
, data2
, data3
,
734 data4_0
, data4_1
, data4_2
, data4_3
,
735 data4_4
, data4_5
, data4_6
, data4_7
) = struct
.unpack(
736 '<IHH8B', guid
.bytes_le
)
737 return (f
'{{ 0x{data1:08X}, 0x{data2:04X}, 0x{data3:04X}, '
738 f
'{{ 0x{data4_0:02X}, 0x{data4_1:02X}, 0x{data4_2:02X}, '
739 f
'0x{data4_3:02X}, 0x{data4_4:02X}, 0x{data4_5:02X}, '
740 f
'0x{data4_6:02X}, 0x{data4_7:02X} }} }}')
743 def from_c_guid(cls
, value
):
745 hex = [int(x
, 16) for x
in re
.findall(r
"[\w']+", value
)]
746 return (f
'{hex[0]:08X}-{hex[1]:04X}-{hex[2]:04X}'
747 + f
'-{hex[3]:02X}{hex[4]:02X}-{hex[5]:02X}{hex[6]:02X}'
748 + f
'{hex[7]:02X}{hex[8]:02X}{hex[9]:02X}{hex[10]:02X}')
753 def is_c_guid(cls
, name
):
754 if not isinstance(name
, str):
756 return name
.count('{') == 2 and name
.count('}') == 2
759 def add_build_guid_file(cls
, module_path
, custom_file
=None):
760 if custom_file
is not None:
763 # module_path will look like:
764 # <repo>/Build/OvmfX64/DEBUG_XCODE5/X64/../DxeCore.dll
765 # Walk backwards looking for a toolchain like name.
766 # Then look for GUID database:
767 # Build/OvmfX64//DEBUG_XCODE5/FV/Guid.xref
768 for i
in reversed(module_path
.split(os
.sep
)):
769 if (i
.startswith('DEBUG_') or
770 i
.startswith('RELEASE_') or
771 i
.startswith('NOOPT_')):
772 build_root
= os
.path
.join(
773 module_path
.rsplit(i
, 1)[0], i
)
776 xref
= os
.path
.join(build_root
, 'FV', 'Guid.xref')
778 if xref
in cls
.guid_files
:
779 # only processes the file one time
782 with
open(xref
) as f
:
783 content
= f
.readlines()
784 cls
.guid_files
.append(xref
)
786 for lines
in content
:
788 if cls
.is_guid_str(lines
):
789 # a regex would be more pedantic
790 words
= lines
.split()
791 cls
._dict
_[words
[0].upper()] = words
[1].strip('\n')
800 class EFI_HOB_GENERIC_HEADER(LittleEndianStructure
):
802 ('HobType', c_uint16
),
803 ('HobLength', c_uint16
),
804 ('Reserved', c_uint32
)
808 class EFI_HOB_HANDOFF_INFO_TABLE(LittleEndianStructure
):
810 ('Header', EFI_HOB_GENERIC_HEADER
),
811 ('Version', c_uint32
),
812 ('BootMode', c_uint32
),
813 ('EfiMemoryTop', c_uint64
),
814 ('EfiMemoryBottom', c_uint64
),
815 ('EfiFreeMemoryTop', c_uint64
),
816 ('EfiFreeMemoryBottom', c_uint64
),
817 ('EfiEndOfHobList', c_uint64
),
821 class EFI_HOB_MEMORY_ALLOCATION(LittleEndianStructure
):
823 ('Header', EFI_HOB_GENERIC_HEADER
),
825 ('MemoryBaseAddress', c_uint64
),
826 ('MemoryLength', c_uint64
),
827 ('MemoryType', c_uint32
),
828 ('Reserved', c_uint32
),
832 class EFI_HOB_RESOURCE_DESCRIPTOR(LittleEndianStructure
):
834 ('Header', EFI_HOB_GENERIC_HEADER
),
836 ('ResourceType', c_uint32
),
837 ('ResourceAttribute', c_uint32
),
838 ('PhysicalStart', c_uint64
),
839 ('ResourceLength', c_uint64
),
843 class EFI_HOB_GUID_TYPE(LittleEndianStructure
):
845 ('Header', EFI_HOB_GENERIC_HEADER
),
850 class EFI_HOB_FIRMWARE_VOLUME(LittleEndianStructure
):
852 ('Header', EFI_HOB_GENERIC_HEADER
),
853 ('BaseAddress', c_uint64
),
854 ('Length', c_uint64
),
858 class EFI_HOB_CPU(LittleEndianStructure
):
860 ('Header', EFI_HOB_GENERIC_HEADER
),
861 ('SizeOfMemorySpace', c_uint8
),
862 ('SizeOfIoSpace', c_uint8
),
863 ('Reserved', ARRAY(c_uint8
, 6)),
867 class EFI_HOB_MEMORY_POOL(LittleEndianStructure
):
869 ('Header', EFI_HOB_GENERIC_HEADER
),
873 class EFI_HOB_FIRMWARE_VOLUME2(LittleEndianStructure
):
875 ('Header', EFI_HOB_GENERIC_HEADER
),
876 ('BaseAddress', c_uint64
),
877 ('Length', c_uint64
),
878 ('FvName', EFI_GUID
),
879 ('FileName', EFI_GUID
)
883 class EFI_HOB_FIRMWARE_VOLUME3(LittleEndianStructure
):
885 ('HobType', c_uint16
),
886 ('HobLength', c_uint16
),
887 ('Reserved', c_uint32
),
888 ('BaseAddress', c_uint64
),
889 ('Length', c_uint64
),
890 ('AuthenticationStatus', c_uint32
),
891 ('ExtractedFv', c_uint8
),
892 ('FvName', EFI_GUID
),
893 ('FileName', EFI_GUID
),
897 class EFI_HOB_UEFI_CAPSULE(LittleEndianStructure
):
899 ('HobType', c_uint16
),
900 ('HobLength', c_uint16
),
901 ('Reserved', c_uint32
),
902 ('BaseAddress', c_uint64
),
903 ('Length', c_uint64
),
909 Parse EFI Device Paths based on the edk2 C Structures defined above.
910 In the context of this class verbose means hexdump extra data.
916 List of HOBs. Each entry contains the name, HOB type, HOB length,
917 the ctype struct for the HOB, and any extra data.
921 get_hob_by_type(hob_type)
922 return string that decodes the HOBs of hob_type. If hob_type is
923 None then return all HOBs.
930 1: EFI_HOB_HANDOFF_INFO_TABLE
,
931 2: EFI_HOB_MEMORY_ALLOCATION
,
932 3: EFI_HOB_RESOURCE_DESCRIPTOR
,
933 4: EFI_HOB_GUID_TYPE
,
934 5: EFI_HOB_FIRMWARE_VOLUME
,
936 7: EFI_HOB_MEMORY_POOL
,
937 9: EFI_HOB_FIRMWARE_VOLUME2
,
938 0xb: EFI_HOB_UEFI_CAPSULE
,
939 0xc: EFI_HOB_FIRMWARE_VOLUME3
,
940 0xffff: EFI_HOB_GENERIC_HEADER
,
943 def __init__(self
, file, address
=None, verbose
=False, count
=1000):
945 EfiHob
.verbose
= verbose
947 if len(EfiHob
.Hob
) != 0 and address
is None:
950 if address
is not None:
953 hob_ptr
= EfiConfigurationTable(file).GetConfigTable(
954 '7739F24C-93D7-11D4-9A3A-0090273FC14D')
956 self
.read_hobs(hob_ptr
)
960 return cls
.get_hob_by_type(None)
963 def get_hob_by_type(cls
, hob_type
):
965 for (Name
, HobType
, HobLen
, chob
, extra
) in cls
.Hob
:
966 if hob_type
is not None:
967 if hob_type
!= HobType
:
970 result
+= f
'Type: {Name:s} (0x{HobType:01x}) Len: 0x{HobLen:03x}\n'
971 result
+= ctype_to_str(chob
, ' ', ['Reserved'])
973 if extra
is not None:
974 result
+= hexdump(extra
, ' ')
978 def read_hobs(self
, hob_ptr
, count
=1000):
983 for _
in range(count
): # while True
984 hdr
, _
= self
._ctype
_read
_ex
(EFI_HOB_GENERIC_HEADER
, hob_ptr
)
985 if hdr
.HobType
== 0xffff:
988 type_str
= self
.hob_dict
.get(
989 hdr
.HobType
, EFI_HOB_GENERIC_HEADER
)
990 hob
, extra
= self
._ctype
_read
_ex
(
991 type_str
, hob_ptr
, hdr
.HobLength
)
998 hob_ptr
+= hdr
.HobLength
1002 def _ctype_read_ex(self
, ctype_struct
, offset
=0, rsize
=None):
1004 self
._file
.seek(offset
)
1006 type_size
= sizeof(ctype_struct
)
1007 size
= rsize
if rsize
else type_size
1008 data
= self
._file
.read(size
)
1009 cdata
= ctype_struct
.from_buffer(bytearray(data
))
1011 if size
> type_size
:
1012 return cdata
, data
[type_size
:]
1017 class EFI_DEVICE_PATH(LittleEndianStructure
):
1021 ('SubType', c_uint8
),
1024 # Cheat and use c_uint16 since we don't care about alignment
1025 ('Length', c_uint16
)
1029 class PCI_DEVICE_PATH(LittleEndianStructure
):
1032 ('Header', EFI_DEVICE_PATH
),
1033 ('Function', c_uint8
),
1038 class PCCARD_DEVICE_PATH(LittleEndianStructure
):
1041 ('Header', EFI_DEVICE_PATH
),
1042 ('FunctionNumber', c_uint8
),
1046 class MEMMAP_DEVICE_PATH(LittleEndianStructure
):
1049 ('Header', EFI_DEVICE_PATH
),
1050 ('StartingAddress', c_uint64
),
1051 ('EndingAddress', c_uint64
),
1055 class VENDOR_DEVICE_PATH(LittleEndianStructure
):
1058 ('Header', EFI_DEVICE_PATH
),
1063 class CONTROLLER_DEVICE_PATH(LittleEndianStructure
):
1066 ('Header', EFI_DEVICE_PATH
),
1067 ('ControllerNumber', c_uint32
),
1071 class BMC_DEVICE_PATH(LittleEndianStructure
):
1074 ('Header', EFI_DEVICE_PATH
),
1075 ('InterfaceType', c_uint8
),
1076 ('BaseAddress', ARRAY(c_uint8
, 8)),
1080 class BBS_BBS_DEVICE_PATH(LittleEndianStructure
):
1083 ('Header', EFI_DEVICE_PATH
),
1084 ('DeviceType', c_uint16
),
1085 ('StatusFlag', c_uint16
)
1089 class ACPI_HID_DEVICE_PATH(LittleEndianStructure
):
1092 ('Header', EFI_DEVICE_PATH
),
1098 class ACPI_EXTENDED_HID_DEVICE_PATH(LittleEndianStructure
):
1101 ('Header', EFI_DEVICE_PATH
),
1108 class ACPI_ADR_DEVICE_PATH(LittleEndianStructure
):
1111 ('Header', EFI_DEVICE_PATH
),
1116 class ACPI_NVDIMM_DEVICE_PATH(LittleEndianStructure
):
1119 ('Header', EFI_DEVICE_PATH
),
1120 ('NFITDeviceHandle', c_uint32
)
1124 class ATAPI_DEVICE_PATH(LittleEndianStructure
):
1127 ('Header', EFI_DEVICE_PATH
),
1128 ("PrimarySecondary", c_uint8
),
1129 ("SlaveMaster", c_uint8
),
1134 class SCSI_DEVICE_PATH(LittleEndianStructure
):
1137 ('Header', EFI_DEVICE_PATH
),
1143 class FIBRECHANNEL_DEVICE_PATH(LittleEndianStructure
):
1146 ('Header', EFI_DEVICE_PATH
),
1147 ("Reserved", c_uint32
),
1153 class F1394_DEVICE_PATH(LittleEndianStructure
):
1156 ('Header', EFI_DEVICE_PATH
),
1157 ("Reserved", c_uint32
),
1162 class USB_DEVICE_PATH(LittleEndianStructure
):
1165 ('Header', EFI_DEVICE_PATH
),
1166 ("ParentPortNumber", c_uint8
),
1167 ("InterfaceNumber", c_uint8
),
1171 class I2O_DEVICE_PATH(LittleEndianStructure
):
1174 ('Header', EFI_DEVICE_PATH
),
1179 class INFINIBAND_DEVICE_PATH(LittleEndianStructure
):
1182 ('Header', EFI_DEVICE_PATH
),
1183 ("ResourceFlags", c_uint32
),
1184 ("PortGid", ARRAY(c_uint8
, 16)),
1185 ("ServiceId", c_uint64
),
1186 ("TargetPortId", c_uint64
),
1187 ("DeviceId", c_uint64
)
1191 class UART_FLOW_CONTROL_DEVICE_PATH(LittleEndianStructure
):
1194 ('Header', EFI_DEVICE_PATH
),
1196 ("FlowControlMap", c_uint32
)
1200 class SAS_DEVICE_PATH(LittleEndianStructure
):
1203 ('Header', EFI_DEVICE_PATH
),
1205 ("Reserved", c_uint32
),
1206 ("SasAddress", c_uint64
),
1208 ("DeviceTopology", c_uint16
),
1209 ("RelativeTargetPort", c_uint16
)
1213 class EFI_MAC_ADDRESS(LittleEndianStructure
):
1216 ("Addr", ARRAY(c_uint8
, 32)),
1220 class MAC_ADDR_DEVICE_PATH(LittleEndianStructure
):
1223 ('Header', EFI_DEVICE_PATH
),
1224 ('MacAddress', EFI_MAC_ADDRESS
),
1229 class IPv4_ADDRESS(LittleEndianStructure
):
1231 ("Addr", ARRAY(c_uint8
, 4)),
1235 class IPv4_DEVICE_PATH(LittleEndianStructure
):
1238 ('Header', EFI_DEVICE_PATH
),
1239 ('LocalIpAddress', IPv4_ADDRESS
),
1240 ('RemoteIpAddress', IPv4_ADDRESS
),
1241 ('LocalPort', c_uint16
),
1242 ('RemotePort', c_uint16
),
1243 ('Protocol', c_uint16
),
1244 ('StaticIpAddress', c_uint8
),
1245 ('GatewayIpAddress', IPv4_ADDRESS
),
1246 ('SubnetMask', IPv4_ADDRESS
)
1250 class IPv6_ADDRESS(LittleEndianStructure
):
1252 ("Addr", ARRAY(c_uint8
, 16)),
1256 class IPv6_DEVICE_PATH(LittleEndianStructure
):
1259 ('Header', EFI_DEVICE_PATH
),
1260 ('LocalIpAddress', IPv6_ADDRESS
),
1261 ('RemoteIpAddress', IPv6_ADDRESS
),
1262 ('LocalPort', c_uint16
),
1263 ('RemotePort', c_uint16
),
1264 ('Protocol', c_uint16
),
1265 ('IpAddressOrigin', c_uint8
),
1266 ('PrefixLength', c_uint8
),
1267 ('GatewayIpAddress', IPv6_ADDRESS
)
1271 class UART_DEVICE_PATH(LittleEndianStructure
):
1274 ('Header', EFI_DEVICE_PATH
),
1275 ('Reserved', c_uint32
),
1276 ('BaudRate', c_uint64
),
1277 ('DataBits', c_uint8
),
1278 ('Parity', c_uint8
),
1279 ('StopBits', c_uint8
)
1283 class USB_CLASS_DEVICE_PATH(LittleEndianStructure
):
1286 ('Header', EFI_DEVICE_PATH
),
1287 ('VendorId', c_uint16
),
1288 ('ProductId', c_uint16
),
1289 ('DeviceClass', c_uint8
),
1290 ('DeviceCSjblass', c_uint8
),
1291 ('DeviceProtocol', c_uint8
),
1295 class USB_WWID_DEVICE_PATH(LittleEndianStructure
):
1298 ('Header', EFI_DEVICE_PATH
),
1299 ('InterfaceNumber', c_uint16
),
1300 ('VendorId', c_uint16
),
1301 ('ProductId', c_uint16
),
1305 class DEVICE_LOGICAL_UNIT_DEVICE_PATH(LittleEndianStructure
):
1308 ('Header', EFI_DEVICE_PATH
),
1313 class SATA_DEVICE_PATH(LittleEndianStructure
):
1316 ('Header', EFI_DEVICE_PATH
),
1317 ('HBAPortNumber', c_uint16
),
1318 ('PortMultiplierPortNumber', c_uint16
),
1323 class ISCSI_DEVICE_PATH(LittleEndianStructure
):
1326 ('Header', EFI_DEVICE_PATH
),
1327 ('NetworkProtocol', c_uint16
),
1328 ('LoginOption', c_uint16
),
1330 ('TargetPortalGroupTag', c_uint16
),
1334 class VLAN_DEVICE_PATH(LittleEndianStructure
):
1337 ('Header', EFI_DEVICE_PATH
),
1338 ("VlandId", c_uint16
)
1342 class FIBRECHANNELEX_DEVICE_PATH(LittleEndianStructure
):
1345 ('Header', EFI_DEVICE_PATH
),
1346 ("Reserved", c_uint16
),
1347 ("WWN", ARRAY(c_uint8
, 8)),
1348 ("Lun", ARRAY(c_uint8
, 8)),
1352 class SASEX_DEVICE_PATH(LittleEndianStructure
):
1355 ('Header', EFI_DEVICE_PATH
),
1356 ("SasAddress", ARRAY(c_uint8
, 8)),
1357 ("Lun", ARRAY(c_uint8
, 8)),
1358 ("DeviceTopology", c_uint16
),
1359 ("RelativeTargetPort", c_uint16
)
1363 class NVME_NAMESPACE_DEVICE_PATH(LittleEndianStructure
):
1366 ('Header', EFI_DEVICE_PATH
),
1367 ("NamespaceId", c_uint32
),
1368 ("NamespaceUuid", c_uint64
)
1372 class DNS_DEVICE_PATH(LittleEndianStructure
):
1375 ('Header', EFI_DEVICE_PATH
),
1376 ("IsIPv6", c_uint8
),
1377 ("DnsServerIp", IPv6_ADDRESS
)
1382 class UFS_DEVICE_PATH(LittleEndianStructure
):
1385 ('Header', EFI_DEVICE_PATH
),
1391 class SD_DEVICE_PATH(LittleEndianStructure
):
1394 ('Header', EFI_DEVICE_PATH
),
1395 ("SlotNumber", c_uint8
)
1399 class BLUETOOTH_ADDRESS(LittleEndianStructure
):
1402 ("Address", ARRAY(c_uint8
, 6))
1406 class BLUETOOTH_LE_ADDRESS(LittleEndianStructure
):
1409 ("Format", c_uint8
),
1414 class BLUETOOTH_DEVICE_PATH(LittleEndianStructure
):
1417 ('Header', EFI_DEVICE_PATH
),
1418 ("BD_ADDR", BLUETOOTH_ADDRESS
)
1422 class WIFI_DEVICE_PATH(LittleEndianStructure
):
1425 ('Header', EFI_DEVICE_PATH
),
1426 ("SSId", ARRAY(c_uint8
, 32))
1430 class EMMC_DEVICE_PATH(LittleEndianStructure
):
1433 ('Header', EFI_DEVICE_PATH
),
1434 ("SlotNumber", c_uint8
)
1438 class BLUETOOTH_LE_DEVICE_PATH(LittleEndianStructure
):
1441 ('Header', EFI_DEVICE_PATH
),
1442 ("BD_ADDR", BLUETOOTH_LE_ADDRESS
)
1446 class NVDIMM_NAMESPACE_DEVICE_PATH(LittleEndianStructure
):
1449 ('Header', EFI_DEVICE_PATH
),
1454 class REST_SERVICE_DEVICE_PATH(LittleEndianStructure
):
1457 ('Header', EFI_DEVICE_PATH
),
1458 ("RESTService", c_uint8
),
1459 ("AccessMode", c_uint8
)
1463 class REST_VENDOR_SERVICE_DEVICE_PATH(LittleEndianStructure
):
1466 ('Header', EFI_DEVICE_PATH
),
1467 ("RESTService", c_uint8
),
1468 ("AccessMode", c_uint8
),
1473 class HARDDRIVE_DEVICE_PATH(LittleEndianStructure
):
1476 ('Header', EFI_DEVICE_PATH
),
1477 ('PartitionNumber', c_uint32
),
1478 ('PartitionStart', c_uint64
),
1479 ('PartitionSize', c_uint64
),
1480 ('Signature', ARRAY(c_uint8
, 16)),
1481 ('MBRType', c_uint8
),
1482 ('SignatureType', c_uint8
)
1486 class CDROM_DEVICE_PATH(LittleEndianStructure
):
1489 ('Header', EFI_DEVICE_PATH
),
1490 ('BootEntry', c_uint32
),
1491 ('PartitionStart', c_uint64
),
1492 ('PartitionSize', c_uint64
)
1496 class MEDIA_PROTOCOL_DEVICE_PATH(LittleEndianStructure
):
1499 ('Header', EFI_DEVICE_PATH
),
1500 ('Protocol', EFI_GUID
)
1504 class MEDIA_FW_VOL_FILEPATH_DEVICE_PATH(LittleEndianStructure
):
1507 ('Header', EFI_DEVICE_PATH
),
1508 ('FvFileName', EFI_GUID
)
1512 class MEDIA_FW_VOL_DEVICE_PATH(LittleEndianStructure
):
1515 ('Header', EFI_DEVICE_PATH
),
1516 ('FvName', EFI_GUID
)
1520 class MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH(LittleEndianStructure
):
1523 ('Header', EFI_DEVICE_PATH
),
1524 ('Reserved', c_uint32
),
1525 ('StartingOffset', c_uint64
),
1526 ('EndingOffset', c_uint64
)
1530 class MEDIA_RAM_DISK_DEVICE_PATH(LittleEndianStructure
):
1533 ('Header', EFI_DEVICE_PATH
),
1534 ('StartingAddr', c_uint64
),
1535 ('EndingAddr', c_uint64
),
1536 ('TypeGuid', EFI_GUID
),
1537 ('Instance', c_uint16
)
1541 class EfiDevicePath
:
1543 Parse EFI Device Paths based on the edk2 C Structures defined above.
1544 In the context of this class verbose means hexdump extra data.
1550 List of devixe path instances. Each instance is a list of nodes
1551 for the given Device Path instance.
1555 device_path_node(address)
1556 return the Device Path ctype hdr, ctype, and any extra data in
1557 the Device Path node. This is just a single Device Path node,
1558 not the entire Device Path.
1559 device_path_node_str(address)
1560 return the device path node (not the entire Device Path) as a string
1565 device_path_dict
= {
1566 # ( Type, SubType ) : Device Path C typedef
1567 # HARDWARE_DEVICE_PATH
1568 (1, 1): PCI_DEVICE_PATH
,
1569 (1, 2): PCCARD_DEVICE_PATH
,
1570 (1, 3): MEMMAP_DEVICE_PATH
,
1571 (1, 4): VENDOR_DEVICE_PATH
,
1572 (1, 5): CONTROLLER_DEVICE_PATH
,
1573 (1, 6): BMC_DEVICE_PATH
,
1576 (2, 1): ACPI_HID_DEVICE_PATH
,
1577 (2, 2): ACPI_EXTENDED_HID_DEVICE_PATH
,
1578 (2, 3): ACPI_ADR_DEVICE_PATH
,
1579 (2, 4): ACPI_NVDIMM_DEVICE_PATH
,
1581 # MESSAGING_DEVICE_PATH
1582 (3, 1): ATAPI_DEVICE_PATH
,
1583 (3, 2): SCSI_DEVICE_PATH
,
1584 (3, 3): FIBRECHANNEL_DEVICE_PATH
,
1585 (3, 4): F1394_DEVICE_PATH
,
1586 (3, 5): USB_DEVICE_PATH
,
1587 (3, 6): I2O_DEVICE_PATH
,
1589 (3, 9): INFINIBAND_DEVICE_PATH
,
1590 (3, 10): VENDOR_DEVICE_PATH
,
1591 (3, 11): MAC_ADDR_DEVICE_PATH
,
1592 (3, 12): IPv4_DEVICE_PATH
,
1593 (3, 13): IPv6_DEVICE_PATH
,
1594 (3, 14): UART_DEVICE_PATH
,
1595 (3, 15): USB_CLASS_DEVICE_PATH
,
1596 (3, 16): USB_WWID_DEVICE_PATH
,
1597 (3, 17): DEVICE_LOGICAL_UNIT_DEVICE_PATH
,
1598 (3, 18): SATA_DEVICE_PATH
,
1599 (3, 19): ISCSI_DEVICE_PATH
,
1600 (3, 20): VLAN_DEVICE_PATH
,
1601 (3, 21): FIBRECHANNELEX_DEVICE_PATH
,
1602 (3, 22): SASEX_DEVICE_PATH
,
1603 (3, 23): NVME_NAMESPACE_DEVICE_PATH
,
1604 (3, 24): DNS_DEVICE_PATH
,
1605 (3, 25): UFS_DEVICE_PATH
,
1606 (3, 26): SD_DEVICE_PATH
,
1607 (3, 27): BLUETOOTH_DEVICE_PATH
,
1608 (3, 28): WIFI_DEVICE_PATH
,
1609 (3, 29): EMMC_DEVICE_PATH
,
1610 (3, 30): BLUETOOTH_LE_DEVICE_PATH
,
1611 (3, 31): DNS_DEVICE_PATH
,
1612 (3, 32): NVDIMM_NAMESPACE_DEVICE_PATH
,
1614 (3, 33): REST_SERVICE_DEVICE_PATH
,
1615 (3, 34): REST_VENDOR_SERVICE_DEVICE_PATH
,
1618 (4, 1): HARDDRIVE_DEVICE_PATH
,
1619 (4, 2): CDROM_DEVICE_PATH
,
1620 (4, 3): VENDOR_DEVICE_PATH
,
1621 (4, 4): EFI_DEVICE_PATH
,
1622 (4, 5): MEDIA_PROTOCOL_DEVICE_PATH
,
1623 (4, 6): MEDIA_FW_VOL_FILEPATH_DEVICE_PATH
,
1624 (4, 7): MEDIA_FW_VOL_DEVICE_PATH
,
1625 (4, 8): MEDIA_RELATIVE_OFFSET_RANGE_DEVICE_PATH
,
1626 (4, 9): MEDIA_RAM_DISK_DEVICE_PATH
,
1629 (5, 1): BBS_BBS_DEVICE_PATH
,
1633 guid_override_dict
= {
1634 uuid
.UUID('37499A9D-542F-4C89-A026-35DA142094E4'):
1635 UART_FLOW_CONTROL_DEVICE_PATH
,
1636 uuid
.UUID('D487DDB4-008B-11D9-AFDC-001083FFCA4D'):
1640 def __init__(self
, file, ptr
=None, verbose
=False, count
=64):
1642 Convert ptr into a list of Device Path nodes. If verbose also hexdump
1646 self
._verbose
= verbose
1652 for _
in range(count
): # while True
1653 hdr
, _
= self
._ctype
_read
_ex
(EFI_DEVICE_PATH
, ptr
)
1654 if hdr
.Length
< sizeof(EFI_DEVICE_PATH
):
1655 # Not a valid device path
1658 if hdr
.Type
== 0x7F: # END_DEVICE_PATH_TYPE
1659 self
.DevicePath
.append(instance
)
1660 if hdr
.SubType
== 0xFF: # END_ENTIRE_DEVICE_PATH_SUBTYPE
1662 if hdr
.SubType
== 0x01: # END_INSTANCE_DEVICE_PATH_SUBTYPE
1663 # start new device path instance
1666 type_str
= self
.device_path_dict
.get(
1667 (hdr
.Type
, hdr
.SubType
), EFI_DEVICE_PATH
)
1668 node
, extra
= self
._ctype
_read
_ex
(type_str
, ptr
, hdr
.Length
)
1669 if 'VENDOR_DEVICE_PATH' in type(node
).__name
__:
1670 guid_type
= self
.guid_override_dict
.get(
1671 GuidNames
.to_uuid(node
.Guid
), None)
1673 # use the ctype associated with the GUID
1674 node
, extra
= self
._ctype
_read
_ex
(
1675 guid_type
, ptr
, hdr
.Length
)
1677 instance
.append((type(node
).__name
__, hdr
.Type
,
1678 hdr
.SubType
, hdr
.Length
, node
, extra
))
1685 if not self
.valid():
1686 return '<class: EfiDevicePath>'
1689 for instance
in self
.DevicePath
:
1690 for (Name
, Type
, SubType
, Length
, cnode
, extra
) in instance
:
1691 result
+= f
'{Name:s} {Type:2d}:{SubType:2d} Len: {Length:3d}\n'
1692 result
+= ctype_to_str(cnode
, ' ', ['Reserved'])
1694 if extra
is not None:
1695 result
+= hexdump(extra
, ' ')
1701 return True if self
.DevicePath
else False
1703 def device_path_node(self
, address
):
1705 hdr
, _
= self
._ctype
_read
_ex
(EFI_DEVICE_PATH
, address
)
1706 if hdr
.Length
< sizeof(EFI_DEVICE_PATH
):
1707 return None, None, None
1709 type_str
= self
.device_path_dict
.get(
1710 (hdr
.Type
, hdr
.SubType
), EFI_DEVICE_PATH
)
1711 cnode
, extra
= self
._ctype
_read
_ex
(type_str
, address
, hdr
.Length
)
1712 return hdr
, cnode
, extra
1714 return None, None, None
1716 def device_path_node_str(self
, address
, verbose
=False):
1717 hdr
, cnode
, extra
= self
.device_path_node(address
)
1721 cname
= type(cnode
).__name
__
1722 result
= f
'{cname:s} {hdr.Type:2d}:{hdr.SubType:2d} '
1723 result
+= f
'Len: 0x{hdr.Length:03x}\n'
1724 result
+= ctype_to_str(cnode
, ' ', ['Reserved'])
1726 if extra
is not None:
1727 result
+= hexdump(extra
, ' ')
1731 def _ctype_read_ex(self
, ctype_struct
, offset
=0, rsize
=None):
1733 self
._file
.seek(offset
)
1735 type_size
= sizeof(ctype_struct
)
1736 size
= rsize
if rsize
else type_size
1737 data
= self
._file
.read(size
)
1741 cdata
= ctype_struct
.from_buffer(bytearray(data
))
1743 if size
> type_size
:
1744 return cdata
, data
[type_size
:]
1749 class EfiConfigurationTable
:
1751 A class to abstract EFI Configuration Tables from gST->ConfigurationTable
1752 and gST->NumberOfTableEntries. Pass in the gST pointer from EFI,
1753 likely you need to look up this address after you have loaded symbols
1757 ConfigurationTableDict : dictionary
1758 dictionary of EFI Configuration Table entries
1762 GetConfigTable(uuid)
1763 pass in VendorGuid and return VendorTable from EFI System Table
1764 DebugImageInfo(table)
1765 return tuple of load address and size of PE/COFF images
1768 ConfigurationTableDict
= {}
1770 def __init__(self
, file, gST_addr
=None):
1772 if gST_addr
is None:
1773 # ToDo add code to search for gST via EFI_SYSTEM_TABLE_POINTER
1776 gST
= self
._ctype
_read
(EFI_SYSTEM_TABLE
, gST_addr
)
1777 self
.read_efi_config_table(gST
.NumberOfTableEntries
,
1778 gST
.ConfigurationTable
,
1783 '''return EFI_CONFIGURATION_TABLE entries as a string'''
1785 for key
, value
in cls
.ConfigurationTableDict
.items():
1786 result
+= f
'{GuidNames().to_name(key):>37s}: '
1787 result
+= f
'VendorTable = 0x{value:08x}\n'
1791 def _ctype_read(self
, ctype_struct
, offset
=0):
1792 '''ctype worker function to read data'''
1794 self
._file
.seek(offset
)
1796 data
= self
._file
.read(sizeof(ctype_struct
))
1797 return ctype_struct
.from_buffer(bytearray(data
))
1800 def read_efi_config_table(cls
, table_cnt
, table_ptr
, ctype_read
):
1801 '''Create a dictionary of EFI Configuration table entries'''
1802 EmptryTables
= EFI_CONFIGURATION_TABLE
* table_cnt
1803 Tables
= ctype_read(EmptryTables
, table_ptr
)
1804 for i
in range(table_cnt
):
1805 cls
.ConfigurationTableDict
[str(GuidNames
.to_uuid(
1806 Tables
[i
].VendorGuid
)).upper()] = Tables
[i
].VendorTable
1808 return cls
.ConfigurationTableDict
1810 def GetConfigTable(self
, uuid
):
1811 ''' Return VendorTable for VendorGuid (uuid.UUID) or None'''
1812 return self
.ConfigurationTableDict
.get(uuid
.upper())
1814 def DebugImageInfo(self
, table
=None):
1816 Walk the debug image info table to find the LoadedImage protocols
1817 for all the loaded PE/COFF images and return a list of load address
1823 table
= self
.GetConfigTable('49152e77-1ada-4764-b7a2-7afefed95e8b')
1825 DbgInfoHdr
= self
._ctype
_read
(EFI_DEBUG_IMAGE_INFO_TABLE_HEADER
, table
)
1826 NormalImageArray
= EFI_DEBUG_IMAGE_INFO
* DbgInfoHdr
.TableSize
1827 NormalImageArray
= self
._ctype
_read
(
1828 NormalImageArray
, DbgInfoHdr
.EfiDebugImageInfoTable
)
1829 for i
in range(DbgInfoHdr
.TableSize
):
1830 ImageInfo
= self
._ctype
_read
(
1831 EFI_DEBUG_IMAGE_INFO_NORMAL
, NormalImageArray
[i
].NormalImage
)
1832 LoadedImage
= self
._ctype
_read
(
1833 EFI_LOADED_IMAGE_PROTOCOL
,
1834 ImageInfo
.LoadedImageProtocolInstance
)
1835 ImageLoad
.append((LoadedImage
.ImageBase
, LoadedImage
.ImageSize
))
1842 A class to abstract PE/COFF or TE image processing via passing in a
1843 Python file like object. If you pass in an address the PE/COFF is parsed,
1844 if you pass in NULL for an address then you get a class instance you can
1845 use to search memory for a PE/COFF hader given a pc value.
1850 Load address of the PE/COFF image
1851 AddressOfEntryPoint : int
1852 Address of the Entry point of the PE/COFF image
1854 Start of the PE/COFF text section
1856 Start of the PE/COFF data section
1858 File name of the symbols file
1859 CodeViewUuid : uuid:UUID
1860 GUID for "RSDS" Debug Directory entry, or Mach-O UUID for "MTOC"
1864 pcToPeCoff(address, step, max_range, rom_range)
1865 Given an address(pc) find the PE/COFF image it is in
1867 return a string giving info for all the PE/COFF sections
1870 def __init__(self
, file, address
=0):
1873 # book keeping, but public
1877 self
.Subsystem
= None
1878 self
.CodeViewSig
= None
1880 self
.NumberOfSections
= 0
1881 self
.Sections
= None
1883 # Things debuggers may want to know
1884 self
.LoadAddress
= 0 if address
is None else address
1885 self
.EndLoadAddress
= 0
1886 self
.AddressOfEntryPoint
= 0
1887 self
.TextAddress
= 0
1888 self
.DataAddress
= 0
1889 self
.CodeViewPdb
= None
1890 self
.CodeViewUuid
= None
1896 2: 'Resource Table',
1897 3: 'Exception Table',
1898 4: 'Certificate Table',
1899 5: 'Relocation Table',
1904 10: 'Load Config Table',
1907 13: 'Delay Import Descriptor',
1908 14: 'CLR Runtime Header',
1912 if address
is not None:
1917 if self
.PeHdr
is None and self
.TeHdr
is None:
1918 # no PE/COFF header found
1919 return "<class: PeTeImage>"
1921 if self
.CodeViewPdb
:
1922 pdb
= f
'{self.Machine}`{self.CodeViewPdb}'
1924 pdb
= 'No Debug Info:'
1926 if self
.CodeViewUuid
:
1927 guid
= f
'{self.CodeViewUuid}:'
1931 slide
= f
'slide = {self.TeAdjust:d} ' if self
.TeAdjust
!= 0 else ' '
1932 res
= guid
+ f
'{pdb} load = 0x{self.LoadAddress:08x} ' + slide
1935 def _seek(self
, offset
):
1937 seek() relative to start of PE/COFF (TE) image
1939 self
._file
.seek(self
.LoadAddress
+ offset
)
1941 def _read_offset(self
, size
, offset
=None):
1943 read() relative to start of PE/COFF (TE) image
1944 if offset is not None then seek() before the read
1946 if offset
is not None:
1949 return self
._file
.read(size
)
1951 def _read_ctype(self
, ctype_struct
, offset
=None):
1952 data
= self
._read
_offset
(sizeof(ctype_struct
), offset
)
1953 return ctype_struct
.from_buffer(bytearray(data
), 0)
1955 def _unsigned(self
, i
):
1956 """return a 32-bit unsigned int (UINT32) """
1957 return int.from_bytes(i
, byteorder
='little', signed
=False)
1959 def pcToPeCoff(self
,
1963 rom_range
=[0xFE800000, 0xFFFFFFFF]):
1965 Given an address search backwards for PE/COFF (TE) header
1966 For DXE 4K is probably OK
1967 For PEI you might have to search every 4 bytes.
1972 if max_range
is None:
1973 max_range
= 0x200000
1975 if address
in range(*rom_range
):
1976 # The XIP code in the ROM ends up 4 byte aligned.
1978 max_range
= min(max_range
, 0x100000)
1980 # Align address to page boundary for memory image search.
1981 address
= address
& ~
(step
-1)
1982 # Search every step backward
1983 offset_range
= list(range(0, min(max_range
, address
), step
))
1984 for offset
in offset_range
:
1985 if self
.maybe(address
- offset
):
1991 def maybe(self
, offset
=None):
1992 """Probe to see if this offset is likely a PE/COFF or TE file """
1993 self
.LoadAddress
= 0
1994 e_magic
= self
._read
_offset
(2, offset
)
1995 header_ok
= e_magic
== b
'MZ' or e_magic
== b
'VZ'
1996 if offset
is not None and header_ok
:
1997 self
.LoadAddress
= offset
2001 """Parse PE/COFF (TE) debug directory entry """
2002 DosHdr
= self
._read
_ctype
(EFI_IMAGE_DOS_HEADER
, 0)
2003 if DosHdr
.e_magic
== self
._unsigned
(b
'VZ'):
2005 self
.TeHdr
= self
._read
_ctype
(EFI_TE_IMAGE_HEADER
, 0)
2007 self
.TeAdjust
= sizeof(self
.TeHdr
) - self
.TeHdr
.StrippedSize
2008 self
.Machine
= image_machine_dict
.get(self
.TeHdr
.Machine
, None)
2009 self
.Subsystem
= self
.TeHdr
.Subsystem
2010 self
.AddressOfEntryPoint
= self
.TeHdr
.AddressOfEntryPoint
2012 debug_dir_size
= self
.TeHdr
.DataDirectoryDebug
.Size
2013 debug_dir_offset
= (self
.TeAdjust
+
2014 self
.TeHdr
.DataDirectoryDebug
.VirtualAddress
)
2016 if DosHdr
.e_magic
== self
._unsigned
(b
'MZ'):
2017 self
.e_lfanew
= DosHdr
.e_lfanew
2021 self
.PeHdr
= self
._read
_ctype
(
2022 EFI_IMAGE_NT_HEADERS64
, self
.e_lfanew
)
2023 if self
.PeHdr
.Signature
!= self
._unsigned
(b
'PE\0\0'):
2026 if self
.PeHdr
.OptionalHeader
.Magic
== \
2027 EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
2028 self
.PeHdr
= self
._read
_ctype
(
2029 EFI_IMAGE_NT_HEADERS32
, self
.e_lfanew
)
2031 if self
.PeHdr
.OptionalHeader
.NumberOfRvaAndSizes
<= \
2035 self
.Machine
= image_machine_dict
.get(
2036 self
.PeHdr
.FileHeader
.Machine
, None)
2037 self
.Subsystem
= self
.PeHdr
.OptionalHeader
.Subsystem
2038 self
.AddressOfEntryPoint
= \
2039 self
.PeHdr
.OptionalHeader
.AddressOfEntryPoint
2042 debug_dir_size
= self
.PeHdr
.OptionalHeader
.DataDirectory
[
2043 DIRECTORY_DEBUG
].Size
2044 debug_dir_offset
= self
.PeHdr
.OptionalHeader
.DataDirectory
[
2045 DIRECTORY_DEBUG
].VirtualAddress
2047 if self
.Machine
is None or self
.Subsystem
not in [0, 10, 11, 12]:
2050 self
.AddressOfEntryPoint
+= self
.LoadAddress
2053 return self
.processDebugDirEntry(debug_dir_offset
, debug_dir_size
)
2056 '''Parse the PE/COFF (TE) section table'''
2057 if self
.Sections
is not None:
2059 elif self
.TeHdr
is not None:
2060 self
.NumberOfSections
= self
.TeHdr
.NumberOfSections
2061 offset
= sizeof(EFI_TE_IMAGE_HEADER
)
2062 elif self
.PeHdr
is not None:
2063 self
.NumberOfSections
= self
.PeHdr
.FileHeader
.NumberOfSections
2064 offset
= sizeof(c_uint32
) + \
2065 sizeof(EFI_IMAGE_FILE_HEADER
)
2066 offset
+= self
.PeHdr
.FileHeader
.SizeOfOptionalHeader
2067 offset
+= self
.e_lfanew
2071 self
.Sections
= EFI_IMAGE_SECTION_HEADER
* self
.NumberOfSections
2072 self
.Sections
= self
._read
_ctype
(self
.Sections
, offset
)
2074 for i
in range(self
.NumberOfSections
):
2075 name
= str(self
.Sections
[i
].Name
, 'ascii', 'ignore')
2076 addr
= self
.Sections
[i
].VirtualAddress
2077 addr
+= self
.LoadAddress
+ self
.TeAdjust
2079 self
.TextAddress
= addr
2080 elif name
== '.data':
2081 self
.DataAddress
= addr
2083 end_addr
= addr
+ self
.Sections
[i
].VirtualSize
- 1
2084 if end_addr
> self
.EndLoadAddress
:
2085 self
.EndLoadAddress
= end_addr
2087 def sections_to_str(self
):
2088 # return text summary of sections
2089 # name virt addr (virt size) flags:Characteristics
2091 for i
in range(self
.NumberOfSections
):
2092 name
= str(self
.Sections
[i
].Name
, 'ascii', 'ignore')
2093 result
+= f
'{name:8s} '
2094 result
+= f
'0x{self.Sections[i].VirtualAddress:08X} '
2095 result
+= f
'(0x{self.Sections[i].VirtualSize:05X}) '
2096 result
+= f
'flags:0x{self.Sections[i].Characteristics:08X}\n'
2100 def directory_to_str(self
):
2103 debug_size
= self
.TeHdr
.DataDirectoryDebug
.Size
2105 debug_offset
= (self
.TeAdjust
2106 + self
.TeHdr
.DataDirectoryDebug
.VirtualAddress
)
2107 result
+= f
"Debug 0x{debug_offset:08X} 0x{debug_size}\n"
2109 relocation_size
= self
.TeHdr
.DataDirectoryBaseReloc
.Size
2110 if relocation_size
> 0:
2111 relocation_offset
= (
2113 + self
.TeHdr
.DataDirectoryBaseReloc
.VirtualAddress
)
2114 result
+= f
'Relocation 0x{relocation_offset:08X} '
2115 result
+= f
' 0x{relocation_size}\n'
2118 for i
in range(self
.PeHdr
.OptionalHeader
.NumberOfRvaAndSizes
):
2119 size
= self
.PeHdr
.OptionalHeader
.DataDirectory
[i
].Size
2123 virt_addr
= self
.PeHdr
.OptionalHeader
.DataDirectory
[
2125 name
= self
.dir_name
.get(i
, '?')
2126 result
+= f
'{name:s} 0x{virt_addr:08X} 0x{size:X}\n'
2130 def processDebugDirEntry(self
, virt_address
, virt_size
):
2131 """Process PE/COFF Debug Directory Entry"""
2132 if (virt_address
== 0 or
2133 virt_size
< sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
)):
2136 data
= bytearray(self
._read
_offset
(virt_size
, virt_address
))
2137 for offset
in range(0,
2139 sizeof(EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
)):
2140 DirectoryEntry
= EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
.from_buffer(
2142 if DirectoryEntry
.Type
!= 2:
2145 entry
= self
._read
_offset
(
2146 DirectoryEntry
.SizeOfData
, DirectoryEntry
.RVA
+ self
.TeAdjust
)
2147 self
.CodeViewSig
= entry
[:4]
2148 if self
.CodeViewSig
== b
'MTOC':
2149 self
.CodeViewUuid
= uuid
.UUID(bytes_le
=entry
[4:4+16])
2151 elif self
.CodeViewSig
== b
'RSDS':
2152 self
.CodeViewUuid
= uuid
.UUID(bytes_le
=entry
[4:4+16])
2154 elif self
.CodeViewSig
== b
'NB10':
2159 # can't find documentation about Pdb string encoding?
2160 # guessing utf-8 since that will match file systems in macOS
2161 # and Linux Windows is UTF-16, or ANSI adjusted for local.
2162 # We might need a different value for Windows here?
2163 self
.CodeViewPdb
= entry
[PdbOffset
:].split(b
'\x00')[
2170 '''Process arguments as PE/COFF files'''
2171 for fname
in sys
.argv
[1:]:
2172 with
open(fname
, 'rb') as f
:
2173 image
= PeTeImage(f
)
2175 res
= f
'EntryPoint = 0x{image.AddressOfEntryPoint:08x} '
2176 res
+= f
'TextAddress = 0x{image.TextAddress:08x} '
2177 res
+= f
'DataAddress = 0x{image.DataAddress:08x}'
2179 print(image
.sections_to_str())
2180 print('Data Directories:')
2181 print(image
.directory_to_str())
2184 if __name__
== "__main__":