1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #include <inttypes.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include "elfparsing.h"
9 #include "common.h"
10 #include "cbfs.h"
11 #include "rmodule.h"
12
13 /* Checks if program segment contains the ignored sections */
is_phdr_ignored(Elf64_Phdr * phdr,Elf64_Shdr ** shdrs)14 static int is_phdr_ignored(Elf64_Phdr *phdr, Elf64_Shdr **shdrs)
15 {
16 /* If no ignored section, return false. */
17 if (shdrs == NULL)
18 return 0;
19
20 while (*shdrs) {
21 Elf64_Addr sh_start = (*shdrs)->sh_addr;
22 Elf64_Addr sh_end = (*shdrs)->sh_addr + (*shdrs)->sh_size;
23 Elf64_Addr ph_start = phdr->p_vaddr;
24 Elf64_Addr ph_end = phdr->p_vaddr + phdr->p_memsz;
25
26 /* Return true only if section occupies whole of segment. */
27 if ((sh_start == ph_start) && (sh_end == ph_end)) {
28 DEBUG("Ignoring program segment at 0x%" PRIx64 "\n", ph_start);
29 return 1;
30 }
31
32 /* If shdr intersects phdr at all, its a conflict */
33 if (((sh_start >= ph_start) && (sh_start <= ph_end)) ||
34 ((sh_end >= ph_start) && (sh_end <= ph_end))) {
35 ERROR("Conflicting sections in segment\n");
36 exit(1);
37 }
38 shdrs++;
39 }
40
41 /* Program header doesn't need to be ignored. */
42 return 0;
43 }
44
45 /* Sections to be ignored are comma separated */
is_ignored_sections(const char * section_name,const char * ignore_sections)46 static bool is_ignored_sections(const char *section_name,
47 const char *ignore_sections)
48 {
49 const char *cur, *comma;
50
51 for (cur = ignore_sections; (comma = strchr(cur, ',')); cur = comma + 1)
52 if (!strncmp(cur, section_name, comma - cur))
53 return true;
54 return !strcmp(cur, section_name);
55 }
56
57 /* Find section headers based on ignored section names.
58 * Returns a NULL-terminated list of section headers.
59 */
find_ignored_sections_header(struct parsed_elf * pelf,const char * ignore_sections)60 static Elf64_Shdr **find_ignored_sections_header(struct parsed_elf *pelf,
61 const char *ignore_sections)
62 {
63 int i;
64 const char *shstrtab;
65 Elf64_Shdr **headers = NULL;
66 size_t size = 1;
67
68 /* No section needs to be ignored */
69 if (ignore_sections == NULL)
70 return NULL;
71
72 DEBUG("Sections to be ignored: %s\n", ignore_sections);
73
74 /* Get pointer to string table */
75 shstrtab = buffer_get(pelf->strtabs[pelf->ehdr.e_shstrndx]);
76
77 for (i = 0; i < pelf->ehdr.e_shnum; i++) {
78 Elf64_Shdr *shdr;
79 const char *section_name;
80
81 shdr = &pelf->shdr[i];
82 section_name = &shstrtab[shdr->sh_name];
83
84 /* If section name matches ignored string, add to list */
85 if (is_ignored_sections(section_name, ignore_sections)) {
86 headers = realloc(headers, sizeof(*headers) * ++size);
87 if (!headers) {
88 ERROR("Memory allocation failed\n");
89 exit(1);
90 }
91 headers[size - 2] = shdr;
92 }
93 }
94
95 if (headers)
96 headers[size - 1] = NULL;
97 return headers;
98 }
99
fill_cbfs_stageheader(struct cbfs_file_attr_stageheader * stageheader,uint64_t entry,uint64_t loadaddr,uint32_t memsize)100 static int fill_cbfs_stageheader(struct cbfs_file_attr_stageheader *stageheader,
101 uint64_t entry, uint64_t loadaddr,
102 uint32_t memsize)
103 {
104 if (entry - loadaddr >= memsize) {
105 ERROR("stage entry point out of bounds!\n");
106 return -1;
107 }
108
109 stageheader->loadaddr = htobe64(loadaddr);
110 stageheader->memlen = htobe32(memsize);
111 stageheader->entry_offset = htobe32(entry - loadaddr);
112
113 return 0;
114 }
115
116 /* returns size of result, or -1 if error.
117 * Note that, with the new code, this function
118 * works for all elf files, not just the restricted set.
119 */
parse_elf_to_stage(const struct buffer * input,struct buffer * output,const char * ignore_section,struct cbfs_file_attr_stageheader * stageheader)120 int parse_elf_to_stage(const struct buffer *input, struct buffer *output,
121 const char *ignore_section,
122 struct cbfs_file_attr_stageheader *stageheader)
123 {
124 struct parsed_elf pelf;
125 Elf64_Phdr *phdr;
126 Elf64_Ehdr *ehdr;
127 Elf64_Shdr **shdrs_ignored;
128 Elf64_Addr virt_to_phys;
129 int ret = -1;
130
131 int headers;
132 int i;
133 uint64_t data_start, data_end, mem_end;
134
135 int flags = ELF_PARSE_PHDR | ELF_PARSE_SHDR | ELF_PARSE_STRTAB;
136
137 if (parse_elf(input, &pelf, flags)) {
138 ERROR("Couldn't parse ELF\n");
139 return -1;
140 }
141
142 ehdr = &pelf.ehdr;
143 phdr = &pelf.phdr[0];
144
145 /* Find the section headers corresponding to ignored-sections */
146 shdrs_ignored = find_ignored_sections_header(&pelf, ignore_section);
147
148 if (ignore_section && (shdrs_ignored == NULL))
149 WARN("Ignore section(s) not found\n");
150
151 headers = ehdr->e_phnum;
152
153 /* Ignore the program header containing ignored section */
154 for (i = 0; i < headers; i++) {
155 if (is_phdr_ignored(&phdr[i], shdrs_ignored))
156 phdr[i].p_type = PT_NULL;
157 }
158
159 data_start = ~0;
160 data_end = 0;
161 mem_end = 0;
162 virt_to_phys = 0;
163
164 for (i = 0; i < headers; i++) {
165 uint64_t start, mend, rend;
166
167 if (phdr[i].p_type != PT_LOAD)
168 continue;
169
170 /* Empty segments are never interesting */
171 if (phdr[i].p_memsz == 0)
172 continue;
173
174 /* BSS */
175
176 start = phdr[i].p_paddr;
177
178 mend = start + phdr[i].p_memsz;
179 rend = start + phdr[i].p_filesz;
180
181 if (start < data_start)
182 data_start = start;
183
184 if (rend > data_end)
185 data_end = rend;
186
187 if (mend > mem_end)
188 mem_end = mend;
189
190 if (virt_to_phys == 0)
191 virt_to_phys = phdr[i].p_paddr - phdr[i].p_vaddr;
192 }
193
194 if (data_end <= data_start) {
195 ERROR("data ends (%08lx) before it starts (%08lx). Make sure "
196 "the ELF file is correct and resides in ROM space.\n",
197 (unsigned long)data_end, (unsigned long)data_start);
198 exit(1);
199 }
200
201 if (buffer_create(output, data_end - data_start, input->name) != 0) {
202 ERROR("Unable to allocate memory: %m\n");
203 goto err;
204 }
205 memset(output->data, 0, output->size);
206
207 /* Copy the file data into the output buffer */
208
209 for (i = 0; i < headers; i++) {
210 if (phdr[i].p_type != PT_LOAD)
211 continue;
212
213 if (phdr[i].p_memsz == 0)
214 continue;
215
216 /* A legal ELF file can have a program header with
217 * non-zero length but zero-length file size and a
218 * non-zero offset which, added together, are > than
219 * input->size (i.e. the total file size). So we need
220 * to not even test in the case that p_filesz is zero.
221 */
222 if (!phdr[i].p_filesz)
223 continue;
224 if (input->size < (phdr[i].p_offset + phdr[i].p_filesz)){
225 ERROR("Underflow copying out the segment."
226 "File has %zu bytes left, segment end is %zu\n",
227 input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz));
228 goto err;
229 }
230 memcpy(&output->data[phdr[i].p_paddr - data_start],
231 &input->data[phdr[i].p_offset],
232 phdr[i].p_filesz);
233 }
234
235 /* coreboot expects entry point to be physical address. Thus, adjust the
236 entry point accordingly. */
237 ret = fill_cbfs_stageheader(stageheader, ehdr->e_entry + virt_to_phys,
238 data_start, mem_end - data_start);
239 err:
240 parsed_elf_destroy(&pelf);
241 return ret;
242 }
243
244 struct xip_context {
245 struct rmod_context rmodctx;
246 Elf64_Shdr **ignored_sections;
247 };
248
rmod_filter(struct reloc_filter * f,const Elf64_Rela * r)249 static int rmod_filter(struct reloc_filter *f, const Elf64_Rela *r)
250 {
251 size_t symbol_index;
252 int reloc_type;
253 struct parsed_elf *pelf;
254 Elf64_Sym *sym;
255 struct xip_context *xipctx;
256 Elf64_Shdr **sections;
257
258 xipctx = f->context;
259 pelf = &xipctx->rmodctx.pelf;
260
261 /* Allow everything through if there isn't an ignored section. */
262 if (xipctx->ignored_sections == NULL)
263 return 1;
264
265 reloc_type = ELF64_R_TYPE(r->r_info);
266 symbol_index = ELF64_R_SYM(r->r_info);
267 sym = &pelf->syms[symbol_index];
268
269 /* Nothing to filter. Relocation is not being applied to the
270 * ignored sections. */
271 for (sections = xipctx->ignored_sections; *sections; sections++)
272 if (sym->st_shndx == *sections - pelf->shdr)
273 break;
274 if (!*sections)
275 return 1;
276
277 /* If there is any relocation to the ignored section that isn't
278 * absolute fail as current assumptions are that all relocations
279 * are absolute. */
280 if ((reloc_type != R_386_32) &&
281 (reloc_type != R_AMD64_64) &&
282 (reloc_type != R_AMD64_32)) {
283 ERROR("Invalid reloc to ignored section: %x\n", reloc_type);
284 return -1;
285 }
286
287 /* Relocation referencing ignored sections. Don't emit it. */
288 return 0;
289 }
290
291 /* Returns a NULL-terminated list of loadable segments. Returns NULL if no
292 * loadable segments were found or if two consecutive segments are not
293 * consecutive in their physical address space.
294 */
find_loadable_segments(struct parsed_elf * pelf)295 static Elf64_Phdr **find_loadable_segments(struct parsed_elf *pelf)
296 {
297 Elf64_Phdr **phdrs = NULL;
298 Elf64_Phdr *prev = NULL, *cur;
299 size_t size = 1, i;
300
301 for (i = 0; i < pelf->ehdr.e_phnum; i++, prev = cur) {
302 cur = &pelf->phdr[i];
303
304 if (cur->p_type != PT_LOAD || cur->p_memsz == 0)
305 continue;
306
307 phdrs = realloc(phdrs, sizeof(*phdrs) * ++size);
308 if (!phdrs) {
309 ERROR("Memory allocation failed\n");
310 return NULL;
311 }
312 phdrs[size - 2] = cur;
313
314 if (!prev)
315 continue;
316
317 if (prev->p_paddr + prev->p_memsz != cur->p_paddr ||
318 prev->p_filesz != prev->p_memsz) {
319 ERROR("Loadable segments physical addresses should "
320 "be consecutive\n");
321 free(phdrs);
322 return NULL;
323 }
324 }
325
326 if (phdrs)
327 phdrs[size - 1] = NULL;
328 return phdrs;
329 }
330
parse_elf_to_xip_stage(const struct buffer * input,struct buffer * output,uint32_t location,const char * ignore_sections,struct cbfs_file_attr_stageheader * stageheader)331 int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
332 uint32_t location, const char *ignore_sections,
333 struct cbfs_file_attr_stageheader *stageheader)
334 {
335 struct xip_context xipctx;
336 struct rmod_context *rmodctx;
337 struct reloc_filter filter;
338 struct parsed_elf *pelf;
339 uint32_t adjustment, memsz = 0;
340 struct buffer binput;
341 struct buffer boutput;
342 Elf64_Phdr **toload, **phdr;
343 Elf64_Xword i;
344 int ret = -1;
345 size_t filesz = 0;
346
347 rmodctx = &xipctx.rmodctx;
348 pelf = &rmodctx->pelf;
349
350 if (rmodule_init(rmodctx, input))
351 return -1;
352
353 /* Only support x86 / x86_64 XIP currently. */
354 if ((rmodctx->pelf.ehdr.e_machine != EM_386) &&
355 (rmodctx->pelf.ehdr.e_machine != EM_X86_64)) {
356 ERROR("Only support XIP stages for x86/x86_64\n");
357 goto out;
358 }
359
360 xipctx.ignored_sections =
361 find_ignored_sections_header(pelf, ignore_sections);
362
363 filter.filter = rmod_filter;
364 filter.context = &xipctx;
365
366 if (rmodule_collect_relocations(rmodctx, &filter))
367 goto out;
368
369 toload = find_loadable_segments(pelf);
370 if (!toload)
371 goto out;
372
373 for (phdr = toload; *phdr; phdr++)
374 filesz += (*phdr)->p_filesz;
375 if (buffer_create(output, filesz, input->name) != 0) {
376 ERROR("Unable to allocate memory: %m\n");
377 goto out;
378 }
379 buffer_clone(&boutput, output);
380 memset(buffer_get(&boutput), 0, filesz);
381 buffer_set_size(&boutput, 0);
382
383 /* The program segment moves to final location from based on virtual
384 * address of loadable segment. */
385 adjustment = location - pelf->phdr->p_vaddr;
386 DEBUG("Relocation adjustment: %08x\n", adjustment);
387
388 for (phdr = toload; *phdr; phdr++)
389 memsz += (*phdr)->p_memsz;
390 fill_cbfs_stageheader(stageheader,
391 (uint32_t)pelf->ehdr.e_entry + adjustment,
392 (uint32_t)pelf->phdr->p_vaddr + adjustment,
393 memsz);
394 for (phdr = toload; *phdr; phdr++) {
395 /* Need an adjustable buffer. */
396 buffer_clone(&binput, input);
397 buffer_seek(&binput, (*phdr)->p_offset);
398 bputs(&boutput, buffer_get(&binput), (*phdr)->p_filesz);
399 }
400
401 buffer_clone(&boutput, output);
402
403 /* Make adjustments to all the relocations within the program. */
404 for (i = 0; i < rmodctx->nrelocs; i++) {
405 size_t reloc_offset;
406 uint32_t val;
407 struct buffer in, out;
408
409 /* The relocations represent in-program addresses of the
410 * linked program. Obtain the offset into the program to do
411 * the adjustment. */
412 reloc_offset = rmodctx->emitted_relocs[i] - pelf->phdr->p_vaddr;
413
414 buffer_clone(&out, &boutput);
415 buffer_seek(&out, reloc_offset);
416 buffer_clone(&in, &out);
417 /* Appease around xdr semantics: xdr decrements buffer
418 * size when get()ing and appends to size when put()ing. */
419 buffer_set_size(&out, 0);
420
421 val = xdr_le.get32(&in);
422 DEBUG("reloc %zx %08x -> %08x\n", reloc_offset, val,
423 val + adjustment);
424 xdr_le.put32(&out, val + adjustment);
425 }
426
427 ret = 0;
428
429 out:
430 rmodule_cleanup(rmodctx);
431 return ret;
432 }
433