ujail: add <stdio.h> to log.h
[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 #include <sys/mman.h>
16
17 #include <stdlib.h>
18 #include <unistd.h>
19 #include <string.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <libgen.h>
23 #include <glob.h>
24 #include <elf.h>
25
26 #include <libubox/utils.h>
27
28 #include "elf.h"
29
30 struct avl_tree libraries;
31 static LIST_HEAD(library_paths);
32
33 void alloc_library_path(const char *path)
34 {
35         struct stat s;
36         if (stat(path, &s))
37                 return;
38
39         struct library_path *p;
40         char *_path;
41
42         p = calloc_a(sizeof(*p),
43                 &_path, strlen(path) + 1);
44         if (!p)
45                 return;
46
47         p->path = strcpy(_path, path);
48
49         list_add_tail(&p->list, &library_paths);
50         DEBUG("adding ld.so path %s\n", path);
51 }
52
53 static void alloc_library(const char *path, const char *name)
54 {
55         struct library *l;
56         char *_name, *_path;
57
58         l = calloc_a(sizeof(*l),
59                 &_path, strlen(path) + 1,
60                 &_name, strlen(name) + 1);
61         if (!l)
62                 return;
63
64         l->avl.key = l->name = strcpy(_name, name);
65         l->path = strcpy(_path, path);
66
67         avl_insert(&libraries, &l->avl);
68         DEBUG("adding library %s/%s\n", path, name);
69 }
70
71 static int elf_open(char **dir, const char *file)
72 {
73         struct library_path *p;
74         char path[256];
75         int fd = -1;
76
77         *dir = NULL;
78
79         list_for_each_entry(p, &library_paths, list) {
80                 if (strlen(p->path))
81                         snprintf(path, sizeof(path), "%s/%s", p->path, file);
82                 else
83                         strncpy(path, file, sizeof(path));
84                 fd = open(path, O_RDONLY);
85                 if (fd >= 0) {
86                         *dir = p->path;
87                         break;
88                 }
89         }
90
91         if (fd == -1)
92                 fd = open(file, O_RDONLY);
93
94         return fd;
95 }
96
97 const char* find_lib(const char *file)
98 {
99         struct library *l;
100         static char path[256];
101         const char *p;
102
103         l = avl_find_element(&libraries, file, l, avl);
104         if (!l)
105                 return NULL;
106
107         p = l->path;
108         if (strstr(p, "local"))
109                 p = "/lib";
110
111         snprintf(path, sizeof(path), "%s/%s", p, file);
112
113         return path;
114 }
115
116 static int elf64_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
117 {
118         Elf64_Ehdr *e;
119         Elf64_Phdr *ph;
120         int i;
121
122         e = (Elf64_Ehdr *) map;
123         ph = (Elf64_Phdr *) (map + e->e_phoff);
124
125         for (i = 0; i < e->e_phnum; i++) {
126                 if (ph[i].p_type == type) {
127                         *offset = ph[i].p_offset;
128                         if (size)
129                                 *size = ph[i].p_filesz;
130                         if (vaddr)
131                                 *vaddr = ph[i].p_vaddr;
132                         return 0;
133                 }
134         }
135
136         return -1;
137 }
138
139 static int elf32_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
140 {
141         Elf32_Ehdr *e;
142         Elf32_Phdr *ph;
143         int i;
144
145         e = (Elf32_Ehdr *) map;
146         ph = (Elf32_Phdr *) (map + e->e_phoff);
147
148         for (i = 0; i < e->e_phnum; i++) {
149                 if (ph[i].p_type == type) {
150                         *offset = ph[i].p_offset;
151                         if (size)
152                                 *size = ph[i].p_filesz;
153                         if (vaddr)
154                                 *vaddr = ph[i].p_vaddr;
155                         return 0;
156                 }
157         }
158
159         return -1;
160 }
161
162 static int elf_find_section(const char *map, unsigned int type, unsigned int *offset, unsigned int *size, unsigned int *vaddr)
163 {
164         int clazz = map[EI_CLASS];
165
166         if (clazz == ELFCLASS32)
167                 return elf32_find_section(map, type, offset, size, vaddr);
168         else if (clazz == ELFCLASS64)
169                 return elf64_find_section(map, type, offset, size, vaddr);
170
171         ERROR("unknown elf format %d\n", clazz);
172
173         return -1;
174 }
175
176 static int elf32_scan_dynamic(const char *map, int dyn_offset, int dyn_size, int load_offset)
177 {
178         Elf32_Dyn *dynamic = (Elf32_Dyn *) (map + dyn_offset);
179         const char *strtab = NULL;
180
181         while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
182                 Elf32_Dyn *curr = dynamic;
183
184                 dynamic++;
185                 if (curr->d_tag != DT_STRTAB)
186                         continue;
187
188                 strtab = map + (curr->d_un.d_val - load_offset);
189                 break;
190         }
191
192         if (!strtab)
193                 return -1;
194
195         dynamic = (Elf32_Dyn *) (map + dyn_offset);
196         while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
197                 Elf32_Dyn *curr = dynamic;
198
199                 dynamic++;
200                 if (curr->d_tag != DT_NEEDED)
201                         continue;
202
203                 if (elf_load_deps(&strtab[curr->d_un.d_val]))
204                         return -1;
205         }
206
207         return 0;
208 }
209
210 static int elf64_scan_dynamic(const char *map, int dyn_offset, int dyn_size, int load_offset)
211 {
212         Elf64_Dyn *dynamic = (Elf64_Dyn *) (map + dyn_offset);
213         const char *strtab = NULL;
214
215         while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
216                 Elf64_Dyn *curr = dynamic;
217
218                 dynamic++;
219                 if (curr->d_tag != DT_STRTAB)
220                         continue;
221
222                 strtab = map + (curr->d_un.d_val - load_offset);
223                 break;
224         }
225
226         if (!strtab)
227                 return -1;
228
229         dynamic = (Elf64_Dyn *) (map + dyn_offset);
230         while ((void *) dynamic < (void *) (map + dyn_offset + dyn_size)) {
231                 Elf64_Dyn *curr = dynamic;
232
233                 dynamic++;
234                 if (curr->d_tag != DT_NEEDED)
235                         continue;
236
237                 if (elf_load_deps(&strtab[curr->d_un.d_val]))
238                         return -1;
239         }
240
241         return 0;
242 }
243
244 int elf_load_deps(const char *library)
245 {
246         unsigned int dyn_offset, dyn_size;
247         unsigned int load_offset, load_vaddr;
248         struct stat s;
249         char *map = NULL, *dir = NULL;
250         int clazz, fd, ret = -1;
251
252         if (avl_find(&libraries, library))
253                 return 0;
254
255         fd = elf_open(&dir, library);
256
257         if (fd < 0) {
258                 ERROR("failed to open %s\n", library);
259                 return -1;
260         }
261
262         if (fstat(fd, &s) == -1) {
263                 ERROR("failed to stat %s\n", library);
264                 ret = -1;
265                 goto err_out;
266         }
267
268         map = mmap(NULL, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
269         if (map == MAP_FAILED) {
270                 ERROR("failed to mmap %s\n", library);
271                 ret = -1;
272                 goto err_out;
273         }
274
275         if (elf_find_section(map, PT_LOAD, &load_offset, NULL, &load_vaddr)) {
276                 ERROR("failed to load the .load section from %s\n", library);
277                 ret = -1;
278                 goto err_out;
279         }
280
281         if (elf_find_section(map, PT_DYNAMIC, &dyn_offset, &dyn_size, NULL)) {
282                 ERROR("failed to load the .dynamic section from %s\n", library);
283                 ret = -1;
284                 goto err_out;
285         }
286
287         if (dir) {
288                 alloc_library(dir, library);
289         } else {
290                 char *elf1 = strdup(library);
291                 char *elf2 = strdup(library);
292
293                 alloc_library(dirname(elf1), basename(elf2));
294                 free(elf1);
295                 free(elf2);
296         }
297         clazz = map[EI_CLASS];
298
299         if (clazz == ELFCLASS32)
300                 ret = elf32_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
301         else if (clazz == ELFCLASS64)
302                 ret = elf64_scan_dynamic(map, dyn_offset, dyn_size, load_vaddr - load_offset);
303
304 err_out:
305         if (map)
306                 munmap(map, s.st_size);
307         close(fd);
308
309         return ret;
310 }
311
312 void load_ldso_conf(const char *conf)
313 {
314         FILE* fp = fopen(conf, "r");
315         char line[256];
316
317         if (!fp) {
318                 DEBUG("failed to open %s\n", conf);
319                 return;
320         }
321
322         while (!feof(fp)) {
323                 int len;
324
325                 if (!fgets(line, 256, fp))
326                         break;
327                 len = strlen(line);
328                 if (len < 2)
329                         continue;
330                 if (*line == '#')
331                         continue;
332                 if (line[len - 1] == '\n')
333                         line[len - 1] = '\0';
334                 if (!strncmp(line, "include ", 8)) {
335                         char *sep = strstr(line, " ");
336                         glob_t gl;
337                         int i;
338
339                         if (!sep)
340                                 continue;;
341                         while (*sep == ' ')
342                                 sep++;
343                         if (glob(sep, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) {
344                                 ERROR("glob failed on %s\n", sep);
345                                 continue;
346                         }
347                         for (i = 0; i < gl.gl_pathc; i++)
348                                 load_ldso_conf(gl.gl_pathv[i]);
349                         globfree(&gl);
350                 } else {
351                         alloc_library_path(line);
352                 }
353         }
354
355         fclose(fp);
356 }