1c206d2566c3d99f31ab63ecbfc793b5d403f47a
[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     if (!str_arr) {
17         *length = 0;
18         return 0;
19     }
20
21     const char* chr = *str_arr;
22     size_t sz = 0, len = 0;
23
24     while (chr) {
25         sz += strlen(chr);
26         len++;
27
28         chr = *(str_arr + sz);
29     }
30
31     *length = len;
32     return sz + 1;
33 }
34
35 void
36 __heap_copied(struct mm_region* region)
37 {
38     mm_index((void**)&region->proc_vms->heap, region);
39 }
40
41 int
42 __exec_remap_heap(struct ld_param* param, struct proc_mm* pvms)
43 {
44     if (pvms->heap) {
45         mem_unmap_region(param->vms_mnt, pvms->heap);
46     }
47
48     struct mmap_param map_param = { .pvms = pvms,
49                                     .vms_mnt = param->vms_mnt,
50                                     .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
51                                     .type = REGION_TYPE_HEAP,
52                                     .proct = PROT_READ | PROT_WRITE,
53                                     .mlen = DEFAULT_HEAP_PAGES * PG_SIZE };
54     int status = 0;
55     struct mm_region* heap;
56     if ((status = mem_map(NULL, &heap, param->info.end, NULL, &map_param))) {
57         param->status |= LD_STAT_FKUP;
58         return status;
59     }
60
61     heap->region_copied = __heap_copied;
62     mm_index((void**)&pvms->heap, heap);
63 }
64
65 int
66 exec_load(struct ld_param* param,
67           struct v_file* executable,
68           const char** argv,
69           const char** envp)
70 {
71     int errno = 0;
72
73     size_t argv_len, envp_len;
74     size_t sz_argv = exec_str_size(argv, &argv_len);
75     size_t sz_envp = exec_str_size(envp, &envp_len);
76     size_t total_sz = ROUNDUP(sz_argv + sz_envp, PG_SIZE);
77
78     if (total_sz / PG_SIZE > MAX_VAR_PAGES) {
79         errno = E2BIG;
80         goto done;
81     }
82
83     if ((errno = elf_load(param, executable))) {
84         goto done;
85     }
86
87     struct proc_mm* pvms = &param->proc->mm;
88     struct mmap_param map_vars = { .pvms = pvms,
89                                    .vms_mnt = param->vms_mnt,
90                                    .flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED,
91                                    .type = REGION_TYPE_VARS,
92                                    .proct = PROT_READ,
93                                    .mlen = MAX_VAR_PAGES * PG_SIZE };
94
95     void* mapped;
96
97     if ((errno = __exec_remap_heap(param, pvms))) {
98         goto done;
99     }
100
101     if ((errno = mem_map(&mapped, NULL, UMMAP_END, NULL, &map_vars))) {
102         goto done;
103     }
104
105     if (param->vms_mnt == VMS_SELF) {
106         // we are loading executable into current addr space
107
108         // make some handy infos available to user space
109         ptr_t arg_start = mapped + sizeof(struct usr_exec_param);
110         if (argv)
111             memcpy(arg_start, (void*)argv, sz_argv);
112         if (envp)
113             memcpy(arg_start + sz_argv, (void*)envp, sz_envp);
114
115         struct usr_exec_param* param = mapped;
116         *param = (struct usr_exec_param){ .argc = argv_len,
117                                           .argv = arg_start,
118                                           .envc = envp_len,
119                                           .envp = arg_start + sz_argv,
120                                           .info = param->info };
121         ptr_t* ustack = (ptr_t*)USTACK_TOP;
122         ustack[-1] = (ptr_t)mapped;
123         param->info.stack_top = &ustack[-1];
124     } else {
125         // TODO need to find a way to inject argv and envp remotely
126         fail("not implemented");
127     }
128
129     param->info.entry = param->info.ehdr_out.e_entry;
130 done:
131     return errno;
132 }
133
134 int
135 exec_load_byname(struct ld_param* param,
136                  const char* filename,
137                  const char** argv,
138                  const char** 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     if ((errno = exec_load(param, file, argv, envp))) {
153         vfs_pclose(file, __current->pid);
154     }
155
156 done:
157     return errno;
158 }
159
160 __DEFINE_LXSYSCALL3(int,
161                     execve,
162                     const char*,
163                     filename,
164                     const char*,
165                     argv[],
166                     const char*,
167                     envp[])
168 {
169     int errno = 0;
170     struct ld_param ldparam;
171     ld_create_param(&ldparam, __current, VMS_SELF);
172
173     if ((errno = exec_load_byname(&ldparam, filename, argv, envp))) {
174         if ((ldparam.status & LD_STAT_FKUP)) {
175             // we fucked up our address space.
176             terminate_proc(11451);
177             schedule();
178             fail("should not reach");
179         }
180     }
181
182     isr_param* intr_ctx = &__current->intr_ctx;
183     intr_ctx->esp = ldparam.info.stack_top;
184     intr_ctx->eip = ldparam.info.entry;
185
186     // we will jump to new entry point (_u_start) upon syscall's
187     // return so execve 'will not return' from the perspective of it's invoker
188
189 done:
190     return errno;
191 }