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