/*********************************************************************** * Microsoft (R) 32-Bit Incremental Linker * * Copyright (C) Microsoft Corp 1992-1996. All rights reserved. * * File: i386.cpp * * File Comments: * * This module contains all i386 specific code. * ***********************************************************************/ #include "link.h" VOID ApplyI386Fixups( PCON pcon, PIMAGE_RELOCATION prel, DWORD creloc, BYTE *pbRawData, PIMAGE_SYMBOL rgsym, PIMAGE pimage, PSYMBOL_INFO rgsymInfo) /*++ Routine Description: Applys all I386 fixups to raw data. Arguments: pcon - contribution pbRawData - raw data to apply fixups to Return Value: None. --*/ { BOOL fVxD; BOOL fFixed; BOOL fDebugFixup; DWORD rvaSec; DWORD iReloc; fVxD = pimage->imaget == imagetVXD; fFixed = pimage->Switch.Link.fFixed; fDebugFixup = (PsecPCON(pcon) == psecDebug); BOOL fSaveDebugFixup = (pimage->Switch.Link.DebugType & FixupDebug) && !fDebugFixup; rvaSec = pcon->rva; for (iReloc = creloc; iReloc; iReloc--, prel++) { DWORD rvaCur; BYTE *pb; DWORD isym; SHORT isecTarget; DWORD rvaTarget; DWORD vaTarget; BOOL fAbsolute; rvaCur = rvaSec + prel->VirtualAddress - RvaSrcPCON(pcon); pb = pbRawData + prel->VirtualAddress - RvaSrcPCON(pcon); isym = prel->SymbolTableIndex; isecTarget = rgsym[isym].SectionNumber; rvaTarget = rgsym[isym].Value; if (fINCR && !fDebugFixup && rgsymInfo[isym].fJmpTbl && (rgsym[isym].StorageClass == IMAGE_SYM_CLASS_EXTERNAL || rgsym[isym].StorageClass == IMAGE_SYM_CLASS_WEAK_EXTERNAL || rgsym[isym].StorageClass == IMAGE_SYM_CLASS_FAR_EXTERNAL)) { if (*(DWORD UNALIGNED *) pb) { // Don't go thru the jump table for fixups to functions on non-zero offset MarkExtern_FuncFixup(&rgsym[isym], pimage, pcon); } else { // -1 since offset is to the addr rvaTarget = pconJmpTbl->rva + rgsymInfo[isym].Offset - 1; } } if (isecTarget == IMAGE_SYM_ABSOLUTE) { fAbsolute = TRUE; vaTarget = rvaTarget; } else { fAbsolute = FALSE; vaTarget = pimage->ImgOptHdr.ImageBase + rvaTarget; // UNDONE: Check for rvaTarget == 0. Possible fixup to discarded code? } if (fSaveDebugFixup && !fAbsolute) { SaveDebugFixup(prel->Type, 0, rvaCur, rvaTarget); } switch (prel->Type) { DWORD ibCur; DWORD ib; DWORD baseRelocVA; DWORD baseRelocValue; PSEC psec; case IMAGE_REL_I386_REL32: if (pimage->Switch.Link.fMap) { SaveFixupForMapFile(rvaCur); } if (fVxD) { // Calculate the source and destination addresses for VxDs ibCur = rvaCur - PsecPCON(pcon)->rva; psec = PsecFindIsec(isecTarget, &pimage->secs); assert(psec != NULL); assert(psec->isec); ib = rvaTarget - psec->rva; if (PsecPCON(pcon) != psec) { // Only store base reloc for inter-section references baseRelocVA = VXD_PACK_VA(PsecPCON(pcon), ibCur); baseRelocValue = VXD_PACK_VA(psec, ib); // Check to see if we are losing info when we squish offset into 24 bits struct _OFF { signed long off:24; } OFF; DWORD ibTemp = OFF.off = VXD_UNPACK_OFFSET(baseRelocValue); if (ibTemp != ib) { FatalPcon(pcon, VXDFIXUPOVERFLOW, SzNameFixupSym(pimage, rgsym + isym)); } StoreBaseRelocation(IMAGE_REL_BASED_VXD_RELATIVE, baseRelocVA, isecTarget, baseRelocValue, fFixed); break; } // Compute the RVA to add in to the fixup destination ... it // is the section-relative offset of the target minus the // section-relative offset of the next instruction. rvaTarget = ib - (ibCur + sizeof(DWORD)); } else { // The displacement is the RVA of the target // minus the RVA of the next instruction. rvaTarget -= rvaCur + sizeof(DWORD); if (!fAbsolute && pimage->Switch.Link.fNewRelocs) { if (isecTarget != PsecPCON(pcon)->isec) { StoreBaseRelocation(IMAGE_REL_BASED_REL32, rvaCur, isecTarget, 0, fFixed); } } } *(DWORD UNALIGNED *) pb += rvaTarget; break; case IMAGE_REL_I386_DIR32: if (fDebugFixup) { // When a DIR32 fixup is found in a debug section, it is // treated as a SECREL fixup followed by a SECTION fixup. if (fAbsolute) { // Max section # + 1 is the sstSegMap entry for absolute // symbols. *(WORD UNALIGNED *) (pb + sizeof(DWORD)) += (WORD) (pimage->ImgFileHdr.NumberOfSections + 1); } else { psec = PsecFindIsec(isecTarget, &pimage->secs); if (psec != NULL) { rvaTarget -= psec->rva; *(WORD UNALIGNED *) (pb + sizeof(DWORD)) += psec->isec; } else { // This occurs when a discarded comdat is the target of // a relocation in the .debug section. assert(rvaTarget == 0); } } *(DWORD UNALIGNED *) pb += rvaTarget; break; } if (fVxD && !fAbsolute) { ibCur = rvaCur - PsecPCON(pcon)->rva; psec = PsecFindIsec(isecTarget, &pimage->secs); assert(psec != NULL); assert(psec->isec); ib = rvaTarget - psec->rva; // VXD relocations are not additive. Add in // the displacement present in the object file. ib += *(DWORD UNALIGNED *) pb; baseRelocVA = VXD_PACK_VA(PsecPCON(pcon), ibCur); baseRelocValue = VXD_PACK_VA(psec, ib); // Check to see if we are losing info when we squish offset into 24 bits struct _OFF { signed long off:24; } OFF; DWORD ibTemp = OFF.off = VXD_UNPACK_OFFSET(baseRelocValue); if (ibTemp != ib) { FatalPcon(pcon, VXDFIXUPOVERFLOW, SzNameFixupSym(pimage, rgsym + isym)); } StoreBaseRelocation(IMAGE_REL_BASED_HIGHLOW, baseRelocVA, isecTarget, baseRelocValue, fFixed); break; } *(DWORD UNALIGNED *) pb += vaTarget; if (!fAbsolute) { StoreBaseRelocation(IMAGE_REL_BASED_HIGHLOW, rvaCur, isecTarget, 0, fFixed); } break; case IMAGE_REL_I386_DIR32NB: *(DWORD UNALIGNED *) pb += rvaTarget; break; case IMAGE_REL_I386_SECREL: if (!fAbsolute) { psec = PsecFindIsec(isecTarget, &pimage->secs); if (psec != NULL) { rvaTarget -= psec->rva; } else { // This occurs when a discarded comdat is the target of // a relocation in the .debug section. assert(rvaTarget == 0); } } *(DWORD UNALIGNED *) pb += rvaTarget; break; case IMAGE_REL_I386_SECTION: if (isecTarget > 0) { *(WORD UNALIGNED *) pb += (WORD) isecTarget; } else if (fAbsolute) { // Max section # + 1 is the sstSegMap entry for absolute // symbols. *(WORD UNALIGNED *) pb += (WORD) (pimage->ImgFileHdr.NumberOfSections + 1); } else { *(WORD UNALIGNED *) pb += 0; } break; case IMAGE_REL_I386_ABSOLUTE: // Ignore (fixup not required). break; case IMAGE_REL_I386_SEG12: WarningPcon(pcon, UNKNOWN_SEG12_FIXUP, prel->VirtualAddress); break; case IMAGE_REL_I386_DIR16: if (fVxD) { psec = PsecFindIsec(isecTarget, &pimage->secs); if (psec != NULL) { rvaTarget -= psec->rva; } else { // UNDONE: Is this possible for VxDs? // This occurs when a discarded comdat is the target of // a relocation in the .debug section. assert(rvaTarget == 0); } *(SHORT UNALIGNED *) pb += (SHORT) rvaTarget; break; } // Fall through for non-VxDs case IMAGE_REL_I386_REL16: if (fVxD) { // The source and target must be in the same section if (PsecPCON(pcon)->isec != isecTarget) { // UNDONE: This should be an error assert(FALSE); } rvaTarget -= rvaCur + sizeof(WORD); *(SHORT UNALIGNED *) pb += (SHORT) rvaTarget; break; } // Fall through for non-VxDs default: ErrorPcon(pcon, UNKNOWNFIXUP, prel->Type, SzNameFixupSym(pimage, rgsym + isym)); CountFixupError(pimage); break; } } } VOID I386LinkerInit(PIMAGE pimage, BOOL *pfIlinkSupported) { // If section alignment switch not used, set the default. if (!FUsedOpt(pimage->SwitchInfo, OP_ALIGN) && pimage->imaget != imagetVXD) { pimage->ImgOptHdr.SectionAlignment = _4K; } if (FUsedOpt(pimage->SwitchInfo, OP_GPSIZE)) { Warning(NULL, SWITCH_INCOMPATIBLE_WITH_MACHINE, "GPSIZE", "IX86"); pimage->Switch.Link.GpSize = 0; } *pfIlinkSupported = TRUE; ApplyFixups = ApplyI386Fixups; // If the section alignment is < _4K then make the file alignment the // same as the section alignment. This ensures that the image will // be the same in memory as in the image file, since the alignment is less // than the maximum alignment of memory-mapped files. if (pimage->ImgOptHdr.SectionAlignment < _4K) { fImageMappedAsFile = TRUE; pimage->ImgOptHdr.FileAlignment = pimage->ImgOptHdr.SectionAlignment; } } const char *SzI386RelocationType(WORD wType, WORD *pcb, BOOL *pfSymValid) { const char *szName; WORD cb; switch (wType) { case IMAGE_REL_I386_ABSOLUTE: szName = "ABS"; cb = 0; break; case IMAGE_REL_I386_DIR16: szName = "DIR16"; cb = sizeof(WORD); break; case IMAGE_REL_I386_REL16: szName = "REL16"; cb = sizeof(WORD); break; case IMAGE_REL_I386_DIR32: szName = "DIR32"; cb = sizeof(DWORD); break; case IMAGE_REL_I386_DIR32NB: szName = "DIR32NB"; cb = sizeof(DWORD); break; case IMAGE_REL_I386_SEG12: szName = "SEG12"; cb = sizeof(WORD); break; case IMAGE_REL_I386_REL32: szName = "REL32"; cb = sizeof(DWORD); break; case IMAGE_REL_I386_SECTION: szName = "SECTION"; cb = sizeof(WORD); break; case IMAGE_REL_I386_SECREL: szName = "SECREL"; cb = sizeof(DWORD); break; default: szName = NULL; cb = 0; break; } *pcb = cb; *pfSymValid = (cb != 0); return(szName); }