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