//--- // fxBoot:loader:entry - ELF Loader entry //--- #include "fxBoot/hypervisor.h" #include "fxBoot/fs/smemfs.h" #include "fxBoot/elf.h" #include "fxBoot/terminal.h" #include #include #include "./src/hypervisor/internal/elf.h" //--- // Internal helpers //--- /* relocinfo: Internal structure used to stoe all ELF information */ struct relocinfo { Elf32_Ehdr *ehdr; Elf32_Shdr ehdr_dynamic; Elf32_Dyn *dynamic; Elf32_Sym *dynsym; char *dynstr; char *shstrtab; struct smemfs_inode *inode; }; /* reloc_dump_section(): Dump one section */ static void *reloc_dump_section(struct smemfs_inode *inode, Elf32_Shdr *shdr) { void *section; section = malloc(shdr->sh_size); if (section == NULL) return (NULL); if (smemfs_pread(inode, section, shdr->sh_size, shdr->sh_offset) != (ssize_t)shdr->sh_size) { free(section); return (NULL); } return (section); } /* reloc_get_dynamic_info(): Dump all dynamic information */ static int reloc_get_dynamic_info(struct relocinfo *elf) { Elf32_Shdr shdr; off_t offset; void **tmp; /* get ELF section header string table */ offset = elf->ehdr->e_shoff; offset += elf->ehdr->e_shstrndx * elf->ehdr->e_shentsize; if (smemfs_pread(elf->inode, &shdr, elf->ehdr->e_shentsize, offset) != elf->ehdr->e_shentsize) { return (-1); } elf->shstrtab = reloc_dump_section(elf->inode, &shdr); if (elf->shstrtab == NULL) return (-2); /* get ELF information */ elf->dynstr = NULL; elf->dynamic = NULL; for (int i = 1 ; i < elf->ehdr->e_shnum ; ++i) { offset = elf->ehdr->e_shoff + (i * elf->ehdr->e_shentsize); if (smemfs_pread(elf->inode, &shdr, elf->ehdr->e_shentsize, offset) != elf->ehdr->e_shentsize) { continue; } tmp = NULL; if (strcmp(".dynamic", &elf->shstrtab[shdr.sh_name]) == 0) { memcpy(&elf->ehdr_dynamic, &shdr, sizeof(Elf32_Shdr)); tmp = (void*)&elf->dynamic; } if (strcmp(".dynstr", &elf->shstrtab[shdr.sh_name]) == 0) tmp = (void*)&elf->dynstr; if (strcmp(".dynsym", &elf->shstrtab[shdr.sh_name]) == 0) tmp = (void*)&elf->dynsym; if (tmp == NULL) continue; *tmp = reloc_dump_section(elf->inode, &shdr); } return (0); } /* reloc_get_dynsym(): Try to find the dynamic symbol address */ static void *reloc_get_dynsym(struct relocinfo *info, int symid) { char *name; if (info->dynsym == NULL || info->dynstr == NULL) return (NULL); //FIXME: check symid validity ! name = &info->dynstr[info->dynsym[symid].st_name]; for (int i = 0; gint_reloc[i].name != NULL; ++i) { if (strcmp(name, gint_reloc[i].name) != 0) continue; terminal_write(" * reloc '%s'\n", name); return ((void*)gint_reloc[i].addr); } terminal_write("unknown '%s' symbols\n", name); return (NULL); } /* reloc_section(): Relocalise section's symbols */ static int reloc_rela(struct hworld *world, struct relocinfo *info, Elf32_Shdr *shdr) { Elf32_Rela rela; uint32_t *prog; off_t offset; void *dyn; if (shdr->sh_size == 0 || shdr->sh_entsize == 0) { terminal_write("empty section\n"); return (0); } prog = (void*)world->memory.program.start; for (uint32_t i = 0 ; i < shdr->sh_size / shdr->sh_entsize ; ++i) { offset = shdr->sh_offset + (i * shdr->sh_entsize); if (smemfs_pread(info->inode, &rela, shdr->sh_entsize, offset) != (ssize_t)shdr->sh_entsize) { return (hel_reloc_size_error); } if (ELF32_R_TYPE(rela.r_info) == R_SH_RELATIVE) { offset = rela.r_offset; offset -= world->memory.program.elf.vmin; prog[offset >> 2] -= world->memory.program.elf.vmin; prog[offset >> 2] += (uintptr_t)world->memory.program.start; continue; } if (ELF32_R_TYPE(rela.r_info) == R_SH_JMP_SLOT) { dyn = reloc_get_dynsym(info, ELF32_R_SYM(rela.r_info)); if (dyn == NULL) return (-1); offset = rela.r_offset; offset -= world->memory.program.elf.vmin; prog[offset >> 2] = (uintptr_t)dyn; world->private.env = HYPERVISOR_ENV_GINT; continue; } terminal_write("unknown type %x\n", rela.r_info); return (-1); } return (hel_reloc_success); } //--- // Public helpers //--- /* loader_reloc_sym(): Relocalise all symbols */ int hypervisor_elf_loader_reloc_sym(struct hworld *world, struct smemfs_inode *inode, Elf32_Ehdr *ehdr) { struct relocinfo info; Elf32_Shdr shdr; off_t offset; /* get dynamic information */ info.inode = inode; info.ehdr = ehdr; if (reloc_get_dynamic_info(&info) != 0) return (-1); /* find all reloc section */ //TODO : support SH_REL //TODO : check if the GOT, PLT sections are relocalized ! for (int i = 1 ; i < ehdr->e_shnum ; ++i) { offset = ehdr->e_shoff + (i * ehdr->e_shentsize); if (smemfs_pread(inode, &shdr, ehdr->e_shentsize, offset) != ehdr->e_shentsize) { return (hel_reloc_size_error); } if (shdr.sh_type == SHT_RELA && reloc_rela(world, &info, &shdr) != hel_reloc_success) return (hel_reloc_error); if (shdr.sh_type == SHT_REL) { terminal_write("SHT_REL not yet supported\n"); return (hel_reloc_error); } } return (hel_reloc_success); }