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 } 471