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