#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <machine/exec.h>
#include <limits.h>
#include <stdio.h>
#include <fcntl.h>
#include <nlist.h>
#include <link.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#if 0
#include "syscall.h"
#include "archdep.h"
#include "util.h"
#endif
#include "path.h"
#include "sod.h"
int _dl_hinthash(char *cp, int vmajor, int vminor);
void _dl_maphints(void);
void
_dl_build_sod(const char *name, struct sod *sodp)
{
unsigned int tuplet;
int major, minor;
char *realname, *tok, *etok, *cp;
sodp->sod_name = (long)strdup(name);
if (sodp->sod_name == 0)
exit(7);
sodp->sod_library = 0;
sodp->sod_major = sodp->sod_minor = 0;
if (strncmp((char *)sodp->sod_name, "lib", 3) != 0)
goto backout;
if (strchr((char *)sodp->sod_name, '/'))
goto backout;
cp = (char *)sodp->sod_name + 3;
realname = cp;
if ((strchr(cp, '.') == NULL) || (*(cp+strlen(cp)-1) == '.'))
goto backout;
cp = strstr(cp, ".so");
if (cp == NULL)
goto backout;
major = minor = -1;
for (tuplet = 0; (tok = strsep(&cp, ".")) != NULL; tuplet++) {
switch (tuplet) {
case 0:
break;
case 1:
break;
case 2:
major = strtol(tok, &etok, 10);
if (*tok == '\0' || *etok != '\0')
goto backout;
break;
case 3:
minor = strtol(tok, &etok, 10);
if (*tok == '\0' || *etok != '\0')
goto backout;
break;
default:
goto backout;
}
}
if (realname == NULL)
goto backout;
cp = (char *)sodp->sod_name;
sodp->sod_name = (long)strdup(realname);
if (sodp->sod_name == 0)
exit(7);
free(cp);
sodp->sod_library = 1;
sodp->sod_major = major;
sodp->sod_minor = minor;
return;
backout:
free((char *)sodp->sod_name);
sodp->sod_name = (long)strdup(name);
if (sodp->sod_name == 0)
exit(7);
}
static struct hints_header *hheader = NULL;
static struct hints_bucket *hbuckets;
static char *hstrtab;
char **_dl_hint_search_path = NULL;
#define HINTS_VALID (hheader != NULL && hheader != (struct hints_header *)-1)
void
_dl_maphints(void)
{
struct stat sb;
caddr_t addr = MAP_FAILED;
long hsize = 0;
int hfd;
if ((hfd = open(_PATH_LD_HINTS, O_RDONLY)) < 0)
goto bad_hints;
if (fstat(hfd, &sb) != 0 || !S_ISREG(sb.st_mode) ||
sb.st_size < sizeof(struct hints_header) || sb.st_size > LONG_MAX)
goto bad_hints;
hsize = (long)sb.st_size;
addr = (void *)mmap(0, hsize, PROT_READ, MAP_PRIVATE, hfd, 0);
if (addr == MAP_FAILED)
goto bad_hints;
hheader = (struct hints_header *)addr;
if (HH_BADMAG(*hheader) || hheader->hh_ehints > hsize)
goto bad_hints;
if (hheader->hh_version != LD_HINTS_VERSION_2)
goto bad_hints;
hbuckets = (struct hints_bucket *)(addr + hheader->hh_hashtab);
hstrtab = (char *)(addr + hheader->hh_strtab);
if (hheader->hh_version >= LD_HINTS_VERSION_2)
_dl_hint_search_path = _dl_split_path(hstrtab + hheader->hh_dirlist);
close(hfd);
return;
bad_hints:
if (addr != MAP_FAILED)
munmap(addr, hsize);
if (hfd != -1)
close(hfd);
hheader = (struct hints_header *)-1;
}
char *
_dl_findhint(char *name, int major, int minor, char *preferred_path)
{
struct hints_bucket *bp;
if (hheader == NULL)
_dl_maphints();
if (!(HINTS_VALID))
return NULL;
if (hheader->hh_nbucket == 0)
return NULL;
bp = hbuckets + (_dl_hinthash(name, major, minor) % hheader->hh_nbucket);
while (1) {
if (bp->hi_namex >= hheader->hh_strtab_sz) {
printf("Bad name index: %#x\n", bp->hi_namex);
exit(7);
break;
}
if (bp->hi_pathx >= hheader->hh_strtab_sz) {
printf("Bad path index: %#x\n", bp->hi_pathx);
exit(7);
break;
}
if (strcmp(name, hstrtab + bp->hi_namex) == 0) {
if (bp->hi_major == major &&
(bp->hi_ndewey < 2 || bp->hi_minor >= minor)) {
if (preferred_path == NULL) {
return hstrtab + bp->hi_pathx;
} else {
char *path = hstrtab + bp->hi_pathx;
char *edir = strrchr(path, '/');
if ((strncmp(preferred_path, path,
(edir - path)) == 0) &&
(preferred_path[edir - path] == '\0'))
return path;
}
}
}
if (bp->hi_next == -1)
break;
bp = &hbuckets[bp->hi_next];
}
return NULL;
}
int
_dl_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;
return k;
}