Commit | Line | Data |
---|---|---|
e1f414b6 | 1 | /** @file\r |
afa22326 | 2 | Provides the services to get the entry point to a PE/COFF image that has either been \r |
22e6fe86 | 3 | loaded into memory or is executing at it's linked address.\r |
4 | \r | |
412e9dea | 5 | Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>\r |
19388d29 HT |
6 | Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r |
7 | This program and the accompanying materials\r | |
e1f414b6 | 8 | are licensed and made available under the terms and conditions of the BSD License\r |
9 | which accompanies this distribution. The full text of the license may be found at\r | |
2fc59a00 | 10 | http://opensource.org/licenses/bsd-license.php.\r |
e1f414b6 | 11 | \r |
12 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r | |
13 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r | |
14 | \r | |
e1f414b6 | 15 | **/\r |
16 | \r | |
c892d846 | 17 | \r |
c7d265a9 | 18 | #include <Base.h>\r |
c892d846 | 19 | \r |
c7d265a9 | 20 | #include <Library/PeCoffGetEntryPointLib.h>\r |
21 | #include <Library/DebugLib.h>\r | |
22 | \r | |
23 | #include <IndustryStandard/PeImage.h>\r | |
e1f414b6 | 24 | \r |
412e9dea JF |
25 | #define PE_COFF_IMAGE_ALIGN_SIZE 4\r |
26 | \r | |
e1f414b6 | 27 | /**\r |
28 | Retrieves and returns a pointer to the entry point to a PE/COFF image that has been loaded\r | |
29 | into system memory with the PE/COFF Loader Library functions.\r | |
30 | \r | |
31 | Retrieves the entry point to the PE/COFF image specified by Pe32Data and returns this entry\r | |
32 | point in EntryPoint. If the entry point could not be retrieved from the PE/COFF image, then\r | |
33 | return RETURN_INVALID_PARAMETER. Otherwise return RETURN_SUCCESS.\r | |
34 | If Pe32Data is NULL, then ASSERT().\r | |
35 | If EntryPoint is NULL, then ASSERT().\r | |
36 | \r | |
2fc59a00 | 37 | @param Pe32Data The pointer to the PE/COFF image that is loaded in system memory.\r |
38 | @param EntryPoint The pointer to entry point to the PE/COFF image to return.\r | |
e1f414b6 | 39 | \r |
40 | @retval RETURN_SUCCESS EntryPoint was returned.\r | |
41 | @retval RETURN_INVALID_PARAMETER The entry point could not be found in the PE/COFF image.\r | |
42 | \r | |
43 | **/\r | |
44 | RETURN_STATUS\r | |
45 | EFIAPI\r | |
46 | PeCoffLoaderGetEntryPoint (\r | |
47 | IN VOID *Pe32Data,\r | |
48 | OUT VOID **EntryPoint\r | |
49 | )\r | |
50 | {\r | |
3d7b0992 LG |
51 | EFI_IMAGE_DOS_HEADER *DosHdr;\r |
52 | EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r | |
e1f414b6 | 53 | \r |
54 | ASSERT (Pe32Data != NULL);\r | |
55 | ASSERT (EntryPoint != NULL);\r | |
56 | \r | |
3d7b0992 | 57 | DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;\r |
afa22326 | 58 | if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r |
e1f414b6 | 59 | //\r |
60 | // DOS image header is present, so read the PE header after the DOS image header.\r | |
61 | //\r | |
3d7b0992 | 62 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));\r |
e1f414b6 | 63 | } else {\r |
64 | //\r | |
65 | // DOS image header is not present, so PE header is at the image base.\r | |
66 | //\r | |
3d7b0992 | 67 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;\r |
e1f414b6 | 68 | }\r |
69 | \r | |
70 | //\r | |
71 | // Calculate the entry point relative to the start of the image.\r | |
72 | // AddressOfEntryPoint is common for PE32 & PE32+\r | |
73 | //\r | |
3d7b0992 LG |
74 | if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {\r |
75 | *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Te->AddressOfEntryPoint & 0x0ffffffff) + sizeof(EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize);\r | |
76 | return RETURN_SUCCESS;\r | |
77 | } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {\r | |
78 | *EntryPoint = (VOID *)((UINTN)Pe32Data + (UINTN)(Hdr.Pe32->OptionalHeader.AddressOfEntryPoint & 0x0ffffffff));\r | |
79 | return RETURN_SUCCESS;\r | |
80 | }\r | |
81 | \r | |
82 | return RETURN_UNSUPPORTED;\r | |
e1f414b6 | 83 | }\r |
84 | \r | |
85 | \r | |
86 | /**\r | |
87 | Returns the machine type of a PE/COFF image.\r | |
88 | \r | |
89 | Returns the machine type from the PE/COFF image specified by Pe32Data.\r | |
90 | If Pe32Data is NULL, then ASSERT().\r | |
91 | \r | |
2fc59a00 | 92 | @param Pe32Data The pointer to the PE/COFF image that is loaded in system\r |
e1f414b6 | 93 | memory.\r |
94 | \r | |
cb6cb44c | 95 | @return Machine type or zero if not a valid image.\r |
e1f414b6 | 96 | \r |
97 | **/\r | |
98 | UINT16\r | |
99 | EFIAPI\r | |
100 | PeCoffLoaderGetMachineType (\r | |
101 | IN VOID *Pe32Data\r | |
102 | )\r | |
103 | {\r | |
104 | EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r | |
105 | EFI_IMAGE_DOS_HEADER *DosHdr;\r | |
106 | \r | |
3d7b0992 LG |
107 | ASSERT (Pe32Data != NULL);\r |
108 | \r | |
109 | DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;\r | |
afa22326 | 110 | if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r |
3d7b0992 LG |
111 | //\r |
112 | // DOS image header is present, so read the PE header after the DOS image header.\r | |
113 | //\r | |
114 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));\r | |
e1f414b6 | 115 | } else {\r |
3d7b0992 LG |
116 | //\r |
117 | // DOS image header is not present, so PE header is at the image base.\r | |
118 | //\r | |
119 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;\r | |
e1f414b6 | 120 | }\r |
121 | \r | |
afa22326 | 122 | if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {\r |
3d7b0992 | 123 | return Hdr.Te->Machine;\r |
afa22326 | 124 | } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {\r |
e1f414b6 | 125 | return Hdr.Pe32->FileHeader.Machine;\r |
126 | }\r | |
127 | \r | |
128 | return 0x0000;\r | |
129 | }\r | |
130 | \r | |
131 | /**\r | |
132 | Returns a pointer to the PDB file name for a PE/COFF image that has been\r | |
122e2191 | 133 | loaded into system memory with the PE/COFF Loader Library functions. \r |
e1f414b6 | 134 | \r |
135 | Returns the PDB file name for the PE/COFF image specified by Pe32Data. If\r | |
136 | the PE/COFF image specified by Pe32Data is not a valid, then NULL is\r | |
137 | returned. If the PE/COFF image specified by Pe32Data does not contain a\r | |
138 | debug directory entry, then NULL is returned. If the debug directory entry\r | |
139 | in the PE/COFF image specified by Pe32Data does not contain a PDB file name,\r | |
140 | then NULL is returned.\r | |
141 | If Pe32Data is NULL, then ASSERT().\r | |
142 | \r | |
2fc59a00 | 143 | @param Pe32Data The pointer to the PE/COFF image that is loaded in system\r |
e1f414b6 | 144 | memory.\r |
145 | \r | |
146 | @return The PDB file name for the PE/COFF image specified by Pe32Data or NULL\r | |
147 | if it cannot be retrieved.\r | |
148 | \r | |
149 | **/\r | |
150 | VOID *\r | |
151 | EFIAPI\r | |
152 | PeCoffLoaderGetPdbPointer (\r | |
153 | IN VOID *Pe32Data\r | |
154 | )\r | |
155 | {\r | |
3d7b0992 | 156 | EFI_IMAGE_DOS_HEADER *DosHdr;\r |
e1f414b6 | 157 | EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r |
158 | EFI_IMAGE_DATA_DIRECTORY *DirectoryEntry;\r | |
159 | EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *DebugEntry;\r | |
160 | UINTN DirCount;\r | |
161 | VOID *CodeViewEntryPointer;\r | |
162 | INTN TEImageAdjust;\r | |
163 | UINT32 NumberOfRvaAndSizes;\r | |
164 | UINT16 Magic;\r | |
165 | \r | |
166 | ASSERT (Pe32Data != NULL);\r | |
167 | \r | |
168 | TEImageAdjust = 0;\r | |
169 | DirectoryEntry = NULL;\r | |
170 | DebugEntry = NULL;\r | |
171 | NumberOfRvaAndSizes = 0;\r | |
172 | \r | |
3d7b0992 | 173 | DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;\r |
afa22326 | 174 | if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r |
e1f414b6 | 175 | //\r |
176 | // DOS image header is present, so read the PE header after the DOS image header.\r | |
177 | //\r | |
3d7b0992 | 178 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));\r |
e1f414b6 | 179 | } else {\r |
180 | //\r | |
181 | // DOS image header is not present, so PE header is at the image base.\r | |
182 | //\r | |
183 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;\r | |
184 | }\r | |
185 | \r | |
afa22326 | 186 | if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {\r |
e1f414b6 | 187 | if (Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress != 0) {\r |
188 | DirectoryEntry = &Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG];\r | |
189 | TEImageAdjust = sizeof (EFI_TE_IMAGE_HEADER) - Hdr.Te->StrippedSize;\r | |
190 | DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *)((UINTN) Hdr.Te +\r | |
191 | Hdr.Te->DataDirectory[EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress +\r | |
192 | TEImageAdjust);\r | |
193 | }\r | |
afa22326 | 194 | } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {\r |
e1f414b6 | 195 | //\r |
196 | // NOTE: We use Machine field to identify PE32/PE32+, instead of Magic.\r | |
197 | // It is due to backward-compatibility, for some system might\r | |
198 | // generate PE32+ image with PE32 Magic.\r | |
199 | //\r | |
200 | switch (Hdr.Pe32->FileHeader.Machine) {\r | |
4ab0dff3 | 201 | case IMAGE_FILE_MACHINE_I386:\r |
e1f414b6 | 202 | //\r |
203 | // Assume PE32 image with IA32 Machine field.\r | |
204 | //\r | |
205 | Magic = EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC;\r | |
206 | break;\r | |
4ab0dff3 | 207 | case IMAGE_FILE_MACHINE_X64:\r |
208 | case IMAGE_FILE_MACHINE_IA64:\r | |
e1f414b6 | 209 | //\r |
4ab0dff3 | 210 | // Assume PE32+ image with x64 or IA64 Machine field\r |
e1f414b6 | 211 | //\r |
212 | Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;\r | |
213 | break;\r | |
214 | default:\r | |
215 | //\r | |
216 | // For unknow Machine field, use Magic in optional Header\r | |
217 | //\r | |
218 | Magic = Hdr.Pe32->OptionalHeader.Magic;\r | |
219 | }\r | |
220 | \r | |
afa22326 | 221 | if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r |
e1f414b6 | 222 | //\r |
223 | // Use PE32 offset get Debug Directory Entry\r | |
224 | //\r | |
225 | NumberOfRvaAndSizes = Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes;\r | |
226 | DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);\r | |
227 | DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);\r | |
228 | } else if (Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\r | |
229 | //\r | |
230 | // Use PE32+ offset get Debug Directory Entry\r | |
231 | //\r | |
232 | NumberOfRvaAndSizes = Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes;\r | |
233 | DirectoryEntry = (EFI_IMAGE_DATA_DIRECTORY *)&(Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);\r | |
234 | DebugEntry = (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *) ((UINTN) Pe32Data + DirectoryEntry->VirtualAddress);\r | |
235 | }\r | |
236 | \r | |
237 | if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_DEBUG) {\r | |
238 | DirectoryEntry = NULL;\r | |
239 | DebugEntry = NULL;\r | |
240 | }\r | |
241 | } else {\r | |
242 | return NULL;\r | |
243 | }\r | |
244 | \r | |
afa22326 | 245 | if (DebugEntry == NULL || DirectoryEntry == NULL) {\r |
e1f414b6 | 246 | return NULL;\r |
247 | }\r | |
248 | \r | |
38bbd3d9 | 249 | //\r |
250 | // Scan the directory to find the debug entry.\r | |
251 | // \r | |
3d7b0992 | 252 | for (DirCount = 0; DirCount < DirectoryEntry->Size; DirCount += sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY), DebugEntry++) {\r |
2b1532df | 253 | if (DebugEntry->Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {\r |
e1f414b6 | 254 | if (DebugEntry->SizeOfData > 0) {\r |
255 | CodeViewEntryPointer = (VOID *) ((UINTN) DebugEntry->RVA + ((UINTN)Pe32Data) + (UINTN)TEImageAdjust);\r | |
256 | switch (* (UINT32 *) CodeViewEntryPointer) {\r | |
257 | case CODEVIEW_SIGNATURE_NB10:\r | |
258 | return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY));\r | |
259 | case CODEVIEW_SIGNATURE_RSDS:\r | |
260 | return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY));\r | |
ebd04fc2 | 261 | case CODEVIEW_SIGNATURE_MTOC:\r |
262 | return (VOID *) ((CHAR8 *)CodeViewEntryPointer + sizeof (EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY));\r | |
e1f414b6 | 263 | default:\r |
264 | break;\r | |
265 | }\r | |
266 | }\r | |
267 | }\r | |
268 | }\r | |
269 | \r | |
270 | return NULL;\r | |
271 | }\r | |
272 | \r | |
225290eb A |
273 | /**\r |
274 | Returns the size of the PE/COFF headers\r | |
275 | \r | |
276 | Returns the size of the PE/COFF header specified by Pe32Data.\r | |
277 | If Pe32Data is NULL, then ASSERT().\r | |
278 | \r | |
2fc59a00 | 279 | @param Pe32Data The pointer to the PE/COFF image that is loaded in system\r |
225290eb A |
280 | memory.\r |
281 | \r | |
cb6cb44c | 282 | @return Size of PE/COFF header in bytes or zero if not a valid image.\r |
225290eb A |
283 | \r |
284 | **/\r | |
42db19ac A |
285 | UINT32\r |
286 | EFIAPI\r | |
287 | PeCoffGetSizeOfHeaders (\r | |
288 | IN VOID *Pe32Data\r | |
289 | )\r | |
290 | {\r | |
291 | EFI_IMAGE_DOS_HEADER *DosHdr;\r | |
292 | EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r | |
293 | UINTN SizeOfHeaders;\r | |
294 | \r | |
225290eb | 295 | ASSERT (Pe32Data != NULL);\r |
42db19ac A |
296 | \r |
297 | DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;\r | |
298 | if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r | |
299 | //\r | |
300 | // DOS image header is present, so read the PE header after the DOS image header.\r | |
301 | //\r | |
302 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN) Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));\r | |
303 | } else {\r | |
304 | //\r | |
305 | // DOS image header is not present, so PE header is at the image base.\r | |
306 | //\r | |
307 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;\r | |
308 | }\r | |
309 | \r | |
310 | if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {\r | |
cb6cb44c | 311 | SizeOfHeaders = sizeof (EFI_TE_IMAGE_HEADER) + (UINTN)Hdr.Te->BaseOfCode - (UINTN)Hdr.Te->StrippedSize;\r |
42db19ac A |
312 | } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {\r |
313 | SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders;\r | |
314 | } else {\r | |
315 | SizeOfHeaders = 0;\r | |
316 | }\r | |
317 | \r | |
cded6218 | 318 | return (UINT32) SizeOfHeaders;\r |
225290eb | 319 | }\r |
e1f414b6 | 320 | \r |
412e9dea JF |
321 | /**\r |
322 | Returns PE/COFF image base is loaded in system memory where the input address is in.\r | |
323 | \r | |
324 | On DEBUG build, searches the PE/COFF image base forward the input address and\r | |
325 | returns it.\r | |
326 | \r | |
327 | @param Address Address located in one PE/COFF image.\r | |
328 | \r | |
329 | @retval 0 RELEASE build or cannot find the PE/COFF image base.\r | |
330 | @retval others PE/COFF image base found.\r | |
331 | \r | |
332 | **/\r | |
333 | UINTN\r | |
334 | EFIAPI\r | |
9e981317 | 335 | PeCoffSearchImageBase (\r |
412e9dea JF |
336 | IN UINTN Address\r |
337 | )\r | |
338 | {\r | |
339 | UINTN Pe32Data;\r | |
340 | \r | |
341 | Pe32Data = 0;\r | |
342 | \r | |
343 | DEBUG_CODE (\r | |
344 | EFI_IMAGE_DOS_HEADER *DosHdr;\r | |
345 | EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;\r | |
346 | \r | |
347 | //\r | |
348 | // Find Image Base\r | |
349 | //\r | |
350 | Pe32Data = Address & ~(PE_COFF_IMAGE_ALIGN_SIZE - 1);\r | |
351 | while (Pe32Data != 0) {\r | |
352 | DosHdr = (EFI_IMAGE_DOS_HEADER *) Pe32Data;\r | |
353 | if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {\r | |
354 | //\r | |
355 | // DOS image header is present, so read the PE header after the DOS image header.\r | |
356 | //\r | |
357 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)(Pe32Data + (UINTN) ((DosHdr->e_lfanew) & 0x0ffff));\r | |
358 | //\r | |
359 | // Make sure PE header address does not overflow and is less than the initial address.\r | |
360 | //\r | |
361 | if (((UINTN)Hdr.Pe32 > Pe32Data) && ((UINTN)Hdr.Pe32 < Address)) {\r | |
362 | if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {\r | |
363 | break;\r | |
364 | }\r | |
365 | }\r | |
366 | } else {\r | |
367 | //\r | |
368 | // DOS image header is not present, TE header is at the image base.\r | |
369 | //\r | |
370 | Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;\r | |
371 | if ((Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) &&\r | |
372 | ((Hdr.Te->Machine == IMAGE_FILE_MACHINE_I386) || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_IA64) ||\r | |
373 | (Hdr.Te->Machine == IMAGE_FILE_MACHINE_EBC) || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_X64) ||\r | |
374 | (Hdr.Te->Machine == IMAGE_FILE_MACHINE_ARM64) || (Hdr.Te->Machine == IMAGE_FILE_MACHINE_ARMTHUMB_MIXED))\r | |
375 | ) {\r | |
376 | break;\r | |
377 | }\r | |
378 | }\r | |
379 | \r | |
380 | //\r | |
381 | // Not found the image base, check the previous aligned address\r | |
382 | //\r | |
383 | Pe32Data -= PE_COFF_IMAGE_ALIGN_SIZE;\r | |
384 | }\r | |
385 | );\r | |
386 | \r | |
387 | return Pe32Data;\r | |
388 | }\r |