static errval_t relocate_cpu_binary(lvaddr_t cpu_binary, struct Elf64_Ehdr *cpu_head, struct elf_allocate_state state, struct frame_identity frameid, genpaddr_t arch_page_size) { switch (cpu_head->e_machine) { case EM_X86_64: case EM_K1OM: { struct Elf64_Shdr *rela, *symtab, *symhead = (struct Elf64_Shdr *)(cpu_binary + (uintptr_t)cpu_head->e_shoff); assert(cpu_head->e_shoff != 0); rela = elf64_find_section_header_type(symhead, cpu_head->e_shnum, SHT_RELA); assert(rela != NULL); symtab = elf64_find_section_header_type(symhead, cpu_head->e_shnum, SHT_DYNSYM); assert(symtab != NULL); elf64_relocate(frameid.base + arch_page_size, state.elfbase, (struct Elf64_Rela *)(uintptr_t)(cpu_binary + rela->sh_offset), rela->sh_size, (struct Elf64_Sym *)(uintptr_t)(cpu_binary + symtab->sh_offset), symtab->sh_size, state.elfbase, state.vbase); break; } case EM_386: { struct Elf32_Ehdr *head32 = (struct Elf32_Ehdr *)cpu_binary; struct Elf32_Shdr *rel, *symtab, *symhead = (struct Elf32_Shdr *)(cpu_binary + (uintptr_t)head32->e_shoff); rel = elf32_find_section_header_type(symhead, head32->e_shnum, SHT_REL); assert(rel != NULL); symtab = elf32_find_section_header_type(symhead, head32->e_shnum, SHT_DYNSYM); assert(symtab != NULL); elf32_relocate(frameid.base + arch_page_size, state.elfbase, (struct Elf32_Rel *)(uintptr_t)(cpu_binary + rel->sh_offset), rel->sh_size, (struct Elf32_Sym *)(uintptr_t)(cpu_binary + symtab->sh_offset), symtab->sh_size, state.elfbase, state.vbase); break; } default: return SPAWN_ERR_UNKNOWN_TARGET_ARCH; } return SYS_ERR_OK; }
/** * \brief Load ELF64 binary image into memory * * This function loads an ELF64 binary image, based at 'base' and of size * 'size' into the memory provided by 'allocate' * * \param allocate Memory allocation function. * \param state Pointer to state for allocation function. * \param base Base address of ELF64 binary image in memory. * \param size Size of ELF64 binary image in bytes. * \param retentry Used to return entry point address */ errval_t elf64_load(elf_allocator_fn allocate_func, void *state, lvaddr_t base, size_t size, genvaddr_t *retentry) { struct Elf64_Ehdr *head = (struct Elf64_Ehdr *)base; errval_t err; int i; // Check for valid file size if (size < sizeof(struct Elf64_Ehdr)) { return -1; } // Check for compatible ELF64 header if (!IS_ELF(*head) || head->e_ident[EI_CLASS] != ELFCLASS64 || head->e_ident[EI_DATA] != ELFDATA2LSB || head->e_ident[EI_VERSION] != EV_CURRENT || head->e_ident[EI_OSABI] != ELFOSABI_SYSV || head->e_ident[EI_ABIVERSION] != 0 || (head->e_type != ET_EXEC && head->e_type != ET_DYN) || head->e_version != EV_CURRENT) { return -2; } // More sanity checks if (head->e_phoff + head->e_phentsize * head->e_phnum > size || head->e_phentsize != sizeof(struct Elf64_Phdr)) { return -3; } struct Elf64_Shdr *shead = (struct Elf64_Shdr *)(base + (uintptr_t)head->e_shoff); struct Elf64_Shdr *rela = elf64_find_section_header_type(shead, head->e_shnum, SHT_RELA); struct Elf64_Shdr *symtab = elf64_find_section_header_type(shead, head->e_shnum, SHT_SYMTAB); size_t rela_size = rela ? rela->sh_size : 0, new_rela_size = 0; struct Elf64_Shdr *new_rela = NULL; // Find dynamic program header, if any struct Elf64_Phdr *phead = (struct Elf64_Phdr *)(base + (uintptr_t)head->e_phoff); for (i = 0; i < head->e_phnum; i++) { struct Elf64_Phdr *p = &phead[i]; if (p->p_type == PT_DYNAMIC) { struct Elf64_Dyn *dynamic = (void *)(base + (uintptr_t)p->p_offset); int n_dynamic = p->p_filesz / sizeof(struct Elf64_Dyn); for (int j = 0; j < n_dynamic; j++) { switch (dynamic[j].d_tag) { case DT_RELA: // virtual address of relocations, look for matching section new_rela = elf64_find_section_header_vaddr(shead, head->e_shnum, dynamic[j].d_un.d_val); break; case DT_RELASZ: // store size of relocations, as they may cover more than // one section new_rela_size = dynamic[j].d_un.d_val; break; case DT_SYMTAB: // virtual address of symtab, look for matching section symtab = elf64_find_section_header_vaddr(shead, head->e_shnum, dynamic[j].d_un.d_val); break; case DT_SYMENT: assert(dynamic[j].d_un.d_val == sizeof(struct Elf64_Sym)); break; } } if (new_rela != NULL) { assert(new_rela_size != 0); rela = new_rela; rela_size = new_rela_size; } break; } } // Process program headers to load file for (i = 0; i < head->e_phnum; i++) { struct Elf64_Phdr *p = &phead[i]; if (p->p_type == PT_LOAD) { //printf("Loading segment: start=0x%lx, size=%lu, flags=%d\n", //p->p_vaddr, p->p_memsz, p->p_flags); // Map segment in user-space memory void *dest = NULL; err = allocate_func(state, (genvaddr_t)p->p_vaddr, p->p_memsz, p->p_flags, &dest); if (err != 0) { return -4; } assert(dest != NULL); // Copy file segment into memory memcpy(dest, (void *)(base + (uintptr_t)p->p_offset), p->p_filesz); // Initialize rest of memory segment (ie. BSS) with all zeroes memset((char *)dest + p->p_filesz, 0, p->p_memsz - p->p_filesz); // Apply relocations if (rela != NULL && symtab != NULL) { elf64_relocate(p->p_vaddr, p->p_vaddr, (struct Elf64_Rela *) (base + (uintptr_t)rela->sh_offset), rela_size, (struct Elf64_Sym *) (base + (uintptr_t)symtab->sh_offset), symtab->sh_size, p->p_vaddr, dest); } } } if (retentry != NULL) { *retentry = head->e_entry; } return 0; }