Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/rtld-elf/libmap.c
34822 views
1
/*
2
*/
3
4
#include <sys/types.h>
5
#include <sys/param.h>
6
#include <sys/fcntl.h>
7
#include <sys/mman.h>
8
#include <sys/queue.h>
9
#include <sys/stat.h>
10
#include <dirent.h>
11
#include <errno.h>
12
#include <stdlib.h>
13
#include <string.h>
14
15
#include "debug.h"
16
#include "rtld.h"
17
#include "libmap.h"
18
#include "rtld_paths.h"
19
#include "rtld_libc.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
static 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
dev_t dev;
40
ino_t ino;
41
TAILQ_ENTRY(lmc) next;
42
};
43
44
static int lm_count;
45
46
static void lmc_parse(char *, size_t);
47
static void lmc_parse_file(const char *);
48
static void lmc_parse_dir(const char *);
49
static void lm_add(const char *, const char *, const char *);
50
static void lm_free(struct lm_list *);
51
static char *lml_find(struct lm_list *, const char *);
52
static struct lm_list *lmp_find(const char *);
53
static struct lm_list *lmp_init(char *);
54
static const char *quickbasename(const char *);
55
56
#define iseol(c) (((c) == '#') || ((c) == '\0') || \
57
((c) == '\n') || ((c) == '\r'))
58
59
/*
60
* Do not use ctype.h macros, which rely on working TLS. Rtld does
61
* not support TLS for itself.
62
*/
63
#define rtld_isspace(c) ((c) == ' ' || (c) == '\t')
64
65
int
66
lm_init(const char *libmap_override)
67
{
68
char *l, *p;
69
70
dbg("lm_init(\"%s\")", libmap_override);
71
TAILQ_INIT(&lmp_head);
72
73
lmc_parse_file(ld_path_libmap_conf);
74
75
if (libmap_override != NULL) {
76
/*
77
* Do some character replacement to make $LD_LIBMAP look
78
* like a text file, then parse it.
79
*/
80
l = xstrdup(libmap_override);
81
for (p = l; *p != 0; p++) {
82
switch (*p) {
83
case '=':
84
*p = ' ';
85
break;
86
case ',':
87
*p = '\n';
88
break;
89
}
90
}
91
lmc_parse(l, p - l);
92
free(l);
93
}
94
95
return (lm_count == 0);
96
}
97
98
static void
99
lmc_parse_file(const char *path)
100
{
101
struct lmc *p;
102
char *lm_map;
103
struct stat st;
104
ssize_t retval;
105
int fd, saved_errno;
106
107
TAILQ_FOREACH(p, &lmc_head, next) {
108
if (strcmp(p->path, path) == 0)
109
return;
110
}
111
112
fd = open(path, O_RDONLY | O_CLOEXEC);
113
if (fd == -1) {
114
dbg("lm_parse_file: open(\"%s\") failed, %s", path,
115
rtld_strerror(errno));
116
return;
117
}
118
if (fstat(fd, &st) == -1) {
119
dbg("lm_parse_file: fstat(\"%s\") failed, %s", path,
120
rtld_strerror(errno));
121
close(fd);
122
return;
123
}
124
125
TAILQ_FOREACH(p, &lmc_head, next) {
126
if (p->dev == st.st_dev && p->ino == st.st_ino) {
127
close(fd);
128
return;
129
}
130
}
131
132
lm_map = xmalloc(st.st_size);
133
retval = read(fd, lm_map, st.st_size);
134
saved_errno = errno;
135
close(fd);
136
if (retval != st.st_size) {
137
if (retval == -1) {
138
dbg("lm_parse_file: read(\"%s\") failed, %s", path,
139
rtld_strerror(saved_errno));
140
} else {
141
dbg("lm_parse_file: short read(\"%s\"), %zd vs %jd",
142
path, retval, (uintmax_t)st.st_size);
143
}
144
free(lm_map);
145
return;
146
}
147
p = xmalloc(sizeof(struct lmc));
148
p->path = xstrdup(path);
149
p->dev = st.st_dev;
150
p->ino = st.st_ino;
151
TAILQ_INSERT_HEAD(&lmc_head, p, next);
152
lmc_parse(lm_map, st.st_size);
153
free(lm_map);
154
}
155
156
static void
157
lmc_parse_dir(const char *idir)
158
{
159
DIR *d;
160
struct dirent *dp;
161
struct lmc *p;
162
char conffile[MAXPATHLEN];
163
char *ext;
164
165
TAILQ_FOREACH(p, &lmc_head, next) {
166
if (strcmp(p->path, idir) == 0)
167
return;
168
}
169
d = opendir(idir);
170
if (d == NULL)
171
return;
172
173
p = xmalloc(sizeof(struct lmc));
174
p->path = xstrdup(idir);
175
p->dev = NODEV;
176
p->ino = 0;
177
TAILQ_INSERT_HEAD(&lmc_head, p, next);
178
179
while ((dp = readdir(d)) != NULL) {
180
if (dp->d_ino == 0)
181
continue;
182
if (dp->d_type != DT_REG)
183
continue;
184
ext = strrchr(dp->d_name, '.');
185
if (ext == NULL)
186
continue;
187
if (strcmp(ext, ".conf") != 0)
188
continue;
189
if (strlcpy(conffile, idir, MAXPATHLEN) >= MAXPATHLEN)
190
continue; /* too long */
191
if (strlcat(conffile, "/", MAXPATHLEN) >= MAXPATHLEN)
192
continue; /* too long */
193
if (strlcat(conffile, dp->d_name, MAXPATHLEN) >= MAXPATHLEN)
194
continue; /* too long */
195
lmc_parse_file(conffile);
196
}
197
closedir(d);
198
}
199
200
static void
201
lmc_parse(char *lm_p, size_t lm_len)
202
{
203
char *cp, *f, *t, *c, *p;
204
char prog[MAXPATHLEN];
205
/* allow includedir + full length path */
206
char line[MAXPATHLEN + 13];
207
size_t cnt, i;
208
209
cnt = 0;
210
p = NULL;
211
while (cnt < lm_len) {
212
i = 0;
213
while (cnt < lm_len && lm_p[cnt] != '\n' &&
214
i < sizeof(line) - 1) {
215
line[i] = lm_p[cnt];
216
cnt++;
217
i++;
218
}
219
line[i] = '\0';
220
while (cnt < lm_len && lm_p[cnt] != '\n')
221
cnt++;
222
/* skip over nl */
223
cnt++;
224
225
cp = &line[0];
226
t = f = c = NULL;
227
228
/* Skip over leading space */
229
while (rtld_isspace(*cp))
230
cp++;
231
232
/* Found a comment or EOL */
233
if (iseol(*cp))
234
continue;
235
236
/* Found a constraint selector */
237
if (*cp == '[') {
238
cp++;
239
240
/* Skip leading space */
241
while (rtld_isspace(*cp))
242
cp++;
243
244
/* Found comment, EOL or end of selector */
245
if (iseol(*cp) || *cp == ']')
246
continue;
247
248
c = cp++;
249
/* Skip to end of word */
250
while (!rtld_isspace(*cp) && !iseol(*cp) && *cp != ']')
251
cp++;
252
253
/* Skip and zero out trailing space */
254
while (rtld_isspace(*cp))
255
*cp++ = '\0';
256
257
/* Check if there is a closing brace */
258
if (*cp != ']')
259
continue;
260
261
/* Terminate string if there was no trailing space */
262
*cp++ = '\0';
263
264
/*
265
* There should be nothing except whitespace or comment
266
from this point to the end of the line.
267
*/
268
while (rtld_isspace(*cp))
269
cp++;
270
if (!iseol(*cp))
271
continue;
272
273
if (strlcpy(prog, c, sizeof prog) >= sizeof prog)
274
continue;
275
p = prog;
276
continue;
277
}
278
279
/* Parse the 'from' candidate. */
280
f = cp++;
281
while (!rtld_isspace(*cp) && !iseol(*cp))
282
cp++;
283
284
/* Skip and zero out the trailing whitespace */
285
while (rtld_isspace(*cp))
286
*cp++ = '\0';
287
288
/* Found a comment or EOL */
289
if (iseol(*cp))
290
continue;
291
292
/* Parse 'to' mapping */
293
t = cp++;
294
while (!rtld_isspace(*cp) && !iseol(*cp))
295
cp++;
296
297
/* Skip and zero out the trailing whitespace */
298
while (rtld_isspace(*cp))
299
*cp++ = '\0';
300
301
/* Should be no extra tokens at this point */
302
if (!iseol(*cp))
303
continue;
304
305
*cp = '\0';
306
if (strcmp(f, "includedir") == 0)
307
lmc_parse_dir(t);
308
else if (strcmp(f, "include") == 0)
309
lmc_parse_file(t);
310
else
311
lm_add(p, f, t);
312
}
313
}
314
315
static void
316
lm_free(struct lm_list *lml)
317
{
318
struct lm *lm;
319
320
dbg("%s(%p)", __func__, lml);
321
322
while (!TAILQ_EMPTY(lml)) {
323
lm = TAILQ_FIRST(lml);
324
TAILQ_REMOVE(lml, lm, lm_link);
325
free(lm->f);
326
free(lm->t);
327
free(lm);
328
}
329
}
330
331
void
332
lm_fini(void)
333
{
334
struct lmp *lmp;
335
struct lmc *p;
336
337
dbg("%s()", __func__);
338
339
while (!TAILQ_EMPTY(&lmc_head)) {
340
p = TAILQ_FIRST(&lmc_head);
341
TAILQ_REMOVE(&lmc_head, p, next);
342
free(p->path);
343
free(p);
344
}
345
346
while (!TAILQ_EMPTY(&lmp_head)) {
347
lmp = TAILQ_FIRST(&lmp_head);
348
TAILQ_REMOVE(&lmp_head, lmp, lmp_link);
349
free(lmp->p);
350
lm_free(&lmp->lml);
351
free(lmp);
352
}
353
}
354
355
static void
356
lm_add(const char *p, const char *f, const char *t)
357
{
358
struct lm_list *lml;
359
struct lm *lm;
360
const char *t1;
361
362
if (p == NULL)
363
p = "$DEFAULT$";
364
365
dbg("%s(\"%s\", \"%s\", \"%s\")", __func__, p, f, t);
366
367
if ((lml = lmp_find(p)) == NULL)
368
lml = lmp_init(xstrdup(p));
369
370
t1 = lml_find(lml, f);
371
if (t1 == NULL || strcmp(t1, t) != 0) {
372
lm = xmalloc(sizeof(struct lm));
373
lm->f = xstrdup(f);
374
lm->t = xstrdup(t);
375
TAILQ_INSERT_HEAD(lml, lm, lm_link);
376
lm_count++;
377
}
378
}
379
380
char *
381
lm_find(const char *p, const char *f)
382
{
383
struct lm_list *lml;
384
char *t;
385
386
dbg("%s(\"%s\", \"%s\")", __func__, p, f);
387
388
if (p != NULL && (lml = lmp_find(p)) != NULL) {
389
t = lml_find(lml, f);
390
if (t != NULL) {
391
/*
392
* Add a global mapping if we have
393
* a successful constrained match.
394
*/
395
lm_add(NULL, f, t);
396
return (t);
397
}
398
}
399
lml = lmp_find("$DEFAULT$");
400
if (lml != NULL)
401
return (lml_find(lml, f));
402
return (NULL);
403
}
404
405
/*
406
* Given a libmap translation list and a library name, return the
407
* replacement library, or NULL.
408
*/
409
char *
410
lm_findn(const char *p, const char *f, const size_t n)
411
{
412
char pathbuf[64], *s, *t;
413
414
if (n < sizeof(pathbuf) - 1)
415
s = pathbuf;
416
else
417
s = xmalloc(n + 1);
418
memcpy(s, f, n);
419
s[n] = '\0';
420
t = lm_find(p, s);
421
if (s != pathbuf)
422
free(s);
423
return (t);
424
}
425
426
static char *
427
lml_find(struct lm_list *lmh, const char *f)
428
{
429
struct lm *lm;
430
431
dbg("%s(%p, \"%s\")", __func__, lmh, f);
432
433
TAILQ_FOREACH(lm, lmh, lm_link) {
434
if (strcmp(f, lm->f) == 0)
435
return (lm->t);
436
}
437
return (NULL);
438
}
439
440
/*
441
* Given an executable name, return a pointer to the translation list or
442
* NULL if no matches.
443
*/
444
static struct lm_list *
445
lmp_find(const char *n)
446
{
447
struct lmp *lmp;
448
449
dbg("%s(\"%s\")", __func__, n);
450
451
TAILQ_FOREACH(lmp, &lmp_head, lmp_link) {
452
if ((lmp->type == T_EXACT && strcmp(n, lmp->p) == 0) ||
453
(lmp->type == T_DIRECTORY && strncmp(n, lmp->p,
454
strlen(lmp->p)) == 0) ||
455
(lmp->type == T_BASENAME && strcmp(quickbasename(n),
456
lmp->p) == 0))
457
return (&lmp->lml);
458
}
459
return (NULL);
460
}
461
462
static struct lm_list *
463
lmp_init(char *n)
464
{
465
struct lmp *lmp;
466
467
dbg("%s(\"%s\")", __func__, n);
468
469
lmp = xmalloc(sizeof(struct lmp));
470
lmp->p = n;
471
if (n[strlen(n) - 1] == '/')
472
lmp->type = T_DIRECTORY;
473
else if (strchr(n,'/') == NULL)
474
lmp->type = T_BASENAME;
475
else
476
lmp->type = T_EXACT;
477
TAILQ_INIT(&lmp->lml);
478
TAILQ_INSERT_HEAD(&lmp_head, lmp, lmp_link);
479
480
return (&lmp->lml);
481
}
482
483
/*
484
* libc basename is overkill. Return a pointer to the character after
485
* the last /, or the original string if there are no slashes.
486
*/
487
static const char *
488
quickbasename(const char *path)
489
{
490
const char *p;
491
492
for (p = path; *path != '\0'; path++) {
493
if (*path == '/')
494
p = path + 1;
495
}
496
return (p);
497
}
498
499