1113229Smdodd/* 2113229Smdodd * $FreeBSD$ 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" 19115150Smdodd 20113229Smdodd#ifndef _PATH_LIBMAP_CONF 21113229Smdodd#define _PATH_LIBMAP_CONF "/etc/libmap.conf" 22113229Smdodd#endif 23113229Smdodd 24127250Speter#ifdef COMPAT_32BIT 25127250Speter#undef _PATH_LIBMAP_CONF 26127250Speter#define _PATH_LIBMAP_CONF "/etc/libmap32.conf" 27127250Speter#endif 28127250Speter 29113229SmdoddTAILQ_HEAD(lm_list, lm); 30113229Smdoddstruct lm { 31113229Smdodd char *f; 32113229Smdodd char *t; 33113229Smdodd TAILQ_ENTRY(lm) lm_link; 34113229Smdodd}; 35113229Smdodd 36113229SmdoddTAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head); 37113229Smdoddstruct lmp { 38113229Smdodd char *p; 39129638Smdodd enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type; 40113229Smdodd struct lm_list lml; 41113229Smdodd TAILQ_ENTRY(lmp) lmp_link; 42113229Smdodd}; 43113229Smdodd 44234851Sbaptstatic TAILQ_HEAD(lmc_list, lmc) lmc_head = TAILQ_HEAD_INITIALIZER(lmc_head); 45234851Sbaptstruct lmc { 46234851Sbapt char *path; 47234851Sbapt TAILQ_ENTRY(lmc) next; 48234851Sbapt}; 49234851Sbapt 50232862Skibstatic int lm_count; 51141232Smdodd 52232862Skibstatic void lmc_parse(char *, size_t); 53234851Sbaptstatic void lmc_parse_file(char *); 54234851Sbaptstatic void lmc_parse_dir(char *); 55232862Skibstatic void lm_add(const char *, const char *, const char *); 56232862Skibstatic void lm_free(struct lm_list *); 57232862Skibstatic char *lml_find(struct lm_list *, const char *); 58232862Skibstatic struct lm_list *lmp_find(const char *); 59232862Skibstatic struct lm_list *lmp_init(char *); 60232862Skibstatic const char *quickbasename(const char *); 61113229Smdodd 62113312Smdodd#define iseol(c) (((c) == '#') || ((c) == '\0') || \ 63113312Smdodd ((c) == '\n') || ((c) == '\r')) 64113312Smdodd 65232572Skib/* 66232572Skib * Do not use ctype.h macros, which rely on working TLS. It is 67232572Skib * too early to have thread-local variables functional. 68232572Skib */ 69232590Spluknet#define rtld_isspace(c) ((c) == ' ' || (c) == '\t') 70232572Skib 71120038Smdoddint 72232862Skiblm_init(char *libmap_override) 73113229Smdodd{ 74234851Sbapt char *p; 75113229Smdodd 76232862Skib dbg("lm_init(\"%s\")", libmap_override); 77113229Smdodd TAILQ_INIT(&lmp_head); 78113229Smdodd 79234851Sbapt lmc_parse_file(_PATH_LIBMAP_CONF); 80113229Smdodd 81141232Smdodd if (libmap_override) { 82232862Skib /* 83266640Skib * Do some character replacement to make $LDLIBMAP look 84232862Skib * like a text file, then parse it. 85232862Skib */ 86141232Smdodd libmap_override = xstrdup(libmap_override); 87141232Smdodd for (p = libmap_override; *p; p++) { 88141232Smdodd switch (*p) { 89232862Skib case '=': 90232862Skib *p = ' '; 91232862Skib break; 92232862Skib case ',': 93232862Skib *p = '\n'; 94232862Skib break; 95141232Smdodd } 96141232Smdodd } 97266640Skib lmc_parse(libmap_override, p - libmap_override); 98266640Skib free(libmap_override); 99141232Smdodd } 100141232Smdodd 101141232Smdodd return (lm_count == 0); 102141232Smdodd} 103141232Smdodd 104141232Smdoddstatic void 105234851Sbaptlmc_parse_file(char *path) 106234851Sbapt{ 107234851Sbapt struct lmc *p; 108234851Sbapt struct stat st; 109234851Sbapt int fd; 110234851Sbapt char *rpath; 111234851Sbapt char *lm_map; 112234851Sbapt 113234851Sbapt rpath = realpath(path, NULL); 114234851Sbapt if (rpath == NULL) 115234851Sbapt return; 116234851Sbapt 117234851Sbapt TAILQ_FOREACH(p, &lmc_head, next) { 118234851Sbapt if (strcmp(p->path, rpath) == 0) { 119234851Sbapt free(rpath); 120234851Sbapt return; 121234851Sbapt } 122234851Sbapt } 123234851Sbapt 124242587Sjilles fd = open(rpath, O_RDONLY | O_CLOEXEC); 125234851Sbapt if (fd == -1) { 126235059Sbapt dbg("lm_parse_file: open(\"%s\") failed, %s", rpath, 127234851Sbapt rtld_strerror(errno)); 128234851Sbapt free(rpath); 129234851Sbapt return; 130234851Sbapt } 131234851Sbapt if (fstat(fd, &st) == -1) { 132234851Sbapt close(fd); 133235059Sbapt dbg("lm_parse_file: fstat(\"%s\") failed, %s", rpath, 134234851Sbapt rtld_strerror(errno)); 135234851Sbapt free(rpath); 136234851Sbapt return; 137234851Sbapt } 138234851Sbapt lm_map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 139234851Sbapt if (lm_map == (const char *)MAP_FAILED) { 140234851Sbapt close(fd); 141235059Sbapt dbg("lm_parse_file: mmap(\"%s\") failed, %s", rpath, 142234851Sbapt rtld_strerror(errno)); 143234851Sbapt free(rpath); 144234851Sbapt return; 145234851Sbapt } 146234851Sbapt close(fd); 147234851Sbapt p = xmalloc(sizeof(struct lmc)); 148234851Sbapt p->path = rpath; 149234851Sbapt TAILQ_INSERT_HEAD(&lmc_head, p, next); 150234851Sbapt lmc_parse(lm_map, st.st_size); 151234851Sbapt munmap(lm_map, st.st_size); 152234851Sbapt} 153234851Sbapt 154234851Sbaptstatic void 155234851Sbaptlmc_parse_dir(char *idir) 156234851Sbapt{ 157234851Sbapt DIR *d; 158234851Sbapt struct dirent *dp; 159234851Sbapt struct lmc *p; 160234851Sbapt char conffile[MAXPATHLEN]; 161234851Sbapt char *ext; 162234851Sbapt char *rpath; 163234851Sbapt 164234851Sbapt rpath = realpath(idir, NULL); 165234851Sbapt if (rpath == NULL) 166234851Sbapt return; 167234851Sbapt 168234851Sbapt TAILQ_FOREACH(p, &lmc_head, next) { 169234851Sbapt if (strcmp(p->path, rpath) == 0) { 170234851Sbapt free(rpath); 171234851Sbapt return; 172234851Sbapt } 173234851Sbapt } 174234851Sbapt d = opendir(idir); 175234851Sbapt if (d == NULL) { 176234851Sbapt free(rpath); 177234851Sbapt return; 178234851Sbapt } 179234851Sbapt 180234851Sbapt p = xmalloc(sizeof(struct lmc)); 181234851Sbapt p->path = rpath; 182234851Sbapt TAILQ_INSERT_HEAD(&lmc_head, p, next); 183234851Sbapt 184234851Sbapt while ((dp = readdir(d)) != NULL) { 185234851Sbapt if (dp->d_ino == 0) 186234851Sbapt continue; 187234851Sbapt if (dp->d_type != DT_REG) 188234851Sbapt continue; 189234851Sbapt ext = strrchr(dp->d_name, '.'); 190234851Sbapt if (ext == NULL) 191234851Sbapt continue; 192234851Sbapt if (strcmp(ext, ".conf") != 0) 193234851Sbapt continue; 194234851Sbapt if (strlcpy(conffile, idir, MAXPATHLEN) >= MAXPATHLEN) 195234851Sbapt continue; /* too long */ 196234851Sbapt if (strlcat(conffile, "/", MAXPATHLEN) >= MAXPATHLEN) 197234851Sbapt continue; /* too long */ 198234851Sbapt if (strlcat(conffile, dp->d_name, MAXPATHLEN) >= MAXPATHLEN) 199234851Sbapt continue; /* too long */ 200234851Sbapt lmc_parse_file(conffile); 201234851Sbapt } 202234851Sbapt closedir(d); 203234851Sbapt} 204234851Sbapt 205234851Sbaptstatic void 206232862Skiblmc_parse(char *lm_p, size_t lm_len) 207141232Smdodd{ 208232862Skib char *cp, *f, *t, *c, *p; 209232862Skib char prog[MAXPATHLEN]; 210234851Sbapt /* allow includedir + full length path */ 211234851Sbapt char line[MAXPATHLEN + 13]; 212232862Skib size_t cnt; 213232862Skib int i; 214234851Sbapt 215232862Skib cnt = 0; 216113229Smdodd p = NULL; 217232862Skib while (cnt < lm_len) { 218232862Skib i = 0; 219270756Spfg while (cnt < lm_len && lm_p[cnt] != '\n' && 220232862Skib i < sizeof(line) - 1) { 221232862Skib line[i] = lm_p[cnt]; 222232862Skib cnt++; 223232862Skib i++; 224232862Skib } 225232862Skib line[i] = '\0'; 226270756Spfg while (cnt < lm_len && lm_p[cnt] != '\n') 227232862Skib cnt++; 228232862Skib /* skip over nl */ 229232862Skib cnt++; 230232862Skib 231232862Skib cp = &line[0]; 232115400Smdodd t = f = c = NULL; 233113312Smdodd 234113229Smdodd /* Skip over leading space */ 235232590Spluknet while (rtld_isspace(*cp)) cp++; 236113312Smdodd 237113229Smdodd /* Found a comment or EOL */ 238114316Skan if (iseol(*cp)) continue; 239113312Smdodd 240113312Smdodd /* Found a constraint selector */ 241113229Smdodd if (*cp == '[') { 242113229Smdodd cp++; 243113312Smdodd 244113229Smdodd /* Skip leading space */ 245232590Spluknet while (rtld_isspace(*cp)) cp++; 246113312Smdodd 247113229Smdodd /* Found comment, EOL or end of selector */ 248114316Skan if (iseol(*cp) || *cp == ']') 249114316Skan continue; 250113312Smdodd 251115400Smdodd c = cp++; 252113229Smdodd /* Skip to end of word */ 253232590Spluknet while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']') 254114316Skan cp++; 255113312Smdodd 256113312Smdodd /* Skip and zero out trailing space */ 257232590Spluknet while (rtld_isspace(*cp)) *cp++ = '\0'; 258113312Smdodd 259113312Smdodd /* Check if there is a closing brace */ 260114316Skan if (*cp != ']') continue; 261113312Smdodd 262113312Smdodd /* Terminate string if there was no trailing space */ 263113229Smdodd *cp++ = '\0'; 264113312Smdodd 265113312Smdodd /* 266113312Smdodd * There should be nothing except whitespace or comment 267115396Skan from this point to the end of the line. 268113312Smdodd */ 269232590Spluknet while(rtld_isspace(*cp)) cp++; 270114316Skan if (!iseol(*cp)) continue; 271113312Smdodd 272234851Sbapt if (strlcpy(prog, c, sizeof prog) >= sizeof prog) 273234851Sbapt continue; 274114316Skan p = prog; 275114316Skan continue; 276113229Smdodd } 277113312Smdodd 278113312Smdodd /* Parse the 'from' candidate. */ 279114316Skan f = cp++; 280232590Spluknet while (!rtld_isspace(*cp) && !iseol(*cp)) cp++; 281113312Smdodd 282113312Smdodd /* Skip and zero out the trailing whitespace */ 283232590Spluknet while (rtld_isspace(*cp)) *cp++ = '\0'; 284113312Smdodd 285113312Smdodd /* Found a comment or EOL */ 286114316Skan if (iseol(*cp)) continue; 287113312Smdodd 288113312Smdodd /* Parse 'to' mapping */ 289114316Skan t = cp++; 290232590Spluknet while (!rtld_isspace(*cp) && !iseol(*cp)) cp++; 291115396Skan 292114316Skan /* Skip and zero out the trailing whitespace */ 293232590Spluknet while (rtld_isspace(*cp)) *cp++ = '\0'; 294113229Smdodd 295114316Skan /* Should be no extra tokens at this point */ 296114316Skan if (!iseol(*cp)) continue; 297114316Skan 298114316Skan *cp = '\0'; 299234851Sbapt if (strcmp(f, "includedir") == 0) 300234851Sbapt lmc_parse_dir(t); 301234851Sbapt else if (strcmp(f, "include") == 0) 302234851Sbapt lmc_parse_file(t); 303234851Sbapt else 304234851Sbapt lm_add(p, f, t); 305113229Smdodd } 306113229Smdodd} 307113229Smdodd 308113229Smdoddstatic void 309113229Smdoddlm_free (struct lm_list *lml) 310113229Smdodd{ 311113229Smdodd struct lm *lm; 312113229Smdodd 313115445Smdodd dbg("%s(%p)", __func__, lml); 314115445Smdodd 315113229Smdodd while (!TAILQ_EMPTY(lml)) { 316113229Smdodd lm = TAILQ_FIRST(lml); 317113229Smdodd TAILQ_REMOVE(lml, lm, lm_link); 318113229Smdodd free(lm->f); 319113229Smdodd free(lm->t); 320113229Smdodd free(lm); 321113229Smdodd } 322113229Smdodd return; 323113229Smdodd} 324113229Smdodd 325113229Smdoddvoid 326113229Smdoddlm_fini (void) 327113229Smdodd{ 328113229Smdodd struct lmp *lmp; 329234851Sbapt struct lmc *p; 330113229Smdodd 331115445Smdodd dbg("%s()", __func__); 332115445Smdodd 333234851Sbapt while (!TAILQ_EMPTY(&lmc_head)) { 334234851Sbapt p = TAILQ_FIRST(&lmc_head); 335234851Sbapt TAILQ_REMOVE(&lmc_head, p, next); 336234851Sbapt free(p->path); 337234851Sbapt free(p); 338234851Sbapt } 339234851Sbapt 340113229Smdodd while (!TAILQ_EMPTY(&lmp_head)) { 341113229Smdodd lmp = TAILQ_FIRST(&lmp_head); 342113229Smdodd TAILQ_REMOVE(&lmp_head, lmp, lmp_link); 343113229Smdodd free(lmp->p); 344113229Smdodd lm_free(&lmp->lml); 345113229Smdodd free(lmp); 346113229Smdodd } 347113229Smdodd return; 348113229Smdodd} 349113229Smdodd 350113229Smdoddstatic void 351115400Smdoddlm_add (const char *p, const char *f, const char *t) 352113229Smdodd{ 353113229Smdodd struct lm_list *lml; 354113229Smdodd struct lm *lm; 355113229Smdodd 356113229Smdodd if (p == NULL) 357113229Smdodd p = "$DEFAULT$"; 358113229Smdodd 359115445Smdodd dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t); 360115445Smdodd 361113229Smdodd if ((lml = lmp_find(p)) == NULL) 362115150Smdodd lml = lmp_init(xstrdup(p)); 363113229Smdodd 364115150Smdodd lm = xmalloc(sizeof(struct lm)); 365115400Smdodd lm->f = xstrdup(f); 366115400Smdodd lm->t = xstrdup(t); 367113229Smdodd TAILQ_INSERT_HEAD(lml, lm, lm_link); 368141232Smdodd lm_count++; 369113229Smdodd} 370113229Smdodd 371113229Smdoddchar * 372113229Smdoddlm_find (const char *p, const char *f) 373113229Smdodd{ 374113229Smdodd struct lm_list *lml; 375113229Smdodd char *t; 376113229Smdodd 377115445Smdodd dbg("%s(\"%s\", \"%s\")", __func__, p, f); 378115445Smdodd 379113229Smdodd if (p != NULL && (lml = lmp_find(p)) != NULL) { 380113229Smdodd t = lml_find(lml, f); 381115150Smdodd if (t != NULL) { 382115150Smdodd /* 383115150Smdodd * Add a global mapping if we have 384115150Smdodd * a successful constrained match. 385115150Smdodd */ 386115400Smdodd lm_add(NULL, f, t); 387113229Smdodd return (t); 388115150Smdodd } 389113229Smdodd } 390113229Smdodd lml = lmp_find("$DEFAULT$"); 391113229Smdodd if (lml != NULL) 392113229Smdodd return (lml_find(lml, f)); 393113229Smdodd else 394113229Smdodd return (NULL); 395113229Smdodd} 396113229Smdodd 397129638Smdodd/* Given a libmap translation list and a library name, return the 398129638Smdodd replacement library, or NULL */ 399127250Speterchar * 400127250Speterlm_findn (const char *p, const char *f, const int n) 401127250Speter{ 402127250Speter char pathbuf[64], *s, *t; 403127250Speter 404155084Speter if (n < sizeof(pathbuf) - 1) 405127250Speter s = pathbuf; 406155084Speter else 407127250Speter s = xmalloc(n + 1); 408155084Speter memcpy(s, f, n); 409155084Speter s[n] = '\0'; 410127250Speter t = lm_find(p, s); 411127250Speter if (s != pathbuf) 412127250Speter free(s); 413127250Speter return (t); 414127250Speter} 415127250Speter 416113229Smdoddstatic char * 417113229Smdoddlml_find (struct lm_list *lmh, const char *f) 418113229Smdodd{ 419113229Smdodd struct lm *lm; 420113229Smdodd 421115445Smdodd dbg("%s(%p, \"%s\")", __func__, lmh, f); 422115445Smdodd 423113229Smdodd TAILQ_FOREACH(lm, lmh, lm_link) 424127250Speter if (strcmp(f, lm->f) == 0) 425113229Smdodd return (lm->t); 426141230Smdodd return (NULL); 427113229Smdodd} 428113229Smdodd 429129638Smdodd/* Given an executable name, return a pointer to the translation list or 430129638Smdodd NULL if no matches */ 431113229Smdoddstatic struct lm_list * 432113229Smdoddlmp_find (const char *n) 433113229Smdodd{ 434113229Smdodd struct lmp *lmp; 435113229Smdodd 436115445Smdodd dbg("%s(\"%s\")", __func__, n); 437115445Smdodd 438113229Smdodd TAILQ_FOREACH(lmp, &lmp_head, lmp_link) 439129638Smdodd if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) || 440129638Smdodd (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) || 441129638Smdodd (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0)) 442113229Smdodd return (&lmp->lml); 443113229Smdodd return (NULL); 444113229Smdodd} 445113229Smdodd 446113229Smdoddstatic struct lm_list * 447113229Smdoddlmp_init (char *n) 448113229Smdodd{ 449113229Smdodd struct lmp *lmp; 450113229Smdodd 451115445Smdodd dbg("%s(\"%s\")", __func__, n); 452115445Smdodd 453115150Smdodd lmp = xmalloc(sizeof(struct lmp)); 454113229Smdodd lmp->p = n; 455129638Smdodd if (n[strlen(n)-1] == '/') 456129638Smdodd lmp->type = T_DIRECTORY; 457129638Smdodd else if (strchr(n,'/') == NULL) 458129638Smdodd lmp->type = T_BASENAME; 459129638Smdodd else 460129638Smdodd lmp->type = T_EXACT; 461113229Smdodd TAILQ_INIT(&lmp->lml); 462113229Smdodd TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link); 463113229Smdodd 464113229Smdodd return (&lmp->lml); 465113229Smdodd} 466129638Smdodd 467129638Smdodd/* libc basename is overkill. Return a pointer to the character after the 468129638Smdodd last /, or the original string if there are no slashes. */ 469129638Smdoddstatic const char * 470129638Smdoddquickbasename (const char *path) 471129638Smdodd{ 472129638Smdodd const char *p = path; 473141230Smdodd for (; *path; path++) { 474129638Smdodd if (*path == '/') 475129638Smdodd p = path+1; 476129638Smdodd } 477141230Smdodd return (p); 478129638Smdodd} 479