#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <limits.h>
#include <fcntl.h>
#include <link.h>
#include <string.h>
#include "syscall.h"
#include "path.h"
#include "util.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)_dl_strdup(name);
if (sodp->sod_name == 0)
_dl_oom();
sodp->sod_library = 0;
sodp->sod_major = sodp->sod_minor = 0;
if (_dl_strncmp((char *)sodp->sod_name, "lib", 3) != 0)
goto backout;
if (_dl_strchr((char *)sodp->sod_name, '/'))
goto backout;
cp = (char *)sodp->sod_name + 3;
realname = cp;
if ((_dl_strchr(cp, '.') == NULL) || (*(cp+_dl_strlen(cp)-1) == '.'))
goto backout;
cp = _dl_strstr(cp, ".so");
if (cp == NULL)
goto backout;
major = minor = -1;
for (tuplet = 0; (tok = _dl_strsep(&cp, ".")) != NULL; tuplet++) {
switch (tuplet) {
case 0:
break;
case 1:
break;
case 2:
major = _dl_strtol(tok, &etok, 10);
if (*tok == '\0' || *etok != '\0')
goto backout;
break;
case 3:
minor = _dl_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)_dl_strdup(realname);
if (sodp->sod_name == 0)
_dl_oom();
_dl_free(cp);
sodp->sod_library = 1;
sodp->sod_major = major;
sodp->sod_minor = minor;
return;
backout:
_dl_free((char *)sodp->sod_name);
sodp->sod_name = (long)_dl_strdup(name);
if (sodp->sod_name == 0)
_dl_oom();
}
void
_dl_set_sod(const char *path, struct sod *sod)
{
char *fname = _dl_strrchr(path, '/');
if (fname != NULL)
_dl_build_sod(++fname, sod);
else
_dl_build_sod(path, sod);
}
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 = _dl_open(_PATH_LD_HINTS, O_RDONLY | O_CLOEXEC)) < 0) {
hfd = -1;
goto bad_hints;
}
if (_dl_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 *)_dl_mmap(0, hsize, PROT_READ, MAP_PRIVATE, hfd, 0);
if (_dl_mmap_error(addr))
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);
_dl_mimmutable(addr, hsize);
_dl_close(hfd);
return;
bad_hints:
if (!_dl_mmap_error(addr))
_dl_munmap(addr, hsize);
if (hfd != -1)
_dl_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)
_dl_die("bad name index: %#x", bp->hi_namex);
if (bp->hi_pathx >= hheader->hh_strtab_sz)
_dl_die("bad path index: %#x", bp->hi_pathx);
if (_dl_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 = _dl_strrchr(path, '/');
if ((_dl_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;
}