1113229Smdodd/* 2113229Smdodd * $FreeBSD: releng/11.0/libexec/rtld-elf/libmap.c 290223 2015-10-31 04:39:55Z imp $ 3113229Smdodd */ 4113229Smdodd 5234851Sbapt#include <sys/types.h> 6232862Skib#include <sys/param.h> 7232862Skib#include <sys/fcntl.h> 8232862Skib#include <sys/mman.h> 9232862Skib#include <sys/queue.h> 10232862Skib#include <sys/stat.h> 11234851Sbapt#include <dirent.h> 12232862Skib#include <errno.h> 13232862Skib#include <stdlib.h> 14113229Smdodd#include <string.h> 15113229Smdodd 16115150Smdodd#include "debug.h" 17115150Smdodd#include "rtld.h" 18116513Smdodd#include "libmap.h" 19290222Simp#include "paths.h" 20115150Smdodd 21113229SmdoddTAILQ_HEAD(lm_list, lm); 22113229Smdoddstruct lm { 23113229Smdodd char *f; 24113229Smdodd char *t; 25113229Smdodd TAILQ_ENTRY(lm) lm_link; 26113229Smdodd}; 27113229Smdodd 28113229SmdoddTAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head); 29113229Smdoddstruct lmp { 30113229Smdodd char *p; 31129638Smdodd enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type; 32113229Smdodd struct lm_list lml; 33113229Smdodd TAILQ_ENTRY(lmp) lmp_link; 34113229Smdodd}; 35113229Smdodd 36234851Sbaptstatic TAILQ_HEAD(lmc_list, lmc) lmc_head = TAILQ_HEAD_INITIALIZER(lmc_head); 37234851Sbaptstruct lmc { 38234851Sbapt char *path; 39234851Sbapt TAILQ_ENTRY(lmc) next; 40234851Sbapt}; 41234851Sbapt 42232862Skibstatic int lm_count; 43141232Smdodd 44232862Skibstatic void lmc_parse(char *, size_t); 45234851Sbaptstatic void lmc_parse_file(char *); 46234851Sbaptstatic void lmc_parse_dir(char *); 47232862Skibstatic void lm_add(const char *, const char *, const char *); 48232862Skibstatic void lm_free(struct lm_list *); 49232862Skibstatic char *lml_find(struct lm_list *, const char *); 50232862Skibstatic struct lm_list *lmp_find(const char *); 51232862Skibstatic struct lm_list *lmp_init(char *); 52232862Skibstatic const char *quickbasename(const char *); 53113229Smdodd 54113312Smdodd#define iseol(c) (((c) == '#') || ((c) == '\0') || \ 55113312Smdodd ((c) == '\n') || ((c) == '\r')) 56113312Smdodd 57232572Skib/* 58232572Skib * Do not use ctype.h macros, which rely on working TLS. It is 59232572Skib * too early to have thread-local variables functional. 60232572Skib */ 61232590Spluknet#define rtld_isspace(c) ((c) == ' ' || (c) == '\t') 62232572Skib 63120038Smdoddint 64232862Skiblm_init(char *libmap_override) 65113229Smdodd{ 66234851Sbapt char *p; 67113229Smdodd 68232862Skib dbg("lm_init(\"%s\")", libmap_override); 69113229Smdodd TAILQ_INIT(&lmp_head); 70113229Smdodd 71290223Simp lmc_parse_file(ld_path_libmap_conf); 72113229Smdodd 73141232Smdodd if (libmap_override) { 74232862Skib /* 75266411Skib * Do some character replacement to make $LDLIBMAP look 76232862Skib * like a text file, then parse it. 77232862Skib */ 78141232Smdodd libmap_override = xstrdup(libmap_override); 79141232Smdodd for (p = libmap_override; *p; p++) { 80141232Smdodd switch (*p) { 81232862Skib case '=': 82232862Skib *p = ' '; 83232862Skib break; 84232862Skib case ',': 85232862Skib *p = '\n'; 86232862Skib break; 87141232Smdodd } 88141232Smdodd } 89266411Skib lmc_parse(libmap_override, p - libmap_override); 90266411Skib free(libmap_override); 91141232Smdodd } 92141232Smdodd 93141232Smdodd return (lm_count == 0); 94141232Smdodd} 95141232Smdodd 96141232Smdoddstatic void 97234851Sbaptlmc_parse_file(char *path) 98234851Sbapt{ 99234851Sbapt struct lmc *p; 100234851Sbapt struct stat st; 101234851Sbapt int fd; 102234851Sbapt char *rpath; 103234851Sbapt char *lm_map; 104234851Sbapt 105234851Sbapt rpath = realpath(path, NULL); 106234851Sbapt if (rpath == NULL) 107234851Sbapt return; 108234851Sbapt 109234851Sbapt TAILQ_FOREACH(p, &lmc_head, next) { 110234851Sbapt if (strcmp(p->path, rpath) == 0) { 111234851Sbapt free(rpath); 112234851Sbapt return; 113234851Sbapt } 114234851Sbapt } 115234851Sbapt 116242587Sjilles fd = open(rpath, O_RDONLY | O_CLOEXEC); 117234851Sbapt if (fd == -1) { 118235059Sbapt dbg("lm_parse_file: open(\"%s\") failed, %s", rpath, 119234851Sbapt rtld_strerror(errno)); 120234851Sbapt free(rpath); 121234851Sbapt return; 122234851Sbapt } 123234851Sbapt if (fstat(fd, &st) == -1) { 124234851Sbapt close(fd); 125235059Sbapt dbg("lm_parse_file: fstat(\"%s\") failed, %s", rpath, 126234851Sbapt rtld_strerror(errno)); 127234851Sbapt free(rpath); 128234851Sbapt return; 129234851Sbapt } 130234851Sbapt lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 131234851Sbapt if (lm_map == (const char *)MAP_FAILED) { 132234851Sbapt close(fd); 133235059Sbapt dbg("lm_parse_file: mmap(\"%s\") failed, %s", rpath, 134234851Sbapt rtld_strerror(errno)); 135234851Sbapt free(rpath); 136234851Sbapt return; 137234851Sbapt } 138234851Sbapt close(fd); 139234851Sbapt p = xmalloc(sizeof(struct lmc)); 140234851Sbapt p->path = rpath; 141234851Sbapt TAILQ_INSERT_HEAD(&lmc_head, p, next); 142234851Sbapt lmc_parse(lm_map, st.st_size); 143234851Sbapt munmap(lm_map, st.st_size); 144234851Sbapt} 145234851Sbapt 146234851Sbaptstatic void 147234851Sbaptlmc_parse_dir(char *idir) 148234851Sbapt{ 149234851Sbapt DIR *d; 150234851Sbapt struct dirent *dp; 151234851Sbapt struct lmc *p; 152234851Sbapt char conffile[MAXPATHLEN]; 153234851Sbapt char *ext; 154234851Sbapt char *rpath; 155234851Sbapt 156234851Sbapt rpath = realpath(idir, NULL); 157234851Sbapt if (rpath == NULL) 158234851Sbapt return; 159234851Sbapt 160234851Sbapt TAILQ_FOREACH(p, &lmc_head, next) { 161234851Sbapt if (strcmp(p->path, rpath) == 0) { 162234851Sbapt free(rpath); 163234851Sbapt return; 164234851Sbapt } 165234851Sbapt } 166234851Sbapt d = opendir(idir); 167234851Sbapt if (d == NULL) { 168234851Sbapt free(rpath); 169234851Sbapt return; 170234851Sbapt } 171234851Sbapt 172234851Sbapt p = xmalloc(sizeof(struct lmc)); 173234851Sbapt p->path = rpath; 174234851Sbapt TAILQ_INSERT_HEAD(&lmc_head, p, next); 175234851Sbapt 176234851Sbapt while ((dp = readdir(d)) != NULL) { 177234851Sbapt if (dp->d_ino == 0) 178234851Sbapt continue; 179234851Sbapt if (dp->d_type != DT_REG) 180234851Sbapt continue; 181234851Sbapt ext = strrchr(dp->d_name, '.'); 182234851Sbapt if (ext == NULL) 183234851Sbapt continue; 184234851Sbapt if (strcmp(ext, ".conf") != 0) 185234851Sbapt continue; 186234851Sbapt if (strlcpy(conffile, idir, MAXPATHLEN) >= MAXPATHLEN) 187234851Sbapt continue; /* too long */ 188234851Sbapt if (strlcat(conffile, "/", MAXPATHLEN) >= MAXPATHLEN) 189234851Sbapt continue; /* too long */ 190234851Sbapt if (strlcat(conffile, dp->d_name, MAXPATHLEN) >= MAXPATHLEN) 191234851Sbapt continue; /* too long */ 192234851Sbapt lmc_parse_file(conffile); 193234851Sbapt } 194234851Sbapt closedir(d); 195234851Sbapt} 196234851Sbapt 197234851Sbaptstatic void 198232862Skiblmc_parse(char *lm_p, size_t lm_len) 199141232Smdodd{ 200232862Skib char *cp, *f, *t, *c, *p; 201232862Skib char prog[MAXPATHLEN]; 202234851Sbapt /* allow includedir + full length path */ 203234851Sbapt char line[MAXPATHLEN + 13]; 204232862Skib size_t cnt; 205232862Skib int i; 206234851Sbapt 207232862Skib cnt = 0; 208113229Smdodd p = NULL; 209232862Skib while (cnt < lm_len) { 210232862Skib i = 0; 211270256Spfg while (cnt < lm_len && lm_p[cnt] != '\n' && 212232862Skib i < sizeof(line) - 1) { 213232862Skib line[i] = lm_p[cnt]; 214232862Skib cnt++; 215232862Skib i++; 216232862Skib } 217232862Skib line[i] = '\0'; 218270256Spfg while (cnt < lm_len && lm_p[cnt] != '\n') 219232862Skib cnt++; 220232862Skib /* skip over nl */ 221232862Skib cnt++; 222232862Skib 223232862Skib cp = &line[0]; 224115400Smdodd t = f = c = NULL; 225113312Smdodd 226113229Smdodd /* Skip over leading space */ 227232590Spluknet while (rtld_isspace(*cp)) cp++; 228113312Smdodd 229113229Smdodd /* Found a comment or EOL */ 230114316Skan if (iseol(*cp)) continue; 231113312Smdodd 232113312Smdodd /* Found a constraint selector */ 233113229Smdodd if (*cp == '[') { 234113229Smdodd cp++; 235113312Smdodd 236113229Smdodd /* Skip leading space */ 237232590Spluknet while (rtld_isspace(*cp)) cp++; 238113312Smdodd 239113229Smdodd /* Found comment, EOL or end of selector */ 240114316Skan if (iseol(*cp) || *cp == ']') 241114316Skan continue; 242113312Smdodd 243115400Smdodd c = cp++; 244113229Smdodd /* Skip to end of word */ 245232590Spluknet while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']') 246114316Skan cp++; 247113312Smdodd 248113312Smdodd /* Skip and zero out trailing space */ 249232590Spluknet while (rtld_isspace(*cp)) *cp++ = '\0'; 250113312Smdodd 251113312Smdodd /* Check if there is a closing brace */ 252114316Skan if (*cp != ']') continue; 253113312Smdodd 254113312Smdodd /* Terminate string if there was no trailing space */ 255113229Smdodd *cp++ = '\0'; 256113312Smdodd 257113312Smdodd /* 258113312Smdodd * There should be nothing except whitespace or comment 259115396Skan from this point to the end of the line. 260113312Smdodd */ 261232590Spluknet while(rtld_isspace(*cp)) cp++; 262114316Skan if (!iseol(*cp)) continue; 263113312Smdodd 264234851Sbapt if (strlcpy(prog, c, sizeof prog) >= sizeof prog) 265234851Sbapt continue; 266114316Skan p = prog; 267114316Skan continue; 268113229Smdodd } 269113312Smdodd 270113312Smdodd /* Parse the 'from' candidate. */ 271114316Skan f = cp++; 272232590Spluknet while (!rtld_isspace(*cp) && !iseol(*cp)) cp++; 273113312Smdodd 274113312Smdodd /* Skip and zero out the trailing whitespace */ 275232590Spluknet while (rtld_isspace(*cp)) *cp++ = '\0'; 276113312Smdodd 277113312Smdodd /* Found a comment or EOL */ 278114316Skan if (iseol(*cp)) continue; 279113312Smdodd 280113312Smdodd /* Parse 'to' mapping */ 281114316Skan t = cp++; 282232590Spluknet while (!rtld_isspace(*cp) && !iseol(*cp)) cp++; 283115396Skan 284114316Skan /* Skip and zero out the trailing whitespace */ 285232590Spluknet while (rtld_isspace(*cp)) *cp++ = '\0'; 286113229Smdodd 287114316Skan /* Should be no extra tokens at this point */ 288114316Skan if (!iseol(*cp)) continue; 289114316Skan 290114316Skan *cp = '\0'; 291234851Sbapt if (strcmp(f, "includedir") == 0) 292234851Sbapt lmc_parse_dir(t); 293234851Sbapt else if (strcmp(f, "include") == 0) 294234851Sbapt lmc_parse_file(t); 295234851Sbapt else 296234851Sbapt lm_add(p, f, t); 297113229Smdodd } 298113229Smdodd} 299113229Smdodd 300113229Smdoddstatic void 301113229Smdoddlm_free (struct lm_list *lml) 302113229Smdodd{ 303113229Smdodd struct lm *lm; 304113229Smdodd 305115445Smdodd dbg("%s(%p)", __func__, lml); 306115445Smdodd 307113229Smdodd while (!TAILQ_EMPTY(lml)) { 308113229Smdodd lm = TAILQ_FIRST(lml); 309113229Smdodd TAILQ_REMOVE(lml, lm, lm_link); 310113229Smdodd free(lm->f); 311113229Smdodd free(lm->t); 312113229Smdodd free(lm); 313113229Smdodd } 314113229Smdodd return; 315113229Smdodd} 316113229Smdodd 317113229Smdoddvoid 318113229Smdoddlm_fini (void) 319113229Smdodd{ 320113229Smdodd struct lmp *lmp; 321234851Sbapt struct lmc *p; 322113229Smdodd 323115445Smdodd dbg("%s()", __func__); 324115445Smdodd 325234851Sbapt while (!TAILQ_EMPTY(&lmc_head)) { 326234851Sbapt p = TAILQ_FIRST(&lmc_head); 327234851Sbapt TAILQ_REMOVE(&lmc_head, p, next); 328234851Sbapt free(p->path); 329234851Sbapt free(p); 330234851Sbapt } 331234851Sbapt 332113229Smdodd while (!TAILQ_EMPTY(&lmp_head)) { 333113229Smdodd lmp = TAILQ_FIRST(&lmp_head); 334113229Smdodd TAILQ_REMOVE(&lmp_head, lmp, lmp_link); 335113229Smdodd free(lmp->p); 336113229Smdodd lm_free(&lmp->lml); 337113229Smdodd free(lmp); 338113229Smdodd } 339113229Smdodd return; 340113229Smdodd} 341113229Smdodd 342113229Smdoddstatic void 343115400Smdoddlm_add (const char *p, const char *f, const char *t) 344113229Smdodd{ 345113229Smdodd struct lm_list *lml; 346113229Smdodd struct lm *lm; 347113229Smdodd 348113229Smdodd if (p == NULL) 349113229Smdodd p = "$DEFAULT$"; 350113229Smdodd 351115445Smdodd dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t); 352115445Smdodd 353113229Smdodd if ((lml = lmp_find(p)) == NULL) 354115150Smdodd lml = lmp_init(xstrdup(p)); 355113229Smdodd 356115150Smdodd lm = xmalloc(sizeof(struct lm)); 357115400Smdodd lm->f = xstrdup(f); 358115400Smdodd lm->t = xstrdup(t); 359113229Smdodd TAILQ_INSERT_HEAD(lml, lm, lm_link); 360141232Smdodd lm_count++; 361113229Smdodd} 362113229Smdodd 363113229Smdoddchar * 364113229Smdoddlm_find (const char *p, const char *f) 365113229Smdodd{ 366113229Smdodd struct lm_list *lml; 367113229Smdodd char *t; 368113229Smdodd 369115445Smdodd dbg("%s(\"%s\", \"%s\")", __func__, p, f); 370115445Smdodd 371113229Smdodd if (p != NULL && (lml = lmp_find(p)) != NULL) { 372113229Smdodd t = lml_find(lml, f); 373115150Smdodd if (t != NULL) { 374115150Smdodd /* 375115150Smdodd * Add a global mapping if we have 376115150Smdodd * a successful constrained match. 377115150Smdodd */ 378115400Smdodd lm_add(NULL, f, t); 379113229Smdodd return (t); 380115150Smdodd } 381113229Smdodd } 382113229Smdodd lml = lmp_find("$DEFAULT$"); 383113229Smdodd if (lml != NULL) 384113229Smdodd return (lml_find(lml, f)); 385113229Smdodd else 386113229Smdodd return (NULL); 387113229Smdodd} 388113229Smdodd 389129638Smdodd/* Given a libmap translation list and a library name, return the 390129638Smdodd replacement library, or NULL */ 391127250Speterchar * 392127250Speterlm_findn (const char *p, const char *f, const int n) 393127250Speter{ 394127250Speter char pathbuf[64], *s, *t; 395127250Speter 396155084Speter if (n < sizeof(pathbuf) - 1) 397127250Speter s = pathbuf; 398155084Speter else 399127250Speter s = xmalloc(n + 1); 400155084Speter memcpy(s, f, n); 401155084Speter s[n] = '\0'; 402127250Speter t = lm_find(p, s); 403127250Speter if (s != pathbuf) 404127250Speter free(s); 405127250Speter return (t); 406127250Speter} 407127250Speter 408113229Smdoddstatic char * 409113229Smdoddlml_find (struct lm_list *lmh, const char *f) 410113229Smdodd{ 411113229Smdodd struct lm *lm; 412113229Smdodd 413115445Smdodd dbg("%s(%p, \"%s\")", __func__, lmh, f); 414115445Smdodd 415113229Smdodd TAILQ_FOREACH(lm, lmh, lm_link) 416127250Speter if (strcmp(f, lm->f) == 0) 417113229Smdodd return (lm->t); 418141230Smdodd return (NULL); 419113229Smdodd} 420113229Smdodd 421129638Smdodd/* Given an executable name, return a pointer to the translation list or 422129638Smdodd NULL if no matches */ 423113229Smdoddstatic struct lm_list * 424113229Smdoddlmp_find (const char *n) 425113229Smdodd{ 426113229Smdodd struct lmp *lmp; 427113229Smdodd 428115445Smdodd dbg("%s(\"%s\")", __func__, n); 429115445Smdodd 430113229Smdodd TAILQ_FOREACH(lmp, &lmp_head, lmp_link) 431129638Smdodd if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) || 432129638Smdodd (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) || 433129638Smdodd (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0)) 434113229Smdodd return (&lmp->lml); 435113229Smdodd return (NULL); 436113229Smdodd} 437113229Smdodd 438113229Smdoddstatic struct lm_list * 439113229Smdoddlmp_init (char *n) 440113229Smdodd{ 441113229Smdodd struct lmp *lmp; 442113229Smdodd 443115445Smdodd dbg("%s(\"%s\")", __func__, n); 444115445Smdodd 445115150Smdodd lmp = xmalloc(sizeof(struct lmp)); 446113229Smdodd lmp->p = n; 447129638Smdodd if (n[strlen(n)-1] == '/') 448129638Smdodd lmp->type = T_DIRECTORY; 449129638Smdodd else if (strchr(n,'/') == NULL) 450129638Smdodd lmp->type = T_BASENAME; 451129638Smdodd else 452129638Smdodd lmp->type = T_EXACT; 453113229Smdodd TAILQ_INIT(&lmp->lml); 454113229Smdodd TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link); 455113229Smdodd 456113229Smdodd return (&lmp->lml); 457113229Smdodd} 458129638Smdodd 459129638Smdodd/* libc basename is overkill. Return a pointer to the character after the 460129638Smdodd last /, or the original string if there are no slashes. */ 461129638Smdoddstatic const char * 462129638Smdoddquickbasename (const char *path) 463129638Smdodd{ 464129638Smdodd const char *p = path; 465141230Smdodd for (; *path; path++) { 466129638Smdodd if (*path == '/') 467129638Smdodd p = path+1; 468129638Smdodd } 469141230Smdodd return (p); 470129638Smdodd} 471