xref: /netbsd-src/lib/librumpuser/rumpuser_dl.c (revision 388550b026d49b7f7b7480b1113bf82bb8d6a480)
1*388550b0Srillig /*      $NetBSD: rumpuser_dl.c,v 1.34 2022/04/19 20:32:17 rillig Exp $	*/
26e4a9f91Spooka 
36e4a9f91Spooka /*
46e4a9f91Spooka  * Copyright (c) 2009 Antti Kantee.  All Rights Reserved.
56e4a9f91Spooka  *
66e4a9f91Spooka  * Redistribution and use in source and binary forms, with or without
76e4a9f91Spooka  * modification, are permitted provided that the following conditions
86e4a9f91Spooka  * are met:
96e4a9f91Spooka  * 1. Redistributions of source code must retain the above copyright
106e4a9f91Spooka  *    notice, this list of conditions and the following disclaimer.
116e4a9f91Spooka  * 2. Redistributions in binary form must reproduce the above copyright
126e4a9f91Spooka  *    notice, this list of conditions and the following disclaimer in the
136e4a9f91Spooka  *    documentation and/or other materials provided with the distribution.
146e4a9f91Spooka  *
156e4a9f91Spooka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
166e4a9f91Spooka  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
176e4a9f91Spooka  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
186e4a9f91Spooka  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
196e4a9f91Spooka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
206e4a9f91Spooka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
216e4a9f91Spooka  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
226e4a9f91Spooka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
236e4a9f91Spooka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
246e4a9f91Spooka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
256e4a9f91Spooka  * SUCH DAMAGE.
266e4a9f91Spooka  */
276e4a9f91Spooka 
286e4a9f91Spooka /*
296e4a9f91Spooka  * Load all module link sets and feed symbol table to the kernel.
306e4a9f91Spooka  * Called during rump bootstrap.
316e4a9f91Spooka  */
326e4a9f91Spooka 
335ccc76a6Spooka /*
345ccc76a6Spooka  * Solaris libelf.h doesn't support _FILE_OFFSET_BITS=64.  Luckily,
355ccc76a6Spooka  * for this module it doesn't matter.
365ccc76a6Spooka  */
377415696eSpooka #if defined(__sun__)
387415696eSpooka #define RUMPUSER_NO_FILE_OFFSET_BITS
395ccc76a6Spooka #endif
403b3ffd70Spooka #include "rumpuser_port.h"
413b3ffd70Spooka 
423b3ffd70Spooka #if !defined(lint)
43*388550b0Srillig __RCSID("$NetBSD: rumpuser_dl.c,v 1.34 2022/04/19 20:32:17 rillig Exp $");
443b3ffd70Spooka #endif /* !lint */
456e4a9f91Spooka 
466e4a9f91Spooka #include <sys/types.h>
476e4a9f91Spooka #include <sys/time.h>
485996efe5Spgoyette #include <sys/evcnt.h>
495996efe5Spgoyette 
506e4a9f91Spooka #include <assert.h>
513b3ffd70Spooka 
526e4a9f91Spooka #include <dlfcn.h>
536e4a9f91Spooka #include <errno.h>
546e4a9f91Spooka #include <fcntl.h>
55d3852222Spooka #include <stdint.h>
566e4a9f91Spooka #include <stdio.h>
576e4a9f91Spooka #include <stdlib.h>
586e4a9f91Spooka #include <string.h>
596e4a9f91Spooka #include <unistd.h>
606e4a9f91Spooka 
616e4a9f91Spooka #include <rump/rumpuser.h>
626e4a9f91Spooka 
6323dfcd74Spooka #if defined(__ELF__) && defined(HAVE_DLINFO)
64d60eb8c1Srmind #include <elf.h>
65e0f5e003Spooka #include <link.h>
66e0f5e003Spooka 
676e4a9f91Spooka static size_t symtabsize = 0, strtabsize = 0;
686e4a9f91Spooka static size_t symtaboff = 0, strtaboff = 0;
696e4a9f91Spooka static uint8_t *symtab = NULL;
706e4a9f91Spooka static char *strtab = NULL;
716e4a9f91Spooka static unsigned char eident;
726e4a9f91Spooka 
73f347b449Spooka /* nb5 compat */
74f347b449Spooka #ifndef Elf_Symindx
75f347b449Spooka #define Elf_Symindx uint32_t
76f347b449Spooka #endif
77f347b449Spooka 
786e4a9f91Spooka static void *
reservespace(void * store,size_t * storesize,size_t storeoff,size_t required)796e4a9f91Spooka reservespace(void *store, size_t *storesize,
806e4a9f91Spooka 	size_t storeoff, size_t required)
816e4a9f91Spooka {
826e4a9f91Spooka 	size_t chunk, newsize;
836e4a9f91Spooka 
846e4a9f91Spooka 	assert(storeoff <= *storesize);
856e4a9f91Spooka 	chunk = *storesize - storeoff;
866e4a9f91Spooka 
876e4a9f91Spooka 	if (chunk >= required)
886e4a9f91Spooka 		return store;
896e4a9f91Spooka 
906e4a9f91Spooka 	newsize = *storesize + ((size_t)required - chunk);
916e4a9f91Spooka 	store = realloc(store, newsize);
926e4a9f91Spooka 	if (store == NULL) {
936e4a9f91Spooka 		return NULL;
946e4a9f91Spooka 	}
956e4a9f91Spooka 	*((uint8_t *)store + storeoff) = '\0';
966e4a9f91Spooka 	*storesize = newsize;
976e4a9f91Spooka 
986e4a9f91Spooka 	return store;
996e4a9f91Spooka }
1006e4a9f91Spooka 
1016e4a9f91Spooka /*
1026e4a9f91Spooka  * Macros to make handling elf32/64 in the code a little saner.
1036e4a9f91Spooka  */
1046e4a9f91Spooka 
1056e4a9f91Spooka #define DYNn_GETMEMBER(base, n, thevar, result)				\
1066e4a9f91Spooka do {									\
1076e4a9f91Spooka 	if (eident == ELFCLASS32) {					\
10823a1592dSpooka 		const Elf32_Dyn *dyn = base;				\
1096e4a9f91Spooka 		/*LINTED*/						\
1106e4a9f91Spooka 		result = dyn[n].thevar;					\
1116e4a9f91Spooka 	} else {							\
11223a1592dSpooka 		const Elf64_Dyn *dyn = base;				\
1136e4a9f91Spooka 		/*LINTED*/						\
1146e4a9f91Spooka 		result = dyn[n].thevar;					\
1156e4a9f91Spooka 	}								\
116*388550b0Srillig } while (0)
1176e4a9f91Spooka 
1186e4a9f91Spooka #define SYMn_GETMEMBER(base, n, thevar, result)				\
1196e4a9f91Spooka do {									\
1206e4a9f91Spooka 	if (eident == ELFCLASS32) {					\
12146ea1019Spooka 		const Elf32_Sym *sym = base;				\
1226e4a9f91Spooka 		/*LINTED*/						\
1236e4a9f91Spooka 		result = sym[n].thevar;					\
1246e4a9f91Spooka 	} else {							\
12546ea1019Spooka 		const Elf64_Sym *sym = base;				\
1266e4a9f91Spooka 		/*LINTED*/						\
1276e4a9f91Spooka 		result = sym[n].thevar;					\
1286e4a9f91Spooka 	}								\
129*388550b0Srillig } while (0)
1306e4a9f91Spooka 
1316e4a9f91Spooka #define SYMn_SETMEMBER(base, n, thevar, value)				\
1326e4a9f91Spooka do {									\
1336e4a9f91Spooka 	if (eident == ELFCLASS32) {					\
1346e4a9f91Spooka 		Elf32_Sym *sym = base;					\
1356e4a9f91Spooka 		/*LINTED*/						\
1366e4a9f91Spooka 		sym[n].thevar = value;					\
1376e4a9f91Spooka 	} else {							\
1386e4a9f91Spooka 		Elf64_Sym *sym = base;					\
1396e4a9f91Spooka 		/*LINTED*/						\
1406e4a9f91Spooka 		sym[n].thevar = value;					\
1416e4a9f91Spooka 	}								\
142*388550b0Srillig } while (0)
1436e4a9f91Spooka 
1446e4a9f91Spooka #define SYM_GETSIZE() ((eident==ELFCLASS32)?sizeof(Elf32_Sym):sizeof(Elf64_Sym))
1456e4a9f91Spooka 
1463b3ffd70Spooka /*
1473b3ffd70Spooka  * On NetBSD, the dynamic section pointer values seem to be relative to
148aaf7a23bSpooka  * the address the dso is mapped at.  On glibc, they seem to contain
1493b3ffd70Spooka  * the absolute address.  I couldn't find anything definite from a quick
1503b3ffd70Spooka  * read of the standard and therefore I will not go and figure beyond ifdef.
151aaf7a23bSpooka  * On Solaris and DragonFly / FreeBSD, the main object works differently
152aaf7a23bSpooka  * ... uuuuh.
1533b3ffd70Spooka  */
154079cd210Spooka #if defined(__GLIBC__) && !defined(__mips__)
1553b3ffd70Spooka #define adjptr(_map_, _ptr_) ((void *)(_ptr_))
15669c4f929Spooka #elif defined(__sun__) || defined(__DragonFly__) || defined(__FreeBSD__)
1574b0fe892Spooka #define adjptr(_map_, _ptr_) \
158f9ef17b1Spooka     (ismainobj ? (void *)(_ptr_) : (void *)(_map_->l_addr + (_ptr_)))
1593b3ffd70Spooka #else
160f3adf1e5Spooka /* NetBSD and some others, e.g. Linux + musl */
1613b3ffd70Spooka #define adjptr(_map_, _ptr_) ((void *)(_map_->l_addr + (_ptr_)))
1623b3ffd70Spooka #endif
1633b3ffd70Spooka 
1646e4a9f91Spooka static int
getsymbols(struct link_map * map,int ismainobj)165f9ef17b1Spooka getsymbols(struct link_map *map, int ismainobj)
1666e4a9f91Spooka {
1676e4a9f91Spooka 	char *str_base;
1686e4a9f91Spooka 	void *syms_base = NULL; /* XXXgcc */
16946ea1019Spooka 	size_t curstrsize;
17023a1592dSpooka 	const void *ed_base;
1716e4a9f91Spooka 	uint64_t ed_tag;
17246ea1019Spooka 	size_t cursymcount;
17346ea1019Spooka 	unsigned i;
1746e4a9f91Spooka 
17546ea1019Spooka 	if (map->l_addr) {
1763b3ffd70Spooka 		if (memcmp((void *)map->l_addr, ELFMAG, SELFMAG) != 0)
1776e4a9f91Spooka 			return ENOEXEC;
1786e4a9f91Spooka 		eident = *(unsigned char *)(map->l_addr + EI_CLASS);
1796e4a9f91Spooka 		if (eident != ELFCLASS32 && eident != ELFCLASS64)
1806e4a9f91Spooka 			return ENOEXEC;
1816e4a9f91Spooka 	}
1826e4a9f91Spooka 
18346ea1019Spooka 	/*
18446ea1019Spooka 	 * ok, we probably have only the main object.  instead of going
18546ea1019Spooka 	 * to disk and reading the ehdr, just try to guess the size.
18646ea1019Spooka 	 */
18746ea1019Spooka 	if (eident == 0) {
18846ea1019Spooka 		if (/*CONSTCOND*/sizeof(void *) == 4)
18946ea1019Spooka 			eident = ELFCLASS32;
19046ea1019Spooka 		else
19146ea1019Spooka 			eident = ELFCLASS64;
1926e4a9f91Spooka 	}
1936e4a9f91Spooka 
19446ea1019Spooka 	/*
19546ea1019Spooka 	 * Find symtab and strtab and their sizes.
19646ea1019Spooka 	 */
1976e4a9f91Spooka 	str_base = NULL;
19846ea1019Spooka 	curstrsize = 0;
19946ea1019Spooka 	cursymcount = 0;
2006e4a9f91Spooka 	ed_base = map->l_ld;
20146ea1019Spooka 	DYNn_GETMEMBER(ed_base, 0, d_tag, ed_tag);
20246ea1019Spooka 	for (i = 0; ed_tag != DT_NULL;) {
2036e4a9f91Spooka 		uintptr_t edptr;
2046e4a9f91Spooka 		size_t edval;
205603f3daeSnjoly 		Elf_Symindx *hashtab;
2066e4a9f91Spooka 
2076e4a9f91Spooka 		switch (ed_tag) {
2086e4a9f91Spooka 		case DT_SYMTAB:
2096e4a9f91Spooka 			DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
2103b3ffd70Spooka 			syms_base = adjptr(map, edptr);
2116e4a9f91Spooka 			break;
2126e4a9f91Spooka 		case DT_STRTAB:
2136e4a9f91Spooka 			DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
2143b3ffd70Spooka 			str_base = adjptr(map, edptr);
2156e4a9f91Spooka 			break;
2166e4a9f91Spooka 		case DT_STRSZ:
2176e4a9f91Spooka 			DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval);
2186e4a9f91Spooka 			curstrsize = edval;
2196e4a9f91Spooka 			break;
22046ea1019Spooka 		case DT_HASH:
22146ea1019Spooka 			DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
2223b3ffd70Spooka 			hashtab = (Elf_Symindx *)adjptr(map, edptr);
223603f3daeSnjoly 			cursymcount = hashtab[1];
22446ea1019Spooka 			break;
2254c7d25c0Spooka #ifdef DT_GNU_HASH
2264c7d25c0Spooka 		/*
2274c7d25c0Spooka 		 * DT_GNU_HASH is a bit more complicated than DT_HASH
2284c7d25c0Spooka 		 * in this regard since apparently there is no field
2294c7d25c0Spooka 		 * telling us the total symbol count.  Instead, we look
23040064e24Smsaitoh 		 * for the last valid hash bucket and add its chain length
2314c7d25c0Spooka 		 * to the bucket's base index.
2324c7d25c0Spooka 		 */
2334c7d25c0Spooka 		case DT_GNU_HASH: {
2344c7d25c0Spooka 			Elf32_Word nbuck, symndx, maskwords, maxchain = 0;
2354c7d25c0Spooka 			Elf32_Word *gnuhash, *buckets, *ptr;
2364c7d25c0Spooka 			int bi;
2374c7d25c0Spooka 
2384c7d25c0Spooka 			DYNn_GETMEMBER(ed_base, i, d_un.d_ptr, edptr);
2394c7d25c0Spooka 			gnuhash = (Elf32_Word *)adjptr(map, edptr);
2404c7d25c0Spooka 
2414c7d25c0Spooka 			nbuck = gnuhash[0];
2424c7d25c0Spooka 			symndx = gnuhash[1];
2434c7d25c0Spooka 			maskwords = gnuhash[2];
2444c7d25c0Spooka 
2454c7d25c0Spooka 			/*
2464c7d25c0Spooka 			 * First, find the last valid bucket and grab its index
2474c7d25c0Spooka 			 */
2484c7d25c0Spooka 			if (eident == ELFCLASS64)
2494c7d25c0Spooka 				maskwords *= 2; /* sizeof(*buckets) == 4 */
2504c7d25c0Spooka 			buckets = gnuhash + 4 + maskwords;
2514c7d25c0Spooka 			for (bi = nbuck-1; bi >= 0; bi--) {
2524c7d25c0Spooka 				if (buckets[bi] != 0) {
2534c7d25c0Spooka 					maxchain = buckets[bi];
2544c7d25c0Spooka 					break;
2554c7d25c0Spooka 				}
2564c7d25c0Spooka 			}
2574c7d25c0Spooka 			if (maxchain == 0 || maxchain < symndx)
2584c7d25c0Spooka 				break;
2594c7d25c0Spooka 
2604c7d25c0Spooka 			/*
2614c7d25c0Spooka 			 * Then, traverse the last chain and count symbols.
2624c7d25c0Spooka 			 */
2634c7d25c0Spooka 
2644c7d25c0Spooka 			cursymcount = maxchain;
2654c7d25c0Spooka 			ptr = buckets + nbuck + (maxchain - symndx);
2664c7d25c0Spooka 			do {
2674c7d25c0Spooka 				cursymcount++;
2684c7d25c0Spooka 			} while ((*ptr++ & 1) == 0);
2694c7d25c0Spooka 		}
2704c7d25c0Spooka 			break;
2714c7d25c0Spooka #endif
27246ea1019Spooka 		case DT_SYMENT:
27346ea1019Spooka 			DYNn_GETMEMBER(ed_base, i, d_un.d_val, edval);
27446ea1019Spooka 			assert(edval == SYM_GETSIZE());
27546ea1019Spooka 			break;
2766e4a9f91Spooka 		default:
2776e4a9f91Spooka 			break;
2786e4a9f91Spooka 		}
2796e4a9f91Spooka 		i++;
2806e4a9f91Spooka 		DYNn_GETMEMBER(ed_base, i, d_tag, ed_tag);
28146ea1019Spooka 	}
2826e4a9f91Spooka 
28346ea1019Spooka 	if (str_base == NULL || syms_base == NULL ||
28446ea1019Spooka 	    curstrsize == 0 || cursymcount == 0) {
28546ea1019Spooka 		fprintf(stderr, "could not find strtab, symtab or their sizes "
2866e4a9f91Spooka 		    "in %s\n", map->l_name);
2876e4a9f91Spooka 		return ENOEXEC;
2886e4a9f91Spooka 	}
2896e4a9f91Spooka 
2906e4a9f91Spooka 	/*
2916e4a9f91Spooka 	 * Make sure we have enough space for the contents of the symbol
2926e4a9f91Spooka 	 * and string tables we are currently processing.  The total used
2936e4a9f91Spooka 	 * space will be smaller due to undefined symbols we are not
2946e4a9f91Spooka 	 * interested in.
2956e4a9f91Spooka 	 */
29646ea1019Spooka 	symtab = reservespace(symtab, &symtabsize,
29746ea1019Spooka 	    symtaboff, cursymcount * SYM_GETSIZE());
2986e4a9f91Spooka 	strtab = reservespace(strtab, &strtabsize, strtaboff, curstrsize);
2996e4a9f91Spooka 	if (symtab == NULL || strtab == NULL) {
3006e4a9f91Spooka 		fprintf(stderr, "failed to reserve memory");
3016e4a9f91Spooka 		return ENOMEM;
3026e4a9f91Spooka 	}
3036e4a9f91Spooka 
3046e4a9f91Spooka 	/* iterate over all symbols in current symtab */
30546ea1019Spooka 	for (i = 0; i < cursymcount; i++) {
30646ea1019Spooka 		const char *cursymname;
3076e4a9f91Spooka 		int shndx, name;
3086e4a9f91Spooka 		uintptr_t value;
3096e4a9f91Spooka 		void *csym;
3106e4a9f91Spooka 
3116e4a9f91Spooka 		SYMn_GETMEMBER(syms_base, i, st_shndx, shndx);
3126e4a9f91Spooka 		SYMn_GETMEMBER(syms_base, i, st_value, value);
3136e4a9f91Spooka 		if (shndx == SHN_UNDEF || value == 0)
3146e4a9f91Spooka 			continue;
3156e4a9f91Spooka 
3166e4a9f91Spooka 		/* get symbol name */
3176e4a9f91Spooka 		SYMn_GETMEMBER(syms_base, i, st_name, name);
3186e4a9f91Spooka 		cursymname = name + str_base;
31946ea1019Spooka 
32046ea1019Spooka 		/*
32146ea1019Spooka 		 * Only accept symbols which are decidedly in
32246ea1019Spooka 		 * the rump kernel namespace.
32346ea1019Spooka 		 * XXX: quirks, but they wouldn't matter here
32446ea1019Spooka 		 */
32546ea1019Spooka 		if (strncmp(cursymname, "rump", 4) != 0 &&
32646ea1019Spooka 		    strncmp(cursymname, "RUMP", 4) != 0 &&
32746ea1019Spooka 		    strncmp(cursymname, "__", 2) != 0) {
32846ea1019Spooka 			continue;
32946ea1019Spooka 		}
33046ea1019Spooka 
3316e4a9f91Spooka 		memcpy(symtab + symtaboff,
33246ea1019Spooka 		    (const uint8_t *)syms_base + i*SYM_GETSIZE(),SYM_GETSIZE());
3336e4a9f91Spooka 
3346e4a9f91Spooka 		/*
3356e4a9f91Spooka 		 * set name to point at new strtab, offset symbol value
3366e4a9f91Spooka 		 * with lib base address.
3376e4a9f91Spooka 		 */
3386e4a9f91Spooka 		csym = symtab + symtaboff;
3396e4a9f91Spooka 		SYMn_SETMEMBER(csym, 0, st_name, strtaboff);
3406e4a9f91Spooka 		SYMn_GETMEMBER(csym, 0, st_value, value);
3416e4a9f91Spooka 		SYMn_SETMEMBER(csym, 0, st_value,(intptr_t)(value+map->l_addr));
3426e4a9f91Spooka 		symtaboff += SYM_GETSIZE();
3436e4a9f91Spooka 
3446e4a9f91Spooka 		strcpy(strtab + strtaboff, cursymname);
3456e4a9f91Spooka 		strtaboff += strlen(cursymname)+1;
3466e4a9f91Spooka 	}
3476e4a9f91Spooka 
3486e4a9f91Spooka 	return 0;
3496e4a9f91Spooka }
3506e4a9f91Spooka 
351c2efc592Spooka static void
process_object(void * handle,rump_modinit_fn domodinit,rump_compload_fn docompload,rump_evcntattach_fn doevcntattach)3524b0fe892Spooka process_object(void *handle,
353561f1f3cSpgoyette 	rump_modinit_fn domodinit, rump_compload_fn docompload,
354561f1f3cSpgoyette 	rump_evcntattach_fn doevcntattach)
3556e4a9f91Spooka {
356c2efc592Spooka 	const struct modinfo *const *mi_start, *const *mi_end;
3574b0fe892Spooka 	struct rump_component *const *rc, *const *rc_end;
3586e4a9f91Spooka 
3595996efe5Spgoyette 	struct sysctllog;
3605996efe5Spgoyette 	typedef void sysctl_setup_func(struct sysctllog **);
3615996efe5Spgoyette 	sysctl_setup_func *const *sfp, *const *sfp_end;
3625996efe5Spgoyette 
3635996efe5Spgoyette 	struct evcnt *const *evp, *const *evp_end;
3645996efe5Spgoyette 
365c2efc592Spooka 	mi_start = dlsym(handle, "__start_link_set_modules");
3666e4a9f91Spooka 	mi_end = dlsym(handle, "__stop_link_set_modules");
3674b0fe892Spooka 	if (mi_start && mi_end)
368c2efc592Spooka 		domodinit(mi_start, (size_t)(mi_end-mi_start));
3696e4a9f91Spooka 
3704b0fe892Spooka 	rc = dlsym(handle, "__start_link_set_rump_components");
3714b0fe892Spooka 	rc_end = dlsym(handle, "__stop_link_set_rump_components");
3724b0fe892Spooka 	if (rc && rc_end) {
3734b0fe892Spooka 		for (; rc < rc_end; rc++)
3744b0fe892Spooka 			docompload(*rc);
3754b0fe892Spooka 		assert(rc == rc_end);
3764b0fe892Spooka 	}
3775996efe5Spgoyette 
3785996efe5Spgoyette 	/* handle link_set_sysctl_funcs */
3795996efe5Spgoyette 	sfp = dlsym(handle, "__start_link_set_sysctl_funcs");
3805996efe5Spgoyette 	sfp_end = dlsym(handle, "__stop_link_set_sysctl_funcs");
3815996efe5Spgoyette 	if (sfp && sfp_end) {
3825996efe5Spgoyette 		for (; sfp < sfp_end; sfp++)
3835996efe5Spgoyette 			(**sfp)(NULL);
3845996efe5Spgoyette 		assert(sfp == sfp_end);
3855996efe5Spgoyette 	}
3865996efe5Spgoyette 
3875996efe5Spgoyette 	/* handle link_set_evcnts */
3885996efe5Spgoyette 	evp = dlsym(handle, "__start_link_set_evcnts");
3895996efe5Spgoyette 	evp_end = dlsym(handle, "__stop_link_set_evcnts");
3905996efe5Spgoyette 	if (evp && evp_end) {
3915996efe5Spgoyette 		for (; evp < evp_end; evp++)
392561f1f3cSpgoyette 			doevcntattach(*evp);
3935996efe5Spgoyette 		assert(evp == evp_end);
3945996efe5Spgoyette 	}
3956e4a9f91Spooka }
3966e4a9f91Spooka 
3976e4a9f91Spooka /*
3986e4a9f91Spooka  * Get the linkmap from the dynlinker.  Try to load kernel modules
3996e4a9f91Spooka  * from all objects in the linkmap.
4006e4a9f91Spooka  */
4016e4a9f91Spooka void
rumpuser_dl_bootstrap(rump_modinit_fn domodinit,rump_symload_fn symload,rump_compload_fn compload,rump_evcntattach_fn doevcntattach)4026e4a9f91Spooka rumpuser_dl_bootstrap(rump_modinit_fn domodinit,
403561f1f3cSpgoyette 	rump_symload_fn symload, rump_compload_fn compload,
404561f1f3cSpgoyette 	rump_evcntattach_fn doevcntattach)
4056e4a9f91Spooka {
4064b0fe892Spooka 	struct link_map *map, *origmap, *mainmap;
4074b0fe892Spooka 	void *mainhandle;
4086e4a9f91Spooka 	int error;
4096e4a9f91Spooka 
4103b3ffd70Spooka 	mainhandle = dlopen(NULL, RTLD_NOW);
41154d90457Sjustin 	/* Will be null if statically linked so just return */
41254d90457Sjustin 	if (mainhandle == NULL)
41354d90457Sjustin 		return;
4144b0fe892Spooka 	if (dlinfo(mainhandle, RTLD_DI_LINKMAP, &mainmap) == -1) {
4156e4a9f91Spooka 		fprintf(stderr, "warning: rumpuser module bootstrap "
4166e4a9f91Spooka 		    "failed: %s\n", dlerror());
4176e4a9f91Spooka 		return;
4186e4a9f91Spooka 	}
4194b0fe892Spooka 	origmap = mainmap;
4204b0fe892Spooka 
4216e4a9f91Spooka 	/*
422300f37afSpooka 	 * Use a heuristic to determine if we are static linked.
423300f37afSpooka 	 * A dynamically linked binary should always have at least
424300f37afSpooka 	 * two objects: itself and ld.so.
425300f37afSpooka 	 *
426300f37afSpooka 	 * In a statically linked binary with glibc the linkmap
427300f37afSpooka 	 * contains some "info" that leads to a segfault.  Since we
428300f37afSpooka 	 * can't really do anything useful in here without ld.so, just
429300f37afSpooka 	 * simply bail and let the symbol references in librump do the
430300f37afSpooka 	 * right things.
431300f37afSpooka 	 */
432300f37afSpooka 	if (origmap->l_next == NULL && origmap->l_prev == NULL) {
433300f37afSpooka 		dlclose(mainhandle);
434300f37afSpooka 		return;
435300f37afSpooka 	}
436300f37afSpooka 
437300f37afSpooka 	/*
4386e4a9f91Spooka 	 * Process last->first because that's the most probable
4396e4a9f91Spooka 	 * order for dependencies
4406e4a9f91Spooka 	 */
4416e4a9f91Spooka 	for (; origmap->l_next; origmap = origmap->l_next)
4426e4a9f91Spooka 		continue;
4436e4a9f91Spooka 
4446e4a9f91Spooka 	/*
4456e4a9f91Spooka 	 * Build symbol table to hand to the rump kernel.  Do this by
4466e4a9f91Spooka 	 * iterating over all rump libraries and collecting symbol
4476e4a9f91Spooka 	 * addresses and relocation info.
4486e4a9f91Spooka 	 */
4496e4a9f91Spooka 	error = 0;
4506e4a9f91Spooka 	for (map = origmap; map && !error; map = map->l_prev) {
4514b0fe892Spooka 		if (strstr(map->l_name, "librump") != NULL || map == mainmap)
452f9ef17b1Spooka 			error = getsymbols(map, map == mainmap);
4536e4a9f91Spooka 	}
4546e4a9f91Spooka 
4556e4a9f91Spooka 	if (error == 0) {
4566e4a9f91Spooka 		void *trimmedsym, *trimmedstr;
4576e4a9f91Spooka 
4586e4a9f91Spooka 		/*
4596e4a9f91Spooka 		 * Allocate optimum-sized memory for storing tables
4606e4a9f91Spooka 		 * and feed to kernel.  If memory allocation fails,
4616e4a9f91Spooka 		 * just give the ones with extra context (although
4626e4a9f91Spooka 		 * I'm pretty sure we'll die moments later due to
4636e4a9f91Spooka 		 * memory running out).
4646e4a9f91Spooka 		 */
4656e4a9f91Spooka 		if ((trimmedsym = malloc(symtaboff)) != NULL) {
4666e4a9f91Spooka 			memcpy(trimmedsym, symtab, symtaboff);
4676e4a9f91Spooka 		} else {
4686e4a9f91Spooka 			trimmedsym = symtab;
4696e4a9f91Spooka 			symtab = NULL;
4706e4a9f91Spooka 		}
4716e4a9f91Spooka 		if ((trimmedstr = malloc(strtaboff)) != NULL) {
4726e4a9f91Spooka 			memcpy(trimmedstr, strtab, strtaboff);
4736e4a9f91Spooka 		} else {
4746e4a9f91Spooka 			trimmedstr = strtab;
4756e4a9f91Spooka 			strtab = NULL;
4766e4a9f91Spooka 		}
4776e4a9f91Spooka 		symload(trimmedsym, symtaboff, trimmedstr, strtaboff);
4786e4a9f91Spooka 	}
4796e4a9f91Spooka 	free(symtab);
4806e4a9f91Spooka 	free(strtab);
4816e4a9f91Spooka 
4826e4a9f91Spooka 	/*
4834b0fe892Spooka 	 * Next, load modules and components.
4844b0fe892Spooka 	 *
4854b0fe892Spooka 	 * Simply loop through all objects, ones unrelated to rump kernels
4864b0fe892Spooka 	 * will not contain link_set_rump_components (well, not including
4874b0fe892Spooka 	 * "sabotage", but that needs to be solved at another level anyway).
4886e4a9f91Spooka 	 */
4894b0fe892Spooka 	for (map = origmap; map; map = map->l_prev) {
490510a73e0Spooka 		void *handle;
491510a73e0Spooka 
4924b0fe892Spooka 		if (map == mainmap) {
4934b0fe892Spooka 			handle = mainhandle;
4944b0fe892Spooka 		} else {
495510a73e0Spooka 			handle = dlopen(map->l_name, RTLD_LAZY);
496510a73e0Spooka 			if (handle == NULL)
497510a73e0Spooka 				continue;
4984b0fe892Spooka 		}
499561f1f3cSpgoyette 		process_object(handle, domodinit, compload, doevcntattach);
5004b0fe892Spooka 		if (map != mainmap)
501510a73e0Spooka 			dlclose(handle);
502510a73e0Spooka 	}
503510a73e0Spooka }
5046e4a9f91Spooka #else
505e0f5e003Spooka /*
50686c3d192Spooka  * no dynamic linking supported
507e0f5e003Spooka  */
5084b0fe892Spooka void
rumpuser_dl_bootstrap(rump_modinit_fn domodinit,rump_symload_fn symload,rump_compload_fn compload,rump_evcntattach_fn doevcntattach)5094b0fe892Spooka rumpuser_dl_bootstrap(rump_modinit_fn domodinit,
510561f1f3cSpgoyette 	rump_symload_fn symload, rump_compload_fn compload,
511561f1f3cSpgoyette 	rump_evcntattach_fn doevcntattach)
5124b0fe892Spooka {
513510a73e0Spooka 
51486c3d192Spooka 	return;
515510a73e0Spooka }
5166e4a9f91Spooka #endif
517