/* %W% %E% */ /* * Copyright Microsoft Corporation, 1983-1987 * * This Module contains Proprietary Information of Microsoft * Corporation and should be treated as Confidential. */ /**************************************************************** * * * LINKER INITIALIZATION * * * ****************************************************************/ #include /* Types and constants */ #include /* More of the same */ #include /* More of the same */ #include /* Linker I/O definitions */ #include /* DOS & 286 .EXE format definitions */ #if EXE386 #include /* 386 .EXE format definitions */ #endif #include /* Signal definitions */ #if QCLINK #include #endif #include /* Error messages */ #if OSMSDOS AND NOT (WIN_NT OR DOSEXTENDER OR DOSX32) AND NOT WIN_3 #define INCL_BASE #define INCL_DOSMISC #include /* OS/2 system calls */ #if defined(M_I86LM) #undef NEAR #define NEAR #endif #endif #include /* External declarations */ #include #include #if defined(DOSX32) OR defined(WIN_NT) extern char FAR * _stdcall GetCommandLineA(void); #endif /* * FUNCTION PROTOTYPES */ LOCAL void NEAR InitLeadByte(void); LOCAL void NEAR SetupEnv(void); LOCAL int NEAR IsPrefix(BYTE *pszPrefix, BYTE *pszString); #if TCE extern SYMBOLUSELIST aEntryPoints; // List of program entry points #endif #if ECS /* * InitLeadByte * * Initialize lead byte table structures. * Returns no meaningful value. */ LOCAL void NEAR InitLeadByte () { struct lbrange { unsigned char low; /* minimum */ unsigned char high; /* maximum */ }; static struct lbrange lbtab[5] = { { 0, 0 } }; struct lbrange *ptab; WORD i; /* index */ COUNTRYCODE cc; /* country code */ cc.country = cc.codepage = 0; if (DosGetDBCSEv(sizeof(lbtab), &cc, (char FAR *)lbtab)) return; // For each range, set corresponding entries in fLeadByte for (ptab = lbtab; ptab->low || ptab->high; ptab++) if (ptab->low >= 0x80) for (i = ptab->low; i <= ptab->high; i++) fLeadByte[i-0x80] = (FTYPE) TRUE; // Mark inclusive range true } #endif /* ECS */ #if NOT (WIN_NT OR DOSX32) /*** _setenvp - stub for C run-time * * Purpose: * Call stub instead of real function, we don't want C run-time to * setup enviroment. * * Input: * None; * * Output: * None; * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ void cdecl _setenvp(void) { return; } /*** IsPrefix - self-explanatory * * Purpose: * Check if one string is a prefix of another. * * Input: * pszPrefix - pointer to prefix string * pszString - the string * * Output: * The function returns TRUE if the first string is a prefix of the * second; otherwise it returns FALSE. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL int NEAR IsPrefix(BYTE *pszPrefix, BYTE *pszString) { while(*pszPrefix) // While not at end of prefix { if (*pszPrefix != *pszString) return(FALSE); // Return zero if mismatch ++pszPrefix; // Increment pointer ++pszString; // Increment pointer } return(TRUE); // We have a prefix } #endif /*** SetupEnv - set up pointer to linker evironment variables * * Purpose: * Every byte in the DGROUP is to valuable to waste it to hold * information available elswere in the memory linker is not using * C run-time GETENV function, which accesses copy of the entire * environemt in the DGROUP placed there by the startup code. * Insted this function scans enviroment and set up pointers to * appropriate strings. Because initially enviroment is in the FAR * memory no space in DGROUP is used. * * Input: * No explicit parameters are passed. * * Output: * Four global pointer set to appropriate enviroment strings * * lpszLink - the LINK * lpszPath - the PATH * lpszTMP - the TMP * lpszLIB - the LIB * lpszQH - the QH for QuickHelp * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL void NEAR SetupEnv(void) { #if WIN_NT OR DOSX32 #if C8_IDE char * pIDE = getenv("_MSC_IDE_FLAGS"); if(pIDE) { if(strstr(pIDE, "FEEDBACK")) { fC8IDE = TRUE; #if DEBUG_IDE fprintf(stdout, "\r\nIDE ACTIVE - FEEDBACK is ON"); #endif } if(strstr(pIDE, "BATCH")) { // NOTE: The link response file will still be echoed in this case! // NOTE: this is different than if you specify /BATCH on the // NOTE: command line -- also, the banner is still displayed // NOTE: this is intentional as the IDE wants to BATCH to cause // NOTE: the linker not to prompt, but it does want the banner // NOTE: and response file echoed unless /NOLOGO is also specified // NOTE: see CAVIAR 2378 [rm] fNoprompt = (FTYPE) TRUE; fPauseRun = FALSE; /* Disable /PAUSE */ #if DEBUG_IDE fprintf(stdout, "\r\nIDE ACTIVE - BATCH is ON"); #endif } if(strstr(pIDE, "NOLOGO")) { fNoBanner = (FTYPE) TRUE; #if DEBUG_IDE fprintf(stdout, "\r\nIDE ACTIVE - LOGO is OFF"); #endif } } #if DEBUG_IDE else fprintf(stdout, "\r\nIDE NOT ACTIVE"); fflush(stdout); #endif #endif // C8_IDE lpszPath = getenv("PATH"); lpszLink = getenv("LINK"); lpszTMP = getenv("TMP"); lpszLIB = getenv("LIB"); lpszQH = getenv("QH"); lpszHELPFILES = getenv("HELPFILES"); lpszCmdLine = GetCommandLineA(); while (*lpszCmdLine != ' ') lpszCmdLine++; #else WORD selEnv; WORD cmdOffset; register WORD offMac; char FAR *lpszEnv; char FAR *lpch; SBTYPE buf; register WORD ich; WORD fEOS; #if QCLINK OR CPU8086 OR DOSEXTENDER // Get the segment address of the environment block // and set the command line offset to infinity. We // stop scanning environment block at NULL string. lpszEnv = (char FAR *) (((long) _psp << 16) + 0x2c); selEnv = *((WORD FAR *) lpszEnv); lpszCmdLine = (char FAR *)(((long) _psp << 16) + 0x80); lpszCmdLine[lpszCmdLine[0] + 1] = '\0'; lpszCmdLine++; cmdOffset = 0xffff; #else if (DosGetEnv((unsigned FAR *) &selEnv, (unsigned FAR *) &cmdOffset)) return; #endif lpszEnv = (char FAR *)((long) selEnv << 16); #if NOT (QCLINK OR CPU8086 OR DOSEXTENDER) lpszCmdLine = lpszEnv + cmdOffset; // Skip LINK lpszCmdLine += _fstrlen(lpszCmdLine) + 1; #endif // Skip leading spaces in command line while (*lpszCmdLine == ' ') lpszCmdLine++; lpch = lpszEnv; for (offMac = 0; offMac < cmdOffset && *lpszEnv; ) { // Copy the enviroment variable string into near buffer ich = 0; while (*lpch && ich < sizeof(buf) - 1) buf[ich++] = *lpch++; if (*lpch == '\0') { // Skip over terminating zero lpch++; fEOS = TRUE; } else fEOS = FALSE; buf[ich] = '\0'; // Check what it is and setup appropriate pointer if (lpszPath == NULL && IsPrefix((BYTE *) "PATH=", buf)) lpszPath = lpszEnv + 5; else if (lpszLink == NULL && IsPrefix((BYTE *) "LINK=", buf)) lpszLink = lpszEnv + 5; else if (lpszTMP == NULL && IsPrefix((BYTE *) "TMP=", buf)) lpszTMP = lpszEnv + 4; else if (lpszLIB == NULL && IsPrefix((BYTE *) "LIB=", buf)) lpszLIB = lpszEnv + 4; else if (lpszQH == NULL && IsPrefix((BYTE *) "QH=", buf)) lpszQH = lpszEnv + 3; else if (lpszHELPFILES == NULL && IsPrefix((BYTE *) "HELPFILES=", buf)) lpszHELPFILES = lpszEnv + 10; // If everything setup don't bother to look father if (lpszPath && lpszLink && lpszTMP && lpszLIB && lpszQH && lpszHELPFILES) break; // Update enviroment pointer and offset in enviroment segment offMac += ich; if (!fEOS) { // Oops ! - enviroment variable longer then buffer // skip to its end while (*lpch && offMac < cmdOffset) { lpch++; offMac++; } // Skip over terminating zero lpch++; offMac++; } lpszEnv = lpch; } #endif } #if FALSE /*** Dos3SetMaxFH - set max file handle count for DOS * * Purpose: * Sets the maximum number of files that may be opened * simultaneously using handles by the linker. * * Input: * cFH - number of desired handles * * Output: * No explicit value is returned. * * Exceptions: * None. * * Notes: * This function uses the int 21h function 67h which available on * DOS 3.3 and higher. The function fails if the requested number of * handles is greater then 20 and there is not sufficient free memory * in the system to allocate a new block to hold the enlarged table. * * If the number of handles requested is larger the available * entries in the system's global table for file handles (controlled * by the FILES entry in CONFIG.SYS), no error is returned. * However, a subsequent attempt to open a file or create a new * file will fail if all entries in the system's global file table * are in use, even if the requesting process has not used up all * of its own handles * * We don't check for error, because we can't do much about it. * Linker will try to run with what is available. * *************************************************************************/ LOCAL void NEAR Dos3SetMaxFH(WORD cFH) { if ((_osmajor >= 3) && (_osminor >= 30)) { _asm { mov ax, 0x6700 mov bx, cFH int 0x21 } } } #endif /**************************************************************** * * * InitializeWorld: * * * * This function takes no arguments and returns no meaningful * * value. It sets up virtual memory, the symbol table * * Handlers, and it initializes segment structures. * * * ****************************************************************/ void InitializeWorld(void) { #if OSMSDOS BYTE buf[512]; /* Temporary buffer */ char FAR *lpch; /* Temporary pointer */ int i; /* Temporary index */ #endif #if NOT (FIXEDSTACK OR CPU386) InitStack(); /* Initialize stack */ #endif #if OSMSDOS DskCur = (BYTE) (_getdrive() - 1); /* Get current (default) disk drive */ #if FALSE if(!isatty(fileno(stderr))) /* No prompts if output not console */ fNoprompt = TRUE; #endif #if CRLF /* Default mode of stdout, stdin, stderr is text, change to binary. */ _setmode(fileno(stdout),O_BINARY); if(stderr != stdout) _setmode(fileno(stderr),O_BINARY); _setmode(fileno(stdin),O_BINARY); #endif #endif InitSym(); /* Initialize symbol table handler */ DeclareStdIds(); // Install CTRL-C handler #if OSMSDOS AND NOT WIN_NT signal(SIGINT, (void (__cdecl *)(int)) UserKill); #endif /* OSMSDOS */ #if OSXENIX if(signal(SIGINT,UserKill) == SIG_IGN) signal(SIGINT,SIG_IGN); /* Trap user interrupts */ if(signal(SIGHUP,UserKill) == SIG_IGN) signal(SIGHUP,SIG_IGN); /* Trap hangup signal */ if(signal(SIGTERM,UserKill) == SIG_IGN) signal(SIGTERM,SIG_IGN); /* Trap software termination */ #endif #if SYMDEB InitDbRhte(); #endif #if ECS InitLeadByte(); /* Initialize lead byte table */ #endif #if OSMSDOS // Initialize LINK environment. // Do it yourself to save the memory. SetupEnv(); /* Process switches from LINK environment variable */ if (lpszLink != NULL) { lpch = lpszLink; /* Skip leading whitespace. */ while(*lpch == ' ' || *lpch == '\t') lpch++; if(*lpch++ == CHSWITCH) { // If string begins with switchr // Copy string to buf, removing whitespace for (i = 1; *lpch && i < sizeof(buf); lpch++) if (*lpch != ' ' && *lpch != '\t') buf[i++] = *lpch; buf[0] = (BYTE) (i - 1); /* Set the length of buf */ if(buf[0]) /* If any switches, process them */ BreakLine(buf,ProcFlag,CHSWITCH); } } #endif #if CPU286 if (_osmode == OS2_MODE) { DosSetMaxFH(128); /* This is the same as _NFILE in crt0dat.asm */ DosError(EXCEPTION_DISABLE); } #if FALSE else Dos3SetMaxFH(40); #endif #endif #if FALSE AND CPU8086 Dos3SetMaxFH(40); #endif // Initialize import/export tables InitByteArray(&ResidentName); InitByteArray(&NonResidentName); InitByteArray(&ImportedName); ImportedName.byteMac++; // Ensure non-zero offsets to imported names InitWordArray(&ModuleRefTable); InitByteArray(&EntryTable); #if TCE aEntryPoints.cMaxEntries = 64; aEntryPoints.pEntries = (RBTYPE*)GetMem(aEntryPoints.cMaxEntries * sizeof(RBTYPE*)); #endif } #if (OSXENIX OR OSMSDOS OR OSPCDOS) AND NOT WIN_NT /**************************************************************** * * * UserKill: * * * * Clean up if linker killed by user. * * * ****************************************************************/ void cdecl UserKill() { signal(SIGINT, SIG_IGN); /* Disallow ctrl-c during handler */ CtrlC(); } #endif /* * InitTabs: * * Initialize tables required in Pass 1. */ void InitTabs(void) { #if NOT FAR_SEG_TABLES char *tabs; /* Pointer to table space */ unsigned cbtabs; #endif /* Initialize the following tables: * * NAME TYPE * ---- ---- * mpsegraFirst RATYPE * mpgsnfCod FTYPE * mpgsndra RATYPE * mpgsnrprop RBTYPE * mplnamerhte RBTYPE */ #if FAR_SEG_TABLES mplnamerhte = (RBTYPE FAR *) GetMem(lnameMax * sizeof(RBTYPE)); mpsegraFirst = (RATYPE FAR *) GetMem(gsnMax * sizeof(RATYPE)); mpgsnfCod = (FTYPE FAR *) mpsegraFirst; /* Use same space twice */ mpgsndra = (RATYPE FAR *) GetMem(gsnMax * sizeof(RATYPE)); mpgsnrprop = (RBTYPE FAR *) GetMem(gsnMax * sizeof(RBTYPE)); #else mplnamerhte = (RBTYPE *) malloc(lnameMax * sizeof(RBTYPE)); if (mplnamerhte == NULL) Fatal(ER_seglim); memset(mplnamerhte, 0, lnameMax * sizeof(RATYPE)); cbtabs = gsnMax * (sizeof(RATYPE) + sizeof(RATYPE) + sizeof(RBTYPE)); if((tabs = malloc(cbtabs)) == NULL) Fatal(ER_seglim); memset(tabs,0,cbtabs); /* Clear everything */ mpsegraFirst = (RATYPE *) tabs; /* Initialize base */ mpgsnfCod = (FTYPE *) mpsegraFirst; /* Use same space twice */ mpgsndra = (RATYPE *) &mpsegraFirst[gsnMax]; mpgsnrprop = (RBTYPE *) &mpgsndra[gsnMax]; #endif } /* * InitP2Tabs: * * Initialize tables not needed until Pass 2. */ void InitP2Tabs (void) { char FAR *tabs; /* Pointer to table space */ unsigned cbtabs; /* Size of table space */ unsigned TabSize; TabSize = gsnMac + iovMac + 1; /* Tables required regardless of exe format generated: * mpsegsa SATYPE * mpgsnseg SEGTYPE */ #if FAR_SEG_TABLES cbtabs = 0; mpsegsa = (SATYPE FAR *) GetMem(TabSize * sizeof(SATYPE)); mpgsnseg = (SEGTYPE FAR *) GetMem(TabSize * sizeof(SEGTYPE)); mpseggsn = (SNTYPE FAR *) GetMem(TabSize * sizeof(SNTYPE)); #else cbtabs = TabSize * (sizeof(RATYPE) + sizeof(SATYPE)); #endif /* Tables required according to exe format generated: * * DOS 3: * mpsegcb[TabSize] long * mpsegFlags[TabSize] FTYPE * mpsegalign[TabSize] ALIGNTYPE * mpsegiov[TabSize] IOVTYPE * mpiovRlc[iovMac] RUNRLC * Seg. exe: * mpsacb[SAMAX] long * mpsadraDP[SAMAX] long; for O68K * mpsacbinit[SAMAX] long * mpsaRlc[SAMAX] HASHRLC FAR * * mpsaflags[SAMAX] WORD; DWORD for EXE386 * htsaraep[HEPLEN] EPTYPE FAR * * X.out: * mpsegcb[TabSize] long * mpsegFlags[TabSize] FTYPE * mpstsa[TabSize] SATYPE */ #if EXE386 if(fNewExe) cbtabs += (SAMAX*(sizeof(long)+sizeof(long)+sizeof(DWORD)+ sizeof(DWORD))) + (HEPLEN * sizeof(WORD)); else #else if(fNewExe) #if O68K cbtabs += (SAMAX*(sizeof(long)+sizeof(long)+sizeof(long)+sizeof(WORD)+ sizeof(WORD))) + (HEPLEN * sizeof(WORD)); #else cbtabs += (SAMAX*(sizeof(long)+sizeof(long)+sizeof(RLCHASH FAR *) + sizeof(WORD))) + (HEPLEN * sizeof(EPTYPE FAR *)); #endif else #endif #if OEXE cbtabs += TabSize * (sizeof(long) + sizeof(FTYPE) + sizeof(ALIGNTYPE)); #else cbtabs += TabSize * (sizeof(long) + sizeof(FTYPE) + sizeof(SATYPE)); #endif cbtabs += sizeof(WORD); tabs = GetMem(cbtabs); #if NOT FAR_SEG_TABLES mpgsnseg = (SEGTYPE *)tabs; mpsegsa = (SATYPE *)&mpgsnseg[TabSize]; tabs = (char *)&mpsegsa[TabSize]; #endif #if OSEGEXE if(fNewExe) { mpsacb = (DWORD FAR *) tabs; #if O68K mpsadraDP = (long *)&mpsacb[SAMAX]; mpsacbinit = (long *)&mpsadraDP[SAMAX]; #else mpsacbinit = (DWORD FAR *)&mpsacb[SAMAX]; #endif #if EXE386 mpsacrlc = (DWORD *)&mpsacbinit[SAMAX]; mpsaflags = (DWORD *)&mpsacrlc[SAMAX]; #else mpsaRlc = (RLCHASH FAR * FAR *) &mpsacbinit[SAMAX]; mpsaflags = (WORD FAR *) &mpsaRlc[SAMAX]; #endif htsaraep = (EPTYPE FAR * FAR *)&mpsaflags[SAMAX]; } else #endif { #if ODOS3EXE OR OIAPX286 mpsegcb = (long FAR *) tabs; mpsegFlags = (FTYPE FAR *)&mpsegcb[TabSize]; #if OEXE mpsegalign = (ALIGNTYPE FAR *)&mpsegFlags[TabSize]; #if OVERLAYS cbtabs = iovMac * sizeof(RUNRLC) + TabSize * sizeof(IOVTYPE) + (sizeof(DWORD) - 1); // leave room to align mpiovRlc mpsegiov = (IOVTYPE FAR*) GetMem(cbtabs); // align mpiovRlc on a DWORD, the alignment needed by struct _RUNRLC mpiovRlc = (RUNRLC FAR*) ( ( (__int64)&mpsegiov[TabSize] + (sizeof(DWORD) - 1) ) & ~(sizeof(DWORD) - 1) ); #endif #endif #if OIAPX286 mpstsa = (SATYPE *)&mpsegFlags[TabSize]; #endif #endif /* ODOS3EXE OR OIAPX286 */ } /* Attempt to allocate space for mpextprop. */ cbtabs = extMax * sizeof(RBTYPE); mpextprop = (RBTYPE FAR *) GetMem(cbtabs); }