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