/* * Copyright Microsoft Corporation 1985-1987 * * This Module contains Proprietary Information of Microsoft * Corporation and should be treated as Confidential. */ /**************************************************************** * * * NEWDEB.C * * * * Symbolic debugging support. * * * ****************************************************************/ #include /* Basic types and constants */ #include /* More types and constants */ #include /* Types and constants */ #include /* Linker input/output */ #if OIAPX286 #include /* Xenix executable format defs. */ #endif #if OEXE #include /* Segmented executable format */ #endif #if EXE386 #include #endif #include /* Error messages */ #include /* External function declarations */ #ifndef CVVERSION #if OIAPX286 #define CVVERSION 0 /* Assume new CV exe format */ #else #define CVVERSION 1 /* Assume new CV exe format */ #endif #endif #if (CPU8086 OR CPU286) #define TFAR far #else #define TFAR #endif #include /* Symbolic debug types */ extern SEGTYPE segAdjCom; /* Segment moved by 0x100 in .com programs */ #if AUTOVM BYTE FAR * NEAR FetchSym1(RBTYPE rb, WORD Dirty); #define FETCHSYM FetchSym1 #define PROPSYMLOOKUP EnterName #else #define FETCHSYM FetchSym #define PROPSYMLOOKUP EnterName #endif #define CVDEBUG FALSE #define SRCDEBUG FALSE #define DNT_START 64 #define Round2Dword(x) (((x) + 3L) & ~3L) typedef struct raPair { DWORD raStart; DWORD raEnd; } RAPAIR; /* * FUNCTION PROTOTYPES */ LOCAL WORD NEAR IsDebTyp(APROPSNPTR prop); LOCAL WORD NEAR IsDebSym(APROPSNPTR prop); LOCAL int NEAR OutLibSec(void); void NEAR GetName(AHTEPTR ahte, BYTE *pBuf); LOCAL DWORD NEAR OutSrcModule(CVSRC FAR *pSrcLines); LOCAL void NEAR PutDnt(DNT *pDnt); LOCAL WORD NEAR HasCVinfo(APROPFILEPTR apropFile); LOCAL void NEAR OutModules(void); LOCAL void NEAR Pad2Dword(void); #if CVDEBUG LOCAL void NEAR DumpDNT(DNT *pDnt); #endif extern long lfaBase; /* Base address */ extern int fSameComdat; /* Set if LINSYM to the same COMDAT */ /* * CodeView signature - if changes notify the developers of the * following programs: * - QuickC * - Resource Compiler - Windows and PM * - CodeView and its utilities */ char szSignature[4] = "NB05"; RBTYPE rhteDebSrc; /* Class "DEBSRC" virt addr */ RBTYPE rhteDebSym; /* Class "DEBSYM" virt addr */ RBTYPE rhteDebTyp; /* Class "DEBTYP" virt addr */ RBTYPE rhteTypes; RBTYPE rhteSymbols; RBTYPE rhte0Types; RBTYPE rhte0Symbols; LOCAL SBTYPE sbLastModule; /* Name of THEADR last observed */ #if NOT CVVERSION LOCAL long lfaDebHdr; /* Position of section table */ LOCAL long lfaSegMod; #endif LOCAL WORD dntMax; // DNT table size LOCAL WORD dntMac; // Count of DNT entries in table LOCAL DNT FAR *rgDnt; // Table of DNT entries LOCAL DWORD FAR *fileBase; // Table of offsets to source file info LOCAL RAPAIR FAR *raSeg; // Table of physical starting and ending offsets // of the contribution to the logical segments LOCAL WORD FAR *segNo; // Table of physical segment indicies LOCAL WORD cMac; // Current number of elements in the above tables #ifdef CVPACK_MONDO #define CVPACK_SHARED 1 #define REVERSE_MODULE_ORDER_FOR_CVPACK 1 #else #define CVPACK_SHARED 0 #define REVERSE_MODULE_ORDER_FOR_CVPACK 0 #endif //these macros help to make the source not so cluttered with #ifdefs... #if CVPACK_SHARED #define IF_NOT_CVPACK_SHARED(x) #define WriteCopy(x,y) WriteSave(TRUE, x, y) #define WriteNocopy(x,y) WriteSave(FALSE, x, y) #define FTELL_BSRUNFILE() lposCur #define LINK_TRACE(x) // cvpack might read parts of the header more than once, we use this // constant to ensure that at least CB_HEADER_SAVE bytes are always // available to be re-read by cvpack #define CB_HEADER_SAVE 128 void WriteSave(FTYPE fCopy, void *pv, UINT cb); void WriteFlushSignature(void); void WriteFlushAll(void); // cvpack cached blocks... typedef struct _BL { long lpos; // position of this block in the file BYTE * pb; // pointer to bytes in this block } BL; #define iblNil (-1) static long lposCur; // current position in the file static long lposMac; // size of the file static long iblLim; // number of blocks used static long iblCur; // current block we are reading static long iblMac; // number of blocks allocated static long cbRealBytes; // number of bytes actually written to the file static int ichCur; // index within the current block static int cbCur; // number of bytes left in the current block static BL *rgbl; // array of buffered write blocks // number of bytes in a particular block __inline int CbIbl(int ibl) { // compute the difference between this block and the next block // unless this is the last block then use lposMac if (ibl == iblLim - 1) return lposMac - rgbl[ibl].lpos; else return rgbl[ibl+1].lpos - rgbl[ibl].lpos; } #define C_BL_INIT 256 #else #define IF_NOT_CVPACK_SHARED(x) x #define WriteCopy(x,y) WriteExe(x,y) #define WriteNocopy(x,y) WriteExe(x,y) #define FTELL_BSRUNFILE() ftell(bsRunfile) #define LINK_TRACE(x) #endif #if CVDEBUG LOCAL void NEAR DumpDNT(DNT *pDnt) { if (pDnt == NULL) return; fprintf(stdout, "iMod = %d(0x%x)", pDnt->iMod, pDnt->iMod); switch (pDnt->sst) { case SSTMODULES: case SSTMODULES4: fprintf(stdout, " SSTMODULES: "); break; case SSTTYPES: case SSTTYPES4: fprintf(stdout, " SSTYPES: "); break; case SSTPUBLICS: case SSTPUBLICS4: fprintf(stdout, " SSTPUBLICS: "); break; case SSTPUBLICSYM: fprintf(stdout, " SSTPUBLICSYM: "); break; case SSTSYMBOLS: case SSTSYMBOLS4: fprintf(stdout, " SSTSYMBOLS: "); break; case SSTALIGNSYM: fprintf(stdout, " SSTALIGNSYM: "); break; case SSTSRCLINES: case SSTNSRCLINES: case SSTSRCLNSEG: fprintf(stdout, " SSTSRCLINES: "); break; case SSTSRCMODULE: fprintf(stdout, " SSTSRCMODULE: "); break; case SSTLIBRARIES: case SSTLIBRARIES4: fprintf(stdout, " SSTLIBRARIES: "); break; case SSTGLOBALSYM: fprintf(stdout, " SSTGLOBALSYM: "); break; case SSTGLOBALPUB: fprintf(stdout, " SSTGLOBALPUB: "); break; case SSTGLOBALTYPES: fprintf(stdout, " SSTGLOBALTYPES: "); break; case SSTMPC: fprintf(stdout, " SSTMPC: "); break; case SSTSEGMAP: fprintf(stdout, " SSTSEGMAP: "); break; case SSTSEGNAME: fprintf(stdout, " SSTSEGNAME: "); break; case SSTIMPORTS: fprintf(stdout, " SSTIMPORTS: "); break; default: fprintf(stdout, " UNKNOWN !?!: "); break; } fprintf(stdout, "file offset 0x%lx; size 0x%x\r\n", lfaBase+pDnt->lfo, pDnt->cb); } #endif #if SRCDEBUG LOCAL void NEAR DumpSrcLines(DWORD vLines) { CVSRC cvSrc; CVGSN cvGsn; CVLINE cvLine; DWORD curSrc; DWORD curGsn; DWORD curLine; SBTYPE fileName; DWORD i; WORD j; fprintf(stdout, "\r\nList at %lx\r\n\r\n", vLines); for (curSrc = vLines; curSrc != 0L; curSrc = cvSrc.vpNext) { memcpy(&cvSrc, mapva(curSrc, FALSE), sizeof(CVSRC)); memcpy(fileName, mapva(cvSrc.vpFileName, FALSE), cvSrc.cbName); fileName[cvSrc.cbName] = '\0'; fprintf(stdout, "'%s' --> code segments: %lu; source lines: %lu\r\n", fileName, cvSrc.cSegs, cvSrc.cLines); for (curGsn = cvSrc.vpGsnFirst; curGsn != 0L; curGsn = cvGsn.vpNext) { memcpy(&cvGsn, mapva(curGsn, FALSE), sizeof(CVGSN)); fprintf(stdout, " Logical segment %d; source lines: %d; start: %lx; end: %lx\r\n", cvGsn.seg, cvGsn.cLines, cvGsn.raStart, cvGsn.raEnd); for (curLine = cvGsn.vpLineFirst, i = 1L; curLine != 0L; curLine = cvLine.vpNext) { memcpy(&cvLine, mapva(curLine, FALSE), sizeof(CVLINE)); for (j = 0; j < cvLine.cPair; j++, i++) fprintf(stdout, " %8lu: %u:%lx\r\n", i, cvLine.rgLn[j], cvLine.rgOff[j]); } } } } #endif /**************************************************************** * * * Initialize variables for symbolic debug processing. * * Pass 1. * * * ****************************************************************/ void NEAR InitDeb1 (void) { #if ODOS3EXE if (vfDSAlloc) { OutWarn(ER_dbgdsa); vfDSAlloc = FALSE; } #endif #if FEXEPACK if (fExePack) { OutWarn(ER_dbgexe); fExePack = FALSE; } #endif } void InitDbRhte () { PROPSYMLOOKUP((BYTE *) "\006DEBTYP", ATTRNIL, TRUE); rhteDebTyp = vrhte; PROPSYMLOOKUP((BYTE *) "\006DEBSYM", ATTRNIL, TRUE); rhteDebSym = vrhte; PROPSYMLOOKUP((BYTE *) "\006 TYPES", ATTRNIL, TRUE); rhte0Types = vrhte; PROPSYMLOOKUP((BYTE *) "\010 SYMBOLS", ATTRNIL, TRUE); rhte0Symbols = vrhte; PROPSYMLOOKUP((BYTE *) "\007$$TYPES", ATTRNIL, TRUE); rhteTypes = vrhte; PROPSYMLOOKUP((BYTE *) "\011$$SYMBOLS", ATTRNIL, TRUE); rhteSymbols = vrhte; } LOCAL void NEAR Pad2Dword(void) { WORD cb; // Number of bytes to write static DWORD dwZero; // Calculate needed padding cb = (WORD)(sizeof(DWORD)-((WORD) FTELL_BSRUNFILE() % sizeof(DWORD))); if (cb != sizeof(DWORD)) WriteCopy(&dwZero, cb); } /*** GetName - get symbol associated with given property cell * * Purpose: * Find the symbol which has given property. * * Input: * - ahte - pointer to property cell * - pBuf - pointer to ASCII buffer * * Output: * No explicit value is passed. If symbol is found the it is * copied into buffer * * Exceptions: * None. * * Notes: * This functional duplicate of GetPropName, but we want to * call both function as near. * *************************************************************************/ void NEAR GetName(AHTEPTR ahte, BYTE *pBuf) { while(ahte->attr != ATTRNIL) ahte = (AHTEPTR ) FETCHSYM(ahte->rhteNext, FALSE); FMEMCPY((char FAR *) pBuf, ahte->cch, B2W(ahte->cch[0]) + 1); if (B2W(pBuf[0]) < SBLEN) pBuf[pBuf[0] + 1] = '\0'; else pBuf[pBuf[0]] = '\0'; } /*** DebPublic - prepare symbols for debugger * * Purpose: * When the /CODEVIEW option is used then all PUBDEFs and COMDEFs * defined in a given object file are linked into one list. This * function adds one symbol to the list and updates the combined * size of symbols * * Input: * vrprop - virtual pointer to symbol descriptor * rt - OMF record type * * Output: * No explicit value is returned. * Side effects: * - symbol is attached to the module symbol list * * Exceptions: * None. * * Notes: * Symbols are placed on the list in reverse order of their apperance * in the object file. * *************************************************************************/ void DebPublic(RBTYPE vrprop, WORD rt) { APROPFILEPTR apropFile; // Pointer to file entry APROPNAMEPTR apropName; // Real pointer to PUBDEF descriptor APROPUNDEFPTR apropUndef; // Real pointer to COMDEF descriptor APROPALIASPTR apropAlias; // Real pointer to ALIAS descriptor RBTYPE symNext; // Virtual pointer to the next symbol // Update the appropriate field in the current file symtab entry apropFile = ((APROPFILEPTR ) FETCHSYM(vrpropFile, TRUE)); symNext = apropFile->af_publics; apropFile->af_publics = vrprop; apropName = (APROPNAMEPTR) FETCHSYM(vrprop, TRUE); if (TYPEOF(rt) == PUBDEF) apropName->an_sameMod = symNext; else if (TYPEOF(rt) == COMDEF) { apropUndef = (APROPUNDEFPTR) apropName; apropUndef->au_sameMod = symNext; } else if (TYPEOF(rt) == ALIAS) { apropAlias = (APROPALIASPTR) apropName; apropAlias->al_sameMod = symNext; } } LOCAL WORD NEAR IsDebTyp (prop) APROPSNPTR prop; /* Pointer to segment record */ { return(prop->as_attr == ATTRLSN && prop->as_rCla == rhteDebTyp); } LOCAL WORD NEAR IsDebSym (prop) APROPSNPTR prop; /* Pointer to segment record */ { return(prop->as_attr == ATTRLSN && prop->as_rCla == rhteDebSym); } /*** DoDebSrc - store source line information * * Purpose: * Stores source line information from object file. * * Input: * No explicit value is passed. * * Global variables: * - vaCVMac - virtual pointer to the free space in the CV info buffer * * Output: * Returns TRUE if the cv info has been stored in the VM ,or FALSE otherwise. * Side effects: * - source line information is stored in the VM * * Exceptions: * More than 32Mb of CV information - dispaly error and quit * * Notes: * None. * *************************************************************************/ #pragma check_stack(on) WORD DoDebSrc(void) { WORD cbRecSav; // LINNUM record size APROPFILEPTR apropFile; // Current object file property cell static SATYPE prevGsn = 0; // GSN of previous LINNUM record GSNINFO gsnInfo; // GSN info for this LINNUM static CVSRC FAR *pCurSrc; // Pointer to the current file source info CVGSN FAR *pCurGsn; // Pointer to the current code segment descriptor CVGSN FAR *pcvGsn; // Real pointer to the code segment descriptor CVLINE FAR *pCurLine; // Pointer to the current offset/line pair bucket RATYPE ra; // Offset WORD line; // Line number RATYPE raPrev; // Offset of the previous line WORD fChangeInSource; WORD fComdatSplit; DWORD gsnStart; // Start of this gsn APROPSNPTR apropSn; WORD align; WORD threshold; #if !defined( M_I386 ) && !defined( _WIN32 ) SBTYPE nameBuf; #endif cbRecSav = cbRec; if (!GetGsnInfo(&gsnInfo)) return(FALSE); // If LINNUM record is empty, don't do anything if (cbRec == 1) return(FALSE); apropFile = (APROPFILEPTR ) FETCHSYM(vrpropFile, TRUE); // If there is a new source file allocate new CVSRC structure // and link it to the current object file descriptor fChangeInSource = (WORD) (apropFile->af_Src == 0 || !SbCompare(sbModule, sbLastModule,TRUE)); if (fChangeInSource) { #if CVDEBUG sbModule[sbModule[0]+1]='\0'; sbLastModule[sbLastModule[0]+1]='\0'; fprintf(stdout, "Change in source file; from '%s' to '%s'\r\n", &sbLastModule[1], &sbModule[1]); #endif // Search the list of CVSRC structures for this object // file and find out if we heave already seen this source file for (pCurSrc = apropFile->af_Src; pCurSrc;) { #if defined(M_I386) || defined( _WIN32 ) if (SbCompare(sbModule, pCurSrc->fname, TRUE)) #else FMEMCPY((char FAR *) nameBuf, pCurSrc->fname, pCurSrc->fname[0] + 1); if (SbCompare(sbModule, nameBuf, TRUE)) #endif break; else pCurSrc = pCurSrc->next; } if (pCurSrc == NULL) { // New source file pCurSrc = (CVSRC FAR *) GetMem(sizeof(CVSRC)); pCurSrc->fname = GetMem(sbModule[0] + 1); FMEMCPY(pCurSrc->fname, (char FAR *) sbModule, sbModule[0] + 1); if (apropFile->af_Src == NULL) apropFile->af_Src = pCurSrc; else apropFile->af_SrcLast->next = pCurSrc; apropFile->af_SrcLast = pCurSrc; } else { // We have already seen this source file } memcpy(sbLastModule, sbModule, B2W(sbModule[0]) + 1); } else { // Use descriptor set last time we changed source files } // Allocate the new CVGSN structure if any of the following is true // // - this is first batch of source lines // - there is a change in GSNs // - there is a change in source file // - we have source lines for explicitly allocated COMDAT // In this last case we assume that the begin portion of a // given logical segment (gsn) has been filled with contributions // from many object files. Because COMDATs are allocated after all // the object files are read, then adding source // lines of COMDAT to the source lines of preceeding LEDATA records // will mask the contributions from other object files, as the picture // below shows: // // +-------------+<--+ // | | | // | LEDATA from | | // | a.obj | | // | | | // +-------------+ | // | | | Without splitting into fake CVGSN // | LEDATA from | \ the source line for a.obj will // | b.obj | / hide the LEDATA contribution from b.obj // | | | // +-------------+ | // | | | // | COMDAT from | | // | a.obj | | // | | | // +-------------+<--+ // | | // | COMDAT from | // | b.obj | // | | // +-------------+ // // This will be unnecessary only if COMDAT from a.obj immediately // follows LEDATA from a.obj fComdatSplit = FALSE; pCurGsn = pCurSrc->pGsnLast; if (pCurGsn) { // Assume we will be using the current CVGSN if (gsnInfo.fComdat) { // Source lines from LINSYM - Calculate the threshold apropSn = (APROPSNPTR ) FETCHSYM(mpgsnrprop[gsnInfo.gsn], FALSE); if (gsnInfo.comdatAlign) align = gsnInfo.comdatAlign; else align = (WORD) ((apropSn->as_tysn >> 2) & 7); threshold = 1; switch (align) { case ALGNWRD: threshold = 2; break; #if OMF386 case ALGNDBL: threshold = 4; break; #endif case ALGNPAR: threshold = 16; break; case ALGNPAG: threshold = 256; break; } // Check if we have to split CVGSN for this COMDAT fComdatSplit = !fSameComdat && !(apropSn->as_fExtra & COMDAT_SEG) && (gsnInfo.comdatRa - pCurGsn->raEnd > threshold); } else { // Source lines from LINNUM if (pCurGsn->flags & SPLIT_GSN) { // The LINNUM record following the LINSYM record that // caused CVGSN split - we have to move back on CVGSN // list until we find first CVGSN not marked as SPLIT_GSN for (pcvGsn = pCurGsn->prev; pcvGsn != (CVGSN FAR *) pCurSrc;) { if (!(pcvGsn->flags & SPLIT_GSN)) break; else pcvGsn = pcvGsn->prev; } if (pcvGsn == (CVGSN FAR *) pCurSrc) { // There are only SPLIT_GSN on the list - make new CVGSN prevGsn = 0; } else { // Use the first non SPLIT_GSN CVGSN as current one pCurGsn = pcvGsn; } } } } if ((prevGsn == 0) || (mpgsnseg[gsnInfo.gsn] != mpgsnseg[prevGsn]) || fChangeInSource || fComdatSplit) { // Make new CVGSN // Remember LOGICAL segment pCurGsn = (CVGSN FAR *) GetMem(sizeof(CVGSN)); pCurGsn->seg = mpgsnseg[gsnInfo.gsn]; // The start and end offset will be derived from line number/offset pairs pCurGsn->raStart = 0xffffffff; if (fComdatSplit) pCurGsn->flags |= SPLIT_GSN; if (pCurSrc->pGsnFirst == NULL) { pCurSrc->pGsnFirst = pCurGsn; pCurGsn->prev = (CVGSN FAR *) pCurSrc; } else { pCurSrc->pGsnLast->next = pCurGsn; pCurGsn->prev = pCurSrc->pGsnLast; } pCurSrc->pGsnLast = pCurGsn; pCurSrc->cSegs++; #if CVDEBUG sbModule[sbModule[0]+1] = '\0'; fprintf(stdout, "New code segment in '%s'; prevGsn = %x; newGsn = %x %s\r\n", &sbModule[1], prevGsn, gsnInfo.gsn, fComdatSplit ? "COMDAT split" : ""); #endif prevGsn = gsnInfo.gsn; } // Get the offset/line bucket if (pCurGsn->pLineFirst == NULL) { pCurLine = (CVLINE FAR *) GetMem(sizeof(CVLINE)); pCurGsn->pLineFirst = pCurLine; pCurGsn->pLineLast = pCurLine; } else pCurLine = pCurGsn->pLineLast; // Fill in offset/line bucket if (gsnInfo.fComdat) gsnStart = gsnInfo.comdatRa; else gsnStart = mpgsndra[gsnInfo.gsn] - mpsegraFirst[pCurGsn->seg]; raPrev = 0xffff; while (cbRec > 1) // While not at checksum { GetLineOff(&line, &ra); ra += gsnStart; // We have to eliminate line pairs with same ra (for MASM 5.1) if(ra == raPrev) continue; raPrev = ra; // Remember the smallest LOGICAL offset for source line if (ra < pCurGsn->raStart) pCurGsn->raStart = ra; if (line != 0) { if (pCurLine->cPair >= CVLINEMAX) { pCurLine->next = (CVLINE FAR *) GetMem(sizeof(CVLINE)); pCurLine = pCurLine->next; pCurGsn->pLineLast = pCurLine; } pCurLine->rgOff[pCurLine->cPair] = ra; pCurLine->rgLn[pCurLine->cPair] = line; pCurLine->cPair++; pCurSrc->cLines++; pCurGsn->cLines++; } } // Remember last line LOGICAL offset pCurGsn->raEnd = ra; #if CVDEBUG fprintf(stdout, "New source lines for the 0x%x logical code segment; lines %d\r\n start offset %x:%lx end offset %x:%lx; physical address of logical segment %x:%lx\r\n", pCurGsn->seg, pCurGsn->cLines, pCurGsn->seg, pCurGsn->raStart, pCurGsn->seg, pCurGsn->raEnd, mpsegsa[pCurGsn->seg], mpsegraFirst[pCurGsn->seg]); #endif // If /LINENUMBERS and list file open, back up if (vfLineNos && fLstFileOpen) { #if ALIGN_REC pbRec += (cbRec - cbRecSav); #else fseek(bsInput, (long)cbRec - cbRecSav, 1); #endif cbRec = cbRecSav; } return(TRUE); } #pragma check_stack(off) /*** CheckTables - check space in table used by OutSrcModule * * Purpose: * While building the new source module subsection linker needs * to store a lot of information about given source file. Since * we can't predict how many source files were compiled to obtain * this object module or to how many logical segments this object * module contributes code we have to dynamically resize appropriate * tables. * * Input: * cFiles - number of source files compiled to produce * this object module * cSegs - number of logical segments this object module * contributes to. * * Output: * No explicit value is returned. As a side effect the following * tables are allocated or reallocated: * * fileBase - table of offsets to source file info * raSeg - table of physical starting and ending offsets * of the contribution to the logical segments * segNo - table of physical segment indicies * * Exceptions: * Memory allocation problems - fatal error and exit. * * Notes: * When we reallocated the tables we don't have to copy * their old content, because it was used in the previous * object module. * *************************************************************************/ LOCAL void NEAR CheckTables(WORD cFiles, WORD cSegs) { WORD cCur; cCur = (WORD) (cFiles < cSegs ? cSegs : cFiles); if (cCur > cMac) { // We have to reallocate tables or allocate for the first time if (fileBase) FFREE(fileBase); if (raSeg) FFREE(raSeg); if (segNo) FFREE(segNo); fileBase = (DWORD FAR *) GetMem(cCur*sizeof(DWORD)); raSeg = (RAPAIR FAR *) GetMem(cCur*sizeof(RAPAIR)); segNo = (WORD FAR *) GetMem(cCur*sizeof(WORD)); cMac = cCur; } } /*** OutSrcModule - write CV source module * * Purpose: * Create the CV 4.00 format source module descrbing the source line * number to addressing mapping information for one object file * * Input: * - pSrcLines - the list of source file information blocks * * Output: * Total size of the subsection in bytes. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL DWORD NEAR OutSrcModule(CVSRC FAR *pSrcLines) { CVSRC FAR *pCurSrc; // Pointer to current source file CVGSN FAR *pCurGsn; // Pointer to current code segment CVLINE FAR *pLine; // Pointer to source line bucket WORD cFiles; // Number of source files WORD cSegs; // Number of code segments WORD xFile; WORD xSeg; DWORD sizeTotal; // Size of source subsection DWORD srcLnBase; WORD counts[2]; CVLINE FAR *pTmp; #if SRCDEBUG DumpSrcLines(vaLines); #endif // Count total number of source files, total number of code segments for (pCurSrc = pSrcLines, cFiles = 0, cSegs = 0; pCurSrc; cFiles++, pCurSrc = pCurSrc->next) cSegs += pCurSrc->cSegs; CheckTables(cFiles, cSegs); sizeTotal = (DWORD) (2*sizeof(WORD) + cFiles*sizeof(DWORD) + cSegs*(sizeof(raSeg[0]) + sizeof(WORD))); sizeTotal = Round2Dword(sizeTotal); // Make second pass througth the source files and fill in // source module header for (pCurSrc = pSrcLines, xFile = 0, xSeg = 0; xFile < cFiles && pCurSrc; xFile++, pCurSrc = pCurSrc->next) { fileBase[xFile] = sizeTotal; // Add the size of this source file information: // // Source file header: // // +------+------+------------+--------------+------+-------------+ // | WORD | WORD | cSeg*DWORD | 2*cSeg*DWORD | BYTE | cbName*BYTE | // +------+------+------------+--------------+------+-------------+ // sizeTotal += (2*sizeof(WORD) + pCurSrc->cSegs*(sizeof(DWORD) + sizeof(raSeg[0])) + sizeof(BYTE) + pCurSrc->fname[0]); // Pad to DWORD boundary sizeTotal = Round2Dword(sizeTotal); // Walk code segment list for (pCurGsn = pCurSrc->pGsnFirst; pCurGsn; pCurGsn = pCurGsn->next, xSeg++) { raSeg[xSeg].raStart = pCurGsn->raStart; raSeg[xSeg].raEnd = pCurGsn->raEnd; segNo[xSeg] = pCurGsn->seg; // Add size of the offset/line table // // +------+------+-------------+------------+ // | WORD | WORD | cLine*DWORD | cLine*WORD | // +------+------+-------------+------------+ sizeTotal += (2*sizeof(WORD) + pCurGsn->cLines*(sizeof(DWORD) + sizeof(WORD))); // Pad to DWORD boundary sizeTotal = Round2Dword(sizeTotal); } } // Write source module header counts[0] = cFiles; counts[1] = cSegs; WriteCopy(counts, sizeof(counts)); WriteCopy(fileBase, cFiles*sizeof(DWORD)); WriteCopy(raSeg, cSegs*sizeof(RAPAIR)); WriteCopy(segNo, cSegs*sizeof(WORD)); // Pad to DWORD boundary Pad2Dword(); // Make third pass througth the source files and fill in // the source file header and write offset/line pairs for (pCurSrc = pSrcLines, srcLnBase = fileBase[0]; pCurSrc != NULL; pCurSrc = pCurSrc->next, xFile++) { // Add the size of source file header: // // +------+------+------------+--------------+------+-------------+ // | WORD | WORD | cSeg*DWORD | 2*cSeg*DWORD | BYTE | cbName*BYTE | // +------+------+------------+--------------+------+-------------+ // srcLnBase += (2*sizeof(WORD) + pCurSrc->cSegs*(sizeof(DWORD) + sizeof(raSeg[0])) + sizeof(BYTE) + pCurSrc->fname[0]); // Round to DWORD boundary srcLnBase = Round2Dword(srcLnBase); // Walk code segment list and store base offsets for source // line offset/line pairs and record start/stop offsets of // code segments for (xSeg = 0, pCurGsn = pCurSrc->pGsnFirst; pCurGsn != NULL; pCurGsn = pCurGsn->next, xSeg++) { fileBase[xSeg] = srcLnBase; srcLnBase += (2*sizeof(WORD) + pCurGsn->cLines*(sizeof(DWORD) + sizeof(WORD))); // Round to DWORD boundary srcLnBase = Round2Dword(srcLnBase); raSeg[xSeg].raStart = pCurGsn->raStart; raSeg[xSeg].raEnd = pCurGsn->raEnd; } // Write source file header counts[0] = (WORD) pCurSrc->cSegs; counts[1] = 0; WriteCopy(counts, sizeof(counts)); WriteCopy(fileBase, pCurSrc->cSegs*sizeof(DWORD)); WriteCopy(raSeg, pCurSrc->cSegs*sizeof(RAPAIR)); WriteCopy(pCurSrc->fname, pCurSrc->fname[0] + 1); // Pad to DWORD boundary Pad2Dword(); // Walk code segment list and write offsets/line pairs for (pCurGsn = pCurSrc->pGsnFirst; pCurGsn != NULL; pCurGsn = pCurGsn->next) { // Write segment index and number of offset/line pairs counts[0] = pCurGsn->seg; counts[1] = pCurGsn->cLines; WriteCopy(counts, sizeof(counts)); // Write offsets for (pLine = pCurGsn->pLineFirst; pLine != NULL; pLine = pLine->next) WriteCopy(&(pLine->rgOff), pLine->cPair * sizeof(DWORD)); // Write line numbers for (pLine = pCurGsn->pLineFirst; pLine != NULL; pLine = pLine->next) WriteCopy(&(pLine->rgLn), pLine->cPair * sizeof(WORD)); // Pad to DWORD boundary Pad2Dword(); // Free memory for (pLine = pCurGsn->pLineFirst; pLine != NULL;) { pTmp = pLine->next; FFREE(pLine); pLine = pTmp; } } } return(sizeTotal); } /*** SaveCode - save code segment information in MODULES entry * * Purpose: * For every module (.OBJ file) save the information about code segments * this module contributes to. COMDATs are threated as contibutions to * the logical segment, so each gets its entry in the CVCODE list attached * to the given .OBJ file (module). * * Input: * gsn - global segment index of logical segment to which this module * contributes * cb - size (in bytes) of contribution * raInit - offset of the contribution inside the logical segment; this * nonzero only for COMDATs. * * Output: * No explicit value is returned. The list of CVCODE attached to the * .OBJ file (module) is updated. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ void SaveCode(SNTYPE gsn, DWORD cb, DWORD raInit) { CVCODE FAR *pSegCur; // Pointer to the current code segment APROPFILEPTR apropFile; apropFile = (APROPFILEPTR) vrpropFile; // Save code segment if module has CV info pSegCur = (CVCODE FAR *) GetMem(sizeof(CVCODE)); // Store LOGICAL segment, offset and size of the contribution pSegCur->seg = mpgsnseg[gsn]; if (raInit != 0xffffffffL) pSegCur->ra = raInit; else pSegCur->ra = mpgsndra[gsn] - mpsegraFirst[mpgsnseg[gsn]]; pSegCur->cb = cb; // Add to the CV code list if (apropFile->af_Code == NULL) apropFile->af_Code = pSegCur; else apropFile->af_CodeLast->next = pSegCur; apropFile->af_CodeLast = pSegCur; apropFile->af_cCodeSeg++; } /**************************************************************** * * * DO SYMBOLIC DEBUG STUFF FOR MODULE JUST PROCESSED. * * Pass 2. * * * ****************************************************************/ void DebMd2(void) { APROPFILEPTR apropFile; sbLastModule[0] = 0; /* Force recognition of new THEADR */ apropFile = (APROPFILEPTR) vrpropFile; if (apropFile->af_cvInfo) ++segDebLast; } /*** PutDnt - store subsection directory entry in the table * * Purpose: * Copy current subsection directory to the DNT table. If no more * room in the table reallocate table doubling its size. * * Input: * - pDnt - pointer to the current directory entry * * Output: * No explicit value is returned. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL void NEAR PutDnt(DNT *pDnt) { WORD newSize; if (dntMac >= dntMax) { if(dntMax) { newSize = dntMax << 1; #if defined(M_I386) || defined( _WIN32 ) rgDnt = (DNT *) REALLOC(rgDnt, newSize * sizeof(DNT)); #else rgDnt = (DNT FAR *) _frealloc(rgDnt, newSize * sizeof(DNT)); #endif } else { newSize = DNT_START; rgDnt = (DNT*) GetMem ( newSize * sizeof(DNT) ); } if (rgDnt == NULL) Fatal(ER_memovf); dntMax = newSize; } rgDnt[dntMac] = *pDnt; dntMac++; #if CVDEBUG DumpDNT(pDnt); #endif } #pragma check_stack(on) /*** OutModule - write out module subsection * * Purpose: * Write into the executable file the module subsections for all * object files compiled with CV information. Only CV 4.0 format. * * Input: * - apropFile - pointer to the current object file descriptor * * Output: * No explicit value is retuned. * Side effects: * - module subsections in executable file * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL DWORD NEAR OutModule(APROPFILEPTR apropFile) { SBTYPE sbName; SSTMOD4 module; CVCODE FAR *pSegCur; CODEINFO codeOnt; WORD cOnt; module.ovlNo = (WORD) apropFile->af_iov; module.iLib = (WORD) (apropFile->af_ifh + 1); module.cSeg = apropFile->af_cCodeSeg; module.style[0] = 'C'; module.style[1] = 'V'; // Get file name or library module name if (apropFile->af_ifh != FHNIL && apropFile->af_rMod != RHTENIL) GetName((AHTEPTR) apropFile->af_rMod, sbName); else GetName((AHTEPTR) apropFile, sbName); #if CVDEBUG sbName[sbName[0]+1] = '\0'; fprintf(stdout, "\r\nCV info for %s\r\n", &sbName[1]); #endif // Write sstModule header followed by the list of code contributions WriteCopy(&module, sizeof(SSTMOD4)); pSegCur = apropFile->af_Code; codeOnt.pad = 0; for (cOnt = 0; cOnt < module.cSeg && pSegCur; cOnt++, pSegCur = pSegCur->next) { codeOnt.seg = pSegCur->seg; codeOnt.off = pSegCur->ra; codeOnt.cbOnt = pSegCur->cb; WriteCopy(&codeOnt, sizeof(CODEINFO)); #if CVDEBUG fprintf(stdout, " Logical segment %d; offset 0x%lx; size 0x%lx\r\n", codeOnt.seg, codeOnt.off, codeOnt.cbOnt); #endif } // Write object file name WriteCopy(sbName, B2W(sbName[0]) + 1); return(sizeof(SSTMOD4) + B2W(sbName[0]) + 1 + module.cSeg * sizeof(CODEINFO)); } /*** OutPublics - write sstPublics subsection * * Purpose: * Write sstPublics subsection of the CV information. The subsection * conforms to the new CV 4.0 format. * * Input: * - firstPub - virtual pointer to the list of public symbols defined * in a given object module * * Output: * Total size of the subsection in bytes. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL DWORD NEAR OutPublics(RBTYPE firstPub) { PUB16 pub16; // CV public descriptor - 16-bit PUB32 pub32; // CV public descriptor - 32-bit APROPNAMEPTR apropPub; // Real pointer to public descriptor APROPSNPTR apropSn; // Real pointer to segment descriptor RBTYPE curPub; // Virtual pointer to the current public symbol WORD f32Bit; // TRUE if public defined in 32-bit segment DWORD sizeTotal; // Total size of subsection SNTYPE seg; // Symbol base RATYPE ra; // Symbol offset WORD CVtype; // CV info type index SBTYPE sbName; // Public symbol char *pPub; WORD len; // Initialize curPub = firstPub; pub16.idx = S_PUB16; pub32.idx = S_PUB32; sizeTotal = 1L; WriteCopy(&sizeTotal, sizeof(DWORD));// sstPublicSym signature sizeTotal = sizeof(DWORD); while (curPub != 0L) { f32Bit = FALSE; apropPub = (APROPNAMEPTR) FETCHSYM(curPub, FALSE); curPub = apropPub->an_sameMod; if (apropPub->an_attr == ATTRALIAS) apropPub = (APROPNAMEPTR) FETCHSYM(((APROPALIASPTR) apropPub)->al_sym, FALSE); if (apropPub->an_attr != ATTRPNM) continue; ra = apropPub->an_ra; if (apropPub->an_gsn) // If not absolute symbol { seg = mpgsnseg[apropPub->an_gsn]; // If this is a .com program and the segment is the one // moved by 0x100, adjust accordingly the SegMap entry if(seg == segAdjCom) { #if FALSE GetName((AHTEPTR) apropPub, sbName); sbName[sbName[0]+1] = '\0'; fprintf(stdout, "\r\nCorrecting public %s : %lx -> %lx", sbName+1, ra, ra+0x100); fflush(stdout); #endif ra += 0x100; } CVtype = 0; // Should be this apropPub->an_CVtype // but cvpack can't handle it. #if O68K if (iMacType == MAC_NONE) #endif ra -= mpsegraFirst[seg]; #if CVDEBUG GetName((AHTEPTR) apropPub, sbName); sbName[sbName[0]+1] = '\0'; fprintf(stdout, "'%s' --> logical address %2x:%lx; physical address %2x:%lx\r\n", &sbName[1], seg, ra, mpsegsa[seg], apropPub->an_ra); #endif apropSn = (APROPSNPTR) FETCHSYM(mpgsnrprop[apropPub->an_gsn], FALSE); #if EXE386 f32Bit = TRUE; #else f32Bit = (WORD) Is32BIT(apropSn->as_flags); #endif } else { seg = 0; // Else no base CVtype = T_ABS; // CV absolute symbol type f32Bit = (WORD) (ra > LXIVK); } GetName((AHTEPTR) apropPub, sbName); if (f32Bit) { pub32.len = (WORD) (sizeof(PUB32) + B2W(sbName[0]) + 1 - sizeof(WORD)); pub32.off = ra; pub32.seg = seg; pub32.type = CVtype; pPub = (char *) &pub32; len = sizeof(PUB32); } else { pub16.len = (WORD) (sizeof(PUB16) + B2W(sbName[0]) + 1 - sizeof(WORD)); pub16.off = (WORD) ra; pub16.seg = seg; pub16.type = CVtype; pPub = (char *) &pub16; len = sizeof(PUB16); } WriteCopy(pPub, len); // Output length-prefixed name WriteCopy(sbName, sbName[0] + 1); sizeTotal += (len + B2W(sbName[0]) + 1); } return(sizeTotal); } /*** OutSegMap - write segment map * * Purpose: * This subsection was introduced in CV 4.0. This subsection * maps the logical segments to physical segments. It also gives * the names and sizes of each logical segment. * * Input: * No explicit value is passed. * Global variables: * - mpsegsa - table mapping logical segment number to its physical * segment number or address * - mpsaflags - table mapping physicla segment index to its flags * - mpseggsn - table mapping the logical segment index to its global * segment index * - mpgsnprop - table mapping global segment index to its symbol table * descriptor * - mpggrgsn - table mapping global group index to global segment index * - mpggrrhte - table mapping global group index to group name * * Output: * Function returns the size of segment map. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL DWORD NEAR OutSegMap(void) { SEGTYPE seg; // Logical segment index APROPSNPTR apropSn; // Real pointer to logical segment descriptor SATYPE sa; // Physical segment index WORD iName; // Index to free space in segment name table DWORD sizeTotal; // Total size of the subsection SEGINFO segInfo; // CV segment descriptor SBTYPE segName; // Segment name AHTEPTR ahte; // Real pointer to symbol table hash table RBTYPE vpClass; // Virtual pointer to class descriptor GRTYPE ggr; // Global group index SATYPE saDGroup; // DGroup's sa WORD counts[2]; iName = 0; counts[0] = (WORD) (segLast + ggrMac - 1); counts[1] = (WORD) segLast; WriteCopy(counts, sizeof(counts)); sizeTotal = sizeof(counts); saDGroup = mpsegsa[mpgsnseg[mpggrgsn[ggrDGroup]]]; // Write all logical segments for (seg = 1; seg <= segLast; ++seg)// For all logical segments { memset(&segInfo, 0, sizeof(SEGINFO)); if (fNewExe) segInfo.flags.fSel = TRUE; sa = mpsegsa[seg]; if (fNewExe) { #if EXE386 segInfo.flags.f32Bit = TRUE; #else segInfo.flags.f32Bit = (WORD) (Is32BIT(mpsaflags[sa])); #endif if (IsDataFlg(mpsaflags[sa])) { segInfo.flags.fRead = TRUE; #if EXE386 if (IsWRITEABLE(mpsaflags[sa])) #else if (!(mpsaflags[sa] & NSEXRD)) #endif segInfo.flags.fWrite = TRUE; } else { segInfo.flags.fExecute = TRUE; #if EXE386 if (IsREADABLE(mpsaflags[sa])) #else if (!(mpsaflags[sa] & NSEXRD)) #endif segInfo.flags.fRead = TRUE; } } else { if (mpsegFlags[seg] & FCODE) { segInfo.flags.fRead = TRUE; segInfo.flags.fExecute = TRUE; } else { segInfo.flags.fRead = TRUE; segInfo.flags.fWrite = TRUE; } } // Look up segment definition apropSn = (APROPSNPTR) FETCHSYM(mpgsnrprop[mpseggsn[seg]], FALSE); vpClass = apropSn->as_rCla; #if OVERLAYS if (!fNewExe) segInfo.ovlNbr = apropSn->as_iov; #endif // if segment doesn't belong to any group it's ggr is 0 if (apropSn->as_ggr != GRNIL) segInfo.ggr = (WORD) (apropSn->as_ggr + segLast - 1); // If segment is a DGROUP member, write DGROUP normalized address if(apropSn->as_ggr == ggrDGroup) { segInfo.sa = saDGroup; segInfo.phyOff = mpsegraFirst[seg] + ((sa - saDGroup) << 4); } else { segInfo.sa = sa; segInfo.phyOff = mpsegraFirst[seg]; } // If this is a .com program and the segment is the one moved by 0x100 // write all the publics at the original addresses, because the offset // adjustment will be made in the SegMap table if(seg == segAdjCom) { segInfo.phyOff -= 0x100; } segInfo.cbSeg = apropSn->as_cbMx; GetName((AHTEPTR) apropSn, segName); if (segName[0] != '\0') { segInfo.isegName = iName; iName += (WORD) (B2W(segName[0]) + 1); } else segInfo.isegName = 0xffff; ahte = (AHTEPTR) FETCHSYM(vpClass, FALSE); if (ahte->cch[0] != 0) { segInfo.iclassName = iName; iName += (WORD) (B2W(ahte->cch[0]) + 1); } else segInfo.iclassName = 0xffff; WriteCopy(&segInfo, sizeof(SEGINFO)); sizeTotal += sizeof(SEGINFO); } // Write all groups for (ggr = 1; ggr < ggrMac; ggr++) { memset(&segInfo, 0, sizeof(SEGINFO)); segInfo.flags.fGroup = TRUE; if (fNewExe) segInfo.flags.fSel = TRUE; segInfo.sa = mpsegsa[mpgsnseg[mpggrgsn[ggr]]]; if (fNewExe) segInfo.cbSeg = mpsacb[segInfo.sa]; else { segInfo.cbSeg = 0L; if (mpggrgsn[ggr] != SNNIL) { // If group has members for (seg = 1; seg <= segLast; seg++) { apropSn = (APROPSNPTR) FETCHSYM(mpgsnrprop[mpseggsn[seg]], FALSE); if (apropSn->as_ggr == ggr) { segInfo.cbSeg += apropSn->as_cbMx; #if OVERLAYS segInfo.ovlNbr = apropSn->as_iov; #endif } } } } segInfo.isegName = iName; ahte = (AHTEPTR) FETCHSYM(mpggrrhte[ggr], FALSE); iName += (WORD) (B2W(ahte->cch[0]) + 1); segInfo.iclassName = 0xffff; WriteCopy(&segInfo, sizeof(SEGINFO)); sizeTotal += sizeof(SEGINFO); } return(sizeTotal); } /*** OutSegNames - write segment name table * * Purpose: * This subsection was introduced in CV 4.0. * The segment name subsection contains all of the logical segment, * class and group names. Each name is a zero terminated ASCII string. * * Input: * No explicit value is passed. * Global variables: * - mpseggsn - table mapping the logical segment index to its global * segment index * - mpgsnprop - table mapping global segment index to its symbol table * descriptor * - mpggrrhte - table mapping global group index to group name * * Output: * Function returns the size of segment name table. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL DWORD NEAR OutSegNames(void) { SEGTYPE seg; // Logical segment index APROPSNPTR apropSn; // Real pointer to logical segment descriptor DWORD sizeTotal; // Size of the segment name table SBTYPE name; // A name RBTYPE vpClass; // Virtual pointer to class descriptor GRTYPE ggr; // Global group index sizeTotal = 0L; // Write names of all logical segments for (seg = 1; seg <= segLast; ++seg) { // Look up segment definition apropSn = (APROPSNPTR ) FETCHSYM(mpgsnrprop[mpseggsn[seg]], FALSE); vpClass = apropSn->as_rCla; GetName((AHTEPTR) apropSn, name); WriteCopy(&name[1], B2W(name[0]) + 1); sizeTotal += (B2W(name[0]) + 1); GetName((AHTEPTR ) FETCHSYM(vpClass, FALSE), name); WriteCopy(&name[1], B2W(name[0]) + 1); sizeTotal += (B2W(name[0]) + 1); } // Write names of all groups for (ggr = 1; ggr < ggrMac; ggr++) { GetName((AHTEPTR ) FETCHSYM(mpggrrhte[ggr], FALSE), name); WriteCopy(&name[1], B2W(name[0]) + 1); sizeTotal += (B2W(name[0]) + 1); } return(sizeTotal); } /*** OutSst - write subsections * * Purpose: * For every object file with CV information write its sstModule, * sstTypes, sstPublics, sstSymbols and sstSrcModule. * Build subsection directory. * * Input: * No explicit value is passed. * Global variables used: * - rprop1stFile - virtual pointer to the first object file descriptor * * Output: * No explicit value is returned. * Side effects: * - subsections in the executable file * - subsection directory in the VM * * Exceptions: * I/O errors - display error message and quit * * Notes: * None. * *************************************************************************/ LOCAL void NEAR OutSst(void) { APROPFILEPTR apropFile; // Real pointer to file entry RBTYPE rbFileNext; // Virtual pointer the next file descriptor struct dnt dntCur; // Current subsection directory entry CVINFO FAR *pCvInfo; // Pointer to the CV info descriptor #if CVPACK_SHARED #if REVERSE_MODULE_ORDER_FOR_CVPACK RBTYPE rbFileCur; RBTYPE rbFileLast; // reverse module list in place // I've been waiting all my life to actually *need* this code... [rm] // this will cause us to write the modules tables in REVERSE order // (this gives better swapping behaviour in the cvpack phase // because the modules cvpack will visit first will be the ones that // are still resident... rbFileCur = rprop1stFile; rbFileLast = NULL; while (rbFileCur != NULL) { apropFile = (APROPFILEPTR ) FETCHSYM(rbFileCur, TRUE); rbFileNext = apropFile->af_FNxt;// Get pointer to next file apropFile->af_FNxt = rbFileLast; rbFileLast = rbFileCur; rbFileCur = rbFileNext; } rprop1stFile = rbFileLast; #endif #endif rbFileNext = rprop1stFile; dntCur.iMod = 1; while (rbFileNext != NULL) // For every module { apropFile = (APROPFILEPTR ) FETCHSYM(rbFileNext, TRUE); rbFileNext = apropFile->af_FNxt;// Get pointer to next file // Skip this module if no debug info for it if (!apropFile->af_cvInfo && !apropFile->af_publics && !apropFile->af_Src) continue; pCvInfo = apropFile->af_cvInfo; // sstModules dntCur.sst = SSTMODULES4; dntCur.lfo = FTELL_BSRUNFILE() - lfaBase; dntCur.cb = OutModule(apropFile); PutDnt(&dntCur); // sstTypes if (pCvInfo && pCvInfo->cv_cbTyp > 0L) { Pad2Dword(); if (apropFile->af_flags & FPRETYPES) dntCur.sst = SSTPRETYPES; else dntCur.sst = SSTTYPES4; dntCur.lfo = FTELL_BSRUNFILE() - lfaBase; dntCur.cb = pCvInfo->cv_cbTyp; WriteNocopy(pCvInfo->cv_typ, pCvInfo->cv_cbTyp); IF_NOT_CVPACK_SHARED(FFREE(pCvInfo->cv_typ)); PutDnt(&dntCur); } // sstPublics if (apropFile->af_publics && !fSkipPublics) { Pad2Dword(); dntCur.sst = SSTPUBLICSYM; dntCur.lfo = FTELL_BSRUNFILE() - lfaBase; dntCur.cb = OutPublics(apropFile->af_publics); PutDnt(&dntCur); } // sstSymbols if (pCvInfo && pCvInfo->cv_cbSym > 0L) { Pad2Dword(); dntCur.sst = SSTSYMBOLS4; dntCur.lfo = FTELL_BSRUNFILE() - lfaBase; dntCur.cb = pCvInfo->cv_cbSym; WriteNocopy(pCvInfo->cv_sym, pCvInfo->cv_cbSym); IF_NOT_CVPACK_SHARED(FFREE(pCvInfo->cv_sym)); PutDnt(&dntCur); } // sstSrcModule if (apropFile->af_Src) { Pad2Dword(); dntCur.sst = SSTSRCMODULE; dntCur.lfo = FTELL_BSRUNFILE() - lfaBase; dntCur.cb = OutSrcModule(apropFile->af_Src); PutDnt(&dntCur); } dntCur.iMod++; } // sstLibraries Pad2Dword(); dntCur.sst = SSTLIBRARIES4; dntCur.iMod = (short) 0xffff; dntCur.lfo = FTELL_BSRUNFILE() - lfaBase; dntCur.cb = OutLibSec(); PutDnt(&dntCur); // sstSegMap Pad2Dword(); dntCur.sst = SSTSEGMAP; dntCur.lfo = FTELL_BSRUNFILE() - lfaBase; dntCur.cb = OutSegMap(); PutDnt(&dntCur); // sstSegNames Pad2Dword(); dntCur.sst = SSTSEGNAME; dntCur.lfo = FTELL_BSRUNFILE() - lfaBase; dntCur.cb = OutSegNames(); PutDnt(&dntCur); FFREE(fileBase); FFREE(raSeg); FFREE(segNo); } #pragma check_stack(off) /* * OutLibSec : Output sstLibraries subsection to bsRunfile * * Path prefix is stripped from library name. * If no libraries, don't output anything. * * Parameters: none * Returns: Number of bytes in Libraries subsection */ LOCAL int NEAR OutLibSec () { WORD ifh; AHTEPTR ahte; int cb = 0; BYTE *pb; if (ifhLibMac == 0) return(0); // Libraries subsection consists of a list of library // names which will be indexed by library numbers in the // sstModules. Those indexes are 1-based. // cb == 0, use it to write a single byte WriteCopy(&cb, 1); // 0th entry is null for now cb++; for (ifh = 0; ifh < ifhLibMac; ifh++) { if (mpifhrhte[ifh] != RHTENIL) { ahte = (AHTEPTR) FETCHSYM(mpifhrhte[ifh],FALSE); #if OSXENIX pb = GetFarSb(ahte->cch); #else pb = StripDrivePath(GetFarSb(ahte->cch)); #endif } else pb = ""; WriteCopy(pb, pb[0] + 1); cb += 1 + B2W(pb[0]); } return(cb); } /*** OutDntDir - write subsection directory * * Purpose: * Write subsection directory * * Input: * No explicit value is passed. * Global variables: * - dntPageMac - number of VM pages with DNTs * * Output: * Function returns the size of the directory in bytes. * * Exceptions: * I/O problems - display error message and quit * * Notes: * None. * *************************************************************************/ LOCAL DWORD NEAR OutDntDir(void) { DNTHDR hdr; // Directory header hdr.cbDirHeader = sizeof(DNTHDR); hdr.cbDirEntry = sizeof(DNT); hdr.cDir = dntMac; hdr.lfoDirNext = 0L; hdr.flags = 0L; // Write header WriteCopy(&hdr, sizeof(DNTHDR)); // Write directory WriteCopy((char FAR *) rgDnt, dntMac * sizeof(DNT)); FFREE(rgDnt); return(sizeof(DNTHDR) + dntMac * sizeof(DNT)); } /*** OutDebSection - allow debugging * * Purpose: * Append to the executable file the CV information. ONLY CV 4.00 * format supported. * * Input: * No explicit value is passed. * Global variables: * - too many to list * * Output: * No explicit value is returned. * Side effects: * - what do you think ?? * * Exceptions: * I/O problems - display error message and quit * * Notes: * None. * *************************************************************************/ void OutDebSections(void) { long lfaDir; // File address of Directory DWORD dirSize; // Directory size long tmp; #if CVPACK_SHARED long * plfoDir; // pointer to directory data fseek(bsRunfile, 0L, 2); // Go to end of file lfaBase = ftell(bsRunfile); // Remember base address lposCur = lfaBase; // set current position... WriteCopy(szSignature, sizeof(szSignature)); // Signature dword WriteCopy(&tmp, sizeof(tmp)); // Skip lfoDir field plfoDir = (long *)rgbl[iblLim-1].pb;// remember address of lfoDir OutSst(); // Output subsections lfaDir = lposCur; // Remember where directory starts *plfoDir = lfaDir - lfaBase; // fix up lfoDir field dirSize = OutDntDir(); // Output subsection directory WriteCopy(szSignature, sizeof(szSignature)); // Signature dword tmp = (lfaDir + dirSize + 2*sizeof(DWORD)) - lfaBase; WriteCopy(&tmp, sizeof(long)); // Distance from EOF to base // write out the bits that cvpack won't overwrite... if (fCVpack) WriteFlushSignature(); else WriteFlushAll(); cbRealBytes = ftell(bsRunfile); // # of real bytes actually written lposMac = lposCur; iblCur = iblNil; #else fseek(bsRunfile, 0L, 2); // Go to end of file lfaBase = FTELL_BSRUNFILE(); // Remember base address WriteExe(szSignature, sizeof(szSignature)); // Signature dword fseek(bsRunfile,4L,1); // Skip lfoDir field OutSst(); // Output subsections lfaDir = FTELL_BSRUNFILE(); // Remember where directory starts fseek(bsRunfile, lfaBase + 4, 0); // Go to lfoDir field tmp = lfaDir - lfaBase; WriteExe(&tmp, sizeof(long)); // Fix it up fseek(bsRunfile, lfaDir, 0); // Go back to directory dirSize = OutDntDir(); // Output subsection directory WriteExe(szSignature, sizeof(szSignature)); // Signature dword tmp = (lfaDir + dirSize + 2*sizeof(DWORD)) - lfaBase; WriteExe(&tmp, sizeof(long)); // Distance from EOF to base fseek(bsRunfile, 0L, 2); // Seek to EOF just in case #endif } #if CVPACK_SHARED // // write data to cvpack memory cache area // void WriteSave(FTYPE fCopy, void *pb, UINT cb) { if (!rgbl) { rgbl = (BL *)GetMem(sizeof(BL) * C_BL_INIT); iblMac = C_BL_INIT; iblLim = 0; } // if this memory isn't going to stay around, then copy it if (fCopy) { void *pbT = (void *)GetMem(cb); memcpy(pbT, pb, cb); pb = pbT; } if (iblLim == iblMac) { BL *rgblT; rgblT = (BL *)GetMem(sizeof(BL) * iblMac * 2); memcpy(rgblT, rgbl, sizeof(BL) * iblMac); iblMac *= 2; FFREE(rgbl); rgbl = rgblT; } rgbl[iblLim].lpos = lposCur; rgbl[iblLim].pb = pb; iblLim++; lposCur += cb; } // we want to write a few of the blocks because cvpack won't rewrite the // first bit... [rm] void WriteFlushSignature() { int ibl, cb; // we know that the signature and offset are written in two pieces... // if this changes we need to change the magic '2' below [rm] for (ibl = 0; ibl < 2; ibl++) { cb = rgbl[ibl+1].lpos - rgbl[ibl].lpos; WriteExe(rgbl[ibl].pb, cb); } } void WriteFlushAll() { int ibl, cb; for (ibl = 0; ibl < iblLim - 1; ibl++) { cb = rgbl[ibl+1].lpos - rgbl[ibl].lpos; WriteExe(rgbl[ibl].pb, cb); } cb = lposCur - rgbl[ibl].lpos; WriteExe(rgbl[ibl].pb, cb); } // the following are the various callback functions needed to support // the cvpack library when we are attempting to not write out the // unpacked types and symbols #include extern int printf(char *,...); int __cdecl link_chsize (int fh, long size) { LINK_TRACE(printf("chsize(%06d, %08ld)\n", fh, size)); // we must keep track of the new size so that we will correctly // process lseeks that are relative to the end of the file lposMac = size; return(_chsize(fh,size)); } int __cdecl link_close (int x) { LINK_TRACE(printf("close (%06d)\n", x)); return(_close(x)); } void __cdecl link_exit (int x) { LINK_TRACE(printf("exit (%06d)\n", x)); #if USE_REAL RealMemExit(); #endif exit(x); } long __cdecl link_lseek (int fh, long lpos, int mode) { int ibl; LINK_TRACE(printf("lseek (%d, %08ld, %2d)\n", fh, lpos, mode)); // if we have no cache blocks, just forward the request... // this will happen on a /CvpackOnly invocation if (rgbl == NULL) return _lseek(fh, lpos, mode); // adjust lpos so that we are always doing an absolute seek if (mode == 1) lpos = lposCur + lpos; else if (mode == 2) lpos = lposMac + lpos; // check for a bogus seek if (lpos > lposMac || lpos < 0) { // this used to be an internal error... but cvpack sometimes does // try to seek beyond the end of the file when it is trying to // distinguish a PE exe from an unsegmented DOS exe // instead of panicing, we just return failure return(-1); } // if we are in the midst of reading a block, then free that block // cvpack never reads the same data twice if (iblCur != iblNil) { // first check if we're in the header -- we might come back to that... if (rgbl[iblCur].lpos > cbRealBytes + CB_HEADER_SAVE) { long lposCurMin, lposCurMac; // check for a seek that is within the current bucket // in case we're skipping within the current block lposCurMin = rgbl[iblCur].lpos; if (iblCur < iblLim) lposCurMac = rgbl[iblCur+1].lpos; else lposCurMac = lposMac; if (lpos < lposCurMin || lpos >= lposCurMac) { FFREE(rgbl[iblCur].pb); rgbl[iblCur].pb = NULL; } } } // if this seek is not in the debug area of the .exe use the real lseek if (lpos < cbRealBytes) { iblCur = iblNil; lposCur = lpos; return(_lseek(fh,lpos,0)); } // see if we are searching forward (the normal case) // if we are, search from the current block, otherwise search from // the start (linear search but OK because cvpack doesn't // jump around much, it just uses lseek to skip a few bytes here and // there) if (lpos > lposCur && iblCur != iblNil) ibl = iblCur; else ibl = 0; // set the current position lposCur = lpos; // loop through the buffered writes looking for the requested position for (; ibl < iblLim - 1; ibl++) { if (lpos >= rgbl[ibl].lpos && lpos < rgbl[ibl+1].lpos) break; // found bucket } // set the bucket number, offset within the bucket, and number of bytes // left in the bucket iblCur = ibl; ichCur = lpos - rgbl[ibl].lpos; cbCur = CbIbl(ibl) - ichCur; // check to make sure we haven't seeked back to a buffer that we already // freed... ASSERT(rgbl[iblCur].pb != NULL); // make sure we get the boundary case... if cvpack is requesting to go to // the end of the data that we have written then we MUST seek because // cvpack might be about to write out the packed stuff... if (lposCur == cbRealBytes) _lseek(fh, lpos, 0); // we set up the current position earlier... return it now return(lposCur); } int __cdecl link_open (const char * x, int y) { LINK_TRACE(printf("open (%s, %06d)\n", x, y)); // setup the static variables to a safe state // the current position is the start of file and there is no buffer // active (iblCur = iblNil) iblCur = iblNil; lposCur = 0; return(_open(x,y)); } int __cdecl link_read (int fh, char *pch, unsigned int cb) { int cbRem; LINK_TRACE(printf("read (%d, %06u)\n", fh, cb)); if (rgbl == NULL) return _read(fh, pch, cb); // special case zero byte read, not really necessary but // avoids any potential problems with trying to setup empty // buffers etc. -- it should just fall out anyways but // just to be safe [rm] if (cb == 0) return 0; // if there is no buffer active, then just forward the read // note that if we are invoked with /CvpackOnly this test will // always succeed if (iblCur == iblNil) { if (lposCur + ((long)(unsigned long)cb) < cbRealBytes) { lposCur += cb; return(_read(fh,pch,cb)); } else { int cbReal = cbRealBytes - lposCur; if (_read(fh, pch, cbReal) != cbReal) return -1; if (link_lseek(fh, cbRealBytes, 0) != cbRealBytes) return -1; // set the number of bytes remaining to be read in cbRem = cb - cbReal; pch += cb - cbReal; } } else { // set the number of bytes remaining to be read in cbRem = cb; } while (cbRem) { // check if the number of bytes we need to read is less than // the number left in the current buffer if (cbRem <= cbCur) { // we can read all the remaining bytes from the current buffer // so do it. Copy bytes and adjust the number of bytes left // in this buffer, the index into the buffer, and the current // position in the file memcpy(pch, rgbl[iblCur].pb+ichCur, cbRem); cbCur -= cbRem; ichCur += cbRem; lposCur += cbRem; #ifdef DUMP_CVPACK_BYTES { int i; for (i=0;i cbRealBytes + CB_HEADER_SAVE) { FFREE(rgbl[iblCur].pb); rgbl[iblCur].pb = NULL; } // move forward to the next bucket, if there are no more buckets // then this is an ERROR -- we'll be returning the number of // bytes that we managed to read iblCur++; if (iblCur == iblLim) { iblCur = iblNil; break; } // check to make sure that we are not reading data that // we've already freed (yipe!) ASSERT(rgbl[iblCur].pb != NULL); // check to make sure that the current position agrees with // the position that this buffer is supposed to occur at ASSERT(lposCur == rgbl[iblCur].lpos); // ok, everything is safe now, set the index into the current // buffer and the number of bytes left in the buffer, then // run the loop again until we've read in all the bytes we need ichCur = 0; cbCur = CbIbl(iblCur); } } // return the number of bytes we actually read return cb - cbRem; } long __cdecl link_tell (int x) { LINK_TRACE(printf("tell (%06d)\n", x)); if (iblCur != iblNil) return(lposCur); return(_tell(x)); } int __cdecl link_write (int x, const void * y, unsigned int z) { LINK_TRACE(printf("write (%06d,%08lx,%06u)\n", x, y, z)); return(_write(x,y,z)); } #ifdef CVPACK_DEBUG_HELPER void dumpstate() { printf("lposCur= %d\n", lposCur); printf("iblCur = %d\n", iblCur); printf("ichCur = %d\n", ichCur); printf("cbReal = %d\n", cbRealBytes); printf("lposMac= %d\n", lposMac); } #endif #else #ifdef CVPACK_MONDO #include int __cdecl link_chsize (int x, long y) { return(_chsize(x,y)); } int __cdecl link_close (int x) { return(_close(x)); } void __cdecl link_exit (int x) { #if USE_REAL RealMemExit(); #endif exit(x); } long __cdecl link_lseek (int x, long y, int z) { return(_lseek(x,y,z)); } int __cdecl link_open (const char *x, int y) { return(_open(x,y)); } int __cdecl link_read (int x, void *y, unsigned int z) { return(_read(x,y,z)); } long __cdecl link_tell (int x) { return(_tell(x)); } int __cdecl link_write (int x, const void * y, unsigned int z) { return(_write(x,y,z)); } #endif #endif