2138 lines
62 KiB
C
2138 lines
62 KiB
C
static char sccsid[] = "@(#)29 1.63.1.3 src/bos/kernel/ldr/ld_libld.c, sysldr, bos41J, 9520A_all 5/16/95 16:46:23";
|
|
/*
|
|
* COMPONENT_NAME: (SYSLDR) Program Management
|
|
*
|
|
* FUNCTIONS: ld_findfp(), ld_libgettext(),
|
|
* ld_provinstance(), ld_kernelld(), ld_prinld(), sortd(),
|
|
* extendd(), ld_allocd(), ld_getmap(), ld_assigndata(), ld_libld(),
|
|
* ld_checkdp(), ld_findle(), ld_getinstance(),
|
|
* ld_instance(), ld_getlib(), ld_libraries(), ld_undothis(),
|
|
* ld_getlibread(), ld_preread(), ld_findtextle(), ld_privgettext()
|
|
*
|
|
* ORIGINS: 27
|
|
*
|
|
* IBM CONFIDENTIAL -- (IBM Confidential Restricted when
|
|
* combined with the aggregated modules for this product)
|
|
* SOURCE MATERIALS
|
|
* (C) COPYRIGHT International Business Machines Corp. 1989, 1995
|
|
* All Rights Reserved
|
|
*
|
|
* US Government Users Restricted Rights - Use, duplication or
|
|
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/fp_io.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ldr.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/pseg.h>
|
|
#include <sys/seg.h>
|
|
#include <sys/syspest.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/vmuser.h>
|
|
#include <sys/xcoff.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/lockl.h>
|
|
#include <sys/sleep.h>
|
|
#include <sys/vfs.h>
|
|
#include "ld_data.h"
|
|
|
|
/* predeclare - mutual recursion requires this */
|
|
struct loader_entry *
|
|
ld_findle(
|
|
struct sharedstuff *sspointer,
|
|
struct file *fp,
|
|
char *member);
|
|
|
|
static struct loader_entry *
|
|
ld_instance(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry *lible);
|
|
|
|
void
|
|
prune_libpath(char *orig_libpath, char *new_libpath);
|
|
|
|
static void format_error(
|
|
char *filename,
|
|
char *member)
|
|
{
|
|
char fbuf[256];
|
|
strncpy(fbuf,filename,128);
|
|
if (member && *member){
|
|
strncat(fbuf,"[",2);
|
|
strncat(fbuf,member,(256-129));
|
|
strncat(fbuf,"]",2);
|
|
}
|
|
ld_emess(L_ERROR_FORMAT,fbuf,NULL);
|
|
}
|
|
|
|
/* searches the load chain starting at le for an entry
|
|
* which matches the file/member specification.
|
|
* unloaded entries are ignored
|
|
*/
|
|
struct loader_entry *
|
|
ld_findfp(
|
|
struct loader_entry *le,
|
|
struct file *fp,
|
|
char *member)
|
|
{
|
|
uint fh;
|
|
fh=FPtoFH(fp);
|
|
|
|
for(;le;le=le->le_next){
|
|
if (le->le_flags & LE_UNLOAD) continue;
|
|
if (FPtoFH(le->le_fp) != fh) continue;
|
|
if (0==strcmp(le->le_filename,member)) return le;
|
|
/* special test for execed program whose filename is
|
|
* unfortunately represented differently. It never has
|
|
* a member name */
|
|
if ( (le->le_flags & LE_EXECLE) &&
|
|
(!member || ! (*member))) return le;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct loader_entry *
|
|
ld_findtextle(
|
|
struct loader_entry *le,
|
|
struct file *fp,
|
|
char *member)
|
|
{
|
|
while(le=ld_findfp(le,fp,member)){
|
|
/* want an entry which just represents the text
|
|
* (findfp skips unloaded entries).
|
|
*/
|
|
if((le->le_flags & (LE_DATA|LE_TEXT))==LE_TEXT) return le;
|
|
le = le->le_next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* add a module to the shared library segment. this module may then
|
|
* be prerelocated, OR it may simply serve as the source for privinstance, OR
|
|
* both. modules are only put in the shared library if they are
|
|
* read other.
|
|
*/
|
|
|
|
static struct loader_entry *
|
|
ld_libgettext(
|
|
struct sharedstuff *sspointer,
|
|
struct file *fp,
|
|
char *member)
|
|
{
|
|
/*N.B. see ldr.h for use of lexpointer*/
|
|
struct loader_entry_extension loader_entry_extension,*lexpointer;
|
|
int rc=0; /*return code */
|
|
int i,j,k;
|
|
struct loader_entry *lible,*le,*prele;
|
|
char *filename;
|
|
uint size,membersize;
|
|
uint flag = 0;
|
|
|
|
/* first see if this is already available */
|
|
do {
|
|
if ((le = ld_findtextle(lib.la.la_loadlist,fp,member))
|
|
&& !(le->le_flags & LE_IOPENDING))
|
|
return le;
|
|
if (le)
|
|
e_sleepl(&kernel_anchor.la_lock, &ld_loader_read,
|
|
EVENT_SHORT);
|
|
} while(le);
|
|
|
|
ASSERT(ss.pre);
|
|
|
|
|
|
lexpointer=&loader_entry_extension;
|
|
bzero(lexpointer,sizeof *lexpointer);
|
|
/* this loader entry represents the library - but not a resolved
|
|
* instance of the library - thus it has no dependencies
|
|
*/
|
|
filename = ld_fptopath(sspointer,fp);
|
|
ASSERT(filename);
|
|
if (!filename) filename = "";
|
|
membersize = strlen(member);
|
|
size = membersize+1+strlen(filename)+1+sizeof(struct loader_entry);
|
|
lible = xmalloc(size,0,lib.la.la_data_heap);
|
|
if (!lible){
|
|
return NULL;
|
|
}
|
|
lible->le_usecount=1; /* counted down after lock is reaquired */
|
|
lible->le_loadcount = 0;
|
|
lible->le_flags=LE_TEXT|LE_LIBRARY|LE_IOPENDING;
|
|
lible->le_fp=fp;
|
|
lible->le_exports=NULL;
|
|
lible->le_lex = NULL;
|
|
lible->le_defered = NULL;
|
|
lible->le_filename = (char *)lible + sizeof(struct loader_entry);
|
|
strcpy(lible->le_filename,member);
|
|
strcpy(lible->le_filename+membersize+1,filename);
|
|
lible->le_maxdepend=1;
|
|
lible->le_ndepend=0;
|
|
/* Note which thread is performing the I/O */
|
|
lible->le_tid = curthread->t_tid;
|
|
/* add it to the library load list*/
|
|
lible->le_next = lib.la.la_loadlist;
|
|
lib.la.la_loadlist = lible;
|
|
unlockl(&kernel_anchor.la_lock);
|
|
/* check for read/execute other permission -
|
|
* without that we can't put this into the library
|
|
*/
|
|
|
|
{
|
|
struct stat stat;
|
|
if (rc = FP_FSTAT(fp,&stat,sizeof stat,FP_SYS))
|
|
goto backout_le;
|
|
if ((stat.st_mode & S_IROTH) != S_IROTH)
|
|
goto backout_le;
|
|
|
|
/* Also determine of the file is in a local filesystem.
|
|
* If it is local, ld_textread will attempt to map it.
|
|
*/
|
|
if (!(stat.st_flag & FS_REMOTE))
|
|
flag = LD_localfile;
|
|
}
|
|
|
|
|
|
/* textread will read the text in - it has to compute the
|
|
* length and then read the right amount
|
|
* a side effect is to set lex.h to the origin
|
|
* The second and third parameters are for execload, which
|
|
* always reads the program into a predetermined place
|
|
*/
|
|
|
|
u.u_error=ld_textread(sspointer,lexpointer,fp,member,NULL,0,
|
|
lib.la.la_text_heap,flag);
|
|
|
|
if (u.u_error)
|
|
goto backout_le;
|
|
|
|
/* remember that sanity initializes a number of values in lex*/
|
|
if (u.u_error=ld_sanity(sspointer, lexpointer))
|
|
goto backout_le;
|
|
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);/*this form does not fail*/
|
|
fp_hold(fp);
|
|
ASSERT(lible->le_usecount == 1);
|
|
lible->le_usecount = 0;
|
|
lible->le_file =(void *) lex.h;
|
|
lible->le_filesize = lex.filesize;
|
|
lible->le_data = lible->le_file + hdr.s[lex.data].s_scnptr;
|
|
lible->le_datasize = hdr.a.o_dsize; /* N.B. don't include BSS */
|
|
lible->le_flags &= ~LE_IOPENDING;
|
|
if (hdr.f.f_flags & F_SHROBJ && strncmp(hdr.a.o_modtype,"RO",2) == 0
|
|
&& hdr.a.o_bsize == 0)
|
|
lible->le_flags |= LE_SRO;
|
|
if (strncmp(hdr.a.o_modtype,"PL",2) == 0)
|
|
lible->le_flags |= LE_PRIVATELOAD;
|
|
if (lex.flags & TEXTMAPPED)
|
|
lible->le_flags |= LE_TEXTMAPPED;
|
|
e_wakeup(&ld_loader_read);
|
|
return lible;
|
|
|
|
backout_le:
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);/*this form does not fail*/
|
|
le = lib.la.la_loadlist;
|
|
prele = NULL;
|
|
while(le!= lible){
|
|
prele = le;
|
|
le = le->le_next;
|
|
}
|
|
if (prele)
|
|
prele->le_next =le->le_next;
|
|
else
|
|
lib.la.la_loadlist = le->le_next;
|
|
|
|
xmfree(le,lib.la.la_data_heap);
|
|
e_wakeup(&ld_loader_read);
|
|
return NULL;
|
|
}
|
|
|
|
static struct loader_entry *
|
|
ld_privgettext(
|
|
struct sharedstuff *sspointer,
|
|
struct file *fp,
|
|
char *member)
|
|
{
|
|
struct loader_entry_extension loader_entry_extension,*lexpointer;
|
|
struct loader_entry *le;
|
|
int rc;
|
|
char *filename;
|
|
uint size,membersize;
|
|
extern int vm_protect();
|
|
|
|
|
|
if(le = ld_findtextle(ss.la->la_loadlist,fp,member))
|
|
return le;
|
|
|
|
lexpointer=&loader_entry_extension;
|
|
bzero(lexpointer,sizeof *lexpointer);
|
|
|
|
/* textread will read the text in - it has to compute the
|
|
* length and then read the right amount
|
|
* a side effect is to set lex.h to the origin
|
|
* an allocation map is passed in. Note that we could improve this
|
|
* by keeping the allocation map up to date throughout.
|
|
*/
|
|
|
|
unlockl(&kernel_anchor.la_lock);
|
|
|
|
rc = ld_textread(sspointer,lexpointer,fp,member,NULL,0,NULL,LD_allocd);
|
|
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);/*this form does not fail*/
|
|
|
|
/* remember that sanity initializes a number of values in lex*/
|
|
if (!rc)
|
|
rc = ld_sanity(sspointer, lexpointer);
|
|
|
|
filename = ld_fptopath(sspointer,fp);
|
|
ASSERT(filename);
|
|
if (!filename) filename = "";
|
|
|
|
if (rc) { /* textread or sanity failed */
|
|
if (rc == ENOEXEC)
|
|
format_error(filename,member);
|
|
u.u_error = rc;
|
|
return NULL;
|
|
}
|
|
|
|
membersize = strlen(member);
|
|
size = membersize+1+strlen(filename)+1+sizeof(struct loader_entry);
|
|
le = ld_ualloc(sspointer,size);
|
|
le->le_usecount=0;
|
|
le->le_loadcount=0;
|
|
le->le_flags=LE_TEXT;
|
|
le->le_fp=fp;
|
|
fp_hold(fp);
|
|
le->le_file =(void *) lex.h;
|
|
le->le_filesize = lex.filesize;
|
|
/* make base copy user read only - important part is text
|
|
* since the user actually executes here - but may as well
|
|
*protect it all. Is undone in unload*/
|
|
VM_PROTECT(le->le_file,le->le_filesize,UTXTKEY);
|
|
le->le_data = le->le_file + hdr.s[lex.data].s_scnptr;
|
|
le->le_datasize = hdr.a.o_dsize; /* N.B. don't include BSS */
|
|
le->le_exports=NULL;
|
|
le->le_lex = NULL;
|
|
le->le_defered = NULL;
|
|
le->le_filename = (char *)le + sizeof(struct loader_entry);
|
|
strcpy(le->le_filename,member);
|
|
strcpy(le->le_filename+membersize+1,filename);
|
|
le->le_maxdepend=1;
|
|
le->le_ndepend=0;
|
|
/* add it to the library load list*/
|
|
le->le_next = ss.la->la_loadlist;
|
|
ss.la->la_loadlist = le;
|
|
return le;
|
|
}
|
|
/* privinstance - given a library loaded text, make a private instance.
|
|
* this is needed if the prerelocated version can't be used for some reason, or
|
|
* as the back end of privld
|
|
*/
|
|
static struct loader_entry *
|
|
ld_privinstance(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry *lible)
|
|
{
|
|
struct loader_entry *le;
|
|
struct loader_entry_extension *lexpointer;
|
|
uint size;
|
|
/* we base a private instance on the unresolved version of the program.
|
|
* sometimes, privinstance is passed a prerelocated library module
|
|
* which for some reason can't be used - the following retrieves
|
|
* the base module
|
|
*/
|
|
while (! (lible->le_flags & LE_TEXT) )
|
|
lible = lible->le_depend[0];
|
|
lexpointer =
|
|
xmalloc(sizeof(struct loader_entry_extension),0,kernel_heap);
|
|
assert(lexpointer); /* if kernel heap is this close we're dead*/
|
|
bzero(lexpointer,sizeof *lexpointer);
|
|
lex.flags = XMALLOC;
|
|
lex.h = (void *)lible->le_file;
|
|
lex.filesize = lible->le_filesize;
|
|
if (ld_sanity(sspointer, lexpointer)) panic("loader error");
|
|
|
|
size = sizeof(struct loader_entry) +
|
|
sizeof(struct loader_entry*)*(lex.nimpid);
|
|
le = (struct loader_entry *) ld_ualloc(sspointer, size );
|
|
if (!le){
|
|
u.u_error = ENOMEM;
|
|
xmfree(lexpointer,kernel_heap);
|
|
return NULL;
|
|
}
|
|
lex.le = le;
|
|
|
|
le->le_lex = lexpointer;
|
|
le->le_usecount = 0;
|
|
le->le_loadcount = 0;
|
|
le->le_flags = LE_DATA|LE_THISLOAD;
|
|
le->le_fp = lible->le_fp;
|
|
fp_hold(le->le_fp);
|
|
le->le_file = lible->le_file;
|
|
le->le_filesize = lible->le_filesize;
|
|
le->le_data = NULL; /* to be allocated later */
|
|
le->le_datasize = hdr.a.o_dsize + hdr.a.o_bsize;
|
|
le->le_exports = NULL;
|
|
le->le_defered = NULL;
|
|
le->le_filename = lible->le_filename;
|
|
le->le_ndepend = 1;
|
|
le->le_maxdepend = lex.nimpid+1;
|
|
le->le_depend[0] = lible;
|
|
lible->le_usecount += 1;
|
|
le->le_next = ss.la->la_loadlist; /* add to the process load list */
|
|
ss.la->la_loadlist = le;
|
|
/*
|
|
* now that this one is on the loadlist we can safely instantiate
|
|
* everything it depends on
|
|
*/
|
|
if (ld_libraries(sspointer,le,ld_create_create))
|
|
/* we don't clean up hear - we assume this is "curtains"
|
|
* for this load and higher level code will clean up
|
|
* everything or kill the process
|
|
*/
|
|
return NULL;
|
|
return le;
|
|
}
|
|
|
|
/* kernel library load
|
|
* called to load an instance into the kernel. kernel programs are not reused
|
|
* so we read in a copy which is executed as is.
|
|
*/
|
|
static struct loader_entry *
|
|
ld_kernelld(
|
|
struct sharedstuff *sspointer,
|
|
struct file *fp,
|
|
char *member)
|
|
{
|
|
struct loader_entry_extension *lexpointer;
|
|
struct loader_entry *le;
|
|
int rc;
|
|
char *filename;
|
|
uint lesize,size,membersize;
|
|
/* if called from library load refuse - this will cause library
|
|
* load to fail. Ultimately will be called again to load the needed
|
|
* library under the original sharedstuff environment.
|
|
*/
|
|
if (ss.type != 'K') return NULL;
|
|
|
|
|
|
lexpointer =
|
|
xmalloc(sizeof(struct loader_entry_extension),0,kernel_heap);
|
|
assert(lexpointer);
|
|
bzero(lexpointer,sizeof *lexpointer);
|
|
lex.flags = XMALLOC;
|
|
|
|
/* textread will read the text in and include room for bss
|
|
* thus this copy cannot be used as the asis for other instances.
|
|
* a side effect is to set lex.h to the origin
|
|
*/
|
|
|
|
rc = ld_textread(sspointer,lexpointer,fp,member,NULL,0,kernel_heap,LD_textreadbss);
|
|
|
|
if (!rc)
|
|
rc = ld_sanity(sspointer, lexpointer);
|
|
|
|
filename = ld_fptopath(sspointer,fp);
|
|
ASSERT(filename);
|
|
if (!filename) filename = "";
|
|
|
|
if (rc){ /*textread or sanity failed*/
|
|
if (rc==ENOEXEC)
|
|
format_error(filename,member);
|
|
u.u_error = rc;
|
|
xmfree(lexpointer,kernel_heap);
|
|
return NULL;
|
|
}
|
|
|
|
membersize = strlen(member);
|
|
lesize = sizeof(struct loader_entry) +
|
|
sizeof(struct loader_entry*)*(lex.nimpid-1);
|
|
size = lesize+membersize+1+strlen(filename)+1;
|
|
/*
|
|
* -1 because sizeof(struct loader_entry) contains space for one
|
|
* slot
|
|
*/
|
|
le = ld_ualloc(sspointer,size);
|
|
assert(le);
|
|
lex.le = le;
|
|
le->le_usecount=0;
|
|
le->le_loadcount=0;
|
|
le->le_flags=LE_TEXT|LE_DATA|LE_DATAEXISTS|LE_THISLOAD|LE_DATAINTEXT;
|
|
le->le_fp=fp;
|
|
fp_hold(fp);
|
|
le->le_file =(void *) lex.h;
|
|
le->le_filesize = lex.filesize;
|
|
le->le_data = le->le_file + hdr.s[lex.data].s_scnptr;
|
|
le->le_datasize = hdr.a.o_dsize + hdr.a.o_bsize;
|
|
le->le_exports=NULL;
|
|
le->le_lex = lexpointer;
|
|
le->le_defered = NULL;
|
|
le->le_filename = (char *)le + lesize;
|
|
strcpy(le->le_filename,member);
|
|
strcpy(le->le_filename+membersize+1,filename);
|
|
le->le_maxdepend=lex.nimpid;
|
|
le->le_ndepend=0;
|
|
le->le_next = ss.la->la_loadlist; /* add to the kernel load list */
|
|
ss.la->la_loadlist = le;
|
|
/*
|
|
* now that this one is on the loadlist we can safely instantiate
|
|
* everything it depends on
|
|
*/
|
|
#if defined(_KDB)
|
|
if (__kdb())
|
|
kdb_getdesc(le);
|
|
#endif /* _KDB */
|
|
|
|
if (ld_libraries(sspointer,le,ld_create_create))
|
|
/* we don't clean up hear - we assume this is "curtains" i
|
|
* for this load and higher level code will clean up
|
|
* everything
|
|
*/
|
|
return NULL;
|
|
return le;
|
|
}
|
|
|
|
/* private library load
|
|
* called when we can't even put the code into the shared library,
|
|
* because of permissions.
|
|
* instead, we read a copy of the code into the data segment. we then use this
|
|
* as if it where shared, which it may become if there are multiple loads
|
|
* of a one-use program.
|
|
*/
|
|
static struct loader_entry *
|
|
ld_privld(
|
|
struct sharedstuff *sspointer,
|
|
struct file *fp,
|
|
char *member)
|
|
{
|
|
struct loader_entry *le;
|
|
|
|
if (ss.type == 'K')
|
|
return ld_kernelld(sspointer,fp,member);
|
|
|
|
/* if called from library load refuse - this will cause library
|
|
* load to fail. Ultimately will be called again to load the needed
|
|
* library under the original sharedstuff environment.
|
|
*/
|
|
if (ss.type == 'L') return NULL;
|
|
|
|
|
|
/* if is possible to make a private instance of a sharable text file.
|
|
* we try that case first */
|
|
le = ld_findtextle(lib.la.la_loadlist,fp,member);
|
|
|
|
/* From the prepass we know a copy of the text already exists.
|
|
* Therefore an entry marked LE_IOPENDING is not the match we
|
|
* want so look to the private load list.
|
|
*/
|
|
if (!le || (le->le_flags & LE_IOPENDING))
|
|
le = ld_findtextle(ss.la->la_loadlist,fp,member);
|
|
|
|
return le ? ld_privinstance(sspointer,le) : le;
|
|
}
|
|
|
|
/* assign data addresses for prerelocated programs. The strategy
|
|
* is to assign addresses which will at least work for the program
|
|
* being loaded now. To do this, we must figure out which data storage
|
|
* is already spoken for.
|
|
* We use a first fit algorithm UNLESS we can find an exact fit between two
|
|
* other in use address ranges.
|
|
*/
|
|
|
|
static void
|
|
sortd(
|
|
struct d *d,
|
|
uint n)
|
|
{
|
|
uint i,j,k;
|
|
#define swapd(i,j) {struct d tempd;tempd=d[i];d[i]=d[j];d[j]=tempd;}
|
|
|
|
d = &(d[-1]); /* convert to 1 based indexing */
|
|
|
|
for(i=2;i<=n;i++){
|
|
j = i;
|
|
do {
|
|
if (d[j/2].org < d[j].org) swapd(j/2,j)
|
|
else break;
|
|
j=j/2;
|
|
} while(j > 1);
|
|
}
|
|
|
|
for(i=n;i>1;i--){
|
|
swapd(1,i);
|
|
j = 1;
|
|
while(j*2 < i){
|
|
if (j*2+1<i && d[j*2+1].org > d[j].org
|
|
&& d[j*2+1].org > d[j*2].org){
|
|
swapd(j,j*2+1);
|
|
j = j*2+1;
|
|
}
|
|
else if (d[j*2].org > d[j].org){
|
|
swapd(j,j*2);
|
|
j = j*2;
|
|
}
|
|
else break;
|
|
}
|
|
}
|
|
|
|
/* sorted by org - now make monotone non overlapping
|
|
* remember that array is 1 based here */
|
|
|
|
for(i = 1;i<n;i++){
|
|
if (d[i].end > d[i+1].end) d[i+1].end = d[i].end;
|
|
if (d[i].end > d[i+1].org) d[i+1].org = d[i].end;
|
|
}
|
|
}
|
|
|
|
#define DSIZE 256 /* DSIZE must be a power of two */
|
|
static int
|
|
extendd(
|
|
struct d **d,
|
|
uint *maxd)
|
|
{
|
|
struct d *newd;
|
|
uint newmaxd;
|
|
/* N.B. checkd ASSUMES that d is a multiple of DSIZE elements long!*/
|
|
newmaxd = *maxd+DSIZE;
|
|
newd = xmalloc(sizeof(struct d)*newmaxd,0,kernel_heap);
|
|
if (newd){
|
|
bcopy(*d,newd,sizeof(struct d)*(*maxd));
|
|
xmfree(*d,kernel_heap);
|
|
*d = newd;
|
|
*maxd = newmaxd;
|
|
return 0;
|
|
}
|
|
/* no more room */
|
|
xmfree(*d,kernel_heap);
|
|
return ENOMEM;
|
|
}
|
|
|
|
static struct d *
|
|
ld_getmap(
|
|
struct sharedstuff *sspointer) /* this represents library */
|
|
{
|
|
uint i,j,k,l,m,maxd,nextd;
|
|
struct loader_entry *le;
|
|
struct loader_entry_extension *lexpointer;
|
|
struct d *d;
|
|
struct dinuse *dinuse,*pdinuse,*ndinuse;
|
|
|
|
if (ss.d) xmfree(ss.d,kernel_heap);
|
|
|
|
/* first find all allocated locations used by this load. We take
|
|
* the user load list into account as well to be robust in the
|
|
* face of future prerelocation at load as well as exec time */
|
|
|
|
d = xmalloc(sizeof(struct d) * DSIZE,0,kernel_heap);
|
|
if (!d) return NULL;
|
|
|
|
maxd = DSIZE;
|
|
|
|
nextd = 2;
|
|
|
|
if (ss.type == 'L'){
|
|
for(le=ss.la->la_loadlist;le;le=le->le_next){
|
|
struct myxcoffhdr *myh;
|
|
if ( (le->le_flags&(LE_DATAEXISTS|LE_DATA|LE_THISLOAD)) !=
|
|
(LE_DATA|LE_THISLOAD))
|
|
continue; /* not a prerelocated instance or
|
|
data in library*/
|
|
if ( ! le->le_exports )
|
|
continue; /* this is one we need to assign
|
|
data address to */
|
|
if ( nextd >= maxd )
|
|
if(extendd(&d,&maxd)) return NULL;
|
|
myh = (void*)le->le_file;
|
|
d[nextd].org = PAGEDOWN(le->le_exports->data);
|
|
d[nextd].end =
|
|
PAGEUP(le->le_exports->data + myh->a.o_dsize + myh->a.o_bsize);
|
|
nextd++;
|
|
}
|
|
sspointer=ss.topstuff;
|
|
ASSERT(sspointer);
|
|
}
|
|
|
|
|
|
/* initialize array with dummy entries for beginning and end
|
|
* N.B. this is done here since sspointer is modified by the code above.
|
|
* maxbreak is only valid in the base shared_stuff. By now, sspointer
|
|
* always points to the base
|
|
*/
|
|
d[0].org = 0;
|
|
d[0].end =PRIVORG;
|
|
d[1].org = (ulong)ss.maxbreak;
|
|
d[1].end = -1; /*fence post*/
|
|
|
|
pdinuse = NULL;
|
|
for(dinuse=ss.la->la_dinuse;dinuse;){
|
|
if (dinuse->length == 0){
|
|
ndinuse=dinuse->next;
|
|
ld_ufree(sspointer,dinuse);
|
|
if (pdinuse)
|
|
pdinuse->next = ndinuse;
|
|
else
|
|
ss.la->la_dinuse = ndinuse;
|
|
dinuse = ndinuse;
|
|
continue;
|
|
}
|
|
|
|
if ( nextd >= maxd )
|
|
if(extendd(&d,&maxd)) return NULL;
|
|
d[nextd].org = (uint)(dinuse->start);
|
|
d[nextd].end = (uint)(dinuse->start)+dinuse->length;
|
|
nextd++;
|
|
dinuse = dinuse->next;
|
|
}
|
|
|
|
for(le = ss.la->la_loadlist;le;le=le->le_next){
|
|
/* if there is text and it is in the private region
|
|
* include it
|
|
*/
|
|
if( (le->le_flags & (LE_TEXT|LE_LIBRARY)) == LE_TEXT){
|
|
if ( nextd >= maxd )
|
|
if(extendd(&d,&maxd)) return NULL;
|
|
d[nextd].org = PAGEDOWN(le->le_file);
|
|
d[nextd].end = PAGEUP(le->le_file + le->le_filesize);
|
|
nextd++;
|
|
}
|
|
/* if there is a data address consider it allocated.
|
|
* some of these addresses may be outside the user
|
|
* data area but including them does no harm */
|
|
if (le->le_data){
|
|
if ( nextd >= maxd )
|
|
if(extendd(&d,&maxd)) return NULL;
|
|
d[nextd].org = PAGEDOWN(le->le_data);
|
|
d[nextd].end = PAGEUP(le->le_data + le->le_datasize);
|
|
nextd++;
|
|
}
|
|
}
|
|
|
|
sortd(d,nextd);
|
|
return d;
|
|
|
|
}
|
|
|
|
|
|
/* allocates in units of pages from the user data area. Uses
|
|
* the results of a previous getmap.*/
|
|
ulong
|
|
ld_allocd(
|
|
struct sharedstuff *sspointer,
|
|
ulong datasize)
|
|
{
|
|
uint i,j,k,fit;
|
|
ulong data;
|
|
struct d *d;
|
|
|
|
if (!ss.d){
|
|
ss.d = ld_getmap(sspointer);
|
|
if (!ss.d)
|
|
return 0;
|
|
}
|
|
d = ss.d;
|
|
datasize=PAGEUP(datasize);
|
|
/* scan for an exact fit, remembering the first fit as we go
|
|
* (given the way sbreak works, first fit seems the best choice)*/
|
|
fit = -1;
|
|
for(i=0;d[i].end != -1;i++){
|
|
long fitsize; /*N.B. SIGNED for test below */
|
|
fitsize=PAGEDOWN(d[i+1].org) - PAGEUP(d[i].end);
|
|
if (fitsize <= 0)
|
|
continue;
|
|
if (fitsize==datasize){
|
|
fit = i;
|
|
break;
|
|
}
|
|
if (fit == -1 && fitsize>datasize)
|
|
fit = i;
|
|
}
|
|
|
|
if (fit == -1) return 0;
|
|
|
|
data = PAGEUP(d[fit].end);
|
|
d[fit].end = data+datasize;
|
|
return data;
|
|
}
|
|
|
|
|
|
/* load an instance of a shared library program.
|
|
* if necessary, the program is actually added to the shared library.
|
|
* this involves loading the text, computing a target data location
|
|
* and prerelocating the data.
|
|
* This process may lead to other library loads etc ...
|
|
* N.B. library load only uses other LIBRARY instances for resolution.
|
|
* If a library program uses a non-sharable program, then the library
|
|
* program is not prerelocated and only private instances are possible.
|
|
* The loader entry stucture is as follows -
|
|
* The entry for the base file, on the libarary load list, is:
|
|
* LE_TEXT and LE_LIBRARY but not LE_DATA.
|
|
* le_data and le_datasize represent the data section of the file
|
|
* there are no dependencies.
|
|
* The entry for a prerelocated instance, on the library load list, is:
|
|
* LE_DATA and LE_LIBRARY but not LE_TEXT
|
|
* le_depend[0] is le of base file:
|
|
* le_data and le_datasize represent the location in the shared
|
|
* library region of the data instance.
|
|
* le_datasize does NOT include bss.
|
|
* le_exports->data is the target location of the data in the private
|
|
* region
|
|
* the prerelocated instances of other files used by this one are
|
|
* listed in ld_depend
|
|
* The entry for an in use instance, on the process load list, is:
|
|
* LE_DATA and LE_USEASIS but not LE_TEXT and not LE_LIBRARY
|
|
* le_depend[0] is le of library instance
|
|
* le_data and le_datasize represent the location and size of data in
|
|
* private region including bss.
|
|
* the private instances of files used by this are listed in ld_depend
|
|
*/
|
|
|
|
|
|
static struct loader_entry *
|
|
ld_libld(
|
|
struct sharedstuff *sspointer,
|
|
struct file *fp,
|
|
char *member)
|
|
{
|
|
/*N.B. see ldr.h for use of lexpointer*/
|
|
struct loader_entry_extension *lexpointer;
|
|
int rc=0; /*return code */
|
|
int i,j,k;
|
|
struct loader_entry *lible,*le;
|
|
struct sharedstuff sharedstuff;
|
|
char *data;
|
|
uint datasize;
|
|
int recursion;
|
|
label_t jbuf;
|
|
int waslongjmpx;
|
|
extern int vm_map();
|
|
|
|
|
|
/* never use shared library code in kernel - just fail -
|
|
* privld does the work. It is possible for the user to
|
|
* mark the executable PRIVATELOAD, again privld will do it
|
|
*/
|
|
if (ps_not_defined || ss.type == 'K' || (ss.flags & SS_PRIVATELOAD))
|
|
return NULL;
|
|
|
|
/* now we try to make an instance of this library for sharing
|
|
*/
|
|
if (ss.type == 'L')
|
|
recursion = 1;
|
|
else {
|
|
recursion = 0;
|
|
bzero(&sharedstuff,sizeof sharedstuff);
|
|
sharedstuff.topstuff = sspointer;
|
|
sspointer = &sharedstuff;
|
|
ss.type = 'L';
|
|
ss.la = &(lib.la);
|
|
ss.end_of_new = lib.la.la_loadlist;
|
|
/* copy some stuff */
|
|
ss.pl = ss.topstuff->pl; /*must be restored on exit*/
|
|
ss.libpath = ss.topstuff->libpath;
|
|
ss.libpath_env = ss.topstuff->libpath_env;
|
|
ss.libpath_saved = ss.topstuff->libpath_saved;
|
|
ss.d = ss.topstuff->d;
|
|
ss.execle = ss.topstuff->execle;
|
|
ss.ld = ss.topstuff->ld; /* loader domain in use */
|
|
ss.ldr = ss.topstuff->ldr; /* loader domain reopen */
|
|
ss.flags |= (ss.topstuff->flags & SS_RELEASE_SRS);
|
|
#if 0 /* if we ever map files into the library we may need this */
|
|
waslongjmpx = rc = setjmpx(&jbuf); /* catch "i/o"
|
|
errors in mapped files */
|
|
if (rc)
|
|
goto exitrc;
|
|
#endif
|
|
}
|
|
|
|
|
|
/* see if the library is instantiated in shared library segment*/
|
|
if (le=ld_findle(sspointer,fp,member))
|
|
goto exitle;
|
|
|
|
|
|
/* Look for a copy of the text in the shared library segment */
|
|
lible = ld_findtextle(lib.la.la_loadlist,fp,member);
|
|
|
|
/* Note that we have already insured that the text is availiable
|
|
* during the prepass. If the LE_IOPENDING flag is set, then
|
|
* the entry found is not the one we want. Return NULL so that
|
|
* we can search for the text on the process private load list.
|
|
*/
|
|
if (!lible || (lible->le_flags & LE_IOPENDING)) {
|
|
if (u.u_error == ENOEXEC)
|
|
format_error(ld_fptopath(sspointer,fp),member);
|
|
goto returnNULL;
|
|
}
|
|
|
|
/* now see if this library is needed by execload or the load command.
|
|
* only if execload do we attempt to pre-relocate it. In the case
|
|
* where the process has specified a laoder domain, we allow the
|
|
* an explicit load operation to create a pre-relocation. It is also
|
|
* possible that the user could have marked the library PRIVATELOAD
|
|
* for compatability reasons. If PRIVATELOAD, we don't pre-relocate.
|
|
*/
|
|
if ((ss.topstuff->type != 'E' && !(lible->le_flags & LE_SRO) &&
|
|
!(ss.topstuff->la->la_domain)) ||
|
|
(lible->le_flags & LE_PRIVATELOAD)) {
|
|
if (recursion)
|
|
return NULL; /* refuse to prerelocate*/
|
|
/* restore topstuff entries which may have changed */
|
|
ss.topstuff->pl = ss.pl;
|
|
ss.topstuff->d = ss.d;
|
|
ss.topstuff->ldr = ss.ldr;
|
|
/*
|
|
* ss.libpath may have been allocated
|
|
*/
|
|
if (ss.flags & SS_LIBPATH_ALLOCED) {
|
|
assert(!(ss.topstuff->flags & SS_LIBPATH_ALLOCED));
|
|
ss.topstuff->libpath = ss.libpath;
|
|
ss.topstuff->flags |= SS_LIBPATH_ALLOCED;
|
|
}
|
|
return ld_privinstance(ss.topstuff,lible);
|
|
}
|
|
|
|
lexpointer =
|
|
xmalloc(sizeof(struct loader_entry_extension),0,kernel_heap);
|
|
assert(lexpointer);
|
|
bzero(lexpointer,sizeof *lexpointer);
|
|
lex.flags = XMALLOC;
|
|
lex.h = (void *)lible->le_file;
|
|
lex.filesize = lible->le_filesize;
|
|
if (ld_sanity(sspointer, lexpointer)) panic("shared library damaged");
|
|
|
|
|
|
/* we do not prerelocate a program in the library unless its
|
|
* data alignment allows mapping - to map the data alignment
|
|
* in the file must satisfy the alignment requirements of the data.
|
|
*/
|
|
{/*block allows local declare*/
|
|
ulong align;
|
|
|
|
align = (1<<hdr.a.o_algndata)-1;
|
|
if ((uint)(lible->le_data-hdr.s[lex.data].s_vaddr) & align) {
|
|
xmfree(lexpointer,kernel_heap);
|
|
goto returnNULL;
|
|
}
|
|
}
|
|
|
|
/* Allocate space for the BSS also */
|
|
datasize = PAGEUP(lible->le_data + hdr.a.o_dsize + hdr.a.o_bsize) -
|
|
PAGEDOWN(lible->le_data);
|
|
|
|
/* Since data is the shared library data segment is not shared(a
|
|
* copy must be created for each process using it), we must allocate
|
|
* space for read only data in the shared library text segment.
|
|
*/
|
|
if (lible->le_flags & LE_SRO)
|
|
data = xmalloc(datasize,PGSHIFT,lib.la.la_text_heap);
|
|
else
|
|
data = xmalloc(datasize,PGSHIFT,lib.la.la_data_heap);
|
|
|
|
if (!data) {
|
|
#ifdef DEBUG
|
|
/* panic("shared library is full"); */
|
|
#endif
|
|
/* if no room, we give up on prerelocation - thus
|
|
* we will load and relocate in private. Set a flag
|
|
* indicating library segment is full. We will try
|
|
* to cleanup before next exec.
|
|
*/
|
|
shlibseg_full++;
|
|
xmfree(lexpointer,kernel_heap);
|
|
goto returnNULL;
|
|
}
|
|
|
|
/* we must get le_data and le_datasize "right" - this means data
|
|
* is no longer the value returned be xmalloc.
|
|
* In unload, we will use PAGEDOWN to get back to that value for xmfree
|
|
*/
|
|
data = data + (uint)lible->le_data - PAGEDOWN(lible->le_data);
|
|
|
|
{
|
|
uint size;
|
|
|
|
size = sizeof(struct loader_entry) +
|
|
sizeof(struct loader_entry*)*(lex.nimpid);
|
|
le = xmalloc(size,0,lib.la.la_data_heap);
|
|
if (!le) {
|
|
#ifdef DEBUG
|
|
/* panic("shared library is full"); */
|
|
#endif
|
|
/* if no room, we give up on prerelocation - thus
|
|
* we will load and relocate in private. Set a
|
|
* flag to indicate shared library seg is full.
|
|
* We will try to cleanup before next exec.
|
|
*/
|
|
shlibseg_full++;
|
|
xmfree(lexpointer,kernel_heap);
|
|
if (lible->le_flags & LE_SRO)
|
|
xmfree(PAGEDOWN(data),lib.la.la_text_heap);
|
|
else
|
|
xmfree(PAGEDOWN(data),lib.la.la_data_heap);
|
|
goto returnNULL;
|
|
}
|
|
|
|
bzero(le,size);
|
|
} /* end of use of size */
|
|
lex.le = le;
|
|
le->le_usecount = 0;
|
|
le->le_loadcount = 0;
|
|
le->le_flags = LE_LIBRARY|LE_DATA|LE_THISLOAD;
|
|
if (lible->le_flags & LE_SRO)
|
|
le->le_flags |= LE_SRO;
|
|
le->le_fp = fp;
|
|
fp_hold(fp);
|
|
le->le_file = (void *)lex.h;
|
|
le->le_filesize = lex.filesize;
|
|
/* Map copy the pristine copy to the new instance */
|
|
if(VM_MAP(lible->le_data,data,lible->le_datasize))
|
|
bcopy(lible->le_data,data,lible->le_datasize);
|
|
le->le_data = data;
|
|
/* This instance represents both the data and bss */
|
|
le->le_datasize = hdr.a.o_dsize + hdr.a.o_bsize;
|
|
le->le_exports=NULL;
|
|
le->le_lex = lexpointer;
|
|
le->le_defered = NULL;
|
|
le->le_filename = lible->le_filename;
|
|
le->le_maxdepend=1+lex.nimpid;
|
|
le->le_ndepend=1;
|
|
le->le_depend[0] = lible;
|
|
lible -> le_usecount += 1;
|
|
|
|
/* Set the data and bss relocation fields for this instance of the
|
|
* data. These will be used when we resolve/relocate the data.
|
|
*/
|
|
lex.datareloc = le->le_data - hdr.s[lex.data].s_vaddr;
|
|
lex.bssreloc = le->le_data + hdr.a.o_dsize - hdr.s[lex.bss].s_vaddr;
|
|
|
|
/* add this entry to the library load list NOW. Note that this le will
|
|
* satisfy library request for this program EVEN though we are not
|
|
* finished resolving it. See the test in checkdep -
|
|
* since LE_THISLOAD is set, this entry is always accepted
|
|
*/
|
|
|
|
le->le_next = ss.la->la_loadlist;
|
|
ss.la->la_loadlist = le;
|
|
|
|
/* resolve libraries for this le - create instances if necessary */
|
|
if (rc = ld_libraries(sspointer,le,ld_create_create)){
|
|
/* we get here whenever one of the dependencies is
|
|
* not shareable. Also, if a real error happened.
|
|
* In that case, uerror will be set */
|
|
le=NULL;
|
|
goto exitle;
|
|
}
|
|
|
|
/* if recursive call stop here - resolve can't be done
|
|
* till all libraries are found
|
|
*/
|
|
if (recursion) return le;
|
|
|
|
if (rc=ld_resolve(sspointer)) goto exitrc;
|
|
|
|
if (rc=ld_relocate(sspointer)) goto exitrc;
|
|
|
|
exitle:
|
|
/* if inside a recursion let the top level clean up the mess */
|
|
if (recursion) return le;
|
|
#if 0 /* see comment above on setjmpx */
|
|
if (! waslongjmpx)
|
|
clrjmpx(&jbuf);
|
|
#endif
|
|
/* at this point, there are temporary data structures hanging from
|
|
* loader_entries in the library. We leave them be - they will be
|
|
* cleaned up in one final pass over the load list at the very end.
|
|
* We do this so that the THISLOAD bits already set are preserved.
|
|
*/
|
|
/* restore pathname lookaside chain - it may have changed */
|
|
ss.topstuff->ldr = ss.ldr;
|
|
ss.topstuff->pl = ss.pl;
|
|
if (ss.flags & SS_LIBPATH_ALLOCED) {
|
|
assert(!(ss.topstuff->flags & SS_LIBPATH_ALLOCED));
|
|
ss.topstuff->libpath = ss.libpath;
|
|
ss.topstuff->flags |= SS_LIBPATH_ALLOCED;
|
|
}
|
|
if (!le) { /*error cleanup*/
|
|
struct loader_entry *tle,*nle;
|
|
ld_freelex(ss.la->la_loadlist,ss.end_of_new);
|
|
/* discard all the new loader entries */
|
|
for(tle=lib.la.la_loadlist;tle!=ss.end_of_new;){
|
|
nle = tle->le_next;
|
|
ld_clearle(sspointer,tle);
|
|
if (tle->le_flags & LE_DATA && tle->le_data) {
|
|
if (tle->le_flags & LE_SRO)
|
|
xmfree((void *)PAGEDOWN(tle->le_data),
|
|
lib.la.la_text_heap);
|
|
else
|
|
xmfree((void *)PAGEDOWN(tle->le_data),
|
|
lib.la.la_data_heap);
|
|
}
|
|
if (tle->le_flags & LE_TEXT && tle->le_file)
|
|
xmfree(tle->le_file,lib.la.la_text_heap);
|
|
xmfree(tle->le_exports,lib.la.la_data_heap);
|
|
xmfree(tle,lib.la.la_data_heap);
|
|
tle = nle;
|
|
}
|
|
ss.la->la_loadlist = ss.end_of_new;
|
|
/* N.B. we don't retain the ss.d structure on error
|
|
* path. In general, we can always correctly
|
|
* compute ss.d - so when in doubt throw the old
|
|
* one away. */
|
|
xmfree(ss.d,kernel_heap);
|
|
ss.topstuff->d = NULL;
|
|
return NULL;
|
|
}
|
|
ss.topstuff->d = ss.d;
|
|
return ld_instance(ss.topstuff,le);
|
|
|
|
exitrc:
|
|
u.u_error=rc;
|
|
le = NULL;;
|
|
goto exitle;
|
|
|
|
returnNULL:
|
|
if ( !recursion){
|
|
#if 0 /* see setjmpx comment above */
|
|
ASSERT(! waslongjmpx);
|
|
clrjmpx(&jbuf);
|
|
#endif
|
|
ss.topstuff->pl = ss.pl;
|
|
ss.topstuff->d = ss.d;
|
|
ss.topstuff->ldr = ss.ldr;
|
|
if (ss.flags & SS_LIBPATH_ALLOCED) {
|
|
assert(!(ss.topstuff->flags & SS_LIBPATH_ALLOCED));
|
|
ss.topstuff->libpath = ss.libpath;
|
|
ss.topstuff->flags |= SS_LIBPATH_ALLOCED;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* Called from ld_checkdep when dependencies for the library
|
|
* do not match. In this case we turn off the LE_THISLOAD
|
|
* flag and turn on the LE_NOTTHISLOAD flag in the passed le.
|
|
* We then do the same for all le's that depend on an le with
|
|
* the LE_NOTTHISLOAD flag on. Multiple passes are made over
|
|
* the load list until no le's are found that satisfy this
|
|
* condition.
|
|
*/
|
|
|
|
static void
|
|
ld_undothis(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry *le)
|
|
{
|
|
int i, check_again;
|
|
|
|
le->le_flags &= ~LE_THISLOAD;
|
|
le->le_flags |= LE_NOTTHISLOAD;
|
|
do {
|
|
check_again = 0;
|
|
for (le = ss.la->la_loadlist; le; le=le->le_next) {
|
|
if (le->le_flags & LE_THISLOAD) {
|
|
for (i=1; i < le->le_ndepend; i++) {
|
|
if (le->le_depend[i]->le_flags &
|
|
LE_NOTTHISLOAD) {
|
|
le->le_flags &= ~LE_THISLOAD;
|
|
le->le_flags |= LE_NOTTHISLOAD;
|
|
check_again++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} while (check_again);
|
|
}
|
|
|
|
/* called with an LD_DATA type le. must resolve each lib path name
|
|
* and see if that is the same file used to make this le. If so,
|
|
* return 0, else return 1
|
|
* we expect sspointer to provide libpath and support for path name lookaside
|
|
*/
|
|
|
|
static int
|
|
ld_checkdep(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry *le)
|
|
{
|
|
|
|
le->le_flags |= LE_THISLOAD; /* mark current this load for now */
|
|
|
|
if ('L' != ss.type)
|
|
return 0;
|
|
|
|
/* inorder for a prerelocated library routine to be useful, it must
|
|
* match exactly - including dependencies*/
|
|
if (ld_libraries(sspointer,le,ld_create_check)){
|
|
/* the libraries required for this le are not the ones it was
|
|
* resolved against
|
|
*/
|
|
ld_undothis(sspointer,le);
|
|
return 1;
|
|
}
|
|
/* if it is good, leave the thisload bit on */
|
|
return 0;
|
|
}
|
|
|
|
/* search a loader chain for an entry which represents the exact file
|
|
* named by fp - the file pointer to an open file.
|
|
* we skip unloaded entries or entries without the LE_DATA flag on
|
|
* first we search for a thisload entry - if we find one it must match.
|
|
* then we search again looking for an entry whose file dependencies match.
|
|
* during the check, we tentitively mark the entry being tested as thisload.
|
|
* Thus, if a depends on b and b depends on a we can find a pair (a,b) whose
|
|
* other dependencies are correct.
|
|
*/
|
|
static struct loader_entry *
|
|
ld_findle(
|
|
struct sharedstuff *sspointer,
|
|
struct file *fp,
|
|
char *member)
|
|
{
|
|
struct loader_entry *le,*firstle;
|
|
le = firstle = ld_findfp(ss.la->la_loadlist,fp,member);
|
|
while(le){
|
|
if ( (le->le_flags & (LE_DATA|LE_THISLOAD)) ==
|
|
(LE_DATA|LE_THISLOAD))
|
|
return le;
|
|
le = ld_findfp(le->le_next,fp,member);
|
|
}
|
|
le = firstle;
|
|
while(le){
|
|
/* It is possible that the LE_THISLOAD flag was set due to a
|
|
* previous call to ld_checkdep(). In this case we know all
|
|
* the dependencies are satisfied, so simply return the le.
|
|
*/
|
|
if ( (le->le_flags & (LE_DATA|LE_THISLOAD)) ==
|
|
(LE_DATA|LE_THISLOAD))
|
|
return le;
|
|
if ( ((le->le_flags & (LE_DATA|LE_NOTTHISLOAD)) == LE_DATA) &&
|
|
( le->le_flags & LE_SRO ||
|
|
! ld_checkdep(sspointer,le))) return le;
|
|
/* when checkdep fails, we may have reserved memory
|
|
* we don't need - so force map to be recomputed */
|
|
xmfree(ss.d,kernel_heap);
|
|
ss.d=NULL;
|
|
le = ld_findfp(le->le_next,fp,member);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* called with an LD_DATA type loader entry - finds or creates a private
|
|
* instance. sspointer is the anchor for the private load
|
|
*/
|
|
static struct loader_entry *
|
|
ld_getinstance(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry *lible)
|
|
{
|
|
struct loader_entry *le;
|
|
ASSERT(ss.type != 'L');
|
|
/* if this is readonly - such as the kernel - use it directly */
|
|
if (lible->le_flags & (LE_USEASIS|LE_SYSCALLS))
|
|
return lible;
|
|
/* search for existing instance - this code must be upgraded to deal
|
|
* with one use programs
|
|
*/
|
|
for(le=ss.la->la_loadlist;le;le=le->le_next)
|
|
if ( (le->le_flags & (LE_UNLOAD|LE_TEXT|LE_DATA))==LE_DATA &&
|
|
le->le_depend[0] == lible) return le;
|
|
return ld_instance(sspointer,lible);
|
|
}
|
|
|
|
BUGVDEF(ld_srodbg,0);
|
|
|
|
static struct loader_entry *
|
|
ld_sroinstance(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry *lible)
|
|
{
|
|
struct loader_entry *le;
|
|
|
|
BUGLPR(ld_srodbg,2,("sro instance: lible = 0x%x\n", lible));
|
|
|
|
if (lible->le_ndepend > 1) {
|
|
return NULL;
|
|
}
|
|
|
|
le = ld_ualloc(sspointer,sizeof(struct loader_entry));
|
|
|
|
if (!le) {
|
|
u.u_error = ENOMEM;
|
|
return NULL;
|
|
}
|
|
|
|
le->le_usecount = 0;
|
|
le->le_loadcount = 0;
|
|
le->le_flags =
|
|
LE_DATA|LE_DATAEXISTS|LE_LIBEXPORTS|LE_USEASIS|
|
|
LE_THISLOAD|LE_SRO;
|
|
le->le_fp = lible->le_fp;
|
|
fp_hold(le->le_fp);
|
|
le->le_file = lible->le_file;
|
|
le->le_filesize = lible->le_filesize;
|
|
le->le_data = lible->le_data; /* target location of data */
|
|
le->le_datasize = lible->le_datasize;
|
|
le->le_exports = lible->le_exports;
|
|
le->le_lex = NULL;
|
|
le->le_defered = NULL;
|
|
le->le_ndepend = 1;
|
|
le->le_maxdepend = lible->le_maxdepend;
|
|
le->le_depend[0] = lible;
|
|
le->le_filename = lible->le_filename;
|
|
lible->le_usecount++;
|
|
le->le_next = ss.la->la_loadlist;
|
|
ss.la->la_loadlist = le;
|
|
BUGLPR(ld_srodbg,2,("sro instance: newle = 0x%x\n", le));
|
|
return le;
|
|
}
|
|
|
|
/*
|
|
* NAME: ld_domain_add(ld, le, can_add)
|
|
*
|
|
* FUNCTION: This routine is designed to be called from ld_instance().
|
|
* It will be called when a new library entry is added to
|
|
* a processes load list. This routine will check to see
|
|
* if the loader entry already exists in the loader domain.
|
|
* If it does not already exist, then it is added to the
|
|
* domain(if the process has sufficient privledge).
|
|
*
|
|
* PARAMETERS: ld - pointer to loader domain structure
|
|
* le - pointer to loader entry structure
|
|
* can_add - flag to indicate process can add entries to the
|
|
* loader domain
|
|
*
|
|
* MODIFIED A new domain entry may be created and added to the
|
|
* DATA specified loader domain
|
|
*
|
|
* RETURN: de - pointer to global domain entry
|
|
*
|
|
*/
|
|
struct domain_entry *
|
|
ld_domain_add(struct loader_domain *ld, struct loader_entry *le, int can_add)
|
|
{
|
|
struct domain_entry *de;
|
|
char *p;
|
|
|
|
/* Search for loader entry within the domain */
|
|
de = ld->ld_entries;
|
|
while (de) {
|
|
if (de->de_le == le)
|
|
return(de);
|
|
de = de->de_next;
|
|
}
|
|
|
|
/*
|
|
* no domain entry for this loader entry
|
|
* add it if permissions allow
|
|
*/
|
|
if (!can_add) {
|
|
ld_emess(L_ERROR_DOMADD, ld->ld_name, le);
|
|
u.u_error = EACCES;
|
|
return(NULL);
|
|
}
|
|
|
|
de = xmalloc(sizeof(struct domain_entry), 2, lib.la.la_data_heap);
|
|
|
|
if (!de) {
|
|
/*
|
|
* system is in serious trouble(out of page space),
|
|
* but we won't force the issue here
|
|
*/
|
|
u.u_error = ENOMEM;
|
|
return(de);
|
|
}
|
|
|
|
/* initialize fields in domain_entry structure */
|
|
de->de_le = le;
|
|
de->de_usecount = 0;
|
|
p = (de->de_le)->le_filename;
|
|
while(*p++); /* skip member name */
|
|
de->de_fullname = p;
|
|
de->de_altname = NULL;
|
|
|
|
/* add loader entry to domain list */
|
|
de->de_next = ld->ld_entries;
|
|
ld->ld_entries = de;
|
|
|
|
return(de);
|
|
}
|
|
|
|
/* instance creates a local instance of a library loaded module
|
|
* all it does is create the loader entry and compute the data location
|
|
* for the instance. Note that this is a USEASIS instance. If ld_instance
|
|
* can't to this, it calls privinstance to make a relocated private copy.
|
|
*/
|
|
static struct loader_entry *
|
|
ld_instance(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry *lible)
|
|
{
|
|
struct loader_entry *le;
|
|
struct domain_entry *de;
|
|
struct loader_domain *ld;
|
|
uint datasize,i;
|
|
|
|
/* if this is a shared library load, we already have the "instance"*/
|
|
if (ss.type == 'L') return lible;
|
|
|
|
if (lible->le_flags & LE_SRO)
|
|
return ld_sroinstance(sspointer,lible);
|
|
|
|
le = ld_ualloc(sspointer,
|
|
sizeof(struct loader_entry) +
|
|
sizeof(struct loader_entry*)*(lible->le_maxdepend-1));
|
|
/* N.B. we don't bzero - so must initialize all fields */
|
|
if (!le){
|
|
u.u_error = ENOMEM;
|
|
return NULL;
|
|
}
|
|
{ /* copy the defered chain for private use
|
|
* N.B. do this first since it can fail.
|
|
*/
|
|
struct loader_defered *ld,*nld,*libld;
|
|
nld = NULL;
|
|
for(libld=lible->le_defered;libld;libld=libld->next){
|
|
ld=ld_ualloc(sspointer,sizeof(struct loader_defered));
|
|
if (!ld) {
|
|
while(nld){
|
|
ld=nld->next;
|
|
ld_ufree(sspointer,nld);
|
|
nld=ld;
|
|
}
|
|
ld_ufree(sspointer,le);
|
|
return NULL;
|
|
}
|
|
*ld = *libld;
|
|
ld->next = nld;
|
|
nld = ld;
|
|
}
|
|
if(le->le_defered = nld)
|
|
ss.la->la_flags |= LA_DEFERED;
|
|
}
|
|
|
|
le->le_usecount = 0;
|
|
le->le_loadcount = 0;
|
|
le->le_flags =
|
|
LE_DATA|LE_DATAMAPPED|LE_LIBEXPORTS|LE_USEASIS|LE_THISLOAD;
|
|
le->le_fp = lible->le_fp;
|
|
fp_hold(le->le_fp);
|
|
le->le_file = lible->le_file;
|
|
le->le_filesize = lible->le_filesize;
|
|
le->le_data = lible->le_data; /* target location of data */
|
|
le->le_datasize = lible->le_datasize; /*N.B. this includes bss*/
|
|
le->le_exports = lible->le_exports;
|
|
le->le_lex = NULL;
|
|
le->le_ndepend = 1;
|
|
le->le_maxdepend = lible->le_maxdepend;
|
|
le->le_depend[0] = lible;
|
|
le->le_filename = lible->le_filename;
|
|
lible->le_usecount += 1;
|
|
le->le_next = ss.la->la_loadlist; /* add to the process load list */
|
|
ss.la->la_loadlist = le;
|
|
/*
|
|
* if the process uses a loader domain, create/add new loader
|
|
* domain entry if necessary.
|
|
*/
|
|
if (ld = ss.la->la_domain) {
|
|
de = ld_domain_add(ld, lible,
|
|
ss.la->la_flags & LA_DOMAIN_WRITE);
|
|
if (!de)
|
|
return NULL;
|
|
/*
|
|
* set flag in loader entry to indicate the loader entry
|
|
* corresponds to a domain entry
|
|
*/
|
|
le->le_flags |= LE_DOMAIN_ENT;
|
|
le->le_de = de;
|
|
de->de_usecount++;
|
|
}
|
|
|
|
/* now that this one is on the loadlist we can safely instantiate
|
|
* everything it depends on - note that previous checks guarantee
|
|
* that this will work
|
|
*/
|
|
for(i=1;i<lible->le_ndepend;i++){
|
|
le->le_depend[i] =
|
|
ld_getinstance(sspointer,lible->le_depend[i]);
|
|
ASSERT(le->le_depend[i]);
|
|
if (!le->le_depend[i]) return NULL;
|
|
le->le_depend[i]->le_usecount += 1;
|
|
le->le_ndepend += 1;
|
|
}
|
|
return le;
|
|
}
|
|
|
|
/*
|
|
* getlibread is the special version of getlib used during the
|
|
* readin prepass. Instead of looking for a "good" le, it looks for
|
|
* any le for the requested text, and if none exists it reads one in.
|
|
* It must look both in the private and library load lists. If it doesn't
|
|
* find an le it must read one into either the private or library text
|
|
*/
|
|
static struct loader_entry *
|
|
ld_getlibread(
|
|
struct sharedstuff *sspointer,
|
|
struct file *fp,
|
|
char *member) /*name of member or null string*/
|
|
{
|
|
struct loader_entry *le,**newpre;
|
|
ulong i;
|
|
if ( ! (le = ld_libgettext(sspointer,fp,member)))
|
|
le = ld_privgettext(sspointer,fp,member);
|
|
|
|
if (!le)
|
|
return NULL;
|
|
|
|
for(i=0;i<ss.precount;i++)
|
|
if (ss.pre[i] == le)
|
|
return le;
|
|
|
|
if(ss.precount == ss.maxprecount){
|
|
ss.maxprecount +=236; /* 236+20 is 256 - see intial value*/
|
|
newpre = xmalloc(ss.maxprecount*(sizeof le),0,kernel_heap);
|
|
bcopy(ss.pre,newpre,ss.precount*(sizeof le));
|
|
if (ss.precount > 236)
|
|
xmfree(ss.pre,kernel_heap);
|
|
ss.pre = newpre;
|
|
}
|
|
|
|
ss.pre[ss.precount++] = le;
|
|
le->le_usecount += 1;
|
|
return le;
|
|
}
|
|
/*
|
|
* ld_preread is called at the first entry to the ld_libld package, which may
|
|
* either be at getlib or ld_libraries. It implements the strategy for avoiding
|
|
* I/O wait deadlocks and livelocks. In otherwords, all I/O will now be done
|
|
* without holding the library lock. To do this, a prepass is taken in which
|
|
* opens and reads are completed but no further loading is done. It is safe
|
|
* to release the loader lock during each I/O in the prepass.
|
|
*
|
|
* The way this works is that we call into the normal load logic. However,
|
|
* getlib will satisfy each request with any le, since if any le exists, the
|
|
* text must be available. The first time getlib finds the le, it adds it
|
|
* to the pre vector and ups its usecount. Thus, none of the things already
|
|
* found can disappear during lock release sequence.
|
|
*/
|
|
|
|
static int
|
|
ld_preread(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry *le,
|
|
char *path, /*search path or NULL*/
|
|
char *filename, /*name of file*/
|
|
char *member) /*name of member or null string*/
|
|
{
|
|
struct loader_entry *localpre[20];
|
|
int rc;
|
|
ulong i,nextle;
|
|
struct loader_entry *getlib_le;
|
|
if (ss.type == 'K')
|
|
return 0;
|
|
ss.execle = ss.la->la_execle;
|
|
ss.pre = localpre;
|
|
ss.precount = 0;
|
|
ss.maxprecount = (sizeof localpre)/(sizeof le);
|
|
nextle = 0;
|
|
/* now call the same entry that just called preread, but
|
|
* specify the 3 mode, which is preread */
|
|
|
|
if (le){
|
|
ss.pre[0] = le;
|
|
ss.precount = 1;
|
|
le -> le_usecount += 1; /* add one only so count down loop
|
|
below is uniform */
|
|
nextle = 1;
|
|
rc=ld_libraries(sspointer,le,ld_create_read);
|
|
}
|
|
else{
|
|
getlib_le=ld_getlib(sspointer,path,filename,member,ld_create_read);
|
|
rc = getlib_le == NULL;
|
|
}
|
|
|
|
while (!rc && nextle < ss.precount){
|
|
rc = ld_libraries(sspointer,ss.pre[nextle],ld_create_read);
|
|
nextle += 1;
|
|
}
|
|
|
|
/*
|
|
* The code in ld_libraries assumes that ss.libpath is initially
|
|
* NULL. If ss.libpath is not null, then it is prepended to
|
|
* the libpath information in the shared object. Therefore, we
|
|
* must insure that ss.libpath is NULL for the initial call.
|
|
* because of the prepass, there really are 2 'initial' calls.
|
|
* save the value of ss.libpath in ss.libpath_saved, this will
|
|
* be restored in ld_libraries.
|
|
*/
|
|
if (ss.libpath) {
|
|
ss.libpath_saved = ss.libpath;
|
|
ss.libpath = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* clean up any loader domain reopen arrays. this could
|
|
* possibly release locks. therefore, it must be done
|
|
* before decrementing use counts.
|
|
*/
|
|
if (ss.ldr) {
|
|
ld_clean_reopen(sspointer, ss.ldr);
|
|
ss.ldr = NULL;
|
|
}
|
|
|
|
for(i=0;i<ss.precount;i++)
|
|
ss.pre[i]->le_usecount -= 1;
|
|
|
|
if(ss.pre != localpre)
|
|
xmfree(ss.pre,kernel_heap);
|
|
ss.pre = NULL; /*this lets us have an assert later on to
|
|
make sure we don't do I/O after the preread*/
|
|
|
|
/* If this is an explicit load call, check the process private
|
|
* load list for unloaded entries. For each unloaded entry mark
|
|
* the library entry as NOTTHISLOAD. This will prevent reuse
|
|
* of an unloaded entry and force creation of a new one if
|
|
* needed. ld_freelex() will reset the bits in the library
|
|
* loader entries.
|
|
*/
|
|
if (ss.type == 'C') {
|
|
for(le=ss.la->la_loadlist; le; le=le->le_next) {
|
|
if((le->le_flags & (LE_DATA|LE_USEASIS|LE_UNLOAD)) ==
|
|
(LE_DATA|LE_USEASIS|LE_UNLOAD))
|
|
le->le_depend[0]->le_flags |= LE_NOTTHISLOAD;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/* getlib processes a request for making a pathname(member) available.
|
|
* If the request is for create, getlib actually calls the routines needed
|
|
* to load the program.
|
|
* create can be 0, 1, 2, or 3. 1 indicates the external call from ld_load.
|
|
* 2 is an internal call to create the library. 0 is an internal call to
|
|
* verify that the library exists. 3 is a call to verify that the text of
|
|
* the library exists.
|
|
*/
|
|
struct loader_entry *
|
|
ld_getlib(
|
|
struct sharedstuff *sspointer,
|
|
char *path, /*search path or NULL*/
|
|
char *filename, /*name of file*/
|
|
char *member, /*name of member or null string*/
|
|
int create) /*create libs or just check them*/
|
|
{
|
|
struct file* fp; /* file pointer to library */
|
|
struct loader_entry *le;
|
|
|
|
if (ld_create_initialcall == create){
|
|
if (ld_preread(sspointer,NULL,path,filename,member))
|
|
return NULL;
|
|
create = ld_create_create;
|
|
}
|
|
|
|
if (! memcmp(filename,"/\0unix\0",7)&& !*member)
|
|
return (ss.type == 'K')?kernel_exports:syscall_exports;
|
|
|
|
fp=ld_pathopen(sspointer,path,filename,NULL); /* NULL for normal
|
|
case - see pathinit*/
|
|
|
|
if (! fp)
|
|
return NULL;
|
|
/* if request if for the exec'd file avoid loading another
|
|
* copy in the shared library*/
|
|
else if (ss.execle && FPtoFH(fp) == FPtoFH(ss.execle->le_fp))
|
|
return ss.type=='L' ? NULL : ss.execle;
|
|
else if (ld_create_read == create)
|
|
return ld_getlibread(sspointer,fp,member);
|
|
/* see if we have already instantiated this library in this process */
|
|
else if (le=ld_findle(sspointer,fp,member));
|
|
/* see if we are just checking - if so give up */
|
|
else if ( ld_create_check == create ) /* do nothing */;
|
|
/* see if we can load an instance into the shared library segment*/
|
|
else if (le=ld_libld(sspointer,fp,member));
|
|
/* is u_error is set something bad happened - give up */
|
|
else if (u.u_error);
|
|
/* see if we can load a private instance */
|
|
else le=ld_privld(sspointer,fp,member);
|
|
/* end of confusing else if sequence - le now has a good value
|
|
* (maybe NULL)
|
|
* N.B. we don't close fp - the pathopen/pathclear mechanism takes
|
|
* care of that
|
|
*/
|
|
|
|
return le;
|
|
}
|
|
|
|
/* libraries is called to process an le which has been added to the load list
|
|
* (private, library, or kernel - see ss.type).
|
|
* it loops through the list of requred files. For each one, it finds or
|
|
* causes to be created an le ON THE SAME load list for that file
|
|
* Note that the process of creating an entry may cause libraries to
|
|
* be called recursively - so the code must be careful about leaving things
|
|
* in a good state.
|
|
* N.B. if the indirect result of this call is to add things to the library,
|
|
* those must be resolved and relocated. HOWEVER, the le's added to
|
|
* the process load list are unresolved unless they are library copies
|
|
* mared USEASIS. They will be resolved by the relocate and resolve
|
|
* loops called from execld.
|
|
*
|
|
* Path processing has been modified so that the concatenation of two
|
|
* paths - the default and the module, are used. This supports better
|
|
* LIBPATH semantics, since LIBPATH adds to rather than replaces the
|
|
* default. Thus, you only need put the new stuff in LIBPATH.
|
|
*
|
|
* We do it by copying since this is simpler than passing two strings around
|
|
* particularly since the pathlook must hash the whole path anyhow.
|
|
*
|
|
* create can be 0, 1, 2, or 3. 1 indicates the external call from ld_execld.
|
|
* 2 is an internal call to create the library. 0 is an internal call to
|
|
* verify that the library exists. 3 is a call to verify that the text of
|
|
* the library exists.
|
|
*/
|
|
int
|
|
ld_libraries(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry *le,
|
|
int create)
|
|
{
|
|
struct loader_entry_extension *lexpointer;
|
|
int i,j,k;
|
|
uint depend,nextdep,size;
|
|
struct loader_entry *lible;
|
|
char *imp,*filename,*member,*libpath,*tend;
|
|
char pathbuffer[128];
|
|
int plen,mlen,tlen;
|
|
int libpathmalloced=0;
|
|
int rc = 0;
|
|
|
|
if (ld_create_initialcall == create){
|
|
if (rc = ld_preread(sspointer,le,NULL,NULL,NULL))
|
|
return rc;
|
|
create = ld_create_create;
|
|
}
|
|
|
|
/* N.B. in the non-create path, lexpointer may be null ! */
|
|
lexpointer = le->le_lex;
|
|
if (ld_create_create == create) {
|
|
depend = lex.nimpid;
|
|
size = sizeof(struct loader_entry) +
|
|
sizeof(struct loader_entry*) * (depend);
|
|
lex.impid =
|
|
(struct loader_entry **) xmalloc(size,0,kernel_heap);
|
|
if (lex.impid == NULL) return ENOMEM;
|
|
imp = (char *)lex.ldhdr+lex.ldhdr->l_impoff;
|
|
}
|
|
else {
|
|
struct myxcoffhdr *h;
|
|
struct ldhdr *ldhdr;
|
|
h = (void *)le->le_file;
|
|
ldhdr = (void *)
|
|
((char *)le->le_file + h->s[h->a.o_snloader-1].s_scnptr);
|
|
depend = ldhdr->l_nimpid - 1;
|
|
imp = (char *)ldhdr + ldhdr->l_impoff;
|
|
if (le->le_flags & LE_TEXT) nextdep = 0;else nextdep = 1;
|
|
}
|
|
|
|
|
|
/* getlib returns the loader entry in the process private segment for
|
|
* this library the path name string is a series of entries, the
|
|
* first of which represents the LIBPATH of this bind.
|
|
* Each entry consists of three NULL terminated strings - the path,
|
|
* the basefilename, and the member. Any of these can be the empty
|
|
* string. depend is the number of strings. The first actual
|
|
* library name is the second string - which is considered ordinal 1.
|
|
*/
|
|
|
|
tend = ss.type != 'K' ?
|
|
le->le_file + le->le_filesize :
|
|
(char *) &hdr + hdr.s[lex.loader].s_scnptr +
|
|
hdr.s[lex.loader].s_size;
|
|
#if 0
|
|
/*
|
|
* tend = le->le_file + le->le_filesize;
|
|
* is not enough because the loader section might not be
|
|
* contiguous with the rest of the file
|
|
*/
|
|
tend = (char *) &hdr + hdr.s[lex.loader].s_scnptr +
|
|
hdr.s[lex.loader].s_size;
|
|
#endif
|
|
|
|
/*
|
|
* ss.libpath is set first time through this code. the ss.libpath
|
|
* string is used throughout the loader operation. it is prepended
|
|
* to the libpath string contained in the loader section.
|
|
*/
|
|
if (!ss.libpath) {
|
|
/*
|
|
* if there is a saved libpath from the prepass, restore
|
|
* it now.
|
|
*/
|
|
if (ss.libpath_saved) {
|
|
ASSERT(!ss.pre) /* must not be in the prepass */
|
|
#ifdef DEBUG
|
|
/*
|
|
* DEBUG code asserts that saved libpath is the same
|
|
* as what a newly constructed libpath would be.
|
|
*/
|
|
if (ss.libpath_env&&(0 != strcmp(ss.libpath_env,imp))){
|
|
plen = strlen(ss.libpath_env);
|
|
mlen = strlen(imp);
|
|
tlen = plen+mlen+2;
|
|
|
|
assert(!memcmp(ss.libpath_saved +
|
|
(ss.ld ? tlen : 0), ss.libpath_env,
|
|
plen));
|
|
assert(!memcmp(ss.libpath_saved + plen +
|
|
(ss.ld ? tlen : 0),":",1));
|
|
assert(!memcmp(ss.libpath_saved + plen + 1 +
|
|
(ss.ld ? tlen : 0), imp, mlen + 1));
|
|
}
|
|
else {
|
|
mlen = strlen(imp) + 1;
|
|
assert(!memcmp(ss.libpath_saved +
|
|
(ss.ld ? mlen : 0), imp, mlen));
|
|
}
|
|
#endif /* DEBUG */
|
|
ss.libpath = ss.libpath_saved;
|
|
ss.libpath_saved = NULL;
|
|
}
|
|
|
|
/*
|
|
* if LIBPATH environment variable was set, then ss.libpath
|
|
* becomes LIBPATH:'libpath string in loader section'
|
|
*/
|
|
else if (ss.libpath_env && (0 != strcmp(ss.libpath_env,imp))) {
|
|
plen = strlen(ss.libpath_env);
|
|
mlen = strlen(imp);
|
|
tlen = plen+mlen+2;
|
|
/*
|
|
* if loader domain is specified, build full libpath
|
|
* in second half of allocated space. the full path
|
|
* is then pruned, and the resulting libpath is placed
|
|
* in the first half of the allocated space.
|
|
*/
|
|
ss.libpath = xmalloc(tlen * (ss.ld ? 2 : 1),
|
|
0, kernel_heap);
|
|
assert(ss.libpath);
|
|
memcpy(ss.libpath + (ss.ld ? tlen : 0),
|
|
ss.libpath_env, plen);
|
|
ss.libpath[plen + (ss.ld ? tlen : 0)] = ':';
|
|
memcpy(ss.libpath + plen + 1 + (ss.ld ? tlen : 0),
|
|
imp, mlen + 1);
|
|
if (ss.ld) {
|
|
ASSERT(ss.pre); /* must be prepass */
|
|
prune_libpath(ss.libpath + tlen, ss.libpath);
|
|
}
|
|
ss.flags |= SS_LIBPATH_ALLOCED;
|
|
}
|
|
else {
|
|
/*
|
|
* ss.libpath is set to libpath string in loader
|
|
* section of the file
|
|
*/
|
|
if (!ss.ld)
|
|
ss.libpath = imp;
|
|
else {
|
|
/*
|
|
* prune libpath if loader domain is in use
|
|
*/
|
|
mlen = strlen(imp) + 1;
|
|
ss.libpath = xmalloc(mlen * 2, 0, kernel_heap);
|
|
assert(ss.libpath);
|
|
memcpy(ss.libpath + mlen, imp, mlen);
|
|
ASSERT(ss.pre); /* must be prepass */
|
|
prune_libpath(ss.libpath + mlen, ss.libpath);
|
|
ss.flags |= SS_LIBPATH_ALLOCED;
|
|
}
|
|
}
|
|
libpath = ss.libpath;
|
|
}
|
|
else {
|
|
/*
|
|
* this is not first time through code. ss.libpath is
|
|
* prepended to libpath information in this file.
|
|
*/
|
|
libpath = ss.libpath;
|
|
if (0 != strcmp(libpath,imp)){
|
|
/* concatenate default and module - only if
|
|
* if makes a difference */
|
|
plen = strlen(libpath);
|
|
mlen = strlen(imp);
|
|
if ((plen+mlen+2) > sizeof pathbuffer){
|
|
libpath = xmalloc(plen+mlen+2,0,kernel_heap);
|
|
assert(libpath);
|
|
libpathmalloced = 1;
|
|
}
|
|
else
|
|
libpath = pathbuffer; /* normal case */
|
|
memcpy(libpath,ss.libpath,plen);
|
|
libpath[plen] = ':';
|
|
memcpy(libpath+plen+1,imp,mlen+1);
|
|
}
|
|
}
|
|
for(;imp<tend&&*imp++;); /* find end of path */
|
|
imp++; /* skip of path part of filename - always null for
|
|
this entry */
|
|
imp++; /* move on to start of first file name entry */
|
|
for(i=0;i<depend;i++){
|
|
filename = imp;
|
|
for(;imp<tend&&*imp++;); /* find end of path part of
|
|
filename*/
|
|
for(;imp<tend&&*imp++;); /* find end of base part of
|
|
filename */
|
|
member = imp;
|
|
for(;imp<tend&&*imp++;); /* find char beyond null at
|
|
end of member */
|
|
if (imp > tend) { /* if imp == text, null at
|
|
end of memberi */
|
|
rc = ENOEXEC;
|
|
goto exit; /* is the very last charcter
|
|
in the file */
|
|
}
|
|
|
|
/* due to binder behavior, we must deal with completely null
|
|
* file name string to represent defered. (It was supposed
|
|
* to be a zero file index in the symbol entry!.
|
|
* We catch that here and iterate.
|
|
* BE CAREFUL about this code. The le_depends list is not
|
|
* one to one with the impid list - the le_depend list
|
|
* does NOT have an entry corresponding to the defered
|
|
* file name. To put a null entry in would cost testing
|
|
* for null all over the place.
|
|
* The actual test below depends on the fact that each string
|
|
* (path,base,member) is null terminated - so if all three are
|
|
* together only three characters long they MUST be three
|
|
* nulls.
|
|
*/
|
|
if (filename+3==imp){
|
|
if (ld_create_create == create)
|
|
lex.impid[i] = NULL;
|
|
continue;
|
|
}
|
|
|
|
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
* rest of loop skipped for null case - be very careful
|
|
* about loop counters. i counts entries in impid - but
|
|
* NOT entries in le_depend.
|
|
*/
|
|
|
|
lible = ld_getlib(sspointer,libpath,filename,member,create);
|
|
|
|
if (!lible){
|
|
/* error message only needed if this failure will
|
|
* really stop the load. The 'L' path is attempting
|
|
* to pre-relocate - if it fails we try again with
|
|
* a private load. The non-create path is verifying
|
|
* that the library can be used - again if this
|
|
* fails we will try to load a fresh copy
|
|
* The create call will fail if the needed library
|
|
* was read but has a bad format. The read call
|
|
* will fail if the library can't be found.
|
|
*/
|
|
if ((ss.type != 'L') && ((ld_create_create==create)
|
|
||(ld_create_read==create))){
|
|
char *p,*q;
|
|
char ebuf[128];
|
|
q = ebuf;
|
|
/* file name is in two parts - path and base
|
|
* each a null terminated string.
|
|
* -10 is to leave some room for member name */
|
|
for(p=filename;*p&&q<ebuf+sizeof ebuf-10;*q++=*p++);
|
|
if ( p != filename && *(p-1) != '/' )
|
|
*q++='/';
|
|
for(p++;*p&&q<ebuf+sizeof ebuf-10;*q++=*p++);
|
|
/* member immediately follows file name.
|
|
* it may be a null string. Right now,
|
|
* p points to null at end of file base name,
|
|
* q to char beyond last copied */
|
|
if (*++p)
|
|
*q++=' '; /* blank - then member */
|
|
for(;(*q++=*p++)&&q<ebuf+sizeof ebuf;);
|
|
*(q-1) = 0; /* if ran out of space */
|
|
ld_emess(L_ERROR_NOLIB,ebuf,NULL);
|
|
}
|
|
rc = u.u_error ? u.u_error : ENOEXEC;
|
|
goto exit;
|
|
}
|
|
|
|
if (ld_create_create == create) {
|
|
ASSERT(le->le_ndepend<le->le_maxdepend);
|
|
lex.impid[i] = lible;
|
|
le->le_depend[le->le_ndepend++] = lible;
|
|
lex.impid[i]->le_usecount += 1;
|
|
}
|
|
else if (ld_create_check == create) {
|
|
ASSERT(nextdep < le->le_maxdepend);
|
|
if (le->le_depend[nextdep] == lible) {
|
|
nextdep += 1;
|
|
}
|
|
else { /* verification failed */
|
|
if (ss.type != 'K' && !*member &&
|
|
!memcmp(filename,"/\0unix\0",7) ) {
|
|
/* The verification failed because of a new kernel
|
|
* name space. It is most often the case that
|
|
* updates to the kernel name space have no effect
|
|
* on the kernel symbols imported by this module.
|
|
* This is the case if this new version of the
|
|
* kernel name space depends on the version of the
|
|
* kernel name space this module depends on AND
|
|
* the new kernel name space does not redefine any
|
|
* symbols defined in the kernel name space this
|
|
* modules depends on. If both of these conditions
|
|
* are met, then we will say the old kernel name
|
|
* space can continue to be used. The loop below
|
|
* checks all this.
|
|
*/
|
|
struct loader_entry *tmple = lible;
|
|
|
|
/*
|
|
* verify should always pass if a loader domain
|
|
* was specified and kernel name space is in
|
|
* question.
|
|
*/
|
|
if (ss.ld)
|
|
nextdep += 1;
|
|
else {
|
|
while (tmple->le_ndepend) {
|
|
if (tmple == le->le_depend[nextdep]) {
|
|
/* This verifies use of this module */
|
|
break;
|
|
}
|
|
else if (tmple->le_flags & LE_REDEFINEEXP) {
|
|
/* can't use this module */
|
|
rc = ENOEXEC;
|
|
goto exit;
|
|
}
|
|
|
|
tmple = tmple->le_depend[0];
|
|
}
|
|
|
|
if (tmple == le->le_depend[nextdep])
|
|
nextdep += 1;
|
|
else {
|
|
/* If we get here it means the new kernel
|
|
* name space does not depend on the previous
|
|
* kernel name space. In this case verification
|
|
* must fail
|
|
*/
|
|
rc = ENOEXEC;
|
|
goto exit;
|
|
}
|
|
} /* else no domain */
|
|
} /* if kernel name space */
|
|
else { /* not kernel name space and verification failed */
|
|
rc = ENOEXEC;
|
|
goto exit;
|
|
}
|
|
} /* else verification failed */
|
|
} /* ld_create_check */
|
|
}
|
|
|
|
rc = 0;
|
|
exit:
|
|
if (libpathmalloced)
|
|
xmfree(libpath,kernel_heap);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* prune_libpath
|
|
* prune all nondirectory paths from the libpath string. two
|
|
* character pointers are passed to this routine:
|
|
* orig_libpath - original libpath string(null terminated).
|
|
* new_libpath - space allocated for new libpath string. this
|
|
* space is at least as big as orig_libpath.
|
|
*
|
|
* NOTE - this routine is required to release the global loader
|
|
* lock while making calls into filesystem code. therefore,
|
|
* this routine can only be called during the pre-pass.
|
|
*/
|
|
void
|
|
prune_libpath(char *orig_libpath, char *new_libpath)
|
|
{
|
|
|
|
char *p, *q, *start_tpath;
|
|
struct file *fp;
|
|
struct stat stat;
|
|
|
|
p = orig_libpath;
|
|
q = new_libpath;
|
|
|
|
/*
|
|
* must not be holding locks while performing open or
|
|
* stat operations
|
|
*/
|
|
unlockl(&kernel_anchor.la_lock);
|
|
|
|
/*
|
|
* parse original string
|
|
*/
|
|
while (*p) {
|
|
|
|
start_tpath = q;
|
|
fp = NULL;
|
|
|
|
/*
|
|
* libpath entries are separated by a ':', note that
|
|
* "::" is a valid entry which will result in searching
|
|
* the current directory.
|
|
*/
|
|
while (*p && *p != ':')
|
|
*q++ = *p++;
|
|
|
|
*q = '\0';
|
|
/*
|
|
* test if libpath entry is NULL(current directory) or
|
|
* a directory
|
|
*/
|
|
if ( (q == start_tpath) ||
|
|
( (q - start_tpath <= PATH_MAX) &&
|
|
(!fp_open(start_tpath, O_RDONLY, 0, 0, FP_SYS, &fp)) &&
|
|
(!fp_fstat(fp, &stat, sizeof(stat), FP_SYS)) &&
|
|
(S_ISDIR(stat.st_mode)) ) ) {
|
|
/*
|
|
* entry is a directory, add it to new string
|
|
*/
|
|
*q++=*p;
|
|
}
|
|
else {
|
|
/*
|
|
* do not include entry in pruned libpath string
|
|
*/
|
|
q = start_tpath;
|
|
}
|
|
|
|
/*
|
|
* advance to next entry in original libpath string
|
|
*/
|
|
if (*p)
|
|
p++;
|
|
|
|
/*
|
|
* close file
|
|
*/
|
|
if (fp)
|
|
fp_close(fp);
|
|
}
|
|
|
|
/*
|
|
* don't let new libpath end with a NULL path unless original
|
|
* libpath also ended with a NULL path.
|
|
*/
|
|
if ( (p != orig_libpath) && (*--p != ':') &&
|
|
(q != new_libpath) && (*--q == ':'))
|
|
*q = '\0';
|
|
|
|
/*
|
|
* be sure to reacquire lock
|
|
*/
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);
|
|
}
|