2013-07-10 09:26:01 -04:00
|
|
|
/*******************************************************************************
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2014-02-17 14:56:19 -05:00
|
|
|
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013, 2014.
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2013-07-10 09:26:01 -04:00
|
|
|
This file is part of Sortix.
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2013-07-10 09:26:01 -04:00
|
|
|
Sortix is free software: you can redistribute it and/or modify it under the
|
|
|
|
terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation, either version 3 of the License, or (at your option) any later
|
|
|
|
version.
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2013-07-10 09:26:01 -04:00
|
|
|
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
|
|
details.
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2013-07-10 09:26:01 -04:00
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
Sortix. If not, see <http://www.gnu.org/licenses/>.
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2013-07-10 09:26:01 -04:00
|
|
|
elf.cpp
|
2014-06-25 19:05:07 -04:00
|
|
|
Load a program in the Executable and Linkable Format into this process.
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2013-07-10 09:26:01 -04:00
|
|
|
*******************************************************************************/
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2013-05-20 15:09:18 -04:00
|
|
|
#include <sys/types.h>
|
|
|
|
|
2012-09-22 08:57:20 -04:00
|
|
|
#include <assert.h>
|
2014-06-25 19:05:07 -04:00
|
|
|
#include <elf.h>
|
|
|
|
#include <endian.h>
|
2012-09-22 10:44:50 -04:00
|
|
|
#include <errno.h>
|
2013-05-20 15:09:18 -04:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdint.h>
|
2013-08-19 20:23:53 -04:00
|
|
|
#include <stdlib.h>
|
2012-09-22 14:38:34 -04:00
|
|
|
#include <string.h>
|
2014-06-25 19:05:07 -04:00
|
|
|
#include <system-elf.h>
|
|
|
|
|
|
|
|
#include <__/wordsize.h>
|
2013-05-20 15:09:18 -04:00
|
|
|
|
|
|
|
#include <sortix/mman.h>
|
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
#include <sortix/kernel/elf.h>
|
2013-10-26 20:42:10 -04:00
|
|
|
#include <sortix/kernel/kernel.h>
|
2012-03-21 19:52:29 -04:00
|
|
|
#include <sortix/kernel/memorymanagement.h>
|
2013-05-20 15:09:18 -04:00
|
|
|
#include <sortix/kernel/process.h>
|
2013-08-19 20:23:53 -04:00
|
|
|
#include <sortix/kernel/segment.h>
|
2013-05-20 15:09:18 -04:00
|
|
|
|
|
|
|
namespace Sortix {
|
|
|
|
namespace ELF {
|
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
static bool is_power_of_two(uintptr_t value)
|
|
|
|
{
|
|
|
|
for ( uintptr_t i = 0; i < sizeof(uintptr_t) * 8; i++ )
|
|
|
|
if ( (uintptr_t) 1 << i == value )
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
uintptr_t Load(const void* file_ptr, size_t file_size, Auxiliary* aux)
|
2013-05-20 15:09:18 -04:00
|
|
|
{
|
2014-06-25 19:05:07 -04:00
|
|
|
memset(aux, 0, sizeof(*aux));
|
2013-05-20 15:09:18 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
Process* process = CurrentProcess();
|
2013-05-20 15:09:18 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
uintptr_t userspace_addr;
|
|
|
|
size_t userspace_size;
|
|
|
|
Memory::GetUserVirtualArea(&userspace_addr, &userspace_size);
|
|
|
|
uintptr_t userspace_end = userspace_addr + userspace_size;
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
const unsigned char* file = (const unsigned char*) file_ptr;
|
2013-08-19 20:23:53 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( file_size < EI_NIDENT )
|
|
|
|
return errno = ENOEXEC, 0;
|
2013-05-20 15:09:18 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( memcmp(file, ELFMAG, SELFMAG) != 0 )
|
|
|
|
return errno = ENOEXEC, 0;
|
2013-08-19 20:23:53 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
#if __WORDSIZE == 32
|
|
|
|
if ( file[EI_CLASS] != ELFCLASS32 )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
#elif __WORDSIZE == 64
|
|
|
|
if ( file[EI_CLASS] != ELFCLASS64 )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
#else
|
|
|
|
#error "You need to add support for your elf class."
|
|
|
|
#endif
|
2013-05-20 15:09:18 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
|
|
if ( file[EI_DATA] != ELFDATA2LSB )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
#elif BYTE_ORDER == BIG_ENDIAN
|
|
|
|
if ( file[EI_DATA] != ELFDATA2MSB )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
#else
|
|
|
|
#error "You need to add support for your endian."
|
|
|
|
#endif
|
2013-05-20 15:09:18 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( file[EI_VERSION] != EV_CURRENT )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( file[EI_OSABI] != ELFOSABI_SORTIX )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( file[EI_ABIVERSION] != 0 )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( file_size < sizeof(Elf_Ehdr) )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
if ( (uintptr_t) file & (alignof(Elf_Ehdr) - 1) )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
const Elf_Ehdr* header = (const Elf_Ehdr*) file;
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( header->e_ehsize < sizeof(Elf_Ehdr) )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( file_size < header->e_ehsize )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
#if defined(__i386__)
|
|
|
|
if ( header->e_machine != EM_386 )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
#elif defined(__x86_64__)
|
|
|
|
if ( header->e_machine != EM_X86_64 )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
#else
|
|
|
|
#error "Please recognize your processor in e_machine."
|
|
|
|
#endif
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( header->e_type != ET_EXEC )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( header->e_entry == 0 )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( file_size < header->e_phoff )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-22 16:11:29 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( file_size < header->e_shoff )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
|
|
|
|
if ( header->e_phentsize < sizeof(Elf_Phdr) )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
|
|
|
|
if ( header->e_shentsize < sizeof(Elf_Shdr) )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-20 15:09:18 -04:00
|
|
|
|
|
|
|
process->ResetForExecute();
|
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( header->e_phnum == (Elf_Half) -1 )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
if ( header->e_shnum == (Elf_Half) -1 )
|
|
|
|
return errno = EINVAL, 0;
|
2013-05-20 15:09:18 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
for ( Elf32_Half i = 0; i < header->e_phnum; i++ )
|
2013-05-20 15:09:18 -04:00
|
|
|
{
|
2014-06-25 19:05:07 -04:00
|
|
|
size_t max_phs = (file_size - header->e_phoff) / header->e_phentsize;
|
|
|
|
if ( max_phs <= i )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
size_t pheader_offset = header->e_phoff + i * header->e_phentsize;
|
|
|
|
if ( (uintptr_t) (file + pheader_offset) & (alignof(Elf_Phdr) - 1) )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
Elf_Phdr* pheader = (Elf_Phdr*) (file + pheader_offset);
|
|
|
|
|
|
|
|
switch ( pheader->p_type )
|
2014-02-17 14:56:19 -05:00
|
|
|
{
|
2014-06-25 19:05:07 -04:00
|
|
|
case PT_TLS: break;
|
|
|
|
case PT_NOTE: break;
|
|
|
|
case PT_LOAD: break;
|
|
|
|
default: continue;
|
|
|
|
};
|
|
|
|
|
|
|
|
if ( !is_power_of_two(pheader->p_align) )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
if ( file_size < pheader->p_offset )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
if ( file_size - pheader->p_offset < pheader->p_filesz )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
|
|
|
|
if ( pheader->p_type == PT_TLS )
|
|
|
|
{
|
|
|
|
if ( pheader->p_memsz < pheader->p_filesz )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
|
|
|
|
aux->tls_file_offset = pheader->p_offset;
|
|
|
|
aux->tls_file_size = pheader->p_filesz;
|
|
|
|
aux->tls_mem_size = pheader->p_memsz;
|
|
|
|
aux->tls_mem_align = pheader->p_align;
|
2014-02-17 14:56:19 -05:00
|
|
|
continue;
|
|
|
|
}
|
2014-06-25 19:05:07 -04:00
|
|
|
|
|
|
|
if ( pheader->p_type == PT_NOTE )
|
2014-02-17 14:56:19 -05:00
|
|
|
{
|
|
|
|
size_t notes_offset = 0;
|
2014-06-25 19:05:07 -04:00
|
|
|
while ( notes_offset < pheader->p_filesz )
|
2014-02-17 14:56:19 -05:00
|
|
|
{
|
2014-06-25 19:05:07 -04:00
|
|
|
size_t available = pheader->p_filesz - notes_offset;
|
|
|
|
size_t note_header_size = 3 * sizeof(uint32_t);
|
|
|
|
if ( available < note_header_size )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
available -= note_header_size;
|
|
|
|
size_t file_offset = pheader->p_offset + notes_offset;
|
|
|
|
if ( ((uintptr_t) file + file_offset) & (alignof(uint32_t) - 1) )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
|
|
|
|
const unsigned char* note = file + file_offset;
|
|
|
|
uint32_t* note_header = (uint32_t*) note;
|
|
|
|
uint32_t namesz = note_header[0];
|
|
|
|
uint32_t descsz = note_header[1];
|
|
|
|
uint32_t type = note_header[2];
|
|
|
|
uint32_t namesz_aligned = -(-namesz & ~(sizeof(uint32_t) - 1));
|
|
|
|
uint32_t descsz_aligned = -(-descsz & ~(sizeof(uint32_t) - 1));
|
|
|
|
if ( available < namesz_aligned )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
available -= namesz_aligned;
|
|
|
|
if ( available < descsz_aligned )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
available -= descsz_aligned;
|
2016-01-07 18:48:01 -05:00
|
|
|
(void) available;
|
2014-06-25 19:05:07 -04:00
|
|
|
notes_offset += note_header_size + namesz_aligned + descsz_aligned;
|
|
|
|
|
|
|
|
const char* name = (const char*) (note + note_header_size);
|
|
|
|
if ( strnlen(name, namesz_aligned) == namesz_aligned )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
const unsigned char* desc = note + note_header_size + namesz_aligned;
|
|
|
|
const uint32_t* desc_32bits = (const uint32_t*) desc;
|
|
|
|
|
|
|
|
if ( strcmp(name, ELF_NOTE_SORTIX) == 0 )
|
2014-02-17 14:56:19 -05:00
|
|
|
{
|
2014-02-17 17:53:03 -05:00
|
|
|
if ( type == ELF_NOTE_SORTIX_UTHREAD_SIZE )
|
|
|
|
{
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( descsz_aligned != 2 * sizeof(size_t) )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
#if __WORDSIZE == 32
|
|
|
|
aux->uthread_size = desc_32bits[0];
|
|
|
|
aux->uthread_align = desc_32bits[1];
|
|
|
|
#elif __WORDSIZE == 64 && BYTE_ORDER == LITTLE_ENDIAN
|
|
|
|
aux->uthread_size = (uint64_t) desc_32bits[0] << 0 |
|
|
|
|
(uint64_t) desc_32bits[1] << 32;
|
|
|
|
aux->uthread_align = (uint64_t) desc_32bits[2] << 0 |
|
|
|
|
(uint64_t) desc_32bits[3] << 32;
|
|
|
|
#elif __WORDSIZE == 64 && BYTE_ORDER == BIG_ENDIAN
|
|
|
|
aux->uthread_size = (uint64_t) desc_32bits[1] << 0 |
|
|
|
|
(uint64_t) desc_32bits[0] << 32;
|
|
|
|
aux->uthread_align = (uint64_t) desc_32bits[3] << 0 |
|
|
|
|
(uint64_t) desc_32bits[2] << 32;
|
|
|
|
#else
|
|
|
|
#error "You need to correctly read the uthread note"
|
|
|
|
#endif
|
|
|
|
if ( !is_power_of_two(aux->uthread_align) )
|
|
|
|
return errno = EINVAL, 0;
|
2014-02-17 17:53:03 -05:00
|
|
|
}
|
2014-02-17 14:56:19 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2014-06-25 19:05:07 -04:00
|
|
|
|
|
|
|
if ( pheader->p_type == PT_LOAD )
|
2013-08-19 20:23:53 -04:00
|
|
|
{
|
2015-05-31 06:03:07 -04:00
|
|
|
if ( pheader->p_memsz < pheader->p_filesz )
|
|
|
|
return errno = EINVAL, 0;
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( pheader->p_filesz &&
|
|
|
|
pheader->p_vaddr % pheader->p_align !=
|
|
|
|
pheader->p_offset % pheader->p_align )
|
|
|
|
return errno = EINVAL, 0;
|
2015-08-27 15:39:35 -04:00
|
|
|
int kprot = PROT_KWRITE | PROT_FORK;
|
|
|
|
int prot = PROT_FORK;
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( pheader->p_flags & PF_X )
|
|
|
|
prot |= PROT_EXEC;
|
|
|
|
if ( pheader->p_flags & PF_R )
|
2015-08-27 15:39:35 -04:00
|
|
|
prot |= PROT_READ | PROT_KREAD;
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( pheader->p_flags & PF_W )
|
2015-08-27 15:39:35 -04:00
|
|
|
prot |= PROT_WRITE | PROT_KWRITE;
|
2014-06-25 19:05:07 -04:00
|
|
|
|
|
|
|
if ( pheader->p_vaddr < userspace_addr )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
if ( userspace_end < pheader->p_vaddr )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
if ( userspace_end - pheader->p_vaddr < pheader->p_memsz )
|
|
|
|
return errno = EINVAL, 0;
|
|
|
|
|
|
|
|
uintptr_t map_start = Page::AlignDown(pheader->p_vaddr);
|
|
|
|
uintptr_t map_end = Page::AlignUp(pheader->p_vaddr + pheader->p_memsz);
|
|
|
|
size_t map_size = map_end - map_start;
|
|
|
|
|
|
|
|
struct segment segment;
|
|
|
|
segment.addr = map_start;
|
|
|
|
segment.size = map_size;
|
2015-08-27 15:39:35 -04:00
|
|
|
segment.prot = kprot;
|
2014-06-25 19:05:07 -04:00
|
|
|
|
|
|
|
assert(IsUserspaceSegment(&segment));
|
|
|
|
|
2015-05-14 09:19:23 -04:00
|
|
|
kthread_mutex_lock(&process->segment_write_lock);
|
2014-06-25 19:05:07 -04:00
|
|
|
kthread_mutex_lock(&process->segment_lock);
|
|
|
|
|
|
|
|
if ( IsSegmentOverlapping(process, &segment) )
|
|
|
|
{
|
|
|
|
kthread_mutex_unlock(&process->segment_lock);
|
2015-05-14 09:19:23 -04:00
|
|
|
kthread_mutex_unlock(&process->segment_write_lock);
|
2014-06-25 19:05:07 -04:00
|
|
|
return errno = EINVAL, 0;
|
|
|
|
}
|
2013-08-19 20:23:53 -04:00
|
|
|
|
2015-08-27 15:39:35 -04:00
|
|
|
if ( !Memory::MapRange(segment.addr, segment.size, kprot, PAGE_USAGE_USER_SPACE) )
|
2014-06-25 19:05:07 -04:00
|
|
|
{
|
|
|
|
kthread_mutex_unlock(&process->segment_lock);
|
2015-05-14 09:19:23 -04:00
|
|
|
kthread_mutex_unlock(&process->segment_write_lock);
|
2014-06-25 19:05:07 -04:00
|
|
|
return errno = EINVAL, 0;
|
|
|
|
}
|
2013-08-19 20:23:53 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
if ( !AddSegment(process, &segment) )
|
|
|
|
{
|
|
|
|
Memory::UnmapRange(segment.addr, segment.size, PAGE_USAGE_USER_SPACE);
|
|
|
|
kthread_mutex_unlock(&process->segment_lock);
|
2015-05-14 09:19:23 -04:00
|
|
|
kthread_mutex_unlock(&process->segment_write_lock);
|
2014-06-25 19:05:07 -04:00
|
|
|
return errno = EINVAL, 0;
|
|
|
|
}
|
2011-08-22 15:54:18 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
memset((void*) segment.addr, 0, segment.size);
|
|
|
|
memcpy((void*) pheader->p_vaddr, file + pheader->p_offset, pheader->p_filesz);
|
2015-08-27 15:39:35 -04:00
|
|
|
Memory::ProtectMemory(CurrentProcess(), segment.addr, segment.size, prot);
|
|
|
|
|
|
|
|
kthread_mutex_unlock(&process->segment_lock);
|
|
|
|
kthread_mutex_unlock(&process->segment_write_lock);
|
2014-06-25 19:05:07 -04:00
|
|
|
}
|
2011-08-22 15:54:18 -04:00
|
|
|
}
|
2013-05-20 15:09:18 -04:00
|
|
|
|
2014-06-25 19:05:07 -04:00
|
|
|
return header->e_entry;
|
2013-05-20 15:09:18 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace ELF
|
|
|
|
} // namespace Sortix
|