565 lines
15 KiB
C
565 lines
15 KiB
C
static char sccsid[] = "@(#)25 1.55.1.2 src/bos/kernel/ldr/ld_execld.c, sysldr, bos41J, 9513A_all 3/28/95 11:28:19";
|
|
/*
|
|
* COMPONENT_NAME: (SYSLDR) Program Management
|
|
*
|
|
* FUNCTIONS: gettext(), ld_execload()
|
|
*
|
|
* 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/adspace.h>
|
|
#include <sys/fp_io.h>
|
|
#include <sys/ldr.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/pseg.h>
|
|
#include <sys/seg.h>
|
|
#include <sys/shm.h>
|
|
#include <sys/syspest.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/user.h>
|
|
#include <sys/vmuser.h>
|
|
#include <sys/xcoff.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/lockl.h>
|
|
#include "ld_data.h"
|
|
|
|
|
|
extern void bcopy();
|
|
extern void bzero();
|
|
|
|
/*
|
|
* N.B. one could add text table logic to remember that a file
|
|
* has already been read into a text segment - since the loader
|
|
* DOES NOT modify the text segment. Although we do some work to
|
|
* avoid reading unneeded parts of the file - all parts of the file
|
|
* which are used are read into the same offsets in the text segment
|
|
* that they have in the file.
|
|
* Once gettext is done the remainder of the loader does not care if
|
|
* the text segment is mapped or copied
|
|
*
|
|
* filesize is a location in which the size of the file read or
|
|
* mapped is returned to help with later sanity checks
|
|
*/
|
|
|
|
static int
|
|
gettext(
|
|
struct sharedstuff *sspointer,
|
|
struct loader_entry_extension *lexpointer,
|
|
struct file *fp)
|
|
{
|
|
int i,j,k;
|
|
int rc;
|
|
|
|
|
|
/* initialize text segment
|
|
* maps file if possible, otherwise makes sure the text region
|
|
* is backed with computational memory
|
|
*/
|
|
if (rc=ld_cleartext(sspointer,(caddr_t)lex.h,fp)) return rc;
|
|
|
|
if (ss.la->la_flags & LA_TEXTALLOC){
|
|
|
|
/* textread reads the right amount at textorg
|
|
* it computes lex.filehdr, lex.aouthdr, lex.scnhdr
|
|
* Last parameter is a flags field
|
|
*/
|
|
if ( rc = ld_textread(sspointer,lexpointer,fp,NULL,(caddr_t)lex.h,
|
|
SEGSIZE,NULL,0) )
|
|
return rc;
|
|
ld_readprotecttext((caddr_t)lex.h);
|
|
}
|
|
else {
|
|
/* text is mapped - so we need to set lex.filesize to the
|
|
* size of the file
|
|
*/
|
|
struct stat stat;
|
|
if (rc=fp_fstat(fp,&stat,sizeof stat,FP_SYS)) return rc;
|
|
lex.filesize = stat.st_size;
|
|
}
|
|
|
|
if ( rc=ld_sanity(sspointer, lexpointer)){
|
|
/* if gettext fails, no le is made so we must
|
|
* do the shmdt now - ld_usecount won't do it */
|
|
if (!(ss.la->la_flags & LA_TEXTALLOC))
|
|
fp_shmdt(fp,SHM_RDONLY);
|
|
}
|
|
|
|
/* Check to see if the user has marked the executable to privately
|
|
* load all libraries
|
|
*/
|
|
if (strncmp(hdr.a.o_modtype,"PL",2) == 0)
|
|
ss.flags |= SS_PRIVATELOAD;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* NAME: ld_get_domain(*sspointer, *dompath, *lexpointer)
|
|
*
|
|
* FUNCTION: This routine will determine if a process should use
|
|
* a loader domain. If it should use a loader domain,
|
|
* then global as well as process specific data structures
|
|
* are initialized or modified.
|
|
*
|
|
* Upon entry to this routine, the global loader lock
|
|
* must be held.
|
|
*
|
|
* PARAMETERS: sspointer - shared stuff(global variables)
|
|
* dompath - pointer to domapn path in LIBPATH env
|
|
* lexpointer - loader entry extension
|
|
*
|
|
* MODIFIED Process anchor will point to loader domain. Shared
|
|
* DATA Stuff will point to loader domain. A new loader
|
|
* domain may be added to global list. Use count in
|
|
* loader domain will be incremented.
|
|
*
|
|
* RETURN: 0 Successful
|
|
* !0 Unsuccessful
|
|
*
|
|
*/
|
|
int
|
|
ld_get_domain(struct sharedstuff *sspointer,
|
|
char *dompath,
|
|
struct loader_entry_extension *lexpointer)
|
|
{
|
|
char *p, *q;
|
|
char path_buf[PATH_MAX];
|
|
struct file *fp;
|
|
struct stat stat;
|
|
struct loader_domain *ld, *back_ld;
|
|
struct domain_entry *de;
|
|
struct domain_altname *da;
|
|
uint fh;
|
|
int can_write = 0;
|
|
|
|
/* initialize loader domain field of loader anchor and ss */
|
|
ss.la->la_domain = ss.ld = NULL;
|
|
|
|
/*
|
|
* determine if a loader domain is specified. check
|
|
* out the libpath information.
|
|
*/
|
|
if (dompath && *dompath)
|
|
p = dompath;
|
|
else
|
|
p = (char *)lex.ldhdr+lex.ldhdr->l_impoff;
|
|
q = path_buf;
|
|
while(*p && *p != ':' && ((q-path_buf) < PATH_MAX))
|
|
*q++ = *p++;
|
|
*q = '\0';
|
|
|
|
/* dompain path is > PATH_MAX, we can't open */
|
|
if ((q-path_buf) == PATH_MAX)
|
|
return(0);
|
|
|
|
/* no libpath information (not very likely/common) */
|
|
if (q == path_buf)
|
|
return(0);
|
|
|
|
/*
|
|
* path_buf now contains the first pathname in the libpath
|
|
* information. if this path represents a regular file which
|
|
* can at least be read by this process, then it will be used
|
|
* as a loader domain.
|
|
*/
|
|
unlockl(&kernel_anchor.la_lock);
|
|
if (fp_open(path_buf, O_RDONLY, 0, 0, FP_SYS, &fp)) {
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);
|
|
return(0); /* unable to open for reading */
|
|
}
|
|
|
|
if (fp_fstat(fp, &stat, sizeof(stat), FP_SYS)) {
|
|
fp_close(fp);
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);
|
|
return(0); /* unable to stat */
|
|
}
|
|
|
|
if (!S_ISREG(stat.st_mode)) {
|
|
fp_close(fp);
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);
|
|
return(0); /* not a regular file */
|
|
}
|
|
|
|
|
|
/*
|
|
* check the processes access to the file. only if the
|
|
* process can write to the file, can it add members
|
|
* to the associated loader domain.
|
|
*/
|
|
if (!fp_accessx(fp, W_OK, ACC_SELF))
|
|
can_write++;
|
|
|
|
|
|
/*
|
|
* fp is a file pointer to a file that is associated with
|
|
* the loader domain. search the list of existing loader
|
|
* domains for a match.
|
|
*/
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);
|
|
ld = lib.la.la_domain;
|
|
fh = FPtoFH(fp);
|
|
while (ld) {
|
|
if (FPtoFH(ld->ld_fp) == fh)
|
|
break;
|
|
else
|
|
ld = ld->ld_next;
|
|
}
|
|
|
|
if (ld) { /* a match was found */
|
|
unlockl(&kernel_anchor.la_lock);
|
|
fp_close(fp);
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);
|
|
}
|
|
|
|
/*
|
|
* no match was found. if process has sufficient permission,
|
|
* create a new domain.
|
|
*/
|
|
else if (can_write) {
|
|
uint alloc_size;
|
|
|
|
/*
|
|
* Allocate space for the loader domain.
|
|
*/
|
|
alloc_size = (q-path_buf) + 1 +
|
|
sizeof(struct loader_domain);
|
|
if (!(ld = xmalloc(alloc_size, 2, lib.la.la_data_heap))) {
|
|
/* not enough space to create a domain */
|
|
unlockl(&kernel_anchor.la_lock);
|
|
fp_close(fp);
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);
|
|
return(ENOMEM);
|
|
}
|
|
|
|
/*
|
|
* Initialize fields in the domain
|
|
*/
|
|
bzero(ld, alloc_size);
|
|
ld->ld_name = (char *)ld + sizeof(struct loader_domain);
|
|
strcpy(ld->ld_name, path_buf);
|
|
ld->ld_fp = fp;
|
|
|
|
/* add new domain to global list */
|
|
ld->ld_next = lib.la.la_domain;
|
|
lib.la.la_domain = ld;
|
|
}
|
|
|
|
else { /* insufficient permission to create a new domain */
|
|
unlockl(&kernel_anchor.la_lock);
|
|
fp_close(fp);
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);
|
|
ld_emess(L_ERROR_DOMCREAT, path_buf, NULL);
|
|
return(EACCES);
|
|
}
|
|
|
|
/*
|
|
* set flag in loader anchor to indicate process can add entries
|
|
* to the loader domain
|
|
*/
|
|
if (can_write)
|
|
ss.la->la_flags |= LA_DOMAIN_WRITE;
|
|
|
|
/*
|
|
* update use count in global domain
|
|
*/
|
|
ld->ld_usecount++;
|
|
ss.la->la_domain = ld;
|
|
ss.ld = ld;
|
|
(void)ld_pathinit(sspointer);
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
int (*
|
|
ld_execload(
|
|
struct file *fp,
|
|
char *libpath,
|
|
char *dompath)
|
|
)()
|
|
{
|
|
|
|
struct sharedstuff sharedstuff,*sspointer;
|
|
/*N.B. see ldr.h for use of lexpointer*/
|
|
struct loader_entry_extension loader_entry_extension,*lexpointer;
|
|
struct loader_entry *le;
|
|
int rc;
|
|
int (*entrypoint)();
|
|
int i;
|
|
uint temp;
|
|
caddr_t textorg;
|
|
int waslongjmpx;
|
|
label_t jbuf;
|
|
|
|
|
|
/* absolutely first - set up shared stuff addressability
|
|
* N.B. ss is really (*sspointer) !
|
|
*/
|
|
sspointer = &sharedstuff;
|
|
bzero(sspointer,sizeof sharedstuff);
|
|
lexpointer = &loader_entry_extension;
|
|
bzero(lexpointer,sizeof loader_entry_extension);
|
|
|
|
u.u_error = 0;
|
|
/* we load into the current data segment, which has already been
|
|
* cleared. However, the code distinquishes between the location
|
|
* of the data and its load origin since these same services are
|
|
* used to load data in the shared library. Shared library data
|
|
* is loaded into the library but relocated to its target location
|
|
* in the data segment.
|
|
*/
|
|
ss.type = 'E'; /* mark this as execload */
|
|
lex.h = (void *)TEXTORG; /* text goes hear */
|
|
|
|
|
|
ss.end_of_new = NULL; /* for execload we are starting empty */
|
|
ss.libpath_env = libpath;
|
|
ss.maxbreak = (char *)DATAORG + U.U_dmax;
|
|
U.U_dsize = 0;
|
|
|
|
/* set up to catch I/O errors in mapped files */
|
|
waslongjmpx = rc = setjmpx(&jbuf);
|
|
if (rc) {
|
|
struct loader_entry *prevle;
|
|
|
|
/* Make sure that we have the kernel lock at this point */
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);
|
|
|
|
/* make sure everything is addressable */
|
|
if (ss.flags & SS_LD_ADDRESSLIB) {
|
|
/*
|
|
* In this case we know ld_addresslib
|
|
* was called, but we still can't be sure
|
|
* the segment we need is addressable. An
|
|
* easy way to do this is to cleanup and
|
|
* then call ld_addresslib again.
|
|
*/
|
|
ld_cleanup(sspointer);
|
|
ld_addresslib(sspointer);
|
|
}
|
|
else
|
|
ld_addresslib(sspointer);
|
|
|
|
/*
|
|
* Look for a library entry marked IOPENDING by this thread.
|
|
* If we find one, then it must be backed out.
|
|
*/
|
|
le = lib.la.la_loadlist;
|
|
prevle = NULL;
|
|
while (le) {
|
|
if ((le->le_flags & LE_IOPENDING) &&
|
|
(le->le_tid == curthread->t_tid)) {
|
|
/* found one */
|
|
if (prevle)
|
|
prevle->le_next = le->le_next;
|
|
else
|
|
lib.la.la_loadlist = le->le_next;
|
|
|
|
xmfree(le,lib.la.la_data_heap);
|
|
/* wakeup anyone waiting on entry */
|
|
e_wakeup(&ld_loader_read);
|
|
break;
|
|
}
|
|
prevle = le;
|
|
le = le->le_next;
|
|
}
|
|
unlockl(&kernel_anchor.la_lock);
|
|
|
|
ld_emess(L_ERROR_SYSTEM,"EFAULT",NULL);
|
|
goto exitrc; /* fault occured - such as I/O error in
|
|
mapped file */
|
|
}
|
|
|
|
/* for now compute the address of the anchor this way for historical
|
|
* reasons - once things stabalize we will move the loader area into
|
|
* the u_block declare and fix this statement
|
|
*/
|
|
ss.la = (struct loader_anchor *)(U.U_loader) ;
|
|
ss.la->la_loadlist = NULL;
|
|
ss.la->la_execle = NULL;
|
|
ss.la->la_text_heap = (caddr_t)(ss.la+1);
|
|
ss.la->la_data_heap = (caddr_t)(ss.la+1);
|
|
ss.la->la_lock = LOCK_AVAIL;
|
|
ss.la->la_flags = LA_UNSURE;
|
|
ss.la->minbreak = (caddr_t)DATAORG;
|
|
ss.la->sbreak = (caddr_t)DATAORG; /* data will be allocated here */
|
|
ss.la->lastbreak = (caddr_t)DATAORG;
|
|
ss.la->la_dinuse = NULL; /* user data allocated */
|
|
ss.la->la_emess = NULL; /* error message chain */
|
|
ss.la->la_ovfl_srval = NULLSEGVAL;
|
|
ss.la->la_save_srval = NULLSEGVAL;
|
|
ss.la->la_domain = NULL;
|
|
|
|
/* get the executable into the text segment - either map it or
|
|
* read what we need
|
|
*/
|
|
if (rc=gettext(sspointer,lexpointer,fp)) {
|
|
if(rc == ENOEXEC)
|
|
ld_emess(L_ERROR_FORMAT,NULL,NULL);
|
|
goto exitrc;
|
|
}
|
|
|
|
|
|
/* initialize loader entry loadlist */
|
|
{
|
|
uint size;
|
|
/* -1 because sizeof(struct loader_entry) contains space for
|
|
* one slot
|
|
*/
|
|
size = sizeof(struct loader_entry) +
|
|
sizeof(struct loader_entry*)*(lex.nimpid-1);
|
|
le = (struct loader_entry *) ld_ualloc(sspointer, size );
|
|
} /* end of use of size */
|
|
lex.le = le;
|
|
ss.la->la_loadlist = le;
|
|
ss.la->la_execle = le;
|
|
le->le_next = NULL;
|
|
le->le_usecount = 1;
|
|
le->le_loadcount = 0;
|
|
le->le_flags = LE_TEXT|LE_DATA|LE_THISLOAD|LE_EXECLE;
|
|
le->le_fp = fp;
|
|
fp_hold(fp);
|
|
le->le_file = (void *)lex.h; /* the execfile always goes here */
|
|
le->le_filesize = lex.filesize;
|
|
le->le_data = NULL;
|
|
le->le_datasize = 0;
|
|
le->le_exports = NULL;
|
|
le->le_lex = lexpointer;
|
|
le->le_defered = NULL;
|
|
le->le_filename = U.U_comm;
|
|
le->le_ndepend=0;
|
|
le->le_maxdepend=lex.nimpid;
|
|
|
|
|
|
|
|
|
|
/* librarys will find and if needed instantiate all the libraries
|
|
* used by this program. In the process, it may allocate load list
|
|
* entries and chain them on the load anchor of the segment
|
|
* liblist is a vector parallel to the list of library file in
|
|
* the loader section. Each entry points to a symbol resolution
|
|
* hash table
|
|
*/
|
|
|
|
|
|
lockl(&kernel_anchor.la_lock,LOCK_SHORT);/*this form does not fail*/
|
|
|
|
/* establish address of library segment in privlege key
|
|
*/
|
|
ld_addresslib(sspointer);
|
|
|
|
/* check for/establish loader domain */
|
|
if (rc = ld_get_domain(sspointer, dompath, lexpointer)){
|
|
unlockl(&kernel_anchor.la_lock);
|
|
goto exitrc;
|
|
}
|
|
|
|
/* before paging is on, we try to keep the shared library
|
|
* as small as possible - part of this is done be purging
|
|
* all zero use count things before each load. This is
|
|
* a poor performer but is the best that can be easily done
|
|
* for space.
|
|
* We also try to cleanup if the shared library segment is
|
|
* getting full. When a request for space in the library
|
|
* segment fails, shlibseg_full is set.
|
|
*/
|
|
|
|
if (ps_not_defined || shlibseg_full){
|
|
struct sharedstuff sharedstuff,*sspointer;
|
|
sspointer = &sharedstuff;
|
|
bzero(sspointer,sizeof sharedstuff);
|
|
ss.la=&(lib.la);
|
|
ss.type='L';
|
|
ld_unload(sspointer);
|
|
if (shlibseg_full)
|
|
shlibseg_full = 0;
|
|
}
|
|
|
|
rc = ld_libraries(sspointer,le,ld_create_initialcall);
|
|
|
|
ld_freelex(lib.la.la_loadlist,NULL);
|
|
|
|
unlockl(&kernel_anchor.la_lock);
|
|
|
|
if (rc) goto exitrc;
|
|
|
|
/* assign data locations for all private loaded programs.
|
|
* Note that the execloaded program is last on the list - so it
|
|
* gets the largest address
|
|
*/
|
|
|
|
if (rc=ld_assigndata(sspointer)) goto exitrc;
|
|
|
|
/* resolve processes the symbol table
|
|
* it computes a vector of relocation factors for each symbol in
|
|
* the table and computes the number of exports (if any)
|
|
* If symbols are unresolved it relocates them to 0 or to a trap
|
|
* depending on type and constructs a list of all such symbols
|
|
*/
|
|
|
|
if (rc=ld_resolve(sspointer)) goto exitrc;
|
|
|
|
/* map or copy data areas */
|
|
|
|
if (rc=ld_mapdata(sspointer)) goto exitrc;
|
|
|
|
/* relocate processes the rld information, relocating each symbol*/
|
|
|
|
if (rc=ld_relocate(sspointer)) goto exitrc;
|
|
|
|
/* Store start of the text and size of the text and data sections
|
|
* (in bytes) in the new u_block
|
|
*/
|
|
U.U_tstart = le->le_file + hdr.s[hdr.a.o_sntext-1].s_scnptr;
|
|
U.U_tsize = hdr.a.tsize;
|
|
if (rc=BRK(ss.la->sbreak))
|
|
goto exitrc;
|
|
ss.la->minbreak = ss.la->sbreak;
|
|
|
|
/* return the entry point */
|
|
|
|
if (hdr.a.o_entry == -1 )
|
|
rc = ENOEXEC;
|
|
else {
|
|
entrypoint = (int(*)())
|
|
(hdr.a.o_entry + (hdr.a.o_snentry == hdr.a.o_sntext ?
|
|
lex.textreloc : lex.datareloc));
|
|
|
|
rc = 0;
|
|
}
|
|
|
|
exitrc:
|
|
if (! waslongjmpx)
|
|
clrjmpx(&jbuf);
|
|
|
|
ld_freelex(ss.la->la_loadlist,NULL);
|
|
ld_pathclear(sspointer);
|
|
/* platform specific cleanup - probably address space manipulation*/
|
|
ld_cleanup(sspointer);
|
|
u.u_error = rc;
|
|
if (rc) {
|
|
char errno[32];
|
|
sprintf(errno,"%d",rc);
|
|
ld_emess(L_ERROR_ERRNO,errno,NULL);
|
|
return NULL;
|
|
}
|
|
else {
|
|
ss.la->la_flags &= ~LA_UNSURE;
|
|
return entrypoint;
|
|
}
|
|
}
|