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