xref: /freebsd-13.1/libexec/rtld-elf/libmap.c (revision b36070f5)
1 /*
2  * $FreeBSD$
3  */
4 
5 #include <stdio.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <sys/queue.h>
9 #include <sys/param.h>
10 
11 #include "debug.h"
12 #include "rtld.h"
13 #include "libmap.h"
14 
15 #ifndef _PATH_LIBMAP_CONF
16 #define	_PATH_LIBMAP_CONF	"/etc/libmap.conf"
17 #endif
18 
19 #ifdef COMPAT_32BIT
20 #undef _PATH_LIBMAP_CONF
21 #define	_PATH_LIBMAP_CONF	"/etc/libmap32.conf"
22 #endif
23 
24 TAILQ_HEAD(lm_list, lm);
25 struct lm {
26 	char *f;
27 	char *t;
28 
29 	TAILQ_ENTRY(lm)	lm_link;
30 };
31 
32 TAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head);
33 struct lmp {
34 	char *p;
35 	enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type;
36 	struct lm_list lml;
37 	TAILQ_ENTRY(lmp) lmp_link;
38 };
39 
40 static int	lm_count;
41 
42 static void		lmc_parse	(FILE *);
43 static void		lm_add		(const char *, const char *, const char *);
44 static void		lm_free		(struct lm_list *);
45 static char *		lml_find	(struct lm_list *, const char *);
46 static struct lm_list *	lmp_find	(const char *);
47 static struct lm_list *	lmp_init	(char *);
48 static const char * quickbasename	(const char *);
49 static int	readstrfn	(void * cookie, char *buf, int len);
50 static int	closestrfn	(void * cookie);
51 
52 #define	iseol(c)	(((c) == '#') || ((c) == '\0') || \
53 			 ((c) == '\n') || ((c) == '\r'))
54 
55 /*
56  * Do not use ctype.h macros, which rely on working TLS.  It is
57  * too early to have thread-local variables functional.
58  */
59 #define	isspace1(c)	((c) == ' ' || (c) == '\t')
60 
61 int
62 lm_init (char *libmap_override)
63 {
64 	FILE	*fp;
65 
66 	dbg("%s(\"%s\")", __func__, libmap_override);
67 
68 	TAILQ_INIT(&lmp_head);
69 
70 	fp = fopen(_PATH_LIBMAP_CONF, "r");
71 	if (fp) {
72 		lmc_parse(fp);
73 		fclose(fp);
74 	}
75 
76 	if (libmap_override) {
77 		char	*p;
78 		/* do some character replacement to make $LIBMAP look like a
79 		   text file, then "open" it with funopen */
80 		libmap_override = xstrdup(libmap_override);
81 
82 		for (p = libmap_override; *p; p++) {
83 			switch (*p) {
84 				case '=':
85 					*p = ' '; break;
86 				case ',':
87 					*p = '\n'; break;
88 			}
89 		}
90 		fp = funopen(libmap_override, readstrfn, NULL, NULL, closestrfn);
91 		if (fp) {
92 			lmc_parse(fp);
93 			fclose(fp);
94 		}
95 	}
96 
97 	return (lm_count == 0);
98 }
99 
100 static void
101 lmc_parse (FILE *fp)
102 {
103 	char	*cp;
104 	char	*f, *t, *c, *p;
105 	char	prog[MAXPATHLEN];
106 	char	line[MAXPATHLEN + 2];
107 
108 	dbg("%s(%p)", __func__, fp);
109 
110 	p = NULL;
111 	while ((cp = fgets(line, MAXPATHLEN + 1, fp)) != NULL) {
112 		t = f = c = NULL;
113 
114 		/* Skip over leading space */
115 		while (isspace1(*cp)) cp++;
116 
117 		/* Found a comment or EOL */
118 		if (iseol(*cp)) continue;
119 
120 		/* Found a constraint selector */
121 		if (*cp == '[') {
122 			cp++;
123 
124 			/* Skip leading space */
125 			while (isspace1(*cp)) cp++;
126 
127 			/* Found comment, EOL or end of selector */
128 			if  (iseol(*cp) || *cp == ']')
129 				continue;
130 
131 			c = cp++;
132 			/* Skip to end of word */
133 			while (!isspace1(*cp) && !iseol(*cp) && *cp != ']')
134 				cp++;
135 
136 			/* Skip and zero out trailing space */
137 			while (isspace1(*cp)) *cp++ = '\0';
138 
139 			/* Check if there is a closing brace */
140 			if (*cp != ']') continue;
141 
142 			/* Terminate string if there was no trailing space */
143 			*cp++ = '\0';
144 
145 			/*
146 			 * There should be nothing except whitespace or comment
147 			  from this point to the end of the line.
148 			 */
149 			while(isspace1(*cp)) cp++;
150 			if (!iseol(*cp)) continue;
151 
152 			strcpy(prog, c);
153 			p = prog;
154 			continue;
155 		}
156 
157 		/* Parse the 'from' candidate. */
158 		f = cp++;
159 		while (!isspace1(*cp) && !iseol(*cp)) cp++;
160 
161 		/* Skip and zero out the trailing whitespace */
162 		while (isspace1(*cp)) *cp++ = '\0';
163 
164 		/* Found a comment or EOL */
165 		if (iseol(*cp)) continue;
166 
167 		/* Parse 'to' mapping */
168 		t = cp++;
169 		while (!isspace1(*cp) && !iseol(*cp)) cp++;
170 
171 		/* Skip and zero out the trailing whitespace */
172 		while (isspace1(*cp)) *cp++ = '\0';
173 
174 		/* Should be no extra tokens at this point */
175 		if (!iseol(*cp)) continue;
176 
177 		*cp = '\0';
178 		lm_add(p, f, t);
179 	}
180 }
181 
182 static void
183 lm_free (struct lm_list *lml)
184 {
185 	struct lm *lm;
186 
187 	dbg("%s(%p)", __func__, lml);
188 
189 	while (!TAILQ_EMPTY(lml)) {
190 		lm = TAILQ_FIRST(lml);
191 		TAILQ_REMOVE(lml, lm, lm_link);
192 		free(lm->f);
193 		free(lm->t);
194 		free(lm);
195 	}
196 	return;
197 }
198 
199 void
200 lm_fini (void)
201 {
202 	struct lmp *lmp;
203 
204 	dbg("%s()", __func__);
205 
206 	while (!TAILQ_EMPTY(&lmp_head)) {
207 		lmp = TAILQ_FIRST(&lmp_head);
208 		TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
209 		free(lmp->p);
210 		lm_free(&lmp->lml);
211 		free(lmp);
212 	}
213 	return;
214 }
215 
216 static void
217 lm_add (const char *p, const char *f, const char *t)
218 {
219 	struct lm_list *lml;
220 	struct lm *lm;
221 
222 	if (p == NULL)
223 		p = "$DEFAULT$";
224 
225 	dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t);
226 
227 	if ((lml = lmp_find(p)) == NULL)
228 		lml = lmp_init(xstrdup(p));
229 
230 	lm = xmalloc(sizeof(struct lm));
231 	lm->f = xstrdup(f);
232 	lm->t = xstrdup(t);
233 	TAILQ_INSERT_HEAD(lml, lm, lm_link);
234 	lm_count++;
235 }
236 
237 char *
238 lm_find (const char *p, const char *f)
239 {
240 	struct lm_list *lml;
241 	char *t;
242 
243 	dbg("%s(\"%s\", \"%s\")", __func__, p, f);
244 
245 	if (p != NULL && (lml = lmp_find(p)) != NULL) {
246 		t = lml_find(lml, f);
247 		if (t != NULL) {
248 			/*
249 			 * Add a global mapping if we have
250 			 * a successful constrained match.
251 			 */
252 			lm_add(NULL, f, t);
253 			return (t);
254 		}
255 	}
256 	lml = lmp_find("$DEFAULT$");
257 	if (lml != NULL)
258 		return (lml_find(lml, f));
259 	else
260 		return (NULL);
261 }
262 
263 /* Given a libmap translation list and a library name, return the
264    replacement library, or NULL */
265 #ifdef COMPAT_32BIT
266 char *
267 lm_findn (const char *p, const char *f, const int n)
268 {
269 	char pathbuf[64], *s, *t;
270 
271 	if (n < sizeof(pathbuf) - 1)
272 		s = pathbuf;
273 	else
274 		s = xmalloc(n + 1);
275 	memcpy(s, f, n);
276 	s[n] = '\0';
277 	t = lm_find(p, s);
278 	if (s != pathbuf)
279 		free(s);
280 	return (t);
281 }
282 #endif
283 
284 static char *
285 lml_find (struct lm_list *lmh, const char *f)
286 {
287 	struct lm *lm;
288 
289 	dbg("%s(%p, \"%s\")", __func__, lmh, f);
290 
291 	TAILQ_FOREACH(lm, lmh, lm_link)
292 		if (strcmp(f, lm->f) == 0)
293 			return (lm->t);
294 	return (NULL);
295 }
296 
297 /* Given an executable name, return a pointer to the translation list or
298    NULL if no matches */
299 static struct lm_list *
300 lmp_find (const char *n)
301 {
302 	struct lmp *lmp;
303 
304 	dbg("%s(\"%s\")", __func__, n);
305 
306 	TAILQ_FOREACH(lmp, &lmp_head, lmp_link)
307 		if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) ||
308 		    (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) ||
309 		    (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0))
310 			return (&lmp->lml);
311 	return (NULL);
312 }
313 
314 static struct lm_list *
315 lmp_init (char *n)
316 {
317 	struct lmp *lmp;
318 
319 	dbg("%s(\"%s\")", __func__, n);
320 
321 	lmp = xmalloc(sizeof(struct lmp));
322 	lmp->p = n;
323 	if (n[strlen(n)-1] == '/')
324 		lmp->type = T_DIRECTORY;
325 	else if (strchr(n,'/') == NULL)
326 		lmp->type = T_BASENAME;
327 	else
328 		lmp->type = T_EXACT;
329 	TAILQ_INIT(&lmp->lml);
330 	TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link);
331 
332 	return (&lmp->lml);
333 }
334 
335 /* libc basename is overkill.  Return a pointer to the character after the
336    last /, or the original string if there are no slashes. */
337 static const char *
338 quickbasename (const char *path)
339 {
340 	const char *p = path;
341 	for (; *path; path++) {
342 		if (*path == '/')
343 			p = path+1;
344 	}
345 	return (p);
346 }
347 
348 static int
349 readstrfn(void * cookie, char *buf, int len)
350 {
351 	static char	*current;
352 	static int	left;
353 	int 	copied;
354 
355 	copied = 0;
356 	if (!current) {
357 		current = cookie;
358 		left = strlen(cookie);
359 	}
360 	while (*current && left && len) {
361 		*buf++ = *current++;
362 		left--;
363 		len--;
364 		copied++;
365 	}
366 	return copied;
367 }
368 
369 static int
370 closestrfn(void * cookie)
371 {
372 	free(cookie);
373 	return 0;
374 }
375