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