File: | util/cbfstool/cbfs-mkstage.c |
Warning: | line 309, column 4 Potential leak of memory pointed to by 'phdrs' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 */ | |||
14 | static int is_phdr_ignored(Elf64_Phdr *phdr, Elf64_Shdr **shdrs) | |||
15 | { | |||
16 | /* If no ignored section, return false. */ | |||
17 | if (shdrs == NULL((void*)0)) | |||
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)do { if (verbose > 1) fprintf(stderr, "DEBUG: " "Ignoring program segment at 0x%" "l" "x" "\n", ph_start); } while (0); | |||
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")fprintf(stderr, "E: " "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 */ | |||
46 | static bool_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 true1; | |||
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 | */ | |||
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((void*)0); | |||
66 | size_t size = 1; | |||
67 | ||||
68 | /* No section needs to be ignored */ | |||
69 | if (ignore_sections == NULL((void*)0)) | |||
70 | return NULL((void*)0); | |||
71 | ||||
72 | DEBUG("Sections to be ignored: %s\n", ignore_sections)do { if (verbose > 1) fprintf(stderr, "DEBUG: " "Sections to be ignored: %s\n" , ignore_sections); } while (0); | |||
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")fprintf(stderr, "E: " "Memory allocation failed\n"); | |||
89 | exit(1); | |||
90 | } | |||
91 | headers[size - 2] = shdr; | |||
92 | } | |||
93 | } | |||
94 | ||||
95 | if (headers) | |||
96 | headers[size - 1] = NULL((void*)0); | |||
97 | return headers; | |||
98 | } | |||
99 | ||||
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")fprintf(stderr, "E: " "stage entry point out of bounds!\n"); | |||
106 | return -1; | |||
107 | } | |||
108 | ||||
109 | stageheader->loadaddr = htobe64(loadaddr)__bswap_64 (loadaddr); | |||
110 | stageheader->memlen = htobe32(memsize)__bswap_32 (memsize); | |||
111 | stageheader->entry_offset = htobe32(entry - loadaddr)__bswap_32 (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 | */ | |||
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(1 << 0) | ELF_PARSE_SHDR(1 << 1) | ELF_PARSE_STRTAB(1 << 3); | |||
136 | ||||
137 | if (parse_elf(input, &pelf, flags)) { | |||
138 | ERROR("Couldn't parse ELF\n")fprintf(stderr, "E: " "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((void*)0))) | |||
149 | WARN("Ignore section(s) not found\n")fprintf(stderr, "W: " "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_NULL0; | |||
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_LOAD1) | |||
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 "fprintf(stderr, "E: " "data ends (%08lx) before it starts (%08lx). Make sure " "the ELF file is correct and resides in ROM space.\n", (unsigned long)data_end, (unsigned long)data_start) | |||
196 | "the ELF file is correct and resides in ROM space.\n",fprintf(stderr, "E: " "data ends (%08lx) before it starts (%08lx). Make sure " "the ELF file is correct and resides in ROM space.\n", (unsigned long)data_end, (unsigned long)data_start) | |||
197 | (unsigned long)data_end, (unsigned long)data_start)fprintf(stderr, "E: " "data ends (%08lx) before it starts (%08lx). Make sure " "the ELF file is correct and resides in ROM space.\n", (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")fprintf(stderr, "E: " "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_LOAD1) | |||
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."fprintf(stderr, "E: " "Underflow copying out the segment." "File has %zu bytes left, segment end is %zu\n" , input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz )) | |||
226 | "File has %zu bytes left, segment end is %zu\n",fprintf(stderr, "E: " "Underflow copying out the segment." "File has %zu bytes left, segment end is %zu\n" , input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz )) | |||
227 | input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz))fprintf(stderr, "E: " "Underflow copying out the segment." "File has %zu bytes left, segment end is %zu\n" , 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 | ||||
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((void*)0)) | |||
263 | return 1; | |||
264 | ||||
265 | reloc_type = ELF64_R_TYPE(r->r_info)((r->r_info) & 0xffffffff); | |||
266 | symbol_index = ELF64_R_SYM(r->r_info)((r->r_info) >> 32); | |||
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_321) && | |||
281 | (reloc_type != R_AMD64_641) && | |||
282 | (reloc_type != R_AMD64_3210)) { | |||
283 | ERROR("Invalid reloc to ignored section: %x\n", reloc_type)fprintf(stderr, "E: " "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 | */ | |||
295 | static Elf64_Phdr **find_loadable_segments(struct parsed_elf *pelf) | |||
296 | { | |||
297 | Elf64_Phdr **phdrs = NULL((void*)0); | |||
298 | Elf64_Phdr *prev = NULL((void*)0), *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_LOAD1 || cur->p_memsz == 0) | |||
305 | continue; | |||
306 | ||||
307 | phdrs = realloc(phdrs, sizeof(*phdrs) * ++size); | |||
308 | if (!phdrs) { | |||
309 | ERROR("Memory allocation failed\n")fprintf(stderr, "E: " "Memory allocation failed\n"); | |||
| ||||
310 | return NULL((void*)0); | |||
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 "fprintf(stderr, "E: " "Loadable segments physical addresses should " "be consecutive\n") | |||
320 | "be consecutive\n")fprintf(stderr, "E: " "Loadable segments physical addresses should " "be consecutive\n"); | |||
321 | free(phdrs); | |||
322 | return NULL((void*)0); | |||
323 | } | |||
324 | } | |||
325 | ||||
326 | if (phdrs) | |||
327 | phdrs[size - 1] = NULL((void*)0); | |||
328 | return phdrs; | |||
329 | } | |||
330 | ||||
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_3863) && | |||
355 | (rmodctx->pelf.ehdr.e_machine != EM_X86_6462)) { | |||
356 | ERROR("Only support XIP stages for x86/x86_64\n")fprintf(stderr, "E: " "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")fprintf(stderr, "E: " "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)do { if (verbose > 1) fprintf(stderr, "DEBUG: " "Relocation adjustment: %08x\n" , adjustment); } while (0); | |||
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,do { if (verbose > 1) fprintf(stderr, "DEBUG: " "reloc %zx %08x -> %08x\n" , reloc_offset, val, val + adjustment); } while (0) | |||
423 | val + adjustment)do { if (verbose > 1) fprintf(stderr, "DEBUG: " "reloc %zx %08x -> %08x\n" , reloc_offset, val, val + adjustment); } while (0); | |||
424 | xdr_le.put32(&out, val + adjustment); | |||
425 | } | |||
426 | ||||
427 | ret = 0; | |||
428 | ||||
429 | out: | |||
430 | rmodule_cleanup(rmodctx); | |||
431 | return ret; | |||
432 | } |