Files
haiku-beta6/src/system/boot/loader/elf.cpp
T
Ingo Weinhold e84b16339c Fixed loading of modules whose text and data segments have a little
space inbetween. The segments can be in inverse order now, too.
The same should be done in the kernel, too.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@14907 a95241bf-73f2-0310-859d-f6bbb57e9c96
2005-11-14 10:24:45 +00:00

356 lines
8.4 KiB
C++

/*
** Copyright 2002-2004, Axel Dörfler, [email protected]. All rights reserved.
** Distributed under the terms of the Haiku License.
*/
#include "elf.h"
#include <boot/platform.h>
#include <boot/stage2.h>
#include <elf32.h>
#include <kernel.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
//#define TRACE_ELF
#ifdef TRACE_ELF
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
static status_t
verify_elf_header(struct Elf32_Ehdr &header)
{
if (memcmp(header.e_ident, ELF_MAGIC, 4) != 0
|| header.e_ident[4] != ELFCLASS32
|| header.e_phoff == 0
|| !header.IsHostEndian()
|| header.e_phentsize != sizeof(struct Elf32_Phdr))
return B_BAD_TYPE;
return B_OK;
}
static status_t
load_elf_symbol_table(int fd, preloaded_image *image)
{
struct Elf32_Ehdr &elfHeader = image->elf_header;
Elf32_Sym *symbolTable = NULL;
Elf32_Shdr *stringHeader = NULL;
uint32 numSymbols = 0;
char *stringTable;
status_t status;
// get section headers
ssize_t size = elfHeader.e_shnum * elfHeader.e_shentsize;
Elf32_Shdr *sectionHeaders = (struct Elf32_Shdr *)malloc(size);
if (sectionHeaders == NULL) {
dprintf("error allocating space for section headers\n");
return B_NO_MEMORY;
}
ssize_t length = read_pos(fd, elfHeader.e_shoff, sectionHeaders, size);
if (length < size) {
TRACE(("error reading in program headers\n"));
status = B_ERROR;
goto error1;
}
// find symbol table in section headers
for (int32 i = 0; i < elfHeader.e_shnum; i++) {
if (sectionHeaders[i].sh_type == SHT_SYMTAB) {
stringHeader = &sectionHeaders[sectionHeaders[i].sh_link];
if (stringHeader->sh_type != SHT_STRTAB) {
TRACE(("doesn't link to string table\n"));
status = B_BAD_DATA;
goto error1;
}
// read in symbol table
symbolTable = (Elf32_Sym *)kernel_args_malloc(size = sectionHeaders[i].sh_size);
if (symbolTable == NULL) {
status = B_NO_MEMORY;
goto error1;
}
length = read_pos(fd, sectionHeaders[i].sh_offset, symbolTable, size);
if (length < size) {
TRACE(("error reading in symbol table\n"));
status = B_ERROR;
goto error1;
}
numSymbols = size / sizeof(Elf32_Sym);
break;
}
}
if (symbolTable == NULL) {
TRACE(("no symbol table\n"));
status = B_BAD_VALUE;
goto error1;
}
// read in string table
stringTable = (char *)kernel_args_malloc(size = stringHeader->sh_size);
if (stringTable == NULL) {
status = B_NO_MEMORY;
goto error2;
}
length = read_pos(fd, stringHeader->sh_offset, stringTable, size);
if (length < size) {
TRACE(("error reading in string table\n"));
status = B_ERROR;
goto error3;
}
TRACE(("loaded debug %ld symbols\n", numSymbols));
// insert tables into image
image->debug_symbols = symbolTable;
image->num_debug_symbols = numSymbols;
image->debug_string_table = stringTable;
image->debug_string_table_size = size;
free(sectionHeaders);
return B_OK;
error3:
kernel_args_free(stringTable);
error2:
kernel_args_free(symbolTable);
error1:
free(sectionHeaders);
return status;
}
status_t
elf_load_image(int fd, preloaded_image *image)
{
size_t totalSize;
status_t status;
TRACE(("elf_load_image(fd = %d, image = %p)\n", fd, image));
struct Elf32_Ehdr &elfHeader = image->elf_header;
ssize_t length = read_pos(fd, 0, &elfHeader, sizeof(Elf32_Ehdr));
if (length < (ssize_t)sizeof(Elf32_Ehdr))
return B_BAD_TYPE;
status = verify_elf_header(elfHeader);
if (status < B_OK)
return status;
ssize_t size = elfHeader.e_phnum * elfHeader.e_phentsize;
Elf32_Phdr *programHeaders = (struct Elf32_Phdr *)malloc(size);
if (programHeaders == NULL) {
dprintf("error allocating space for program headers\n");
return B_NO_MEMORY;
}
length = read_pos(fd, elfHeader.e_phoff, programHeaders, size);
if (length < size) {
TRACE(("error reading in program headers\n"));
status = B_ERROR;
goto error1;
}
// create an area large enough to hold the image
image->data_region.size = 0;
image->text_region.size = 0;
for (int32 i = 0; i < elfHeader.e_phnum; i++) {
Elf32_Phdr &header = programHeaders[i];
switch (header.p_type) {
case PT_LOAD:
break;
case PT_DYNAMIC:
image->dynamic_section.start = header.p_vaddr;
image->dynamic_section.size = header.p_memsz;
continue;
default:
dprintf("unhandled pheader type 0x%lx\n", header.p_type);
continue;
}
elf_region *region;
if (header.IsReadWrite()) {
if (image->data_region.size != 0) {
dprintf("elf: rw already handled!\n");
continue;
}
region = &image->data_region;
} else if (header.IsExecutable()) {
if (image->text_region.size != 0) {
dprintf("elf: ro already handled!\n");
continue;
}
region = &image->text_region;
} else
continue;
region->start = ROUNDOWN(header.p_vaddr, B_PAGE_SIZE);
region->size = ROUNDUP(header.p_memsz + (header.p_vaddr % B_PAGE_SIZE), B_PAGE_SIZE);
region->delta = -region->start;
TRACE(("segment %d: start = %p, size = %lu, delta = %lx\n", i,
region->start, region->size, region->delta));
}
// get the segment order
elf_region *firstRegion;
elf_region *secondRegion;
if (image->text_region.start < image->data_region.start) {
firstRegion = &image->text_region;
secondRegion = &image->data_region;
} else {
firstRegion = &image->data_region;
secondRegion = &image->text_region;
}
// Check whether the segments have an unreasonable amount of unused space
// inbetween.
totalSize = secondRegion->start + secondRegion->size - firstRegion->start;
if (totalSize > image->text_region.size + image->data_region.size
+ 8 * 1024) {
status = B_BAD_DATA;
goto error1;
}
// if image->text_region.start == NULL (image is relocatable),
// platform_allocate_region() automatically allocates an address
if (platform_allocate_region((void **)&firstRegion->start, totalSize,
B_READ_AREA | B_WRITE_AREA) < B_OK) {
status = B_NO_MEMORY;
goto error1;
}
// initialize the region pointers to the allocated region
secondRegion->start += firstRegion->start + firstRegion->delta;
image->data_region.delta += image->data_region.start;
image->text_region.delta += image->text_region.start;
// load program data
for (int i = 0; i < elfHeader.e_phnum; i++) {
Elf32_Phdr &header = programHeaders[i];
if (header.p_type != PT_LOAD)
continue;
elf_region *region;
if (header.IsReadWrite())
region = &image->data_region;
else if (header.IsExecutable())
region = &image->text_region;
else
continue;
TRACE(("load segment %d (%ld bytes)...\n", i, header.p_filesz));
length = read_pos(fd, header.p_offset,
(void *)(region->start + (header.p_vaddr % B_PAGE_SIZE)), header.p_filesz);
if (length < (ssize_t)header.p_filesz) {
status = B_BAD_DATA;
dprintf("error reading in seg %d\n", i);
goto error2;
}
// clear anything above the file size (that may also contain the BSS area)
uint32 offset = (header.p_vaddr % B_PAGE_SIZE) + header.p_filesz;
if (offset < region->size)
memset((void *)(region->start + offset), 0, region->size - offset);
}
// modify the dynamic section by the delta of the regions
image->dynamic_section.start += image->text_region.delta;
image->num_debug_symbols = 0;
image->debug_symbols = NULL;
image->debug_string_table = NULL;
// ToDo: this should be enabled by kernel settings!
if (1)
load_elf_symbol_table(fd, image);
free(programHeaders);
return B_OK;
error2:
if (image->text_region.start != NULL)
platform_free_region((void *)image->text_region.start, totalSize);
error1:
free(programHeaders);
return status;
}
status_t
elf_load_image(Directory *directory, const char *path)
{
preloaded_image *image;
TRACE(("elf_load_image(directory = %p, \"%s\")\n", directory, path));
int fd = open_from(directory, path, O_RDONLY);
if (fd < 0)
return fd;
// check if this file has already been loaded
struct stat stat;
fstat(fd, &stat);
image = gKernelArgs.preloaded_images;
for (; image != NULL; image = image->next) {
if (image->inode == stat.st_ino) {
// file has already been loaded, no need to load it twice!
close(fd);
return B_OK;
}
}
// we still need to load it, so do it
image = (preloaded_image *)kernel_args_malloc(sizeof(preloaded_image));
if (image == NULL) {
close(fd);
return B_NO_MEMORY;
}
status_t status = elf_load_image(fd, image);
if (status == B_OK) {
image->name = kernel_args_strdup(path);
image->inode = stat.st_ino;
// insert to kernel args
image->next = gKernelArgs.preloaded_images;
gKernelArgs.preloaded_images = image;
} else
kernel_args_free(image);
close(fd);
return status;
}