cbfs-mkstage.c
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) 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 */ 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 */ 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 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 */ 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 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 */ 295 static Elf64_Phdr **find_loadable_segments(struct parsed_elf *pelf) 296 { 297 Elf64_Phdr **phdrs = NULL; 298 Elf64_Phdr *prev = NULL, *cur = NULL; 299 size_t size = 1, i; 300 301 for (i = 0; i < pelf->ehdr.e_phnum; i++) { 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 const bool bfd_is_consecutive = prev->p_paddr + prev->p_memsz == cur->p_paddr 316 && prev->p_filesz == prev->p_memsz; 317 /* 318 * lld pads the memsz of the .text vaddr till the vaddr of car.data. 319 * Since we don't load XIP stages at runtime, we don't care. 320 */ 321 const bool lld_is_consecutive = prev->p_vaddr + prev->p_memsz == cur->p_vaddr; 322 323 if (!bfd_is_consecutive && !lld_is_consecutive) { 324 ERROR("Loadable segments physical addresses should " 325 "be consecutive\n"); 326 free(phdrs); 327 return NULL; 328 } 329 } 330 prev = cur; 331 } 332 333 if (phdrs) 334 phdrs[size - 1] = NULL; 335 return phdrs; 336 } 337 338 int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output, 339 uint32_t location, const char *ignore_sections, 340 struct cbfs_file_attr_stageheader *stageheader) 341 { 342 struct xip_context xipctx; 343 struct rmod_context *rmodctx; 344 struct reloc_filter filter; 345 struct parsed_elf *pelf; 346 uint32_t adjustment, memsz = 0; 347 struct buffer binput; 348 struct buffer boutput; 349 Elf64_Phdr **toload, **phdr; 350 Elf64_Xword i; 351 int ret = -1; 352 size_t filesz = 0; 353 354 rmodctx = &xipctx.rmodctx; 355 pelf = &rmodctx->pelf; 356 357 if (rmodule_init(rmodctx, input)) 358 return -1; 359 360 /* Only support x86 / x86_64 XIP currently. */ 361 if ((rmodctx->pelf.ehdr.e_machine != EM_386) && 362 (rmodctx->pelf.ehdr.e_machine != EM_X86_64)) { 363 ERROR("Only support XIP stages for x86/x86_64\n"); 364 goto out; 365 } 366 367 xipctx.ignored_sections = 368 find_ignored_sections_header(pelf, ignore_sections); 369 370 filter.filter = rmod_filter; 371 filter.context = &xipctx; 372 373 if (rmodule_collect_relocations(rmodctx, &filter)) 374 goto out; 375 376 toload = find_loadable_segments(pelf); 377 if (!toload) 378 goto out; 379 380 for (phdr = toload; *phdr; phdr++) 381 filesz += (*phdr)->p_filesz; 382 if (buffer_create(output, filesz, input->name) != 0) { 383 ERROR("Unable to allocate memory: %m\n"); 384 goto out; 385 } 386 buffer_clone(&boutput, output); 387 memset(buffer_get(&boutput), 0, filesz); 388 buffer_set_size(&boutput, 0); 389 390 /* The program segment moves to final location from based on virtual 391 * address of loadable segment. */ 392 adjustment = location - pelf->phdr->p_vaddr; 393 DEBUG("Relocation adjustment: %08x\n", adjustment); 394 395 for (phdr = toload; *phdr; phdr++) 396 memsz += (*phdr)->p_memsz; 397 fill_cbfs_stageheader(stageheader, 398 (uint32_t)pelf->ehdr.e_entry + adjustment, 399 (uint32_t)pelf->phdr->p_vaddr + adjustment, 400 memsz); 401 for (phdr = toload; *phdr; phdr++) { 402 /* Need an adjustable buffer. */ 403 buffer_clone(&binput, input); 404 buffer_seek(&binput, (*phdr)->p_offset); 405 bputs(&boutput, buffer_get(&binput), (*phdr)->p_filesz); 406 } 407 408 buffer_clone(&boutput, output); 409 410 /* Make adjustments to all the relocations within the program. */ 411 for (i = 0; i < rmodctx->nrelocs; i++) { 412 size_t reloc_offset; 413 uint32_t val; 414 struct buffer in, out; 415 Elf64_Addr reloc = rmodctx->emitted_relocs[i]; 416 417 /* The relocations represent in-program addresses of the 418 * linked program. Obtain the offset into the program to do 419 * the adjustment. */ 420 reloc_offset = 0; 421 for (phdr = toload; *phdr; phdr++) { 422 if (reloc >= (*phdr)->p_vaddr && 423 reloc < (*phdr)->p_vaddr + (*phdr)->p_memsz) 424 break; 425 reloc_offset += (*phdr)->p_filesz; 426 } 427 if (!*phdr) { 428 ERROR("Relocation outside of loadable segments\n"); 429 goto out; 430 } 431 reloc_offset += reloc - (*phdr)->p_vaddr; 432 433 buffer_clone(&out, &boutput); 434 buffer_seek(&out, reloc_offset); 435 buffer_clone(&in, &out); 436 /* Appease around xdr semantics: xdr decrements buffer 437 * size when get()ing and appends to size when put()ing. */ 438 buffer_set_size(&out, 0); 439 440 val = xdr_le.get32(&in); 441 DEBUG("reloc %zx %08x -> %08x\n", reloc_offset, val, 442 val + adjustment); 443 xdr_le.put32(&out, val + adjustment); 444 } 445 446 ret = 0; 447 448 out: 449 rmodule_cleanup(rmodctx); 450 return ret; 451 }