summaryrefslogtreecommitdiff
path: root/src/kernel/elf.c
blob: bd2f12d7b5764099fbfc55fc776b75c2db4dde1b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <stdint.h>
#include "drive.h"
#include "elf.h"
#include "task.h"
#include "util.h"
#include "paging.h"
#include "pmap.h"

#define ELF_MAGIC 0x464c457f

enum {
  ELF_32 = 1,
  ELF_64 = 2
};

enum {
  LITTLE_ENDIAN = 1,
  BIG_ENDIAN    = 2
};

enum {
  //...
  ARCH_X86 = 0x03
  //...
};

struct elf_header {
  uint32_t magic;
  uint8_t word_size;
  uint8_t endianness;
  uint8_t elf_version;
  uint8_t target_os;//ignored
  uint8_t os_abi_version;//should be zero
  uint8_t reserved[7];
  uint16_t object_type;//TODO
  uint16_t architecture;//should be ARCH_X86
  uint32_t elf_version_2;
  uint32_t entry_vma;
  uint32_t phtable_fa;
  uint32_t shtable_fa;
  uint32_t flags;
  uint16_t eh_size;
  uint16_t phentry_size;
  uint16_t phtable_count;
  uint16_t shentry_size;
  uint16_t shtable_count;
  uint16_t sh_names_entry;
} __attribute__ ((packed));

enum {
  PT_UNUSED  = 0,
  PT_LOADME  = 1,
  PT_DYNLINK = 2,
  PT_INTERP  = 3,
  PT_COMMENT = 4,
  PT_SHARED  = 5,
  PT_PHTABLE = 6,
  PT_TL_TMPL = 7
};

enum {
  PH_WRITABLE = 0x02
};

struct ph_entry {
  uint32_t type;
  uint32_t fa;
  uint32_t vma;//must be page-aligned
  uint32_t pma;//ignored
  uint32_t fs;
  uint32_t vms;
  uint32_t flags;
  uint32_t align;
} __attribute__ ((packed));

bool try_elf_run(const struct drive *d, const char *path) {
  file_id_t h = d->get_file(d, path);
  if (!h)
    return false;

  struct elf_header ehead;

  fmcpy(&ehead, d, h, 0, sizeof(struct elf_header));

  if ((ehead.magic != ELF_MAGIC) ||
      (ehead.word_size != ELF_32) ||
      (ehead.endianness != LITTLE_ENDIAN) ||
       ehead.os_abi_version) {
    d->free_file(d, h);
    return false;
  }

  uint32_t phtable_size = ehead.phentry_size * ehead.phtable_count;
  uint16_t phtable_pages = (phtable_size - 1) / 4096 + 1;

  void *phtable = allocate_kernel_pages(phtable_pages);
  fmcpy(phtable, d, h, ehead.phtable_fa, phtable_size);

  void *pd = new_task_pd();

  for (uint32_t phi = 0; phi < ehead.phtable_count; ++phi) {
    struct ph_entry *entry = phtable + phi * ehead.phentry_size;
    if (entry->type != PT_LOADME)
      continue;
    void *pma = pd_user_allocate(pd, entry->vma, (entry->vms - 1) / 4096 + 1, entry->flags & PH_WRITABLE);
    fmcpy(pma, d, h, entry->fa, entry->fs);
  }

  free_pages(phtable, phtable_pages);
  d->free_file(d, h);

  struct task_state tstate;
  tstate.page_directory = pd;
  tstate.ret_addr = ehead.entry_vma;
  new_task(tstate);
  return true;
}