rumpuser_dl.c revision 1.12 1 /* $NetBSD: rumpuser_dl.c,v 1.12 2013/01/14 21:00:16 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2009 Antti Kantee. All Rights Reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 /*
29 * Load all module link sets and feed symbol table to the kernel.
30 * Called during rump bootstrap.
31 */
32
33 #include "rumpuser_port.h"
34
35 #if !defined(lint)
36 __RCSID("$NetBSD: rumpuser_dl.c,v 1.12 2013/01/14 21:00:16 pooka Exp $");
37 #endif /* !lint */
38
39 #include <sys/types.h>
40 #include <sys/time.h>
41 #include <assert.h>
42
43 #include <dlfcn.h>
44 #include <elf.h>
45 #include <errno.h>
46 #include <fcntl.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #include <rump/rumpuser.h>
53
54 #if defined(__ELF__) && (defined(__NetBSD__) || defined(__FreeBSD__) \
55 || (defined(__sun__) && defined(__svr4__))) || defined(__linux__) \
56 || defined(__DragonFly__)
57 #include <link.h>
58
59 static size_t symtabsize = 0, strtabsize = 0;
60 static size_t symtaboff = 0, strtaboff = 0;
61 static uint8_t *symtab = NULL;
62 static char *strtab = NULL;
63 static unsigned char eident;
64
65 /* nb5 compat */
66 #ifndef Elf_Symindx
67 #define Elf_Symindx uint32_t
68 #endif
69
70 /*
71 * Linux ld.so requires a valid handle for dlinfo(), so use the main
72 * handle. We initialize this variable in rumpuser_dl_bootstrap()
73 */
74 static void *mainhandle;
75
76 static void *
77 reservespace(void *store, size_t *storesize,
78 size_t storeoff, size_t required)
79 {
80 size_t chunk, newsize;
81
82 assert(storeoff <= *storesize);
83 chunk = *storesize - storeoff;
84
85 if (chunk >= required)
86 return store;
87
88 newsize = *storesize + ((size_t)required - chunk);
89 store = realloc(store, newsize);
90 if (store == NULL) {
91 return NULL;
92 }
93 *((uint8_t *)store + storeoff) = '\0';
94 *storesize = newsize;
95
96 return store;
97 }
98
99 /*
100 * Macros to make handling elf32/64 in the code a little saner.
101 */
102
103 #define DYNn_GETMEMBER(base, n, thevar, result) \
104 do { \
105 if (eident == ELFCLASS32) { \
106 const Elf32_Dyn *dyn = base; \
107 /*LINTED*/ \
108 result = dyn[n].thevar; \
109 } else { \
110 const Elf64_Dyn *dyn = base; \
111 /*LINTED*/ \
112 result = dyn[n].thevar; \
113 } \
114 } while (/*CONSTCOND*/0)
115
116 #define SYMn_GETMEMBER(base, n, thevar, result) \
117 do { \
118 if (eident == ELFCLASS32) { \
119 const Elf32_Sym *sym = base; \
120 /*LINTED*/ \
121 result = sym[n].thevar; \
122 } else { \
123 const Elf64_Sym *sym = base; \
124 /*LINTED*/ \
125 result = sym[n].thevar; \
126 } \
127 } while (/*CONSTCOND*/0)
128
129 #define SYMn_SETMEMBER(base, n, thevar, value) \
130 do { \
131 if (eident == ELFCLASS32) { \
132 Elf32_Sym *sym = base; \
133 /*LINTED*/ \
134 sym[n].thevar = value; \
135 } else { \
136 Elf64_Sym *sym = base; \
137 /*LINTED*/ \
138 sym[n].thevar = value; \
139 } \
140 } while (/*CONSTCOND*/0)
141
142 #define SYM_GETSIZE() ((eident==ELFCLASS32)?sizeof(Elf32_Sym):sizeof(Elf64_Sym))
143
144 /*
145 * On NetBSD, the dynamic section pointer values seem to be relative to
146 * the address the dso is mapped at. On Linux, they seem to contain
147 * the absolute address. I couldn't find anything definite from a quick
148 * read of the standard and therefore I will not go and figure beyond ifdef.
149 */
150 #ifdef __linux__
151 #define adjptr(_map_, _ptr_) ((void *)(_ptr_))
152 #else
153 #define adjptr(_map_, _ptr_) ((void *)(_map_->l_addr + (_ptr_)))
154 #endif
155
156 static int
157 getsymbols(struct link_map *map)
158 {
159 char *str_base;
160 void *syms_base = NULL; /* XXXgcc */
161 size_t curstrsize;
162 const void *ed_base;
163 uint64_t ed_tag;
164 size_t cursymcount;
165 unsigned i;
166
167 if (map->l_addr) {
168 if (memcmp((void *)map->l_addr, ELFMAG, SELFMAG) != 0)
169 return ENOEXEC;
170 eident = *(unsigned char *)(map->l_addr + EI_CLASS);
171 if (eident != ELFCLASS32 && eident != ELFCLASS64)
172 return ENOEXEC;
173 }
174
175 /*
176 * ok, we probably have only the main object. instead of going
177 * to disk and reading the ehdr, just try to guess the size.
178 */
179 if (eident == 0) {
180 if (/*CONSTCOND*/sizeof(void *) == 4)
181 eident = ELFCLASS32;
182 else
183 eident = ELFCLASS64;
184 }
185
186 /*
187 * Find symtab and strtab and their sizes.
188 */
189 str_base = NULL;
190 curstrsize = 0;
191 cursymcount = 0;
192 ed_base = map->l_ld;
193 DYNn_GETMEMBER(ed_base, 0, d_tag, ed_tag);
194 for (i = 0; ed_tag != DT_NULL;) {
195 uintptr_t edptr;
196 size_t edval;
197 Elf_Symindx *hashtab;
198
199 switch (ed_tag) {
200 case DT_SYMTAB:
201 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
202 syms_base = adjptr(map, edptr);
203 break;
204 case DT_STRTAB:
205 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
206 str_base = adjptr(map, edptr);
207 break;
208 case DT_STRSZ:
209 DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval);
210 curstrsize = edval;
211 break;
212 case DT_HASH:
213 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
214 hashtab = (Elf_Symindx *)adjptr(map, edptr);
215 cursymcount = hashtab[1];
216 break;
217 #ifdef DT_GNU_HASH
218 /*
219 * DT_GNU_HASH is a bit more complicated than DT_HASH
220 * in this regard since apparently there is no field
221 * telling us the total symbol count. Instead, we look
222 * for the last valid hash bucket and add its chain lenght
223 * to the bucket's base index.
224 */
225 case DT_GNU_HASH: {
226 Elf32_Word nbuck, symndx, maskwords, maxchain = 0;
227 Elf32_Word *gnuhash, *buckets, *ptr;
228 int bi;
229
230 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
231 gnuhash = (Elf32_Word *)adjptr(map, edptr);
232
233 nbuck = gnuhash[0];
234 symndx = gnuhash[1];
235 maskwords = gnuhash[2];
236
237 /*
238 * First, find the last valid bucket and grab its index
239 */
240 if (eident == ELFCLASS64)
241 maskwords *= 2; /* sizeof(*buckets) == 4 */
242 buckets = gnuhash + 4 + maskwords;
243 for (bi = nbuck-1; bi >= 0; bi--) {
244 if (buckets[bi] != 0) {
245 maxchain = buckets[bi];
246 break;
247 }
248 }
249 if (maxchain == 0 || maxchain < symndx)
250 break;
251
252 /*
253 * Then, traverse the last chain and count symbols.
254 */
255
256 cursymcount = maxchain;
257 ptr = buckets + nbuck + (maxchain - symndx);
258 do {
259 cursymcount++;
260 } while ((*ptr++ & 1) == 0);
261 }
262 break;
263 #endif
264 case DT_SYMENT:
265 DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval);
266 assert(edval == SYM_GETSIZE());
267 break;
268 default:
269 break;
270 }
271 i++;
272 DYNn_GETMEMBER(ed_base, i, d_tag, ed_tag);
273 }
274
275 if (str_base == NULL || syms_base == NULL ||
276 curstrsize == 0 || cursymcount == 0) {
277 fprintf(stderr, "could not find strtab, symtab or their sizes "
278 "in %s\n", map->l_name);
279 return ENOEXEC;
280 }
281
282 /*
283 * Make sure we have enough space for the contents of the symbol
284 * and string tables we are currently processing. The total used
285 * space will be smaller due to undefined symbols we are not
286 * interested in.
287 */
288 symtab = reservespace(symtab, &symtabsize,
289 symtaboff, cursymcount * SYM_GETSIZE());
290 strtab = reservespace(strtab, &strtabsize, strtaboff, curstrsize);
291 if (symtab == NULL || strtab == NULL) {
292 fprintf(stderr, "failed to reserve memory");
293 return ENOMEM;
294 }
295
296 /* iterate over all symbols in current symtab */
297 for (i = 0; i < cursymcount; i++) {
298 const char *cursymname;
299 int shndx, name;
300 uintptr_t value;
301 void *csym;
302
303 SYMn_GETMEMBER(syms_base, i, st_shndx, shndx);
304 SYMn_GETMEMBER(syms_base, i, st_value, value);
305 if (shndx == SHN_UNDEF || value == 0)
306 continue;
307
308 /* get symbol name */
309 SYMn_GETMEMBER(syms_base, i, st_name, name);
310 cursymname = name + str_base;
311
312 /*
313 * Only accept symbols which are decidedly in
314 * the rump kernel namespace.
315 * XXX: quirks, but they wouldn't matter here
316 */
317 if (strncmp(cursymname, "rump", 4) != 0 &&
318 strncmp(cursymname, "RUMP", 4) != 0 &&
319 strncmp(cursymname, "__", 2) != 0) {
320 continue;
321 }
322
323 memcpy(symtab + symtaboff,
324 (const uint8_t *)syms_base + i*SYM_GETSIZE(),SYM_GETSIZE());
325
326 /*
327 * set name to point at new strtab, offset symbol value
328 * with lib base address.
329 */
330 csym = symtab + symtaboff;
331 SYMn_SETMEMBER(csym, 0, st_name, strtaboff);
332 SYMn_GETMEMBER(csym, 0, st_value, value);
333 SYMn_SETMEMBER(csym, 0, st_value,(intptr_t)(value+map->l_addr));
334 symtaboff += SYM_GETSIZE();
335
336 strcpy(strtab + strtaboff, cursymname);
337 strtaboff += strlen(cursymname)+1;
338 }
339
340 return 0;
341 }
342
343 static void
344 process(const char *soname, rump_modinit_fn domodinit)
345 {
346 void *handle;
347 const struct modinfo *const *mi_start, *const *mi_end;
348
349 if (strstr(soname, "librump") == NULL)
350 return;
351
352 handle = dlopen(soname, RTLD_LAZY);
353 if (handle == NULL)
354 return;
355
356 mi_start = dlsym(handle, "__start_link_set_modules");
357 if (!mi_start)
358 goto out;
359 mi_end = dlsym(handle, "__stop_link_set_modules");
360 if (!mi_end)
361 goto out;
362
363 domodinit(mi_start, (size_t)(mi_end-mi_start));
364
365 out:
366 dlclose(handle);
367 }
368
369 /*
370 * Get the linkmap from the dynlinker. Try to load kernel modules
371 * from all objects in the linkmap.
372 */
373 void
374 rumpuser_dl_bootstrap(rump_modinit_fn domodinit,
375 rump_symload_fn symload)
376 {
377 struct link_map *map, *origmap;
378 int error;
379
380 mainhandle = dlopen(NULL, RTLD_NOW);
381 if (dlinfo(mainhandle, RTLD_DI_LINKMAP, &origmap) == -1) {
382 fprintf(stderr, "warning: rumpuser module bootstrap "
383 "failed: %s\n", dlerror());
384 return;
385 }
386 /*
387 * Process last->first because that's the most probable
388 * order for dependencies
389 */
390 for (; origmap->l_next; origmap = origmap->l_next)
391 continue;
392
393 /*
394 * Build symbol table to hand to the rump kernel. Do this by
395 * iterating over all rump libraries and collecting symbol
396 * addresses and relocation info.
397 */
398 error = 0;
399 for (map = origmap; map && !error; map = map->l_prev) {
400 if (strstr(map->l_name, "librump") != NULL)
401 error = getsymbols(map);
402 /* this should be the main object */
403 else if (!map->l_addr && map->l_prev == NULL)
404 error = getsymbols(map);
405 }
406
407 if (error == 0) {
408 void *trimmedsym, *trimmedstr;
409
410 /*
411 * Allocate optimum-sized memory for storing tables
412 * and feed to kernel. If memory allocation fails,
413 * just give the ones with extra context (although
414 * I'm pretty sure we'll die moments later due to
415 * memory running out).
416 */
417 if ((trimmedsym = malloc(symtaboff)) != NULL) {
418 memcpy(trimmedsym, symtab, symtaboff);
419 } else {
420 trimmedsym = symtab;
421 symtab = NULL;
422 }
423 if ((trimmedstr = malloc(strtaboff)) != NULL) {
424 memcpy(trimmedstr, strtab, strtaboff);
425 } else {
426 trimmedstr = strtab;
427 strtab = NULL;
428 }
429 symload(trimmedsym, symtaboff, trimmedstr, strtaboff);
430 }
431 free(symtab);
432 free(strtab);
433
434 /*
435 * Next, load modules from dynlibs.
436 */
437 for (map = origmap; map; map = map->l_prev)
438 process(map->l_name, domodinit);
439 }
440
441 void
442 rumpuser_dl_component_init(int type, rump_component_init_fn compinit)
443 {
444 struct link_map *map;
445
446 if (dlinfo(mainhandle, RTLD_DI_LINKMAP, &map) == -1) {
447 fprintf(stderr, "warning: rumpuser module bootstrap "
448 "failed: %s\n", dlerror());
449 return;
450 }
451
452 for (; map->l_next; map = map->l_next)
453 continue;
454 for (; map; map = map->l_prev) {
455 if (strstr(map->l_name, "librump") != NULL) {
456 void *handle;
457 struct rump_component **rc, **rc_end;
458
459 handle = dlopen(map->l_name, RTLD_LAZY);
460 if (handle == NULL)
461 continue;
462
463 rc = dlsym(handle,
464 "__start_link_set_rump_components");
465 if (!rc)
466 goto loop;
467 rc_end = dlsym(handle,
468 "__stop_link_set_rump_components");
469 if (!rc_end)
470 goto loop;
471
472 for (; rc < rc_end; rc++)
473 compinit(*rc, type);
474 assert(rc == rc_end);
475 loop:
476 dlclose(handle);
477 }
478 }
479 }
480 #else
481 void
482 rumpuser_dl_bootstrap(rump_modinit_fn domodinit,
483 rump_symload_fn symload)
484 {
485
486 fprintf(stderr, "Warning, dlinfo() unsupported on host?\n");
487 }
488
489 /*
490 * "default" implementation for platforms where we don't support
491 * dynamic linking. Assumes that all rump kernel components are
492 * statically linked with the local client.
493 */
494
495 extern void *__start_link_set_rump_components;
496 extern void *__stop_link_set_rump_components;
497 void
498 rumpuser_dl_component_init(int type, rump_component_init_fn compinit)
499 {
500 void **rc = &__start_link_set_rump_components;
501 void **rc_end = &__stop_link_set_rump_components;
502
503 for (; rc < rc_end; rc++)
504 compinit(*rc, type);
505
506 }
507 #endif
508
509 void *
510 rumpuser_dl_globalsym(const char *symname)
511 {
512
513 return dlsym(RTLD_DEFAULT, symname);
514 }
515