xref: /freebsd/libexec/rtld-elf/libmap.c (revision 390e8cc2974df1888369c06339ef8e0e92b312b6)
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 
15 #ifndef _PATH_LIBMAP_CONF
16 #define	_PATH_LIBMAP_CONF	"/etc/libmap.conf"
17 #endif
18 
19 TAILQ_HEAD(lm_list, lm);
20 struct lm {
21 	char *f;
22 	char *t;
23 
24 	TAILQ_ENTRY(lm)	lm_link;
25 };
26 
27 TAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head);
28 struct lmp {
29 	char *p;
30 	struct lm_list lml;
31 	TAILQ_ENTRY(lmp) lmp_link;
32 };
33 
34 static void		lm_add		(const char *, const char *, const char *);
35 static void		lm_free		(struct lm_list *);
36 static char *		lml_find	(struct lm_list *, const char *);
37 static struct lm_list *	lmp_find	(const char *);
38 static struct lm_list *	lmp_init	(char *);
39 
40 #define	iseol(c)	(((c) == '#') || ((c) == '\0') || \
41 			 ((c) == '\n') || ((c) == '\r'))
42 
43 void
44 lm_init (void)
45 {
46 	FILE	*fp;
47 	char	*cp;
48 	char	*f, *t, *p, *c;
49 	char	prog[MAXPATHLEN];
50 	char	line[MAXPATHLEN + 2];
51 
52 	dbg("%s()", __func__);
53 
54 	TAILQ_INIT(&lmp_head);
55 
56 	if ((fp = fopen(_PATH_LIBMAP_CONF, "r")) == NULL)
57 		return;
58 
59 	p = NULL;
60 	while ((cp = fgets(line, MAXPATHLEN + 1, fp)) != NULL) {
61 		t = f = c = NULL;
62 
63 		/* Skip over leading space */
64 		while (isspace(*cp)) cp++;
65 
66 		/* Found a comment or EOL */
67 		if (iseol(*cp)) continue;
68 
69 		/* Found a constraint selector */
70 		if (*cp == '[') {
71 			cp++;
72 
73 			/* Skip leading space */
74 			while (isspace(*cp)) cp++;
75 
76 			/* Found comment, EOL or end of selector */
77 			if  (iseol(*cp) || *cp == ']')
78 				continue;
79 
80 			c = cp++;
81 			/* Skip to end of word */
82 			while (!isspace(*cp) && !iseol(*cp) && *cp != ']')
83 				cp++;
84 
85 			/* Skip and zero out trailing space */
86 			while (isspace(*cp)) *cp++ = '\0';
87 
88 			/* Check if there is a closing brace */
89 			if (*cp != ']') continue;
90 
91 			/* Terminate string if there was no trailing space */
92 			*cp++ = '\0';
93 
94 			/*
95 			 * There should be nothing except whitespace or comment
96 			  from this point to the end of the line.
97 			 */
98 			while(isspace(*cp)) *cp++;
99 			if (!iseol(*cp)) continue;
100 
101 			strcpy(prog, c);
102 			p = prog;
103 			continue;
104 		}
105 
106 		/* Parse the 'from' candidate. */
107 		f = cp++;
108 		while (!isspace(*cp) && !iseol(*cp)) cp++;
109 
110 		/* Skip and zero out the trailing whitespace */
111 		while (isspace(*cp)) *cp++ = '\0';
112 
113 		/* Found a comment or EOL */
114 		if (iseol(*cp)) continue;
115 
116 		/* Parse 'to' mapping */
117 		t = cp++;
118 		while (!isspace(*cp) && !iseol(*cp)) cp++;
119 
120 		/* Skip and zero out the trailing whitespace */
121 		while (isspace(*cp)) *cp++ = '\0';
122 
123 		/* Should be no extra tokens at this point */
124 		if (!iseol(*cp)) continue;
125 
126 		*cp = '\0';
127 		lm_add(p, f, t);
128 	}
129 	fclose(fp);
130 	return;
131 }
132 
133 static void
134 lm_free (struct lm_list *lml)
135 {
136 	struct lm *lm;
137 
138 	dbg("%s(%p)", __func__, lml);
139 
140 	while (!TAILQ_EMPTY(lml)) {
141 		lm = TAILQ_FIRST(lml);
142 		TAILQ_REMOVE(lml, lm, lm_link);
143 		free(lm->f);
144 		free(lm->t);
145 		free(lm);
146 	}
147 	return;
148 }
149 
150 void
151 lm_fini (void)
152 {
153 	struct lmp *lmp;
154 
155 	dbg("%s()", __func__);
156 
157 	while (!TAILQ_EMPTY(&lmp_head)) {
158 		lmp = TAILQ_FIRST(&lmp_head);
159 		TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
160 		free(lmp->p);
161 		lm_free(&lmp->lml);
162 		free(lmp);
163 	}
164 	return;
165 }
166 
167 static void
168 lm_add (const char *p, const char *f, const char *t)
169 {
170 	struct lm_list *lml;
171 	struct lm *lm;
172 
173 	if (p == NULL)
174 		p = "$DEFAULT$";
175 
176 	dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t);
177 
178 	if ((lml = lmp_find(p)) == NULL)
179 		lml = lmp_init(xstrdup(p));
180 
181 	lm = xmalloc(sizeof(struct lm));
182 	lm->f = xstrdup(f);
183 	lm->t = xstrdup(t);
184 	TAILQ_INSERT_HEAD(lml, lm, lm_link);
185 }
186 
187 char *
188 lm_find (const char *p, const char *f)
189 {
190 	struct lm_list *lml;
191 	char *t;
192 
193 	dbg("%s(\"%s\", \"%s\")", __func__, p, f);
194 
195 	if (p != NULL && (lml = lmp_find(p)) != NULL) {
196 		t = lml_find(lml, f);
197 		if (t != NULL) {
198 			/*
199 			 * Add a global mapping if we have
200 			 * a successful constrained match.
201 			 */
202 			lm_add(NULL, f, t);
203 			return (t);
204 		}
205 	}
206 	lml = lmp_find("$DEFAULT$");
207 	if (lml != NULL)
208 		return (lml_find(lml, f));
209 	else
210 		return (NULL);
211 }
212 
213 static char *
214 lml_find (struct lm_list *lmh, const char *f)
215 {
216 	struct lm *lm;
217 
218 	dbg("%s(%p, \"%s\")", __func__, lmh, f);
219 
220 	TAILQ_FOREACH(lm, lmh, lm_link)
221 		if ((strncmp(f, lm->f, strlen(lm->f)) == 0) &&
222 		    (strlen(f) == strlen(lm->f)))
223 			return (lm->t);
224 	return NULL;
225 }
226 
227 static struct lm_list *
228 lmp_find (const char *n)
229 {
230 	struct lmp *lmp;
231 
232 	dbg("%s(\"%s\")", __func__, n);
233 
234 	TAILQ_FOREACH(lmp, &lmp_head, lmp_link)
235 		if ((strncmp(n, lmp->p, strlen(lmp->p)) == 0) &&
236 		    (strlen(n) == strlen(lmp->p)))
237 			return (&lmp->lml);
238 	return (NULL);
239 }
240 
241 static struct lm_list *
242 lmp_init (char *n)
243 {
244 	struct lmp *lmp;
245 
246 	dbg("%s(\"%s\")", __func__, n);
247 
248 	lmp = xmalloc(sizeof(struct lmp));
249 	lmp->p = n;
250 	TAILQ_INIT(&lmp->lml);
251 	TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link);
252 
253 	return (&lmp->lml);
254 }
255