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