1 /*
2  * xen/common/efi/pe.c
3  *
4  * PE executable header parser.
5  *
6  * Derived from https://github.com/systemd/systemd/blob/master/src/boot/efi/pe.c
7  * commit 07d5ed536ec0a76b08229c7a80b910cb9acaf6b1
8  *
9  * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
10  * Copyright (C) 2020 Trammell Hudson <hudson@trmm.net>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful, but
18  * WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20  * Lesser General Public License for more details.
21  */
22 
23 
24 #include "efi.h"
25 
26 struct DosFileHeader {
27     UINT8   Magic[2];
28     UINT16  LastSize;
29     UINT16  nBlocks;
30     UINT16  nReloc;
31     UINT16  HdrSize;
32     UINT16  MinAlloc;
33     UINT16  MaxAlloc;
34     UINT16  ss;
35     UINT16  sp;
36     UINT16  Checksum;
37     UINT16  ip;
38     UINT16  cs;
39     UINT16  RelocPos;
40     UINT16  nOverlay;
41     UINT16  reserved[4];
42     UINT16  OEMId;
43     UINT16  OEMInfo;
44     UINT16  reserved2[10];
45     UINT32  ExeHeader;
46 };
47 
48 #if defined(__arm__) || defined (__aarch64__)
49 #define PE_HEADER_MACHINE 0xaa64
50 #elif defined(__x86_64__)
51 #define PE_HEADER_MACHINE 0x8664
52 #else
53 #error "Unknown architecture"
54 #endif
55 
56 struct PeFileHeader {
57     UINT16  Machine;
58     UINT16  NumberOfSections;
59     UINT32  TimeDateStamp;
60     UINT32  PointerToSymbolTable;
61     UINT32  NumberOfSymbols;
62     UINT16  SizeOfOptionalHeader;
63     UINT16  Characteristics;
64 };
65 
66 struct PeHeader {
67     UINT8   Magic[4];
68     struct PeFileHeader FileHeader;
69 };
70 
71 struct PeSectionHeader {
72     CHAR8   Name[8];
73     UINT32  VirtualSize;
74     UINT32  VirtualAddress;
75     UINT32  SizeOfRawData;
76     UINT32  PointerToRawData;
77     UINT32  PointerToRelocations;
78     UINT32  PointerToLinenumbers;
79     UINT16  NumberOfRelocations;
80     UINT16  NumberOfLinenumbers;
81     UINT32  Characteristics;
82 };
83 
pe_name_compare(const struct PeSectionHeader * sect,const CHAR16 * name)84 static bool __init pe_name_compare(const struct PeSectionHeader *sect,
85                                    const CHAR16 *name)
86 {
87     size_t i;
88 
89     if ( sect->Name[0] != '.' )
90         return false;
91 
92     for ( i = 1; i < sizeof(sect->Name); i++ )
93     {
94         const char c = sect->Name[i];
95 
96         if ( c != name[i - 1] )
97             return false;
98         if ( c == '\0' )
99             return true;
100     }
101 
102     return name[i - 1] == L'\0';
103 }
104 
pe_find_section(const void * image,const UINTN image_size,const CHAR16 * section_name,UINTN * size_out)105 const void *__init pe_find_section(const void *image, const UINTN image_size,
106                                    const CHAR16 *section_name, UINTN *size_out)
107 {
108     const struct DosFileHeader *dos = image;
109     const struct PeHeader *pe;
110     const struct PeSectionHeader *sect;
111     UINTN offset, i;
112 
113     if ( image_size < sizeof(*dos) ||
114          dos->Magic[0] != 'M' ||
115          dos->Magic[1] != 'Z' )
116         return NULL;
117 
118     offset = dos->ExeHeader;
119     pe = image + offset;
120 
121     offset += sizeof(*pe);
122     if ( image_size < offset ||
123          pe->Magic[0] != 'P' ||
124          pe->Magic[1] != 'E' ||
125          pe->Magic[2] != '\0' ||
126          pe->Magic[3] != '\0' )
127         return NULL;
128 
129     if ( pe->FileHeader.Machine != PE_HEADER_MACHINE )
130         return NULL;
131 
132     offset += pe->FileHeader.SizeOfOptionalHeader;
133 
134     for ( i = 0; i < pe->FileHeader.NumberOfSections; i++ )
135     {
136         sect = image + offset;
137         if ( image_size < offset + sizeof(*sect) )
138             return NULL;
139 
140         if ( !pe_name_compare(sect, section_name) )
141         {
142             offset += sizeof(*sect);
143             continue;
144         }
145 
146         if ( image_size < sect->VirtualSize + sect->VirtualAddress )
147             blexit(L"PE invalid section size + address");
148 
149         if ( size_out )
150             *size_out = sect->VirtualSize;
151 
152         return image + sect->VirtualAddress;
153     }
154 
155     return NULL;
156 }
157