rumpuser_dl.c revision 1.7 1 /* $NetBSD: rumpuser_dl.c,v 1.7 2011/03/22 22:27:33 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 <sys/cdefs.h>
34 __RCSID("$NetBSD: rumpuser_dl.c,v 1.7 2011/03/22 22:27:33 pooka Exp $");
35
36 #include <sys/types.h>
37 #include <sys/time.h>
38
39 #include <assert.h>
40 #include <dlfcn.h>
41 #include <elf.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <link.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include <rump/rumpuser.h>
51
52 #if defined(__ELF__) && (defined(__NetBSD__) || defined(__FreeBSD__) \
53 || (defined(__sun__) && defined(__svr4__)))
54 static size_t symtabsize = 0, strtabsize = 0;
55 static size_t symtaboff = 0, strtaboff = 0;
56 static uint8_t *symtab = NULL;
57 static char *strtab = NULL;
58 static unsigned char eident;
59
60 /* nb5 compat */
61 #ifndef Elf_Symindx
62 #define Elf_Symindx uint32_t
63 #endif
64
65 static void *
66 reservespace(void *store, size_t *storesize,
67 size_t storeoff, size_t required)
68 {
69 size_t chunk, newsize;
70
71 assert(storeoff <= *storesize);
72 chunk = *storesize - storeoff;
73
74 if (chunk >= required)
75 return store;
76
77 newsize = *storesize + ((size_t)required - chunk);
78 store = realloc(store, newsize);
79 if (store == NULL) {
80 return NULL;
81 }
82 *((uint8_t *)store + storeoff) = '\0';
83 *storesize = newsize;
84
85 return store;
86 }
87
88 /*
89 * Macros to make handling elf32/64 in the code a little saner.
90 */
91
92 #define DYNn_GETMEMBER(base, n, thevar, result) \
93 do { \
94 if (eident == ELFCLASS32) { \
95 Elf32_Dyn *dyn = base; \
96 /*LINTED*/ \
97 result = dyn[n].thevar; \
98 } else { \
99 Elf64_Dyn *dyn = base; \
100 /*LINTED*/ \
101 result = dyn[n].thevar; \
102 } \
103 } while (/*CONSTCOND*/0)
104
105 #define SYMn_GETMEMBER(base, n, thevar, result) \
106 do { \
107 if (eident == ELFCLASS32) { \
108 const Elf32_Sym *sym = base; \
109 /*LINTED*/ \
110 result = sym[n].thevar; \
111 } else { \
112 const Elf64_Sym *sym = base; \
113 /*LINTED*/ \
114 result = sym[n].thevar; \
115 } \
116 } while (/*CONSTCOND*/0)
117
118 #define SYMn_SETMEMBER(base, n, thevar, value) \
119 do { \
120 if (eident == ELFCLASS32) { \
121 Elf32_Sym *sym = base; \
122 /*LINTED*/ \
123 sym[n].thevar = value; \
124 } else { \
125 Elf64_Sym *sym = base; \
126 /*LINTED*/ \
127 sym[n].thevar = value; \
128 } \
129 } while (/*CONSTCOND*/0)
130
131 #define SYM_GETSIZE() ((eident==ELFCLASS32)?sizeof(Elf32_Sym):sizeof(Elf64_Sym))
132
133 static int
134 getsymbols(struct link_map *map)
135 {
136 char *str_base;
137 void *syms_base = NULL; /* XXXgcc */
138 size_t curstrsize;
139 void *ed_base;
140 uint64_t ed_tag;
141 size_t cursymcount;
142 unsigned i;
143
144 if (map->l_addr) {
145 if (memcmp(map->l_addr, ELFMAG, SELFMAG) != 0)
146 return ENOEXEC;
147 eident = *(unsigned char *)(map->l_addr + EI_CLASS);
148 if (eident != ELFCLASS32 && eident != ELFCLASS64)
149 return ENOEXEC;
150 }
151
152 /*
153 * ok, we probably have only the main object. instead of going
154 * to disk and reading the ehdr, just try to guess the size.
155 */
156 if (eident == 0) {
157 if (/*CONSTCOND*/sizeof(void *) == 4)
158 eident = ELFCLASS32;
159 else
160 eident = ELFCLASS64;
161 }
162
163 /*
164 * Find symtab and strtab and their sizes.
165 */
166 str_base = NULL;
167 curstrsize = 0;
168 cursymcount = 0;
169 ed_base = map->l_ld;
170 DYNn_GETMEMBER(ed_base, 0, d_tag, ed_tag);
171 for (i = 0; ed_tag != DT_NULL;) {
172 uintptr_t edptr;
173 size_t edval;
174 Elf_Symindx *hashtab;
175
176 switch (ed_tag) {
177 case DT_SYMTAB:
178 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
179 syms_base = map->l_addr + edptr;
180 break;
181 case DT_STRTAB:
182 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
183 str_base = map->l_addr + edptr;
184 break;
185 case DT_STRSZ:
186 DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval);
187 curstrsize = edval;
188 break;
189 case DT_HASH:
190 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
191 hashtab = (Elf_Symindx *)(map->l_addr + edptr);
192 cursymcount = hashtab[1];
193 break;
194 case DT_SYMENT:
195 DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval);
196 assert(edval == SYM_GETSIZE());
197 break;
198 default:
199 break;
200 }
201 i++;
202 DYNn_GETMEMBER(ed_base, i, d_tag, ed_tag);
203 }
204
205 if (str_base == NULL || syms_base == NULL ||
206 curstrsize == 0 || cursymcount == 0) {
207 fprintf(stderr, "could not find strtab, symtab or their sizes "
208 "in %s\n", map->l_name);
209 return ENOEXEC;
210 }
211
212 /*
213 * Make sure we have enough space for the contents of the symbol
214 * and string tables we are currently processing. The total used
215 * space will be smaller due to undefined symbols we are not
216 * interested in.
217 */
218 symtab = reservespace(symtab, &symtabsize,
219 symtaboff, cursymcount * SYM_GETSIZE());
220 strtab = reservespace(strtab, &strtabsize, strtaboff, curstrsize);
221 if (symtab == NULL || strtab == NULL) {
222 fprintf(stderr, "failed to reserve memory");
223 return ENOMEM;
224 }
225
226 /* iterate over all symbols in current symtab */
227 for (i = 0; i < cursymcount; i++) {
228 const char *cursymname;
229 int shndx, name;
230 uintptr_t value;
231 void *csym;
232
233 SYMn_GETMEMBER(syms_base, i, st_shndx, shndx);
234 SYMn_GETMEMBER(syms_base, i, st_value, value);
235 if (shndx == SHN_UNDEF || value == 0)
236 continue;
237
238 /* get symbol name */
239 SYMn_GETMEMBER(syms_base, i, st_name, name);
240 cursymname = name + str_base;
241
242 /*
243 * Only accept symbols which are decidedly in
244 * the rump kernel namespace.
245 * XXX: quirks, but they wouldn't matter here
246 */
247 if (strncmp(cursymname, "rump", 4) != 0 &&
248 strncmp(cursymname, "RUMP", 4) != 0 &&
249 strncmp(cursymname, "__", 2) != 0) {
250 continue;
251 }
252
253 memcpy(symtab + symtaboff,
254 (const uint8_t *)syms_base + i*SYM_GETSIZE(),SYM_GETSIZE());
255
256 /*
257 * set name to point at new strtab, offset symbol value
258 * with lib base address.
259 */
260 csym = symtab + symtaboff;
261 SYMn_SETMEMBER(csym, 0, st_name, strtaboff);
262 SYMn_GETMEMBER(csym, 0, st_value, value);
263 SYMn_SETMEMBER(csym, 0, st_value,(intptr_t)(value+map->l_addr));
264 symtaboff += SYM_GETSIZE();
265
266 strcpy(strtab + strtaboff, cursymname);
267 strtaboff += strlen(cursymname)+1;
268 }
269
270 return 0;
271 }
272
273 static void
274 process(const char *soname, rump_modinit_fn domodinit)
275 {
276 void *handle;
277 const struct modinfo *const *mi_start, *const *mi_end;
278
279 if (strstr(soname, "librump") == NULL)
280 return;
281
282 handle = dlopen(soname, RTLD_LAZY);
283 if (handle == NULL)
284 return;
285
286 mi_start = dlsym(handle, "__start_link_set_modules");
287 if (!mi_start)
288 goto out;
289 mi_end = dlsym(handle, "__stop_link_set_modules");
290 if (!mi_end)
291 goto out;
292
293 domodinit(mi_start, (size_t)(mi_end-mi_start));
294
295 out:
296 dlclose(handle);
297 }
298
299 /*
300 * Get the linkmap from the dynlinker. Try to load kernel modules
301 * from all objects in the linkmap.
302 */
303 void
304 rumpuser_dl_bootstrap(rump_modinit_fn domodinit,
305 rump_symload_fn symload)
306 {
307 struct link_map *map, *origmap;
308 int error;
309
310 if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &origmap) == -1) {
311 fprintf(stderr, "warning: rumpuser module bootstrap "
312 "failed: %s\n", dlerror());
313 return;
314 }
315 /*
316 * Process last->first because that's the most probable
317 * order for dependencies
318 */
319 for (; origmap->l_next; origmap = origmap->l_next)
320 continue;
321
322 /*
323 * Build symbol table to hand to the rump kernel. Do this by
324 * iterating over all rump libraries and collecting symbol
325 * addresses and relocation info.
326 */
327 error = 0;
328 for (map = origmap; map && !error; map = map->l_prev) {
329 if (strstr(map->l_name, "librump") != NULL)
330 error = getsymbols(map);
331 /* this should be the main object */
332 else if (map->l_addr == NULL && map->l_prev == NULL)
333 error = getsymbols(map);
334 }
335
336 if (error == 0) {
337 void *trimmedsym, *trimmedstr;
338
339 /*
340 * Allocate optimum-sized memory for storing tables
341 * and feed to kernel. If memory allocation fails,
342 * just give the ones with extra context (although
343 * I'm pretty sure we'll die moments later due to
344 * memory running out).
345 */
346 if ((trimmedsym = malloc(symtaboff)) != NULL) {
347 memcpy(trimmedsym, symtab, symtaboff);
348 } else {
349 trimmedsym = symtab;
350 symtab = NULL;
351 }
352 if ((trimmedstr = malloc(strtaboff)) != NULL) {
353 memcpy(trimmedstr, strtab, strtaboff);
354 } else {
355 trimmedstr = strtab;
356 strtab = NULL;
357 }
358 symload(trimmedsym, symtaboff, trimmedstr, strtaboff);
359 }
360 free(symtab);
361 free(strtab);
362
363 /*
364 * Next, load modules from dynlibs.
365 */
366 for (map = origmap; map; map = map->l_prev)
367 process(map->l_name, domodinit);
368 }
369
370 void
371 rumpuser_dl_component_init(int type, rump_component_init_fn compinit)
372 {
373 struct link_map *map;
374
375 if (dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map) == -1) {
376 fprintf(stderr, "warning: rumpuser module bootstrap "
377 "failed: %s\n", dlerror());
378 return;
379 }
380
381 for (; map->l_next; map = map->l_next)
382 continue;
383 for (; map; map = map->l_prev) {
384 if (strstr(map->l_name, "librump") != NULL) {
385 void *handle;
386 struct rump_component **rc, **rc_end;
387
388 handle = dlopen(map->l_name, RTLD_LAZY);
389 if (handle == NULL)
390 continue;
391
392 rc = dlsym(handle,
393 "__start_link_set_rump_components");
394 if (!rc)
395 goto loop;
396 rc_end = dlsym(handle,
397 "__stop_link_set_rump_components");
398 if (!rc_end)
399 goto loop;
400
401 for (; rc < rc_end; rc++)
402 compinit(*rc, type);
403 assert(rc == rc_end);
404 loop:
405 dlclose(handle);
406 }
407 }
408 }
409 #else
410 void
411 rumpuser_dl_bootstrap(rump_modinit_fn domodinit,
412 rump_symload_fn symload)
413 {
414
415 fprintf(stderr, "Warning, dlinfo() unsupported on host?\n");
416 }
417
418 void
419 rumpuser_dl_component_init(int type, rump_component_init_fn compinit)
420 {
421
422 fprintf(stderr, "Warning, dlinfo() unsupported on host?\n");
423 }
424 #endif
425
426 void *
427 rumpuser_dl_globalsym(const char *symname)
428 {
429
430 return dlsym(RTLD_DEFAULT, symname);
431 }
432