]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - libexec/rtld-elf/libmap.c
MFC r270256:
[FreeBSD/stable/10.git] / libexec / rtld-elf / libmap.c
1 /*
2  * $FreeBSD$
3  */
4
5 #include <sys/types.h>
6 #include <sys/param.h>
7 #include <sys/fcntl.h>
8 #include <sys/mman.h>
9 #include <sys/queue.h>
10 #include <sys/stat.h>
11 #include <dirent.h>
12 #include <errno.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "debug.h"
17 #include "rtld.h"
18 #include "libmap.h"
19
20 #ifndef _PATH_LIBMAP_CONF
21 #define _PATH_LIBMAP_CONF       "/etc/libmap.conf"
22 #endif
23
24 #ifdef COMPAT_32BIT
25 #undef _PATH_LIBMAP_CONF
26 #define _PATH_LIBMAP_CONF       "/etc/libmap32.conf"
27 #endif
28
29 TAILQ_HEAD(lm_list, lm);
30 struct lm {
31         char *f;
32         char *t;
33         TAILQ_ENTRY(lm) lm_link;
34 };
35
36 TAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head);
37 struct lmp {
38         char *p;
39         enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type;
40         struct lm_list lml;
41         TAILQ_ENTRY(lmp) lmp_link;
42 };
43
44 static TAILQ_HEAD(lmc_list, lmc) lmc_head = TAILQ_HEAD_INITIALIZER(lmc_head);
45 struct lmc {
46         char *path;
47         TAILQ_ENTRY(lmc) next;
48 };
49
50 static int lm_count;
51
52 static void lmc_parse(char *, size_t);
53 static void lmc_parse_file(char *);
54 static void lmc_parse_dir(char *);
55 static void lm_add(const char *, const char *, const char *);
56 static void lm_free(struct lm_list *);
57 static char *lml_find(struct lm_list *, const char *);
58 static struct lm_list *lmp_find(const char *);
59 static struct lm_list *lmp_init(char *);
60 static const char *quickbasename(const char *);
61
62 #define iseol(c)        (((c) == '#') || ((c) == '\0') || \
63                          ((c) == '\n') || ((c) == '\r'))
64
65 /*
66  * Do not use ctype.h macros, which rely on working TLS.  It is
67  * too early to have thread-local variables functional.
68  */
69 #define rtld_isspace(c) ((c) == ' ' || (c) == '\t')
70
71 int
72 lm_init(char *libmap_override)
73 {
74         char *p;
75
76         dbg("lm_init(\"%s\")", libmap_override);
77         TAILQ_INIT(&lmp_head);
78
79         lmc_parse_file(_PATH_LIBMAP_CONF);
80
81         if (libmap_override) {
82                 /*
83                  * Do some character replacement to make $LDLIBMAP look
84                  * like a text file, then parse it.
85                  */
86                 libmap_override = xstrdup(libmap_override);
87                 for (p = libmap_override; *p; p++) {
88                         switch (*p) {
89                         case '=':
90                                 *p = ' ';
91                                 break;
92                         case ',':
93                                 *p = '\n';
94                                 break;
95                         }
96                 }
97                 lmc_parse(libmap_override, p - libmap_override);
98                 free(libmap_override);
99         }
100
101         return (lm_count == 0);
102 }
103
104 static void
105 lmc_parse_file(char *path)
106 {
107         struct lmc *p;
108         struct stat st;
109         int fd;
110         char *rpath;
111         char *lm_map;
112
113         rpath = realpath(path, NULL);
114         if (rpath == NULL)
115                 return;
116
117         TAILQ_FOREACH(p, &lmc_head, next) {
118                 if (strcmp(p->path, rpath) == 0) {
119                         free(rpath);
120                         return;
121                 }
122         }
123
124         fd = open(rpath, O_RDONLY | O_CLOEXEC);
125         if (fd == -1) {
126                 dbg("lm_parse_file: open(\"%s\") failed, %s", rpath,
127                     rtld_strerror(errno));
128                 free(rpath);
129                 return;
130         }
131         if (fstat(fd, &st) == -1) {
132                 close(fd);
133                 dbg("lm_parse_file: fstat(\"%s\") failed, %s", rpath,
134                     rtld_strerror(errno));
135                 free(rpath);
136                 return;
137         }
138         lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
139         if (lm_map == (const char *)MAP_FAILED) {
140                 close(fd);
141                 dbg("lm_parse_file: mmap(\"%s\") failed, %s", rpath,
142                     rtld_strerror(errno));
143                 free(rpath);
144                 return;
145         }
146         close(fd);
147         p = xmalloc(sizeof(struct lmc));
148         p->path = rpath;
149         TAILQ_INSERT_HEAD(&lmc_head, p, next);
150         lmc_parse(lm_map, st.st_size);
151         munmap(lm_map, st.st_size);
152 }
153
154 static void
155 lmc_parse_dir(char *idir)
156 {
157         DIR *d;
158         struct dirent *dp;
159         struct lmc *p;
160         char conffile[MAXPATHLEN];
161         char *ext;
162         char *rpath;
163
164         rpath = realpath(idir, NULL);
165         if (rpath == NULL)
166                 return;
167
168         TAILQ_FOREACH(p, &lmc_head, next) {
169                 if (strcmp(p->path, rpath) == 0) {
170                         free(rpath);
171                         return;
172                 }
173         }
174         d = opendir(idir);
175         if (d == NULL) {
176                 free(rpath);
177                 return;
178         }
179
180         p = xmalloc(sizeof(struct lmc));
181         p->path = rpath;
182         TAILQ_INSERT_HEAD(&lmc_head, p, next);
183
184         while ((dp = readdir(d)) != NULL) {
185                 if (dp->d_ino == 0)
186                         continue;
187                 if (dp->d_type != DT_REG)
188                         continue;
189                 ext = strrchr(dp->d_name, '.');
190                 if (ext == NULL)
191                         continue;
192                 if (strcmp(ext, ".conf") != 0)
193                         continue;
194                 if (strlcpy(conffile, idir, MAXPATHLEN) >= MAXPATHLEN)
195                         continue; /* too long */
196                 if (strlcat(conffile, "/", MAXPATHLEN) >= MAXPATHLEN)
197                         continue; /* too long */
198                 if (strlcat(conffile, dp->d_name, MAXPATHLEN) >= MAXPATHLEN)
199                         continue; /* too long */
200                 lmc_parse_file(conffile);
201         }
202         closedir(d);
203 }
204
205 static void
206 lmc_parse(char *lm_p, size_t lm_len)
207 {
208         char *cp, *f, *t, *c, *p;
209         char prog[MAXPATHLEN];
210         /* allow includedir + full length path */
211         char line[MAXPATHLEN + 13];
212         size_t cnt;
213         int i;
214
215         cnt = 0;
216         p = NULL;
217         while (cnt < lm_len) {
218                 i = 0;
219                 while (cnt < lm_len && lm_p[cnt] != '\n' &&
220                     i < sizeof(line) - 1) {
221                         line[i] = lm_p[cnt];
222                         cnt++;
223                         i++;
224                 }
225                 line[i] = '\0';
226                 while (cnt < lm_len && lm_p[cnt] != '\n')
227                         cnt++;
228                 /* skip over nl */
229                 cnt++;
230
231                 cp = &line[0];
232                 t = f = c = NULL;
233
234                 /* Skip over leading space */
235                 while (rtld_isspace(*cp)) cp++;
236
237                 /* Found a comment or EOL */
238                 if (iseol(*cp)) continue;
239
240                 /* Found a constraint selector */
241                 if (*cp == '[') {
242                         cp++;
243
244                         /* Skip leading space */
245                         while (rtld_isspace(*cp)) cp++;
246
247                         /* Found comment, EOL or end of selector */
248                         if  (iseol(*cp) || *cp == ']')
249                                 continue;
250
251                         c = cp++;
252                         /* Skip to end of word */
253                         while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']')
254                                 cp++;
255
256                         /* Skip and zero out trailing space */
257                         while (rtld_isspace(*cp)) *cp++ = '\0';
258
259                         /* Check if there is a closing brace */
260                         if (*cp != ']') continue;
261
262                         /* Terminate string if there was no trailing space */
263                         *cp++ = '\0';
264
265                         /*
266                          * There should be nothing except whitespace or comment
267                           from this point to the end of the line.
268                          */
269                         while(rtld_isspace(*cp)) cp++;
270                         if (!iseol(*cp)) continue;
271
272                         if (strlcpy(prog, c, sizeof prog) >= sizeof prog)
273                                 continue;
274                         p = prog;
275                         continue;
276                 }
277
278                 /* Parse the 'from' candidate. */
279                 f = cp++;
280                 while (!rtld_isspace(*cp) && !iseol(*cp)) cp++;
281
282                 /* Skip and zero out the trailing whitespace */
283                 while (rtld_isspace(*cp)) *cp++ = '\0';
284
285                 /* Found a comment or EOL */
286                 if (iseol(*cp)) continue;
287
288                 /* Parse 'to' mapping */
289                 t = cp++;
290                 while (!rtld_isspace(*cp) && !iseol(*cp)) cp++;
291
292                 /* Skip and zero out the trailing whitespace */
293                 while (rtld_isspace(*cp)) *cp++ = '\0';
294
295                 /* Should be no extra tokens at this point */
296                 if (!iseol(*cp)) continue;
297
298                 *cp = '\0';
299                 if (strcmp(f, "includedir") == 0)
300                         lmc_parse_dir(t);
301                 else if (strcmp(f, "include") == 0)
302                         lmc_parse_file(t);
303                 else
304                         lm_add(p, f, t);
305         }
306 }
307
308 static void
309 lm_free (struct lm_list *lml)
310 {
311         struct lm *lm;
312
313         dbg("%s(%p)", __func__, lml);
314
315         while (!TAILQ_EMPTY(lml)) {
316                 lm = TAILQ_FIRST(lml);
317                 TAILQ_REMOVE(lml, lm, lm_link);
318                 free(lm->f);
319                 free(lm->t);
320                 free(lm);
321         }
322         return;
323 }
324
325 void
326 lm_fini (void)
327 {
328         struct lmp *lmp;
329         struct lmc *p;
330
331         dbg("%s()", __func__);
332
333         while (!TAILQ_EMPTY(&lmc_head)) {
334                 p = TAILQ_FIRST(&lmc_head);
335                 TAILQ_REMOVE(&lmc_head, p, next);
336                 free(p->path);
337                 free(p);
338         }
339
340         while (!TAILQ_EMPTY(&lmp_head)) {
341                 lmp = TAILQ_FIRST(&lmp_head);
342                 TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
343                 free(lmp->p);
344                 lm_free(&lmp->lml);
345                 free(lmp);
346         }
347         return;
348 }
349
350 static void
351 lm_add (const char *p, const char *f, const char *t)
352 {
353         struct lm_list *lml;
354         struct lm *lm;
355
356         if (p == NULL)
357                 p = "$DEFAULT$";
358
359         dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t);
360
361         if ((lml = lmp_find(p)) == NULL)
362                 lml = lmp_init(xstrdup(p));
363
364         lm = xmalloc(sizeof(struct lm));
365         lm->f = xstrdup(f);
366         lm->t = xstrdup(t);
367         TAILQ_INSERT_HEAD(lml, lm, lm_link);
368         lm_count++;
369 }
370
371 char *
372 lm_find (const char *p, const char *f)
373 {
374         struct lm_list *lml;
375         char *t;
376
377         dbg("%s(\"%s\", \"%s\")", __func__, p, f);
378
379         if (p != NULL && (lml = lmp_find(p)) != NULL) {
380                 t = lml_find(lml, f);
381                 if (t != NULL) {
382                         /*
383                          * Add a global mapping if we have
384                          * a successful constrained match.
385                          */
386                         lm_add(NULL, f, t);
387                         return (t);
388                 }
389         }
390         lml = lmp_find("$DEFAULT$");
391         if (lml != NULL)
392                 return (lml_find(lml, f));
393         else
394                 return (NULL);
395 }
396
397 /* Given a libmap translation list and a library name, return the
398    replacement library, or NULL */
399 char *
400 lm_findn (const char *p, const char *f, const int n)
401 {
402         char pathbuf[64], *s, *t;
403
404         if (n < sizeof(pathbuf) - 1)
405                 s = pathbuf;
406         else
407                 s = xmalloc(n + 1);
408         memcpy(s, f, n);
409         s[n] = '\0';
410         t = lm_find(p, s);
411         if (s != pathbuf)
412                 free(s);
413         return (t);
414 }
415
416 static char *
417 lml_find (struct lm_list *lmh, const char *f)
418 {
419         struct lm *lm;
420
421         dbg("%s(%p, \"%s\")", __func__, lmh, f);
422
423         TAILQ_FOREACH(lm, lmh, lm_link)
424                 if (strcmp(f, lm->f) == 0)
425                         return (lm->t);
426         return (NULL);
427 }
428
429 /* Given an executable name, return a pointer to the translation list or
430    NULL if no matches */
431 static struct lm_list *
432 lmp_find (const char *n)
433 {
434         struct lmp *lmp;
435
436         dbg("%s(\"%s\")", __func__, n);
437
438         TAILQ_FOREACH(lmp, &lmp_head, lmp_link)
439                 if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) ||
440                     (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) ||
441                     (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0))
442                         return (&lmp->lml);
443         return (NULL);
444 }
445
446 static struct lm_list *
447 lmp_init (char *n)
448 {
449         struct lmp *lmp;
450
451         dbg("%s(\"%s\")", __func__, n);
452
453         lmp = xmalloc(sizeof(struct lmp));
454         lmp->p = n;
455         if (n[strlen(n)-1] == '/')
456                 lmp->type = T_DIRECTORY;
457         else if (strchr(n,'/') == NULL)
458                 lmp->type = T_BASENAME;
459         else
460                 lmp->type = T_EXACT;
461         TAILQ_INIT(&lmp->lml);
462         TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link);
463
464         return (&lmp->lml);
465 }
466
467 /* libc basename is overkill.  Return a pointer to the character after the
468    last /, or the original string if there are no slashes. */
469 static const char *
470 quickbasename (const char *path)
471 {
472         const char *p = path;
473         for (; *path; path++) {
474                 if (*path == '/')
475                         p = path+1;
476         }
477         return (p);
478 }