#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <ar.h>
#include <ranlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include "ld.h"
#undef major
#undef minor
extern char *__progname;
int verbose;
static int nostd;
static int justread;
int merge;
static int rescan;
static int unconfig;
struct shlib_list {
char *name;
char *path;
int dewey[MAXDEWEY];
int ndewey;
#define major dewey[0]
#define minor dewey[1]
struct shlib_list *next;
};
static struct shlib_list *shlib_head = NULL, **shlib_tail = &shlib_head;
static char *dir_list;
static void enter(char *, char *, char *, int *, int);
static int dodir(char *, int);
static int buildhints(void);
static int readhints(void);
static void listhints(void);
static void
usage(void)
{
fprintf(stderr,
"usage: %s [-mRrsUv] [path ...]\n", __progname);
exit(1);
}
int
main(int argc, char *argv[])
{
int i, c;
int rval = 0;
if (pledge("stdio rpath wpath cpath fattr", NULL) == -1)
err(1, "pledge");
while ((c = getopt(argc, argv, "DmPrRsSUv")) != -1) {
switch (c) {
case 'R':
rescan = 1;
break;
case 'U':
rescan = unconfig = 1;
break;
case 'm':
merge = 1;
break;
case 'r':
justread = 1;
break;
case 's':
nostd = 1;
break;
case 'v':
verbose = 1;
break;
default:
usage();
break;
}
}
if (unconfig && merge)
errx(1, "cannot use -U with -m");
dir_list = xmalloc(1);
*dir_list = '\0';
if (justread || merge || rescan) {
if ((rval = readhints()) != 0)
return rval;
if (justread) {
listhints();
return 0;
}
add_search_path(dir_list);
dir_list = xrealloc(dir_list, 1);
*dir_list = '\0';
} else if (!nostd)
std_search_path();
if (unconfig) {
if (optind < argc)
for (i = optind; i < argc; i++)
remove_search_dir(argv[i]);
else {
i = 0;
while (i < n_search_dirs) {
if (access(search_dirs[i], R_OK) < 0)
remove_search_dir(search_dirs[i]);
else
i++;
}
}
} else
for (i = optind; i < argc; i++)
add_search_dir(argv[i]);
for (i = 0; i < n_search_dirs; i++) {
char *cp = concat(dir_list, *dir_list?":":"", search_dirs[i]);
free(dir_list);
dir_list = cp;
rval |= dodir(search_dirs[i], 0);
}
rval |= buildhints();
return rval;
}
int
dodir(char *dir, int silent)
{
DIR *dd;
struct dirent *dp;
char name[PATH_MAX];
int dewey[MAXDEWEY], ndewey;
if ((dd = opendir(dir)) == NULL) {
if (!silent || errno != ENOENT)
warn("%s", dir);
return -1;
}
while ((dp = readdir(dd)) != NULL) {
size_t n;
char *cp;
if (dp->d_name[0] != 'l' ||
dp->d_name[1] != 'i' ||
dp->d_name[2] != 'b')
continue;
(void)strlcpy(name, dp->d_name + 3, sizeof name);
n = strlen(name);
if (n < 4)
continue;
for (cp = name + n - 4; cp > name; --cp) {
if (cp[0] == '.' &&
cp[1] == 's' &&
cp[2] == 'o' &&
cp[3] == '.')
break;
}
if (cp <= name)
continue;
*cp = '\0';
bzero((caddr_t)dewey, sizeof(dewey));
ndewey = getdewey(dewey, cp + 3);
if (ndewey > 0)
enter(dir, dp->d_name, name, dewey, ndewey);
}
closedir(dd);
return 0;
}
static void
enter(char *dir, char *file, char *name, int dewey[], int ndewey)
{
struct shlib_list *shp;
for (shp = shlib_head; shp; shp = shp->next) {
if (strcmp(name, shp->name) != 0 || major != shp->major)
continue;
if (cmpndewey(dewey, ndewey, shp->dewey, shp->ndewey) > 0) {
if (verbose)
printf("Updating lib%s.%d.%d to %s/%s\n",
shp->name, shp->major, shp->minor,
dir, file);
free(shp->name);
shp->name = xstrdup(name);
free(shp->path);
shp->path = concat(dir, "/", file);
bcopy(dewey, shp->dewey, sizeof(shp->dewey));
shp->ndewey = ndewey;
}
break;
}
if (shp)
return;
if (verbose)
printf("Adding %s/%s\n", dir, file);
shp = (struct shlib_list *)xmalloc(sizeof *shp);
shp->name = xstrdup(name);
shp->path = concat(dir, "/", file);
bcopy(dewey, shp->dewey, sizeof(shp->dewey));
shp->ndewey = ndewey;
shp->next = NULL;
*shlib_tail = shp;
shlib_tail = &shp->next;
}
#if DEBUG
#undef _PATH_LD_HINTS
#define _PATH_LD_HINTS "./ld.so.hints"
#endif
static int
hinthash(char *cp, int vmajor, int vminor)
{
int k = 0;
while (*cp)
k = (((k << 1) + (k >> 14)) ^ (*cp++)) & 0x3fff;
k = (((k << 1) + (k >> 14)) ^ (vmajor*257)) & 0x3fff;
#if 0
k = (((k << 1) + (k >> 14)) ^ (vminor*167)) & 0x3fff;
#endif
return k;
}
int
buildhints(void)
{
int strtab_sz = 0, nhints = 0, fd = -1, i, ret = -1, str_index = 0;
struct hints_bucket *blist;
struct hints_header hdr;
struct shlib_list *shp;
char *strtab, *tmpfilenam;
size_t n;
for (shp = shlib_head; shp; shp = shp->next) {
strtab_sz += 1 + strlen(shp->name);
strtab_sz += 1 + strlen(shp->path);
nhints++;
}
hdr.hh_magic = HH_MAGIC;
hdr.hh_version = LD_HINTS_VERSION_2;
hdr.hh_nbucket = 1 * nhints;
n = hdr.hh_nbucket * sizeof(struct hints_bucket);
hdr.hh_hashtab = sizeof(struct hints_header);
hdr.hh_strtab = hdr.hh_hashtab + n;
hdr.hh_dirlist = strtab_sz;
strtab_sz += 1 + strlen(dir_list);
hdr.hh_strtab_sz = strtab_sz;
hdr.hh_ehints = hdr.hh_strtab + hdr.hh_strtab_sz;
if (verbose)
printf("Totals: entries %d, buckets %ld, string size %d\n",
nhints, hdr.hh_nbucket, strtab_sz);
blist = (struct hints_bucket *)xmalloc(n);
bzero(blist, n);
for (i = 0; i < hdr.hh_nbucket; i++)
blist[i].hi_next = -1;
strtab = xmalloc(strtab_sz);
for (shp = shlib_head; shp; shp = shp->next) {
struct hints_bucket *bp;
bp = blist + (hinthash(shp->name, shp->major, shp->minor) %
hdr.hh_nbucket);
if (bp->hi_pathx) {
int j;
for (j = 0; j < hdr.hh_nbucket; j++) {
if (blist[j].hi_pathx == 0)
break;
}
if (j == hdr.hh_nbucket) {
warnx("Bummer!");
goto out;
}
while (bp->hi_next != -1)
bp = &blist[bp->hi_next];
bp->hi_next = j;
bp = blist + j;
}
bp->hi_namex = str_index;
strlcpy(strtab + str_index, shp->name, strtab_sz - str_index);
str_index += 1 + strlen(shp->name);
bp->hi_pathx = str_index;
strlcpy(strtab + str_index, shp->path, strtab_sz - str_index);
str_index += 1 + strlen(shp->path);
bcopy(shp->dewey, bp->hi_dewey, sizeof(bp->hi_dewey));
bp->hi_ndewey = shp->ndewey;
}
strlcpy(strtab + str_index, dir_list, strtab_sz - str_index);
str_index += 1 + strlen(dir_list);
if (str_index != strtab_sz)
errx(1, "str_index(%d) != strtab_sz(%d)", str_index, strtab_sz);
tmpfilenam = concat(_PATH_LD_HINTS, ".XXXXXXXXXX", "");
if ((fd = mkstemp(tmpfilenam)) == -1) {
warn("%s", tmpfilenam);
goto out;
}
if (fchmod(fd, 0444) == -1) {
warn("%s: failed to change mode", tmpfilenam);
goto out;
}
if (write(fd, &hdr, sizeof(struct hints_header)) !=
sizeof(struct hints_header)) {
warn("%s", _PATH_LD_HINTS);
goto out;
}
if (write(fd, blist, hdr.hh_nbucket * sizeof(struct hints_bucket)) !=
hdr.hh_nbucket * sizeof(struct hints_bucket)) {
warn("%s", _PATH_LD_HINTS);
goto out;
}
if (write(fd, strtab, strtab_sz) != strtab_sz) {
warn("%s", _PATH_LD_HINTS);
goto out;
}
if (rename(tmpfilenam, _PATH_LD_HINTS) != 0) {
warn("%s", _PATH_LD_HINTS);
goto out;
}
ret = 0;
out:
if (fd != -1)
close(fd);
free(blist);
free(strtab);
return (ret);
}
static int
readhints(void)
{
struct stat sb;
struct hints_bucket *blist;
struct hints_header *hdr;
struct shlib_list *shp;
caddr_t addr;
char *strtab;
long msize;
int fd, i;
if ((fd = open(_PATH_LD_HINTS, O_RDONLY)) == -1) {
warn("%s", _PATH_LD_HINTS);
return -1;
}
if (fstat(fd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX) {
warn("%s", _PATH_LD_HINTS);
return -1;
}
msize = (long)sb.st_size;
addr = mmap(0, msize, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) {
warn("%s", _PATH_LD_HINTS);
return -1;
}
hdr = (struct hints_header *)addr;
if (HH_BADMAG(*hdr)) {
warnx("%s: Bad magic: %lo",
_PATH_LD_HINTS, hdr->hh_magic);
return -1;
}
if (hdr->hh_ehints > msize) {
warnx("%s: hintsize greater than filesize: 0x%lx > 0x%lx ",
_PATH_LD_HINTS, hdr->hh_ehints, msize);
return -1;
}
if (hdr->hh_version != LD_HINTS_VERSION_2) {
warnx("Unsupported version: %ld", hdr->hh_version);
return -1;
}
close(fd);
blist = (struct hints_bucket *)(addr + hdr->hh_hashtab);
strtab = (char *)(addr + hdr->hh_strtab);
dir_list = xstrdup(strtab + hdr->hh_dirlist);
if (rescan)
return (0);
for (i = 0; i < hdr->hh_nbucket; i++) {
struct hints_bucket *bp = &blist[i];
if (bp->hi_namex >= hdr->hh_strtab_sz) {
warnx("Bad name index: %#x", bp->hi_namex);
return -1;
}
if (bp->hi_pathx >= hdr->hh_strtab_sz) {
warnx("Bad path index: %#x", bp->hi_pathx);
return -1;
}
shp = (struct shlib_list *)xmalloc(sizeof *shp);
shp->name = xstrdup(strtab + bp->hi_namex);
shp->path = xstrdup(strtab + bp->hi_pathx);
bcopy(bp->hi_dewey, shp->dewey, sizeof(shp->dewey));
shp->ndewey = bp->hi_ndewey;
shp->next = NULL;
*shlib_tail = shp;
shlib_tail = &shp->next;
}
return 0;
}
static void
listhints(void)
{
struct shlib_list *shp;
int i;
printf("%s:\n", _PATH_LD_HINTS);
printf("\tsearch directories: %s\n", dir_list);
for (i = 0, shp = shlib_head; shp; i++, shp = shp->next)
printf("\t%d:-l%s.%d.%d => %s\n",
i, shp->name, shp->major, shp->minor, shp->path);
}