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