1 /* $NetBSD: rumpuser_dl.c,v 1.22 2013/10/27 16:39:46 rmind 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.22 2013/10/27 16:39:46 rmind 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 <errno.h> 52 #include <fcntl.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <unistd.h> 57 58 #include <rump/rumpuser.h> 59 60 #if defined(__ELF__) && (defined(__NetBSD__) || defined(__FreeBSD__) \ 61 || (defined(__sun__) && defined(__svr4__))) || defined(__linux__) \ 62 || defined(__DragonFly__) 63 #include <elf.h> 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 glibc, 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 / FreeBSD, the main object works differently 151 * ... uuuuh. 152 */ 153 #if defined(__GLIBC__) 154 /* XXX: not true for e.g. MIPS, but we'll cross that hurdle later */ 155 #define adjptr(_map_, _ptr_) ((void *)(_ptr_)) 156 #elif defined(__sun__) || defined(__DragonFly__) || defined(__FreeBSD__) 157 #define adjptr(_map_, _ptr_) \ 158 (ismainobj ? (void *)(_ptr_) : (void *)(_map_->l_addr + (_ptr_))) 159 #else 160 /* NetBSD and some others, e.g. Linux + musl */ 161 #define adjptr(_map_, _ptr_) ((void *)(_map_->l_addr + (_ptr_))) 162 #endif 163 164 static int 165 getsymbols(struct link_map *map, int ismainobj) 166 { 167 char *str_base; 168 void *syms_base = NULL; /* XXXgcc */ 169 size_t curstrsize; 170 const void *ed_base; 171 uint64_t ed_tag; 172 size_t cursymcount; 173 unsigned i; 174 175 if (map->l_addr) { 176 if (memcmp((void *)map->l_addr, ELFMAG, SELFMAG) != 0) 177 return ENOEXEC; 178 eident = *(unsigned char *)(map->l_addr + EI_CLASS); 179 if (eident != ELFCLASS32 && eident != ELFCLASS64) 180 return ENOEXEC; 181 } 182 183 /* 184 * ok, we probably have only the main object. instead of going 185 * to disk and reading the ehdr, just try to guess the size. 186 */ 187 if (eident == 0) { 188 if (/*CONSTCOND*/sizeof(void *) == 4) 189 eident = ELFCLASS32; 190 else 191 eident = ELFCLASS64; 192 } 193 194 /* 195 * Find symtab and strtab and their sizes. 196 */ 197 str_base = NULL; 198 curstrsize = 0; 199 cursymcount = 0; 200 ed_base = map->l_ld; 201 DYNn_GETMEMBER(ed_base, 0, d_tag, ed_tag); 202 for (i = 0; ed_tag != DT_NULL;) { 203 uintptr_t edptr; 204 size_t edval; 205 Elf_Symindx *hashtab; 206 207 switch (ed_tag) { 208 case DT_SYMTAB: 209 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr); 210 syms_base = adjptr(map, edptr); 211 break; 212 case DT_STRTAB: 213 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr); 214 str_base = adjptr(map, edptr); 215 break; 216 case DT_STRSZ: 217 DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval); 218 curstrsize = edval; 219 break; 220 case DT_HASH: 221 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr); 222 hashtab = (Elf_Symindx *)adjptr(map, edptr); 223 cursymcount = hashtab[1]; 224 break; 225 #ifdef DT_GNU_HASH 226 /* 227 * DT_GNU_HASH is a bit more complicated than DT_HASH 228 * in this regard since apparently there is no field 229 * telling us the total symbol count. Instead, we look 230 * for the last valid hash bucket and add its chain lenght 231 * to the bucket's base index. 232 */ 233 case DT_GNU_HASH: { 234 Elf32_Word nbuck, symndx, maskwords, maxchain = 0; 235 Elf32_Word *gnuhash, *buckets, *ptr; 236 int bi; 237 238 DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr); 239 gnuhash = (Elf32_Word *)adjptr(map, edptr); 240 241 nbuck = gnuhash[0]; 242 symndx = gnuhash[1]; 243 maskwords = gnuhash[2]; 244 245 /* 246 * First, find the last valid bucket and grab its index 247 */ 248 if (eident == ELFCLASS64) 249 maskwords *= 2; /* sizeof(*buckets) == 4 */ 250 buckets = gnuhash + 4 + maskwords; 251 for (bi = nbuck-1; bi >= 0; bi--) { 252 if (buckets[bi] != 0) { 253 maxchain = buckets[bi]; 254 break; 255 } 256 } 257 if (maxchain == 0 || maxchain < symndx) 258 break; 259 260 /* 261 * Then, traverse the last chain and count symbols. 262 */ 263 264 cursymcount = maxchain; 265 ptr = buckets + nbuck + (maxchain - symndx); 266 do { 267 cursymcount++; 268 } while ((*ptr++ & 1) == 0); 269 } 270 break; 271 #endif 272 case DT_SYMENT: 273 DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval); 274 assert(edval == SYM_GETSIZE()); 275 break; 276 default: 277 break; 278 } 279 i++; 280 DYNn_GETMEMBER(ed_base, i, d_tag, ed_tag); 281 } 282 283 if (str_base == NULL || syms_base == NULL || 284 curstrsize == 0 || cursymcount == 0) { 285 fprintf(stderr, "could not find strtab, symtab or their sizes " 286 "in %s\n", map->l_name); 287 return ENOEXEC; 288 } 289 290 /* 291 * Make sure we have enough space for the contents of the symbol 292 * and string tables we are currently processing. The total used 293 * space will be smaller due to undefined symbols we are not 294 * interested in. 295 */ 296 symtab = reservespace(symtab, &symtabsize, 297 symtaboff, cursymcount * SYM_GETSIZE()); 298 strtab = reservespace(strtab, &strtabsize, strtaboff, curstrsize); 299 if (symtab == NULL || strtab == NULL) { 300 fprintf(stderr, "failed to reserve memory"); 301 return ENOMEM; 302 } 303 304 /* iterate over all symbols in current symtab */ 305 for (i = 0; i < cursymcount; i++) { 306 const char *cursymname; 307 int shndx, name; 308 uintptr_t value; 309 void *csym; 310 311 SYMn_GETMEMBER(syms_base, i, st_shndx, shndx); 312 SYMn_GETMEMBER(syms_base, i, st_value, value); 313 if (shndx == SHN_UNDEF || value == 0) 314 continue; 315 316 /* get symbol name */ 317 SYMn_GETMEMBER(syms_base, i, st_name, name); 318 cursymname = name + str_base; 319 320 /* 321 * Only accept symbols which are decidedly in 322 * the rump kernel namespace. 323 * XXX: quirks, but they wouldn't matter here 324 */ 325 if (strncmp(cursymname, "rump", 4) != 0 && 326 strncmp(cursymname, "RUMP", 4) != 0 && 327 strncmp(cursymname, "__", 2) != 0) { 328 continue; 329 } 330 331 memcpy(symtab + symtaboff, 332 (const uint8_t *)syms_base + i*SYM_GETSIZE(),SYM_GETSIZE()); 333 334 /* 335 * set name to point at new strtab, offset symbol value 336 * with lib base address. 337 */ 338 csym = symtab + symtaboff; 339 SYMn_SETMEMBER(csym, 0, st_name, strtaboff); 340 SYMn_GETMEMBER(csym, 0, st_value, value); 341 SYMn_SETMEMBER(csym, 0, st_value,(intptr_t)(value+map->l_addr)); 342 symtaboff += SYM_GETSIZE(); 343 344 strcpy(strtab + strtaboff, cursymname); 345 strtaboff += strlen(cursymname)+1; 346 } 347 348 return 0; 349 } 350 351 static void 352 process_object(void *handle, 353 rump_modinit_fn domodinit, rump_compload_fn docompload) 354 { 355 const struct modinfo *const *mi_start, *const *mi_end; 356 struct rump_component *const *rc, *const *rc_end; 357 358 mi_start = dlsym(handle, "__start_link_set_modules"); 359 mi_end = dlsym(handle, "__stop_link_set_modules"); 360 if (mi_start && mi_end) 361 domodinit(mi_start, (size_t)(mi_end-mi_start)); 362 363 rc = dlsym(handle, "__start_link_set_rump_components"); 364 rc_end = dlsym(handle, "__stop_link_set_rump_components"); 365 if (rc && rc_end) { 366 for (; rc < rc_end; rc++) 367 docompload(*rc); 368 assert(rc == rc_end); 369 } 370 } 371 372 /* 373 * Get the linkmap from the dynlinker. Try to load kernel modules 374 * from all objects in the linkmap. 375 */ 376 void 377 rumpuser_dl_bootstrap(rump_modinit_fn domodinit, 378 rump_symload_fn symload, rump_compload_fn compload) 379 { 380 struct link_map *map, *origmap, *mainmap; 381 void *mainhandle; 382 int error; 383 384 mainhandle = dlopen(NULL, RTLD_NOW); 385 if (dlinfo(mainhandle, RTLD_DI_LINKMAP, &mainmap) == -1) { 386 fprintf(stderr, "warning: rumpuser module bootstrap " 387 "failed: %s\n", dlerror()); 388 return; 389 } 390 origmap = mainmap; 391 392 /* 393 * Process last->first because that's the most probable 394 * order for dependencies 395 */ 396 for (; origmap->l_next; origmap = origmap->l_next) 397 continue; 398 399 /* 400 * Build symbol table to hand to the rump kernel. Do this by 401 * iterating over all rump libraries and collecting symbol 402 * addresses and relocation info. 403 */ 404 error = 0; 405 for (map = origmap; map && !error; map = map->l_prev) { 406 if (strstr(map->l_name, "librump") != NULL || map == mainmap) 407 error = getsymbols(map, map == mainmap); 408 } 409 410 if (error == 0) { 411 void *trimmedsym, *trimmedstr; 412 413 /* 414 * Allocate optimum-sized memory for storing tables 415 * and feed to kernel. If memory allocation fails, 416 * just give the ones with extra context (although 417 * I'm pretty sure we'll die moments later due to 418 * memory running out). 419 */ 420 if ((trimmedsym = malloc(symtaboff)) != NULL) { 421 memcpy(trimmedsym, symtab, symtaboff); 422 } else { 423 trimmedsym = symtab; 424 symtab = NULL; 425 } 426 if ((trimmedstr = malloc(strtaboff)) != NULL) { 427 memcpy(trimmedstr, strtab, strtaboff); 428 } else { 429 trimmedstr = strtab; 430 strtab = NULL; 431 } 432 symload(trimmedsym, symtaboff, trimmedstr, strtaboff); 433 } 434 free(symtab); 435 free(strtab); 436 437 /* 438 * Next, load modules and components. 439 * 440 * Simply loop through all objects, ones unrelated to rump kernels 441 * will not contain link_set_rump_components (well, not including 442 * "sabotage", but that needs to be solved at another level anyway). 443 */ 444 for (map = origmap; map; map = map->l_prev) { 445 void *handle; 446 447 if (map == mainmap) { 448 handle = mainhandle; 449 } else { 450 handle = dlopen(map->l_name, RTLD_LAZY); 451 if (handle == NULL) 452 continue; 453 } 454 process_object(handle, domodinit, compload); 455 if (map != mainmap) 456 dlclose(handle); 457 } 458 } 459 #else 460 /* 461 * no dynamic linking supported 462 */ 463 void 464 rumpuser_dl_bootstrap(rump_modinit_fn domodinit, 465 rump_symload_fn symload, rump_compload_fn compload) 466 { 467 468 return; 469 } 470 #endif 471 472 void * 473 rumpuser_dl_globalsym(const char *symname) 474 { 475 476 return dlsym(RTLD_DEFAULT, symname); 477 } 478