Files
Arquivotheca.SunOS-4.1.4/lang/rtld/cache_common.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

422 lines
11 KiB
C

/* @(#)cache_common.c 1.1 94/10/31 SMI */
/*
* ld.so directory caching: common code
*/
/*
* Copyright (c) 1989 by Sun Microsystems, Inc.
*/
#include <sys/param.h>
#include <sys/file.h>
#include <sys/dirent.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/exec.h>
#include "link.h"
#include "cache.h"
#include <string.h>
extern char *getenv();
extern int stol();
extern int rest_ok();
static struct link_object *get_lo();
static struct dbd *new_dbd();
static struct db *find_so();
static void fix_lo();
static void get_cache_file();
#define LIB "lib" /* library name prefix string */
#define SO ".so." /* extension for shared object */
#define LIBLEN (sizeof (LIB) - 1) /* lengths of same */
#define SOLEN (sizeof (SO) - 1)
caddr_t db_base; /* base address from mmap() */
static struct dbd *dbd_head = NULL; /* head of data bases */
/*
* Given a directory name - give back a data base. The data base may have
* orginated from the mmapped file or temporarily created
*/
struct db *
lo_cache(ds)
char *ds; /* directory to get cache for */
{
struct db *dbp; /* database pointer */
struct dbd *dbdp; /* working database descriptor */
struct dbd **dbdpp; /* insertion pointer */
static int once = 1; /* once-only flag */
if (once) {
get_cache_file();
once = 0;
}
dbdpp = &dbd_head;
for (dbdp = dbd_head; dbdp; dbdp = dbdp->dbd_next) {
if (!strcmp(ds, &AP(dbdp->dbd_db)[dbdp->dbd_db->db_name]))
return (dbdp->dbd_db);
dbdpp = &dbdp->dbd_next;
}
if (dbp = find_so(ds)){
(void) new_dbd(dbdpp, dbp);
}
return (dbp);
}
/*
* Delete from the db list those dbs that came from the mmapped file
*/
void
dbd_flush()
{
struct dbd *dbdp; /* working dbd ptr. */
for (dbdp = dbd_head; dbdp; dbdp = dbdp->dbd_next) {
if (dbdp->dbd_db >= (struct db *)db_base) {
if (dbdp == dbd_head)
dbd_head = dbdp->dbd_next;
else if (dbdp->dbd_db >= (struct db *)db_base)
dbdp = dbdp->dbd_next;
}
if (dbdp->dbd_next == NULL)
break;
}
};
/*
* Get the cache file, if appropriate.
*/
static void
get_cache_file()
{
int fd; /* descriptor on cache file */
struct stat sb; /* used to find size */
struct dbf *dbf_base; /* working cache file pointer */
struct db *dbp; /* working data base pointer */
struct dbd *dbdp; /* working dbd */
struct dbd **dbdpp; /* insertion point for dbd list */
/*
* If cache use is suppressed, simply skip all this.
*/
if (!use_cache)
return;
/*
* Open, map in the file. Failures occur silently. (XXX)
*/
if ((fd = open(CACHE_FILE, O_RDONLY)) == -1)
return;
if (fstat(fd, &sb) == -1) {
(void) close(fd);
return;
}
dbf_base = (struct dbf *)mmap(0, sb.st_size, PROT_READ, MAP_SHARED,
fd, 0);
(void) close(fd);
if ((dbf_base == (struct dbf *)-1) ||
(dbf_base->dbf_magic != LD_CACHE_MAGIC))
return;
if ((dbf_base->dbf_version != LD_CACHE_VERSION) ||
#if TARGET==SUN2
(dbf_base->dbf_machtype != M_68010))
#endif
#if TARGET==SUN3
(dbf_base->dbf_machtype != M_68010) &&
(dbf_base->dbf_machtype != M_68020))
#endif
#if TARGET==SUN4
(dbf_base->dbf_machtype != M_SPARC))
#endif
return;
db_base = &AP(dbf_base)[(int)dbf_base->dbf_db];
/*
* For each data base in the file, build a dbd and link it
* on to the master list.
*/
for (dbdpp = &dbd_head, dbp = (struct db *)db_base;
(dbp < (struct db *)(db_base + sb.st_size)) && (dbp->db_chain != 0);
(char *)dbp += (int)dbp->db_chain) {
dbdp = new_dbd(dbdpp, dbp);
dbdpp = &dbdp->dbd_next;
}
}
/*
* Build a database for the directory "ds".
*/
static struct db *
find_so(ds)
char *ds; /* directory to search */
{
int fd; /* descriptor on directory */
int n; /* bytes from getdents */
char *cp; /* working char * */
struct stat sb; /* buffer for stat'ing directory */
struct db *dbp; /* database */
static caddr_t buf = NULL; /* buffer for doing getdents */
static long bs; /* cached blocksize for getdents */
struct link_object *tlop; /* working link object ptr. */
struct dirent *dp; /* directory entry ptr. */
struct dbe *ep; /* working db_entry ptr. */
char *mnp; /* where minor version begins */
char *mjp; /* where major version begins */
int m; /* the major number */
int to_min; /* index into string of minor */
int cplen; /* length of X */
int index; /* the hash value */
/*
* Try to open directory. Failing that, just return silently.
*/
if ((fd = open(ds, O_RDONLY)) == -1)
return ((struct db *)NULL);
/*
* If we have not yet gotten a buffer for reading directories,
* allocate it now. Size it according to the most efficient size
* for the first directory we open successfully.
*/
if (!buf) {
if (fstat(fd, &sb) == -1) {
(void) close(fd);
return ((struct db *)NULL);
}
buf = (*heap_malloc)(bs = sb.st_blksize);
}
/*
* Have a directory, have a buffer. Allocate up a database
* and initialize it.
*/
dbp = (struct db *)(*db_malloc)(sizeof (struct db));
dbp->db_name = RELPTR(dbp, (*db_malloc)(strlen(ds) + 1));
(void) strcpy((char *)&AP(dbp)[dbp->db_name], ds);
/*
* Scan the directory looking for shared libraries. getdents()
* failures are silently ignored and terminate the scan.
*/
while ((n = getdents(fd, buf, bs)) > 0)
for (dp = (struct dirent *)buf;
dp && (dp < (struct dirent *)(buf + n));
dp = (struct dirent *)((dp->d_reclen == 0) ?
NULL : (char *)dp + dp->d_reclen)) {
/*
* If file starts with a "lib", then extract the X
* from libX.
*/
cp = dp->d_name;
if ((cplen = extract_name(&cp)) == -1)
continue;
/*
* Is the next component ".so."?
*/
if (strncmp(SO, cp + cplen, SOLEN))
continue;
/*
* Check if next component is the major number and
* whether following components are legal.
*/
mnp = mjp = (dp->d_name + LIBLEN + cplen + SOLEN);
if (!(stol(mjp, '.', &mnp, &m) && rest_ok(mnp + 1)))
continue;
to_min = mnp - dp->d_name + 1;
/*
* Have libX.so.major.minor - attempt to add it to the
* cache. If there is another with the same major
* number then the chose the object with the highest
* minor number
*/
index = hash(cp, cplen, m);
ep = &(dbp->db_hash[index]);
if (ep->dbe_lop == NULL) {
ep->dbe_lop = (long)get_lo(dbp, cp,
cplen, m, to_min);
tlop = (struct link_object *)
&AP(dbp)[ep->dbe_lop];
strcpy(&AP(dbp)[tlop->lo_next], dp->d_name);
continue;
}
for(ep = &(dbp->db_hash[index]); ep;
ep = (struct dbe *) &AP(dbp)[ep->dbe_next]) {
tlop = (struct link_object *)
&AP(dbp)[ep->dbe_lop];
/*
* Choose the highest minor version
*/
if ((tlop->lo_major == m) &&
(!strncmp(&AP(dbp)[tlop->lo_name],
cp, cplen)) &&
(*(&AP(dbp)[tlop->lo_name + cplen]) ==
'\0')) {
if (verscmp(dp->d_name + to_min,
(char *)(&AP(dbp)[tlop->lo_next] + to_min))
> 0)
strcpy(&AP(dbp)[tlop->lo_next],
dp->d_name);
break;
}
if (ep->dbe_next == NULL) {
ep->dbe_next = RELPTR(dbp,
(*db_malloc)(sizeof(struct dbe)));
ep = (struct dbe *)
&AP(dbp)[ep->dbe_next];
ep->dbe_lop = (long)get_lo(dbp,
cp, cplen, m, to_min);
tlop = (struct link_object *)
&AP(dbp)[ep->dbe_lop];
strcpy(&AP(dbp)[tlop->lo_next], dp->d_name);
break;
}
}
}
fix_lo(dbp);
(void) close(fd);
return (dbp);
}
/*
* Allocate and fill in the fields for a link_object
*/
static struct link_object *
get_lo(dbp, cp, cplen, m, n)
struct db *dbp; /* data base */
char *cp; /* ptr. to X of libX */
int cplen; /* length of X */
int m; /* major version */
int n; /* index to minor version */
{
struct link_object *lop; /* link_object to be returned */
struct link_object *tlop; /* working copy of the above */
/*
* Allocate a link object prototype in the database heap.
* Store the numeric major (interface) number, but the minor
* number is stored in the database as an index to the string
* representing the minor version. By keeping the minor version
* as a string, "subfields" (i.e., major.minor[.other.fields. etc.])
* are permitted. Although not meaningful to the link editor, this
* permits run-time substitution of arbitrary customer revisions,
* although introducing the confusion of overloading the lo_minor
* field in the database (!)
*/
lop = (struct link_object *)RELPTR(dbp,
(*db_malloc)(sizeof(struct link_object)));
tlop = (struct link_object *)&AP(dbp)[(long)lop];
tlop->lo_major = m;
tlop->lo_minor = n;
/*
* Allocate space for the complete path name on the host program's
* heap -- as we have to save it from the directory buffer which
* might otherwise get re-used on us. Note that this space
* is wasted -- we can not assume that it can be reclaimed.
*/
tlop->lo_next = (long)RELPTR(dbp, (*heap_malloc)(MAXNAMLEN));
/*
* Store the prototype name in the link object in the database.
*/
tlop->lo_name = (long)RELPTR(dbp, (*db_malloc)(cplen + 1));
strncpy((char *)&AP(dbp)[tlop->lo_name], cp, cplen);
return(lop);
}
/*
* Pull the "X" from libX, set name to X and return the
* length of X
*/
static int
extract_name(name)
char **name;
{
char *ls; /* string after LIB root */
char *dp; /* string before first delimiter */
if (strncmp(*name, LIB, LIBLEN) == 0) {
ls = *name + LIBLEN;
if ((dp = (char *)index(ls, '.')) != (char *)0) {
*name = ls;
return (dp - ls);
}
}
return (-1);
}
/*
* Make a pass through the data base to set the dbe_name of a dbe. This
* is necessary because there may be several revisions of a library
* but only one will be chosen.
*/
static void
fix_lo(dbp)
struct db *dbp;
{
int i; /* loop temporary */
int dirlen = strlen(&AP(dbp)[dbp->db_name]);
/* length of directory pathname */
char *cp; /* working temporary */
char *tp; /* working temporary */
struct dbe *ep; /* working copy of dbe */
struct link_object *lop; /* working copy of link_object */
for(i = 0; i < DB_HASH; i++) {
for (ep = &(dbp->db_hash[i]); ep && ep->dbe_lop;
(ep = ep->dbe_next == 0 ? NULL :
(struct dbe *)&AP(dbp)[ep->dbe_next])) {
lop = (struct link_object *)&AP(dbp)[ep->dbe_lop];
tp = &AP(dbp)[lop->lo_next];
ep->dbe_name = RELPTR(dbp,
(*db_malloc)(dirlen + strlen(tp) + 2));
lop->lo_minor += dirlen + 1;
cp = strncpy(&AP(dbp)[ep->dbe_name],
&AP(dbp)[dbp->db_name], dirlen);
cp = strncpy(cp + dirlen, "/", 1);
(void) strcpy(cp + 1, tp);
}
}
}
/*
* Allocate a new dbd, append it after dbdpp and set the dbd_dbp to dbp.
*/
static struct dbd *
new_dbd(dbdpp, dbp)
struct dbd **dbdpp; /* insertion point */
struct db *dbp; /* db associated with this dbd */
{
struct dbd *dbdp; /* working dbd ptr. */
dbdp = (struct dbd *)(*heap_malloc)(sizeof(struct dbd));
dbdp->dbd_db = dbp;
dbdp->dbd_next = NULL;
*dbdpp = dbdp;
return (dbdp);
}
/*
* Calculate hash index for link object.
* This is based on X.major from libX.so.major.minor.
*/
hash(np, nchrs, m)
char *np; /* X of libX */
int nchrs; /* no of chrs. to hash on */
int m; /* the major version */
{
int h; /* for loop counter */
char *cp; /* working (char *) ptr */
for (h = 0, cp = np; h < nchrs; h++,*cp++)
h = (h << 1) + *cp;
h += (h << 1) + m;
h = ((h & 0x7fffffff) % DB_HASH);
return (h);
}