Home | History | Annotate | Line # | Download | only in ld.elf_so
      1 /*	$NetBSD: paths.c,v 1.43 2025/05/02 23:04:56 riastradh Exp $	 */
      2 
      3 /*
      4  * Copyright 1996 Matt Thomas <matt (at) 3am-software.com>
      5  * Copyright 2002 Charles M. Hannum <root (at) ihack.net>
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. The name of the author may not be used to endorse or promote products
     17  *    derived from this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 #ifndef lint
     33 __RCSID("$NetBSD: paths.c,v 1.43 2025/05/02 23:04:56 riastradh Exp $");
     34 #endif /* not lint */
     35 
     36 #include <err.h>
     37 #include <errno.h>
     38 #include <fcntl.h>
     39 #include <stdarg.h>
     40 #include <stdio.h>
     41 #include <stdlib.h>
     42 #include <string.h>
     43 #include <unistd.h>
     44 #include <sys/types.h>
     45 #include <sys/param.h>
     46 #include <sys/sysctl.h>
     47 #include <sys/mman.h>
     48 #include <sys/stat.h>
     49 #include <sys/gmon.h>
     50 #include <sys/socket.h>
     51 #include <sys/mount.h>
     52 #include <sys/resource.h>
     53 #include <machine/cpu.h>
     54 
     55 #include "debug.h"
     56 #include "rtld.h"
     57 
     58 #include "../../lib/libc/include/__sysctl.h" /* __sysctl syscall stub */
     59 
     60 static Search_Path *_rtld_find_path(Search_Path *, const char *, size_t);
     61 static Search_Path **_rtld_append_path(Search_Path **, Search_Path **,
     62     const char *, const char *, const char *);
     63 static void _rtld_process_mapping(Library_Xform **, const char *,
     64     const char *);
     65 static char *exstrdup(const char *, const char *);
     66 static const char *getstr(const char **, const char *, const char *);
     67 static const char *getcstr(const char **, const char *, const char *);
     68 static const char *getword(const char **, const char *, const char *);
     69 static int matchstr(const char *, const char *, const char *);
     70 
     71 static const char WS[] = " \t\n";
     72 
     73 /*
     74  * Like xstrdup(), but takes end of string as a argument.
     75  */
     76 static char *
     77 exstrdup(const char *bp, const char *ep)
     78 {
     79 	char *cp;
     80 	size_t len = ep - bp;
     81 
     82 	cp = xmalloc(len + 1);
     83 	memcpy(cp, bp, len);
     84 	cp[len] = '\0';
     85 	return (cp);
     86 }
     87 
     88 /*
     89  * Like strsep(), but takes end of string and doesn't put any NUL.  To
     90  * detect empty string, compare `*p' and return value.
     91  */
     92 static const char *
     93 getstr(const char **p, const char *ep, const char *delim)
     94 {
     95 	const char *cp = *p, *q, *r;
     96 
     97 	if (ep < cp)
     98 		/* End of string */
     99 		return (NULL);
    100 
    101 	for (q = cp; q < ep; q++)
    102 		for (r = delim; *r != 0; r++)
    103 			if (*r == *q)
    104 				goto done;
    105 
    106 done:
    107 	*p = q;
    108 	return (cp);
    109 }
    110 
    111 /*
    112  * Like getstr() above, but delim[] is complemented.
    113  */
    114 static const char *
    115 getcstr(const char **p, const char *ep, const char *delim)
    116 {
    117 	const char *cp = *p, *q, *r;
    118 
    119 	if (ep < cp)
    120 		/* End of string */
    121 		return (NULL);
    122 
    123 	for (q = cp; q < ep; q++)
    124 		for (r = delim; *r != *q; r++)
    125 			if (*r == 0)
    126 				goto done;
    127 
    128 done:
    129 	*p = q;
    130 	return (cp);
    131 }
    132 
    133 static const char *
    134 getword(const char **p, const char *ep, const char *delim)
    135 {
    136 
    137 	(void)getcstr(p, ep, delim);
    138 
    139 	/*
    140 	 * Now, we're looking non-delim, or end of string.
    141 	 */
    142 
    143 	return (getstr(p, ep, delim));
    144 }
    145 
    146 /*
    147  * Match `bp' against NUL terminated string pointed by `p'.
    148  */
    149 static int
    150 matchstr(const char *p, const char *bp, const char *ep)
    151 {
    152 	int c;
    153 
    154 	while (bp < ep)
    155 		if ((c = *p++) == 0 || c != *bp++)
    156 			return (0);
    157 
    158 	return (*p == 0);
    159 }
    160 
    161 static Search_Path *
    162 _rtld_find_path(Search_Path *path, const char *pathstr, size_t pathlen)
    163 {
    164 
    165 	for (; path != NULL; path = path->sp_next) {
    166 		if (pathlen == path->sp_pathlen &&
    167 		    memcmp(path->sp_path, pathstr, pathlen) == 0)
    168 			return path;
    169 	}
    170 	return NULL;
    171 }
    172 
    173 static Search_Path **
    174 _rtld_append_path(Search_Path **head_p, Search_Path **path_p,
    175     const char *execname, const char *bp, const char *ep)
    176 {
    177 	Search_Path *path;
    178 	char epath[MAXPATHLEN];
    179 	size_t len;
    180 
    181 	len = _rtld_expand_path(epath, sizeof(epath), execname, bp, ep);
    182 	if (len == 0)
    183 		return path_p;
    184 
    185 	if (_rtld_find_path(*head_p, bp, ep - bp) != NULL)
    186 		return path_p;
    187 
    188 	path = NEW(Search_Path);
    189 	path->sp_pathlen = len;
    190 	path->sp_path = exstrdup(epath, epath + len);
    191 	path->sp_next = (*path_p);
    192 	(*path_p) = path;
    193 	path_p = &path->sp_next;
    194 
    195 	dbg((" added path \"%s\"", path->sp_path));
    196 	return path_p;
    197 }
    198 
    199 void
    200 _rtld_add_paths(const char *execname, Search_Path **path_p, const char *pathstr)
    201 {
    202 	Search_Path **head_p = path_p;
    203 
    204 	if (pathstr == NULL)
    205 		return;
    206 
    207 	if (pathstr[0] == ':') {
    208 		/*
    209 		 * Leading colon means append to current path
    210 		 */
    211 		while ((*path_p) != NULL)
    212 			path_p = &(*path_p)->sp_next;
    213 		pathstr++;
    214 	}
    215 
    216 	for (;;) {
    217 		const char *bp = pathstr;
    218 		const char *ep = strchr(bp, ':');
    219 		if (ep == NULL)
    220 			ep = &pathstr[strlen(pathstr)];
    221 
    222 		path_p = _rtld_append_path(head_p, path_p, execname, bp, ep);
    223 
    224 		if (ep[0] == '\0')
    225 			break;
    226 		pathstr = ep + 1;
    227 	}
    228 }
    229 
    230 /*
    231  * Process library mappings of the form:
    232  *	<library_name>	<machdep_variable> <value,...:library_name,...> ...
    233  */
    234 static void
    235 _rtld_process_mapping(Library_Xform **lib_p, const char *bp, const char *ep)
    236 {
    237 	Library_Xform *hwptr = NULL;
    238 	const char *ptr, *key, *ekey, *lib, *elib, *l;
    239 	int i, j;
    240 
    241 	dbg((" processing mapping \"%.*s\"", (int)(ep - bp), bp));
    242 
    243 	if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp)
    244 		return;
    245 
    246 	dbg((" library \"%.*s\"", (int)(bp - ptr), ptr));
    247 
    248 	hwptr = xmalloc(sizeof(*hwptr));
    249 	memset(hwptr, 0, sizeof(*hwptr));
    250 	hwptr->name = exstrdup(ptr, bp);
    251 
    252 	bp++;
    253 
    254 	if ((ptr = getword(&bp, ep, WS)) == NULL || ptr == bp) {
    255 		xwarnx("missing sysctl variable name");
    256 		goto cleanup;
    257 	}
    258 
    259 	dbg((" sysctl \"%.*s\"", (int)(bp - ptr), ptr));
    260 
    261 	hwptr->ctlname = exstrdup(ptr, bp);
    262 
    263 	for (i = 0; bp++, (ptr = getword(&bp, ep, WS)) != NULL;) {
    264 		dbg((" ptr = %.*s", (int)(bp - ptr), ptr));
    265 		if (ptr == bp)
    266 			continue;
    267 
    268 		if (i == RTLD_MAX_ENTRY) {
    269 no_more:
    270 			xwarnx("maximum library entries exceeded `%s'",
    271 			    hwptr->name);
    272 			goto cleanup;
    273 		}
    274 		if ((key = getstr(&ptr, bp, ":")) == NULL) {
    275 			xwarnx("missing sysctl variable value for `%s'",
    276 			    hwptr->name);
    277 			goto cleanup;
    278 		}
    279 		ekey = ptr++;
    280 		if ((lib = getstr(&ptr, bp, ":")) == NULL) {
    281 			xwarnx("missing sysctl library list for `%s'",
    282 			    hwptr->name);
    283 			goto cleanup;
    284 		}
    285 		elib = ptr;		/* No need to advance */
    286 		for (j = 0; (l = getstr(&lib, elib, ",")) != NULL;
    287 		    j++, lib++) {
    288 			if (j == RTLD_MAX_LIBRARY) {
    289 				xwarnx("maximum library entries exceeded `%s'",
    290 				    hwptr->name);
    291 				goto cleanup;
    292 			}
    293 			dbg((" library \"%.*s\"", (int)(lib - l), l));
    294 			hwptr->entry[i].library[j] = exstrdup(l, lib);
    295 		}
    296 		if (j == 0) {
    297 			xwarnx("No library map entries for `%s/%.*s'",
    298 			    hwptr->name, (int)(bp - ptr), ptr);
    299 			goto cleanup;
    300 		}
    301 		j = i;
    302 		for (; (l = getstr(&key, ekey, ",")) != NULL; i++, key++) {
    303 			/*
    304 			 * Allow empty key (it is valid as string
    305 			 * value).  Thus, we loop at least once and
    306 			 * `i' is incremented.
    307 			 */
    308 
    309 			dbg((" key \"%.*s\"", (int)(key - l), l));
    310 			if (i == RTLD_MAX_ENTRY)
    311 				goto no_more;
    312 			if (i != j)
    313 				(void)memcpy(hwptr->entry[i].library,
    314 				    hwptr->entry[j].library,
    315 				    sizeof(hwptr->entry[j].library));
    316 			hwptr->entry[i].value = exstrdup(l, key);
    317 		}
    318 	}
    319 
    320 	if (i == 0) {
    321 		xwarnx("No library entries for `%s'", hwptr->name);
    322 		goto cleanup;
    323 	}
    324 
    325 	hwptr->next = *lib_p;
    326 	*lib_p = hwptr;
    327 
    328 	return;
    329 
    330 cleanup:
    331 	if (hwptr->name)
    332 		xfree(hwptr->name);
    333 	xfree(hwptr);
    334 }
    335 
    336 void
    337 _rtld_process_hints(const char *execname, Search_Path **path_p,
    338     Library_Xform **lib_p, const char *fname)
    339 {
    340 	int fd;
    341 	char *buf, small[128];
    342 	const char *b, *ep, *ptr;
    343 	struct stat st;
    344 	ssize_t sz;
    345 	Search_Path **head_p = path_p;
    346 
    347 	if ((fd = open(fname, O_RDONLY)) == -1) {
    348 		/* Don't complain */
    349 		return;
    350 	}
    351 
    352 	/* Try to avoid mmap/stat on the file. */
    353 	buf = small;
    354 	buf[0] = '\0';
    355 	sz = read(fd, buf, sizeof(small));
    356 	if (sz == -1) {
    357 		xwarn("read: %s", fname);
    358 		(void)close(fd);
    359 		return;
    360 	}
    361 	if (sz >= (ssize_t)sizeof(small)) {
    362 		if (fstat(fd, &st) == -1) {
    363 			/* Complain */
    364 			xwarn("fstat: %s", fname);
    365 			(void)close(fd);
    366 			return;
    367 		}
    368 
    369 		sz = (ssize_t) st.st_size;
    370 
    371 		buf = mmap(0, sz, PROT_READ, MAP_SHARED|MAP_FILE, fd, 0);
    372 		if (buf == MAP_FAILED) {
    373 			xwarn("mmap: %s", fname);
    374 			(void)close(fd);
    375 			return;
    376 		}
    377 	}
    378 	(void)close(fd);
    379 
    380 	while ((*path_p) != NULL)
    381 		path_p = &(*path_p)->sp_next;
    382 
    383 	for (b = buf, ep = buf + sz; b < ep; b++) {
    384 		(void)getcstr(&b, ep, WS);
    385 		if (b == ep)
    386 			break;
    387 
    388 		ptr = getstr(&b, ep, "\n#");
    389 		if (*ptr == '/') {
    390 			/*
    391 			 * Since '/' != '\n' and != '#', we know ptr <
    392 			 * b.  And we will stop when b[-1] == '/'.
    393 			 */
    394 			while (b[-1] == ' ' || b[-1] == '\t')
    395 				b--;
    396 			path_p = _rtld_append_path(head_p, path_p, execname,
    397 			    ptr, b);
    398 		} else
    399 			_rtld_process_mapping(lib_p, ptr, b);
    400 
    401 		/*
    402 		 * b points one of ' ', \t, \n, # or equal to ep.  So,
    403 		 * make sure we are at newline or end of string.
    404 		 */
    405 		(void)getstr(&b, ep, "\n");
    406 	}
    407 
    408 	if (buf != small)
    409 		(void)munmap(buf, sz);
    410 }
    411 
    412 /* Basic name -> sysctl MIB translation */
    413 int
    414 _rtld_sysctl(const char *name, void *oldp, size_t *oldlen)
    415 {
    416 	const char *node, *ep;
    417 	struct sysctlnode query, *result, *newresult;
    418 	int mib[CTL_MAXNAME], r;
    419 	size_t res_size, n, i;
    420 	u_int miblen = 0;
    421 
    422 	/* Start with 16 entries, will grow it up as needed. */
    423 	res_size = 16 * sizeof(struct sysctlnode);
    424 	result = xmalloc(res_size);
    425 	if (result == NULL)
    426 		return (-1);
    427 
    428 	ep = name + strlen(name);
    429 	do {
    430 		i = ~0ul;
    431 		while (*name == '/' || *name == '.')
    432 			name++;
    433 		if (name >= ep)
    434 			break;
    435 
    436 		mib[miblen] = CTL_QUERY;
    437 		memset(&query, 0, sizeof(query));
    438 		query.sysctl_flags = SYSCTL_VERSION;
    439 
    440 		n = res_size;
    441 		if (__sysctl(mib, miblen + 1, result, &n, &query,
    442 		    sizeof(query)) == -1) {
    443 			if (errno != ENOMEM)
    444 				goto bad;
    445 			/* Grow up result */
    446 			res_size = n;
    447 			newresult = xrealloc(result, res_size);
    448 			if (newresult == NULL)
    449 				goto bad;
    450 			result = newresult;
    451 			if (__sysctl(mib, miblen + 1, result, &n, &query,
    452 			    sizeof(query)) == -1)
    453 				goto bad;
    454 		}
    455 		n /= sizeof(struct sysctlnode);
    456 
    457 		node = getstr(&name, ep, "./");
    458 
    459 		for (i = 0; i < n; i++)
    460 			if (matchstr(result[i].sysctl_name, node, name)) {
    461 				mib[miblen] = result[i].sysctl_num;
    462 				miblen++;
    463 				break;
    464 			}
    465 	} while (name < ep && miblen <= CTL_MAXNAME);
    466 
    467 	if (name < ep || i == ~0ul)
    468 		goto bad;
    469 	r = SYSCTL_TYPE(result[i].sysctl_flags);
    470 
    471 	xfree(result);
    472 	if (__sysctl(mib, miblen, oldp, oldlen, NULL, 0) == -1)
    473 		return (-1);
    474 	return r;
    475 
    476 bad:
    477 	xfree(result);
    478 	return (-1);
    479 }
    480