fxBoot/src/hypervisor/internal/elf/reloc.c

188 lines
4.9 KiB
C

//---
// fxBoot:loader:entry - ELF Loader entry
//---
#include "fxBoot/hypervisor.h"
#include "fxBoot/fs/smemfs.h"
#include "fxBoot/elf.h"
#include "fxBoot/terminal.h"
#include <gint/std/stdlib.h>
#include <gint/std/string.h>
#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);
}