1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
6 #include "missing_efi.h"
10 #define DOS_FILE_MAGIC "MZ"
11 #define PE_FILE_MAGIC "PE\0\0"
12 #define MAX_SECTIONS 96
15 # define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
16 # define TARGET_MACHINE_TYPE_COMPATIBILITY EFI_IMAGE_MACHINE_X64
17 #elif defined(__x86_64__)
18 # define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
19 #elif defined(__aarch64__)
20 # define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
21 #elif defined(__arm__)
22 # define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
23 #elif defined(__riscv) && __riscv_xlen == 64
24 # define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
26 # error Unknown EFI arch
29 #ifndef TARGET_MACHINE_TYPE_COMPATIBILITY
30 # define TARGET_MACHINE_TYPE_COMPATIBILITY 0
33 struct DosFileHeader
{
51 uint16_t reserved2
[10];
55 struct CoffFileHeader
{
57 uint16_t NumberOfSections
;
58 uint32_t TimeDateStamp
;
59 uint32_t PointerToSymbolTable
;
60 uint32_t NumberOfSymbols
;
61 uint16_t SizeOfOptionalHeader
;
62 uint16_t Characteristics
;
65 #define OPTHDR32_MAGIC 0x10B /* PE32 OptionalHeader */
66 #define OPTHDR64_MAGIC 0x20B /* PE32+ OptionalHeader */
68 struct PeOptionalHeader
{
73 uint32_t SizeOfInitializedData
;
74 uint32_t SizeOfUninitializeData
;
75 uint32_t AddressOfEntryPoint
;
82 uint64_t ImageBase64
; /* PE32+ */
84 uint32_t SectionAlignment
;
85 uint32_t FileAlignment
;
86 uint16_t MajorOperatingSystemVersion
;
87 uint16_t MinorOperatingSystemVersion
;
88 uint16_t MajorImageVersion
;
89 uint16_t MinorImageVersion
;
90 uint16_t MajorSubsystemVersion
;
91 uint16_t MinorSubsystemVersion
;
92 uint32_t Win32VersionValue
;
94 uint32_t SizeOfHeaders
;
97 uint16_t DllCharacteristics
;
98 /* fields with different sizes for 32/64 omitted */
101 struct PeFileHeader
{
103 struct CoffFileHeader FileHeader
;
104 struct PeOptionalHeader OptionalHeader
;
107 struct PeSectionHeader
{
109 uint32_t VirtualSize
;
110 uint32_t VirtualAddress
;
111 uint32_t SizeOfRawData
;
112 uint32_t PointerToRawData
;
113 uint32_t PointerToRelocations
;
114 uint32_t PointerToLinenumbers
;
115 uint16_t NumberOfRelocations
;
116 uint16_t NumberOfLinenumbers
;
117 uint32_t Characteristics
;
120 static inline BOOLEAN
verify_dos(const struct DosFileHeader
*dos
) {
122 return memcmp(dos
->Magic
, DOS_FILE_MAGIC
, STRLEN(DOS_FILE_MAGIC
)) == 0;
125 static inline BOOLEAN
verify_pe(const struct PeFileHeader
*pe
, BOOLEAN allow_compatibility
) {
127 return memcmp(pe
->Magic
, PE_FILE_MAGIC
, STRLEN(PE_FILE_MAGIC
)) == 0 &&
128 (pe
->FileHeader
.Machine
== TARGET_MACHINE_TYPE
||
129 (allow_compatibility
&& pe
->FileHeader
.Machine
== TARGET_MACHINE_TYPE_COMPATIBILITY
)) &&
130 pe
->FileHeader
.NumberOfSections
> 0 &&
131 pe
->FileHeader
.NumberOfSections
<= MAX_SECTIONS
&&
132 IN_SET(pe
->OptionalHeader
.Magic
, OPTHDR32_MAGIC
, OPTHDR64_MAGIC
);
135 static inline UINTN
section_table_offset(const struct DosFileHeader
*dos
, const struct PeFileHeader
*pe
) {
138 return dos
->ExeHeader
+ offsetof(struct PeFileHeader
, OptionalHeader
) + pe
->FileHeader
.SizeOfOptionalHeader
;
141 static void locate_sections(
142 const struct PeSectionHeader section_table
[],
144 const CHAR8
**sections
,
149 assert(section_table
);
153 for (UINTN i
= 0; i
< n_table
; i
++) {
154 const struct PeSectionHeader
*sect
= section_table
+ i
;
156 for (UINTN j
= 0; sections
[j
]; j
++) {
157 if (memcmp(sect
->Name
, sections
[j
], strlen8((const char *) sections
[j
])) != 0)
161 addrs
[j
] = sect
->VirtualAddress
;
163 offsets
[j
] = sect
->PointerToRawData
;
164 sizes
[j
] = sect
->VirtualSize
;
169 static uint32_t get_compatibility_entry_address(const struct DosFileHeader
*dos
, const struct PeFileHeader
*pe
) {
170 UINTN addr
= 0, size
= 0;
171 static const CHAR8
*sections
[] = { (CHAR8
*) ".compat", NULL
};
173 /* The kernel may provide alternative PE entry points for different PE architectures. This allows
174 * booting a 64bit kernel on 32bit EFI that is otherwise running on a 64bit CPU. The locations of any
175 * such compat entry points are located in a special PE section. */
177 locate_sections((const struct PeSectionHeader
*) ((const uint8_t *) dos
+ section_table_offset(dos
, pe
)),
178 pe
->FileHeader
.NumberOfSections
,
190 uint16_t machine_type
;
191 uint32_t entry_point
;
192 } _packed_ LinuxPeCompat1
;
194 while (size
>= sizeof(LinuxPeCompat1
) && addr
% __alignof__(LinuxPeCompat1
) == 0) {
195 LinuxPeCompat1
*compat
= (LinuxPeCompat1
*) ((uint8_t *) dos
+ addr
);
197 if (compat
->type
== 0 || compat
->size
== 0 || compat
->size
> size
)
200 if (compat
->type
== 1 &&
201 compat
->size
>= sizeof(LinuxPeCompat1
) &&
202 compat
->machine_type
== TARGET_MACHINE_TYPE
)
203 return compat
->entry_point
;
205 addr
+= compat
->size
;
206 size
-= compat
->size
;
212 EFI_STATUS
pe_alignment_info(
214 uint32_t *ret_entry_point_address
,
215 uint32_t *ret_size_of_image
,
216 uint32_t *ret_section_alignment
) {
218 const struct DosFileHeader
*dos
;
219 const struct PeFileHeader
*pe
;
222 assert(ret_entry_point_address
);
224 dos
= (const struct DosFileHeader
*) base
;
225 if (!verify_dos(dos
))
226 return EFI_LOAD_ERROR
;
228 pe
= (const struct PeFileHeader
*) ((const uint8_t *)base
+ dos
->ExeHeader
);
229 if (!verify_pe(pe
, /* allow_compatibility= */ TRUE
))
230 return EFI_LOAD_ERROR
;
232 uint32_t entry_address
= pe
->OptionalHeader
.AddressOfEntryPoint
;
234 /* Look for a compat entry point. */
235 if (pe
->FileHeader
.Machine
!= TARGET_MACHINE_TYPE
) {
236 entry_address
= get_compatibility_entry_address(dos
, pe
);
237 if (entry_address
== 0)
238 /* Image type not supported and no compat entry found. */
239 return EFI_UNSUPPORTED
;
242 *ret_entry_point_address
= entry_address
;
243 if (ret_size_of_image
)
244 *ret_size_of_image
= pe
->OptionalHeader
.SizeOfImage
;
245 if (ret_section_alignment
)
246 *ret_section_alignment
= pe
->OptionalHeader
.SectionAlignment
;
250 EFI_STATUS
pe_memory_locate_sections(
252 const CHAR8
**sections
,
255 const struct DosFileHeader
*dos
;
256 const struct PeFileHeader
*pe
;
264 dos
= (const struct DosFileHeader
*)base
;
265 if (!verify_dos(dos
))
266 return EFI_LOAD_ERROR
;
268 pe
= (const struct PeFileHeader
*)&base
[dos
->ExeHeader
];
269 if (!verify_pe(pe
, /* allow_compatibility= */ FALSE
))
270 return EFI_LOAD_ERROR
;
272 offset
= section_table_offset(dos
, pe
);
273 locate_sections((struct PeSectionHeader
*)&base
[offset
], pe
->FileHeader
.NumberOfSections
,
274 sections
, addrs
, NULL
, sizes
);
279 EFI_STATUS
pe_file_locate_sections(
281 const char16_t
*path
,
282 const CHAR8
**sections
,
285 _cleanup_freepool_
struct PeSectionHeader
*section_table
= NULL
;
286 _cleanup_(file_closep
) EFI_FILE
*handle
= NULL
;
287 struct DosFileHeader dos
;
288 struct PeFileHeader pe
;
289 UINTN len
, section_table_len
;
298 err
= dir
->Open(dir
, &handle
, (char16_t
*) path
, EFI_FILE_MODE_READ
, 0ULL);
299 if (err
!= EFI_SUCCESS
)
303 err
= handle
->Read(handle
, &len
, &dos
);
304 if (err
!= EFI_SUCCESS
)
306 if (len
!= sizeof(dos
) || !verify_dos(&dos
))
307 return EFI_LOAD_ERROR
;
309 err
= handle
->SetPosition(handle
, dos
.ExeHeader
);
310 if (err
!= EFI_SUCCESS
)
314 err
= handle
->Read(handle
, &len
, &pe
);
315 if (err
!= EFI_SUCCESS
)
317 if (len
!= sizeof(pe
) || !verify_pe(&pe
, /* allow_compatibility= */ FALSE
))
318 return EFI_LOAD_ERROR
;
320 section_table_len
= pe
.FileHeader
.NumberOfSections
* sizeof(struct PeSectionHeader
);
321 section_table
= xmalloc(section_table_len
);
323 return EFI_OUT_OF_RESOURCES
;
325 err
= handle
->SetPosition(handle
, section_table_offset(&dos
, &pe
));
326 if (err
!= EFI_SUCCESS
)
329 len
= section_table_len
;
330 err
= handle
->Read(handle
, &len
, section_table
);
331 if (err
!= EFI_SUCCESS
)
333 if (len
!= section_table_len
)
334 return EFI_LOAD_ERROR
;
336 locate_sections(section_table
, pe
.FileHeader
.NumberOfSections
,
337 sections
, NULL
, offsets
, sizes
);