summaryrefslogtreecommitdiff
path: root/sys/kern/exec_elf64.c
diff options
context:
space:
mode:
authorIan Moffett <ian@osmora.org>2024-06-28 23:25:32 -0400
committerIan Moffett <ian@osmora.org>2024-06-28 23:25:32 -0400
commit4302cf39db615ece37bb00c6d5245200165b838e (patch)
treeb5e0a545491c07cc2a70d62a77b2c3e5b228ea21 /sys/kern/exec_elf64.c
parent9de701838239a9d5ff033799978427ce1822c5a5 (diff)
kernel: elf: Add ELF64 loader
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'sys/kern/exec_elf64.c')
-rw-r--r--sys/kern/exec_elf64.c240
1 files changed, 240 insertions, 0 deletions
diff --git a/sys/kern/exec_elf64.c b/sys/kern/exec_elf64.c
new file mode 100644
index 0000000..568c7c8
--- /dev/null
+++ b/sys/kern/exec_elf64.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Hyra nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/elf.h>
+#include <sys/exec.h>
+#include <sys/param.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <vm/pmap.h>
+#include <vm/physmem.h>
+#include <vm/dynalloc.h>
+#include <vm/vm.h>
+#include <vm/map.h>
+#include <string.h>
+#include <machine/pcb.h>
+
+#define PHDR(HDRP, IDX) \
+ (void *)((uintptr_t)HDRP + (HDRP)->e_phoff + (HDRP->e_phentsize * IDX))
+
+struct elf_file {
+ char *data;
+ size_t size;
+};
+
+/*
+ * Load the file and give back an "elf_file"
+ * structure.
+ */
+static int
+elf_get_file(const char *pathname, struct elf_file *res)
+{
+ struct vnode *vp = NULL;
+ struct nameidata nd;
+ struct vattr vattr;
+ struct vop_getattr_args getattr_args;
+ struct sio_txn read_txn;
+ int status = 0;
+
+ nd.path = pathname;
+ nd.flags = 0;
+
+ if (res == NULL)
+ return -EINVAL;
+ if ((status = namei(&nd)) != 0)
+ return status;
+
+ vp = nd.vp;
+
+ getattr_args.res = &vattr;
+ getattr_args.vp = vp;
+ status = vfs_vop_getattr(nd.vp, &getattr_args);
+ if (status != 0)
+ goto done;
+
+ /* Can we use the size field? */
+ if (vattr.size == VNOVAL) {
+ status = -EIO;
+ goto done;
+ }
+
+ res->size = vattr.size;
+ res->data = dynalloc(sizeof(char) * res->size);
+ if (res->data == NULL) {
+ status = -ENOMEM;
+ goto done;
+ }
+
+ /* Read data into our buffer */
+ read_txn.buf = res->data;
+ read_txn.len = res->size;
+ read_txn.offset = 0;
+ vfs_vop_read(vp, &read_txn);
+
+done:
+ if (vp != NULL) {
+ vfs_release_vnode(nd.vp);
+ }
+ return status;
+}
+
+/*
+ * Verify the validity of the ELF header.
+ * Returns 0 on success.
+ */
+static int
+elf64_verify(Elf64_Ehdr *hdr)
+{
+ const char *mag = &hdr->e_ident[EI_MAG0];
+
+ if (memcmp(mag, ELFMAG, SELFMAG) != 0) {
+ /* Bad magic */
+ return -ENOEXEC;
+ }
+
+ if (hdr->e_ident[EI_OSABI] != ELFOSABI_SYSV) {
+ /* ABI used is not System V */
+ return -ENOEXEC;
+ }
+
+ if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) {
+ /* Not little-endian */
+ return -ENOEXEC;
+ }
+
+ if (hdr->e_ident[EI_CLASS] != ELFCLASS64) {
+ /* Not 64-bits */
+ return -ENOEXEC;
+ }
+
+ if (hdr->e_type != ET_EXEC) {
+ /* Not executable */
+ return -ENOEXEC;
+ }
+
+ if (hdr->e_phnum > MAX_PHDRS) {
+ /* Too many program headers */
+ return -ENOEXEC;
+ }
+
+ return 0;
+}
+
+void
+elf_unload(struct proc *td, struct exec_prog *prog)
+{
+ size_t map_len;
+ struct pcb *pcbp = &td->pcb;
+ struct auxval auxval = prog->auxval;
+ struct exec_range *loadmap = prog->loadmap;
+
+ for (size_t i = 0; i < auxval.at_phnum; ++i) {
+ map_len = (loadmap[i].end - loadmap[i].start);
+ vm_unmap(pcbp->addrsp, loadmap[i].start, map_len);
+ vm_free_frame(loadmap[i].start, map_len / DEFAULT_PAGESIZE);
+ }
+}
+
+int
+elf64_load(const char *pathname, struct proc *td, struct exec_prog *prog)
+{
+ vm_prot_t prot = (PROT_READ | PROT_USER);
+ Elf64_Ehdr *hdr;
+ Elf64_Phdr *phdr;
+ paddr_t physmem;
+ off_t misalign;
+ void *tmp;
+ size_t page_count, map_len;
+ struct elf_file file;
+ struct pcb *pcbp;
+ struct exec_range loadmap[MAX_PHDRS];
+ struct auxval *auxvalp;
+ int error = 0;
+
+ if ((error = elf_get_file(pathname, &file)) != 0)
+ return error;
+
+ hdr = (Elf64_Ehdr *)file.data;
+ if ((error = elf64_verify(hdr)) != 0)
+ return error;
+
+ pcbp = &td->pcb;
+
+ /* Load program headers */
+ for (size_t i = 0; i < hdr->e_phnum; ++i) {
+ phdr = PHDR(hdr, i);
+ switch (phdr->p_type) {
+ case PT_LOAD:
+ if (ISSET(phdr->p_flags, PF_W))
+ prot |= PROT_WRITE;
+ if (ISSET(phdr->p_flags, PF_X))
+ prot |= PROT_EXEC;
+
+ misalign = phdr->p_vaddr & (DEFAULT_PAGESIZE - 1);
+ map_len = ALIGN_UP(phdr->p_memsz + misalign, DEFAULT_PAGESIZE);
+ page_count = map_len / DEFAULT_PAGESIZE;
+
+ /* Try to allocate page frames */
+ physmem = vm_alloc_frame(page_count);
+ if (physmem == 0) {
+ error = -ENOMEM;
+ break;
+ }
+
+ error = vm_map(pcbp->addrsp, phdr->p_vaddr, physmem,
+ prot, map_len);
+
+ if (error != 0) {
+ break;
+ }
+
+ tmp = (void *)((uintptr_t)hdr + phdr->p_offset);
+ memcpy(PHYS_TO_VIRT(physmem), tmp, phdr->p_filesz);
+
+ loadmap[i].start = physmem;
+ loadmap[i].end = physmem + map_len;
+ }
+ }
+
+ memcpy(prog->loadmap, loadmap, sizeof(loadmap));
+ auxvalp = &prog->auxval;
+ auxvalp->at_entry = hdr->e_entry;
+ auxvalp->at_phent = hdr->e_phentsize;
+ auxvalp->at_phnum = hdr->e_phnum;
+
+ /* Did program header loading fail? */
+ if (error != 0) {
+ elf_unload(td, prog);
+ return error;
+ }
+
+ dynfree(file.data);
+ return 0;
+}