summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordzwdz2022-07-18 14:01:21 +0200
committerdzwdz2022-07-18 14:01:21 +0200
commitd43c1af88b1834a00f8b5f09aa0af1a5e4f5b4aa (patch)
tree7743cd9eb761dee12711cf719ab846228a888dbd /src
parent620bd6af8e005057e04c8a2891c7537ec3556345 (diff)
user: a super primitive ELF loader
holy shit. this was simpler than i expected it to be
Diffstat (limited to 'src')
-rw-r--r--src/user/app/shell.c14
-rw-r--r--src/user/lib/elf.h188
-rw-r--r--src/user/lib/elfload.c59
-rw-r--r--src/user/lib/elfload.h5
-rw-r--r--src/usertestelf.c6
-rw-r--r--src/usertestelf.ld23
6 files changed, 293 insertions, 2 deletions
diff --git a/src/user/app/shell.c b/src/user/app/shell.c
index 952b9b7..9085aed 100644
--- a/src/user/app/shell.c
+++ b/src/user/app/shell.c
@@ -1,8 +1,9 @@
+#include <shared/syscalls.h>
+#include <stdbool.h>
#include <user/app/shell.h>
+#include <user/lib/elfload.h>
#include <user/lib/stdlib.h>
#include <user/tests/main.h>
-#include <shared/syscalls.h>
-#include <stdbool.h>
static bool isspace(char c) {
return c == ' ' || c == '\t' || c == '\n';
@@ -157,6 +158,15 @@ void shell_loop(void) {
if (!strcmp(cmd, "echo")) {
printf("%s\n", args);
+ } else if (!strcmp(cmd, "exec")) {
+ libc_file *file = file_open(args, 0);
+ if (!file) {
+ printf("couldn't open file\n");
+ } else {
+ elf_execf(file);
+ file_close(file);
+ printf("elf_execf failed\n");
+ }
} else if (!strcmp(cmd, "cat")) {
cmd_cat_ls(args, false);
} else if (!strcmp(cmd, "ls")) {
diff --git a/src/user/lib/elf.h b/src/user/lib/elf.h
new file mode 100644
index 0000000..8dc6242
--- /dev/null
+++ b/src/user/lib/elf.h
@@ -0,0 +1,188 @@
+/* shamelessly stolen from https://github.com/adachristine/sophia/tree/main/api/elf */
+#pragma once
+#include <stdint.h>
+
+typedef uint8_t Elf_Byte;
+
+#define EI_MAG0 0
+#define EI_MAG1 1
+#define EI_MAG2 2
+#define EI_MAG3 3
+#define EI_CLASS 4
+#define EI_DATA 5
+#define EI_VERSION 6
+#define EI_OSABI 7
+#define EI_ABIVERSION 8
+#define EI_PAD 9
+#define EI_NIDENT 16
+
+#define ELFMAG0 0x7f
+#define ELFMAG1 'E'
+#define ELFMAG2 'L'
+#define ELFMAG3 'F'
+
+#define ELFCLASSNONE 0
+#define ELFCLASS32 1
+#define ELFCLASS64 2
+
+#define ELFDATANONE 0
+#define ELFDATA2LSB 1
+#define ELFDATA2MSB 2
+
+#define EI_VERSION 6
+#define EV_NONE 0
+#define EV_CURRENT 1
+
+#define ELFOSABI_NONE 0
+#define ELFOSABI_SYSV EI_OSABI_NONE
+
+#define ET_NONE 0
+#define ET_REL 1
+#define ET_EXEC 2
+#define ET_DYN 3
+#define ET_CORE 4
+#define ET_LOOS 0xfe00
+#define ET_HIOS 0xfeff
+
+#define EM_NONE 0
+
+#define PT_NULL 0
+#define PT_LOAD 1
+#define PT_DYNAMIC 2
+
+#define PF_X 0x01
+#define PF_W 0x02
+#define PF_R 0x04
+
+#define SHT_NULL 0
+#define SHT_PROGITS 1
+#define SHT_SYMTAB 2
+#define SHT_STRTAB 3
+#define SHT_RELA 4
+#define SHT_HASH 5
+#define SHT_DYNAMIC 6
+
+#define SHF_WRITE 0x1
+#define SHF_ALLOC 0x2
+#define SHF_EXEC 0x4
+#define SHF_MERGE 0x10
+#define SHF_STRINGS 0x20
+
+#define DT_NULL 0
+#define DT_NEEDED 1
+#define DT_PLTRELSZ 2
+#define DT_PLTGOT 3
+#define DT_HASH 4
+#define DT_STRTAB 5
+#define DT_SYMTAB 6
+#define DT_RELA 7
+#define DT_RELASZ 8
+#define DT_RELAENT 9
+#define DT_STRSZ 10
+#define DT_SYMENT 11
+#define DT_JMPREL 0x17
+
+typedef uint64_t Elf64_Addr;
+typedef uint64_t Elf64_Off;
+typedef uint16_t Elf64_Section;
+typedef uint16_t Elf64_Versym;
+typedef uint16_t Elf64_Half;
+typedef int32_t Elf64_Sword;
+typedef uint32_t Elf64_Word;
+typedef int64_t Elf64_Sxword;
+typedef uint64_t Elf64_Xword;
+
+typedef struct Elf64_Ehdr Elf64_Ehdr;
+typedef struct Elf64_Phdr Elf64_Phdr;
+typedef struct Elf64_Shdr Elf64_Shdr;
+typedef struct Elf64_Sym Elf64_Sym;
+typedef struct Elf64_Dyn Elf64_Dyn;
+
+typedef struct Elf64_Rel Elf64_Rel;
+typedef struct Elf64_Rela Elf64_Rela;
+
+#define EM_X86_64 62
+
+struct Elf64_Ehdr
+{
+ Elf_Byte e_ident[EI_NIDENT];
+ Elf64_Half e_type;
+ Elf64_Half e_machine;
+ Elf64_Word e_version;
+ Elf64_Addr e_entry;
+ Elf64_Off e_phoff;
+ Elf64_Off e_shoff;
+ Elf64_Word e_flags;
+ Elf64_Half e_ehsize;
+ Elf64_Half e_phentsize;
+ Elf64_Half e_phnum;
+ Elf64_Half e_shentsize;
+ Elf64_Half e_shnum;
+ Elf64_Half e_shstrndx;
+};
+
+struct Elf64_Phdr
+{
+ Elf64_Word p_type;
+ Elf64_Word p_flags;
+ Elf64_Off p_offset;
+ Elf64_Addr p_vaddr;
+ Elf64_Addr p_paddr;
+ Elf64_Xword p_filesz;
+ Elf64_Xword p_memsz;
+ Elf64_Xword p_align;
+};
+
+struct Elf64_Shdr
+{
+ Elf64_Word sh_name;
+ Elf64_Word sh_type;
+ Elf64_Xword sh_flags;
+ Elf64_Addr sh_addr;
+ Elf64_Off sh_offset;
+ Elf64_Xword sh_size;
+ Elf64_Word sh_link;
+ Elf64_Word sh_info;
+ Elf64_Off sh_addralign;
+ Elf64_Xword sh_entsize;
+};
+
+struct Elf64_Sym
+{
+ Elf64_Word st_name;
+ Elf_Byte st_info;
+ Elf_Byte st_other;
+ Elf64_Half st_shndx;
+ Elf64_Addr st_value;
+ Elf64_Xword st_size;
+};
+
+struct Elf64_Dyn
+{
+ Elf64_Xword d_tag;
+ union
+ {
+ Elf64_Xword d_val;
+ Elf64_Addr d_ptr;
+ };
+};
+
+#define ELF64_R_SYM(info) ((info)>>32)
+#define ELF64_R_TYPE(info) ((Elf64_Word)(info))
+#define ELF64_R_INFO(sym, type) (((Elf64_Xword)(sym)<<32)+(Elf64_Xword)(type))
+
+#define R_X86_64_JUMP_SLOT 7
+#define R_X86_64_RELATIVE 8
+
+struct Elf64_Rel
+{
+ Elf64_Addr r_offset;
+ Elf64_Xword r_info;
+};
+
+struct Elf64_Rela
+{
+ Elf64_Addr r_offset;
+ Elf64_Xword r_info;
+ Elf64_Sxword r_addend;
+};
diff --git a/src/user/lib/elfload.c b/src/user/lib/elfload.c
new file mode 100644
index 0000000..67cef68
--- /dev/null
+++ b/src/user/lib/elfload.c
@@ -0,0 +1,59 @@
+#include <shared/flags.h>
+#include <shared/syscalls.h>
+#include <user/lib/elf.h>
+#include <user/lib/elfload.h>
+
+void elf_execf(libc_file *f) {
+ const size_t cap = 0x60000;
+ size_t pos = 0;
+ void *buf = malloc(cap); // TODO a way to get file size
+ if (!buf) goto fail;
+
+ while (!f->eof) {
+ long ret = file_read(f, buf, cap - pos);
+ if (ret < 0) goto fail;
+ pos += ret;
+ if (pos >= cap) goto fail;
+ }
+ elf_exec(buf);
+
+fail:
+ free(buf);
+}
+
+static bool valid_ehdr(const struct Elf64_Ehdr *h) {
+ return h->e_ident[0] == 0x7f
+ && h->e_ident[1] == 'E'
+ && h->e_ident[2] == 'L'
+ && h->e_ident[3] == 'F'
+ && h->e_type == ET_EXEC
+ && h->e_machine == EM_X86_64
+ && h->e_version == 1;
+}
+
+static bool load_phdr(const void *base, size_t idx) {
+ const struct Elf64_Ehdr *ehdr = base;
+ const struct Elf64_Phdr *phdr = base + ehdr->e_phoff + idx * ehdr->e_phentsize;
+
+ if (phdr->p_type != PT_LOAD) {
+ printf("unknown type %x\n", phdr->p_type);
+ return false;
+ }
+ // TODO overlap check
+ // TODO don't ignore flags
+ _syscall_memflag((void*)phdr->p_vaddr, phdr->p_memsz, MEMFLAG_PRESENT);
+ // TODO check that filesz <= memsz
+ memcpy((void*)phdr->p_vaddr, base + phdr->p_offset, phdr->p_filesz);
+ return true;
+}
+
+void elf_exec(void *base) {
+ struct Elf64_Ehdr *ehdr = base;
+ if (!valid_ehdr(ehdr)) return;
+ for (size_t phi = 0; phi < ehdr->e_phnum; phi++) {
+ if (!load_phdr(base, phi))
+ return;
+ }
+ ((void(*)())ehdr->e_entry)();
+ _syscall_exit(1);
+}
diff --git a/src/user/lib/elfload.h b/src/user/lib/elfload.h
new file mode 100644
index 0000000..c6afbdd
--- /dev/null
+++ b/src/user/lib/elfload.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <user/lib/stdlib.h>
+
+void elf_execf(libc_file *f);
+void elf_exec(void *elf);
diff --git a/src/usertestelf.c b/src/usertestelf.c
new file mode 100644
index 0000000..19f0096
--- /dev/null
+++ b/src/usertestelf.c
@@ -0,0 +1,6 @@
+#include <user/lib/syscall.c>
+
+int main(void) {
+ _syscall_write(1, "Hello!", 6, 0);
+ _syscall_exit(0);
+}
diff --git a/src/usertestelf.ld b/src/usertestelf.ld
new file mode 100644
index 0000000..2567f77
--- /dev/null
+++ b/src/usertestelf.ld
@@ -0,0 +1,23 @@
+ENTRY(main)
+
+SECTIONS
+{
+ . = 8M;
+ .text BLOCK(4K) : ALIGN(4K)
+ {
+ *(.text)
+ }
+ .rodata BLOCK(4K) : ALIGN(4K)
+ {
+ *(.rodata)
+ }
+ .data BLOCK(4K) : ALIGN(4K)
+ {
+ *(.data)
+ }
+ .bss BLOCK(4K) : ALIGN(4K)
+ {
+ *(COMMON)
+ *(.bss)
+ }
+}