static char sccsid[] = "@(#)26 1.78.1.4 src/bos/kernel/ldr/ld_files.c, sysldr, bos41J, 9520A_all 5/16/95 16:45:47"; /* * COMPONENT_NAME: (SYSLDR) Program Management * * FUNCTIONS: ld_assigndata1(), ld_assigndata(), oldar(), newar(), * ld_textread(), ld_sanity(), ld_freelex(), ld_pathinit(), * ld_pathclear(),ld_pathopen(), ld_fptopath(), knlist(), * ld_getinfo(), ld_getinfo_user() * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ld_data.h" #include "ff_alloc.h" #include "ld_fflist.h" int firstfitsize = 30 * 1024 * 1024; int firstfitmax = 300 * 1024; FF_DECLARE(pinned_fflist); FF_DECLARE(unpinned_fflist); #define memcpy(t,s,l) bcopy(s,t,l) /* compute data locations for exec'd program. This is special * because it must come at the end. This code assumes sbreak * had been updated. */ static int ld_assigndataexec( struct sharedstuff *sspointer, struct loader_entry *le, ulong minbreak) { struct loader_entry_extension *lexpointer; ulong align,temp,dbreak; int rc; lexpointer = le->le_lex; ASSERT(lexpointer); ASSERT(!le->le_data); /* round to next page boundary */ dbreak = minbreak = PAGEUP(minbreak); /* If alignment of data in memory is as required we can page map * copy it. To do this, we must compute an origin which has the * same page offset as the data in memory and which is on a new page. * Start is the location in the first page where data must start. * If we pagemap, it is the page offset of the data in the file. * If we copy, it is the offset at which the ldr relocated (vaddr) * masked by align. */ align=(1<le_file + hdr.s[lex.data].s_scnptr - hdr.s[lex.data].s_vaddr) & align & (PAGESIZE-1) ) == 0){ le->le_flags |= LE_DATAMAPPED; minbreak += (ulong)(le->le_file + hdr.s[lex.data].s_scnptr)&(PAGESIZE-1); } /* general fix up of address is to take the difference of where the * code wants to be and the current break mod the alignment requirement * and add that to the break. Note that taking mod by anding the * mask is correct mathematically - that is the result is always * positive even if the difference is negative * N.B. we do this EVEN when we have already page aligned above - this * will only make a difference if the required alignment is STRONGER * than PAGESIZE */ minbreak += align & (hdr.s[lex.data].s_vaddr - minbreak); if (hdr.a.o_maxdata) { struct dinuse *dinuse; /* adjust break before changing minbreak */ if (BRK(dbreak)) return ENOMEM; U.U_sdsize = dbreak - PRIVORG; /* create additional segments required by the program */ if (rc = ld_crsegs(sspointer,hdr.a.o_maxdata,&minbreak)) return rc; /* * Mark the chunk from dbreak to the end of the segment * as being in use. This forces load(s) into segment 3. */ dinuse = ld_ualloc(sspointer,sizeof(struct dinuse)); dinuse->next = ss.la->la_dinuse; ss.la->la_dinuse = dinuse; dinuse->start = dbreak; dinuse->length = (ulong)(SEGSIZE - (dbreak - PRIVORG)); } else U.U_sdsize = 0; le->le_data = (void *)(minbreak); lex.datareloc = (uint)minbreak - hdr.s[lex.data].s_vaddr; lex.bssreloc = (uint)minbreak + hdr.a.o_dsize - hdr.s[lex.bss].s_vaddr; temp=hdr.a.o_dsize + hdr.a.o_bsize; le->le_datasize = temp; /* set break here as a performance optimization - it gets set * for good in mapdata1 if necessary */ return BRK(minbreak+temp) ? ENOMEM : 0; } /* compute pirvate area data locations for execloaded, loaded, or * private libarary loaded programs, but NOT the exec'd program. */ static int ld_assigndata1( struct sharedstuff *sspointer, struct loader_entry *le) { struct loader_entry_extension *lexpointer; ulong align,temp,size,start; int rc; lexpointer = le->le_lex; ASSERT(lexpointer); if ( le->le_flags & LE_DATAEXISTS) { /* just compute relocation factors in case needed */ lex.datareloc = (uint)le->le_data - hdr.s[lex.data].s_vaddr; lex.bssreloc = (uint)le->le_data + hdr.a.o_dsize - hdr.s[lex.bss].s_vaddr; return 0; } ASSERT(!le->le_data); /* If alignment of data in memory is as required we can page map * copy it. To do this, we must compute an origin which has the * same page offset as the data in memory and which is on a new page. * Start is the location in the first page where data must start. * If we pagemap, it is the page offset of the data in the file. * If we copy, it is the offset at which the ldr relocated (vaddr) * masked by align. Note that we only support alignments up to a page * here. To do better, allocd must be extended to support alignment. */ align=(1<le_file + hdr.s[lex.data].s_scnptr - hdr.s[lex.data].s_vaddr) & align & (PAGESIZE-1)) == 0){ le->le_flags |= LE_DATAMAPPED; start = (ulong)(le->le_file + hdr.s[lex.data].s_scnptr)&(PAGESIZE-1); } else start = (ulong)(hdr.s[lex.data].s_vaddr)&align; /* now compute the size required for this data area. Size * is an integral number of pages. Take alignment into account. */ le->le_datasize = hdr.a.o_dsize + hdr.a.o_bsize; size = PAGEUP(start+hdr.a.o_dsize + hdr.a.o_bsize); #ifdef notyet if (ss.load_addr && ss.type == 'C') temp = ss.load_addr; else #endif if (!(temp = ld_allocd(sspointer,size))) return ENOMEM; le->le_data = (void *)(temp+start); lex.datareloc = (uint)(le->le_data) - hdr.s[lex.data].s_vaddr; lex.bssreloc = (uint)(le->le_data) + hdr.a.o_dsize - hdr.s[lex.bss].s_vaddr; return 0; } int ld_assigndata( struct sharedstuff *sspointer) { struct loader_entry *le,*execle; int rc; ulong minbreak; minbreak=DATAORG; execle=NULL; for(le=ss.la->la_loadlist;le != ss.end_of_new;le=le->le_next){ /* USEASIS means this data is a copy of pre-relocated data * thus its location has already been determined*/ if ( (le->le_flags & (LE_DATA|LE_USEASIS|LE_EXECLE)) == LE_DATA){ if (rc = ld_assigndata1(sspointer,le)) return rc; } else if ((le->le_flags & LE_EXECLE)) execle = le; /* DATAEXISTS means this data is in the library */ if ((le->le_flags &(LE_DATA|LE_DATAEXISTS|LE_USEASIS))==LE_DATA) minbreak = MAX(minbreak,(ulong)le->le_data+le->le_datasize); if (le->le_flags & LE_TEXT) minbreak = MAX(minbreak,(ulong)le->le_file+le->le_filesize); } if (execle) return ld_assigndataexec(sspointer,execle,minbreak); return 0; } static ulong oldar( struct sharedstuff *sspointer, struct file *fp, char *member) { /* TEMPORARY - old style ar.h */ /* ar.h 5.1 - 86/12/09 - 06:03:39 */ /* COMMON ARCHIVE FORMAT * * ARCHIVE File Organization: * _______________________________________________ * |__________ARCHIVE_MAGIC_STRING_______________| * |__________ARCHIVE_FILE_MEMBER_1______________| * | | * | Archive File Header "ar_hdr" | * |.............................................| * | Member Contents | * | 1. External symbol directory | * | 2. Text file | * |_____________________________________________| * |________ARCHIVE_FILE_MEMBER_2________________| * | "ar_hdr" | * |.............................................| * | Member Contents (.o or text file) | * |_____________________________________________| * | . . . | * | . . . | * | . . . | * |_____________________________________________| * |________ARCHIVE_FILE_MEMBER_n________________| * | "ar_hdr" | * |.............................................| * | Member Contents | * |_____________________________________________| * */ #define old_ARMAG "!\n" #define old_SARMAG 8 #define old_ARFMAG "`\n" struct ar_hdr /* archive file member header - printable ascii */ { char ar_name[16]; /* file member name - `/' terminated */ char ar_date[12]; /* file member date - decimal */ char ar_uid[6]; /* file member user id - decimal */ char ar_gid[6]; /* file member group id - decimal */ char ar_mode[8]; /* file member mode - octal */ char ar_size[10]; /* file member size - decimal */ char ar_fmag[2]; /* ARFMAG - string to end header */ }; struct ar_hdr ar; char armag[old_SARMAG]; ulong incr; ulong memberoffset,member_name_length; int count; member_name_length = strlen(member); (void)fp_lseek(fp, 0, SEEK_SET); if (FP_READ(fp, armag, old_SARMAG, 0, UIO_SYSSPACE, &count) || (count != old_SARMAG)) return 0; if (0 != memcmp(armag,old_ARMAG,8) ) return 0; memberoffset = 8; while(1) { (void)fp_lseek(fp,memberoffset,SEEK_SET); if (FP_READ(fp, &ar, sizeof ar, 0, UIO_SYSSPACE, &count) || (count != sizeof ar)) return 0; if ( (ar.ar_name[member_name_length] == '/' ) && (memcmp(member,ar.ar_name,member_name_length) == 0)) break; incr = atoi(ar.ar_size); incr += incr & 1; /* round up to even number */ memberoffset += sizeof ar + incr; } memberoffset += sizeof ar; return memberoffset; } /* * NAME: newar * * FUNCTION: Find the offset of a given member in an archive file * * EXECUTION ENVIRONMENT: * * NOTES: * * The member table looks as follows: (offset to member table * is fl_memoff) * * fl_hdr * +------------------------------------+ * | +---------------+ | * | | fl_memoff |-------------+ * | +---------------+ | | * +------------------------------------+ | * | * | * | * +------------------------------------------------------+ * | * | MEMBER TABLE STRUCTURE * | * | +------------------------------------+ * +----->| ar.hdr | * +------------------------------------+ * | number of members (n) | * +------------------------------------+ * | offset to member 1 (12 characters) | * +------------------------------------+ * | offset to member 2 (12 characters) | * +------------------------------------+ * | offset to member 3 (12 characters) | * +------------------------------------+ * ....... * ....... * +------------------------------------+ * | offset to member n (12 characters) | * +------------------------------------+ * | name of member 1 (ends with a null)| * +------------------------------------+ * | name of member 2 (ends with a null)| * +------------------------------------+ * | name of member 3 (ends with a null)| * +------------------------------------+ * ....... * ....... * +------------------------------------+ * | name of member n (ends with a null)| * +------------------------------------+ * * RECOVERY OPERATION: * None * * DATA STRUCTURES: * None * * RETURNS: * 0 - member not found in archive file * offset to member in archive file. */ static ulong newar( struct sharedstuff *sspointer, struct file *fp, char *member) { #define FIELD_LEN_MEMBER_ENTRY 12 struct fl_hdr fl_hdr; struct ar_hdr ar_hdr; struct member_offset_entry { char offset_ascii[FIELD_LEN_MEMBER_ENTRY]; }; struct member_offset_entry *entry_offset_addr; ulong memberoffset,member_name_length,tableoffset; ulong size; char *table; /* local copy of member table */ char *mnames; /* pointer to member name */ char *start_mnames; /* pointer to start of 'member names' section in member table */ uint nummem; /* number of members */ ulong rc = 0; uint len; int count; /* count from fp_read() */ member_name_length = strlen(member); fp_lseek(fp,0,SEEK_SET); (void)FP_READ(fp, &fl_hdr, sizeof fl_hdr, 0, UIO_SYSSPACE, &count); if (count != sizeof fl_hdr) return 0; if (0 != memcmp(fl_hdr.fl_magic,AIAMAG,sizeof fl_hdr.fl_magic)) return 0; /* get to member table */ if ( ! (tableoffset = atoi(fl_hdr.fl_memoff)) ) return 0; /* * read in ar.hdr to get the size of the member table * and read in member table. (see NOTES section for a * description). */ (void)fp_lseek(fp,tableoffset,SEEK_SET); (void)FP_READ(fp, &ar_hdr, sizeof ar_hdr, 0, UIO_SYSSPACE, &count); if (count != sizeof ar_hdr) return 0; size = atoi(ar_hdr.ar_size); if (! (table=xmalloc(size+1,0,kernel_heap)) ) return 0; (void)FP_READ(fp, table, size, 0, UIO_SYSSPACE, &count); if (count != size) goto errexit; /* * since each member name ends with a 'null' (i.e. 0), * setting the last byte to null will guarrantee a * name match. */ ((char *)table)[size] = 0; memberoffset = 0; nummem = atoi(table); /* set number of members */ /* * get the starting addresses of member offset entries, and * member name entries. */ entry_offset_addr = (struct member_offset_entry *) (table + sizeof(struct member_offset_entry)); start_mnames = ((char *) entry_offset_addr) + (nummem * sizeof(struct member_offset_entry)); /* * now need to find the member name in the 'member names' * section and its relative offset in the ar file. * * NOTE: When entry_offset_addr = start_mames then all the * member names have been processed. * * When the offset for a member is null, that * member is not in the archive file. * * If start_mnames is is too small, because nummen was absurd, * the loop below terminates at once and no harm is done. * The check for mnames protects against start_mnames too big * and not enough strings in the name part of the table. */ for(mnames = start_mnames; (char *) entry_offset_addr < start_mnames && mnames < table+size; mnames += len + 1) { len = strlen(mnames); if ( len == member_name_length && 0 == memcmp(member,mnames,len) ) { memberoffset = atoi(entry_offset_addr->offset_ascii); break; } entry_offset_addr++; } /* * if the member was found, then go ahead and read the * ar.hdr for that member. */ if(memberoffset) { (void)fp_lseek(fp,memberoffset,SEEK_SET); (void)FP_READ(fp, &ar_hdr, sizeof ar_hdr, 0, UIO_SYSSPACE, &count); if (count != sizeof ar_hdr) goto errexit; len = atoi(ar_hdr.ar_namlen); len = len + (len & 1); /* round up to even */ memberoffset = memberoffset + sizeof ar_hdr + len; } rc = memberoffset; errexit: xmfree(table,kernel_heap); return rc; } /* this routine reads text into memory. It is used whenever the text cannot be mapped */ int ld_textread( struct sharedstuff *sspointer, struct loader_entry_extension *lexpointer, struct file *fp, char *member, char *origin, uint length, heapaddr_t heap, uint flags) { uint hdrsize,startofrest,whdrsize; uint lix,tix,dix; /* must be unsigned - see comments below */ int rc; int i,j,k; uint bssroom,filesize; uint memberoffset; struct myxcoffhdr temphdr; char *loader_section; lex.heap = heap; /* if this is an archive, find the right member */ if (member && *member) { struct ar_hdr ar; char armag[SAIAMAG]; ulong incr; (void)fp_lseek(fp,0,SEEK_SET); (void)FP_READ(fp, armag, SAIAMAG, 0, UIO_SYSSPACE, &rc); if (rc != SAIAMAG ) memberoffset = 0; else if (0 != memcmp(armag,AIAMAG,8) ) memberoffset = oldar(sspointer,fp,member); else memberoffset = newar(sspointer,fp,member); if (!memberoffset) { if (ss.type != 'L') ld_emess(L_ERROR_MEMBER,member,NULL); return ENOENT; } } else memberoffset = 0; lex.h = &temphdr; /* N.B. this so hdr. names the local storage */ /* read in the file header.*/ (void)fp_lseek(fp,memberoffset,SEEK_SET); /* read the headers into local storage */ (void)FP_READ(fp, &temphdr, sizeof hdr, 0, UIO_SYSSPACE, &rc); hdrsize = sizeof(struct filehdr) + hdr.f.f_opthdr + hdr.f.f_nscns*sizeof(struct scnhdr); if ( rc < hdrsize || hdr.f.f_opthdr > sizeof(struct aouthdr)) return ENOEXEC; if (hdr.f.f_opthdr != sizeof(struct aouthdr)){ /* * the following code contains a major KLUDGE. We needed to make the * aouthdr larger. In the transition, we wanted old modules to work. * The approach is to supress mapping them and then fix up here. * The fix up involves modifying the header so it appears to be in the * new format. This moves the section tables and everything past away from * the header to leave space. This code only works for modules whose * header is too small. It should be left in in case we need to do this * again. There is essentially no cost in the normal path * DBX may have trouble with this. * * hdrsize is size of header in the file. * sizeof hdr is size of current header * startofrest is the offset in memory of the rest of the data. This * equals hdrsize in the normal case, is larger otherwise. */ uint aoutdelta,restdelta; uint i,j,k; char *p; aoutdelta = sizeof(struct aouthdr) - hdr.f.f_opthdr; whdrsize = hdrsize + aoutdelta; /* first relocate the section headers this aoutdelta down */ j = sizeof(struct filehdr) + hdr.f.f_opthdr; p = (char *)&temphdr; for(i=hdrsize-1;i>=j;p[i+aoutdelta]=p[i],i--); for(i=j;i= hdr.f.f_nscns ) return ENOEXEC; lex.filesize = hdr.s[tix].s_size + hdr.s[tix].s_scnptr; if ((dix=hdr.a.o_sndata-1) >= hdr.f.f_nscns ) return ENOEXEC; lex.filesize = MAX(lex.filesize,hdr.s[dix].s_size + hdr.s[dix].s_scnptr); if ((lix=hdr.a.o_snloader-1) >= hdr.f.f_nscns ) return ENOEXEC; if (ss.type != 'K') lex.filesize = MAX(lex.filesize,hdr.s[lix].s_size + hdr.s[lix].s_scnptr); else { /* * For kernel extensions don't count * the loader section as part of the file. * Instead check that the loader section * is the last section in the file. */ if (lix < tix || lix < dix) goto errenoexec; } /* * The text and data sections must be aligned in the file * according to the alignment information in the header. * Enforcing the alignment from the first fit allocator * is reduced to allocate memory aligned to the max of * the text and data aligments. * Formal proof: * align_text = 2 ^ r * align_data = 2 ^ s * align_text >= align_data * (otherwise exchange text/data everywhere) * ==>> align_text = align_data * t * (trivial, t = 2 ^ (r - s)) * off_text = align_text . k (aligned in file) * off_data = align_data . l (aligned in file) * off_mem = align_text . m (mem aligned to align_text) * off_mem + off_text = align_text . m + align_text . k = * align_text ( m + k ) * ==>> off_mem + off_text is aligned to align_text * off_mem + off_data = align_text . m + align_data . l = * align_data . t . m + align_data . l = * align_data ( t . m + l ) * ==>> off_mem + off_data is aligned to align_data * XXX * XXX The check for data alignment below is not correct. First, * XXX for user loads (exec, load, shared objects), the data gets * XXX copied. The file alignment only affects whether or not the * XXX data can be map copied. For kernel extensions, strictly * XXX speaking, the check is correct, however; for binary * XXX compatibility, we must continue to load them incorrectly * XXX at the very least. The proper solution would be to allocate * XXX the data in a separate chunk with the required alignment. * XXX Text alignment is checked in ld_sanity. * XXX if (((1 << hdr.a.o_algndata) - 1) & hdr.s[dix].s_scnptr || ((1 << hdr.a.o_algntext) - 1) & hdr.s[tix].s_scnptr) goto errenoexec; */ filesize = lex.filesize; if ( flags & LD_textreadbss ){ bssroom=((ulong)hdr.a.o_bsize+3)&(~3); lex.filesize = lex.filesize + bssroom; } { uint needed; needed = lex.filesize; if (flags & LD_allocd){ if(!(lex.h = (void *)ld_allocd(sspointer,needed))) return ENOMEM; if ((ulong)ss.la->sbreak < (ulong)lex.h+needed) if (BRK((ulong)lex.h+needed)) return ENOMEM; ss.la->minbreak=(char*)MAX((ulong)ss.la->minbreak,(ulong)lex.h+needed); } else if (origin){ if (length < needed) return ENOMEM; lex.h = (struct myxcoffhdr *)origin; } else { lex.h = NULL; /* * For small kernel modules use a first * fit allocator to avoid fragmentation. */ if (ss.type == 'K' && needed <= firstfitmax) { #if 0 /* * Modules that will be pinned allocate their * memory from the pinned_fflist the others get * it from the unpinned_fflist. * This code uses the sticky bit as a hint that * indicates that the extension will probably be * pinned. */ struct stat stat; if (FP_FSTAT(fp, &stat, sizeof(stat), FP_SYS) == -1) return ENOEXEC; if (lex.h = (struct myxcoffhdr *) ff_alloc(stat.st_mode & ISVTX ? &pinned_fflist : &unpinned_fflist, needed, MAX(temphdr.a.o_algndata, temphdr.a.o_algntext))) lex.flags |= TEXTFFALLOCED; #else /* * Assume that all will be pinned until a * place for the hint is decided. */ if (lex.h = (struct myxcoffhdr *) ff_alloc(&pinned_fflist, needed, MAX(temphdr.a.o_algndata, temphdr.a.o_algntext))) lex.flags |= TEXTFFALLOCED; #endif } /* If the file is local, and going into the shared library * segment, we will try to map it. The only thing * preventing this would be improper alignment. */ if (!lex.h && (flags & LD_localfile)) { /* First check the alignment */ unsigned data_offset, text_offset; unsigned data_align, text_align; text_offset = memberoffset + temphdr.s[tix].s_scnptr - temphdr.s[tix].s_vaddr; text_align = (1 << temphdr.a.o_algntext) - 1; data_offset = memberoffset + temphdr.s[dix].s_scnptr - temphdr.s[dix].s_vaddr; data_align = (1 << temphdr.a.o_algndata) - 1; if (((text_offset & text_align) == 0) && ((data_offset & data_align) == 0)) { /* File can be mapped, allocate storage from heap, * this may require additional storage. */ size_t size=PAGEUP(memberoffset+lex.filesize) - PAGEDOWN(memberoffset); if (!(lex.h = (struct myxcoffhdr *) xmalloc(size, PGSHIFT, heap))) return ENOMEM; /* Now map the file into the shared segment */ if (ld_filemap(sspointer, lex.h, size, fp, PAGEDOWN(memberoffset))) { /* if we are unable to map, fall * through and attempt to read. */ xmfree(lex.h, heap); lex.h = NULL; } else { /* set flag so we know text is * already in file */ lex.flags |= (TEXTMAPPED|TEXTMALLOCED); lex.h = (struct myxcoffhdr *) ((unsigned)lex.h + (memberoffset- PAGEDOWN(memberoffset))); } } /* if aligned */ } /* * If not a kernel module or a large kernel module or * the first fit allocator failed, then use xmalloc. */ if (!lex.h) { if (!(lex.h = (struct myxcoffhdr *) xmalloc(needed,PGSHIFT,heap))) return ENOMEM; lex.flags |= TEXTMALLOCED; } if (ss.type == 'K') { /* * The loader section memory is not allocated as part * of the memory allocated for the rest of the file. * The memory used for the loader section comes * from the kernel_heap so that it is pageable * but still available to the loader. * There is code that assumes that the loader section * gets allocated contiguously with the rest of * the file. That code gets to the loader section * by adding the section pointer in the header to * the address of the rest of the file. That code * continues to work by forcing the loader section * pointer in the header to be such that when added * to the memory of the file a pointer to the memory * of the loader section is reached. */ /* * Allocate memory for the loader section from * the first fit unpinned pool if possible */ if ((temphdr.s[lix].s_size <= firstfitmax) && (loader_section = (char *) ff_alloc(&unpinned_fflist, temphdr.s[lix].s_size, (int)2))) { lex.flags |= LOADERFFALLOCED; } else { /* ffalloc won't work, try xmalloc */ if (loader_section = (char *) xmalloc(temphdr.s[lix].s_size, PGSHIFT, kernel_heap)) { lex.flags |= LOADERMALLOCED; } else { goto errenoexec; } } /* * Read the loader section. */ (void) fp_lseek(fp, temphdr.s[lix].s_scnptr, SEEK_SET); (void) FP_READ(fp, loader_section, temphdr.s[lix].s_size, 0, UIO_SYSSPACE, &rc); if (rc != temphdr.s[lix].s_size) goto errenoexec; (void) fp_lseek(fp, memberoffset + sizeof hdr, SEEK_SET); /* * Fix the offset to the loader section */ temphdr.s[lix].s_scnptr = loader_section - (char *) lex.h; } } } /*end of needed scope*/ /* No need to read rest of file or copy header if file was mapped. */ if (!(lex.flags & TEXTMAPPED)) { /* copy header from temporary area - it may have been modified in * KLUDGE mode */ bcopy(&temphdr,lex.h,whdrsize); (void)fp_lseek(fp,memberoffset+hdrsize,SEEK_SET); if (flags & LD_textreadbss) { /* leave a hole between the data section and anything * that follows it */ uint dataend; /* N.B. lex.h was reassigned just above - but we haven't * read into it yet. Take care if KLUDGE is operating. * dataend is the offset is the "fixed up file image", not * in the file. By subtracting startofrest we get the right * length even in this case. (in the normal case, * hdrsize==startofrest) */ dataend=temphdr.s[dix].s_size+temphdr.s[dix].s_scnptr; (void)FP_READ(fp,(char *)lex.h+startofrest,dataend-startofrest, 0, UIO_SYSSPACE, &rc); if (rc != (dataend-startofrest)) goto errenoexec; /* in KLUDGE case, following two uses of dataend are relative * to the remapped memory image, not the original file image */ bzero((char *)lex.h+dataend,bssroom); /* * Read other sections that need to be after the bss. * For regular executables the sections are in this * order: * [pad] text [pad] data bss [pad] loader * For those executables this code only reads in: * [pad] loader * There could be bizare executables with this order: * [pad] data bss [pad] text [pad] loader * for those, this code would read: * [pad] text [pad] loader * In any case the secton pointers are adjusted to account * for the space allocated for the bss. * For kernel extensions the loader section was read above * into a different piece of memory and filesize as been * adjusted so that this code does not read the loader section. */ (void)FP_READ(fp, (caddr_t)lex.h+dataend+bssroom, filesize-dataend, 0, UIO_SYSSPACE, &rc); if (rc != filesize-dataend) goto errenoexec; /* section offset fix ups - must fix up real header, * not our copy */ if (hdr.s[tix].s_scnptr >= dataend) hdr.s[tix].s_scnptr+=bssroom; if (ss.type != 'K') { if (hdr.s[lix].s_scnptr >= dataend) hdr.s[lix].s_scnptr+=bssroom; } } else { (void)FP_READ(fp,(char *)lex.h+startofrest, lex.filesize-startofrest, 0, UIO_SYSSPACE, &rc); if (rc != (lex.filesize-startofrest)) goto errenoexec; } #ifdef _POWER vm_cflush(lex.h,lex.filesize); #endif } /* if file was mapped */ return 0; errenoexec: /* Note that we will NEVER get here with the TEXTMAPPED flag set */ if (lex.flags & TEXTMALLOCED) xmfree(lex.h,heap); if (lex.flags & TEXTFFALLOCED) ff_free (lex.h); if (lex.flags & LOADERFFALLOCED) ff_free (loader_section); if (lex.flags & LOADERMALLOCED) xmfree (loader_section, kernel_heap); lex.flags &= ~(TEXTFFALLOCED | LOADERMALLOCED | LOADERFFALLOCED); lex.h = NULL; /* just so no-one trys to free it again!*/ return ENOEXEC; } /* sanity initializes values in lex and validity checks the module so that * subsequent code can just use values without checking * N.B. this code only assumes that textorg and filesize are set * this appears to repeat checks already made in textread - * but if the text is mapped, the normal case, these checks must be done * values set are: * * lex.h * lex.ldhdr * lex.ldsym * lex.ldrel * lex.textreloc * lex.nimpid * * N.B. sanity assumes that lex.h and lex.filesize are correctly set on entry! * If sanity fails and TEXTMALLOCED is set in lex.flags, it frees the * memory in which the text was allocated. */ int ld_sanity( struct sharedstuff *sspointer, struct loader_entry_extension *lexpointer) { int i,j,k; int rc; char *ldrscn = (char *) lex.h + hdr.s[hdr.a.o_snloader-1].s_scnptr; char *ldrscnend; ulong ldrscnsz; extern int vm_release(); #if defined(_POWER) if ( hdr.f.f_magic != U802TOCMAGIC ) goto errenoexec; #elif defined(_IBMRT) if ( hdr.f.f_magic != U800TOCMAGIC ) goto errenoexec; #else /* only compile this if you haven't added your machine to the list!*/ goto errenoexec; #endif if (hdr.f.f_opthdr != sizeof(struct aouthdr)) goto errenoexec; lex.f_nscns = hdr.f.f_nscns; /* * N.B. because text etc. are unsigned, this check catches zero * for free */ if ((lex.text = hdr.a.o_sntext-1) >= lex.f_nscns) goto errenoexec ; if ((lex.data = hdr.a.o_sndata-1) >= lex.f_nscns) goto errenoexec ; if ((lex.bss = hdr.a.o_snbss-1) >= lex.f_nscns) goto errenoexec; if ((lex.loader = hdr.a.o_snloader-1) >= lex.f_nscns) goto errenoexec; if ((hdr.s[lex.text].s_scnptr + hdr.s[lex.text].s_size) > lex.filesize) goto errenoexec; if ((hdr.s[lex.data].s_scnptr + hdr.s[lex.data].s_size) > lex.filesize) goto errenoexec; /* * N.B. don't repeat this "check" for bss - bss is not a real * section in the file */ #if 0 /* XXX this check commented out to avoid breaking the build * XXX the code will go back in when the shared stuff is passed * XXX in from all ld_sanity callers. this is done in this way * XXX to prevent lots of re-merge on an unrelated defect that * XXX had lots of files locked at this time. * XXX THIS COMMENT SHOULD GO AWAY WHEN THE CODE GOES BACK IN */ if (ss.type != 'K' && hdr.s[lex.loader].s_scnptr + hdr.s[lex.loader].s_size > lex.filesize) goto errenoexec; #endif ldrscnsz = hdr.s[lex.loader].s_size; ldrscnend = ldrscn + ldrscnsz; if ((hdr.a.dsize != hdr.s[lex.data].s_size)) goto errenoexec; if ((hdr.a.bsize != hdr.s[lex.bss].s_size)) goto errenoexec; if ((hdr.a.tsize != hdr.s[lex.text].s_size)) goto errenoexec; lex.ldhdr = (struct ldhdr*)((char *)lex.h+hdr.s[lex.loader].s_scnptr); lex.ldsym = (struct ldsym*)&(lex.ldhdr[1]); /* * Validate that the number of symbols will not couse us to * step memory beyond the memory allocated for the loader * section. */ if ((char *) &lex.ldsym[lex.ldhdr->l_nsyms] < ldrscn || (char *) &lex.ldsym[lex.ldhdr->l_nsyms] > ldrscnend) goto errenoexec; lex.ldrel = (struct ldrel*)&lex.ldsym[lex.ldhdr->l_nsyms]; /* * Validate that the number of relocation entries will not * couse us to step memory beyond the memory allocated for * the loader section. */ if ((char *) &lex.ldrel[lex.ldhdr->l_nreloc] < ldrscn || (char *) &lex.ldrel[lex.ldhdr->l_nreloc] > ldrscnend) goto errenoexec; if ( lex.ldhdr->l_impoff >= ldrscnsz || lex.ldhdr->l_impoff + lex.ldhdr->l_istlen > ldrscnsz || lex.ldhdr->l_stoff >= ldrscnsz || lex.ldhdr->l_stoff + lex.ldhdr->l_stlen > ldrscnsz) goto errenoexec; /* compute the text segment relocation. Note that even though the * text has been relocated, if it is in fact position independent * there will be no RLD's pointing to it. However, textreloc may be * applied to data section values, particularly in the TOC. * Relocation factor is computed as the file location in memory * (textorg) plus the text location in the file (s_scnptr) minus * the location to which the text was relocated (s_vaddr) */ lex.textloc = (char *)lex.h + hdr.s[lex.text].s_scnptr; lex.textreloc = (uint)lex.textloc - hdr.s[lex.text].s_vaddr; /* since we never move the text again, it must have the same alignment * in the file as it requires. We check this now. * algntext is definied as the power of two to which the alignment * of vaddr must be respected. Iff textreloc is zero mod 2**aligntext * will this be true */ { ulong align; align = (1 << hdr.a.o_algntext) - 1; if (lex.textreloc & align) { ld_emess(L_ERROR_ALIGN,NULL,NULL); goto errenoexec; } } if ( ! lex.ldhdr->l_nimpid) goto errenoexec; /* count in header includes the path name string - so we reduce * it by 1 */ lex.nimpid = lex.ldhdr->l_nimpid - 1; return 0; errenoexec: if (lex.flags & TEXTMAPPED) { VM_RELEASE(PAGEDOWN(lex.h), PAGEUP((unsigned)lex.h + lex.filesize) - PAGEDOWN(lex.h)); lex.h = PAGEDOWN(lex.h); /* will be xmfree'ed below */ } if (lex.flags & TEXTMALLOCED) xmfree(lex.h,lex.heap); /* lex.heap set in textread */ if (lex.flags & TEXTFFALLOCED) ff_free ((void *) lex.h); if (lex.flags & LOADERFFALLOCED) ff_free (ldrscn); if (lex.flags & LOADERMALLOCED) xmfree (ldrscn, kernel_heap); lex.flags &= ~(TEXTFFALLOCED | LOADERMALLOCED | LOADERFFALLOCED | TEXTMAPPED); lex.h = NULL; /* just so no-one trys to free it again!*/ return ENOEXEC; } /* loop through the list of loader entries for this load freeing load time only * data structures */ #define free_if_is(p) if ( p != NULL ) xmfree(p,kernel_heap) void ld_freelex( struct loader_entry *firstle, struct loader_entry *lastle) { struct loader_entry *le; struct loader_entry_extension *lexpointer; for(le=firstle;le!=lastle;le=le->le_next){ le->le_flags &= ~(LE_THISLOAD | LE_NOTTHISLOAD); if (lexpointer = le->le_lex) { free_if_is(lex.locs); free_if_is(lex.impid); if (XMALLOC&lex.flags) xmfree(lexpointer,kernel_heap); le->le_lex = NULL; } } } /*initialize the path structures corresponding to the already loaded *modules so new requests for those names will get the same answer */ ld_pathinit( struct sharedstuff *sspointer) { struct loader_entry *le; char *filename; struct loader_domain *ld; struct domain_entry *de; struct domain_altname *da; for(le=ss.la->la_loadlist;le;le=le->le_next){ if (le->le_flags & LE_UNLOAD) continue; if (!le->le_fp) continue; if (!(filename = le->le_filename)) continue; if (le != ss.la->la_execle) for(;*filename++;); /*skip member name*/ if ('/' == *filename){ /* absolute path name - so put it into the lookaside*/ ld_pathopen(sspointer,filename,NULL,le->le_fp); } } /* * if process has specified a loader domain, prime the pathanme * lookaside with path names in the loader domain. */ if (ld = ss.ld) { for (de = ld->ld_entries; de; de = de->de_next) { if (!(filename = de->de_fullname)) continue; if ('/' == *filename) { ld_pathopen(sspointer, filename, NULL, (de->de_le)->le_fp); } /* * also initialize pathname structures for all * alternate names this file is known by. */ for(da = de->de_altname; da; da = da->da_next) { if (!(filename = da->da_fullname)) continue; if ('/' == *filename) { ld_pathopen(sspointer, filename, NULL, (de->de_le)->le_fp); } } } } } /*deallocate a pathname lookaside and closes the file *(of course, file will really stay open if it has been loaded *since load did an fp_hold when it copied the fp into the loader_entry *Also clean up other sharedstuff allocated storage. */ void ld_pathclear( struct sharedstuff *sspointer) { struct pathlook *p,*np; if (ss.d){ xmfree(ss.d,kernel_heap); ss.d = NULL; } for(p=ss.pl;p;){ np=p->next; if (p->type == 'F' && p->val) FP_CLOSE(p->val); xmfree(p,kernel_heap); p=np; } ss.pl = NULL; /* clean up any allocated libpath string */ if (ss.flags & SS_LIBPATH_ALLOCED) { if (ss.libpath) xmfree(ss.libpath, kernel_heap); if (ss.libpath_saved) xmfree(ss.libpath_saved, kernel_heap); ss.flags &= ~SS_LIBPATH_ALLOCED; } ss.libpath = NULL; ss.libpath_saved = NULL; } /* * clean up and toss ld_reopen array */ void ld_clean_reopen( struct sharedstuff *sspointer, struct ld_reopen *ldr) { struct ld_reopen *t_ldr; int cleanup = 0; int had_lock = 1; int waslocked; /* * acquire global loader lock if necessary */ waslocked = lockl(&kernel_anchor.la_lock,LOCK_SHORT); /* * first decrement use counts */ t_ldr = ldr; while (t_ldr->ldr_de) { (t_ldr->ldr_de)->de_usecount--; if (!(t_ldr->ldr_de)->de_usecount) cleanup++; ((t_ldr->ldr_de)->de_le)->le_usecount--; t_ldr++; } /* * if necessary clean up zero use count entries in the domain */ if (cleanup) ld_clean_domain(ss.ld); /* * release global loader lock and close files */ unlockl(&kernel_anchor.la_lock); t_ldr = ldr; while (t_ldr->ldr_de) { if (t_ldr->ldr_fp) FP_CLOSE(t_ldr->ldr_fp); t_ldr++; } /* * reacquire lock if necessary */ if (waslocked == LOCK_NEST) lockl(&kernel_anchor.la_lock,LOCK_SHORT); /* * release virtual space for array */ xmfree(ldr, kernel_heap); } /* * NAME: ld_domain_reopen(sspointer) * * FUNCTION: This routine is designed to reopen all pathnames contained * in a loader domain. It will build an array of ld_reopen * structures that correspond to the state of the loader domain * at the time of the call. Since files will be opened in this * routine, the global loader lock must be released. When the * global loader lock is released, the state of the loader * domain can change. Therefore, we must be very careful to * get an array of reopen structures that match the state of * the loader domain. * * This routine will increnet use counts in both domain entries * and loader entries, that will not be cleaned up until a call * to ld_clean_reopen() is made at the end of the pre-pass. * * PARAMETERS: sspointer - shared stuff(global data) * * RETURN: pointer to array of ld_reopen structures(or possibly NULL). * */ static struct ld_reopen * ld_domain_reopen( struct sharedstuff *sspointer) { int before_count, after_count, cleanup; struct domain_entry *de; struct ld_reopen *ldr, *t_ldr; /* * if reopen array already exists, then determine if it matches * the current state of the loader domain. note that because * use counts are incremented for the duration of the pre-pass * entries can not be deleted from the domain. */ if (ldr = ss.ldr) { t_ldr = ldr; de = ss.ld->ld_entries; while(t_ldr->ldr_de && de) { t_ldr++; de = de->de_next; } /* * no match, toss the current reopen array */ if (t_ldr->ldr_de || de) { ld_clean_reopen(sspointer, ldr); ldr = ss.ldr = NULL; } /* * if there is a match, ldr will remain set and we * will bypass the loop below and simply return ldr. */ } /* * keep trying until we get a vaild reopen array */ while (!ldr) { /* * count the entries in loader domain */ before_count = 0; for(de = ss.ld->ld_entries; de; de = de->de_next) before_count++; if (!before_count) return(NULL); /* * allocate space for ld_reopen array */ ldr = (struct ld_reopen *) xmalloc((before_count+1) * sizeof(struct ld_reopen), 0, kernel_heap); if (!ldr) return(NULL); /* * initialize ld_reopen array, and increment use counts in * domain entries and loader entries to prevent their removal. */ t_ldr = ldr; for(de = ss.ld->ld_entries; de; de = de->de_next) { t_ldr->ldr_de = de; de->de_usecount++; (de->de_le)->le_usecount++; t_ldr++; } t_ldr->ldr_de = NULL; /* * release lock, and reopen all files in the domain */ unlockl(&kernel_anchor.la_lock); t_ldr = ldr; while (t_ldr->ldr_de) { if (FP_OPEN((t_ldr->ldr_de)->de_fullname, O_RDONLY, 0, 0, FP_SYS, &(t_ldr->ldr_fp))) t_ldr->ldr_fp = NULL; t_ldr++; } /* * reacquire lock, and count entries in the loader domain * to see if anything new was added. we know nothing could * have been deleted. */ lockl(&kernel_anchor.la_lock,LOCK_SHORT); after_count = 0; for(de = ss.ld->ld_entries; de; de = de->de_next) after_count++; /* * if after_count != before_count, then we know the domain * changed when the lock was released. therefore, the array * of reopen structures we have is invalid. throw them away * and try again. */ if (before_count != after_count) { ld_clean_reopen(sspointer, ldr); ldr = NULL; } } return(ldr); } /* * create a domain_altname structure for the passed pathname. space * for this structure will be allocated from the shared library data * segment. * return - pointer to newly created domain_altname structure */ static struct domain_altname * ld_create_dom_altname(char *pathname) { struct domain_altname *da; da = xmalloc(sizeof(struct domain_altname) + strlen(pathname) + 1, 2, lib.la.la_data_heap); if (da) { da->da_fullname = (char *)da + sizeof(struct domain_altname); strcpy(da->da_fullname, pathname); } return da; } /* * NAME: ld_domain_open(sspointer, pathname, fp) * * FUNCTION: This routine is designed to search a loader domain * for a match based on the pathname and fp parameters. * This routine should only be called from ld_pathopen(), * AFTER fp_open() is called to open the file, and the * process has specified a loader domain. At this point * it is known there was no match in the pathanme lookaside * structures. * * This routine will first search for a match based on * the fp by doing gnode comparisons. If no match is * found this way, then all files in the domain are * "reopen'ed". A match is then searched for by doing * gnode comparisons on the reopen'ed fps. If a match * is found in either case, then an entry is added to * the alternate name list for the domain entry. * * PARAMETERS: sspointer - shared stuff(global data) * pathname - full pathname of library file * fp - open file pointer to library file * * RETURN: fp - the original passed fp is returned if a match * is found based on the gnode comparisons, OR * no match was found * fp - the fp from the matched loader entry in the * domain is returned if a match was found. */ static struct file * ld_domain_open( struct sharedstuff *sspointer, char *pathname, struct file *fp) { struct loader_domain *ld; struct domain_entry *de; struct domain_altname *da, *tmp_da; struct ld_reopen *ldr; uint fh; fh = FPtoFH(fp); /* * first search the domain for a match based on gnode number */ ld = ss.ld; de = ld->ld_entries; while (de) { if (FPtoFH((de->de_le)->le_fp) == fh) { /* * gnodes match. this means the file was * opened by another name(otherwise the * lookaside mechanism would have found * the match). create an alternate name * structure for this domain entry. */ da = ld_create_dom_altname(pathname); if (!da) return(fp); /* link new structure into chain */ da->da_next = de->de_altname; de->de_altname = da; /* return passed fp */ return fp; } de = de->de_next; } /* * no gnode match found * reopen all files in the domain, ld_domain_reopen will build * such an array */ ss.ldr = ld_domain_reopen(sspointer); /* * now search this reopen array for a gnode match */ ldr = ss.ldr; if (ldr) { while(ldr->ldr_de) { if (FPtoFH(ldr->ldr_fp) == fh) { /* * gnodes match. * create altname structure as above */ da = ld_create_dom_altname(pathname); if (!da) return(fp); /* link new structure into chain */ da->da_next = (ldr->ldr_de)->de_altname; (ldr->ldr_de)->de_altname = da; /* * return fp from domain * * close the file pointer that was passed in. be * sure to keep the open counts correct. the global * loader lock must be released before making the * filesystem calls. */ unlockl(&kernel_anchor.la_lock); FP_CLOSE(fp); fp_hold(((ldr->ldr_de)->de_le)->le_fp); lockl(&kernel_anchor.la_lock,LOCK_SHORT); fp = ((ldr->ldr_de)->de_le)->le_fp; return fp; } ldr++; } } /* no match found, return original fp */ return(fp); } struct file * ld_pathopen( struct sharedstuff *sspointer, char *path, char *filename, struct file *initfp) { uint hash, mode; uint lpath,ltemp,lfilename; struct pathlook *p; struct pathlook *relative_p = NULL; char *x,*y,*z; struct file *fp; char *realfilename; extern int ps_not_defined; extern char *kern_getcwd(); lpath = lfilename = 1<<28; /* to pass a simple pathname, pass it in path, with filename NULL*/ if (!filename) { filename = path; path = ""; lpath = 0; hash = 0; } /* filename points to two successive null terminated strings * the first is everything except the basename with the seperating * slash removed - the second is the basename * Thus, if the first string is non-null we use it as the path, * else we use libpath */ else if (*filename){ path = filename; hash = ld_hash(filename,&lpath); filename += lpath+1; } else { hash = ld_hash(path,&lpath); filename += 1; } hash ^= ld_hash(filename,&lfilename); /* ^ is bitwise exclusive or */ for(p=ss.pl;p;p=p->next){ if (hash != p->hash) continue; if (p->type != 'F') continue; if ((lpath + lfilename) != p->length) continue; if (lpath && memcmp(&(p->name[0]),path,lpath)) continue; if (memcmp(&(p->name[lpath]),filename,lfilename)) continue; u.u_error = 0; return p->val; } p = xmalloc(sizeof(struct pathlook) + 2 * (lpath + lfilename)+1,0,kernel_heap); assert(p); /* if we can't get this much system is dead*/ /* if path points to null string we really open - otherwise * call pathopen recursively so we remember the base pathname as * well as the * libpath / basename pair */ x=path; realfilename = &(p->name[lpath+lfilename]); while(1) { char last='/'; z=realfilename; for(;*x && *x != ':';last=*z++=*x++); /* scan for null or : */ if('/' != last) *z++ = '/'; /* needed for case of '/' */ strcpy(z,filename); if (0 != lpath){ /* for now initfp is always NULL on this path*/ fp=ld_pathopen(sspointer,realfilename,NULL,initfp); if (fp) { fp_hold(fp); *realfilename = '\0'; } } else if(initfp){ /* this occurs during a load command when we * "prime" the lookaside with the paths * which are already known*/ fp_hold(initfp); fp=initfp; } else { /* N.B. library load files need only have read permission * O_RSHARE requests that no writer is allowed. * before paging space is defined the libraries are * opened so that they can be truncated to conserve * space. only those libraries which stay in use or * will be never needed afterwards can be truncated. */ /* ASSERT(ss.pre); */ unlockl(&kernel_anchor.la_lock); u.u_error = 0; /* we don't trust fp_open */ mode = ps_not_defined ? O_RDONLY : O_RDONLY | O_RSHARE; u.u_error = FP_OPEN(realfilename,mode,0,0,FP_SYS, &fp); if (u.u_error) fp = NULL; /* * if a loader domain was specified, and a relative * path name was used, construct the absolute path * name for the file just opened file. */ if (fp && ss.ld && *realfilename != '/') { char *cwd, *cwd_buf; /* * first built a relative pathname lookaside * structure using the parameters passed to * this invocation of the routine. */ p->hash = hash; p->length = lpath + lfilename; p->type = 'F'; if (lpath) memcpy(&(p->name[0]),path,lpath); memcpy(&(p->name[lpath]),filename,lfilename); p->name[lpath + lfilename] = '\0'; /* * allocate buffer for cwd string, and * call routine to determine cwd */ cwd_buf = xmalloc(PATH_MAX, 0, kernel_heap); assert(cwd_buf); cwd = kern_getcwd(cwd_buf, PATH_MAX); if (cwd) { struct pathlook *p2; int lcwd; /* * fix up local variables to reflect * the absolute full pathname rather * than the relative one. first * construct the absolute path. */ lcwd = strlen(cwd); p2 = xmalloc(sizeof(struct pathlook) + 2 * (lcwd + lfilename + 1), 0, kernel_heap); assert(p2); realfilename = &(p2->name[lcwd + lfilename + 1]); memcpy(realfilename, cwd, lcwd); memcpy(realfilename + lcwd, "/", 1); memcpy(realfilename + lcwd + 1, filename, lfilename + 1); /* * recompute hash */ filename = realfilename; lfilename = 1<<28; hash = ld_hash(filename, &lfilename); /* * p points to pathlook structure with * absolute pathname. relative_p * points to structure with relative * pathname. */ relative_p = p; p = p2; } xmfree(cwd_buf, kernel_heap); } /* this form of lock does not fail */ lockl(&kernel_anchor.la_lock,LOCK_SHORT); /* * if a loader domain was specified, look for * a match in the loader domain. note that this * call may change the value of fp if a match is * found. if ld_domain_open() changes the value of * the fp, then it will keep open counts correct. */ if (fp && ss.ld) fp = ld_domain_open(sspointer,realfilename,fp); } if (fp) break; if (0==*x) break; x++; } /* only use files - not directories, specials, etc */ if ((!initfp) && fp) { struct stat stst; if (FP_FSTAT(fp, &stst, sizeof(stst),FP_SYS) == -1 || ! S_ISREG(stst.st_mode) ){ /* not a regular file - its a directory * or other bad thing */ FP_CLOSE(fp); u.u_error = EACCES; fp = NULL; } } if (fp) { /* * if there is a pathname lookaside structure for a relative * pathname, add it to the list. */ if (relative_p) { fp_hold(fp); /* so close count is correct */ relative_p->val = fp; relative_p->next = ss.pl; ss.pl = relative_p; } p->next = ss.pl; ss.pl = p; p->hash = hash; p->val = fp; p->length = lpath + lfilename; p->type = 'F'; if (lpath) memcpy(&(p->name[0]),path,lpath); memcpy(&(p->name[lpath]),filename,lfilename); } else { xmfree(p,kernel_heap); if (relative_p) xmfree(relative_p,kernel_heap); } return fp; } char * ld_fptopath( struct sharedstuff *sspointer, struct file *fp) { struct pathlook *p; for(p=ss.pl;p;p=p->next){ if (p->type != 'F') continue; if (p->val != fp) continue; if (!(p->name[p->length])) continue; return &(p->name[p->length]); } return NULL; } /* * NAME: knlist() * * FUNCTION: Translate names to addresses in the running system. * * EXECUTION ENVIRONMENT: This routine may only be called by a process * This routine may page fault * * NOTES: The returned "nlist" array is consistent per entry, but the entire * array is not guaranteed to be consistent due to possible kernel * updates concurrent with this command. * * RETURN VALUES: * 0 = successful completion - symbol type & value set if found, else 0 * -1 = failed, errno indicates cause of failure */ int knlist( struct nlist *nl, /* pointer to array of nlist struct */ int nelem, /* number of user nlist struct */ int sizelem) /* size of expected nlist structure */ { extern int copyout(); /* copies from kernel to user */ extern int copyin(); /* copies from user to kernel */ extern int fubyte(); /* returns byte from user */ extern int priv_chk(); /* performs privilege check */ register int ret; /* remember return code */ struct nlist nltmp; /* area for temporary nlist */ uint elem; #define MAX_EXT 32 char symbuf[MAX_EXT+1]; /* temporary symbol storage */ struct exp_index *ex; uint actual; int waslocked; ret = 0; /* parameter ok and number of symbols is 1 or more */ if (sizelem != sizeof(struct nlist) || nelem < 1) { u.u_error = EINVAL; return(BAD); } /* while elements repeat search, zero tmp, increment user ptr */ for (elem=0;elemle_exports,symbuf); if (ex){ nltmp.n_value = (long)ex->exp_location; } else { char *stmp = nltmp.n_name; u.u_error = EFAULT; ret = -1; bzero(&nltmp,sizeof nltmp); nltmp.n_name = stmp; } if (waslocked != LOCK_NEST) /* release loader lock */ unlockl(&kernel_anchor.la_lock); /* protected copy all nlist to user space, checks perm */ if (copyout(&nltmp,&nl[elem], sizelem)) { u.u_error = EFAULT; return(BAD); } } /* successful search, return GOOD value */ return(ret); } /* ld_getinfo - Get list of loader entries for the specified process * * Input parameters * pid - Process id to query or -1 to specify current * process * length - Length of callers buffer * ld_info - Pointer to callers buffer * userspace - only specified on calls to ld_getingo_all * a nonzero value indicates callers buffer is * in a processes address space * Return Values * 0 Success * !0 Failure * * N.B. it is assumed that either the caller is the process * being queried OR that the process being queried is stopped and * NOT in the loader. This is true for ptraced processes when * the debugging process is running and the ptrace process is stopped. * * Note that the real workhorse routine is ld_getinfo_all(). There * are two separate paths to this routine. ld_getinfo is used if the * buffer is in the kernel domain. ld_getinfo_user is used if the * buffer is in the user address space. */ int ld_getinfo( pid_t pid, unsigned int length, struct ld_info *ld_info) { /* call ld_getinfo_all indicating buffer is in kernel space */ return(ld_getinfo_all(pid, length, ld_info, 0)); } int ld_getinfo_user( pid_t pid, unsigned int length, struct ld_info *ld_info) { /* call ld_getinfo_all indicating buffer is in user space */ return(ld_getinfo_all(pid, length, ld_info, 1)); } static int ld_getinfo_all( pid_t pid, unsigned int length, struct ld_info *ld_info, int userspace) { struct loader_anchor *a; struct loader_entry *le,*execle,*nextle; int sid,libsid; int rc = 0; vmhandle_t srval; void *liborg,*privorg; struct proc *procp; struct user *user; ulong privreloc,libreloc,namereloc; void *ldmax; char *p,*q; uint ldinfo_next; char tmp_buf[sizeof(struct ld_info)+PATH_MAX+PATH_MAX]; struct ld_info *tmp_ld_info = (struct ld_info *) tmp_buf; uint tmp_ld_info_length; int ovfl_was_attached; /* Establish addressability to the process private segment. * Remember that the process whose load list we are traversing * may not be the current process. */ privorg = liborg = NULL; procp = ((pid == -1) ? curproc : PROCPTR(pid)); privorg = vm_att(procp->p_adspace,0); privreloc = (ulong)privorg - PRIVORG; user = PTRADD(&U,privreloc); /* make sure this process has the goods - kprocs don't*/ a=PTRADD(&(U.U_loader[0]),privreloc); if (a->la_execle==NULL || a->la_loadlist==NULL){ rc = EINVAL; goto exit; } /* make global library data segment addressable */ liborg = vm_att(vm_setkey(library_data_handle,VM_PRIV),0); libreloc = (ulong)liborg - SHDATAORG; /* Attach to overflow segment if necessary */ assert((privorg != OVFLORG) && (liborg != OVFLORG)); ovfl_was_attached = ld_ovflatt(a); /* ldmax is address of first byte past the end of passed buffer. * Its value does not change. ldinfo_next is the address in the * user buffer where the next ldinfo structure will be put. */ ldmax = PTRADD(ld_info,length); ldinfo_next = ld_info; /* in following, we process execle first - that is how users can * tell it from loaded programs. We start with le pointing to * the execle. At the bottom of the loop where we update le to * point to the next le, a special check is made in the case of * the execle so we start at the beginning of the list. Yuck!!! */ le = nextle = execle = OVFL_PTRADD(a->la_execle,privreloc); while (le) { /* Find the next le in the load list. Note that when * we get here 'le == nextle && nextle != NULL'. */ do { if (nextle->le_next == NULL) /* go to beginning of load list */ nextle = OVFL_PTRADD(a->la_loadlist, privreloc); else nextle = OVFL_PTRADD(nextle->le_next, privreloc); /* If 'nextle == execle' we know that we have * looked at every entry in the list. Set * nextle to NULL to indicate the end of the * list */ if (nextle == execle) nextle = NULL; /* quit when we have looked at every entry in the list * OR we have found the next entry. Upon exit of this * loop, nextle will point to the next valid entry in * the load list(possibly NULL). */ } while( nextle && (nextle->le_flags&(LE_DATA|LE_UNLOAD)) != LE_DATA); /* Build up temporary structure, this will be copied to * the buffer specified by the calling routine */ tmp_ld_info->ldinfo_fp = le->le_fp; tmp_ld_info->ldinfo_textorg = le->le_file; tmp_ld_info->ldinfo_textsize = le->le_filesize; tmp_ld_info->ldinfo_dataorg = le->le_data; tmp_ld_info->ldinfo_datasize = le->le_datasize; /* copy filename/member if it exists * Note that in the loader entry the member name comes * first and is followed by the member name. In the * ldinfo structure the file name comes first and is * followed by the member name. Hence, the following * code must reverse their order. Yuck!!! */ p = tmp_ld_info->ldinfo_filename; if(le->le_filename){ namereloc = ((uint)le->le_filename >= SHDATAORG && (uint)le->le_filename < SHDATAORG+(SEGSIZE-1)) ? libreloc: IS_OVFLSEG(le->le_filename) ? 0 : privreloc; q = le->le_filename + namereloc; /* for technical reasons the filename field of the * execd entry points to u_comm. This is a file * name, with no member. Fix up hear so interface * outside is uniform. (Sorry about that - MAA). */ if (le != execle) /* skip past member name, it comes first here */ for(;*q++;); /* copy file name */ while (*p++ = *q++); /* now copy member name */ if (le == execle) { /* remember - execd has no member */ *p++ = 0; } else{ q = le->le_filename + namereloc; /* copy member name */ while (*p++ = *q++); }/*end member exists*/ }/*end le_filename exists*/ else { /* If no file name put out two zero length * strings */ *p++ = 0; *p++ = 0; } /* Check to see if this structure will fit in the * callers buffer */ tmp_ld_info_length = (uint)p - (uint)tmp_ld_info; if ((ldinfo_next + tmp_ld_info_length) >= ldmax) { rc = ENOMEM; goto exit; } /* Determine if there is another entry in list */ if (nextle) { /* Calculate position of next entry in users * list. Round up to a word boundary. */ p = (void *)(((uint)p+sizeof(int)-1) & ~(sizeof(int)-1)); /* The current entry will point to next */ tmp_ld_info->ldinfo_next = (uint)p - (uint) tmp_ld_info; } else { /* This is the last entry in the list */ tmp_ld_info->ldinfo_next = 0; } /* Copy the temporary structure to the buffer * specified by the caller */ if (userspace) { if (rc = copyout(tmp_ld_info, ldinfo_next, tmp_ld_info_length)) goto exit; } else { bcopy (tmp_ld_info, ldinfo_next, tmp_ld_info_length); } /* Update ldinfo_next pointer for next loop iteration * Grab size of the current structure out of the entry * we just completed(this includes the necessary * alignment) */ ldinfo_next += tmp_ld_info->ldinfo_next; /* process next entry */ le = nextle; } exit: if (ovfl_was_attached) ld_ovfldet(a); if (privorg) vm_det(privorg); if (liborg) vm_det(liborg); return rc; } /* chain an error message on the error chain. The storage ALWAYS * comes from the user process kernel area. If this is an execload * the errors will be passed to /etc/execerror. If this is a load, * the user can call loadquery with the errorget option to retrieve the * messages */ void ld_emess( int errorid, char *errordata, struct loader_entry *le) { struct sharedstuff *sspointer,sharedstuff; struct emess *e,*ep; int mlen,flen,elen; char fbuf[128],ebuf[32]; char *mp; int ovfl_was_attached; /* set up a sufficient ss so that ualloc will work. These call * must always manipulate the process anchor and memory. It may * be called while the "real" sharedstuff names process, kernel, or * library */ sspointer = &sharedstuff; bzero(&sharedstuff,sizeof(sharedstuff)); ss.type = 'C'; ss.la = (struct loader_anchor *)(U.U_loader) ; /* Attach overflow segment if needed */ ovfl_was_attached = ld_ovflatt(ss.la); sprintf(ebuf,"%d",errorid); elen=strlen(ebuf); /* if an le is passed, the file name associated with is is added to * to the message */ if (le){ char *file,*member,*t; file = le->le_filename; member = file; if (file && (le->le_flags & LE_EXECLE)) member = "\0"; else if (file) for(;*file++;); else file = member = "?"; t = fbuf; for(;(*t++ = *file++) && t < fbuf+sizeof fbuf - 2;); if(*member) { *(t-1) = ' '; for(;(*t++ = *member++) && t < fbuf+sizeof fbuf - 1;); } else *(t-1) = 0; flen = t-fbuf; } else flen=0; mlen = strlen(errordata); e = ld_ualloc(sspointer,elen+flen+mlen + sizeof(struct emess)); if (!e) goto exit; memcpy(e->errordata,ebuf,elen); mp = e->errordata+elen; *mp++ = ' '; memcpy(mp,errordata,mlen+1); mp+=mlen; /* points to the null which was last char copied*/ if (flen){ *mp++ = ' '; memcpy(mp,fbuf,flen); /* N.B. flen includes the null at end of fbuf */ } e->next = NULL; if ( ss.la->la_emess ) { int count=100; for(ep=ss.la->la_emess;count-- && ep->next;ep=ep->next); if (count) ep->next = e; else { sprintf(ep->errordata,"%d",L_ERROR_TOOMANY); ld_ufree(sspointer,e); } } else ss.la->la_emess = e; exit: /* detach from overflow segment if necessary */ if (ovfl_was_attached) ld_ovfldet(ss.la); } void ld_emessinit() { struct sharedstuff *sspointer,sharedstuff; struct emess *e,*ep; int mlen; int ovfl_was_attached; /* set up a sufficient ss so that ualloc will work. These call * must always manipulate the process anchor and memory. It may * be called while the "real" sharedstuff names process, kernel, or * library */ sspointer = &sharedstuff; bzero(&sharedstuff,sizeof(sharedstuff)); ss.type = 'C'; ss.la = (struct loader_anchor *)(U.U_loader) ; /* Attach to overflow heap if necessary */ ovfl_was_attached = ld_ovflatt(ss.la); while (e = ss.la->la_emess) { ss.la->la_emess = e->next; ld_ufree(sspointer,e); } if (ovfl_was_attached) ld_ovfldet(ss.la); } int ld_emessdump( char *buf, int blen, int adspace) { struct loader_anchor *la; struct emess *e; char *pp,*mp; int n, mlen; int rc=0; int ovfl_was_attached; if (blen < sizeof(void *)) return ENOMEM; /* The head of the list of error messages is in the loader anchor */ la = (struct loader_anchor *)(U.U_loader) ; /* attach to overflow segment if necessary */ ovfl_was_attached = ld_ovflatt(la); /* count the messages */ n = 0; for(e = la->la_emess;e;e=e->next) n++; /* For each error message: * - provide a pointer to the string within the buffer itself * - copy the error message string to the buffer */ pp = buf; mp = buf + sizeof(void *)*(n+1); for(e=la->la_emess;e;e=e->next){ mlen = strlen(e->errordata)+1; if(mp+mlen > buf+blen){ rc = ENOMEM; goto exit; } /* First copy the pointer to string */ if (adspace==SYS_ADSPACE) bcopy(&mp,pp,sizeof(void*)); else { if (rc = copyout(&mp,pp,sizeof(void *))) goto exit; } /* Then copy the string itself */ pp += sizeof(void *); if (adspace==SYS_ADSPACE) bcopy(e->errordata,mp,mlen); else { if (rc = copyout(e->errordata,mp,mlen)) goto exit; } mp += mlen; } /* Terminate the list of message pointers with a NULL pointer */ mp = NULL; if (adspace == SYS_ADSPACE) bcopy(&mp,pp,sizeof(void *)); else rc = copyout(&mp,pp,sizeof(void *)); exit: if (ovfl_was_attached) ld_ovfldet(la); return rc; } /* * NAME: char *kern_getcwd(char *buf, int buf_size) * * FUNCTION: This routine is designed to get the path name of the * current directory. It is identical in function to the * library routine getcwd(). However, kern_getcwd() is * designed to run in the kernel environment. * * PARAMETERS: buf - Points to a buffer that will contain the path * name. This string must be addressable(in the * kernel). * buf_size - Size(in bytes) of the buffer. * * RETURN: pointer path name of current directory, or NULL in case * of an error. * static char * */ char * kern_getcwd(char *buf, int buf_size) { struct file *cur_fp; struct file *par_fp; ulong_t cur_vfs; ino_t cur_ino; ulong_t root_vfs; ino_t root_ino; struct stat s; char curdir[PATH_MAX]; char *curptr; char *pnptr; char *pbufptr; struct vattr vat; struct vattr *vattrp = &vat; struct ucred *crp; /* * initialize path pointers. note that pnptr points to the next * character to be written. it does not point to a valid * character. */ if (!buf || buf_size < 2) return(NULL); pnptr = buf + buf_size - 1; *pnptr-- = '\0'; pbufptr = buf; curptr = curdir; /* * get information about root directory, so we know where to stop */ crp = crref(); if (VNOP_GETATTR(rootdir, vattrp, crp)) return(NULL); /* unable to stat */ crfree(crp); root_vfs = rootdir->v_vfsp->vfs_number; root_ino = vat.va_serialno; /* * get information about current directory */ strcpy(curptr, "./"); curptr += 2; if (fp_open(curdir, O_RDONLY, 0, 0, FP_SYS, &cur_fp)) return(NULL); if (fp_fstat(cur_fp, &s, sizeof(s), FP_SYS)) { fp_close(cur_fp); return(NULL); /* unable to stat */ } cur_vfs = s.st_vfs; cur_ino = s.st_ino; /* * loop until we reach the root directory */ while (cur_vfs != root_vfs || cur_ino != root_ino) { /* * open parent directory */ if (((curptr + 3) - curdir) > PATH_MAX) { fp_close(cur_fp); return(NULL); } strcpy(curptr, "../"); curptr += 3; if (fp_open(curdir, O_RDONLY, 0, 0, FP_SYS, &par_fp)) { fp_close(cur_fp); return(NULL); } if (fp_fstat(par_fp, &s, sizeof(s), FP_SYS) || !S_ISDIR(s.st_mode)) { fp_close(cur_fp); fp_close(par_fp); return(NULL); /* unable to stat or not dir */ } /* * search for name associated with curdir in parent directory */ if (find_dir_name(par_fp, cur_fp, cur_vfs, cur_ino, &pnptr, &pbufptr)) { fp_close(cur_fp); fp_close(par_fp); return(NULL); } /* * current directory is now parent directory */ cur_vfs = s.st_vfs; cur_ino = s.st_ino; fp_close(cur_fp); cur_fp = par_fp; } fp_close(cur_fp); pnptr++; return(pnptr); } /* * search for the name associated with the current directory in the parent * directory. this is done by reading the directory entries in the parent * directory, while looking for an inode match. when a match is found the * name in the direcotry entry is used. static */ find_dir_name( struct file *dir_fp, struct file *cur_fp, ulong_t cur_vfs, ino_t cur_ino, char **pnptr, char **pbufptr) { struct vnode *dir_vp; ulong_t dir_vfs; struct iovec aiov; struct uio auio; char dir_buf[PAGESIZE]; struct dirent *dirp; int found = 0; int bytes_read; int rc; /* * determine if we are crossing a vfs */ dir_vp = dir_fp->f_vnode; if ((dir_vp->v_vfsp)->vfs_number != cur_vfs) { struct vnode *mnt_vp; struct vattr vat; struct vattr *vattrp = &vat; /* * if crossing a vfs, get the inode of the 'mounted over' * vnode. this is what we want to match below. */ if (!((cur_fp->f_vnode)->v_flag & V_ROOT)) return(-1); mnt_vp = ((cur_fp->f_vnode)->v_vfsp)->vfs_mntdover; if ((mnt_vp->v_vfsp)->vfs_number != (dir_vp->v_vfsp)->vfs_number) return(-1); if (VNOP_GETATTR(mnt_vp, vattrp, dir_fp->f_cred)) return(-1); cur_ino = vat.va_serialno; } /* * read directory entries block by block, until the directory * name is found or we reach the end of the directory. */ do { /* * contruct iovec structure */ aiov.iov_base = dir_buf; aiov.iov_len = PAGESIZE; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_fmode = dir_fp->f_flag & FMASK; auio.uio_segflg = UIO_SYSSPACE; auio.uio_resid = PAGESIZE; auio.uio_offset = dir_fp->f_offset; /* * read a block of directory entries */ if (VNOP_READDIR(dir_vp, &auio, dir_fp->f_cred) == 0) dir_fp->f_offset = auio.uio_offset; else break; /* * check for end of file/directory */ if (!(bytes_read = PAGESIZE - auio.uio_resid)) break; /* * we now scan the directory entries just read for the * name of the file associated with the passed current * directory. */ dirp = (struct dirent *)dir_buf; while (dirp < (struct dirent *)(dir_buf + bytes_read)) { if (dirp->d_ino == cur_ino) { /* found match */ found++; break; } dirp = (struct dirent *) ((char *)dirp + dirp->d_reclen); } } while (!found); if (found) rc = prepend(dirp->d_name, dirp->d_namlen, pnptr, pbufptr); else rc = -1; return(rc); } /* * prepend the directory name to the existing path name static */ prepend(char *node, int node_len, char **pnptr, char **pbufptr) { if ((*pnptr - node_len - 1) < *pbufptr) return(-1); node += (node_len - 1); while(node_len) { *(*pnptr)-- = *node--; node_len--; } *(*pnptr)-- = '/'; return(0); }