procd: Don't use syslog before its initialization
[project/procd.git] / jail / elf.c
1 /*
2  * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License version 2.1
6  * as published by the Free Software Foundation
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #define _GNU_SOURCE
15
16 #include <string.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <glob.h>
20 #include <elf.h>
21 #include <linux/limits.h>
22
23 #include <libubox/utils.h>
24
25 #include "elf.h"
26 #include "fs.h"
27 #include "log.h"
28
29 struct avl_tree libraries;
30 static LIST_HEAD(library_paths);
31
32 static void alloc_library_path(const char *path)
33 {
34         struct stat s;
35         if (stat(path, &s))
36                 return;
37
38         struct library_path *p;
39         char *_path;
40
41         p = calloc_a(sizeof(*p),
42                 &_path, strlen(path) + 1);
43         if (!p)
44                 return;
45
46         p->path = strcpy(_path, path);
47
48         list_add_tail(&p->list, &library_paths);
49         DEBUG("adding ld.so path %s\n", path);
50 }
51
52 /*
53  * path = full path
54  * name = soname/avl key
55  */
56 void alloc_library(const char *path, const char *name)
57 {
58         struct library *l;
59         char *_name, *_path;
60
61         l = calloc_a(sizeof(*l),
62                 &_path, strlen(path) + 1,
63                 &_name, strlen(name) + 1);
64         if (!l)
65                 return;
66
67         l->avl.key = l->name = strcpy(_name, name);
68         l->path = strcpy(_path, path);
69
70         avl_insert(&libraries, &l->avl);
71         DEBUG("adding library %s (%s)\n", path, name);
72 }
73
74 int lib_open(char **fullpath, const char *file)
75 {
76         struct library_path *p;
77         char path[PATH_MAX];
78         int fd = -1;
79
80         *fullpath = NULL;
81
82         list_for_each_entry(p, &library_paths, list) {
83                 snprintf(path, sizeof(path), "%s/%s", p->path, file);
84                 fd = open(path, O_RDONLY|O_CLOEXEC);
85                 if (fd >= 0) {
86                         *fullpath = strdup(path);
87                         break;
88                 }
89         }
90
91         return fd;
92 }
93
94 const char* find_lib(const char *file)
95 {
96         struct library *l;
97
98         l = avl_find_element(&libraries, file, l, avl);
99         if (!l)
100                 return NULL;
101
102         return l->path;
103 }
104
105 static int elf64_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
106 {
107         Elf64_Ehdr *e;
108         Elf64_Phdr *ph;
109         int i;
110
111         e = (Elf64_Ehdr *) map;
112         ph = (Elf64_Phdr *) (map + e->e_phoff);
113
114         for (i = 0; i < e->e_phnum; i++) {
115                 if (ph[i].p_type == type) {
116                         *offset = ph[i].p_offset;
117                         if (size)
118                                 *size = ph[i].p_filesz;
119                         if (vaddr)
120                                 *vaddr = ph[i].p_vaddr;
121                         return 0;
122                 }
123         }
124
125         return -1;
126 }
127
128 static int elf32_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
129 {
130         Elf32_Ehdr *e;
131         Elf32_Phdr *ph;
132         int i;
133
134         e = (Elf32_Ehdr *) map;
135         ph = (Elf32_Phdr *) (map + e->e_phoff);
136
137         for (i = 0; i < e->e_phnum; i++) {
138                 if (ph[i].p_type == type) {
139                         *offset = ph[i].p_offset;
140                         if (size)
141                                 *size = ph[i].p_filesz;
142                         if (vaddr)
143                                 *vaddr = ph[i].p_vaddr;
144                         return 0;
145                 }
146         }
147
148         return -1;
149 }
150
151 static int elf_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
152 {
153         int clazz = map[EI_CLASS];
154
155         if (clazz == ELFCLASS32)
156                 return elf32_find_section(map, type, offset, size, vaddr);
157         else if (clazz == ELFCLASS64)
158                 return elf64_find_section(map, type, offset, size, vaddr);
159
160         ERROR("unknown elf format %d\n", clazz);
161
162         return -1;
163 }
164
165 static int elf32_scan_dynamic(const char *map, int dyn_offset, int dyn_size, int load_offset)
166 {
167         Elf32_Dyn *dynamic = (Elf32_Dyn *) (map + dyn_offset);
168         const char *strtab = NULL;
169
170         while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
171                 Elf32_Dyn *curr = dynamic;
172
173                 dynamic++;
174                 if (curr->d_tag != DT_STRTAB)
175                         continue;
176
177                 strtab = map + (curr->d_un.d_ptr - load_offset);
178                 break;
179         }
180
181         if (!strtab)
182                 return -1;
183
184         dynamic = (Elf32_Dyn *) (map + dyn_offset);
185         while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
186                 Elf32_Dyn *curr = dynamic;
187
188                 dynamic++;
189                 if (curr->d_tag != DT_NEEDED)
190                         continue;
191
192                 if (add_path_and_deps(&strtab[curr->d_un.d_val], 1, -1, 1))
193                         return -1;
194         }
195
196         return 0;
197 }
198
199 static int elf64_scan_dynamic(const char *map, int dyn_offset, int dyn_size, int load_offset)
200 {
201         Elf64_Dyn *dynamic = (Elf64_Dyn *) (map + dyn_offset);
202         const char *strtab = NULL;
203
204         while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
205                 Elf64_Dyn *curr = dynamic;
206
207                 dynamic++;
208                 if (curr->d_tag != DT_STRTAB)
209                         continue;
210
211                 strtab = map + (curr->d_un.d_ptr - load_offset);
212                 break;
213         }
214
215         if (!strtab)
216                 return -1;
217
218         dynamic = (Elf64_Dyn *) (map + dyn_offset);
219         while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
220                 Elf64_Dyn *curr = dynamic;
221
222                 dynamic++;
223                 if (curr->d_tag != DT_NEEDED)
224                         continue;
225
226                 if (add_path_and_deps(&strtab[curr->d_un.d_val], 1, -1, 1))
227                         return -1;
228         }
229
230         return 0;
231 }
232
233 int elf_load_deps(const char *path, const char *map)
234 {
235         unsigned int dyn_offset, dyn_size;
236         unsigned int load_offset, load_vaddr;
237         unsigned int interp_offset;
238
239         if (elf_find_section(map, PT_LOAD, &load_offset, NULL, &load_vaddr)) {
240                 ERROR("failed to load the .load section from %s\n", path);
241                 return -1;
242         }
243
244         if (elf_find_section(map, PT_DYNAMIC, &dyn_offset, &dyn_size, NULL)) {
245                 ERROR("failed to load the .dynamic section from %s\n", path);
246                 return -1;
247         }
248
249         if (elf_find_section(map, PT_INTERP, &interp_offset, NULL, NULL) == 0) {
250                 add_path_and_deps(map+interp_offset, 1, -1, 0);
251         }
252
253         int clazz = map[EI_CLASS];
254
255         if (clazz == ELFCLASS32)
256                 return elf32_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
257         else if (clazz == ELFCLASS64)
258                 return elf64_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
259
260         ERROR("unknown elf format %d\n", clazz);
261         return -1;
262 }
263
264 static void load_ldso_conf(const char *conf)
265 {
266         FILE* fp = fopen(conf, "r");
267         char line[PATH_MAX];
268
269         if (!fp) {
270                 DEBUG("failed to open %s\n", conf);
271                 return;
272         }
273
274         while (!feof(fp)) {
275                 int len;
276
277                 if (!fgets(line, sizeof(line), fp))
278                         break;
279                 len = strlen(line);
280                 if (len < 2)
281                         continue;
282                 if (*line == '#')
283                         continue;
284                 if (line[len - 1] == '\n')
285                         line[len - 1] = '\0';
286                 if (!strncmp(line, "include ", 8)) {
287                         char *sep = strstr(line, " ");
288                         glob_t gl;
289                         int i;
290
291                         if (!sep)
292                                 continue;;
293                         while (*sep == ' ')
294                                 sep++;
295                         if (glob(sep, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) {
296                                 ERROR("glob failed on %s\n", sep);
297                                 continue;
298                         }
299                         for (i = 0; i < gl.gl_pathc; i++)
300                                 load_ldso_conf(gl.gl_pathv[i]);
301                         globfree(&gl);
302                 } else {
303                         alloc_library_path(line);
304                 }
305         }
306
307         fclose(fp);
308 }
309
310 void init_library_search(void)
311 {
312         avl_init(&libraries, avl_strcmp, false, NULL);
313         alloc_library_path("/lib");
314         alloc_library_path("/lib64");
315         alloc_library_path("/usr/lib");
316         load_ldso_conf("/etc/ld.so.conf");
317 }