21 TAILQ_HEAD(lm_list
, lm
);
25 TAILQ_ENTRY(lm
) lm_link
;
28 TAILQ_HEAD(lmp_list
, lmp
) lmp_head
= TAILQ_HEAD_INITIALIZER(lmp_head
);
31 enum { T_EXACT
=0, T_BASENAME
, T_DIRECTORY
} type
;
33 TAILQ_ENTRY(lmp
) lmp_link
;
36 static TAILQ_HEAD(lmc_list
, lmc
) lmc_head
= TAILQ_HEAD_INITIALIZER(lmc_head
);
39 TAILQ_ENTRY(lmc
) next
;
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 *);
54 #define iseol(c) (((c) == '#') || ((c) == '\0') || \
55 ((c) == '\n') || ((c) == '\r'))
58 * Do not use ctype.h macros, which rely on working TLS. It is
59 * too early to have thread-local variables functional.
61 #define rtld_isspace(c) ((c) == ' ' || (c) == '\t')
64 lm_init(char *libmap_override
)
68 dbg("lm_init(\"%s\")", libmap_override
);
69 TAILQ_INIT(&lmp_head
);
71 lmc_parse_file(ld_path_libmap_conf
);
73 if (libmap_override
) {
75 * Do some character replacement to make $LDLIBMAP look
76 * like a text file, then parse it.
78 libmap_override
= xstrdup(libmap_override
);
79 for (p
= libmap_override
; *p
; p
++) {
89 lmc_parse(libmap_override
, p
- libmap_override
);
90 free(libmap_override
);
93 return (lm_count
== 0);
97 lmc_parse_file(char *path
)
105 rpath
= realpath(path
, NULL
);
109 TAILQ_FOREACH(p
, &lmc_head
, next
) {
110 if (strcmp(p
->path
, rpath
) == 0) {
116 fd
= open(rpath
, O_RDONLY
| O_CLOEXEC
);
118 dbg("lm_parse_file: open(\"%s\") failed, %s", rpath
,
119 rtld_strerror(errno
));
123 if (fstat(fd
, &st
) == -1) {
125 dbg("lm_parse_file: fstat(\"%s\") failed, %s", rpath
,
126 rtld_strerror(errno
));
130 lm_map
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
131 if (lm_map
== (const char *)MAP_FAILED
) {
133 dbg("lm_parse_file: mmap(\"%s\") failed, %s", rpath
,
134 rtld_strerror(errno
));
139 p
= xmalloc(sizeof(struct lmc
));
141 TAILQ_INSERT_HEAD(&lmc_head
, p
, next
);
142 lmc_parse(lm_map
, st
.st_size
);
143 munmap(lm_map
, st
.st_size
);
147 lmc_parse_dir(char *idir
)
152 char conffile
[MAXPATHLEN
];
156 rpath
= realpath(idir
, NULL
);
160 TAILQ_FOREACH(p
, &lmc_head
, next
) {
161 if (strcmp(p
->path
, rpath
) == 0) {
172 p
= xmalloc(sizeof(struct lmc
));
174 TAILQ_INSERT_HEAD(&lmc_head
, p
, next
);
176 while ((dp
= readdir(d
)) != NULL
) {
179 if (dp
->d_type
!= DT_REG
)
181 ext
= strrchr(dp
->d_name
, '.');
184 if (strcmp(ext
, ".conf") != 0)
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
);
198 lmc_parse(char *lm_p
, size_t lm_len
)
200 char *cp
, *f
, *t
, *c
, *p
;
201 char prog
[MAXPATHLEN
];
202 /* allow includedir + full length path */
203 char line
[MAXPATHLEN
+ 13];
209 while (cnt
< lm_len
) {
211 while (cnt
< lm_len
&& lm_p
[cnt
] != '\n' &&
212 i
< sizeof(line
) - 1) {
218 while (cnt
< lm_len
&& lm_p
[cnt
] != '\n')
226 /* Skip over leading space */
227 while (rtld_isspace(*cp
)) cp
++;
229 /* Found a comment or EOL */
230 if (iseol(*cp
)) continue;
232 /* Found a constraint selector */
236 /* Skip leading space */
237 while (rtld_isspace(*cp
)) cp
++;
239 /* Found comment, EOL or end of selector */
240 if (iseol(*cp
) || *cp
== ']')
244 /* Skip to end of word */
245 while (!rtld_isspace(*cp
) && !iseol(*cp
) && *cp
!= ']')
248 /* Skip and zero out trailing space */
249 while (rtld_isspace(*cp
)) *cp
++ = '\0';
251 /* Check if there is a closing brace */
252 if (*cp
!= ']') continue;
254 /* Terminate string if there was no trailing space */
258 * There should be nothing except whitespace or comment
259 from this point to the end of the line.
261 while(rtld_isspace(*cp
)) cp
++;
262 if (!iseol(*cp
)) continue;
264 if (strlcpy(prog
, c
, sizeof prog
) >= sizeof prog
)
270 /* Parse the 'from' candidate. */
272 while (!rtld_isspace(*cp
) && !iseol(*cp
)) cp
++;
274 /* Skip and zero out the trailing whitespace */
275 while (rtld_isspace(*cp
)) *cp
++ = '\0';
277 /* Found a comment or EOL */
278 if (iseol(*cp
)) continue;
280 /* Parse 'to' mapping */
282 while (!rtld_isspace(*cp
) && !iseol(*cp
)) cp
++;
284 /* Skip and zero out the trailing whitespace */
285 while (rtld_isspace(*cp
)) *cp
++ = '\0';
287 /* Should be no extra tokens at this point */
288 if (!iseol(*cp
)) continue;
291 if (strcmp(f
, "includedir") == 0)
293 else if (strcmp(f
, "include") == 0)
301 lm_free (struct lm_list
*lml
)
305 dbg("%s(%p)", __func__
, lml
);
307 while (!TAILQ_EMPTY(lml
)) {
308 lm
= TAILQ_FIRST(lml
);
309 TAILQ_REMOVE(lml
, lm
, lm_link
);
323 dbg("%s()", __func__
);
325 while (!TAILQ_EMPTY(&lmc_head
)) {
326 p
= TAILQ_FIRST(&lmc_head
);
327 TAILQ_REMOVE(&lmc_head
, p
, next
);
332 while (!TAILQ_EMPTY(&lmp_head
)) {
333 lmp
= TAILQ_FIRST(&lmp_head
);
334 TAILQ_REMOVE(&lmp_head
, lmp
, lmp_link
);
343 lm_add (const char *p
, const char *f
, const char *t
)
351 dbg("%s(\"%s\", \"%s\", \"%s\")", __func__
, p
, f
, t
);
353 if ((lml
= lmp_find(p
)) == NULL
)
354 lml
= lmp_init(xstrdup(p
));
356 lm
= xmalloc(sizeof(struct lm
));
359 TAILQ_INSERT_HEAD(lml
, lm
, lm_link
);
364 lm_find (const char *p
, const char *f
)
369 dbg("%s(\"%s\", \"%s\")", __func__
, p
, f
);
371 if (p
!= NULL
&& (lml
= lmp_find(p
)) != NULL
) {
372 t
= lml_find(lml
, f
);
375 * Add a global mapping if we have
376 * a successful constrained match.
382 lml
= lmp_find("$DEFAULT$");
384 return (lml_find(lml
, f
));
389 /* Given a libmap translation list and a library name, return the
390 replacement library, or NULL */
392 lm_findn (const char *p
, const char *f
, const int n
)
394 char pathbuf
[64], *s
, *t
;
396 if (n
< sizeof(pathbuf
) - 1)
409 lml_find (struct lm_list
*lmh
, const char *f
)
413 dbg("%s(%p, \"%s\")", __func__
, lmh
, f
);
415 TAILQ_FOREACH(lm
, lmh
, lm_link
)
416 if (strcmp(f
, lm
->f
) == 0)
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
)
428 dbg("%s(\"%s\")", __func__
, n
);
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))
438 static struct lm_list
*
443 dbg("%s(\"%s\")", __func__
, n
);
445 lmp
= xmalloc(sizeof(struct lmp
));
447 if (n
[strlen(n
)-1] == '/')
448 lmp
->type
= T_DIRECTORY
;
449 else if (strchr(n
,'/') == NULL
)
450 lmp
->type
= T_BASENAME
;
453 TAILQ_INIT(&lmp
->lml
);
454 TAILQ_INSERT_HEAD(&lmp_head
, lmp
, lmp_link
);
459 /* libc basename is overkill. Return a pointer to the character after the
460 last /, or the original string if there are no slashes. */
462 quickbasename (const char *path
)
464 const char *p
= path
;
465 for (; *path
; path
++) {