Implement basic ELF executable loading
authorcoderain <coderain@sdf.org>
Wed, 15 Nov 2017 04:35:50 +0000 (05:35 +0100)
committercoderain <coderain@sdf.org>
Wed, 15 Nov 2017 04:35:50 +0000 (05:35 +0100)
kernel/src/exec/elf.c [new file with mode: 0644]

diff --git a/kernel/src/exec/elf.c b/kernel/src/exec/elf.c
new file mode 100644 (file)
index 0000000..29993c7
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * elf.c
+ *
+ * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <process.h>
+
+#define ELF_MAGIC       0x464C457F
+#define ELF_CLASS_32BIT 1
+
+#define ELF_SEGMENT_NULL    0
+#define ELF_SEGMENT_LOAD    1
+#define ELF_SEGMENT_DYNAMIC 2
+#define ELF_SEGMENT_INTERP  3
+#define ELF_SEGMENT_NOTE    4
+#define ELF_SEGMENT_SHLIB   5
+#define ELF_SEGMENT_PHDR    6
+#define ELF_SEGMENT_TLS     7
+
+#define ELF_SEGMENT_EXEC  (1 << 0)
+#define ELF_SEGMENT_WRITE (1 << 1)
+#define ELF_SEGMENT_READ  (1 << 2)
+
+#pragma pack(push, 1)
+typedef struct
+{
+    dword_t magic;
+    byte_t class;
+    byte_t encoding;
+    byte_t version;
+    byte_t system_abi;
+    byte_t abi_version;
+} elf_ident_t;
+#pragma pack(pop)
+
+typedef struct
+{
+    word_t type;
+    word_t machine;
+    dword_t version;
+    dword_t entry;
+    dword_t segment_offset;
+    dword_t section_offset;
+    dword_t flags;
+    word_t header_size;
+    word_t segment_entry_size;
+    word_t segment_count;
+    word_t section_entry_size;
+    word_t section_count;
+    word_t section_string_index;
+} elf32_header_t;
+
+typedef struct
+{
+    dword_t type;
+    dword_t offset;
+    dword_t virtual_address;
+    dword_t physical_address;
+    dword_t file_size;
+    dword_t mem_size;
+    dword_t flags;
+    dword_t alignment;
+} elf32_segment_t;
+
+dword_t load_elf(handle_t file, process_params_t *parameters, thread_state_t *initial_state)
+{
+    elf_ident_t ident;
+    char filename[MAX_PATH];
+    qword_t filesize;
+    size_t bytes_read;
+
+    sysret_t ret = syscall(SYSCALL_QUERY_FILE, file, FILE_INFO_NAME, filename, sizeof(filename));
+    if (ret != ERR_SUCCESS) return ret;
+
+    ret = syscall(SYSCALL_QUERY_FILE, file, FILE_INFO_SIZE, &filesize, sizeof(filesize));
+    if (ret != ERR_SUCCESS) return ret;
+
+    ret = syscall(SYSCALL_READ_FILE, file, &ident, 0ULL, (size_t)sizeof(ident), &bytes_read);
+    if (ret != ERR_SUCCESS) return ret;
+
+    if (ident.magic != ELF_MAGIC) return ERR_INVALID;
+    if (ident.class != ELF_CLASS_32BIT) return ERR_INVALID;
+
+    elf32_header_t header;
+    ret = syscall(SYSCALL_READ_FILE, file, &header, 16ULL, (size_t)sizeof(header), &bytes_read);
+    if (ret != ERR_SUCCESS) return ret;
+
+    handle_t section;
+    ret = syscall(SYSCALL_CREATE_MEMORY_SECTION, filename, file, (size_t)filesize, MEMORY_SECTION_WRITABLE, &section);
+    if (ret != ERR_SUCCESS) return ret;
+
+    dword_t i;
+    elf32_segment_t *segments = __builtin_alloca(header.segment_count * sizeof(elf32_segment_t));
+
+    ret = syscall(SYSCALL_READ_FILE,
+                  file,
+                  segments,
+                  (qword_t)header.segment_offset,
+                  (size_t)(header.segment_count * sizeof(elf32_segment_t)),
+                  &bytes_read);
+    if (ret != ERR_SUCCESS) return ret;
+
+    for (i = 0; i < header.segment_count; i++)
+    {
+        if (segments[i].type ==  ELF_SEGMENT_NULL)
+        {
+            continue;
+        }
+        else if (ELF_SEGMENT_LOAD)
+        {
+            dword_t flags = MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE;
+            if (segments[i].flags & ELF_SEGMENT_READ) flags |= MEMORY_BLOCK_ACCESSIBLE;
+            if (segments[i].flags & ELF_SEGMENT_WRITE) flags |= MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE;
+            if (segments[i].flags & ELF_SEGMENT_EXEC) flags |= MEMORY_BLOCK_EXECUTABLE;
+
+            qword_t offset = (qword_t)segments[i].offset - PAGE_OFFSET(segments[i].virtual_address);
+
+            ret = syscall(SYSCALL_MAP_MEMORY_SECTION,
+                          INVALID_HANDLE,
+                          section,
+                          &segments[i].virtual_address,
+                          offset,
+                          PAGE_ALIGN_UP(segments[i].mem_size),
+                          flags);
+            if (ret != ERR_SUCCESS) break;
+        }
+        else
+        {
+            ret = ERR_INVALID;
+            break;
+        }
+    }
+
+    initial_state->regs.eip = header.entry;
+
+    void *stack_address = NULL;
+    ret = syscall(SYSCALL_ALLOC_MEMORY,
+                  INVALID_HANDLE,
+                  &stack_address,
+                  0x200000,
+                  MEMORY_BLOCK_ACCESSIBLE | MEMORY_BLOCK_WRITABLE | MEMORY_BLOCK_USERMODE | MEMORY_BLOCK_EVICTABLE);
+    if (ret != ERR_SUCCESS) return ret;
+
+    initial_state->regs.esp = (uintptr_t)stack_address + 0x200000;
+    init_user_stack((uintptr_t*)&initial_state->regs.esp, parameters);
+
+    syscall(SYSCALL_CLOSE_OBJECT, section);
+    return ret;
+}