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