/*** *crt0dat.c - 32-bit C run-time initialization/termination routines * * Copyright (c) 1986-1992, Microsoft Corporation. All rights reserved. * *Purpose: * This module contains the routines _cinit, exit, and _exit * for C run-time startup and termination. _cinit and exit * are called from the _astart code in crt0.asm. * This module also defines several data variables used by the * runtime. * * [NOTE: Lock segment definitions are at end of module.] * * *** FLOATING POINT INITIALIZATION AND TERMINATION ARE NOT *** * *** YET IMPLEMENTED IN THIS FILE *** * *Revision History: * 06-28-89 PHG Module created, based on asm version * 04-09-90 GJF Added #include . Made calling type * explicit (_CALLTYPE1 or _CALLTYPE4). Also, fixed * the copyright. * 04-10-90 GJF Fixed compiler warnings (-W3). * 05-21-90 GJF Added #undef _NFILE_ (temporary hack) and fixed the * indents. * 08-31-90 GJF Removed 32 from API names. * 09-25-90 GJF Merged tree version with local (8-31 and 5-21 changes). * 10-08-90 GJF New-style function declarators. * 10-12-90 GJF Removed divide by 0 stuff. * 10-18-90 GJF Added _pipech[] array. * 11-05-90 GJF Added _umaskval. * 12-04-90 GJF Added _osfinfo[] definition for Win32 target. Note that * the Win32 support is still incomplete! * 12-04-90 SRW Changed to include instead of * 12-04-90 SRW Added _osfile back for win32. Changed _osfinfo from * an array of structures to an array of 32-bit handles * (_osfhnd) * 12-28-90 SRW Added _CRUISER_ conditional around pack pragmas * 01-29-91 GJF ANSI naming. * 01-29-91 SRW Added call to GetFileType [_WIN32_] * 02-18-91 SRW Removed duplicate defintion of _NFILE_ (see os2dll.h) * 04-04-91 GJF Added definitions for _base[version|major|minor] * (_WIN32_). * 04-08-91 GJF Temporary hack for Win32/DOS folks - added HeapDestroy * call to doexit to tear down the OS heap used by C * heap. * 04-09-91 PNT Added _MAC_ conditional * 04-26-91 SRW Removed level 3 warnings * 07-16-91 GJF Added fp initialization test-and-call [_WIN32_]. * 07-26-91 GJF Revised initialization and termination stuff. In * particular, removed need for win32ini.c [_WIN32_]. * 08-07-91 GJF Added init. for FORTRAN runtime, if present [_WIN32_]. * 08-21-91 GJF Test _prmtmp against NULL, not _prmtmp(). * 08-21-91 JCR Added _exitflag, _endstdio, _cpumode, etc. * 09-09-91 GJF Revised _doinitterm for C++ init. support and to make * _onexit/atexit compatible with C++ needs. * 09-16-91 GJF Must test __onexitend before calling _doinitterm. * 10-29-91 GJF Force in floating point initialization for MIPS * compiler [_WIN32_]. * 11-13-91 GJF FORTRAN needs _onexit/atexit init. before call thru * _pFFinit. * 01-10-92 GJF Merged. Also, added _C_Termination_Done [_WIN32_]. * 02-13-92 GJF Moved all lowio initialization to ioinit.c for Win32. * 03-12-92 SKS Major changes to initialization/termination scheme * 04-16-92 DJM POSIX support. * 04-17-92 SKS Export _initterm() for CRTDLL model * 05-07-92 DJM Removed _exit() from POSIX build. * 06-03-92 GJF Temporarily restored call to FORTRAN init. * 08-26-92 SKS Add _osver, _winver, _winmajor, _winminor * 08-28-92 GJF Use unistd.h for POSIX build. * 09-02-92 SKS Fix _onexit table traversal to be LIFO. * Since table is built forward (my changes 03-12-92) * the table must be traversed in reverse order. * 10-30-92 TVB Force in floating point initialization for ALPHA * compiler as was done for MIPS. [_WIN32_]. * 11-12-92 SKS Remove hard-coded call to FORTRAN initializer * 05-11-93 SKS _C_Termination_Done is now used by DLLs in LIBC/LIBCMT * models, not just in CRTDLL.DLL. * Remove obsolete variable _child. * 07-16-93 SRW ALPHA Merge * *******************************************************************************/ #include #ifdef _POSIX_ #include #else #include #endif #include #include #include #include #include #include #include /* define errno */ #ifndef MTHREAD int _VARTYPE1 errno = 0; /* libc error value */ #ifdef _CRUISER_ int _VARTYPE1 _doserrno = 0; /* OS system error value */ #else /* ndef _CRUISER_ */ #ifdef _WIN32_ unsigned long _VARTYPE1 _doserrno = 0; /* OS system error value */ #endif /* _WIN32_ */ #endif /* _CRUISER_ */ #endif /* MTHREAD */ /* define umask */ int _umaskval = 0; /* define version info variables */ unsigned int _VARTYPE1 _osver = 0; unsigned int _VARTYPE1 _winver = 0; unsigned int _VARTYPE1 _winmajor = 0; unsigned int _VARTYPE1 _winminor = 0; /* --------- The following block is OBSOLETE --------- */ unsigned int _VARTYPE1 _osversion = 0; unsigned int _VARTYPE1 _osmajor = 0; unsigned int _VARTYPE1 _osminor = 0; #if defined(_WIN32_) || defined(_POSIX_) unsigned int _VARTYPE1 _baseversion = 0; unsigned int _VARTYPE1 _basemajor = 0; unsigned int _VARTYPE1 _baseminor = 0; #endif /* _WIN32_ || _POSIX_ */ /* define _osmode/_cpumode */ #ifdef _CRUISER_ unsigned char _osmode = _OS2_20_MODE; #else /* ndef _CRUISER_ */ #ifdef _DOSX32_ unsigned char _osmode = _DOSX32_MODE; #else /* ndef _DOSX32_ */ #ifdef _WIN32_ unsigned char _osmode = _WIN_MODE; #else /* ndef _WIN32_ */ #ifdef _POSIX_ unsigned char _osmode = _POSIX_MODE_; #endif /* _POSIX_ */ #endif /* _WIN32DOS_ */ #endif /* _WIN32_ */ #endif /* _CRUISER_ */ unsigned char _cpumode = _FLAT_MODE; /* --------- The preceding block is OBSOLETE --------- */ #ifdef _CRUISER_ /* number of allowable file handles */ int _nfile = _NFILE_; /* file handle database -- stdout, stdin, stderr are open */ char _osfile[_NFILE_] = {FOPEN+FTEXT, FOPEN+FTEXT, FOPEN+FTEXT}; #endif /* _CRUISER_ */ #ifdef _WIN32DOS_ extern HANDLE _HeapHandle; #endif /* _WIN32DOS_ */ #ifdef _CRUISER_ /* peek-ahead buffers for pipes, each initialized to a newline */ char _pipech[_NFILE_] = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 #ifdef MTHREAD , 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 #endif }; #endif /* _CRUISER_ */ /* argument vector and environment */ int __argc = 0; char **__argv = NULL; #ifdef _POSIX_ char **environ = NULL; #else char **_environ = NULL; #endif char *_pgmptr; /* ptr to program name */ /* callable exit flag */ char _exitflag = 0; #if defined(_WIN32_) /* * flag indicating if C runtime termination has been done. set if exit, * _exit, _cexit or _c_exit has been called. checked when _CRTDLL_INIT * or user DLL's _CRT_INIT is called with DLL_PROCESS_DETACH. */ int _C_Termination_Done = FALSE; #endif /* * useful type for initialization and termination declarations */ typedef void (_CALLTYPE1 *PF)(void); #ifdef _CRUISER_ PF *__onexittable = NULL; /* ptr to on exit function table */ #else /* ndef _CRUISER_ */ #if defined(_WIN32_) || defined(_POSIX_) /* * NOTE: THE USE OF THE POINTERS DECLARED BELOW DEPENDS ON THE PROPERTIES * OF C COMMUNAL VARIABLES. SPECIFICALLY, THEY ARE NON-NULL IFF THERE EXISTS * A DEFINITION ELSEWHERE INITIALIZING THEM TO NON-NULL VALUES. */ /* * pointers to initialization functions */ PF _FPinit; /* floating point init. */ /* * pointers to initialization sections */ extern PF __xi_a[], __xi_z[]; /* C initializers */ extern PF __xc_a[], __xc_z[]; /* C++ initializers */ extern PF __xp_a[], __xp_z[]; /* C pre-terminators */ extern PF __xt_a[], __xt_z[]; /* C terminators */ #if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) /* * For MIPS or ALPHA compilers, must explicitly force in and call the floating * point initialization (those system always have floating-point hardware). */ extern void _CALLTYPE1 _fpmath(void); #endif /* * pointers to the start and finish of the _onexit/atexit table */ PF *__onexitbegin; PF *__onexitend; /* * static (internal) function that walks a table of function pointers, * calling each entry between the two pointers, skipping NULL entries * * Needs to be exported for CRT DLL so that C++ initializers in the * client EXE / DLLs can be initialized */ #ifdef CRTDLL void _CALLTYPE1 _initterm(PF *, PF *); #else static void _CALLTYPE4 _initterm(PF *, PF *); #endif #else /* ndef _WIN32_ || _POSIX_ */ #error ERROR - ONLY CRUISER, WIN32, OR POSIX TARGET SUPPORTED! #endif /* _WIN32_ || _POSIX_ */ #endif /* _CRUISER_ */ /*** *_cinit - C initialization * *Purpose: * This routine performs the shared DOS and Windows initialization. * The following order of initialization must be preserved - * ifdef MTHREAD * 0. Call OS2 to bump max file count (mthread only) endif * 1. Check for devices for file handles 0 - 2 * 2. Integer divide interrupt vector setup * 3. General C initializer routines * *Entry: * No parameters: Called from __crtstart and assumes data * set up correctly there. * *Exit: * Initializes C runtime data. * *Exceptions: * *******************************************************************************/ void _CALLTYPE1 _cinit ( void ) { #ifdef _CRUISER_ int fh; /* file handle we're checking */ ULONG htype; /* handle type */ #ifdef MTHREAD /* 0. Call OS/2 to bump max file count -- even though the IO database supports _NFILE_ (256) files, only bump the count to _NFILE_MAXFH_ (40) */ DOSSETMAXFH(_NFILE_MAXFH_); #else #if 0 /* This code is intentionally commented out -- to access more than 20 files re-enable this code */ DOSSETMAXFH(_NFILE_); #endif #endif /* 1. check for devices on file handles 0 - 2 */ for (fh = 0; fh <= 2; ++fh) { ULONG devattr; /* device attributes */ /* default: neither pipe nor device */ _osfile[fh] &= ~(FDEV | FPIPE); if (DOSQUERYHTYPE(fh, &htype, &devattr) == 0) { /* check returned handle type -- only lo byte */ if ((htype & 0xFF) == HANDTYPE_DEVICE) _osfile[fh] |= FDEV; else if ((htype & 0xFF) == HANDTYPE_PIPE) _osfile[fh] |= FPIPE; } } /* 2. general C initializer routines */ /******* FLOATING POINT INIT SHOULD GO HERE (before initclock) ******/ __doinits(); /* execute initializers */ #else /* ndef _CRUISER_ */ #if defined(_WIN32_) || defined(_POSIX_) /* * initialize floating point package, if present */ #if defined(_M_MRX000) || defined(_M_ALPHA) || defined(_M_PPC) /* * The Mips, Alpha, and PPC compilers don't emit external reference to * _fltused. Therefore, must always force in the floating point * initialization. */ _fpmath(); #else if ( _FPinit != NULL ) (*_FPinit)(); #endif /* * do initializations */ _initterm( __xi_a, __xi_z ); /* * do C++ initializations */ _initterm( __xc_a, __xc_z ); #else /* ndef _WIN32_ || _POSIX_ */ #ifdef _MAC_ TBD(); #else /* ndef _MAC_ */ #error ERROR - ONLY CRUISER, WIN32, MAC, OR POSIX TARGET SUPPORTED! #endif /* _MAC_ */ #endif /* _WIN32_ */ #endif /* _CRUISER_ */ } /*** *exit(status), _exit(status), _cexit(void), _c_exit(void) - C termination * *Purpose: * * Entry points: * * exit(code): Performs all the C termination functions * and terminates the process with the return code * supplied by the user. * * _exit(code): Performs a quick exit routine that does not * do certain 'high-level' exit processing. The _exit * routine terminates the process with the return code * supplied by the user. * * _cexit(): Performs the same C lib termination processing * as exit(code) but returns control to the caller * when done (i.e., does NOT terminate the process). * * _c_exit(): Performs the same C lib termination processing * as _exit(code) but returns control to the caller * when done (i.e., does NOT terminate the process). * * Termination actions: * * exit(), _cexit(): * * 1. call user's terminator routines * 2. call C runtime preterminators * * _exit(), _c_exit(): * * 3. call C runtime terminators * 4. return to DOS or caller * * Notes: * * The termination sequence is complicated due to the multiple entry * points sharing the common code body while having different entry/exit * sequences. * * Multi-thread notes: * * 1. exit() should NEVER be called when mthread locks are held. * The exit() routine can make calls that try to get mthread locks. * * 2. _exit()/_c_exit() can be called from anywhere, with or without locks held. * Thus, _exit() can NEVER try to get locks (otherwise, deadlock * may occur). _exit() should always 'work' (i.e., the process * should always terminate successfully). * * 3. Only one thread is allowed into the exit code (see _lockexit() * and _unlockexit() routines). * *Entry: * exit(), _exit() * int status - exit status (0-255) * * _cexit(), _c_exit() * * *Exit: * exit(), _exit() * * * _cexit(), _c_exit() * Return to caller * *Uses: * *Exceptions: * *******************************************************************************/ /* worker routine prototype */ static void _CALLTYPE4 doexit (int code, int quick, int retcaller); void _CALLTYPE1 exit ( int code ) { doexit(code, 0, 0); /* full term, kill process */ } #ifndef _POSIX_ void _CALLTYPE1 _exit ( int code ) { doexit(code, 1, 0); /* quick term, kill process */ } void _CALLTYPE1 _cexit ( void ) { doexit(0, 0, 1); /* full term, return to caller */ } void _CALLTYPE1 _c_exit ( void ) { doexit(0, 1, 1); /* quick term, return to caller */ } #endif /* _POSIX_ */ static void _CALLTYPE4 doexit ( int code, int quick, int retcaller ) { #ifdef MTHREAD _lockexit(); /* assure only 1 thread in exit path */ #endif #if defined(_WIN32_) && defined(CRTDLL) _C_Termination_Done = TRUE; #endif /* save callable exit flag (for use by terminators) */ _exitflag = (char) retcaller; /* 0 = term, !0 = callable exit */ if (!quick) { #ifdef _CRUISER_ /* * do _onexit/atexit() terminators */ if ( __onexitend != NULL ) _doinitterm(__onexitend); /* call pre-terminators (flushall, rmtmp) */ __dopreterms(); } /* do C terminators */ __doterms(); #else /* ndef _CRUISER_ */ #if defined(_WIN32_) || defined(_POSIX_) /* * do _onexit/atexit() terminators * (if there are any) * * These terminators MUST be executed in reverse order (LIFO)! * * NOTE: * This code assumes that __onexitbegin points * to the first valid onexit() entry and that * __onexitend points past the last valid entry. * If __onexitbegin == __onexitend, the table * is empty and there are no routines to call. */ if (__onexitbegin) { PF * pfend = __onexitend; while ( -- pfend >= __onexitbegin ) /* * if current table entry is non-NULL, * call thru it. */ if ( *pfend != NULL ) (**pfend)(); } /* * do pre-terminators */ _initterm(__xp_a, __xp_z); } /* * do terminators */ _initterm(__xt_a, __xt_z); #else /* ndef _WIN32_ || _POSIX_ */ #error ERROR - ONLY CRUISER, WIN32, OR POSIX TARGET SUPPORTED! #endif /* _WIN32_ || _POSIX_ */ #endif /* _CRUISER_ */ #ifdef _WIN32DOS_ /* * TEMPORARY HACK! THE CODE BELOW IS INTENDED TO ALLOW LIMITED USE OF THE * C RUNTIME ON WIN32/DOS. IT WILL BE DELETED AS SOON AS THEY IMPLEMENT * VirtualAlloc()! */ HeapDestroy(_HeapHandle); #endif /* _WIN32DOS_ */ /********** FLOATING POINT TERMINATION SHOULD GO HERE ************/ /* return to OS/2 or to caller */ if (retcaller) { #ifdef MTHREAD _unlockexit(); /* unlock the exit code path */ #endif return; } #ifdef _CRUISER_ DOSEXIT(EXIT_PROCESS, code); #else /* ndef _CRUISER_ */ #ifdef _WIN32_ ExitProcess(code); #else /* ndef _WIN32_ */ #ifdef _POSIX_ _exit(code); #else /* ndef _POSIX_ */ #ifdef _MAC_ TBD(); #else /* ndef _MAC_ */ #error ERROR - ONLY CRUISER, WIN32, MAC, OR POSIX TARGET SUPPORTED! #endif /* _POSIX_ */ #endif /* _MAC_ */ #endif /* _WIN32_ */ #endif /* _CRUISER_ */ } #ifdef MTHREAD /*** * _lockexit - Aquire the exit code lock * *Purpose: * Makes sure only one thread is in the exit code at a time. * If a thread is already in the exit code, it must be allowed * to continue. All other threads must pend. * * Notes: * * (1) It is legal for a thread that already has the lock to * try and get it again(!). That is, consider the following * sequence: * * (a) program calls exit() * (b) thread locks exit code * (c) user onexit() routine calls _exit() * (d) same thread tries to lock exit code * * Since _exit() must ALWAYS be able to work (i.e., can be called * from anywhere with no regard for locking), we must make sure the * program does not deadlock at step (d) above. * * (2) If a thread executing exit() or _exit() aquires the exit lock, * other threads trying to get the lock will pend forever. That is, * since exit() and _exit() terminate the process, there is not need * for them to unlock the exit code path. * * (3) Note that onexit()/atexit() routines call _lockexit/_unlockexit * to protect mthread access to the onexit table. * * (4) The _lockexit/_unlockexit routines are very complicated in 286 * OS/2 since a thread that held a lock could not request the lock again. * The 32-bit OS/2 semaphore calls DO allow a single thread to aquire the * same lock multiple times* thus, this version is straight forward. * *Entry: * *Exit: * Calling thread has exit code path locked on return. * *Exceptions: * *******************************************************************************/ void _CALLTYPE1 _lockexit ( void ) { _mlock(_EXIT_LOCK1); } /*** * _unlockexit - Release exit code lock * *Purpose: * [See _lockexit() description above.] * * This routine is called by _cexit(), _c_exit(), and onexit()/atexit(). * The exit() and _exit() routines never unlock the exit code path since * they are terminating the process. * *Entry: * Exit code path is unlocked. * *Exit: * *Exceptions: * *******************************************************************************/ void _CALLTYPE1 _unlockexit ( void ) { _munlock(_EXIT_LOCK1); } #endif /* MTHREAD */ #if defined(_WIN32_) || defined(_POSIX_) /*** * static void _initterm(PF * pfbegin, PF * pfend) - call entries in * function pointer table * *Purpose: * Walk a table of function pointers, calling each entry, as follows: * * 1. walk from beginning to end, pfunctbl is assumed to point * to the beginning of the table, which is currently a null entry, * as is the end entry. * 2. skip NULL entries * 3. stop walking when the end of the table is encountered * *Entry: * PF *pfbegin - pointer to the beginning of the table (first valid entry). * PF *pfend - pointer to the end of the table (after last valid entry). * *Exit: * No return value * *Notes: * This routine must be exported in the CRT DLL model so that the client * EXE and client DLL(s) can call it to initialize their C++ constructors. * *Exceptions: * If either pfbegin or pfend is NULL, or invalid, all bets are off! * *******************************************************************************/ #ifdef CRTDLL void _CALLTYPE1 _initterm ( #else static void _CALLTYPE4 _initterm ( #endif PF * pfbegin, PF * pfend ) { /* * walk the table of function pointers from the bottom up, until * the end is encountered. Do not skip the first entry. The initial * value of pfbegin points to the first valid entry. Do not try to * execute what pfend points to. Only entries before pfend are valid. */ while ( pfbegin < pfend ) { /* * if current table entry is non-NULL, call thru it. */ if ( *pfbegin != NULL ) (**pfbegin)(); ++pfbegin; } } #endif /* _WIN32_ || _POSIX_ */