libmap.c revision 232590
1113229Smdodd/*
2113229Smdodd * $FreeBSD: head/libexec/rtld-elf/libmap.c 232590 2012-03-06 09:34:30Z pluknet $
3113229Smdodd */
4113229Smdodd
5113229Smdodd#include <stdio.h>
6113229Smdodd#include <string.h>
7113229Smdodd#include <stdlib.h>
8113229Smdodd#include <sys/queue.h>
9113229Smdodd#include <sys/param.h>
10113229Smdodd
11115150Smdodd#include "debug.h"
12115150Smdodd#include "rtld.h"
13116513Smdodd#include "libmap.h"
14115150Smdodd
15113229Smdodd#ifndef _PATH_LIBMAP_CONF
16113229Smdodd#define	_PATH_LIBMAP_CONF	"/etc/libmap.conf"
17113229Smdodd#endif
18113229Smdodd
19127250Speter#ifdef COMPAT_32BIT
20127250Speter#undef _PATH_LIBMAP_CONF
21127250Speter#define	_PATH_LIBMAP_CONF	"/etc/libmap32.conf"
22127250Speter#endif
23127250Speter
24113229SmdoddTAILQ_HEAD(lm_list, lm);
25113229Smdoddstruct lm {
26113229Smdodd	char *f;
27113229Smdodd	char *t;
28113229Smdodd
29113229Smdodd	TAILQ_ENTRY(lm)	lm_link;
30113229Smdodd};
31113229Smdodd
32113229SmdoddTAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head);
33113229Smdoddstruct lmp {
34113229Smdodd	char *p;
35129638Smdodd	enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type;
36113229Smdodd	struct lm_list lml;
37113229Smdodd	TAILQ_ENTRY(lmp) lmp_link;
38113229Smdodd};
39113229Smdodd
40141232Smdoddstatic int	lm_count;
41141232Smdodd
42141232Smdoddstatic void		lmc_parse	(FILE *);
43115400Smdoddstatic void		lm_add		(const char *, const char *, const char *);
44113229Smdoddstatic void		lm_free		(struct lm_list *);
45113229Smdoddstatic char *		lml_find	(struct lm_list *, const char *);
46113229Smdoddstatic struct lm_list *	lmp_find	(const char *);
47113229Smdoddstatic struct lm_list *	lmp_init	(char *);
48129638Smdoddstatic const char * quickbasename	(const char *);
49141232Smdoddstatic int	readstrfn	(void * cookie, char *buf, int len);
50141232Smdoddstatic int	closestrfn	(void * cookie);
51113229Smdodd
52113312Smdodd#define	iseol(c)	(((c) == '#') || ((c) == '\0') || \
53113312Smdodd			 ((c) == '\n') || ((c) == '\r'))
54113312Smdodd
55232572Skib/*
56232572Skib * Do not use ctype.h macros, which rely on working TLS.  It is
57232572Skib * too early to have thread-local variables functional.
58232572Skib */
59232590Spluknet#define	rtld_isspace(c)	((c) == ' ' || (c) == '\t')
60232572Skib
61120038Smdoddint
62141232Smdoddlm_init (char *libmap_override)
63113229Smdodd{
64113229Smdodd	FILE	*fp;
65113229Smdodd
66141232Smdodd	dbg("%s(\"%s\")", __func__, libmap_override);
67115445Smdodd
68113229Smdodd	TAILQ_INIT(&lmp_head);
69113229Smdodd
70141232Smdodd	fp = fopen(_PATH_LIBMAP_CONF, "r");
71141232Smdodd	if (fp) {
72141232Smdodd		lmc_parse(fp);
73141232Smdodd		fclose(fp);
74141232Smdodd	}
75113229Smdodd
76141232Smdodd	if (libmap_override) {
77141232Smdodd		char	*p;
78141232Smdodd		/* do some character replacement to make $LIBMAP look like a
79141232Smdodd		   text file, then "open" it with funopen */
80141232Smdodd		libmap_override = xstrdup(libmap_override);
81141232Smdodd
82141232Smdodd		for (p = libmap_override; *p; p++) {
83141232Smdodd			switch (*p) {
84141232Smdodd				case '=':
85141232Smdodd					*p = ' '; break;
86141232Smdodd				case ',':
87141232Smdodd					*p = '\n'; break;
88141232Smdodd			}
89141232Smdodd		}
90141232Smdodd		fp = funopen(libmap_override, readstrfn, NULL, NULL, closestrfn);
91141232Smdodd		if (fp) {
92141232Smdodd			lmc_parse(fp);
93141232Smdodd			fclose(fp);
94141232Smdodd		}
95141232Smdodd	}
96141232Smdodd
97141232Smdodd	return (lm_count == 0);
98141232Smdodd}
99141232Smdodd
100141232Smdoddstatic void
101141232Smdoddlmc_parse (FILE *fp)
102141232Smdodd{
103141232Smdodd	char	*cp;
104141232Smdodd	char	*f, *t, *c, *p;
105141232Smdodd	char	prog[MAXPATHLEN];
106141232Smdodd	char	line[MAXPATHLEN + 2];
107141232Smdodd
108141232Smdodd	dbg("%s(%p)", __func__, fp);
109141232Smdodd
110113229Smdodd	p = NULL;
111113229Smdodd	while ((cp = fgets(line, MAXPATHLEN + 1, fp)) != NULL) {
112115400Smdodd		t = f = c = NULL;
113113312Smdodd
114113229Smdodd		/* Skip over leading space */
115232590Spluknet		while (rtld_isspace(*cp)) cp++;
116113312Smdodd
117113229Smdodd		/* Found a comment or EOL */
118114316Skan		if (iseol(*cp)) continue;
119113312Smdodd
120113312Smdodd		/* Found a constraint selector */
121113229Smdodd		if (*cp == '[') {
122113229Smdodd			cp++;
123113312Smdodd
124113229Smdodd			/* Skip leading space */
125232590Spluknet			while (rtld_isspace(*cp)) cp++;
126113312Smdodd
127113229Smdodd			/* Found comment, EOL or end of selector */
128114316Skan			if  (iseol(*cp) || *cp == ']')
129114316Skan				continue;
130113312Smdodd
131115400Smdodd			c = cp++;
132113229Smdodd			/* Skip to end of word */
133232590Spluknet			while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']')
134114316Skan				cp++;
135113312Smdodd
136113312Smdodd			/* Skip and zero out trailing space */
137232590Spluknet			while (rtld_isspace(*cp)) *cp++ = '\0';
138113312Smdodd
139113312Smdodd			/* Check if there is a closing brace */
140114316Skan			if (*cp != ']') continue;
141113312Smdodd
142113312Smdodd			/* Terminate string if there was no trailing space */
143113229Smdodd			*cp++ = '\0';
144113312Smdodd
145113312Smdodd			/*
146113312Smdodd			 * There should be nothing except whitespace or comment
147115396Skan			  from this point to the end of the line.
148113312Smdodd			 */
149232590Spluknet			while(rtld_isspace(*cp)) cp++;
150114316Skan			if (!iseol(*cp)) continue;
151113312Smdodd
152115400Smdodd			strcpy(prog, c);
153114316Skan			p = prog;
154114316Skan			continue;
155113229Smdodd		}
156113312Smdodd
157113312Smdodd		/* Parse the 'from' candidate. */
158114316Skan		f = cp++;
159232590Spluknet		while (!rtld_isspace(*cp) && !iseol(*cp)) cp++;
160113312Smdodd
161113312Smdodd		/* Skip and zero out the trailing whitespace */
162232590Spluknet		while (rtld_isspace(*cp)) *cp++ = '\0';
163113312Smdodd
164113312Smdodd		/* Found a comment or EOL */
165114316Skan		if (iseol(*cp)) continue;
166113312Smdodd
167113312Smdodd		/* Parse 'to' mapping */
168114316Skan		t = cp++;
169232590Spluknet		while (!rtld_isspace(*cp) && !iseol(*cp)) cp++;
170115396Skan
171114316Skan		/* Skip and zero out the trailing whitespace */
172232590Spluknet		while (rtld_isspace(*cp)) *cp++ = '\0';
173113229Smdodd
174114316Skan		/* Should be no extra tokens at this point */
175114316Skan		if (!iseol(*cp)) continue;
176114316Skan
177114316Skan		*cp = '\0';
178115400Smdodd		lm_add(p, f, t);
179113229Smdodd	}
180113229Smdodd}
181113229Smdodd
182113229Smdoddstatic void
183113229Smdoddlm_free (struct lm_list *lml)
184113229Smdodd{
185113229Smdodd	struct lm *lm;
186113229Smdodd
187115445Smdodd	dbg("%s(%p)", __func__, lml);
188115445Smdodd
189113229Smdodd	while (!TAILQ_EMPTY(lml)) {
190113229Smdodd		lm = TAILQ_FIRST(lml);
191113229Smdodd		TAILQ_REMOVE(lml, lm, lm_link);
192113229Smdodd		free(lm->f);
193113229Smdodd		free(lm->t);
194113229Smdodd		free(lm);
195113229Smdodd	}
196113229Smdodd	return;
197113229Smdodd}
198113229Smdodd
199113229Smdoddvoid
200113229Smdoddlm_fini (void)
201113229Smdodd{
202113229Smdodd	struct lmp *lmp;
203113229Smdodd
204115445Smdodd	dbg("%s()", __func__);
205115445Smdodd
206113229Smdodd	while (!TAILQ_EMPTY(&lmp_head)) {
207113229Smdodd		lmp = TAILQ_FIRST(&lmp_head);
208113229Smdodd		TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
209113229Smdodd		free(lmp->p);
210113229Smdodd		lm_free(&lmp->lml);
211113229Smdodd		free(lmp);
212113229Smdodd	}
213113229Smdodd	return;
214113229Smdodd}
215113229Smdodd
216113229Smdoddstatic void
217115400Smdoddlm_add (const char *p, const char *f, const char *t)
218113229Smdodd{
219113229Smdodd	struct lm_list *lml;
220113229Smdodd	struct lm *lm;
221113229Smdodd
222113229Smdodd	if (p == NULL)
223113229Smdodd		p = "$DEFAULT$";
224113229Smdodd
225115445Smdodd	dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t);
226115445Smdodd
227113229Smdodd	if ((lml = lmp_find(p)) == NULL)
228115150Smdodd		lml = lmp_init(xstrdup(p));
229113229Smdodd
230115150Smdodd	lm = xmalloc(sizeof(struct lm));
231115400Smdodd	lm->f = xstrdup(f);
232115400Smdodd	lm->t = xstrdup(t);
233113229Smdodd	TAILQ_INSERT_HEAD(lml, lm, lm_link);
234141232Smdodd	lm_count++;
235113229Smdodd}
236113229Smdodd
237113229Smdoddchar *
238113229Smdoddlm_find (const char *p, const char *f)
239113229Smdodd{
240113229Smdodd	struct lm_list *lml;
241113229Smdodd	char *t;
242113229Smdodd
243115445Smdodd	dbg("%s(\"%s\", \"%s\")", __func__, p, f);
244115445Smdodd
245113229Smdodd	if (p != NULL && (lml = lmp_find(p)) != NULL) {
246113229Smdodd		t = lml_find(lml, f);
247115150Smdodd		if (t != NULL) {
248115150Smdodd			/*
249115150Smdodd			 * Add a global mapping if we have
250115150Smdodd			 * a successful constrained match.
251115150Smdodd			 */
252115400Smdodd			lm_add(NULL, f, t);
253113229Smdodd			return (t);
254115150Smdodd		}
255113229Smdodd	}
256113229Smdodd	lml = lmp_find("$DEFAULT$");
257113229Smdodd	if (lml != NULL)
258113229Smdodd		return (lml_find(lml, f));
259113229Smdodd	else
260113229Smdodd		return (NULL);
261113229Smdodd}
262113229Smdodd
263129638Smdodd/* Given a libmap translation list and a library name, return the
264129638Smdodd   replacement library, or NULL */
265127250Speter#ifdef COMPAT_32BIT
266127250Speterchar *
267127250Speterlm_findn (const char *p, const char *f, const int n)
268127250Speter{
269127250Speter	char pathbuf[64], *s, *t;
270127250Speter
271155084Speter	if (n < sizeof(pathbuf) - 1)
272127250Speter		s = pathbuf;
273155084Speter	else
274127250Speter		s = xmalloc(n + 1);
275155084Speter	memcpy(s, f, n);
276155084Speter	s[n] = '\0';
277127250Speter	t = lm_find(p, s);
278127250Speter	if (s != pathbuf)
279127250Speter		free(s);
280127250Speter	return (t);
281127250Speter}
282127250Speter#endif
283127250Speter
284113229Smdoddstatic char *
285113229Smdoddlml_find (struct lm_list *lmh, const char *f)
286113229Smdodd{
287113229Smdodd	struct lm *lm;
288113229Smdodd
289115445Smdodd	dbg("%s(%p, \"%s\")", __func__, lmh, f);
290115445Smdodd
291113229Smdodd	TAILQ_FOREACH(lm, lmh, lm_link)
292127250Speter		if (strcmp(f, lm->f) == 0)
293113229Smdodd			return (lm->t);
294141230Smdodd	return (NULL);
295113229Smdodd}
296113229Smdodd
297129638Smdodd/* Given an executable name, return a pointer to the translation list or
298129638Smdodd   NULL if no matches */
299113229Smdoddstatic struct lm_list *
300113229Smdoddlmp_find (const char *n)
301113229Smdodd{
302113229Smdodd	struct lmp *lmp;
303113229Smdodd
304115445Smdodd	dbg("%s(\"%s\")", __func__, n);
305115445Smdodd
306113229Smdodd	TAILQ_FOREACH(lmp, &lmp_head, lmp_link)
307129638Smdodd		if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) ||
308129638Smdodd		    (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) ||
309129638Smdodd		    (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0))
310113229Smdodd			return (&lmp->lml);
311113229Smdodd	return (NULL);
312113229Smdodd}
313113229Smdodd
314113229Smdoddstatic struct lm_list *
315113229Smdoddlmp_init (char *n)
316113229Smdodd{
317113229Smdodd	struct lmp *lmp;
318113229Smdodd
319115445Smdodd	dbg("%s(\"%s\")", __func__, n);
320115445Smdodd
321115150Smdodd	lmp = xmalloc(sizeof(struct lmp));
322113229Smdodd	lmp->p = n;
323129638Smdodd	if (n[strlen(n)-1] == '/')
324129638Smdodd		lmp->type = T_DIRECTORY;
325129638Smdodd	else if (strchr(n,'/') == NULL)
326129638Smdodd		lmp->type = T_BASENAME;
327129638Smdodd	else
328129638Smdodd		lmp->type = T_EXACT;
329113229Smdodd	TAILQ_INIT(&lmp->lml);
330113229Smdodd	TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link);
331113229Smdodd
332113229Smdodd	return (&lmp->lml);
333113229Smdodd}
334129638Smdodd
335129638Smdodd/* libc basename is overkill.  Return a pointer to the character after the
336129638Smdodd   last /, or the original string if there are no slashes. */
337129638Smdoddstatic const char *
338129638Smdoddquickbasename (const char *path)
339129638Smdodd{
340129638Smdodd	const char *p = path;
341141230Smdodd	for (; *path; path++) {
342129638Smdodd		if (*path == '/')
343129638Smdodd			p = path+1;
344129638Smdodd	}
345141230Smdodd	return (p);
346129638Smdodd}
347141232Smdodd
348141232Smdoddstatic int
349141232Smdoddreadstrfn(void * cookie, char *buf, int len)
350141232Smdodd{
351141232Smdodd	static char	*current;
352141232Smdodd	static int	left;
353141232Smdodd	int 	copied;
354141232Smdodd
355141232Smdodd	copied = 0;
356141232Smdodd	if (!current) {
357141232Smdodd		current = cookie;
358141232Smdodd		left = strlen(cookie);
359141232Smdodd	}
360141232Smdodd	while (*current && left && len) {
361141232Smdodd		*buf++ = *current++;
362141232Smdodd		left--;
363141232Smdodd		len--;
364141232Smdodd		copied++;
365141232Smdodd	}
366141232Smdodd	return copied;
367141232Smdodd}
368141232Smdodd
369141232Smdoddstatic int
370141232Smdoddclosestrfn(void * cookie)
371141232Smdodd{
372141232Smdodd	free(cookie);
373141232Smdodd	return 0;
374141232Smdodd}
375