1 /* $NetBSD: rumpuser_dl.c,v 1.19 2013/06/04 15:17:28 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 /* 34 * Solaris libelf.h doesn't support _FILE_OFFSET_BITS=64. Luckily, 35 * for this module it doesn't matter. 36 */ 37 #if defined(__sun__) 38 #define RUMPUSER_NO_FILE_OFFSET_BITS 39 #endif 40 #include "rumpuser_port.h" 41 42 #if !defined(lint) 43 __RCSID("$NetBSD: rumpuser_dl.c,v 1.19 2013/06/04 15:17:28 pooka Exp $"); 44 #endif /* !lint */ 45 46 #include <sys/types.h> 47 #include <sys/time.h> 48 #include <assert.h> 49 50 #include <dlfcn.h> 51 #include <elf.h> 52 #include <errno.h> 53 #include <fcntl.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <unistd.h> 58 59 #include <rump/rumpuser.h> 60 61 #if defined(__ELF__) && (defined(__NetBSD__) || defined(__FreeBSD__) \ 62 || (defined(__sun__) && defined(__svr4__))) || defined(__linux__) \ 63 || defined(__DragonFly__) 64 #include <link.h> 65 66 static size_t symtabsize = 0, strtabsize = 0; 67 static size_t symtaboff = 0, strtaboff = 0; 68 static uint8_t *symtab = NULL; 69 static char *strtab = NULL; 70 static unsigned char eident; 71 72 /* nb5 compat */ 73 #ifndef Elf_Symindx 74 #define Elf_Symindx uint32_t 75 #endif 76 77 static void * 78 reservespace(void *store, size_t *storesize, 79 size_t storeoff, size_t required) 80 { 81 size_t chunk, newsize; 82 83 assert(storeoff <= *storesize); 84 chunk = *storesize - storeoff; 85 86 if (chunk >= required) 87 return store; 88 89 newsize = *storesize + ((size_t)required - chunk); 90 store = realloc(store, newsize); 91 if (store == NULL) { 92 return NULL; 93 } 94 *((uint8_t *)store + storeoff) = '\0'; 95 *storesize = newsize; 96 97 return store; 98 } 99 100 /* 101 * Macros to make handling elf32/64 in the code a little saner. 102 */ 103 104 #define DYNn_GETMEMBER(base, n, thevar, result) \ 105 do { \ 106 if (eident == ELFCLASS32) { \ 107 const Elf32_Dyn *dyn = base; \ 108 /*LINTED*/ \ 109 result = dyn[n].thevar; \ 110 } else { \ 111 const Elf64_Dyn *dyn = base; \ 112 /*LINTED*/ \ 113 result = dyn[n].thevar; \ 114 } \ 115 } while (/*CONSTCOND*/0) 116 117 #define SYMn_GETMEMBER(base, n, thevar, result) \ 118 do { \ 119 if (eident == ELFCLASS32) { \ 120 const Elf32_Sym *sym = base; \ 121 /*LINTED*/ \ 122 result = sym[n].thevar; \ 123 } else { \ 124 const Elf64_Sym *sym = base; \ 125 /*LINTED*/ \ 126 result = sym[n].thevar; \ 127 } \ 128 } while (/*CONSTCOND*/0) 129 130 #define SYMn_SETMEMBER(base, n, thevar, value) \ 131 do { \ 132 if (eident == ELFCLASS32) { \ 133 Elf32_Sym *sym = base; \ 134 /*LINTED*/ \ 135 sym[n].thevar = value; \ 136 } else { \ 137 Elf64_Sym *sym = base; \ 138 /*LINTED*/ \ 139 sym[n].thevar = value; \ 140 } \ 141 } while (/*CONSTCOND*/0) 142 143 #define SYM_GETSIZE() ((eident==ELFCLASS32)?sizeof(Elf32_Sym):sizeof(Elf64_Sym)) 144 145 /* 146 * On NetBSD, the dynamic section pointer values seem to be relative to 147 * the address the dso is mapped at. On Linux, they seem to contain 148 * the absolute address. I couldn't find anything definite from a quick 149 * read of the standard and therefore I will not go and figure beyond ifdef. 150 * On Solaris and DragonFly, the main object works differently ... uuuuh. 151 */ 152 #if defined(__linux__) 153 #define adjptr(_map_, _ptr_) ((void *)(_ptr_)) 154 #elif defined(__sun__) || defined(__DragonFly__) || defined(__FreeBSD__) 155 #define adjptr(_map_, _ptr_) \ 156 (ismainobj ? (void *)(_ptr_) : (void *)(_map_->l_addr + (_ptr_))) 157 #else 158 #define adjptr(_map_, _ptr_) ((void *)(_map_->l_addr + (_ptr_))) 159 #endif 160 161 static int 162 getsymbols(struct link_map *map, int ismainobj) 163 { 164 char *str_base; 165 void *syms_base = NULL; /* XXXgcc */ 166 size_t curstrsize; 167 const void *ed_base; 168 uint64_t ed_tag; 169 size_t cursymcount; 170 unsigned i; 171 172 if (map->l_addr) { 173 if (memcmp((void *)map->l_addr, ELFMAG, SELFMAG) != 0) 174 return ENOEXEC; 175 eident = *(unsigned char *)(map->l_addr + EI_CLASS); 176 if (eident != ELFCLASS32 && eident != ELFCLASS64) 177 return ENOEXEC; 178 } 179 180 /* 181 * ok, we probably have only the main object. instead of going 182 * to disk and reading the ehdr, just try to guess the size. 183 */ 184 if (eident == 0) { 185 if (/*CONSTCOND*/sizeof(void *) == 4) 186 eident = ELFCLASS32; 187 else 188 eident = ELFCLASS64; 189 } 190 191 /* 192 * Find symtab and strtab and their sizes. 193 */ 194 str_base = NULL; 195 curstrsize = 0; 196 cursymcount = 0; 197 ed_base = map->l_ld; 198 DYNn_GETMEMBER(ed_base, 0, d_tag, ed_tag); 199 for (i = 0; ed_tag != DT_NULL;) { 200 uintptr_t edptr; 201 size_t edval; 202 Elf_Symindx *hashtab; 203 204 switch (ed_tag) { 205 case DT_SYMTAB: 206 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr); 207 syms_base = adjptr(map, edptr); 208 break; 209 case DT_STRTAB: 210 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr); 211 str_base = adjptr(map, edptr); 212 break; 213 case DT_STRSZ: 214 DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval); 215 curstrsize = edval; 216 break; 217 case DT_HASH: 218 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr); 219 hashtab = (Elf_Symindx *)adjptr(map, edptr); 220 cursymcount = hashtab[1]; 221 break; 222 #ifdef DT_GNU_HASH 223 /* 224 * DT_GNU_HASH is a bit more complicated than DT_HASH 225 * in this regard since apparently there is no field 226 * telling us the total symbol count. Instead, we look 227 * for the last valid hash bucket and add its chain lenght 228 * to the bucket's base index. 229 */ 230 case DT_GNU_HASH: { 231 Elf32_Word nbuck, symndx, maskwords, maxchain = 0; 232 Elf32_Word *gnuhash, *buckets, *ptr; 233 int bi; 234 235 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr); 236 gnuhash = (Elf32_Word *)adjptr(map, edptr); 237 238 nbuck = gnuhash[0]; 239 symndx = gnuhash[1]; 240 maskwords = gnuhash[2]; 241 242 /* 243 * First, find the last valid bucket and grab its index 244 */ 245 if (eident == ELFCLASS64) 246 maskwords *= 2; /* sizeof(*buckets) == 4 */ 247 buckets = gnuhash + 4 + maskwords; 248 for (bi = nbuck-1; bi >= 0; bi--) { 249 if (buckets[bi] != 0) { 250 maxchain = buckets[bi]; 251 break; 252 } 253 } 254 if (maxchain == 0 || maxchain < symndx) 255 break; 256 257 /* 258 * Then, traverse the last chain and count symbols. 259 */ 260 261 cursymcount = maxchain; 262 ptr = buckets + nbuck + (maxchain - symndx); 263 do { 264 cursymcount++; 265 } while ((*ptr++ & 1) == 0); 266 } 267 break; 268 #endif 269 case DT_SYMENT: 270 DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval); 271 assert(edval == SYM_GETSIZE()); 272 break; 273 default: 274 break; 275 } 276 i++; 277 DYNn_GETMEMBER(ed_base, i, d_tag, ed_tag); 278 } 279 280 if (str_base == NULL || syms_base == NULL || 281 curstrsize == 0 || cursymcount == 0) { 282 fprintf(stderr, "could not find strtab, symtab or their sizes " 283 "in %s\n", map->l_name); 284 return ENOEXEC; 285 } 286 287 /* 288 * Make sure we have enough space for the contents of the symbol 289 * and string tables we are currently processing. The total used 290 * space will be smaller due to undefined symbols we are not 291 * interested in. 292 */ 293 symtab = reservespace(symtab, &symtabsize, 294 symtaboff, cursymcount * SYM_GETSIZE()); 295 strtab = reservespace(strtab, &strtabsize, strtaboff, curstrsize); 296 if (symtab == NULL || strtab == NULL) { 297 fprintf(stderr, "failed to reserve memory"); 298 return ENOMEM; 299 } 300 301 /* iterate over all symbols in current symtab */ 302 for (i = 0; i < cursymcount; i++) { 303 const char *cursymname; 304 int shndx, name; 305 uintptr_t value; 306 void *csym; 307 308 SYMn_GETMEMBER(syms_base, i, st_shndx, shndx); 309 SYMn_GETMEMBER(syms_base, i, st_value, value); 310 if (shndx == SHN_UNDEF || value == 0) 311 continue; 312 313 /* get symbol name */ 314 SYMn_GETMEMBER(syms_base, i, st_name, name); 315 cursymname = name + str_base; 316 317 /* 318 * Only accept symbols which are decidedly in 319 * the rump kernel namespace. 320 * XXX: quirks, but they wouldn't matter here 321 */ 322 if (strncmp(cursymname, "rump", 4) != 0 && 323 strncmp(cursymname, "RUMP", 4) != 0 && 324 strncmp(cursymname, "__", 2) != 0) { 325 continue; 326 } 327 328 memcpy(symtab + symtaboff, 329 (const uint8_t *)syms_base + i*SYM_GETSIZE(),SYM_GETSIZE()); 330 331 /* 332 * set name to point at new strtab, offset symbol value 333 * with lib base address. 334 */ 335 csym = symtab + symtaboff; 336 SYMn_SETMEMBER(csym, 0, st_name, strtaboff); 337 SYMn_GETMEMBER(csym, 0, st_value, value); 338 SYMn_SETMEMBER(csym, 0, st_value,(intptr_t)(value+map->l_addr)); 339 symtaboff += SYM_GETSIZE(); 340 341 strcpy(strtab + strtaboff, cursymname); 342 strtaboff += strlen(cursymname)+1; 343 } 344 345 return 0; 346 } 347 348 static void 349 process_object(void *handle, 350 rump_modinit_fn domodinit, rump_compload_fn docompload) 351 { 352 const struct modinfo *const *mi_start, *const *mi_end; 353 struct rump_component *const *rc, *const *rc_end; 354 355 mi_start = dlsym(handle, "__start_link_set_modules"); 356 mi_end = dlsym(handle, "__stop_link_set_modules"); 357 if (mi_start && mi_end) 358 domodinit(mi_start, (size_t)(mi_end-mi_start)); 359 360 rc = dlsym(handle, "__start_link_set_rump_components"); 361 rc_end = dlsym(handle, "__stop_link_set_rump_components"); 362 if (rc && rc_end) { 363 for (; rc < rc_end; rc++) 364 docompload(*rc); 365 assert(rc == rc_end); 366 } 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, rump_compload_fn compload) 376 { 377 struct link_map *map, *origmap, *mainmap; 378 void *mainhandle; 379 int error; 380 381 mainhandle = dlopen(NULL, RTLD_NOW); 382 if (dlinfo(mainhandle, RTLD_DI_LINKMAP, &mainmap) == -1) { 383 fprintf(stderr, "warning: rumpuser module bootstrap " 384 "failed: %s\n", dlerror()); 385 return; 386 } 387 origmap = mainmap; 388 389 /* 390 * Process last->first because that's the most probable 391 * order for dependencies 392 */ 393 for (; origmap->l_next; origmap = origmap->l_next) 394 continue; 395 396 /* 397 * Build symbol table to hand to the rump kernel. Do this by 398 * iterating over all rump libraries and collecting symbol 399 * addresses and relocation info. 400 */ 401 error = 0; 402 for (map = origmap; map && !error; map = map->l_prev) { 403 if (strstr(map->l_name, "librump") != NULL || map == mainmap) 404 error = getsymbols(map, map == mainmap); 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 and components. 436 * 437 * Simply loop through all objects, ones unrelated to rump kernels 438 * will not contain link_set_rump_components (well, not including 439 * "sabotage", but that needs to be solved at another level anyway). 440 */ 441 for (map = origmap; map; map = map->l_prev) { 442 void *handle; 443 444 if (map == mainmap) { 445 handle = mainhandle; 446 } else { 447 handle = dlopen(map->l_name, RTLD_LAZY); 448 if (handle == NULL) 449 continue; 450 } 451 process_object(handle, domodinit, compload); 452 if (map != mainmap) 453 dlclose(handle); 454 } 455 } 456 #else 457 /* 458 * no dynamic linking supported 459 */ 460 void 461 rumpuser_dl_bootstrap(rump_modinit_fn domodinit, 462 rump_symload_fn symload, rump_compload_fn compload) 463 { 464 465 return; 466 } 467 #endif 468 469 void * 470 rumpuser_dl_globalsym(const char *symname) 471 { 472 473 return dlsym(RTLD_DEFAULT, symname); 474 } 475