1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <efi.h>
4 #include <efilib.h>
5
6 #include "missing_efi.h"
7 #include "pe.h"
8 #include "util.h"
9
10 #define DOS_FILE_MAGIC "MZ"
11 #define PE_FILE_MAGIC "PE\0\0"
12 #define MAX_SECTIONS 96
13
14 #if defined(__i386__)
15 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
16 #elif defined(__x86_64__)
17 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
18 #elif defined(__aarch64__)
19 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
20 #elif defined(__arm__)
21 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
22 #elif defined(__riscv) && __riscv_xlen == 64
23 #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
24 #else
25 #error Unknown EFI arch
26 #endif
27
28 struct DosFileHeader {
29 UINT8 Magic[2];
30 UINT16 LastSize;
31 UINT16 nBlocks;
32 UINT16 nReloc;
33 UINT16 HdrSize;
34 UINT16 MinAlloc;
35 UINT16 MaxAlloc;
36 UINT16 ss;
37 UINT16 sp;
38 UINT16 Checksum;
39 UINT16 ip;
40 UINT16 cs;
41 UINT16 RelocPos;
42 UINT16 nOverlay;
43 UINT16 reserved[4];
44 UINT16 OEMId;
45 UINT16 OEMInfo;
46 UINT16 reserved2[10];
47 UINT32 ExeHeader;
48 } _packed_;
49
50 struct CoffFileHeader {
51 UINT16 Machine;
52 UINT16 NumberOfSections;
53 UINT32 TimeDateStamp;
54 UINT32 PointerToSymbolTable;
55 UINT32 NumberOfSymbols;
56 UINT16 SizeOfOptionalHeader;
57 UINT16 Characteristics;
58 } _packed_;
59
60 #define OPTHDR32_MAGIC 0x10B /* PE32 OptionalHeader */
61 #define OPTHDR64_MAGIC 0x20B /* PE32+ OptionalHeader */
62
63 struct PeOptionalHeader {
64 UINT16 Magic;
65 UINT8 LinkerMajor;
66 UINT8 LinkerMinor;
67 UINT32 SizeOfCode;
68 UINT32 SizeOfInitializedData;
69 UINT32 SizeOfUninitializeData;
70 UINT32 AddressOfEntryPoint;
71 UINT32 BaseOfCode;
72 union {
73 struct { /* PE32 */
74 UINT32 BaseOfData;
75 UINT32 ImageBase32;
76 };
77 UINT64 ImageBase64; /* PE32+ */
78 };
79 UINT32 SectionAlignment;
80 UINT32 FileAlignment;
81 UINT16 MajorOperatingSystemVersion;
82 UINT16 MinorOperatingSystemVersion;
83 UINT16 MajorImageVersion;
84 UINT16 MinorImageVersion;
85 UINT16 MajorSubsystemVersion;
86 UINT16 MinorSubsystemVersion;
87 UINT32 Win32VersionValue;
88 UINT32 SizeOfImage;
89 UINT32 SizeOfHeaders;
90 UINT32 CheckSum;
91 UINT16 Subsystem;
92 UINT16 DllCharacteristics;
93 /* fields with different sizes for 32/64 omitted */
94 } _packed_;
95
96 struct PeFileHeader {
97 UINT8 Magic[4];
98 struct CoffFileHeader FileHeader;
99 struct PeOptionalHeader OptionalHeader;
100 } _packed_;
101
102 struct PeSectionHeader {
103 UINT8 Name[8];
104 UINT32 VirtualSize;
105 UINT32 VirtualAddress;
106 UINT32 SizeOfRawData;
107 UINT32 PointerToRawData;
108 UINT32 PointerToRelocations;
109 UINT32 PointerToLinenumbers;
110 UINT16 NumberOfRelocations;
111 UINT16 NumberOfLinenumbers;
112 UINT32 Characteristics;
113 } _packed_;
114
verify_dos(const struct DosFileHeader * dos)115 static inline BOOLEAN verify_dos(const struct DosFileHeader *dos) {
116 assert(dos);
117 return CompareMem(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
118 }
119
verify_pe(const struct PeFileHeader * pe)120 static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) {
121 assert(pe);
122 return CompareMem(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
123 pe->FileHeader.Machine == TARGET_MACHINE_TYPE &&
124 pe->FileHeader.NumberOfSections > 0 &&
125 pe->FileHeader.NumberOfSections <= MAX_SECTIONS &&
126 IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC);
127 }
128
section_table_offset(const struct DosFileHeader * dos,const struct PeFileHeader * pe)129 static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
130 assert(dos);
131 assert(pe);
132 return dos->ExeHeader + offsetof(struct PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader;
133 }
134
locate_sections(const struct PeSectionHeader section_table[],UINTN n_table,const CHAR8 ** sections,UINTN * addrs,UINTN * offsets,UINTN * sizes)135 static void locate_sections(
136 const struct PeSectionHeader section_table[],
137 UINTN n_table,
138 const CHAR8 **sections,
139 UINTN *addrs,
140 UINTN *offsets,
141 UINTN *sizes) {
142
143 assert(section_table);
144 assert(sections);
145 assert(sizes);
146
147 for (UINTN i = 0; i < n_table; i++) {
148 const struct PeSectionHeader *sect = section_table + i;
149
150 for (UINTN j = 0; sections[j]; j++) {
151 if (CompareMem(sect->Name, sections[j], strlena(sections[j])) != 0)
152 continue;
153
154 if (addrs)
155 addrs[j] = sect->VirtualAddress;
156 if (offsets)
157 offsets[j] = sect->PointerToRawData;
158 sizes[j] = sect->VirtualSize;
159 }
160 }
161 }
162
pe_alignment_info(const void * base,UINT32 * ret_entry_point_address,UINT32 * ret_size_of_image,UINT32 * ret_section_alignment)163 EFI_STATUS pe_alignment_info(
164 const void *base,
165 UINT32 *ret_entry_point_address,
166 UINT32 *ret_size_of_image,
167 UINT32 *ret_section_alignment) {
168
169 const struct DosFileHeader *dos;
170 const struct PeFileHeader *pe;
171
172 assert(base);
173 assert(ret_entry_point_address);
174 assert(ret_size_of_image);
175 assert(ret_section_alignment);
176
177 dos = (const struct DosFileHeader *) base;
178 if (!verify_dos(dos))
179 return EFI_LOAD_ERROR;
180
181 pe = (const struct PeFileHeader*) ((const UINT8 *)base + dos->ExeHeader);
182 if (!verify_pe(pe))
183 return EFI_LOAD_ERROR;
184
185 *ret_entry_point_address = pe->OptionalHeader.AddressOfEntryPoint;
186 *ret_size_of_image = pe->OptionalHeader.SizeOfImage;
187 *ret_section_alignment = pe->OptionalHeader.SectionAlignment;
188 return EFI_SUCCESS;
189 }
190
pe_memory_locate_sections(const CHAR8 * base,const CHAR8 ** sections,UINTN * addrs,UINTN * sizes)191 EFI_STATUS pe_memory_locate_sections(
192 const CHAR8 *base,
193 const CHAR8 **sections,
194 UINTN *addrs,
195 UINTN *sizes) {
196 const struct DosFileHeader *dos;
197 const struct PeFileHeader *pe;
198 UINTN offset;
199
200 assert(base);
201 assert(sections);
202 assert(addrs);
203 assert(sizes);
204
205 dos = (const struct DosFileHeader*)base;
206 if (!verify_dos(dos))
207 return EFI_LOAD_ERROR;
208
209 pe = (const struct PeFileHeader*)&base[dos->ExeHeader];
210 if (!verify_pe(pe))
211 return EFI_LOAD_ERROR;
212
213 offset = section_table_offset(dos, pe);
214 locate_sections((struct PeSectionHeader*)&base[offset], pe->FileHeader.NumberOfSections,
215 sections, addrs, NULL, sizes);
216
217 return EFI_SUCCESS;
218 }
219
pe_file_locate_sections(EFI_FILE * dir,const CHAR16 * path,const CHAR8 ** sections,UINTN * offsets,UINTN * sizes)220 EFI_STATUS pe_file_locate_sections(
221 EFI_FILE *dir,
222 const CHAR16 *path,
223 const CHAR8 **sections,
224 UINTN *offsets,
225 UINTN *sizes) {
226 _cleanup_freepool_ struct PeSectionHeader *section_table = NULL;
227 _cleanup_(file_closep) EFI_FILE *handle = NULL;
228 struct DosFileHeader dos;
229 struct PeFileHeader pe;
230 UINTN len, section_table_len;
231 EFI_STATUS err;
232
233 assert(dir);
234 assert(path);
235 assert(sections);
236 assert(offsets);
237 assert(sizes);
238
239 err = dir->Open(dir, &handle, (CHAR16*)path, EFI_FILE_MODE_READ, 0ULL);
240 if (EFI_ERROR(err))
241 return err;
242
243 len = sizeof(dos);
244 err = handle->Read(handle, &len, &dos);
245 if (EFI_ERROR(err))
246 return err;
247 if (len != sizeof(dos) || !verify_dos(&dos))
248 return EFI_LOAD_ERROR;
249
250 err = handle->SetPosition(handle, dos.ExeHeader);
251 if (EFI_ERROR(err))
252 return err;
253
254 len = sizeof(pe);
255 err = handle->Read(handle, &len, &pe);
256 if (EFI_ERROR(err))
257 return err;
258 if (len != sizeof(pe) || !verify_pe(&pe))
259 return EFI_LOAD_ERROR;
260
261 section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
262 section_table = xallocate_pool(section_table_len);
263 if (!section_table)
264 return EFI_OUT_OF_RESOURCES;
265
266 err = handle->SetPosition(handle, section_table_offset(&dos, &pe));
267 if (EFI_ERROR(err))
268 return err;
269
270 len = section_table_len;
271 err = handle->Read(handle, &len, section_table);
272 if (EFI_ERROR(err))
273 return err;
274 if (len != section_table_len)
275 return EFI_LOAD_ERROR;
276
277 locate_sections(section_table, pe.FileHeader.NumberOfSections,
278 sections, NULL, offsets, sizes);
279
280 return EFI_SUCCESS;
281 }
282