mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
254 lines
8 KiB
C++
254 lines
8 KiB
C++
/******************************************************************************
|
|
|
|
COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011.
|
|
|
|
This file is part of Sortix.
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
You should have received a copy of the GNU General Public License along
|
|
with Sortix. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
elf.cpp
|
|
Constructs processes from ELF files.
|
|
|
|
******************************************************************************/
|
|
|
|
#include <sortix/kernel/platform.h>
|
|
#include <sortix/mman.h>
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include "elf.h"
|
|
#include <sortix/kernel/memorymanagement.h>
|
|
#include <sortix/kernel/panic.h>
|
|
#include "process.h"
|
|
|
|
namespace Sortix
|
|
{
|
|
namespace ELF
|
|
{
|
|
int ToProgramSectionType(int flags)
|
|
{
|
|
switch ( flags & (PF_X | PF_R | PF_W) )
|
|
{
|
|
case 0:
|
|
return SEG_NONE;
|
|
case PF_X:
|
|
case PF_X | PF_R:
|
|
case PF_X | PF_W:
|
|
case PF_X | PF_R | PF_W:
|
|
return SEG_TEXT;
|
|
case PF_R:
|
|
case PF_W:
|
|
case PF_R | PF_W:
|
|
default:
|
|
return SEG_DATA;
|
|
}
|
|
}
|
|
|
|
addr_t Construct32(Process* process, const void* file, size_t filelen)
|
|
{
|
|
if ( filelen < sizeof(Header32) ) { return 0; }
|
|
const Header32* header = (const Header32*) file;
|
|
|
|
// Check for little endian.
|
|
if ( header->dataencoding != DATA2LSB ) { return 0; }
|
|
if ( header->version != CURRENTVERSION ) { return 0; }
|
|
|
|
addr_t entry = header->entry;
|
|
|
|
// Find the location of the program headers.
|
|
addr_t phtbloffset = header->programheaderoffset;
|
|
if ( filelen < phtbloffset ) { return 0; }
|
|
addr_t phtblpos = ((addr_t) file) + phtbloffset;
|
|
size_t phsize = header->programheaderentrysize;
|
|
const ProgramHeader32* phtbl = (const ProgramHeader32*) phtblpos;
|
|
|
|
// Validate that all program headers are present.
|
|
uint16_t numprogheaders = header->numprogramheaderentries;
|
|
size_t neededfilelen = phtbloffset + numprogheaders * phsize;
|
|
if ( filelen < neededfilelen ) { return 0; }
|
|
|
|
// Prepare the process for execution (clean up address space, etc.)
|
|
process->ResetForExecute();
|
|
|
|
// Flush the TLB such that no stale information from the last
|
|
// address space is used when creating the new one.
|
|
Memory::Flush();
|
|
|
|
// Create all the segments in the final process.
|
|
// TODO: Handle errors on bad/malicious input or out-of-mem!
|
|
for ( uint16_t i = 0; i < numprogheaders; i++ )
|
|
{
|
|
const ProgramHeader32* pht = &(phtbl[i]);
|
|
if ( pht->type != PT_LOAD ) { continue; }
|
|
addr_t virtualaddr = pht->virtualaddr;
|
|
addr_t mapto = Page::AlignDown(virtualaddr);
|
|
addr_t mapbytes = virtualaddr - mapto + pht->memorysize;
|
|
assert(pht->offset % pht->align == virtualaddr % pht->align);
|
|
assert(pht->offset + pht->filesize < filelen);
|
|
assert(pht->filesize <= pht->memorysize);
|
|
|
|
ProcessSegment* segment = new ProcessSegment;
|
|
if ( segment == NULL ) { return 0; }
|
|
segment->position = mapto;
|
|
segment->size = Page::AlignUp(mapbytes);
|
|
segment->type = ToProgramSectionType(pht->flags);
|
|
|
|
int prot = PROT_FORK | PROT_KREAD | PROT_KWRITE;
|
|
if ( pht->flags & PF_X ) { prot |= PROT_EXEC; }
|
|
if ( pht->flags & PF_R ) { prot |= PROT_READ; }
|
|
if ( pht->flags & PF_W ) { prot |= PROT_WRITE; }
|
|
|
|
if ( segment->Intersects(process->segments) )
|
|
{
|
|
delete segment;
|
|
return 0;
|
|
}
|
|
|
|
if ( !Memory::MapRange(mapto, mapbytes, prot))
|
|
{
|
|
// TODO: Memory leak of segment?
|
|
return 0;
|
|
}
|
|
|
|
// Insert our newly allocated memory into the processes segment
|
|
// list such that it can be reclaimed later.
|
|
if ( process->segments ) { process->segments->prev = segment; }
|
|
segment->next = process->segments;
|
|
process->segments = segment;
|
|
|
|
// Copy as much data as possible and memset the rest to 0.
|
|
uint8_t* memdest = (uint8_t*) virtualaddr;
|
|
uint8_t* memsource = (uint8_t*) ( ((addr_t)file) + pht->offset);
|
|
memcpy(memdest, memsource, pht->filesize);
|
|
memset(memdest + pht->filesize, 0, pht->memorysize - pht->filesize);
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
addr_t Construct64(Process* process, const void* file, size_t filelen)
|
|
{
|
|
#ifndef PLATFORM_X64
|
|
(void) process;
|
|
(void) file;
|
|
(void) filelen;
|
|
errno = ENOEXEC;
|
|
return 0;
|
|
#else
|
|
if ( filelen < sizeof(Header64) ) { return 0; }
|
|
const Header64* header = (const Header64*) file;
|
|
|
|
// Check for little endian.
|
|
if ( header->dataencoding != DATA2LSB ) { return 0; }
|
|
if ( header->version != CURRENTVERSION ) { return 0; }
|
|
|
|
addr_t entry = header->entry;
|
|
|
|
// Find the location of the program headers.
|
|
addr_t phtbloffset = header->programheaderoffset;
|
|
if ( filelen < phtbloffset ) { return 0; }
|
|
addr_t phtblpos = ((addr_t) file) + phtbloffset;
|
|
size_t phsize = header->programheaderentrysize;
|
|
const ProgramHeader64* phtbl = (const ProgramHeader64*) phtblpos;
|
|
|
|
// Validate that all program headers are present.
|
|
uint16_t numprogheaders = header->numprogramheaderentries;
|
|
size_t neededfilelen = phtbloffset + numprogheaders * phsize;
|
|
if ( filelen < neededfilelen ) { return 0; }
|
|
|
|
// Prepare the process for execution (clean up address space, etc.)
|
|
process->ResetForExecute();
|
|
|
|
// Flush the TLB such that no stale information from the last
|
|
// address space is used when creating the new one.
|
|
Memory::Flush();
|
|
|
|
// Create all the segments in the final process.
|
|
// TODO: Handle errors on bad/malicious input or out-of-mem!
|
|
for ( uint16_t i = 0; i < numprogheaders; i++ )
|
|
{
|
|
const ProgramHeader64* pht = &(phtbl[i]);
|
|
if ( pht->type != PT_LOAD ) { continue; }
|
|
addr_t virtualaddr = pht->virtualaddr;
|
|
addr_t mapto = Page::AlignDown(virtualaddr);
|
|
addr_t mapbytes = virtualaddr - mapto + pht->memorysize;
|
|
assert(pht->offset % pht->align == virtualaddr % pht->align);
|
|
assert(pht->offset + pht->filesize < filelen);
|
|
assert(pht->filesize <= pht->memorysize);
|
|
|
|
ProcessSegment* segment = new ProcessSegment;
|
|
if ( segment == NULL ) { return 0; }
|
|
segment->position = mapto;
|
|
segment->size = Page::AlignUp(mapbytes);
|
|
segment->type = ToProgramSectionType(pht->flags);
|
|
|
|
int prot = PROT_FORK | PROT_KREAD | PROT_KWRITE;
|
|
if ( pht->flags & PF_X ) { prot |= PROT_EXEC; }
|
|
if ( pht->flags & PF_R ) { prot |= PROT_READ; }
|
|
if ( pht->flags & PF_W ) { prot |= PROT_WRITE; }
|
|
|
|
if ( segment->Intersects(process->segments) )
|
|
{
|
|
delete segment;
|
|
return 0;
|
|
}
|
|
|
|
if ( !Memory::MapRange(mapto, mapbytes, prot))
|
|
{
|
|
// TODO: Memory leak of segment?
|
|
return 0;
|
|
}
|
|
|
|
// Insert our newly allocated memory into the processes segment
|
|
// list such that it can be reclaimed later.
|
|
if ( process->segments ) { process->segments->prev = segment; }
|
|
segment->next = process->segments;
|
|
process->segments = segment;
|
|
|
|
// Copy as much data as possible and memset the rest to 0.
|
|
uint8_t* memdest = (uint8_t*) virtualaddr;
|
|
uint8_t* memsource = (uint8_t*) ( ((addr_t)file) + pht->offset);
|
|
memcpy(memdest, memsource, pht->filesize);
|
|
memset(memdest + pht->filesize, 0, pht->memorysize - pht->filesize);
|
|
}
|
|
|
|
return entry;
|
|
#endif
|
|
}
|
|
|
|
addr_t Construct(Process* process, const void* file, size_t filelen)
|
|
{
|
|
if ( filelen < sizeof(Header) ) { errno = ENOEXEC; return 0; }
|
|
const Header* header = (const Header*) file;
|
|
|
|
if ( !(header->magic[0] == 0x7F && header->magic[1] == 'E' &&
|
|
header->magic[2] == 'L' && header->magic[3] == 'F' ) )
|
|
{
|
|
errno = ENOEXEC;
|
|
return 0;
|
|
}
|
|
|
|
switch ( header->fileclass )
|
|
{
|
|
case CLASS32:
|
|
return Construct32(process, file, filelen);
|
|
case CLASS64:
|
|
return Construct64(process, file, filelen);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|