4 Copyright (c) 2019 - 2021, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
9 #include "ElfLibInternal.h"
12 Return the section header specified by Index.
14 @param ImageBase The image base.
15 @param Index The section index.
17 @return Pointer to the section header.
20 GetElf64SectionByIndex (
27 Ehdr
= (Elf64_Ehdr
*)ImageBase
;
28 if (Index
>= Ehdr
->e_shnum
) {
32 return (Elf64_Shdr
*)(ImageBase
+ Ehdr
->e_shoff
+ Index
* Ehdr
->e_shentsize
);
36 Return the segment header specified by Index.
38 @param ImageBase The image base.
39 @param Index The segment index.
41 @return Pointer to the segment header.
44 GetElf64SegmentByIndex (
51 Ehdr
= (Elf64_Ehdr
*)ImageBase
;
52 if (Index
>= Ehdr
->e_phnum
) {
56 return (Elf64_Phdr
*)(ImageBase
+ Ehdr
->e_phoff
+ Index
* Ehdr
->e_phentsize
);
60 Return the section header specified by the range.
62 @param ImageBase The image base.
63 @param Offset The section offset.
64 @param Size The section size.
66 @return Pointer to the section header.
69 GetElf64SectionByRange (
79 Ehdr
= (Elf64_Ehdr
*)ImageBase
;
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
)) {
86 Shdr
= ELF_NEXT_ENTRY (Elf64_Shdr
, Shdr
, Ehdr
->e_shentsize
);
92 Fix up the image based on the relocation entries.
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.
101 @retval EFI_SUCCESS The image fix up is processed successfully.
104 ProcessRelocation64 (
107 IN UINT64 RelaEntrySize
,
110 IN BOOLEAN DynamicLinking
118 ; MultU64x64 (RelaEntrySize
, Index
) < RelaSize
119 ; Index
++, Rela
= ELF_NEXT_ENTRY (Elf64_Rela
, Rela
, RelaEntrySize
)
122 // r_offset is the virtual address of the storage unit affected by the relocation.
124 Ptr
= (UINT64
*)(UINTN
)(Rela
->r_offset
+ Delta
);
125 Type
= ELF64_R_TYPE(Rela
->r_info
);
130 case R_X86_64_GOTPCREL
:
131 case R_X86_64_GOTPCRELX
:
132 case R_X86_64_REX_GOTPCRELX
:
136 if (DynamicLinking
) {
138 // Dynamic section doesn't contain entries of this type.
140 DEBUG ((DEBUG_INFO
, "Unsupported relocation type %02X\n", Type
));
149 // Dynamic section doesn't contain entries of this type.
151 DEBUG ((DEBUG_INFO
, "Unsupported relocation type %02X\n", Type
));
155 case R_X86_64_RELATIVE
:
156 if (DynamicLinking
) {
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.
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.
169 // *** The base address is the difference between the truncated memory address and the truncated p_vaddr value. ***
171 // Delta in this function is B.
173 // Calculation: B + A
175 if (RelaType
== SHT_RELA
) {
177 *Ptr
= Delta
+ Rela
->r_addend
;
180 // A is stored in the field of relocation for REL type.
186 // non-Dynamic section doesn't contain entries of this type.
188 DEBUG ((DEBUG_INFO
, "Unsupported relocation type %02X\n", Type
));
194 DEBUG ((DEBUG_INFO
, "Unsupported relocation type %02X\n", Type
));
201 Relocate the DYN type image.
203 @param ElfCt Point to image context.
205 @retval EFI_SUCCESS The relocation succeeds.
206 @retval EFI_UNSUPPORTED The image doesn't contain a dynamic section.
209 RelocateElf64Dynamic (
210 IN ELF_IMAGE_CONTEXT
*ElfCt
221 UINT64 RelaEntrySize
;
225 // 1. Locate the dynamic section.
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.
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
) {
238 // Verify the existence of the dynamic section.
240 DynShdr
= GetElf64SectionByRange (ElfCt
->FileBase
, Phdr
->p_offset
, Phdr
->p_filesz
);
246 // It's abnormal a DYN ELF doesn't contain a dynamic section.
248 ASSERT (DynShdr
!= NULL
);
249 if (DynShdr
== NULL
) {
250 return EFI_UNSUPPORTED
;
252 ASSERT (DynShdr
->sh_type
== SHT_DYNAMIC
);
253 ASSERT (DynShdr
->sh_entsize
>= sizeof (*Dyn
));
256 // 2. Locate the relocation section from the dynamic section.
258 RelaAddress
= MAX_UINT64
;
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
)
267 switch (Dyn
->d_tag
) {
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.
277 RelaAddress
= Dyn
->d_un
.d_ptr
;
278 RelaType
= (Dyn
->d_tag
== DT_RELA
) ? SHT_RELA
: SHT_REL
;
282 RelaCount
= Dyn
->d_un
.d_val
;
286 RelaEntrySize
= Dyn
->d_un
.d_val
;
290 RelaSize
= Dyn
->d_un
.d_val
;
297 if (RelaAddress
== MAX_UINT64
) {
298 ASSERT (RelaCount
== 0);
299 ASSERT (RelaEntrySize
== 0);
300 ASSERT (RelaSize
== 0);
302 // It's fine that a DYN ELF doesn't contain relocation section.
308 // Verify the existence of the relocation section.
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
)) {
320 if (RelShdr
== NULL
) {
321 return EFI_UNSUPPORTED
;
323 ASSERT (RelShdr
->sh_type
== RelaType
);
324 ASSERT (RelShdr
->sh_entsize
== RelaEntrySize
);
327 // 3. Process the relocation section.
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
,
339 Relocate all sections in a ELF image.
341 @param[in] ElfCt ELF image context pointer.
343 @retval EFI_UNSUPPORTED Relocation is not supported.
344 @retval EFI_SUCCESS ELF image was relocated successfully.
347 RelocateElf64Sections (
348 IN ELF_IMAGE_CONTEXT
*ElfCt
358 Ehdr
= (Elf64_Ehdr
*)ElfCt
->FileBase
;
359 if (Ehdr
->e_machine
!= EM_X86_64
) {
360 return EFI_UNSUPPORTED
;
363 Delta
= (UINTN
) ElfCt
->ImageAddress
- (UINTN
) ElfCt
->PreferredImageAddress
;
364 ElfCt
->EntryPoint
= (UINTN
)(Ehdr
->e_entry
+ Delta
);
367 // 1. Relocate dynamic ELF using the relocation section pointed by dynamic section
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
);
377 // 2. Executable ELF: Fix up the delta between actual image address and preferred image address.
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.
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
)
390 if ((RelShdr
->sh_type
!= SHT_REL
) && (RelShdr
->sh_type
!= SHT_RELA
)) {
393 Shdr
= GetElf64SectionByIndex (ElfCt
->FileBase
, RelShdr
->sh_info
);
394 if ((Shdr
->sh_flags
& SHF_ALLOC
) == SHF_ALLOC
) {
396 // Only fix up sections that occupy memory during process execution.
398 ProcessRelocation64 (
399 (Elf64_Rela
*)((UINT8
*)Ehdr
+ RelShdr
->sh_offset
),
400 RelShdr
->sh_size
, RelShdr
->sh_entsize
, RelShdr
->sh_type
,
410 Load ELF image which has 64-bit architecture.
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.
416 @param[in] ElfCt ELF image context pointer.
418 @retval EFI_SUCCESS ELF binary is loaded successfully.
419 @retval Others Loading ELF binary fails.
424 IN ELF_IMAGE_CONTEXT
*ElfCt
432 ASSERT (ElfCt
!= NULL
);
435 // Per the sprit of ELF, loading to memory only consumes info from program headers.
437 Ehdr
= (Elf64_Ehdr
*)ElfCt
->FileBase
;
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
)
444 // Skip segments that don't require load (type tells, or size is 0)
446 if ((Phdr
->p_type
!= PT_LOAD
) ||
447 (Phdr
->p_memsz
== 0)) {
452 // The memory offset of segment relative to the image base
453 // Note: CopyMem() does nothing when the dst equals to src.
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
));
461 // Relocate when new new image base is not the preferred image base.
463 if (ElfCt
->ImageAddress
!= ElfCt
->PreferredImageAddress
) {
464 RelocateElf64Sections (ElfCt
);