BarryServer : Git

All the code for all my projects
// BarryServer : Git / Nucleus / blob / master / task / exec.c

// Related

Nucleus

Barry Kernel threads + threads share address space 6217f0d (3 years, 1 month ago)
/*
 * This file deals with loading programs into memory and executing them.  It
 * parses ELF files for details and loads them into memory, sets up the stack
 * and finally jumps into user-mode.
 */

#include <sys/errno.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <nucleus/lib.h>
#include <nucleus/memory.h>
#include <nucleus/task.h>
#include <nucleus/vfs.h>

/* ELF Types */
enum ELFType {
	ELF_NONE,
	ELF_RELOCATABLE,
	ELF_EXECUTABLE,
	ELF_SHARED,
	ELF_CORE,
};

/* ELF Program Header Types */
enum ProgramType {
	PH_NONE,
	PH_LOAD,
	PH_DYNAMIC,
	PH_INTERPRET,
	PH_NOTE,
	PH_TLS = 7,
};

/* ELF Program Header Flags */
enum ProgramFlag {
	PH_EXECUTABLE = 1,
	PH_WRITABLE = 2,
	PH_READABLE = 4,
};

/* ELF Header */
struct ELFHeader {
	char magic[4];
	uint8_t arch;
	uint8_t endian;
	uint8_t headerVersion;
	uint8_t osAbi;
	uint8_t reserved[8];
	uint16_t type;
	uint16_t isa;
	uint32_t version;
	uint32_t entry;
	uint32_t programHeader;
	uint32_t sectionHeader;
	uint32_t flags;
	uint16_t headerSize;
	uint16_t programEntrySize;
	uint16_t numProgramEntries;
	uint16_t sectionEntrySize;
	uint16_t numSectionEntries;
	uint16_t sectionNames;
};

/* ELF Program Header */
struct ProgramHeader {
	uint32_t type;
	uint32_t offset;
	uint32_t address;
	uint32_t reserved;
	uint32_t filesz;
	uint32_t memsz;
	uint32_t flags;
	uint32_t align;
};

/* Execute a program */
int
execve(const char *file, char *argv[], char *envp[])
{
	if (!file || !verify_access(file, strnlen(file, PATH_MAX), PROT_READ))
		return -EFAULT;

	/* Count argv and envp */
	int argc, argi;
	if (argv == NULL) argc = 0;
	else for (argc = 0; argv[argc]; argc++);
	int envc, envi;
	if (envp == NULL) envc = 0;
	else for (envc = 0; envp[envc]; envc++);

	/* Find size of argv and envp strings */
	size_t ssz = sizeof(int)
	           + (sizeof(uintptr_t) * (argc + 1))
	           + (sizeof(uintptr_t) * (envc + 1));
	for (argi = 0; argi < argc; argi++) {
		if (!verify_access(argv[argi], strlen(argv[argi]), PROT_READ))
			return -EFAULT;
		ssz += strlen(argv[argi]) + 1;
	}
	for (envi = 0; envi < envc; envi++) {
		if (!verify_access(envp[envi], strlen(envp[envi]), PROT_READ))
			return -EFAULT;
		ssz += strlen(envp[envi]) + 1;
	}

	/* Open file */
	int fd = open(file, O_RDONLY);
	if (fd < 0)
		return fd;

	/* Only execute regular files */
	File *executable = get_file_by_fd(fd);
	if (!S_ISREG(executable->inode->mode)) {
		close(fd);
		return -EACCES;
	}

	/* Read ELF header */
	struct ELFHeader header;
	file_read(executable, (char *) &header, sizeof(struct ELFHeader));
	if (memcmp(header.magic, "\x7F""ELF", 4) || header.isa != 3) {
		close(fd);
		return -ENOEXEC;
	}
	if (header.type != ELF_EXECUTABLE) {
		close(fd);
		return -ENOEXEC;
	}

	/*
	 * POINT OF NO RETURN
	 */

	exit_syscall_context();

	/* Store everything (pointers adjusted) in temporary buffer */
	uintptr_t esp = 0xE0000000 - ssz;
	char *istack = kmalloc(ssz);
	char *isp = istack + ssz;
	for (envi = envc - 1; envi >= 0 && envi < envc; envi--) {
		isp -= strlen(envp[envi]) + 1;
		memcpy(isp, envp[envi], strlen(envp[envi]) + 1);
		envp[envi] = (char *) (isp - istack) + esp;
	}
	if (envp)
		envp[envc] = NULL;
	for (argi = argc - 1; argi >= 0 && argi < argc; argi--) {
		isp -= strlen(argv[argi]) + 1;
		memcpy(isp, argv[argi], strlen(argv[argi]) + 1);
		argv[argi] = (char *) (isp - istack) + esp;
	}
	if (argv)
		argv[argc] = NULL;
	isp -= sizeof(uintptr_t);
	*((uintptr_t *) isp) = (uintptr_t) NULL;
	isp -= sizeof(uintptr_t) * envc;
	memcpy(isp, envp, sizeof(uintptr_t) * envc);
	isp -= sizeof(uintptr_t);
	*((uintptr_t *) isp) = (uintptr_t) NULL;
	isp -= sizeof(uintptr_t) * argc;
	memcpy(isp, argv, sizeof(uintptr_t) * argc);
	isp -= sizeof(int);
	*((int *) isp) = argc;

	/* Destroy previous executable */
	VMRegion *region;
	put(current->vm);
	current->vm = new(&virtualMemoryType);

	/* Program headers */
	size_t p;
	off_t off;
	uintptr_t pgbrk, heapEnd;
	struct ProgramHeader ph, tlsph;
	memset(&tlsph, 0, sizeof(struct ProgramHeader));
	for (p = 0; p < header.numProgramEntries; p++) {
		off = header.programHeader + (p * header.programEntrySize);
		executable->pos = off;
		file_read(executable, (char *) &ph,
		          sizeof(struct ProgramHeader));
		if (ph.type != PH_LOAD) {
//			if (ph.type == PH_TLS)
			continue;
		}

		/* Map data into region */
		mmap((void *) ph.address, ph.filesz, ph.flags, MAP_PRIVATE, fd,
		     PAGE_ADDR(ph.offset));
		/* Space left before */
		if (ph.address % PAGE_SIZE) {
			memset((void *) PAGE_ADDR(ph.address), 0,
			       ph.address - PAGE_ADDR(ph.address));
		}
		/* Unset memory */
		if (ph.memsz > ph.filesz) {
			pgbrk = PAGE_ADDR(ph.address + ph.filesz);
			if ((ph.address + ph.filesz) % PAGE_SIZE)
				pgbrk += PAGE_SIZE;
			memset((void *) ph.address + ph.filesz, 0,
			       pgbrk - ph.address - ph.filesz);
			if (ph.memsz > pgbrk - ph.address) {
				mmap((void *) pgbrk,
				     ph.memsz - (pgbrk - ph.address),
				     ph.flags, MAP_PRIVATE | MAP_ANONYMOUS,
				     -1, 0);
			}
		}
		if (ph.address + ph.memsz > heapEnd)
			heapEnd = ph.address + ph.memsz;
	}

	/* Store executable */
	if (current->executable)
		put(current->executable);
	current->executable = get(executable);
	close(fd);

	/* Stack area */
	memcpy((void *) esp, istack, ssz);
	kfree(istack);

	/* Switch to user-mode */
	asm volatile(
		"cli;"
		"mov $0x23, %%ax;"
		"mov %%ax, %%ds;"
		"mov %%ax, %%es;"
		"mov %%ax, %%fs;"
		"mov %%ax, %%gs;"
		"mov %%esi, %%eax;"
		"pushl $0x23;"
		"pushl %%eax;"
		"pushf;"
		"pop %%eax;"
		"or $0x200, %%eax;"
		"push %%eax;"
		"pushl $0x1B;"
		"pushl %%ebx;"
		"iret;"
		: : "b" (header.entry), "S" (esp)
	);
	__builtin_unreachable();
}