libmap.c revision 232590
1113229Smdodd/* 2113229Smdodd * $FreeBSD: head/libexec/rtld-elf/libmap.c 232590 2012-03-06 09:34:30Z pluknet $ 3113229Smdodd */ 4113229Smdodd 5113229Smdodd#include <stdio.h> 6113229Smdodd#include <string.h> 7113229Smdodd#include <stdlib.h> 8113229Smdodd#include <sys/queue.h> 9113229Smdodd#include <sys/param.h> 10113229Smdodd 11115150Smdodd#include "debug.h" 12115150Smdodd#include "rtld.h" 13116513Smdodd#include "libmap.h" 14115150Smdodd 15113229Smdodd#ifndef _PATH_LIBMAP_CONF 16113229Smdodd#define _PATH_LIBMAP_CONF "/etc/libmap.conf" 17113229Smdodd#endif 18113229Smdodd 19127250Speter#ifdef COMPAT_32BIT 20127250Speter#undef _PATH_LIBMAP_CONF 21127250Speter#define _PATH_LIBMAP_CONF "/etc/libmap32.conf" 22127250Speter#endif 23127250Speter 24113229SmdoddTAILQ_HEAD(lm_list, lm); 25113229Smdoddstruct lm { 26113229Smdodd char *f; 27113229Smdodd char *t; 28113229Smdodd 29113229Smdodd TAILQ_ENTRY(lm) lm_link; 30113229Smdodd}; 31113229Smdodd 32113229SmdoddTAILQ_HEAD(lmp_list, lmp) lmp_head = TAILQ_HEAD_INITIALIZER(lmp_head); 33113229Smdoddstruct lmp { 34113229Smdodd char *p; 35129638Smdodd enum { T_EXACT=0, T_BASENAME, T_DIRECTORY } type; 36113229Smdodd struct lm_list lml; 37113229Smdodd TAILQ_ENTRY(lmp) lmp_link; 38113229Smdodd}; 39113229Smdodd 40141232Smdoddstatic int lm_count; 41141232Smdodd 42141232Smdoddstatic void lmc_parse (FILE *); 43115400Smdoddstatic void lm_add (const char *, const char *, const char *); 44113229Smdoddstatic void lm_free (struct lm_list *); 45113229Smdoddstatic char * lml_find (struct lm_list *, const char *); 46113229Smdoddstatic struct lm_list * lmp_find (const char *); 47113229Smdoddstatic struct lm_list * lmp_init (char *); 48129638Smdoddstatic const char * quickbasename (const char *); 49141232Smdoddstatic int readstrfn (void * cookie, char *buf, int len); 50141232Smdoddstatic int closestrfn (void * cookie); 51113229Smdodd 52113312Smdodd#define iseol(c) (((c) == '#') || ((c) == '\0') || \ 53113312Smdodd ((c) == '\n') || ((c) == '\r')) 54113312Smdodd 55232572Skib/* 56232572Skib * Do not use ctype.h macros, which rely on working TLS. It is 57232572Skib * too early to have thread-local variables functional. 58232572Skib */ 59232590Spluknet#define rtld_isspace(c) ((c) == ' ' || (c) == '\t') 60232572Skib 61120038Smdoddint 62141232Smdoddlm_init (char *libmap_override) 63113229Smdodd{ 64113229Smdodd FILE *fp; 65113229Smdodd 66141232Smdodd dbg("%s(\"%s\")", __func__, libmap_override); 67115445Smdodd 68113229Smdodd TAILQ_INIT(&lmp_head); 69113229Smdodd 70141232Smdodd fp = fopen(_PATH_LIBMAP_CONF, "r"); 71141232Smdodd if (fp) { 72141232Smdodd lmc_parse(fp); 73141232Smdodd fclose(fp); 74141232Smdodd } 75113229Smdodd 76141232Smdodd if (libmap_override) { 77141232Smdodd char *p; 78141232Smdodd /* do some character replacement to make $LIBMAP look like a 79141232Smdodd text file, then "open" it with funopen */ 80141232Smdodd libmap_override = xstrdup(libmap_override); 81141232Smdodd 82141232Smdodd for (p = libmap_override; *p; p++) { 83141232Smdodd switch (*p) { 84141232Smdodd case '=': 85141232Smdodd *p = ' '; break; 86141232Smdodd case ',': 87141232Smdodd *p = '\n'; break; 88141232Smdodd } 89141232Smdodd } 90141232Smdodd fp = funopen(libmap_override, readstrfn, NULL, NULL, closestrfn); 91141232Smdodd if (fp) { 92141232Smdodd lmc_parse(fp); 93141232Smdodd fclose(fp); 94141232Smdodd } 95141232Smdodd } 96141232Smdodd 97141232Smdodd return (lm_count == 0); 98141232Smdodd} 99141232Smdodd 100141232Smdoddstatic void 101141232Smdoddlmc_parse (FILE *fp) 102141232Smdodd{ 103141232Smdodd char *cp; 104141232Smdodd char *f, *t, *c, *p; 105141232Smdodd char prog[MAXPATHLEN]; 106141232Smdodd char line[MAXPATHLEN + 2]; 107141232Smdodd 108141232Smdodd dbg("%s(%p)", __func__, fp); 109141232Smdodd 110113229Smdodd p = NULL; 111113229Smdodd while ((cp = fgets(line, MAXPATHLEN + 1, fp)) != NULL) { 112115400Smdodd t = f = c = NULL; 113113312Smdodd 114113229Smdodd /* Skip over leading space */ 115232590Spluknet while (rtld_isspace(*cp)) cp++; 116113312Smdodd 117113229Smdodd /* Found a comment or EOL */ 118114316Skan if (iseol(*cp)) continue; 119113312Smdodd 120113312Smdodd /* Found a constraint selector */ 121113229Smdodd if (*cp == '[') { 122113229Smdodd cp++; 123113312Smdodd 124113229Smdodd /* Skip leading space */ 125232590Spluknet while (rtld_isspace(*cp)) cp++; 126113312Smdodd 127113229Smdodd /* Found comment, EOL or end of selector */ 128114316Skan if (iseol(*cp) || *cp == ']') 129114316Skan continue; 130113312Smdodd 131115400Smdodd c = cp++; 132113229Smdodd /* Skip to end of word */ 133232590Spluknet while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']') 134114316Skan cp++; 135113312Smdodd 136113312Smdodd /* Skip and zero out trailing space */ 137232590Spluknet while (rtld_isspace(*cp)) *cp++ = '\0'; 138113312Smdodd 139113312Smdodd /* Check if there is a closing brace */ 140114316Skan if (*cp != ']') continue; 141113312Smdodd 142113312Smdodd /* Terminate string if there was no trailing space */ 143113229Smdodd *cp++ = '\0'; 144113312Smdodd 145113312Smdodd /* 146113312Smdodd * There should be nothing except whitespace or comment 147115396Skan from this point to the end of the line. 148113312Smdodd */ 149232590Spluknet while(rtld_isspace(*cp)) cp++; 150114316Skan if (!iseol(*cp)) continue; 151113312Smdodd 152115400Smdodd strcpy(prog, c); 153114316Skan p = prog; 154114316Skan continue; 155113229Smdodd } 156113312Smdodd 157113312Smdodd /* Parse the 'from' candidate. */ 158114316Skan f = cp++; 159232590Spluknet while (!rtld_isspace(*cp) && !iseol(*cp)) cp++; 160113312Smdodd 161113312Smdodd /* Skip and zero out the trailing whitespace */ 162232590Spluknet while (rtld_isspace(*cp)) *cp++ = '\0'; 163113312Smdodd 164113312Smdodd /* Found a comment or EOL */ 165114316Skan if (iseol(*cp)) continue; 166113312Smdodd 167113312Smdodd /* Parse 'to' mapping */ 168114316Skan t = cp++; 169232590Spluknet while (!rtld_isspace(*cp) && !iseol(*cp)) cp++; 170115396Skan 171114316Skan /* Skip and zero out the trailing whitespace */ 172232590Spluknet while (rtld_isspace(*cp)) *cp++ = '\0'; 173113229Smdodd 174114316Skan /* Should be no extra tokens at this point */ 175114316Skan if (!iseol(*cp)) continue; 176114316Skan 177114316Skan *cp = '\0'; 178115400Smdodd lm_add(p, f, t); 179113229Smdodd } 180113229Smdodd} 181113229Smdodd 182113229Smdoddstatic void 183113229Smdoddlm_free (struct lm_list *lml) 184113229Smdodd{ 185113229Smdodd struct lm *lm; 186113229Smdodd 187115445Smdodd dbg("%s(%p)", __func__, lml); 188115445Smdodd 189113229Smdodd while (!TAILQ_EMPTY(lml)) { 190113229Smdodd lm = TAILQ_FIRST(lml); 191113229Smdodd TAILQ_REMOVE(lml, lm, lm_link); 192113229Smdodd free(lm->f); 193113229Smdodd free(lm->t); 194113229Smdodd free(lm); 195113229Smdodd } 196113229Smdodd return; 197113229Smdodd} 198113229Smdodd 199113229Smdoddvoid 200113229Smdoddlm_fini (void) 201113229Smdodd{ 202113229Smdodd struct lmp *lmp; 203113229Smdodd 204115445Smdodd dbg("%s()", __func__); 205115445Smdodd 206113229Smdodd while (!TAILQ_EMPTY(&lmp_head)) { 207113229Smdodd lmp = TAILQ_FIRST(&lmp_head); 208113229Smdodd TAILQ_REMOVE(&lmp_head, lmp, lmp_link); 209113229Smdodd free(lmp->p); 210113229Smdodd lm_free(&lmp->lml); 211113229Smdodd free(lmp); 212113229Smdodd } 213113229Smdodd return; 214113229Smdodd} 215113229Smdodd 216113229Smdoddstatic void 217115400Smdoddlm_add (const char *p, const char *f, const char *t) 218113229Smdodd{ 219113229Smdodd struct lm_list *lml; 220113229Smdodd struct lm *lm; 221113229Smdodd 222113229Smdodd if (p == NULL) 223113229Smdodd p = "$DEFAULT$"; 224113229Smdodd 225115445Smdodd dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t); 226115445Smdodd 227113229Smdodd if ((lml = lmp_find(p)) == NULL) 228115150Smdodd lml = lmp_init(xstrdup(p)); 229113229Smdodd 230115150Smdodd lm = xmalloc(sizeof(struct lm)); 231115400Smdodd lm->f = xstrdup(f); 232115400Smdodd lm->t = xstrdup(t); 233113229Smdodd TAILQ_INSERT_HEAD(lml, lm, lm_link); 234141232Smdodd lm_count++; 235113229Smdodd} 236113229Smdodd 237113229Smdoddchar * 238113229Smdoddlm_find (const char *p, const char *f) 239113229Smdodd{ 240113229Smdodd struct lm_list *lml; 241113229Smdodd char *t; 242113229Smdodd 243115445Smdodd dbg("%s(\"%s\", \"%s\")", __func__, p, f); 244115445Smdodd 245113229Smdodd if (p != NULL && (lml = lmp_find(p)) != NULL) { 246113229Smdodd t = lml_find(lml, f); 247115150Smdodd if (t != NULL) { 248115150Smdodd /* 249115150Smdodd * Add a global mapping if we have 250115150Smdodd * a successful constrained match. 251115150Smdodd */ 252115400Smdodd lm_add(NULL, f, t); 253113229Smdodd return (t); 254115150Smdodd } 255113229Smdodd } 256113229Smdodd lml = lmp_find("$DEFAULT$"); 257113229Smdodd if (lml != NULL) 258113229Smdodd return (lml_find(lml, f)); 259113229Smdodd else 260113229Smdodd return (NULL); 261113229Smdodd} 262113229Smdodd 263129638Smdodd/* Given a libmap translation list and a library name, return the 264129638Smdodd replacement library, or NULL */ 265127250Speter#ifdef COMPAT_32BIT 266127250Speterchar * 267127250Speterlm_findn (const char *p, const char *f, const int n) 268127250Speter{ 269127250Speter char pathbuf[64], *s, *t; 270127250Speter 271155084Speter if (n < sizeof(pathbuf) - 1) 272127250Speter s = pathbuf; 273155084Speter else 274127250Speter s = xmalloc(n + 1); 275155084Speter memcpy(s, f, n); 276155084Speter s[n] = '\0'; 277127250Speter t = lm_find(p, s); 278127250Speter if (s != pathbuf) 279127250Speter free(s); 280127250Speter return (t); 281127250Speter} 282127250Speter#endif 283127250Speter 284113229Smdoddstatic char * 285113229Smdoddlml_find (struct lm_list *lmh, const char *f) 286113229Smdodd{ 287113229Smdodd struct lm *lm; 288113229Smdodd 289115445Smdodd dbg("%s(%p, \"%s\")", __func__, lmh, f); 290115445Smdodd 291113229Smdodd TAILQ_FOREACH(lm, lmh, lm_link) 292127250Speter if (strcmp(f, lm->f) == 0) 293113229Smdodd return (lm->t); 294141230Smdodd return (NULL); 295113229Smdodd} 296113229Smdodd 297129638Smdodd/* Given an executable name, return a pointer to the translation list or 298129638Smdodd NULL if no matches */ 299113229Smdoddstatic struct lm_list * 300113229Smdoddlmp_find (const char *n) 301113229Smdodd{ 302113229Smdodd struct lmp *lmp; 303113229Smdodd 304115445Smdodd dbg("%s(\"%s\")", __func__, n); 305115445Smdodd 306113229Smdodd TAILQ_FOREACH(lmp, &lmp_head, lmp_link) 307129638Smdodd if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) || 308129638Smdodd (lmp->type == T_DIRECTORY && strncmp(n, lmp->p, strlen(lmp->p)) == 0) || 309129638Smdodd (lmp->type == T_BASENAME && strcmp(quickbasename(n), lmp->p) == 0)) 310113229Smdodd return (&lmp->lml); 311113229Smdodd return (NULL); 312113229Smdodd} 313113229Smdodd 314113229Smdoddstatic struct lm_list * 315113229Smdoddlmp_init (char *n) 316113229Smdodd{ 317113229Smdodd struct lmp *lmp; 318113229Smdodd 319115445Smdodd dbg("%s(\"%s\")", __func__, n); 320115445Smdodd 321115150Smdodd lmp = xmalloc(sizeof(struct lmp)); 322113229Smdodd lmp->p = n; 323129638Smdodd if (n[strlen(n)-1] == '/') 324129638Smdodd lmp->type = T_DIRECTORY; 325129638Smdodd else if (strchr(n,'/') == NULL) 326129638Smdodd lmp->type = T_BASENAME; 327129638Smdodd else 328129638Smdodd lmp->type = T_EXACT; 329113229Smdodd TAILQ_INIT(&lmp->lml); 330113229Smdodd TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link); 331113229Smdodd 332113229Smdodd return (&lmp->lml); 333113229Smdodd} 334129638Smdodd 335129638Smdodd/* libc basename is overkill. Return a pointer to the character after the 336129638Smdodd last /, or the original string if there are no slashes. */ 337129638Smdoddstatic const char * 338129638Smdoddquickbasename (const char *path) 339129638Smdodd{ 340129638Smdodd const char *p = path; 341141230Smdodd for (; *path; path++) { 342129638Smdodd if (*path == '/') 343129638Smdodd p = path+1; 344129638Smdodd } 345141230Smdodd return (p); 346129638Smdodd} 347141232Smdodd 348141232Smdoddstatic int 349141232Smdoddreadstrfn(void * cookie, char *buf, int len) 350141232Smdodd{ 351141232Smdodd static char *current; 352141232Smdodd static int left; 353141232Smdodd int copied; 354141232Smdodd 355141232Smdodd copied = 0; 356141232Smdodd if (!current) { 357141232Smdodd current = cookie; 358141232Smdodd left = strlen(cookie); 359141232Smdodd } 360141232Smdodd while (*current && left && len) { 361141232Smdodd *buf++ = *current++; 362141232Smdodd left--; 363141232Smdodd len--; 364141232Smdodd copied++; 365141232Smdodd } 366141232Smdodd return copied; 367141232Smdodd} 368141232Smdodd 369141232Smdoddstatic int 370141232Smdoddclosestrfn(void * cookie) 371141232Smdodd{ 372141232Smdodd free(cookie); 373141232Smdodd return 0; 374141232Smdodd} 375