NutsOS/kernel/src/elf_loader.c
2020-04-17 17:44:23 +02:00

223 lines
6.0 KiB
C

/* elf_loader.c - This file is a part of NutsOS
*
* NutsOS
* Copyright (C) 2013 Free Software Foundation, Inc.
*
* NutsOS 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.
*
* NutsOS 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 NutsOS; If not, see <http://www.gnu.org/licenses/>.
*
* Valentin Verdier <valentin.verdier03@gmail.com>
*/
#define ELF_LOADER_PRIVATE
#include "elf_loader.h"
#include "types.h"
#include "print.h"
#include "utils.h"
#include "memory.h"
#include "process.h"
/* Index dans id[] */
#define ID_MAG_0 0
#define ID_MAG_1 1
#define ID_MAG_2 2
#define ID_MAG_3 3
#define ID_CLASS 4
#define ID_DATA 5
#define ID_VERSION 6
#define ID_PAD 7
/* Classes ELF */
#define ELF_CLASS_NONE 0
#define ELF_CLASS_32 1
#define ELF_CLASS_64 2
/* Encodage des données */
#define ELF_DATA_NONE 0
#define ELF_DATA_LSB 1
#define ELF_DATA_MSB 2
/* Types de fichier ELF */
#define ELF_TYPE_NONE 0
#define ELF_TYPE_RELOC 1
#define ELF_TYPE_EXEC 2
#define ELF_TYPE_DYN 3
#define ELF_TYPE_CORE 4
/* Version */
#define ELF_VERSION_NONE 0
#define ELF_VERSION_CURRENT 1
/* Architectures cible */
#define ELF_ARCH_NONE 0
#define ELF_ARCH_M32 1
#define ELF_ARCH_SPARC 2
#define ELF_ARCH_386 3
#define ELF_ARCH_68K 4
#define ELF_ARCH_88K 5
#define ELF_ARCH_860 7
#define ELF_ARCH_MIPS 8
#define ELF_ARCH_MIPS_RS4_BE 10
/* Structure de l'entête des fichier ELF32 */
typedef struct {
T_byte id[16];
T_word type;
T_word targetArch;
T_dword version;
T_dword entry;
T_dword prgHdrTblOff;
T_dword secHdrTblOff; /* Pas utilisé ici */
T_dword flags; /* Pas utilisé ici */
T_word hdrSize;
T_word prgHdrTblEntrySize;
T_word prgHdrTblEntryCount;
T_word secHdrTblEntrySize; /* Pas utilisé ici */
T_word secHdrTblEntryCount; /* Pas utilisé ici */
T_word strTblIndex; /* Pas utilisé ici */
}__attribute__((packed)) T_ELF32_hdr;
/* Types de segment */
#define ELF_SEG_NULL 0
#define ELF_SEG_LOAD 1
#define ELF_SEG_DYNAMIC 2
#define ELF_SEG_INTERP 3
#define ELF_SEG_NOTE 4
#define ELF_SEG_SHLIB 5
#define ELF_SEG_PHDR 6
/* Permissions R|W|X */
#define ELF_SEG_PERM_READ 4
#define ELF_SEG_PERM_WRITE 2
#define ELF_SEG_PERM_EXEC 1
/* Structure d'une entrée du Program header */
typedef struct {
T_dword type;
T_dword offset;
T_dword vaddr;
T_dword paddr;
T_dword sizeInFile;
T_dword sizeInMem;
T_dword flags;
T_dword align;
}__attribute__((packed)) T_ELF32_prgHdrEntry;
int ELF32_checkFile(void *file)
{
T_ELF32_hdr* elfHdr = file;
if(elfHdr->id[ID_MAG_0] != 0x7F || elfHdr->id[ID_MAG_1] != 'E' || elfHdr->id[ID_MAG_2] != 'L' || elfHdr->id[ID_MAG_3] != 'F') {
print("[ELF Loader] : The file at 0x%x is not ELF type\n", DEFAULT_COLOR, file);
return 0;
}
if(elfHdr->id[ID_CLASS] != ELF_CLASS_32) {
print("[ELF Loader] : The file at 0x%x is not a 32 bits ELF\n", DEFAULT_COLOR, file);
return 0;
}
if(elfHdr->id[ID_DATA] != ELF_DATA_LSB) {
print("[ELF Loader] : The ELF file at 0x%x has a bad encoding\n", DEFAULT_COLOR, file);
return 0;
}
if(elfHdr->type == ELF_TYPE_EXEC) {
print("[ELF Loader] : The ELF file at 0x%x is not executable\n", DEFAULT_COLOR, file);
return 0;
}
if(elfHdr->targetArch == ELF_ARCH_386) {
print("[ELF_Loader] : The ELF file at 0x%x is not a x86 executable\n", DEFAULT_COLOR, file);
return 0;
}
if(elfHdr->entry >= KERNEL_SPACE_SIZE) {
return 1;
} else {
print("[ELF_Loader] : The ELF file at 0x%x has a bad entry point\n", DEFAULT_COLOR, file);
return 0;
}
}
unsigned int ELF32_Load(void *file, T_LinkedList* regionsList)
{
if(ELF32_checkFile(file)) {
T_ELF32_hdr* elfHdr = file;
unsigned int i, sc;
for(i = 0, sc = 0; i < elfHdr->prgHdrTblEntryCount; i++, sc++) {
T_ELF32_prgHdrEntry* prgHdrEntry = file + elfHdr->prgHdrTblOff + i * elfHdr->prgHdrTblEntrySize;
if(prgHdrEntry->type == ELF_SEG_LOAD) {
if(!prgHdrEntry->sizeInMem) {
goto error;
}
T_Page segStartPage = prgHdrEntry->vaddr & 0xFFFFF000;
unsigned int pageCount = prgHdrEntry->sizeInMem & 0xFFFFF000;
if(prgHdrEntry->sizeInMem & 0x00000FFF) {
pageCount++;
}
if((prgHdrEntry->vaddr & 0x00000FFF) >= PAGE_SIZE - (prgHdrEntry->sizeInMem & 0x00000FFF)) {
pageCount++;
}
if(prgHdrEntry->flags == (ELF_SEG_PERM_READ | ELF_SEG_PERM_EXEC)) {
T_Page* pglist = malloc(sizeof(T_Page) * pageCount);
if(!pglist) {
goto error;
}
unsigned int j;
for(j = 0; j < pageCount; j++) {
pglist[j] = ((T_Page) file + prgHdrEntry->offset & 0xFFFFF000) + (j << PAGE_OFFSET_BITS);
}
if(!ProcessCreateRegion_RO(segStartPage, pglist, pageCount, regionsList)) {
free(pglist);
goto error;
}
free(pglist);
} else if(prgHdrEntry->flags == (ELF_SEG_PERM_READ | ELF_SEG_PERM_WRITE)) {
if(!ProcessCreateRegion_RW(segStartPage, pageCount, regionsList)) {
goto error;
}
unsigned int j;
for(j = 0; j < pageCount; j++) {
memcopy((char*) (((unsigned int) file + prgHdrEntry->offset & 0xFFFFF000) + (j << PAGE_OFFSET_BITS)),(char*) (segStartPage + (j << PAGE_OFFSET_BITS)), PAGE_SIZE);
}
}
}
}
return elfHdr->entry;
error:
for(i = 0; i < sc; i++) {
T_ELF32_prgHdrEntry* prgHdrEntry = file + elfHdr->prgHdrTblOff + i * elfHdr->prgHdrTblEntrySize;
if(prgHdrEntry->type == ELF_SEG_LOAD) {
T_Page segStartPage = prgHdrEntry->vaddr & 0xFFFFF000;
unsigned int pageCount;
if(prgHdrEntry->sizeInMem & 0x00000FFF) {
pageCount = (prgHdrEntry->sizeInMem & 0xFFFFF000) + 1;
}
if(prgHdrEntry->align > 0) {
pageCount++;
}
ProcessRemoveRegion(segStartPage, pageCount, regionsList);
}
}
return 0;
} else {
return 0;
}
}