• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
3  * Licensed under the Mulan PSL v2.
4  * You can use this software according to the terms and conditions of the Mulan PSL v2.
5  * You may obtain a copy of Mulan PSL v2 at:
6  *     http://license.coscl.org.cn/MulanPSL2
7  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8  * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9  * PURPOSE.
10  * See the Mulan PSL v2 for more details.
11  */
12 #include "loader.h"
13 #include "liblaunch.h"
14 #include "libchcoreelf.h"
15 #include <chcore/container/list.h>
16 #include <chcore/memory.h>
17 #include <chcore/type.h>
18 #include <pthread.h>
19 #include <fcntl.h>
20 #include <stdlib.h>
21 #include <string.h>
22 
23 #define LOADER_PATH_MAX_LENGTH (255)
24 
25 /**
26  * @brief Defining loader interface or base loader object. So this
27  * struct represents an abstract loader. And concrete loader objects
28  * should embed it as the first member. This layout constraint is used
29  * by following simple OOP system to implement polymorphism.
30  */
31 struct loader {
32     struct list_head node;
33     char path[LOADER_PATH_MAX_LENGTH + 1];
34     /**
35      * @brief Method to init private state of a concrete
36      * loader object, is invoked when initializing a new concrete object.
37      *
38      * @return 0 if success, otherwise -errno. All memory resources used
39      * by this function should be freed on error.
40      */
41     int (*init_loader)(struct loader *this);
42     /**
43      * @brief Method to launch the dynamically linked program specified in
44      * lp_args.
45      *
46      * Implementation of this method is must Thread-Safe.
47      *
48      * @return 0 if success, otherwise -errno. All memory resources used
49      * by this function should be freed on error.
50      */
51     int (*launch_process)(struct loader *this,
52                           struct launch_process_args *lp_args);
53     /**
54      * @brief Method to free a concrete loader object. This
55      * function should free not only the object itself, but also all
56      * memory resources used by its private state.
57      */
58     void (*destructor)(struct loader *this);
59 };
60 
61 /**
62  * This list protected by a mutex is functioned as a thread-safe cache for all
63  * concrete loader objects being used previously.
64  */
65 struct list_head loaders_list;
66 pthread_mutex_t loaders_mu;
67 pthread_once_t loaders_ctrl = PTHREAD_ONCE_INIT;
68 
__init_loaders_list(void)69 static void __init_loaders_list(void)
70 {
71     init_list_head(&loaders_list);
72     pthread_mutex_init(&loaders_mu, NULL);
73 }
74 
init_loaders_list(void)75 static void init_loaders_list(void)
76 {
77     pthread_once(&loaders_ctrl, __init_loaders_list);
78 }
79 
__find_loader_in_list(const char * path)80 static struct loader *__find_loader_in_list(const char *path)
81 {
82     struct loader *iter;
83     for_each_in_list (iter, struct loader, node, &loaders_list) {
84         if (strncmp(path, iter->path, LOADER_PATH_MAX_LENGTH) == 0) {
85             return iter;
86         }
87     }
88     return NULL;
89 }
90 
91 /**
92  * @brief Defining constructors, or "classes" of concrete loader
93  * implementations. A constructor function should return a pointer to a concrete
94  * loader object, it don't have to implement singleton pattern. It should
95  * allocate memory for the new object, performing interface binding, but
96  * **don't** invoke init_loader method.
97  */
98 typedef struct loader *(*constructor_t)(const char *path);
99 
100 /**
101  * @brief Each different path corrsponds to a constructor function. And the same
102  * constructor can be used by multiple entries.
103  */
104 struct constructor_table_entry {
105     char path[LOADER_PATH_MAX_LENGTH + 1];
106     constructor_t constructor;
107 };
108 
109 static struct loader *elf_so_loader_constructor(const char *path);
110 
111 struct constructor_table_entry constructor_table[] = {
112     {CHCORE_LOADER, elf_so_loader_constructor}};
113 
114 #define CONSTRUCTOR_CNT \
115     (sizeof(constructor_table) / sizeof(struct constructor_table_entry))
116 
__find_loader_constructor(const char * path)117 constructor_t __find_loader_constructor(const char *path)
118 {
119     for (int i = 0; i < CONSTRUCTOR_CNT; i++) {
120         if (strncmp(path, constructor_table[i].path, LOADER_PATH_MAX_LENGTH)
121             == 0) {
122             return constructor_table[i].constructor;
123         }
124     }
125 
126     return NULL;
127 }
128 
129 /**
130  * @brief Factory function to create a concrete loader object. This function
131  * implements singleton pattern, so it will return the same object if the same
132  * path is passed in.
133  *
134  * Specifically, this function will lookup the path in the cache above to see if
135  * there is a loader object for the path. If not, it will create a new object by
136  * querying the constructor table and invoking the corresponding constructor
137  * function, and add it to the cache.
138  *
139  * @param loader_path [In]
140  * @param loader [Out] WARNING: the returning pointer is owned by this function,
141  * and caller just borrow it. Caller is not allowed to free this pointer,
142  * otherwise the behavior is undefined.
143  * @return 0 if success, otherwise -errno is returned. All memory resources
144  * consumed by this function are guaranteed to be freed if not success.
145  */
loader_factory(const char * loader_path,struct loader ** loader)146 static int loader_factory(const char *loader_path, struct loader **loader)
147 {
148     int ret = 0;
149     struct loader *target;
150     constructor_t constructor;
151     pthread_mutex_lock(&loaders_mu);
152     target = __find_loader_in_list(loader_path);
153     if (target) {
154         *loader = target;
155         goto out;
156     }
157 
158     constructor = __find_loader_constructor(loader_path);
159     if (!constructor) {
160         ret = -EINVAL;
161         goto out;
162     }
163 
164     target = constructor(loader_path);
165     if (!target) {
166         ret = -ENOMEM;
167         goto out;
168     }
169 
170     ret = target->init_loader(target);
171     if (ret < 0) {
172         goto out_init_fail;
173     }
174 
175     list_append(&target->node, &loaders_list);
176     *loader = target;
177 out:
178     pthread_mutex_unlock(&loaders_mu);
179     return ret;
180 out_init_fail:
181     pthread_mutex_unlock(&loaders_mu);
182     target->destructor(target);
183     return ret;
184 }
185 
find_loader(const char * loader_path,struct loader ** loader)186 int find_loader(const char *loader_path, struct loader **loader)
187 {
188     init_loaders_list();
189 
190     if (!loader_path) {
191         return -EINVAL;
192     }
193 
194     return loader_factory(loader_path, loader);
195 }
196 
launch_process_using_loader(struct loader * this,struct launch_process_args * lp_args)197 int launch_process_using_loader(struct loader *this,
198                                 struct launch_process_args *lp_args)
199 {
200     if (!this) {
201         return -EINVAL;
202     }
203 
204     return this->launch_process(this, lp_args);
205 }
206 
207 /* Load offset of libc.so */
208 #if __SIZEOF_POINTER__ == 4
209 #define LIBC_LDSO_OFFSET (0x40000000UL)
210 #else
211 #define LIBC_LDSO_OFFSET (0x400000000000UL)
212 #endif
213 
214 /**
215  * Concrete loader implementation for loaders who are a DYN ELF file itself.
216  */
217 struct elf_so_loader {
218     /** Note: this member must be the first member */
219     struct loader base;
220     struct user_elf *elf_so;
221 };
222 
elf_so_loader_init(struct loader * _this)223 static int elf_so_loader_init(struct loader *_this)
224 {
225     int ret, i;
226     struct user_elf *loader_elf;
227     struct user_elf_seg *cur_seg;
228     struct elf_so_loader *this = (struct elf_so_loader *)_this;
229 
230     ret = load_elf_from_fs(this->base.path, &loader_elf);
231 
232     if (ret < 0) {
233         return ret;
234     }
235 
236     for (i = 0; i < loader_elf->segs_nr; i++) {
237         /**
238          * Because the loader is a DYN ELF file itself, it should be
239          * shared by all processes created using this loader through CoW
240          * to save memory. So for each loadable writable segment, we
241          * remove VMR_WRITE permission and make it VMR_COW.
242          */
243         cur_seg = &loader_elf->user_elf_segs[i];
244         if (cur_seg->perm & VMR_WRITE) {
245             cur_seg->perm &= (~VMR_WRITE);
246             cur_seg->perm |= VMR_COW;
247         }
248     }
249     this->elf_so = loader_elf;
250     return 0;
251 }
252 
elf_so_loader_deinit(struct loader * _this)253 static void elf_so_loader_deinit(struct loader *_this)
254 {
255     struct elf_so_loader *this = (struct elf_so_loader *)_this;
256 
257     if (this->elf_so) {
258         free_user_elf(this->elf_so);
259     }
260 
261     free(this);
262 }
263 
elf_so_loader_launch_process(struct loader * _this,struct launch_process_args * lp_args)264 static int elf_so_loader_launch_process(struct loader *_this,
265                                         struct launch_process_args *lp_args)
266 {
267     int ret = 0, i, argc;
268     char **prepended_argv, **argv;
269     struct elf_so_loader *this = (struct elf_so_loader *)_this;
270 
271     /**
272      * The ELF file content should be loaded by the loader program, instead
273      * of the caller.
274      */
275     if (lp_args->user_elf) {
276         ret = -EINVAL;
277         goto out;
278     }
279 
280     printf(
281         "CHCORE_LOADER warning: loader itself is loaded at a magic address\n");
282 
283     argc = lp_args->argc;
284     argv = lp_args->argv;
285 
286     /**
287      * The loader itself would be launched as the new process at first, and
288      * it would find the real program to be launched from command line
289      * arguments. So its command line arguments should be prepended with
290      * itself, then the original command line arguments.
291      *
292      * E.g., /hello_dl.bin -> /libc.so /hello_dl.bin
293      */
294     prepended_argv = malloc(sizeof(char *) * (argc + 1));
295     if (!prepended_argv) {
296         ret = -ENOMEM;
297         goto out;
298     }
299 
300     prepended_argv[0] = this->base.path;
301     for (i = 0; i < argc; i++) {
302         prepended_argv[1 + i] = argv[i];
303     }
304 
305     /**
306      * Modify lp_args like a proxy.
307      */
308     lp_args->user_elf = this->elf_so;
309     lp_args->argc = argc + 1;
310     lp_args->argv = prepended_argv;
311     /**
312      * Loader itself would use a high range of virtual address space of the
313      * new process. Avoid conflicts with the real memory image of new
314      * process.
315      */
316     lp_args->load_offset = LIBC_LDSO_OFFSET;
317 
318     ret = launch_process_with_pmos_caps(lp_args);
319 
320     /**
321      * Backup and resume original lp_args content, because we just borrow
322      * the pointer.
323      */
324     lp_args->user_elf = NULL;
325     lp_args->argv = argv;
326 
327     free(prepended_argv);
328 out:
329     return ret;
330 }
331 
elf_so_loader_constructor(const char * path)332 static struct loader *elf_so_loader_constructor(const char *path)
333 {
334     struct elf_so_loader *this = malloc(sizeof(*this));
335     if (!this) {
336         return NULL;
337     }
338     this->elf_so = NULL;
339 
340     strncpy(this->base.path, path, LOADER_PATH_MAX_LENGTH);
341     this->base.init_loader = elf_so_loader_init;
342     this->base.launch_process = elf_so_loader_launch_process;
343     this->base.destructor = elf_so_loader_deinit;
344     return (struct loader *)this;
345 }