/*** comdat.c - handle COMDAT records * * Copyright 1990, Microsoft Corporation * * Purpose: * Process COMDAT records in various stages of linker work. * * Revision History: * * [] 06-Jun-1990 WJK Created * *************************************************************************/ #include /* Basic types and constants */ #include /* More types and constants */ #include /* More types and constants */ #include /* Linker I/O definitions */ #include /* DOS & 286 .EXE data structures */ #if EXE386 #include /* 386 .EXE data structures */ #endif #include /* Error messages */ #if OXOUT OR OIAPX286 #include /* Xenix format definitions */ #endif #include /* External declarations */ #include #if OSEGEXE extern RLCPTR rlcLidata; /* Pointer to LIDATA fixup array */ extern RLCPTR rlcCurLidata; /* Pointer to current LIDATA fixup */ #endif #define COMDAT_SEG_NAME "\012COMDAT_SEG" #define COMDAT_NAME_LEN 11 #define COMDATDEBUG FALSE #define ALLOC_TYPE(x) ((x)&ALLOCATION_MASK) #define SELECT_TYPE(x) ((x)&SELECTION_MASK) #define IsVTABLE(x) ((x)&VTABLE_BIT) #define IsLOCAL(x) ((x)&LOCAL_BIT) #define IsORDERED(x) ((x)&ORDER_BIT) #define IsCONCAT(x) ((x)&CONCAT_BIT) #define IsDEFINED(x) ((x)&DEFINED_BIT) #define IsALLOCATED(x) ((x)&ALLOCATED_BIT) #define IsITERATED(x) ((x)&ITER_BIT) #define IsREFERENCED(x) ((x)&REFERENCED_BIT) #define IsSELECTED(x) ((x)&SELECTED_BIT) #define SkipCONCAT(x) ((x)&SKIP_BIT) /* * Global data exported from this module */ FTYPE fSkipFixups; // TRUE if skiping COMDAT and its fixups FTYPE fFarCallTransSave; // Previous state of /FarCallTarnaslation /* * Local types */ typedef struct comdatRec { GRTYPE ggr; SNTYPE gsn; RATYPE ra; WORD flags; WORD attr; WORD align; WORD type; RBTYPE name; } COMDATREC; /* * Local data */ LOCAL SNTYPE curGsnCode16; // Current 16-bit code segment LOCAL DWORD curCodeSize16; // Current 16-bit code segment size LOCAL SNTYPE curGsnData16; // Current 16-bit data segment LOCAL DWORD curDataSize16; // Current 16-bit data segment size #if EXE386 LOCAL SNTYPE curGsnCode32; // Current 32-bit code segment LOCAL DWORD curCodeSize32; // Current 32-bit code segment size LOCAL SNTYPE curGsnData32; // Current 32-bit data segment LOCAL DWORD curDataSize32; // Current 32-bit data segment size #endif #if OVERLAYS LOCAL WORD iOvlCur; // Current overlay index #endif #if TCE SYMBOLUSELIST aEntryPoints; // List of program entry points #endif extern BYTE * ObExpandIteratedData(unsigned char *pb, unsigned short cBlocks, WORD *pSize); /* * Local function prototypes */ LOCAL DWORD NEAR DoCheckSum(void); LOCAL void NEAR PickComDat(RBTYPE vrComdat, COMDATREC *omfRec, WORD fNew); LOCAL void NEAR ReadComDat(COMDATREC *omfRec); LOCAL void NEAR ConcatComDat(RBTYPE vrComdat, COMDATREC *omfRec); LOCAL void NEAR AttachPublic(RBTYPE vrComdat, COMDATREC *omfRec); LOCAL void NEAR AdjustOffsets(RBTYPE vrComdat, DWORD startOff, SNTYPE gsnAlloc); LOCAL WORD NEAR SizeOfComDat(RBTYPE vrComdat, DWORD *pActual); LOCAL RATYPE NEAR DoAligment(RATYPE value, WORD align); LOCAL DWORD NEAR DoAllocation(SNTYPE gsnAlloc, DWORD size, RBTYPE vrComdat, DWORD comdatSize); LOCAL WORD NEAR NewSegment(SNTYPE *gsnPrev, DWORD *sizePrev, WORD allocKind); LOCAL void NEAR AllocAnonymus(RBTYPE vrComdat); LOCAL WORD NEAR SegSizeOverflow(DWORD segSize, DWORD comdatSize, WORD f16bit, WORD fCode); #if TCE LOCAL void NEAR MarkAlive( APROPCOMDAT *pC ); LOCAL void PerformTce( void ); void AddTceEntryPoint( APROPCOMDAT *pC ); #endif #if COMDATDEBUG void DisplayComdats(char *title, WORD fPhysical); #endif #pragma check_stack(on) /*** DoCheckSum - calculate a check sum of a COMDAT data block * * Purpose: * The check sum is used for match exact criteria. * * Input: * No explicit value is passed. The following global data is used as * input to this function: * * cbRec - current record lenght; decremented by every read from * the OMF record, so effectivelly cbRec tells you how * many bytes are left in the record. * * Output: * Returns the 32-bit check sum of COMDAT data block. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL DWORD NEAR DoCheckSum(void) { DWORD result; DWORD shift; result = 0L; shift = 0; while (cbRec > 1) { result += ((DWORD ) Gets()) << (BYTELN * shift); shift = (shift + 1) & 3; } return(result); } /*** PickComDat - fill in the COMDAT descriptor * * Purpose: * Fill in the linkers symbol table COMDAT descriptor. This function * is called for new descriptors and for already entered one which * need to be updated - remember we have plenty of COMDAT selection criteria. * * Input: * vrComDat - virtual pointer to symbol table entry * omfRec - pointer to COMDAT OMF data * fNew - TRUE if new entry in symbol table * mpgsnprop - table mapping global segment index to symbol table entry * This one of those friendly global variables linker is full of. * vrprop - virtual pointer to symbol table entry - another global * variable; it is set by call to PropSymLookup which must * proceed call to PickComDat * cbRec - size of current OMF record - global variable - decremented * by every read from record * vrpropFile - current .OBJ file - global variable * lfaLast - current offset in the .OBJ - global variable * * Output: * No explicit value is returned. As a side effect symbol table entry * is updated and VM page is marked dirty. * * Exceptions: * Explicit allocation, but target segment not defined - issue error and * return. * * Notes: * None. * *************************************************************************/ LOCAL void NEAR PickComDat(RBTYPE vrComdat, COMDATREC *omfRec, WORD fNew) { RBTYPE vrTmp; // Virtual pointer to COMDAT symbol table entry APROPCOMDATPTR apropComdat; // Pointer to symbol table descriptor APROPFILEPTR apropFile; // Current object module prop. cell WORD cbDataExp = 0; // Length of expanded DATA field #if RGMI_IN_PLACE rgmi = bufg; /* use temporary buffer for holding info */ #endif apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, TRUE); if ((ALLOC_TYPE(omfRec->attr) == EXPLICIT) && omfRec->gsn && (mpgsnrprop[omfRec->gsn] == VNIL)) { // ERROR - explicit allocation segment not defined OutError(ER_comdatalloc, 1 + GetPropName(apropComdat)); return; } // Fill in the COMDAT descriptor apropComdat->ac_ggr = omfRec->ggr; apropComdat->ac_gsn = omfRec->gsn; apropComdat->ac_ra = omfRec->ra; if (IsITERATED (omfRec->flags)) // We'll need to find size of expanded block { BYTE *pb = rgmi; vcbData = (WORD)(cbRec - 1);// Length of the DATA field GetBytesNoLim (rgmi, vcbData); while((pb = ObExpandIteratedData(pb,1, &cbDataExp)) < rgmi + vcbData); apropComdat->ac_size = cbDataExp; } else { apropComdat->ac_size = cbRec - 1; } if (fNew) { apropComdat->ac_flags = omfRec->flags; #if OVERLAYS if (IsVTABLE(apropComdat->ac_flags)) apropComdat->ac_iOvl = IOVROOT; else apropComdat->ac_iOvl = NOTIOVL; #endif } else apropComdat->ac_flags |= omfRec->flags; apropComdat->ac_selAlloc = (BYTE) omfRec->attr; apropComdat->ac_align = (BYTE) omfRec->align; if (ALLOC_TYPE(omfRec->attr) == EXACT) apropComdat->ac_data = DoCheckSum(); else apropComdat->ac_data = 0L; apropComdat->ac_obj = vrpropFile; apropComdat->ac_objLfa = lfaLast; if (IsORDERED(apropComdat->ac_flags)) apropComdat->ac_flags |= DEFINED_BIT; if (!IsCONCAT(omfRec->flags)) { if (ALLOC_TYPE(omfRec->attr) == EXPLICIT) { // Attach this COMDAT to its segment list AttachComdat(vrComdat, omfRec->gsn); } #if OVERLAYS else if (fOverlays && (apropComdat->ac_iOvl == NOTIOVL)) apropComdat->ac_iOvl = iovFile; #endif // Attach this COMDAT to its file list apropFile = (APROPFILEPTR ) FetchSym(vrpropFile, TRUE); if (apropFile->af_ComDat == VNIL) { apropFile->af_ComDat = vrComdat; apropFile->af_ComDatLast = vrComdat; } else { vrTmp = apropFile->af_ComDatLast; apropFile->af_ComDatLast = vrComdat; apropComdat = (APROPCOMDATPTR ) FetchSym(vrTmp, TRUE); apropComdat->ac_sameFile = vrComdat; } } } /*** ReadComDat - self-explanatory * * Purpose: * Decode the COMDAT record. * * Input: * omfRec - pointer to COMDAT OMF record * bsInput - current .OBJ file - global variable * grMac - current maximum number of group defined in this .OBJ module * global variable * snMac - current maximum number of segments defined in this .OBJ * module - global variable * mpgrggr - table mapping local group index to global group index - global * variable * mpsngsn - table mapping local segment index to global segment index * - global variable * * Output: * Returns COMDAT symbol name, group and segment indexes, data offset * attributes and aligment. * * Exceptions: * Invalid .OBJ format. * * Notes: * None. * *************************************************************************/ LOCAL void NEAR ReadComDat(COMDATREC *omfRec) { SNTYPE sn; // Local SEGDEF no. - module scope only omfRec->ggr = 0; omfRec->gsn = 0; // The record header (type and length) has been already read // and stored in rect and cbRec - read the COMDAT attribute byte omfRec->flags = (WORD) Gets(); omfRec->attr = (WORD) Gets(); omfRec->align = (WORD) Gets(); #if OMF386 if(rect & 1) omfRec->ra = LGets(); else #endif omfRec->ra = WGets(); // Get COMDAT data offset omfRec->type = GetIndex(0, 0x7FFF); // Get type index if (ALLOC_TYPE(omfRec->attr) == EXPLICIT) { // If explicit allocation read the public base of COMDAT symbol omfRec->ggr = (GRTYPE) GetIndex(0, (WORD) (grMac - 1)); // Get group index if(!(sn = GetIndex(0, (WORD) (snMac - 1)))) { // If frame number present omfRec->gsn = 0; // No global SEGDEF no. SkipBytes(2); // Skip the frame number } else // Else if local SEGDEF no. given { if (omfRec->ggr != GRNIL) omfRec->ggr = mpgrggr[omfRec->ggr]; // If group specified, get global no omfRec->gsn = mpsngsn[sn]; // Get global SEGDEF no. } } omfRec->name = mplnamerhte[GetIndex(1, (WORD) (lnameMac - 1))]; // Get symbol length } /*** ConcatComDat - append COMDAT to the list of concatenated records * * Purpose: * Concatenate COMDAT records. This function build the list of COMDAT * descriptors for contatenated records. Only the first descriptor * on the list has attribute COMDAT, which means that the head of the * list can be found when looking up the symbol table. The rest of the * elements on the list remain anonymus. * * Input: * vrComdat - virtual pointer to the first descriptor on the list * omfRec - pointer to COMDAT OMF record * * Output: * No explicit value is returned. As a side effect this function * adds the descriptor to the list of concatenated COMDAT records. * * Exceptions: * Different attributes in the first and concatenated records - display * error message and skip the concatenated record. * * Notes: * None. * *************************************************************************/ LOCAL void NEAR ConcatComDat(RBTYPE vrComdat, COMDATREC *omfRec) { APROPCOMDATPTR apropComdatNew; // Real pointer to added COMDAT RBTYPE vrComdatNew; // Virtual pointer to added COMDAT APROPCOMDATPTR apropComdat; // Real pointer to the head of the list RBTYPE vrTmp; apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, TRUE); if (apropComdat->ac_gsn != omfRec->gsn || apropComdat->ac_ggr != omfRec->ggr || apropComdat->ac_selAlloc != (BYTE) omfRec->attr) { if(IsORDERED(apropComdat->ac_flags) && (ALLOC_TYPE(apropComdat->ac_selAlloc) == EXPLICIT)) { // Must preserve the allocation info from the .def file omfRec->gsn = apropComdat->ac_gsn; omfRec->ggr = apropComdat->ac_ggr; omfRec->attr = apropComdat->ac_selAlloc; } else OutError(ER_badconcat, 1 + GetPropName(apropComdat)); } vrComdatNew = RbAllocSymNode(sizeof(APROPCOMDAT)); // Allocate symbol space apropComdatNew = (APROPCOMDATPTR ) FetchSym(vrComdatNew, TRUE); apropComdatNew->ac_next = NULL; apropComdatNew->ac_attr = ATTRNIL; PickComDat(vrComdatNew, omfRec, TRUE); apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, TRUE); // Refetch head of the list if (apropComdat->ac_concat == VNIL) apropComdat->ac_concat = vrComdatNew; else { // Append at the end of the list vrTmp = apropComdat->ac_concat; while (vrTmp != VNIL) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrTmp, TRUE); vrTmp = apropComdat->ac_concat; } apropComdat->ac_concat = vrComdatNew; } } /*** AttachPublic - add matching public symbol to the COMDAT * * Purpose: * Attaches public symbol definition to the COMDAT definition. It is * necessary because the fixups work only on matched EXTDEF with PUBDEF * not with COMDAT, so in order to correctly resolve references to COMDAT * symbol linker needs the PUBDEF for the same symbol, which eventually * will be matched with references made to the EXTDEF. * The public symbols for COMDAT's are created when we see new COMDAT * symbol or we have COMDAT introduced by ORDER statement in the .DEF * file. * * Input: * vrComdat - virtual pointer to COMDAT descriptor * omfRec - COMDAT record * * Output: * No explicit value is returned. As a side effect the link is created * between COMDAT descriptor and new PUBDEF descriptor. * * Exceptions: * COMDAT symbol matches COMDEF symbol - display error and contine * with COMDEF symbol converted to PUBDEF. The .EXE will not load * under OS/2 or Windows, because the error bit will be set. * * Notes: * None. * *************************************************************************/ LOCAL void NEAR AttachPublic(RBTYPE vrComdat, COMDATREC *omfRec) { APROPNAMEPTR apropName; // Symbol table entry for public name APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol WORD fReferenced; // TRUE if we've seen CEXTDEF fReferenced = FALSE; apropName = (APROPNAMEPTR ) PropRhteLookup(omfRec->name, ATTRUND, FALSE); // Look for symbol among undefined if (apropName != PROPNIL) // Symbol known to be undefined { fReferenced = TRUE; #if TCE if(((APROPUNDEFPTR)apropName)->au_fAlive) // was called from a non-COMDAT { #if TCE_DEBUG fprintf(stdout, "\r\nAlive UNDEF -> COMDAT %s ", 1+GetPropName(apropName)); #endif AddTceEntryPoint((APROPCOMDAT*)vrComdat); } #endif } else apropName = (APROPNAMEPTR ) PropAdd(omfRec->name, ATTRPNM); // Else try to create new entry apropName->an_attr = ATTRPNM; // Symbol is a public name apropName->an_thunk = THUNKNIL; if (IsLOCAL(omfRec->flags)) { apropName->an_flags = 0; #if ILINK ++locMac; #endif } else { apropName->an_flags = FPRINT; ++pubMac; } #if ILINK apropName->an_module = imodFile; #endif MARKVP(); // Mark virtual page as changed apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, TRUE); apropComdat->ac_pubSym = vrprop; if (fReferenced) apropComdat->ac_flags |= REFERENCED_BIT; #if SYMDEB if (fSymdeb && !IsLOCAL(omfRec->flags) && !fSkipPublics) { DebPublic(vrprop, PUBDEF); } #endif } /*** ComDatRc1 - process COMDAT record in pass 1 * * Purpose: * Process COMDAT record in pass 1. Select appropriate copy of COMDAT. * * Input: * No explicit value is passed to this function. The OMF record is * read from input file bsInput - global variable. * * Output: * No explicit value is returned. Valid COMDAT descriptor is entered into * the linker symbol table. If necessary list of COMDATs allocated in the * explicit segment is updated. * * Exceptions: * Explicit allocation segment not found - error message and skip the record. * We have public with the same name as COMDAT - error message and skip * the record. * * Notes: * None. * *************************************************************************/ void NEAR ComDatRc1(void) { COMDATREC omfRec; // COMDAT OMF record APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol RBTYPE vrComdat; // Virtual pointer to COMDAT record char *sbName; // COMDAT symbol ReadComDat(&omfRec); apropComdat = (APROPCOMDATPTR ) PropRhteLookup(omfRec.name, ATTRCOMDAT, FALSE); // Look for symbol among COMDATs vrComdat = vrprop; #if TCE pActiveComdat = apropComdat; #endif if (apropComdat != PROPNIL) { // We already know a COMDAT with this name if (IsORDERED(apropComdat->ac_flags) && !IsDEFINED(apropComdat->ac_flags)) { if (ALLOC_TYPE(apropComdat->ac_selAlloc) == EXPLICIT) { // Preserve explicit allocation from the .def file omfRec.gsn = apropComdat->ac_gsn; omfRec.attr = apropComdat->ac_selAlloc; } PickComDat(vrComdat, &omfRec, FALSE); AttachPublic(vrComdat, &omfRec); } else if (!SkipCONCAT(apropComdat->ac_flags) && IsCONCAT(omfRec.flags) && (apropComdat->ac_obj == vrpropFile)) { // Append concatenation record ConcatComDat(vrComdat, &omfRec); } else { #if TCE pActiveComdat = NULL; // not needed for TCE #endif apropComdat->ac_flags |= SKIP_BIT; sbName = 1 + GetPropName(apropComdat); switch (SELECT_TYPE(omfRec.attr)) { case ONLY_ONCE: DupErr(sbName); break; case PICK_FIRST: break; case SAME_SIZE: if (apropComdat->ac_size != (DWORD) (cbRec - 1)) OutError(ER_badsize, sbName); break; case EXACT: if (apropComdat->ac_data != DoCheckSum()) OutError(ER_badexact, sbName); break; default: OutError(ER_badselect, sbName); break; } } } else { // Check if we know a public symbol with this name apropComdat = (APROPCOMDATPTR ) PropRhteLookup(omfRec.name, ATTRPNM, FALSE); if (apropComdat != PROPNIL) { if (!IsCONCAT(omfRec.flags)) { sbName = 1 + GetPropName(apropComdat); DupErr(sbName); // COMDAT matches code PUBDEF } // Display error only for the first COMDAT // ignore concatenation records } else { // Enter COMDAT into symbol table apropComdat = (APROPCOMDATPTR ) PropAdd(omfRec.name, ATTRCOMDAT); vrComdat = vrprop; PickComDat(vrprop, &omfRec, TRUE); AttachPublic(vrComdat, &omfRec); #if TCE #define ENTRIES 32 #if 0 fprintf(stdout, "\r\nNew COMDAT '%s' at %X; ac_uses %X, ac_usedby %X ", 1+GetPropName(apropComdat), apropComdat,&(apropComdat->ac_uses), &(apropComdat->ac_usedby)); fprintf(stdout, "\r\nNew COMDAT '%s' ",1+GetPropName(apropComdat)); #endif apropComdat->ac_fAlive = FALSE; // Assume it is unreferenced apropComdat->ac_uses.cEntries = 0; apropComdat->ac_uses.cMaxEntries = ENTRIES; apropComdat->ac_uses.pEntries = GetMem(ENTRIES * sizeof(APROPCOMDAT*)); pActiveComdat = apropComdat; #endif } } SkipBytes((WORD) (cbRec - 1)); // Skip to check sum byte } /*** AdjustOffsets - adjust COMDATs offsets * * Purpose: * Adjust COMDATs offsets to reflect their position inside * logical segments. * * Input: * vrComdat - virtual pointer to COMDAT symbol table entry * startOff - starting offset in the logical segment * gsnAlloc - global segment index of allocation segment * * Output: * No explicit value is returned. As a side effect offsets * of COMDAT data block are adjusted to their final position * inside the logical segment. All concatenated COMDAT block * get proper global logical segment index. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL void NEAR AdjustOffsets(RBTYPE vrComdat, DWORD startOff, SNTYPE gsnAlloc) { APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol while (vrComdat != VNIL) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, TRUE); // Fetch COMDAT descriptor from VM apropComdat->ac_ra += startOff; apropComdat->ac_gsn = gsnAlloc; vrComdat = apropComdat->ac_concat; } } /*** SizeOfComDat - return the size of COMDAT data * * Purpose: * Calculate the size of COMDAT data block. Takes into account the initial * offset from the COMDAT symbol and concatenation records. * * Input: * vrComdat - virtual pointer to COMDAT symbol table entry * * Output: * Returns TRUE and the size of COMDAT data block in pActual, otherwise * function returns FALSE and pActual is invalid. * * Exceptions: * COMDAT data block greater then 64k and allocation in 16-bit segment * - issue error and return FALSE * COMDAT data block grater then 4Gb - issue error and return FALSE * * Notes: * None. * *************************************************************************/ LOCAL WORD NEAR SizeOfComDat(RBTYPE vrComdat, DWORD *pActual) { APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol APROPSNPTR apropSn; // Pointer to COMDAT explicit segment long raInit; // Initial offset from COMDAT symbol DWORD sizeTotal; // Total size of data blocks WORD f16bitAlloc; // TRUE if allocation in 16-bit segment RBTYPE vrTmp; *pActual = 0L; raInit = -1L; sizeTotal= 0L; f16bitAlloc = FALSE; vrTmp = vrComdat; while (vrTmp != VNIL) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrTmp, FALSE); // Fetch COMDAT descriptor from VM if (raInit == -1L) raInit = apropComdat->ac_ra;// Remember initial offset else if (apropComdat->ac_ra < sizeTotal) sizeTotal = apropComdat->ac_ra; // Correct size for overlaping blocks if (sizeTotal + apropComdat->ac_size < sizeTotal) { // Oops !!! more then 4Gb OutError(ER_size4Gb, 1 + GetPropName(apropComdat)); return(FALSE); } sizeTotal += apropComdat->ac_size; vrTmp = apropComdat->ac_concat; } sizeTotal += raInit; apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); // Refetch COMDAT descriptor from VM if (apropComdat->ac_gsn) { apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[apropComdat->ac_gsn],FALSE); // Fetch SEGDEF from virtual memory #if NOT EXE386 if (!Is32BIT(apropSn->as_flags)) f16bitAlloc = TRUE; #endif } else f16bitAlloc = (WORD) ((ALLOC_TYPE(apropComdat->ac_selAlloc) == CODE16) || (ALLOC_TYPE(apropComdat->ac_selAlloc) == DATA16)); if (f16bitAlloc && sizeTotal > LXIVK) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); OutError(ER_badsize, 1 + GetPropName(apropComdat)); return(FALSE); } *pActual = sizeTotal; return(TRUE); } /*** DoAligment - self-explanatory * * Purpose: * Given the aligment type round the value to the specific boundary. * * Input: * value - value to align * align - aligment type * * Output: * Returns the aligned value. * * Exceptions: * Unknow aligment type - do nothing. * * Notes: * None. * *************************************************************************/ LOCAL RATYPE NEAR DoAligment(RATYPE value, WORD align) { if (align == ALGNWRD) value = (~0L<<1) & (value + (1<<1) - 1); // Round size up to word boundary #if OMF386 else if (align == ALGNDBL) value = (~0L<<2) & (value + (1<<2) - 1); #endif // Round size up to double boundary else if (align == ALGNPAR) value = (~0L<<4) & (value + (1<<4) - 1); // Round size up to para. boundary else if (align == ALGNPAG) value = (~0L<<8) & (value + (1<<8) - 1); // Round size up to page boundary return(value); } /*** DoAllocation - palce COMDAT inside given segment * * Purpose: * Perform actual COMDAT allocation in given segment. Adjust COMDAT * position according to the segment or COMDAT (if specified) aligment. * * Input: * gsnAlloc - allocation segment global index * gsnSize - current segment size * vrComdat - virtual pointer to COMDAT descriptor * comdatSize - comdat size * * Output: * Function returns the new segment size. As a side effect the COMDAT * descriptor is updated to reflect its allocation and the matching * PUBDEF is entered into symbol table. * * Exceptions: * None. * * Notes: * After allocation the field ac_size is the size of the whole * COMDAT allocation including all concatenated records, so don't use * it for determinig the size of this COMDAT record.. * *************************************************************************/ LOCAL DWORD NEAR DoAllocation(SNTYPE gsnAlloc, DWORD size, RBTYPE vrComdat, DWORD comdatSize) { APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol APROPSNPTR apropSn; // Pointer to COMDAT segment APROPNAMEPTR apropName; // Symbol table entry for public name WORD comdatAlloc; // Allocation criteria WORD comdatAlign; // COMDAT alignment WORD align; // Alignment type WORD f16bitAlloc; // TRUE if allocation in 16-bit segment WORD fCode; // TRUE if allocation in code segment GRTYPE comdatGgr; // Global group index RATYPE comdatRa; // Offset in logical segmet apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); if (IsALLOCATED(apropComdat->ac_flags) #if OVERLAYS || (fOverlays && (apropComdat->ac_iOvl != iOvlCur)) #endif ) return(size); #if TCE if (fTCE && !apropComdat->ac_fAlive) { #if TCE_DEBUG fprintf(stdout, "\r\nComdat '%s' not included due to TCE ", 1+GetPropName(apropComdat)); #endif apropComdat->ac_flags = apropComdat->ac_flags & (!REFERENCED_BIT); return(size); } #endif comdatAlloc = (WORD) (ALLOC_TYPE(apropComdat->ac_selAlloc)); comdatAlign = apropComdat->ac_align; comdatGgr = apropComdat->ac_ggr; apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[gsnAlloc],FALSE); fCode = (WORD) IsCodeFlg(apropSn->as_flags); if (comdatAlign) align = comdatAlign; else align = (WORD) ((apropSn->as_tysn >> 2) & 7); size = DoAligment(size, align); if (comdatAlloc == CODE16 || comdatAlloc == DATA16) f16bitAlloc = TRUE; #if NOT EXE386 else if (!Is32BIT(apropSn->as_flags)) f16bitAlloc = TRUE; #endif else f16bitAlloc = FALSE; if (SegSizeOverflow(size, comdatSize, f16bitAlloc, fCode)) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[gsnAlloc],FALSE); Fatal(ER_nospace, 1 + GetPropName(apropComdat), 1 + GetPropName(apropSn)); } else { AdjustOffsets(vrComdat, size, gsnAlloc); size += comdatSize; apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, TRUE); comdatRa = apropComdat->ac_ra; apropComdat->ac_flags |= ALLOCATED_BIT; apropComdat->ac_size = comdatSize; if (apropComdat->ac_pubSym != VNIL) { apropName = (APROPNAMEPTR ) FetchSym(apropComdat->ac_pubSym, TRUE); apropName->an_ggr = comdatGgr; apropName->an_gsn = gsnAlloc; apropName->an_ra = comdatRa; } else Fatal(ER_unalloc, 1 + GetPropName(apropComdat)); } return(size); } /*** AllocComDat - allocate COMDATs * * Purpose: * Allocate COMDATs in the final memory image. First start with ordered * allocation - in .DEF file we saw the list of procedures. Next allocate * explicitly assinged COMDATs to specific logical segments. And finally * allocate the rest of COMDATs creating as many as necessary segments * to hold all allocations. * * Input: * No ecplicit value is passed - I love this - side effects forever. * In the good linker tradition of using global variables, here is the * list of globals used by this function: * * mpgsnaprop - table mapping global segment index to symbol table entry * gsnMac - maximum global segment index * * Output: * No explicit value is returned - didn't I tell you - side effects forever. * So, the side effect of this function is the allocation of COMDATs in the * appropriate logical segments. The offset fields in the COMDAT descriptor * (ac_ra) now reflect the final posiotion of data block associated with the * COMDAT symbol inside given logical segment. The sizes of appropriate * segments are updated to reflect allocation of COMDATs. For every COMDAT * symbol there is a matching PUBDEF created by this function, so in pass2 * linker can resolve correctly all references (via fixups to EXTDEF with * the COMDAT name) to COMDAT symbols. * * Exceptions: * No space in explicitly designated segment for COMDAT - print error * message and skip COMDAT; probably in pass2 user will see many * unresolved externals. * * Notes: * This function MUST be called before AssignAddresses. Otherwise there * will be no spcase for COMDAT allocation, because logical segments will * packed into physical ones. * *************************************************************************/ void NEAR AllocComDat(void) { SNTYPE gsn; RBTYPE vrComdat; // Virtual pointer to COMDAT descriptor APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol APROPSNPTR apropSn; // Pointer to COMDAT explicit segment RATYPE gsnSize; // Size of explicit segment DWORD comdatSize; // COMDAT data block size APROPCOMDAT comdatDsc; // COMDAT symbol table descriptor APROPFILEPTR apropFile; // Pointer to file entry APROPNAMEPTR apropName; // Pointer to matching PUBDEF RBTYPE vrFileNext; // Virtual pointer to prop list of next file #if COMDATDEBUG DisplayComdats("BEFORE allocation", FALSE); #endif #if TCE APROPCOMDATPTR apropMain; // Do Transitive Comdat Elimination (TCE) if(fTCE) { apropMain = PropSymLookup("\5_main", ATTRCOMDAT, FALSE); if(apropMain) AddTceEntryPoint(apropMain); #if TCE_DEBUG else fprintf(stdout, "\r\nUnable to find block '_main' "); #endif apropMain = PropSymLookup("\7WINMAIN", ATTRCOMDAT, FALSE); if(apropMain) AddTceEntryPoint(apropMain); #if TCE_DEBUG else fprintf(stdout, "\r\nUnable to find block 'WINMAIN' "); #endif apropMain = PropSymLookup("\7LIBMAIN", ATTRCOMDAT, FALSE); if(apropMain) AddTceEntryPoint(apropMain); #if TCE_DEBUG else fprintf(stdout, "\r\nUnable to find block 'LIBMAIN' "); #endif PerformTce(); } #endif // Loop thru overlays - for non overlayed executables // the following loop is executed only once - iovMac = 1 #if OVERLAYS for (iOvlCur = 0; iOvlCur < iovMac; iOvlCur++) { #endif // Do ordered allocation for (vrComdat = procOrder; vrComdat != VNIL; vrComdat = comdatDsc.ac_order) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); comdatDsc = *apropComdat; if (!(comdatDsc.ac_flags & DEFINED_BIT)) { OutWarn(ER_notdefcomdat, 1 + GetPropName(apropComdat)); continue; } #if OVERLAYS if (fOverlays && (apropComdat->ac_iOvl != NOTIOVL) && (apropComdat->ac_iOvl != iOvlCur)) continue; #endif if (fPackFunctions && !IsREFERENCED(apropComdat->ac_flags)) continue; if (SizeOfComDat(vrComdat, &comdatSize)) { if (ALLOC_TYPE(comdatDsc.ac_selAlloc) == EXPLICIT) { gsn = comdatDsc.ac_gsn; apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[gsn], FALSE); gsnSize = apropSn->as_cbMx; gsnSize = DoAllocation(gsn, gsnSize, vrComdat, comdatSize); apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[gsn], TRUE); apropSn->as_cbMx = gsnSize; } else if (ALLOC_TYPE(comdatDsc.ac_selAlloc) != ALLOC_UNKNOWN) AllocAnonymus(vrComdat); } } // Do explicit allocation for (gsn = 1; gsn < gsnMac; gsn++) { apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[gsn],FALSE); // Fetch SEGDEF from virtual memory #if OVERLAYS if (fOverlays && apropSn->as_iov != iOvlCur) continue; #endif if (apropSn->as_ComDat != VNIL) { gsnSize = apropSn->as_cbMx; for (vrComdat = apropSn->as_ComDat; vrComdat != VNIL && SizeOfComDat(vrComdat, &comdatSize); vrComdat = apropComdat->ac_sameSeg) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); if ((!fPackFunctions || IsREFERENCED(apropComdat->ac_flags)) && !IsALLOCATED(apropComdat->ac_flags)) { gsnSize = DoAllocation(gsn, gsnSize, vrComdat, comdatSize); } } apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[gsn], TRUE); apropSn->as_cbMx = gsnSize; } } // Now allocate the rest of COMDATs vrFileNext = rprop1stFile; // Next file to look at is first while (vrFileNext != VNIL) // Loop to process objects { apropFile = (APROPFILEPTR ) FetchSym(vrFileNext, FALSE); // Fetch table entry from VM vrFileNext = apropFile->af_FNxt; // Get pointer to next file for (vrComdat = apropFile->af_ComDat; vrComdat != VNIL; vrComdat = apropComdat->ac_sameFile) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); // Fetch table entry from VM #if OVERLAYS if (fOverlays && (apropComdat->ac_iOvl != NOTIOVL) && (apropComdat->ac_iOvl != iOvlCur)) continue; #endif if (!IsREFERENCED(apropComdat->ac_flags)) { // Mark matching PUBDEF as unreferenced, so it shows // in the map file apropName = (APROPNAMEPTR) FetchSym(apropComdat->ac_pubSym, TRUE); apropName->an_flags |= FUNREF; } if ((!fPackFunctions || IsREFERENCED(apropComdat->ac_flags)) && !IsALLOCATED(apropComdat->ac_flags)) { if (ALLOC_TYPE(apropComdat->ac_selAlloc) != ALLOC_UNKNOWN) { AllocAnonymus(vrComdat); } } } } // Close all open segments for anonymus allocation if (curGsnCode16) { apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[curGsnCode16], TRUE); apropSn->as_cbMx = curCodeSize16; curGsnCode16 = 0; curCodeSize16 = 0; } if (curGsnData16) { apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[curGsnData16], TRUE); apropSn->as_cbMx = curDataSize16; curGsnData16 = 0; curDataSize16 = 0; } #if EXE386 if (curGsnCode32) { apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[curGsnCode32], TRUE); apropSn->as_cbMx = curCodeSize32; curGsnCode32 = 0; curCodeSize32 = 0; } if (curGsnData32) { apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[curGsnData32], TRUE); apropSn->as_cbMx = curDataSize32; curGsnData32 = 0; curDataSize32 = 0; } #endif #if OVERLAYS } #endif #if COMDATDEBUG DisplayComdats("AFTER allocation", FALSE); #endif } /*** NewSegment - open new COMDAT segment * * Purpose: * Open new linker defined segment. The name of the segment is created * according to the following template: * * COMDAT_SEG * * where - is the segment number. * If there was previous segment, then update its size. * * Input: * gsnPrev - previous segment global index * sizePrev - previous segment size * allocKind - segment type to open * * Output: * Function returns the new global segment index in gsnPrev and * new segment aligment; * * Exceptions: * To many logical segments - error message displayed by GenSeg * and abort. * * Notes: * None. * *************************************************************************/ LOCAL WORD NEAR NewSegment(SNTYPE *gsnPrev, DWORD *sizePrev, WORD allocKind) { static int segNo = 1; // Segment number char segName[20]; // Segment name - no names longer then // 20 chars since we are generating them APROPSNPTR apropSn; // Pointer to COMDAT segment if (*gsnPrev) { // Update previous segment size apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[*gsnPrev], TRUE); apropSn->as_cbMx = *sizePrev; *sizePrev = 0L; } // Create segment name strcpy(segName, COMDAT_SEG_NAME); _itoa(segNo, &segName[COMDAT_NAME_LEN], 10); segName[0] = (char) strlen(&segName[1]); ++segNo; if (allocKind == CODE16 || allocKind == CODE32) { apropSn = GenSeg(segName, "\004CODE", GRNIL, (FTYPE) TRUE); apropSn->as_flags = dfCode; // Use default code flags } else { apropSn = GenSeg(segName, allocKind == DATA16 ? "\010FAR_DATA" : "\004DATA", GRNIL, (FTYPE) TRUE); apropSn->as_flags = dfData; // Use default data flags } #if OVERLAYS apropSn->as_iov = (IOVTYPE) NOTIOVL; CheckOvl(apropSn, iOvlCur); #endif // Set segment aligment #if EXE386 apropSn->as_tysn = DWORDPUBSEG; // DWORD #else apropSn->as_tysn = PARAPUBSEG; // Paragraph #endif *gsnPrev = apropSn->as_gsn; apropSn->as_fExtra |= COMDAT_SEG; return((WORD) ((apropSn->as_tysn >> 2) & 7)); } /*** SegSizeOverflow - check the segment size * * Purpose: * Check if the allocation of the COMDAT in a given segment will * overflow its size limits. The segment size limit can be changed * by the /PACKCODE: or /PACKDATA: options. If the /PACKxxxx * options are not used the limits are: * * - 64k - 36 - for 16-bit code segments * - 64k - for 16-bit data segments * - 4Gb - for 32-bit code/data segments * * Input: * segSize - segment size * comdatsize - COMDAT size * f16bit - TRUE if 16-bit segment * fCode - TRUE if code segment * * Output: * Function returns TRUE if size overflow, otherwise function * returns FALSE. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL WORD NEAR SegSizeOverflow(DWORD segSize, DWORD comdatSize, WORD f16bit, WORD fCode) { DWORD limit; if (fCode) { if (packLim) limit = packLim; else if (f16bit) limit = LXIVK - 36; else limit = CBMAXSEG32; } else { if (DataPackLim) limit = DataPackLim; else if (f16bit) limit = LXIVK; else limit = CBMAXSEG32; } return(limit - comdatSize < segSize); } /*** AttachComdat - add comdat to the segment list * * Purpose: * Add comdat descriptor to the list of comdats allocated in the * given logical segment. Check for overlay assigment missmatch * and report problems. * * Input: * vrComdat - virtual pointer to the comdat descriptor * gsn - global logical segment index * * Output: * No explicit value is returned. As a side effect the comdat * descriptor is placed on the allocation list of given segment. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ void AttachComdat(RBTYPE vrComdat, SNTYPE gsn) { RBTYPE vrTmp; // Virtual pointer to COMDAT symbol table entry APROPSNPTR apropSn; // Pointer to COMDAT segment if explicit allocation APROPCOMDATPTR apropComdat; // Pointer to symbol table descriptor apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, TRUE); // Attach this COMDAT to its segment list apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[gsn], TRUE); #if OVERLAYS if (fOverlays && (apropComdat->ac_iOvl != apropSn->as_iov)) { if (apropComdat->ac_iOvl != NOTIOVL) OutWarn(ER_badcomdatovl, 1 + GetPropName(apropComdat), apropComdat->ac_iOvl, apropSn->as_iov); apropComdat->ac_iOvl = apropSn->as_iov; } #endif if (apropSn->as_ComDat == VNIL) { apropSn->as_ComDat = vrComdat; apropSn->as_ComDatLast = vrComdat; apropComdat->ac_sameSeg = VNIL; } else { // Because COMDATs can be attached to a given segment in the // .DEF file and later in the .OBJ file, we have to check // if a given comdat is already on the segment list for (vrTmp = apropSn->as_ComDat; vrTmp != VNIL;) { if (vrTmp == vrComdat) return; apropComdat = (APROPCOMDATPTR ) FetchSym(vrTmp, FALSE); vrTmp = apropComdat->ac_sameSeg; } // Add new COMDAT to the segment list apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[gsn], TRUE); vrTmp = apropSn->as_ComDatLast; apropSn->as_ComDatLast = vrComdat; apropComdat = (APROPCOMDATPTR ) FetchSym(vrTmp, TRUE); apropComdat->ac_sameSeg = vrComdat; } } /*** AllocAnonymus - allocate COMDATs without explicit segment * * Purpose: * Allocate COMDATs without explicit segment. Create as many as necessary * code/data segments to hold all COMDATs. * * Input: * vrComdat - virtual pointer to symbol table entry for COMDAT name * * Output: * No explicit value is returned. As a side effect the COMDAT symbol is * allocated and matching public symbol is created. If necessary * appropriate segment is defined. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ LOCAL void NEAR AllocAnonymus(RBTYPE vrComdat) { WORD comdatAlloc; // Allocation type WORD comdatAlign; // COMDAT aligment WORD align; DWORD comdatSize; // COMDAT data block size APROPCOMDATPTR apropComdat; // Real pointer to COMDAT descriptor static WORD segAlign16; // Aligment for 16-bit segments #if EXE386 static WORD segAlign32; // Aligment for 32-bit segments #endif apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); comdatAlloc = (WORD) (ALLOC_TYPE(apropComdat->ac_selAlloc)); comdatAlign = apropComdat->ac_align; if (SizeOfComDat(vrComdat, &comdatSize)) { if (comdatAlign) align = comdatAlign; else { #if EXE386 if (comdatAlloc == CODE32 || comdatAlloc == DATA32) align = segAlign32; else #endif align = segAlign16; } switch (comdatAlloc) { case CODE16: if (!curGsnCode16 || SegSizeOverflow(DoAligment(curCodeSize16, align), comdatSize, TRUE, TRUE)) { // Open new 16-bit code segment segAlign16 = NewSegment(&curGsnCode16, &curCodeSize16, comdatAlloc); } curCodeSize16 = DoAllocation(curGsnCode16, curCodeSize16, vrComdat, comdatSize); AttachComdat(vrComdat, curGsnCode16); break; case DATA16: if (!curGsnData16 || SegSizeOverflow(DoAligment(curDataSize16, align), comdatSize, TRUE, FALSE)) { // Open new 16-bit data segment segAlign16 = NewSegment(&curGsnData16, &curDataSize16, comdatAlloc); } curDataSize16 = DoAllocation(curGsnData16, curDataSize16, vrComdat, comdatSize); AttachComdat(vrComdat, curGsnData16); break; #if EXE386 case CODE32: if (!curGsnCode32 || SegSizeOverflow(DoAligment(curCodeSize32, align), comdatSize, FALSE, TRUE)) { // Open new 32-bit code segment segAlign32 = NewSegment(&curGsnCode32, &curCodeSize32, comdatAlloc); } curCodeSize32 = DoAllocation(curGsnCode32, curCodeSize32, vrComdat, comdatSize); AttachComdat(vrComdat, curGsnCode32); break; case DATA32: if (!curGsnData32 || SegSizeOverflow(DoAligment(curDataSize32, align), comdatSize, FALSE, FALSE)) { // Open new 32-bit data segment segAlign32 = NewSegment(&curGsnData32, &curDataSize32, comdatAlloc); } curDataSize32 = DoAllocation(curGsnData32, curDataSize32, vrComdat, comdatSize); AttachComdat(vrComdat, curGsnData32); break; #endif default: OutError(ER_badalloc, 1 + GetPropName(apropComdat)); return; } } } /*** FixComdatRa - shift by 16 bytes COMDATs allocated in _TEXT * * Purpose: * Follow the /DOSSEG convention for logical segment _TEXT, * and shift up by 16 bytes all COMDATS allocated in this segment. * * Input: * gsnText - _TEXT global segment index - global variable * * Output: * No explicit value is returned. As a side effect the offset of * the COMDAT allocated in _TEXT is increased by 16. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ void FixComdatRa(void) { APROPSNPTR apropSn; // Pointer to COMDAT explicit segment RBTYPE vrComdat; // Virtual pointer to COMDAT descriptor RBTYPE vrConcat; // Virtual pointer to concatenated records APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol RATYPE raShift; apropSn = (APROPSNPTR ) FetchSym(mpgsnrprop[gsnText], FALSE); // Fetch SEGDEF from virtual memory raShift = mpgsndra[gsnText] - mpsegraFirst[mpgsnseg[gsnText]]; for (vrComdat = apropSn->as_ComDat; vrComdat != VNIL;) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, TRUE); vrComdat = apropComdat->ac_sameSeg; if (fPackFunctions && !IsREFERENCED(apropComdat->ac_flags)) continue; apropComdat->ac_ra += raShift; // Search concatenation list for (vrConcat = apropComdat->ac_concat; vrConcat != VNIL; vrConcat = apropComdat->ac_concat) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrConcat, TRUE); apropComdat->ac_ra += raShift; } } } /*** UpdateComdatContrib - update COMDATs contributions * * Purpose: * For every file with COMDATs add contribution information to the * .ILK file. Some COMDATs are allocated in named segments, some * in anonynus segments created by linker. The ILINK needs to know * how much given .OBJ file contributed to given logical segment. * Since the COMDAT contributions are not visible while processing * object files in pass one, this function is required to update * contribution information. Also if /MAP:FULL i used then add * COMDATs contributions to the map file information * * Input: * - fIlk - TRUE if updating ILINK information * - fMap - TRUE if updating MAP file information * - rprop1stFile - head of .OBJ file list * - vrpropFile - pointer to the current .OBJ * * Output: * No explicit value is returned. * * Exceptions: * None. * * Notes: * This function has to be called after pass 2, so all non-COMDAT * contributions are already recorded. This allows us to detect the * fact that named COMDAT allocation (explicit segment) has increased * contribution to given logical segment by the size of COMDATs. * *************************************************************************/ void UpdateComdatContrib( #if ILINK WORD fIlk, #endif WORD fMap) { APROPFILEPTR apropFile; // Pointer to file entry RBTYPE vrFileNext; // Virtual pointer to prop list of next file APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol RBTYPE vrComdat; // Virtual pointer to COMDAT descriptor SNTYPE gsnCur; // Global segment index of current segment DWORD sizeCur; // Current segment size RATYPE raInit; // Initial offset of the first COMDAT // allocated in the given segment #if ILINK RATYPE raEnd; // End offset #endif vrFileNext = rprop1stFile; // Next file to look at is first while (vrFileNext != VNIL) // Loop to process objects { vrpropFile = vrFileNext; // Make next file the current file apropFile = (APROPFILEPTR ) FetchSym(vrFileNext, FALSE); // Fetch table entry from VM vrFileNext = apropFile->af_FNxt;// Get pointer to next file vrComdat = apropFile->af_ComDat; #if ILINK imodFile = apropFile->af_imod; #endif sizeCur = 0L; while (vrComdat != VNIL) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); // Fetch table entry from VM vrComdat = apropComdat->ac_sameFile; if (fPackFunctions && !IsREFERENCED(apropComdat->ac_flags)) continue; raInit = apropComdat->ac_ra; gsnCur = apropComdat->ac_gsn; #if ILINK raEnd = raInit + apropComdat->ac_size; #endif sizeCur = apropComdat->ac_size; // Save information about contributions #if ILINK if (fIlk) AddContribution(gsnCur, (WORD) raInit, (WORD) raEnd, cbPadCode); #endif if (fMap) AddContributor(gsnCur, raInit, sizeCur); } } } #if SYMDEB /*** DoComdatDebugging - notify CodeView about segments with COMDATs * * Purpose: * CodeView expects in sstModules subsection an information about code * segments defined in the given object module (.OBJ file). When COMDATs * are present linker has no way of figuring this out in pass 1, because * there is no code segment definitions (all COMDATs have anonymus * allocation) or the code segments have size zero (explicit allocation). * This function is called after the COMDAT allocation is performed and * segments have assigned their addresses. The list of .OBJ files is * traversed and for each .OBJ file the list of COMDATs defined in this * file is examined and the appropriate code segment information is * stored for CodeView. * * Input: * No explicit value is passed. The following global variables are used: * * rprop1stFile - head of .OBJ file list * vrpropFile - pointer to the current .OBJ * * Output: * No explicit value is returned. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ void NEAR DoComdatDebugging(void) { APROPFILEPTR apropFile; // Pointer to file entry RBTYPE vrFileNext; // Virtual pointer to prop list of next file APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol RBTYPE vrComdat; // Virtual pointer to COMDAT descriptor SNTYPE gsnCur; // Global segment index of current segment RATYPE raInit; // Initial offset of the first COMDAT // allocated in the given segment RATYPE raEnd; // End of contributor vrFileNext = rprop1stFile; // Next file to look at is first while (vrFileNext != VNIL) // Loop to process objects { vrpropFile = vrFileNext; // Make next file the current file apropFile = (APROPFILEPTR ) FetchSym(vrFileNext, FALSE); // Fetch table entry from VM vrFileNext = apropFile->af_FNxt;// Get pointer to next file vrComdat = apropFile->af_ComDat; while (vrComdat != VNIL) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); // Fetch table entry from VM raInit = (RATYPE)-1; raEnd = 0; gsnCur = apropComdat->ac_gsn; while (vrComdat != VNIL && gsnCur == apropComdat->ac_gsn) { if(apropComdat->ac_ra < raInit && IsALLOCATED(apropComdat->ac_flags)) raInit = apropComdat->ac_ra; if(apropComdat->ac_ra + apropComdat->ac_size > raEnd) raEnd = apropComdat->ac_ra + apropComdat->ac_size; vrComdat = apropComdat->ac_sameFile; if (vrComdat != VNIL) apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); } // Contribution to the new logical segment from this .OBJ file SaveCode(gsnCur, raEnd - raInit, raInit); } } } #endif /*** ComDatRc2 - process COMDAT record in pass 2 * * Purpose: * Process COMDAT record in pass 1. Select appropriate copy of COMDAT. * * Input: * No explicit value is passed to this function. The OMF record is * read from input file bsInput - global variable. * * Output: * No explicit value is returned. Apropriate copy of COMDAT data block * is loaded into final memory image. * * Exceptions: * Unknown COMDAT name - phase error - display internal LINK error and quit * Unallocated COMDAT - phase error - display internal LINK error and quit * * Notes: * None. * *************************************************************************/ void NEAR ComDatRc2(void) { COMDATREC omfRec; // COMDAT OMF record APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol WORD fRightCopy; // TRUE if we have rigth instance of COMDAT RBTYPE vrTmp; // Temporary char *sbName; // COMDAT symbol AHTEPTR ahte; // Hash table entry ReadComDat(&omfRec); apropComdat = (APROPCOMDATPTR ) PropRhteLookup(omfRec.name, ATTRCOMDAT, FALSE); // Look for symbol among COMDATs ahte = (AHTEPTR) FetchSym(omfRec.name, FALSE); sbName = 1 + GetFarSb(ahte->cch); if (apropComdat == PROPNIL) Fatal(ER_undefcomdat, sbName); if (fPackFunctions && !IsREFERENCED(apropComdat->ac_flags)) { SkipBytes((WORD) (cbRec - 1)); // Skip to checksum byte fSkipFixups = TRUE; // Skip fixups if any return; } if (!IsCONCAT(omfRec.flags)) fRightCopy = (WORD) (apropComdat->ac_obj == vrpropFile && apropComdat->ac_objLfa == lfaLast); else { // Search concatenation list vrTmp = apropComdat->ac_concat; fRightCopy = FALSE; while (vrTmp != VNIL && !fRightCopy) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrTmp, TRUE); vrTmp = apropComdat->ac_concat; fRightCopy = (WORD) (apropComdat->ac_obj == vrpropFile && apropComdat->ac_objLfa == lfaLast); } } if (fRightCopy) { // This is the right copy of COMDAT if (!apropComdat->ac_gsn) Fatal(ER_unalloc, sbName); apropComdat->ac_flags |= SELECTED_BIT; fSkipFixups = FALSE; // Process fixups if any omfRec.gsn = apropComdat->ac_gsn; omfRec.ra = apropComdat->ac_ra; // Set relative address omfRec.flags = apropComdat->ac_flags; vcbData = (WORD) (cbRec - 1); // set no. of data bytes in rec if (vcbData > DATAMAX) Fatal(ER_datarec); // Check if record too large #if NOT RGMI_IN_PLACE GetBytesNoLim(rgmi, vcbData); // Fill the buffer #endif vgsnCur = omfRec.gsn; // Set global segment index fDebSeg = (FTYPE) ((fSymdeb) ? (((0x8000 & omfRec.gsn) != 0)) : FALSE); // If debug option on check for debug segs if (fDebSeg) { // If debug segment vraCur = omfRec.ra; // Set current relative address vsegCur = vgsnCur = (SEGTYPE) (0x7fff & omfRec.gsn); // Set current segment } else { // If not a valid segment, don't process datarec #if SYMDEB if (omfRec.gsn == 0xffff || !omfRec.gsn || mpgsnseg[omfRec.gsn] > segLast) #else if (!omfRec.gsn || mpgsnseg[omfRec.gsn] > segLast) #endif { vsegCur = SEGNIL; vrectData = RECTNIL; #if RGMI_IN_PLACE SkipBytes(vcbData); // must skip bytes for this record... #endif return; // Good-bye! } vsegCur = mpgsnseg[omfRec.gsn]; // Set current segment vraCur = mpsegraFirst[vsegCur] + omfRec.ra; // Set current relative address if (IsVTABLE(apropComdat->ac_flags)) { fFarCallTransSave = fFarCallTrans; fFarCallTrans = (FTYPE) FALSE; } } if (IsITERATED(omfRec.flags)) { #if RGMI_IN_PLACE rgmi = bufg; GetBytesNoLim(rgmi, vcbData); // Fill the buffer #endif vrectData = LIDATA; // Simulate LIDATA #if OSEGEXE if(fNewExe) { if (vcbData >= DATAMAX) Fatal(ER_lidata); rlcLidata = (RLCPTR) &rgmi[(vcbData + 1) & ~1]; // Set base of fixup array rlcCurLidata = rlcLidata;// Initialize pointer return; } #endif #if ODOS3EXE OR OIAPX286 if(vcbData > (DATAMAX / 2)) { OutError(ER_lidata); memset(&rgmi[vcbData],0,DATAMAX - vcbData); } else memset(&rgmi[vcbData],0,vcbData); ompimisegDstIdata = (char *) rgmi + vcbData; #endif } else { #if RGMI_IN_PLACE rgmi = PchSegAddress(vcbData, vsegCur, vraCur); GetBytesNoLim(rgmi, vcbData); // Fill the buffer #endif vrectData = LEDATA; // Simulate LEDATA } if (rect & 1) vrectData++; // Simulate 32-bit version } else { SkipBytes((WORD) (cbRec - 1)); // Skip to checksum byte fSkipFixups = TRUE; // Skip fixups if any } } #if COMDATDEBUG #include /*** DisplayOne - display one COMDAT symbol table entry * * Purpose: * Debug aid. Display on standard output the contents of given * COMDAT symbol table entry. * * Input: * papropName - real pointer to symbol table entry * rhte - hash vector entry * rprop - pointer to property cell * fNewHte - TRUE if new proprerty list (new entry in hash vector) * * Output: * No explicit value is returned. * * Exceptions: * None. * * Notes: * This function is in standard EnSyms format. * *************************************************************************/ LOCAL void DisplayOne(APROPCOMDATPTR apropName, WORD fPhysical) { APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol RBTYPE vrComdat; // Virtual pointer to COMDAT descriptor APROPCOMDAT comdatDsc; // COMDAT descriptor SEGTYPE seg; FMEMCPY((char FAR *) &comdatDsc, apropName, sizeof(APROPCOMDAT)); if (fPhysical) seg = mpgsnseg[comdatDsc.ac_gsn]; fprintf(stdout, "%s:\r\n", 1 + GetPropName(apropName)); fprintf(stdout, "ggr = %d; gsn = %d; ra = 0x%lx; size = %d\r\n", comdatDsc.ac_ggr, comdatDsc.ac_gsn, comdatDsc.ac_ra, comdatDsc.ac_size); if (fPhysical) fprintf(stdout, "sa = 0x%x; ra = 0x%lx\r\n", mpsegsa[seg], mpsegraFirst[seg] + comdatDsc.ac_ra); fprintf(stdout, "flags = 0x%x; selAlloc = 0x%x; align = 0x%x\r\n", comdatDsc.ac_flags, comdatDsc.ac_selAlloc, comdatDsc.ac_align); fprintf(stdout, "data = 0x%lx; obj = 0x%lx; objLfa = 0x%lx\r\n", comdatDsc.ac_data, comdatDsc.ac_obj, comdatDsc.ac_objLfa); fprintf(stdout, "concat = 0x%lx; sameSeg = 0x%lx pubSym = 0x%lx\r\n", comdatDsc.ac_concat, comdatDsc.ac_sameSeg, comdatDsc.ac_pubSym); fprintf(stdout, "order = 0x%lx; iOvl = 0x%x\r\n", comdatDsc.ac_order, comdatDsc.ac_iOvl); vrComdat = comdatDsc.ac_concat; while (vrComdat != VNIL) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); FMEMCPY((char FAR *) &comdatDsc, apropComdat, sizeof(APROPCOMDAT)); fprintf(stdout, " +++ ggr = %d; gsn = %d; ra = 0x%lx; size = %d\r\n", comdatDsc.ac_ggr, comdatDsc.ac_gsn, comdatDsc.ac_ra, comdatDsc.ac_size); if (fPhysical) fprintf(stdout, " sa = 0x%x; ra = 0x%lx\r\n", mpsegsa[seg], mpsegraFirst[seg] + comdatDsc.ac_ra); fprintf(stdout, " flags = 0x%x; selAlloc = 0x%x; align = 0x%x\r\n", comdatDsc.ac_flags, comdatDsc.ac_selAlloc, comdatDsc.ac_align); fprintf(stdout, " data = 0x%lx; obj = 0x%lx; objLfa = 0x%lx\r\n", comdatDsc.ac_data, comdatDsc.ac_obj, comdatDsc.ac_objLfa); fprintf(stdout, " concat = 0x%lx; sameSeg = 0x%lx pubSym = 0x%lx\r\n", comdatDsc.ac_concat, comdatDsc.ac_sameSeg, comdatDsc.ac_pubSym); fprintf(stdout, " order = 0x%lx; iOvl = 0x%x\r\n", comdatDsc.ac_order, comdatDsc.ac_iOvl); vrComdat = comdatDsc.ac_concat; } fprintf(stdout, "\r\n"); fflush(stdout); } /*** DisplayComdats - self-expalnatory * * Purpose: * Debug aid. Enumerates all COMDAT records in the linker symbol table * displaying each entry. * * Input: * title - pointer to info string. * fPhysical - display physical addresses - allowed only if you call * this function after AssignAddresses. * * Output: * No explicit value is returned. COMDAT information is written to sdtout. * * Exceptions: * None. * * Notes: * None. * *************************************************************************/ void DisplayComdats(char *title, WORD fPhysical) { APROPFILEPTR apropFile; // Pointer to file entry RBTYPE rbFileNext; // Virtual pointer to prop list of next file APROPCOMDATPTR apropComdat; // Symbol table entry for COMDAT symbol RBTYPE vrComdat; // Virtual pointer to COMDAT descriptor fprintf(stdout, "\r\nDisplayComdats: %s\r\n\r\n", title); rbFileNext = rprop1stFile; // Next file to look at is first while (rbFileNext != VNIL) // Loop to process objects { apropFile = (APROPFILEPTR ) FetchSym(rbFileNext, FALSE); // Fetch table entry from VM rbFileNext = apropFile->af_FNxt;// Get pointer to next file vrComdat = apropFile->af_ComDat; if (vrComdat != VNIL) { fprintf(stdout, "COMDATs from file: '%s'\r\n\r\n", 1+GetPropName(apropFile)); while (vrComdat != VNIL) { apropComdat = (APROPCOMDATPTR ) FetchSym(vrComdat, FALSE); // Fetch table entry from VM vrComdat = apropComdat->ac_sameFile; DisplayOne(apropComdat, fPhysical); } } } } #endif #if TCE void AddComdatUses(APROPCOMDAT *pAC, APROPCOMDAT *pUses) { int i; SYMBOLUSELIST *pA; // ac_uses of this comdat ASSERT(pAC); ASSERT(pUses); ASSERT(pAC->ac_uses.pEntries); ASSERT(pUses->ac_usedby.pEntries); // update the ac_uses list pA = &pAC->ac_uses; for(i=0; icEntries; i++) // eliminate duplicate entries { if((APROPCOMDAT*)pA->pEntries[i] == pUses) return; } if(pA->cEntries >= pA->cMaxEntries-1) { #if TCE_DEBUG fprintf(stdout,"\r\nReallocating ac_uses list of '%s'old size %d -> %d ", 1 + GetPropName(pAC), pA->cMaxEntries, pA->cMaxEntries <<1); #endif pA->cMaxEntries <<= 1; if(!(pA->pEntries= REALLOC(pA->pEntries, pA->cMaxEntries*sizeof(RBTYPE*)))) Fatal(ER_memovf); } pA->pEntries[pA->cEntries++] = pUses; #if TCE_DEBUG fprintf(stdout, "\r\nComdat '%s'uses '%s' ", 1 + GetPropName(pAC), 1 + GetPropName(pUses)); #endif } void MarkAlive( APROPCOMDAT *pC ) { int i; SYMBOLUSELIST * pU; APROPCOMDAT * pCtmp; RBTYPE rhte; pU = &pC->ac_uses; #if TCE_DEBUG fprintf(stdout, "\r\nMarking alive '%s', attr %d ", 1+GetPropName(pC), pC->ac_attr); fprintf(stdout, " uses %d symbols ", pU->cEntries); for(i=0; icEntries; i++) fprintf(stdout, " '%s'",1+GetPropName(pU->pEntries[i])); fflush(stdout); #endif pC->ac_fAlive = TRUE; for(i=0; icEntries; i++) { pCtmp = (APROPCOMDATPTR)(pU->pEntries[i]); if(pCtmp->ac_attr != ATTRCOMDAT) { // find the COMDAT descriptor, or abort rhte = RhteFromProp((APROPPTR)pCtmp); ASSERT(rhte); pCtmp = PropRhteLookup(rhte, ATTRCOMDAT, FALSE); if(!pCtmp) { #if TCE_DEBUG fprintf(stdout, " comdat cell not found. "); #endif continue; } AddTceEntryPoint(pCtmp); #if TCE_DEBUG fprintf(stdout, "\r\nSwitching to COMDAT %s ", 1+GetPropName(pCtmp)); #endif } if(!pCtmp->ac_fAlive) { #if TCE_DEBUG fprintf(stdout, "\r\n Recursing with '%s' ", 1+GetPropName(pCtmp)); #endif MarkAlive(pCtmp); } #if TCE_DEBUG else fprintf(stdout,"\r\n already alive: '%s' ",1+GetPropName(pCtmp)); #endif } #if TCE_DEBUG fprintf(stdout, "\r\n Marking alive finished for '%s' ",1+GetPropName(pC)); #endif } void PerformTce( void ) { int i; for(i=0; i= aEntryPoints.cMaxEntries -1) { aEntryPoints.cMaxEntries <<= 1; aEntryPoints.pEntries = REALLOC(aEntryPoints.pEntries, aEntryPoints.cMaxEntries * sizeof(RBTYPE*)); #if TCE_DEBUG fprintf(stdout,"\r\nREALLOCATING aEntryPoints List, new size is %d ", aEntryPoints.cMaxEntries); #endif } aEntryPoints.pEntries[aEntryPoints.cEntries++] = pC; #if TCE_DEBUG fprintf(stdout, "\r\nNew TCE Entry point %d : %s ", aEntryPoints.cEntries, 1+GetPropName(pC)); #endif } #endif