]> git.proxmox.com Git - mirror_edk2.git/blame - UefiPayloadPkg/PayloadLoaderPeim/ElfLib/Elf64Lib.c
UefiPayloadPkg/PayloadLoaderPeim: Use INT64 as input parameter
[mirror_edk2.git] / UefiPayloadPkg / PayloadLoaderPeim / ElfLib / Elf64Lib.c
CommitLineData
fe471d4a
RN
1/** @file\r
2 ELF library\r
3\r
4 Copyright (c) 2019 - 2021, Intel Corporation. All rights reserved.<BR>\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6\r
7**/\r
8\r
9#include "ElfLibInternal.h"\r
10\r
11/**\r
12 Return the section header specified by Index.\r
13\r
14 @param ImageBase The image base.\r
15 @param Index The section index.\r
16\r
17 @return Pointer to the section header.\r
18**/\r
19Elf64_Shdr *\r
20GetElf64SectionByIndex (\r
e5efcf8b
MK
21 IN UINT8 *ImageBase,\r
22 IN UINT32 Index\r
fe471d4a
RN
23 )\r
24{\r
e5efcf8b 25 Elf64_Ehdr *Ehdr;\r
fe471d4a 26\r
e5efcf8b 27 Ehdr = (Elf64_Ehdr *)ImageBase;\r
fe471d4a
RN
28 if (Index >= Ehdr->e_shnum) {\r
29 return NULL;\r
30 }\r
31\r
32 return (Elf64_Shdr *)(ImageBase + Ehdr->e_shoff + Index * Ehdr->e_shentsize);\r
33}\r
34\r
35/**\r
36 Return the segment header specified by Index.\r
37\r
38 @param ImageBase The image base.\r
39 @param Index The segment index.\r
40\r
41 @return Pointer to the segment header.\r
42**/\r
43Elf64_Phdr *\r
44GetElf64SegmentByIndex (\r
e5efcf8b
MK
45 IN UINT8 *ImageBase,\r
46 IN UINT32 Index\r
fe471d4a
RN
47 )\r
48{\r
e5efcf8b 49 Elf64_Ehdr *Ehdr;\r
fe471d4a 50\r
e5efcf8b 51 Ehdr = (Elf64_Ehdr *)ImageBase;\r
fe471d4a
RN
52 if (Index >= Ehdr->e_phnum) {\r
53 return NULL;\r
54 }\r
55\r
56 return (Elf64_Phdr *)(ImageBase + Ehdr->e_phoff + Index * Ehdr->e_phentsize);\r
57}\r
58\r
59/**\r
60 Return the section header specified by the range.\r
61\r
62 @param ImageBase The image base.\r
63 @param Offset The section offset.\r
64 @param Size The section size.\r
65\r
66 @return Pointer to the section header.\r
67**/\r
68Elf64_Shdr *\r
69GetElf64SectionByRange (\r
e5efcf8b
MK
70 IN UINT8 *ImageBase,\r
71 IN UINT64 Offset,\r
72 IN UINT64 Size\r
fe471d4a
RN
73 )\r
74{\r
e5efcf8b
MK
75 UINT32 Index;\r
76 Elf64_Ehdr *Ehdr;\r
77 Elf64_Shdr *Shdr;\r
fe471d4a
RN
78\r
79 Ehdr = (Elf64_Ehdr *)ImageBase;\r
80\r
e5efcf8b 81 Shdr = (Elf64_Shdr *)(ImageBase + Ehdr->e_shoff);\r
fe471d4a
RN
82 for (Index = 0; Index < Ehdr->e_shnum; Index++) {\r
83 if ((Shdr->sh_offset == Offset) && (Shdr->sh_size == Size)) {\r
84 return Shdr;\r
85 }\r
e5efcf8b 86\r
fe471d4a
RN
87 Shdr = ELF_NEXT_ENTRY (Elf64_Shdr, Shdr, Ehdr->e_shentsize);\r
88 }\r
e5efcf8b 89\r
fe471d4a
RN
90 return NULL;\r
91}\r
92\r
93/**\r
94 Fix up the image based on the relocation entries.\r
95\r
96 @param Rela Relocation entries.\r
97 @param RelaSize Total size of relocation entries.\r
98 @param RelaEntrySize Relocation entry size.\r
99 @param RelaType Type of relocation entry.\r
100 @param Delta The delta between preferred image base and the actual image base.\r
101 @param DynamicLinking TRUE when fixing up according to dynamic relocation.\r
102\r
103 @retval EFI_SUCCESS The image fix up is processed successfully.\r
104**/\r
105EFI_STATUS\r
106ProcessRelocation64 (\r
e5efcf8b
MK
107 IN Elf64_Rela *Rela,\r
108 IN UINT64 RelaSize,\r
109 IN UINT64 RelaEntrySize,\r
110 IN UINT64 RelaType,\r
497ac7b6 111 IN INT64 Delta,\r
e5efcf8b 112 IN BOOLEAN DynamicLinking\r
fe471d4a
RN
113 )\r
114{\r
e5efcf8b
MK
115 UINTN Index;\r
116 UINT64 *Ptr;\r
117 UINT32 Type;\r
fe471d4a
RN
118\r
119 for ( Index = 0\r
e5efcf8b
MK
120 ; MultU64x64 (RelaEntrySize, Index) < RelaSize\r
121 ; Index++, Rela = ELF_NEXT_ENTRY (Elf64_Rela, Rela, RelaEntrySize)\r
122 )\r
123 {\r
fe471d4a
RN
124 //\r
125 // r_offset is the virtual address of the storage unit affected by the relocation.\r
126 //\r
e5efcf8b
MK
127 Ptr = (UINT64 *)(UINTN)(Rela->r_offset + Delta);\r
128 Type = ELF64_R_TYPE (Rela->r_info);\r
fe471d4a
RN
129 switch (Type) {\r
130 case R_X86_64_NONE:\r
131 case R_X86_64_PC32:\r
132 case R_X86_64_PLT32:\r
133 case R_X86_64_GOTPCREL:\r
134 case R_X86_64_GOTPCRELX:\r
135 case R_X86_64_REX_GOTPCRELX:\r
136 break;\r
137\r
138 case R_X86_64_64:\r
139 if (DynamicLinking) {\r
140 //\r
141 // Dynamic section doesn't contain entries of this type.\r
142 //\r
143 DEBUG ((DEBUG_INFO, "Unsupported relocation type %02X\n", Type));\r
144 ASSERT (FALSE);\r
145 } else {\r
146 *Ptr += Delta;\r
147 }\r
e5efcf8b 148\r
fe471d4a
RN
149 break;\r
150\r
151 case R_X86_64_32:\r
152 //\r
153 // Dynamic section doesn't contain entries of this type.\r
154 //\r
155 DEBUG ((DEBUG_INFO, "Unsupported relocation type %02X\n", Type));\r
156 ASSERT (FALSE);\r
157 break;\r
158\r
159 case R_X86_64_RELATIVE:\r
160 if (DynamicLinking) {\r
161 //\r
162 // A: Represents the addend used to compute the value of the relocatable field.\r
163 // B: Represents the base address at which a shared object has been loaded into memory during execution.\r
164 // Generally, a shared object is built with a 0 base virtual address, but the execution address will be different.\r
165 //\r
166 // B (Base Address) in ELF spec is slightly different:\r
167 // An executable or shared object file's base address (on platforms that support the concept) is calculated during\r
168 // execution from three values: the virtual memory load address, the maximum page size, and the lowest virtual address\r
169 // of a program's loadable segment. To compute the base address, one determines the memory address associated with the\r
170 // lowest p_vaddr value for a PT_LOAD segment. This address is truncated to the nearest multiple of the maximum page size.\r
171 // The corresponding p_vaddr value itself is also truncated to the nearest multiple of the maximum page size.\r
172 //\r
173 // *** The base address is the difference between the truncated memory address and the truncated p_vaddr value. ***\r
174 //\r
175 // Delta in this function is B.\r
176 //\r
177 // Calculation: B + A\r
178 //\r
179 if (RelaType == SHT_RELA) {\r
fe471d4a
RN
180 *Ptr = Delta + Rela->r_addend;\r
181 } else {\r
182 //\r
183 // A is stored in the field of relocation for REL type.\r
184 //\r
185 *Ptr = Delta + *Ptr;\r
186 }\r
187 } else {\r
188 //\r
189 // non-Dynamic section doesn't contain entries of this type.\r
190 //\r
191 DEBUG ((DEBUG_INFO, "Unsupported relocation type %02X\n", Type));\r
192 ASSERT (FALSE);\r
193 }\r
e5efcf8b 194\r
fe471d4a
RN
195 break;\r
196\r
197 default:\r
198 DEBUG ((DEBUG_INFO, "Unsupported relocation type %02X\n", Type));\r
199 }\r
200 }\r
e5efcf8b 201\r
fe471d4a
RN
202 return EFI_SUCCESS;\r
203}\r
204\r
205/**\r
206 Relocate the DYN type image.\r
207\r
208 @param ElfCt Point to image context.\r
209\r
210 @retval EFI_SUCCESS The relocation succeeds.\r
211 @retval EFI_UNSUPPORTED The image doesn't contain a dynamic section.\r
212**/\r
213EFI_STATUS\r
214RelocateElf64Dynamic (\r
e5efcf8b 215 IN ELF_IMAGE_CONTEXT *ElfCt\r
fe471d4a
RN
216 )\r
217{\r
e5efcf8b
MK
218 UINT32 Index;\r
219 Elf64_Phdr *Phdr;\r
220 Elf64_Shdr *DynShdr;\r
221 Elf64_Shdr *RelShdr;\r
222 Elf64_Dyn *Dyn;\r
223 UINT64 RelaAddress;\r
224 UINT64 RelaCount;\r
225 UINT64 RelaSize;\r
226 UINT64 RelaEntrySize;\r
227 UINT64 RelaType;\r
fe471d4a
RN
228\r
229 //\r
230 // 1. Locate the dynamic section.\r
231 //\r
232 // If an object file participates in dynamic linking, its program header table\r
233 // will have an element of type PT_DYNAMIC.\r
234 // This ``segment'' contains the .dynamic section. A special symbol, _DYNAMIC,\r
235 // labels the section, which contains an array of Elf32_Dyn or Elf64_Dyn.\r
236 //\r
237 DynShdr = NULL;\r
238 for (Index = 0; Index < ElfCt->PhNum; Index++) {\r
239 Phdr = GetElf64SegmentByIndex (ElfCt->FileBase, Index);\r
240 ASSERT (Phdr != NULL);\r
241 if (Phdr->p_type == PT_DYNAMIC) {\r
242 //\r
243 // Verify the existence of the dynamic section.\r
244 //\r
245 DynShdr = GetElf64SectionByRange (ElfCt->FileBase, Phdr->p_offset, Phdr->p_filesz);\r
246 break;\r
247 }\r
248 }\r
249\r
250 //\r
251 // It's abnormal a DYN ELF doesn't contain a dynamic section.\r
252 //\r
253 ASSERT (DynShdr != NULL);\r
254 if (DynShdr == NULL) {\r
255 return EFI_UNSUPPORTED;\r
256 }\r
e5efcf8b 257\r
fe471d4a
RN
258 ASSERT (DynShdr->sh_type == SHT_DYNAMIC);\r
259 ASSERT (DynShdr->sh_entsize >= sizeof (*Dyn));\r
260\r
261 //\r
262 // 2. Locate the relocation section from the dynamic section.\r
263 //\r
e5efcf8b 264 RelaAddress = MAX_UINT64;\r
fe471d4a
RN
265 RelaSize = 0;\r
266 RelaCount = 0;\r
267 RelaEntrySize = 0;\r
268 RelaType = 0;\r
e5efcf8b
MK
269 for ( Index = 0, Dyn = (Elf64_Dyn *)(ElfCt->FileBase + DynShdr->sh_offset)\r
270 ; Index < DivU64x64Remainder (DynShdr->sh_size, DynShdr->sh_entsize, NULL)\r
271 ; Index++, Dyn = ELF_NEXT_ENTRY (Elf64_Dyn, Dyn, DynShdr->sh_entsize)\r
272 )\r
273 {\r
fe471d4a
RN
274 switch (Dyn->d_tag) {\r
275 case DT_RELA:\r
276 case DT_REL:\r
277 //\r
278 // DT_REL represent program virtual addresses.\r
279 // A file's virtual addresses might not match the memory virtual addresses during execution.\r
280 // When interpreting addresses contained in the dynamic structure, the dynamic linker computes actual addresses,\r
281 // based on the original file value and the memory base address.\r
282 // For consistency, files do not contain relocation entries to ``correct'' addresses in the dynamic structure.\r
283 //\r
939ed3a5 284 RelaAddress = Dyn->d_un.d_ptr;\r
e5efcf8b 285 RelaType = (Dyn->d_tag == DT_RELA) ? SHT_RELA : SHT_REL;\r
fe471d4a
RN
286 break;\r
287 case DT_RELACOUNT:\r
288 case DT_RELCOUNT:\r
289 RelaCount = Dyn->d_un.d_val;\r
290 break;\r
291 case DT_RELENT:\r
292 case DT_RELAENT:\r
293 RelaEntrySize = Dyn->d_un.d_val;\r
294 break;\r
295 case DT_RELSZ:\r
296 case DT_RELASZ:\r
297 RelaSize = Dyn->d_un.d_val;\r
298 break;\r
299 default:\r
300 break;\r
301 }\r
302 }\r
303\r
939ed3a5 304 if (RelaAddress == MAX_UINT64) {\r
fe471d4a
RN
305 ASSERT (RelaCount == 0);\r
306 ASSERT (RelaEntrySize == 0);\r
307 ASSERT (RelaSize == 0);\r
308 //\r
309 // It's fine that a DYN ELF doesn't contain relocation section.\r
310 //\r
311 return EFI_SUCCESS;\r
312 }\r
313\r
314 //\r
315 // Verify the existence of the relocation section.\r
316 //\r
939ed3a5
RN
317 RelShdr = NULL;\r
318 for (Index = 0; Index < ElfCt->ShNum; Index++) {\r
319 RelShdr = GetElf64SectionByIndex (ElfCt->FileBase, Index);\r
320 ASSERT (RelShdr != NULL);\r
321 if ((RelShdr->sh_addr == RelaAddress) && (RelShdr->sh_size == RelaSize)) {\r
322 break;\r
323 }\r
e5efcf8b 324\r
939ed3a5
RN
325 RelShdr = NULL;\r
326 }\r
327\r
fe471d4a
RN
328 if (RelShdr == NULL) {\r
329 return EFI_UNSUPPORTED;\r
330 }\r
e5efcf8b 331\r
fe471d4a
RN
332 ASSERT (RelShdr->sh_type == RelaType);\r
333 ASSERT (RelShdr->sh_entsize == RelaEntrySize);\r
334\r
335 //\r
336 // 3. Process the relocation section.\r
337 //\r
338 ProcessRelocation64 (\r
e5efcf8b
MK
339 (Elf64_Rela *)(ElfCt->FileBase + RelShdr->sh_offset),\r
340 RelShdr->sh_size,\r
341 RelShdr->sh_entsize,\r
342 RelShdr->sh_type,\r
343 (UINTN)ElfCt->ImageAddress - (UINTN)ElfCt->PreferredImageAddress,\r
fe471d4a
RN
344 TRUE\r
345 );\r
346 return EFI_SUCCESS;\r
347}\r
348\r
349/**\r
350 Relocate all sections in a ELF image.\r
351\r
352 @param[in] ElfCt ELF image context pointer.\r
353\r
354 @retval EFI_UNSUPPORTED Relocation is not supported.\r
355 @retval EFI_SUCCESS ELF image was relocated successfully.\r
356**/\r
357EFI_STATUS\r
358RelocateElf64Sections (\r
e5efcf8b 359 IN ELF_IMAGE_CONTEXT *ElfCt\r
fe471d4a
RN
360 )\r
361{\r
e5efcf8b
MK
362 EFI_STATUS Status;\r
363 Elf64_Ehdr *Ehdr;\r
364 Elf64_Shdr *RelShdr;\r
365 Elf64_Shdr *Shdr;\r
366 UINT32 Index;\r
367 UINTN Delta;\r
368\r
369 Ehdr = (Elf64_Ehdr *)ElfCt->FileBase;\r
fe471d4a
RN
370 if (Ehdr->e_machine != EM_X86_64) {\r
371 return EFI_UNSUPPORTED;\r
372 }\r
373\r
e5efcf8b 374 Delta = (UINTN)ElfCt->ImageAddress - (UINTN)ElfCt->PreferredImageAddress;\r
fe471d4a
RN
375 ElfCt->EntryPoint = (UINTN)(Ehdr->e_entry + Delta);\r
376\r
377 //\r
378 // 1. Relocate dynamic ELF using the relocation section pointed by dynamic section\r
379 //\r
380 if (Ehdr->e_type == ET_DYN) {\r
381 DEBUG ((DEBUG_INFO, "DYN ELF: Relocate using dynamic sections...\n"));\r
382 Status = RelocateElf64Dynamic (ElfCt);\r
383 ASSERT_EFI_ERROR (Status);\r
384 return Status;\r
385 }\r
386\r
387 //\r
388 // 2. Executable ELF: Fix up the delta between actual image address and preferred image address.\r
389 //\r
390 // Linker already fixed up EXEC ELF based on the preferred image address.\r
391 // A ELF loader in modern OS only loads it into the preferred image address.\r
392 // The below relocation is unneeded in that case.\r
393 // But the ELF loader in firmware supports to load the image to a different address.\r
394 // The below relocation is needed in this case.\r
395 //\r
396 DEBUG ((DEBUG_INFO, "EXEC ELF: Fix actual/preferred base address delta ...\n"));\r
e5efcf8b
MK
397 for ( Index = 0, RelShdr = (Elf64_Shdr *)(ElfCt->FileBase + Ehdr->e_shoff)\r
398 ; Index < Ehdr->e_shnum\r
399 ; Index++, RelShdr = ELF_NEXT_ENTRY (Elf64_Shdr, RelShdr, Ehdr->e_shentsize)\r
400 )\r
401 {\r
fe471d4a
RN
402 if ((RelShdr->sh_type != SHT_REL) && (RelShdr->sh_type != SHT_RELA)) {\r
403 continue;\r
404 }\r
e5efcf8b 405\r
fe471d4a
RN
406 Shdr = GetElf64SectionByIndex (ElfCt->FileBase, RelShdr->sh_info);\r
407 if ((Shdr->sh_flags & SHF_ALLOC) == SHF_ALLOC) {\r
408 //\r
409 // Only fix up sections that occupy memory during process execution.\r
410 //\r
411 ProcessRelocation64 (\r
e5efcf8b
MK
412 (Elf64_Rela *)((UINT8 *)Ehdr + RelShdr->sh_offset),\r
413 RelShdr->sh_size,\r
414 RelShdr->sh_entsize,\r
415 RelShdr->sh_type,\r
416 Delta,\r
417 FALSE\r
fe471d4a
RN
418 );\r
419 }\r
420 }\r
421\r
422 return EFI_SUCCESS;\r
423}\r
424\r
425/**\r
426 Load ELF image which has 64-bit architecture.\r
427\r
428 Caller should set Context.ImageAddress to a proper value, either pointing to\r
429 a new allocated memory whose size equal to Context.ImageSize, or pointing\r
430 to Context.PreferredImageAddress.\r
431\r
432 @param[in] ElfCt ELF image context pointer.\r
433\r
434 @retval EFI_SUCCESS ELF binary is loaded successfully.\r
435 @retval Others Loading ELF binary fails.\r
436\r
437**/\r
438EFI_STATUS\r
439LoadElf64Image (\r
e5efcf8b 440 IN ELF_IMAGE_CONTEXT *ElfCt\r
fe471d4a
RN
441 )\r
442{\r
e5efcf8b
MK
443 Elf64_Ehdr *Ehdr;\r
444 Elf64_Phdr *Phdr;\r
445 UINT16 Index;\r
446 UINTN Delta;\r
fe471d4a
RN
447\r
448 ASSERT (ElfCt != NULL);\r
449\r
450 //\r
451 // Per the sprit of ELF, loading to memory only consumes info from program headers.\r
452 //\r
453 Ehdr = (Elf64_Ehdr *)ElfCt->FileBase;\r
454\r
455 for ( Index = 0, Phdr = (Elf64_Phdr *)(ElfCt->FileBase + Ehdr->e_phoff)\r
e5efcf8b
MK
456 ; Index < Ehdr->e_phnum\r
457 ; Index++, Phdr = ELF_NEXT_ENTRY (Elf64_Phdr, Phdr, Ehdr->e_phentsize)\r
458 )\r
459 {\r
fe471d4a
RN
460 //\r
461 // Skip segments that don't require load (type tells, or size is 0)\r
462 //\r
463 if ((Phdr->p_type != PT_LOAD) ||\r
e5efcf8b
MK
464 (Phdr->p_memsz == 0))\r
465 {\r
fe471d4a
RN
466 continue;\r
467 }\r
468\r
469 //\r
470 // The memory offset of segment relative to the image base\r
471 // Note: CopyMem() does nothing when the dst equals to src.\r
472 //\r
e5efcf8b
MK
473 Delta = (UINTN)Phdr->p_paddr - (UINTN)ElfCt->PreferredImageAddress;\r
474 CopyMem (ElfCt->ImageAddress + Delta, ElfCt->FileBase + (UINTN)Phdr->p_offset, (UINTN)Phdr->p_filesz);\r
475 ZeroMem (ElfCt->ImageAddress + Delta + (UINTN)Phdr->p_filesz, (UINTN)(Phdr->p_memsz - Phdr->p_filesz));\r
fe471d4a
RN
476 }\r
477\r
478 //\r
479 // Relocate when new new image base is not the preferred image base.\r
480 //\r
481 if (ElfCt->ImageAddress != ElfCt->PreferredImageAddress) {\r
482 RelocateElf64Sections (ElfCt);\r
483 }\r
484\r
485 return EFI_SUCCESS;\r
486}\r