3c40f6cd227b21e77e52c4c523b7f8de8fd72c8b
[lunaix-os.git] / lunaix-os / kernel / loader / exec.c
1 #include <lunaix/elf.h>
2 #include <lunaix/fs.h>
3 #include <lunaix/ld.h>
4 #include <lunaix/mm/mmap.h>
5 #include <lunaix/mm/vmm.h>
6 #include <lunaix/process.h>
7 #include <lunaix/spike.h>
8 #include <lunaix/status.h>
9 #include <lunaix/syscall.h>
10
11 #include <klibc/string.h>
12
13 size_t
14 exec_str_size(const char** str_arr, size_t* length)
15 {
16     const char* chr = *str_arr;
17     size_t sz = 0, len = 0;
18
19     while (chr) {
20         sz += strlen(chr);
21         len++;
22
23         chr = *(str_arr + sz);
24     }
25
26     *length = len;
27     return sz + 1;
28 }
29
30 void
31 __heap_copied(struct mm_region* region)
32 {
33     mm_index((void**)&region->proc_vms->heap, region);
34 }
35
36 int
37 __exec_remap_heap(struct ld_param* param, struct proc_mm* pvms)
38 {
39     if (pvms->heap) {
40         mem_unmap_region(param->vms_mnt, pvms->heap);
41     }
42
43     struct mmap_param map_param = { .pvms = pvms,
44                                     .vms_mnt = param->vms_mnt,
45                                     .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
46                                     .type = REGION_TYPE_HEAP,
47                                     .proct = PROT_READ | PROT_WRITE,
48                                     .mlen = DEFAULT_HEAP_PAGES * PG_SIZE };
49     int status = 0;
50     struct mm_region* heap;
51     if ((status = mem_map(NULL, &heap, param->info.end, NULL, &map_param))) {
52         param->status |= LD_STAT_FKUP;
53         return status;
54     }
55
56     heap->region_copied = __heap_copied;
57     mm_index((void**)&pvms->heap, heap);
58 }
59
60 int
61 exec_loadto(struct ld_param* param,
62             struct v_file* executable,
63             const char** argv,
64             const char** envp)
65 {
66     int errno = 0;
67
68     size_t argv_len, envp_len;
69     size_t sz_argv = exec_str_size(argv, &argv_len);
70     size_t sz_envp = exec_str_size(envp, &envp_len);
71     size_t total_sz = ROUNDUP(sz_argv + sz_envp, PG_SIZE);
72
73     if (total_sz / PG_SIZE > MAX_VAR_PAGES) {
74         errno = E2BIG;
75         goto done;
76     }
77
78     if ((errno = elf_load(param, executable))) {
79         goto done;
80     }
81
82     struct proc_mm* pvms = &param->proc->mm;
83     struct mmap_param map_vars = { .pvms = pvms,
84                                    .vms_mnt = param->vms_mnt,
85                                    .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
86                                    .type = REGION_TYPE_VARS,
87                                    .proct = PROT_READ,
88                                    .mlen = MAX_VAR_PAGES * PG_SIZE };
89
90     void* mapped;
91     isr_param* intr_ctx = &param->proc->intr_ctx;
92
93     if ((errno = __exec_remap_heap(param, pvms))) {
94         goto done;
95     }
96
97     if ((errno = mem_map(&mapped, NULL, UMMAP_END, NULL, &map_vars))) {
98         goto done;
99     }
100
101     if (param->vms_mnt == VMS_SELF) {
102         // we are loading executable into current addr space
103
104         // make some handy infos available to user space
105         ptr_t arg_start = mapped + sizeof(struct usr_exec_param);
106         memcpy(arg_start, (void*)argv, sz_argv);
107         memcpy(arg_start + sz_argv, (void*)envp, sz_envp);
108
109         struct usr_exec_param* param = mapped;
110         *param = (struct usr_exec_param){ .argc = argv_len,
111                                           .argv = arg_start,
112                                           .envc = envp_len,
113                                           .envp = arg_start + sz_argv,
114                                           .info = param->info };
115         ptr_t* ustack = (ptr_t*)USTACK_TOP;
116         ustack[-1] = (ptr_t)mapped;
117         intr_ctx->esp = &ustack[-1];
118     } else {
119         // TODO need to find a way to inject argv and envp remotely
120         fail("not implemented");
121     }
122
123     intr_ctx->eip = param->info.ehdr_out.e_entry;
124     // we will jump to new entry point (_u_start) upon syscall's
125     // return so execve 'will not return' from the perspective of it's invoker
126
127 done:
128     return errno;
129 }
130
131 __DEFINE_LXSYSCALL3(int,
132                     execve,
133                     const char*,
134                     filename,
135                     const char*,
136                     argv[],
137                     const char*,
138                     envp[])
139 {
140     int errno = 0;
141     struct v_dnode* dnode;
142     struct v_file* file;
143
144     if ((errno = vfs_walk_proc(filename, &dnode, NULL, 0))) {
145         goto done;
146     }
147
148     if ((errno = vfs_open(dnode, &file))) {
149         goto done;
150     }
151
152     struct ld_param ldparam;
153     ld_create_param(&ldparam, __current, VMS_SELF);
154
155     if ((errno = exec_loadto(&ldparam, file, argv, envp))) {
156         vfs_pclose(file, __current->pid);
157
158         if ((ldparam.status & LD_STAT_FKUP)) {
159             // we fucked up our address space.
160             terminate_proc(11451);
161             schedule();
162             fail("should not reach");
163         }
164     }
165
166 done:
167     return errno;
168 }