aos/lib/elf/elf.c
Daniel Schwyn 6d444bf552 Main handout
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>
2022-03-03 14:57:51 +01:00

273 lines
7.6 KiB
C

/**
* \file
* \brief Rudimentary ELF loader and handling routines.
*/
/*
* Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
*/
/* Restricted includes, because this file is used in three environments:
* in the preboot loader tool "elver", in 32-bit mode
* in-kernel, 64-bit mode
* userspace, 64-bit mode
*/
#include <stddef.h>
#include <stdint.h>
#include <barrelfish_kpi/paging_arch.h>
#include <barrelfish_kpi/types.h>
#include <errors/errno.h>
#include <elf/elf.h>
/**
* \brief Load ELF binary image into memory
*
* This function loads an ELF binary image, based at 'base' and of size
* 'size' into the memory provided by 'allocate'
*
* \param em_machine ELF machine type.
* \param allocate Memory allocation function.
* \param state Pointer to state for allocation function.
* \param base Base address of ELF binary image in memory.
* \param size Size of ELF binary image in bytes.
* \param retentry Used to return entry point address
* \param ret_tlsbase Used to return TLS block base address
* \param ret_tlssize Used to return TLS block size
*/
errval_t elf_load_tls(uint16_t em_machine, elf_allocator_fn allocate_func,
void *state, lvaddr_t base,
size_t size, genvaddr_t *retentry,
genvaddr_t *ret_tlsbase, size_t *ret_tlsinitlen,
size_t *ret_tlstotallen)
{
struct Elf64_Ehdr *head = (struct Elf64_Ehdr *)base;
if(!IS_ELF(*head)) {
return ELF_ERR_HEADER;
}
switch(head->e_ident[EI_CLASS]) {
case ELFCLASS32:
return elf32_load(em_machine, allocate_func, state, base, size,
retentry, ret_tlsbase, ret_tlsinitlen, ret_tlstotallen);
case ELFCLASS64:
return elf64_load(em_machine, allocate_func, state, base, size,
retentry, ret_tlsbase, ret_tlsinitlen, ret_tlstotallen);
}
return ELF_ERR_HEADER;
}
errval_t elf_load(uint16_t em_machine, elf_allocator_fn allocate_func,
void *state, lvaddr_t base,
size_t size, genvaddr_t *retentry)
{
return elf_load_tls(em_machine, allocate_func, state, base, size,
retentry, NULL, NULL, NULL);
}
static size_t elf_virtual_size64(struct Elf64_Ehdr *ehead)
{
struct Elf64_Phdr *phead =
(struct Elf64_Phdr *)((uintptr_t)ehead + (uintptr_t)ehead->e_phoff);
size_t retval = 0;
int i;
for (i = 0; i < ehead->e_phnum; i++) {
struct Elf64_Phdr *p = &phead[i];
if (p->p_type == PT_LOAD) {
retval = p->p_vaddr + p->p_memsz;
}
}
return retval - elf_virtual_base64(ehead);
}
static size_t elf_virtual_size32(struct Elf32_Ehdr *ehead)
{
struct Elf32_Phdr *phead =
(struct Elf32_Phdr *)((uintptr_t)ehead + (uintptr_t)ehead->e_phoff);
size_t retval = 0;
int i;
for (i = 0; i < ehead->e_phnum; i++) {
struct Elf32_Phdr *p = &phead[i];
if (p->p_type == PT_LOAD) {
retval = p->p_vaddr + p->p_memsz;
}
}
return retval - elf_virtual_base32(ehead);
}
/**
* \brief Calculates the size of the loadable portion of the elf image in
* virtual memory. This is the amount of virtual memory required to load an
* image.
*/
size_t elf_virtual_size(lvaddr_t base)
{
size_t elfsize = 0;
struct Elf64_Ehdr *ehead = (struct Elf64_Ehdr *)base;
if (IS_ELF(*ehead)) {
switch (ehead->e_ident[EI_CLASS]) {
case ELFCLASS64:
elfsize = elf_virtual_size64(ehead);
break;
case ELFCLASS32:
elfsize = elf_virtual_size32((struct Elf32_Ehdr*)base);
break;
}
}
return elfsize;
}
/**
* \brief Calculates the base of the loadable portion of the elf image in
* virtual memory.
*/
genvaddr_t elf_virtual_base(lvaddr_t base)
{
genvaddr_t elfbase = 0;
struct Elf64_Ehdr *ehead = (struct Elf64_Ehdr *)base;
if (IS_ELF(*ehead)) {
switch (ehead->e_ident[EI_CLASS]) {
case ELFCLASS64:
elfbase = elf_virtual_base64(ehead);
break;
case ELFCLASS32:
elfbase = elf_virtual_base32((struct Elf32_Ehdr*)base);
break;
}
}
return elfbase;
}
static errval_t elf32_get_eh_info(lvaddr_t elfbase, size_t elfsize,
lvaddr_t *eh_frame, size_t *eh_frame_size,
lvaddr_t *eh_frame_hdr, size_t *eh_frame_hdr_size)
{
struct Elf32_Shdr *shdr;
lvaddr_t addr = 0;
size_t size = 0;
shdr= elf32_find_section_header_name(elfbase, elfsize, ".eh_frame");
if (shdr != NULL) {
size = shdr->sh_size;
addr = shdr->sh_addr;
}
if (eh_frame) {
*eh_frame = addr;
}
if (eh_frame_size) {
*eh_frame_size = size;
}
size = 0;
addr = 0;
shdr= elf32_find_section_header_name(elfbase, elfsize, ".eh_frame_hdr");
if (shdr != NULL) {
size = shdr->sh_size;
addr = shdr->sh_addr;
}
if (eh_frame_hdr) {
*eh_frame_hdr = addr;
}
if (eh_frame_hdr_size) {
*eh_frame_hdr_size = size;
}
return SYS_ERR_OK;
}
static errval_t elf64_get_eh_info(lvaddr_t elfbase, size_t elfsize,
lvaddr_t *eh_frame, size_t *eh_frame_size,
lvaddr_t *eh_frame_hdr, size_t *eh_frame_hdr_size)
{
struct Elf64_Shdr *shdr;
lvaddr_t addr = 0;
size_t size = 0;
shdr= elf64_find_section_header_name(elfbase, elfsize, ".eh_frame");
if (shdr != NULL) {
size = shdr->sh_size;
addr = shdr->sh_addr;
}
if (eh_frame) {
*eh_frame = addr;
}
if (eh_frame_size) {
*eh_frame_size = size;
}
size = 0;
addr = 0;
shdr= elf64_find_section_header_name(elfbase, elfsize, ".eh_frame_hdr");
if (shdr != NULL) {
size = shdr->sh_size;
addr = shdr->sh_addr;
}
if (eh_frame_hdr) {
*eh_frame_hdr = addr;
}
if (eh_frame_hdr_size) {
*eh_frame_hdr_size = size;
}
return SYS_ERR_OK;
}
/**
* \brief obtains the error handling frame information form the elf image
*
* \param elfbase virtual base address of the mapped elf
* \param elfsize size of the elf in bytes
* \param eh_frame returns the virtual address of the eh_frame
* \param eh_frame_size returns the size of the eh_frame
* \param eh_frame_hdr returns the virtual address of the eh_frame_hdr
* \param eh_frame_hdr_size returns the size of the eh_frame_hdr
*
* \returns SYS_ERR_OK on success
*/
errval_t elf_get_eh_info(lvaddr_t elfbase, size_t elfsize,
lvaddr_t *eh_frame, size_t *eh_frame_size,
lvaddr_t *eh_frame_hdr, size_t *eh_frame_hdr_size)
{
struct Elf64_Ehdr *ehead = (struct Elf64_Ehdr *)elfbase;
if (IS_ELF(*ehead)) {
switch (ehead->e_ident[EI_CLASS]) {
case ELFCLASS64:
return elf64_get_eh_info(elfbase, elfsize, eh_frame, eh_frame_size,
eh_frame_hdr, eh_frame_hdr_size);
break;
case ELFCLASS32:
return elf32_get_eh_info(elfbase, elfsize, eh_frame, eh_frame_size,
eh_frame_hdr, eh_frame_hdr_size);
break;
}
}
return ELF_ERR_HEADER;
}