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
356 lines
8.4 KiB
C++
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 = §ionHeaders[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;
|
|
}
|
|
|