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