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