|
|
/***********************************************************************
* Microsoft (R) 32-Bit Incremental Linker * * Copyright (C) Microsoft Corp 1992-1996. All rights reserved. * * File: incr.cpp * * File Comments: * * ilink routines that didn't find a place elsewhere. * ***********************************************************************/
#include "link.h"
// statics
static PLIB plibModObjs; // linker defined lib for the modified cmdline objs
static LPEXT lpextWeak = {CPEXT_WEAK, 0, 0, 0}; // list that has the weak/lazy externs
static PLPEXT plpextWeak = &lpextWeak; // list that has the weak/lazy externs
static LPEXT lpextMultDef = {CPEXT_MULT, 0, 0, 0}; // list that will hold potential
static PLPEXT plpextMultDef = &lpextMultDef; // multiply defined syms.
static PLEXT plextMovedData; // list of data externs that have moved
static WORD cmods; // count of MODs in project
static DWORD cReloc; // count of relocs
static PGRP pgrpIdata$4; static PGRP pgrpIdata$5; static PGRP pgrpIdata$6;
// globals
struct ILINK_INFO { const void *pvJumpEntry; size_t cbJumpEntry; WORD address_offset; BYTE bPad; };
static const ILINK_INFO *ilink_info;
static const BYTE I386JmpTblEntry[] = { // jmp rel32
0xE9, 0x00, 0x00, 0x00, 0x00 };
static const ILINK_INFO I386_ilink = { I386JmpTblEntry, sizeof(I386JmpTblEntry), 1, X86_INT3 };
static const BYTE MPPCJmpTblEntry[] = { // for MacPPC
0x00, 0x00, 0x00, 0x00 };
static const ILINK_INFO MPPC_ilink = { MPPCJmpTblEntry, sizeof(MPPCJmpTblEntry), 0, 0 // 0xFF
};
static const DWORD MIPSJmpTblEntry[] = { 0x03e04025, /* or t0,ra,zero */ 0x04110001, /* bgezal zero,0xc */ 0x00000000, /* nop */ 0x8fe90014, /* lw t1,20(ra) */ 0x013f4821, /* addu t1,t1,ra */ 0x25290018, /* addiu t1,t1,24 */ 0x01200008, /* jr t1 */ 0x0100f825, /* or ra,t0,zero */ 0xdeadbeef, /* ld t5,-16657(s5) */ };
static const ILINK_INFO MIPS_ilink = { MIPSJmpTblEntry, sizeof(MIPSJmpTblEntry), sizeof(MIPSJmpTblEntry)-4, 0xff }; static const DWORD ALPHAJmpTblEntry[] = { 0xc0000000, // br $0, zero Pick up PC value
0x20600014, // lda 20($3), zero
0xa0a3fffc, // ldl $5, -4($3)
0x40a30003, // addl $5, $3, $3
0x68030000, // jmp zero, $3
0x00000000 // halt (maintain 16 byte align and puke if execute)
};
static const ILINK_INFO ALPHA_ilink = { ALPHAJmpTblEntry, sizeof(ALPHAJmpTblEntry), sizeof(ALPHAJmpTblEntry)-4, 0xff };
size_t cbJumpEntry; PLMOD plmodNewModsFromLibSrch; // list of mods added as a result of lib search.
// reloc counting function
void CountBaseRelocsPMOD(PMOD, DWORD *);
// weak extern prototypes
void AssignWeakDefinition(PEXTERNAL, PEXTERNAL, PST);
// calc ptrs prototypes
void FindSlotForPCON(PCON);
// export function prototypes
BOOL FExpFileChanged(PEXPINFO);
// library inclusion
BOOL CheckForUnrefLibMods(PIMAGE); void CheckForMultDefns(PIMAGE, PLPEXT);
#ifdef ILINKLOG
DWORD dwBegin;
void IlinkLog ( UINT FullLinkErr ) { DWORD cb; char McName[32]; char rgchOut[1024]; char szFname[_MAX_FNAME + _MAX_EXT]; char szExt[_MAX_EXT]; char rgchLogFilename[] = "\\\\fabrice2\\public\\linker\\ilink.log"; HANDLE hFile; const int cRetry = 1; int i;
// user requested no logging
if (!fIlinkLog) { return; }
if (!fINCR) { // non-incremental link
if (FullLinkErr == -1) { strcpy(rgchOut, "RLINK "); } else { strcpy(rgchOut, "RFAIL "); } } else if (fIncrDbFile) { // Incremental link
if (errInc == errNone) { strcpy(rgchOut, "ILINK "); } else { strcpy(rgchOut, "IFAIL "); } } else { // Full link
if (FullLinkErr == -1) { strcpy(rgchOut, "FLINK "); } else { strcpy(rgchOut, "FFAIL "); } }
// Name of machine
cb = 32; if (!GetComputerName(McName, &cb)) { strcpy(McName, "UNKNOWN"); }
sprintf(szFname, "%-10s", McName); // for better formatting
strcat(rgchOut, szFname); strcat(rgchOut, " ");
// put in linker version
strcat(rgchOut, " "); strcat(rgchOut, VERSION_STR); strcat(rgchOut, " ");
// time for the link
sprintf(McName, "%08ld ", (DWORD)(GetTickCount() - dwBegin)); strcat(rgchOut, McName);
// name of target
if (OutFilename) { _splitpath(OutFilename, NULL, NULL, szFname, szExt); strcat(rgchOut, "\""); strcat(rgchOut, szFname); strcat(rgchOut, szExt); strcat(rgchOut, "\" "); } else { strcat(rgchOut, " NOOUTPUTFILE "); }
// report target platform
char *szPlatform; switch (wMachine) { case IMAGE_FILE_MACHINE_I386 : szPlatform = " IX86 "; break;
case IMAGE_FILE_MACHINE_R3000 :
case IMAGE_FILE_MACHINE_R4000 : case IMAGE_FILE_MACHINE_R10000 : szPlatform = " MIPS "; break;
case IMAGE_FILE_MACHINE_ALPHA : szPlatform = " ALFA "; break;
case IMAGE_FILE_MACHINE_POWERPC : szPlatform = " PWPC "; break;
case IMAGE_FILE_MACHINE_M68K : szPlatform = " M68K "; break;
case IMAGE_FILE_MACHINE_MPPC_601 : szPlatform = " MPPC "; break;
default : szPlatform = " UNKN "; break; } strcat(rgchOut, szPlatform);
// put out the host platform
if (IsOSWin95()) { strcat(rgchOut, " WIN95 "); } else { strcat(rgchOut, " WINNT "); }
// put out the date
time_t ltime; _tzset(); time((time_t *)<ime);
char *szTime = ctime(<ime); if (szTime != NULL) { szTime[strlen(szTime) - 1] = '\0'; strcat(rgchOut, szTime); } else { strcat(rgchOut, "notime"); } strcat(rgchOut, " ");
if (fIncrDbFile) { // Incremental link
switch (errInc) { case errNone: break;
case errOutOfDiskSpace: strcat(rgchOut, "OUT_OF_DISKSPACE"); break;
case errOutOfMemory: strcat(rgchOut, "OUT_OF_MEMORY"); break;
case errFpo: strcat(rgchOut, "FPO_PAD_OVERFLOW"); break;
case errTypes: strcat(rgchOut, "ERROR_TYPES"); break;
case errDataMoved: strcat(rgchOut, "DATA_MOVED"); break;
case errCalcPtrs: strcat(rgchOut, "PAD_OVERFLOW"); break;
case errUndefinedSyms: strcat(rgchOut, "UNDEFINED_SYMS"); break;
case errWeakExtern: strcat(rgchOut, "WEAK_EXTERN"); break;
case errCommonSym: strcat(rgchOut, "NEW_BSS_SYM"); break;
case errAbsolute: strcat(rgchOut, "ABSOLUTE_SYM"); break;
case errJmpTblOverflow: strcat(rgchOut, "JMP_TBL_OVERFLOW"); break;
case errDirectives: strcat(rgchOut, "DIRECTIVS_CHNG"); break;
case errBaseReloc: strcat(rgchOut, "BASERELOC_PAD_OVERFLOW"); break;
case errFileAdded: strcat(rgchOut, "NEW_FILE_ADDED"); break;
case errFileDeleted: strcat(rgchOut, "FILE_DELETED_OR_RENAMED"); break;
case errLibChanged: strcat(rgchOut, "LIB_CHANGED"); break;
case errTooManyChanges: strcat(rgchOut, "TOO_MANY_CHANGES"); break;
case errExports: strcat(rgchOut, "EXPORTS_CHANGED"); break;
case errLibRefSetChanged: strcat(rgchOut, "LIB_REFSET_CHANGED"); break;
case errMultDefFound: strcat(rgchOut, "MULTIPLE_DEFN_FOUND"); break;
case errComdat: strcat(rgchOut, "COMDAT_SEL_FAILED"); break;
case errNoChanges: strcat(rgchOut, "NO_CHANGES"); break;
default: strcat(rgchOut, "UNKNOWN"); } } else { // Full link
if (FullLinkErr != -1) { sprintf(McName, "error %04u", FullLinkErr); strcat(rgchOut, McName); } }
strcat(rgchOut, "\r\n");
for (i = 0; i < cRetry; i++) { hFile = CreateFile(rgchLogFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) { break; }
// Sleep(100); // sleep for 0.1s and retry
}
// tried our best to log an entry.
if (i == cRetry) { return; }
assert(hFile != INVALID_HANDLE_VALUE);
// Seek to end of file
SetFilePointer(hFile, 0, NULL, FILE_END);
// Write
WriteFile(hFile, rgchOut, strlen(rgchOut), &cb, NULL);
// Close the file.
CloseHandle(hFile); }
#endif // ILINKLOG
BOOL FArgOnList ( PNAME_LIST pnl, PARGUMENT_LIST parg )
/*++
Routine Description:
Searches for the arg on the list. Marks the list entries as processed.
Arguments:
pnl - list to search
parg - arg to look for.
Return Value:
TRUE if found else FALSE
--*/
{ WORD i; PARGUMENT_LIST pal;
assert(parg); // walk the list
for (i = 0, pal = pnl->First; i < pnl->Count; i++, pal = pal->Next) {
// skip already processed entries
if (pal->Flags & ARG_Processed) { continue; }
if (!strcmp(pal->OriginalName, parg->OriginalName)) { pal->Flags |= ARG_Processed; return 1; } }
// not found
return 0; }
void AddArgToListOnHeap ( PNAME_LIST pnl, PARGUMENT_LIST parg )
/*++
Routine Description:
Adds the arg to the name list on private heap.
Arguments:
pnl - ptr to name list on private heap
parg - arg to add
Return Value:
None.
--*/
{ PARGUMENT_LIST pal;
assert(parg); // alloc space
pal = (PARGUMENT_LIST) Malloc(sizeof(ARGUMENT_LIST));
// fill in fields
pal->OriginalName = Strdup(parg->OriginalName); if (parg->ModifiedName) { pal->ModifiedName = Strdup(parg->ModifiedName); } else { pal->ModifiedName = NULL; } pal->Next = NULL;
// attach it to list
if (!pnl->First) { pnl->First = pal; } else { pnl->Last->Next = pal; }
// update count
pnl->Count++;
// update last member
pnl->Last = pal;
// done
return; }
void AddExtToList( PLPEXT plpext, BOOL fTempList, PEXTERNAL pext )
/*++
Routine Description:
Adds the extern to the specified list.
Arguments:
plpext - pointer to list of externs
fTempList - list is a temporary list
pext - external sym to add.
Return Value:
None.
--*/
{ EXTERNAL **rgpext;
if ((plpext->pextChunkCur == NULL) || (plpext->cpextCur >= plpext->cpextMax)) { // allocate a chunk
size_t cb = sizeof(EXTCHUNK) + plpext->cpextMax * sizeof(PEXTERNAL); EXTCHUNK *pextChunk = fTempList ? (EXTCHUNK *) PvAlloc(cb): (EXTCHUNK *) Malloc(cb);
// update state
pextChunk->pextChunkNext = plpext->pextChunkCur; plpext->pextChunkCur = pextChunk; plpext->cpextCur = 0; }
rgpext = RgPext(plpext->pextChunkCur); rgpext[plpext->cpextCur++] = pext; plpext->cpextTotal++; }
void DelExtFromList( PLPEXT plpext, PEXTERNAL pext )
/*++
Routine Description:
Removes the extern from the specified list.
Arguments:
plpext - pointer to list of externs
pext - external sym to add.
Return Value:
None.
--*/
{ EXTCHUNK *pextChunk; WORD cpextMax;
for (pextChunk = plpext->pextChunkCur, cpextMax = plpext->cpextCur; pextChunk != NULL; pextChunk = pextChunk->pextChunkNext, cpextMax = plpext->cpextMax) { EXTERNAL **rgpext; WORD ipext;
rgpext = RgPext(pextChunk);
for (ipext = 0; ipext < cpextMax; ipext++) { if (rgpext[ipext] == pext) { rgpext[ipext] = NULL; plpext->cpextTotal--; return; } } } }
BOOL IsExtListEmpty ( PLPEXT plpext )
/*++
Routine Description:
Checks to see if the specified list is empty.
Arguments:
plpext - pointer to list of externs
Return Value:
TRUE if list is empty.
--*/
{ return(!plpext->cpextTotal); }
//
// Enumerator of all externs in list
//
INIT_ENM(ExtList, EXT_LIST, (ENM_EXT_LIST *penm, PLPEXT plpext)) { if (plpext) { penm->lpext = (*plpext); } else { penm->lpext.pextChunkCur = NULL; }
penm->ipext = 0; } NEXT_ENM(ExtList, EXT_LIST) { if (penm->lpext.pextChunkCur == NULL) { return(FALSE); }
{ EXTERNAL **rgpext = RgPext(penm->lpext.pextChunkCur);
penm->pext = rgpext[penm->ipext++]; }
if (penm->ipext == penm->lpext.cpextCur) { penm->lpext.pextChunkCur = penm->lpext.pextChunkCur->pextChunkNext; penm->lpext.cpextCur = penm->lpext.cpextMax; penm->ipext = 0; }
return(TRUE); } END_ENM(ExtList, EXT_LIST) { } DONE_ENM
void AddExtToModRefList ( PMOD pmod, PEXTERNAL pext )
/*++
Routine Description:
Adds the extern to the specified MODs reference list.
Arguments:
pmod - pointer to MOD
pext - external sym to add.
Return Value:
None.
--*/
{ if (!(pext->Flags & EXTERN_NO_REFS)) { AddExtToList(pmod->plpextRef, FALSE, pext); } }
void RemoveAllRefsToPext ( PEXTERNAL pext )
/*++
Routine Description:
Removes all references made to this extern. This is done to enable checking for correct library module inclusion
Arguments:
pext - external sym whose references need to be removed
Return Value:
None.
--*/
{ ENM_MOD_EXT enmModExt;
if (!pext->pmodOnly) { return; }
// walk the list of referencing MODs kept at each sym
InitEnmModExt(&enmModExt, pext); while (FNextEnmModExt(&enmModExt)) {
if (!enmModExt.pmod) { continue; // for ilink we remove references
}
DelExtFromList(enmModExt.pmod->plpextRef, pext); } }
BOOL RemoveExtFromDefList ( PMOD pmod, PEXTERNAL pext )
/*++
Routine Description:
Removes extern from defined list of MOD if present.
Arguments:
pmod - ptr to MOD.
pext - external sym which needs to be removed from DEF list
Return Value:
TRUE if we did find pext in the list & removed it
--*/
{ PEXTERNAL *ppextPrev, pextCur;
pextCur = pmod->pextFirstDefined; ppextPrev = &pmod->pextFirstDefined;
while (pextCur) { if (pextCur == pext) { (*ppextPrev) = pextCur->pextNextDefined; // remove pext from chain
pextCur->pextNextDefined = NULL; // set next field to NULL for pext just removed
return(TRUE); }
ppextPrev = &pextCur->pextNextDefined; pextCur = pextCur->pextNextDefined; }
return(FALSE); }
void RemovePrevDefn ( PEXTERNAL pext )
/*++
Routine Description:
All externs defined by a MOD are chained together. Need to remove it from that chain.
For COMMON syms this is difficult since pext->pcon->pmodBack is a "linker defined module". .
Arguments:
pext - external sym whose references need to be removed
Return Value:
None.
--*/
{ ENM_MOD_EXT enmModExt;
assert(pext->Flags & EXTERN_COMMON); if (fIncrDbFile) { assert(PmodPCON(pext->pcon) == pmodLinkerDefined); } assert(pext->pmodsFirst);
// walk the list of referencing MODs kept at each sym
// one of these MODs has defined the COMMON (eeewww!!!)
InitEnmModExt(&enmModExt, pext); while (FNextEnmModExt(&enmModExt)) {
if (!enmModExt.pmod) { continue; // for ilink we remove references
}
if (RemoveExtFromDefList(enmModExt.pmod, pext)) { return; // found the definition
} } }
void ProcessUndefinedExternals( PIMAGE pimage )
/*++
Routine Description:
Makes a pass over the undefined externals to see if they are really undefined.
REVIEW: Need to make a pass over this again w/ regard to weak externs since we are doing lib searches now.
Arguments:
pimage - pointer to image
Return Value:
None.
--*/
{ PEXTERNAL pext; ENM_UNDEF_EXT enmUndefExt; PST pst = pimage->pst;
// First pass thru symbols checks for cases which may cause a full
// link. This way we avoid giving any warnings & later decide to do
// a full link after all.
InitEnmUndefExt(&enmUndefExt, pst); while (FNextEnmUndefExt(&enmUndefExt)) {
pext = enmUndefExt.pext;
// symbol to be ignored
if ((pext->Flags & EXTERN_DEFINED) || (pext->Flags & EXTERN_IGNORE) ) continue;
// symbol no longer referenced by anybody
if (!FPextRef(pext)) { SetDefinedExt(pext, TRUE, pst); // take it off undef list
pext->Flags = EXTERN_IGNORE; pext->pcon = NULL; // clean
pext->ImageSymbol = NullSymbol; // clean
continue; }
// if symbol undefined & marked relink then relink (for bss with multiple defn)
if (pext->Flags & EXTERN_RELINK) { errInc = errCommonSym; return; }
// a COMDAT with a reference, dirty files didn't have defn but atleast one of the
// non-dirty files has a reference & may (or may not) have a defn
if (pext->Flags & EXTERN_COMDAT) { errInc = errComdat; return; }
// undefined sym
#ifdef INSTRUMENT
{ char *szOutSymName;
szOutSymName = SzOutputSymbolName(SzNamePext(pext, pst), TRUE); LogNoteEvent(Log, SZILINK, NULL, letypeEvent, "undefined sym: %s", szOutSymName); if (szOutSymName != SzNamePext(pext, pst)) { free(szOutSymName); } } #endif // INSTRUMENT
errInc = errUndefinedSyms; } // end while
}
BOOL IsExpObj ( const char *szName )
/*++
Routine Description:
Is the name specified an export object.
Arguments:
szName - name of file.
Return Value:
TRUE if export object else FALSE
--*/
{ char szDrive[_MAX_DRIVE]; char szDir[_MAX_DIR]; char szFname[_MAX_FNAME]; char szExt[_MAX_EXT]; char szExpFilename[_MAX_FNAME]; char szImplibFilename[_MAX_FNAME]; const char *szImplibT;
// generate possible import lib name (could be user specified)
if ((szImplibT = ImplibFilename) == NULL) { _splitpath(OutFilename, szDrive, szDir, szFname, szExt); _makepath(szImplibFilename, szDrive, szDir, szFname, ".lib");
szImplibT = szImplibFilename; }
// Generate possible export filename
_splitpath(szImplibT, szDrive, szDir, szFname, NULL); _makepath(szExpFilename, szDrive, szDir, szFname, ".exp");
// check to see if the names match
if (!_tcsicmp(szName, szExpFilename)) { return 1; }
return 0; }
PARGUMENT_LIST PargFindSz ( const char *szName, PNAME_LIST ptrList )
/*++
Routine Description:
Find the module in the given list
Arguments:
szName - name of file.
ptrList - list to search
Return Value:
pointer to argument or NULL
--*/
{ DWORD i; PARGUMENT_LIST parg;
for (i = 0, parg = ptrList->First; i < ptrList->Count; i++, parg=parg->Next) { // original name to handle resource files etc.
if (!_tcsicmp(szName, parg->OriginalName)) { return parg; } }
return NULL; }
BOOL IsDirtyPMOD ( PMOD pmod )
/*++
Routine Description:
Checks to see if this MOD if dirty.
Arguments:
pmod - ptr to a MOD.
Return Value:
TRUE if it is dirty.
--*/
{ // is it referenced by a modified file?
PARGUMENT_LIST parg = PargFindSz(SzOrigFilePMOD(pmod), &ModFileList);
return(parg != NULL); }
void AddToModList ( PARGUMENT_LIST parg, WORD Flags )
/*++
Routine Description:
Adds entry to modified list.
Arguments:
parg - pointer to entry to be added.
Flags - flags of entry
Return Value:
None.
--*/
{ PARGUMENT_LIST ptrList;
ptrList = (PARGUMENT_LIST) PvAlloc(sizeof(ARGUMENT_LIST));
// fill in fields
ptrList->OriginalName = parg->OriginalName; ptrList->ModifiedName = parg->ModifiedName; ptrList->TimeStamp = parg->TimeStamp; ptrList->Flags = Flags; ptrList->Next = NULL;
// If first member to be added.
if (!ModFileList.Last) { ModFileList.Last = ptrList; } else { // Not first member, so add to the front.
ptrList->Next = ModFileList.First; }
// Increment number of members in list.
++ModFileList.Count;
// Remember first member in list.
ModFileList.First = ptrList; }
void ProcessFileArg ( PARGUMENT_LIST parg, WORD Flags, DWORD TimeStamp, DWORD HdrTimeStamp, DWORD cbFile, char *szOrigName, BOOL fExpFileGen, BOOL *pfLib, BOOL *pfDel )
/*++
Routine Description:
Adds entry to modified list.
Arguments:
parg - pointer to entry to be added if not NULL.
Flags - obj or lib
Timestamp - filssystem timestamp of obj or lib
HdrTimeStamp - timestamp in the hdr of obj file (ignored for obj in lib)
cbFile - size of object file (ignored for obj in lib)
szOrigName - original name of file (used for deleted files)
Return Value:
None.
--*/
{ ARGUMENT_LIST arg;
// found a matching name
if (parg) { parg->Flags |= ARG_Processed; // modified file
if (parg->TimeStamp != TimeStamp) { if (Flags & ARG_Library) { // library modified
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "lib modified: %s", parg->OriginalName); #endif // INSTRUMENT
*pfLib = 1; } else { // object file modified
IMAGE_FILE_HEADER imFileHdr;
FileReadHandle = FileOpen(parg->ModifiedName, O_RDONLY | O_BINARY, 0); ReadFileHeader(FileReadHandle, &imFileHdr);
assert(HdrTimeStamp); assert(cbFile);
// file not really changed - touched for MR
if (imFileHdr.TimeDateStamp == HdrTimeStamp && FileLength(FileReadHandle) == (LONG) cbFile) {
FileClose(FileReadHandle, TRUE); return; } FileClose(FileReadHandle, FALSE); } Flags |= ARG_Modified; AddToModList(parg, Flags); DBEXEC(DB_LISTMODFILES, DBPRINT("Modified File= %s\n", parg->OriginalName)); DBEXEC(DB_LISTMODFILES, DBPRINT("\tOld TimeStamp= %s", ctime((time_t *)&TimeStamp))); DBEXEC(DB_LISTMODFILES, DBPRINT("\tNew TimeStamp= %s", ctime((time_t *)&parg->TimeStamp))); // unmodified file
} else { DBEXEC(DB_LISTMODFILES, DBPRINT("Unchanged File= %s\n",parg->OriginalName)); } // did not find matching name
} else { // check to see if this is an export object
if (fExpFileGen && IsExpObj(szOrigName)) { return; }
*pfDel = 1; arg.OriginalName = arg.ModifiedName = szOrigName; Flags |= ARG_Deleted; AddToModList(&arg, Flags);
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "file deleted: %s", szOrigName); #endif // INSTRUMENT
DBEXEC(DB_LISTMODFILES, DBPRINT("Deleted File= %s\n",szOrigName)); } }
void InitModFileList ( PIMAGE pimage, BOOL *pfLib, BOOL *pfNew, BOOL *pfDel )
/*++
Routine Description:
Builds a list of files whose timestamp is newer than the previous link.
Arguments:
pimage - image structure
pfLib - set to TRUE if LIB was modified
pfNew - set to TRUE if a NEW file was added
pfDel - Set to TRUE if an existing MOD was deleted
Return Value:
None.
--*/
{ PARGUMENT_LIST parg; ARGUMENT_LIST arg; DWORD i; PLIB plib; PMOD pmod; ENM_MOD enm_mod; ENM_LIB enm_lib;
*pfLib = 0; *pfNew = 0; *pfDel = 0;
// check out mods
InitEnmMod(&enm_mod, pimage->plibCmdLineObjs); while (FNextEnmMod(&enm_mod)) { pmod = enm_mod.pmod; cmods++; assert(pmod); parg = PargFindSz(SzOrigFilePMOD(pmod), &FilenameArguments); ProcessFileArg(parg, ARG_Object, pmod->TimeStamp, pmod->HdrTimeStamp, pmod->cbFile, SzOrigFilePMOD(pmod), pimage->ExpInfo.pmodGen != NULL, pfLib, pfDel); } EndEnmMod(&enm_mod);
// check out libs
InitEnmLib(&enm_lib, pimage->libs.plibHead); while (FNextEnmLib(&enm_lib)) { plib = enm_lib.plib; assert(plib); if (plib->flags & LIB_DontSearch) { continue; } if (plib->flags & LIB_Default) { struct _stat statfile; char szFname[_MAX_FNAME]; char szExt[_MAX_EXT]; char szFilename[_MAX_FNAME + _MAX_EXT];
_splitpath(plib->szName, NULL, NULL, szFname, szExt); strcpy(szFilename, szFname); strcat(szFilename, szExt);
char *sz = SzSearchEnv("LIB", szFilename, LIB_EXT);
// lib names don't match
if (_tcsicmp(plib->szName, sz)) { parg = NULL; } else { arg.OriginalName = arg.ModifiedName = sz; if (_stat(arg.OriginalName, &statfile) == -1) { Fatal(NULL, CANTOPENFILE, arg.OriginalName); } arg.TimeStamp = statfile.st_mtime; parg = &arg; } } else { parg = PargFindSz(plib->szName, &FilenameArguments); } ProcessFileArg(parg, ARG_Library, plib->TimeStamp, 0, 0, plib->szName, FALSE, pfLib, pfDel); } EndEnmLib(&enm_lib);
// check for new files
for (i = 0, parg = FilenameArguments.First; i < FilenameArguments.Count; i++, parg = parg->Next) {
RESN *pTempResn;
// already processed
if (parg->Flags & ARG_Processed) { continue; }
// check for repeated args
if (PmodFind(pimage->plibCmdLineObjs, parg->OriginalName, 0) || PlibFind(parg->OriginalName, pimage->libs.plibHead, FALSE)) { continue; }
parg->Flags |= ARG_Processed;
// check to see if it happens to be a PowerMac resource
if ((pTempResn = GetMacResourcePointer(parg->OriginalName, pimage)) != NULL) { if (pTempResn->TimeStamp == parg->TimeStamp) { continue; } else { PostNote(NULL, RESFILECHANGE, parg->OriginalName); } }
AddToModList(parg, ARG_NewFile); *pfNew = 1;
DBEXEC(DB_LISTMODFILES, DBPRINT("New File= %s\n",parg->OriginalName)); #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "new file: %s", parg->OriginalName); #endif // INSTRUMENT
}
}
void DoPass2PMOD ( IN PMOD pmod, IN BOOL fDoDebug )
/*++
Routine Description:
Mark the MOD for doing pass2 and for doing debug info if required.
CAVEAT: A MOD cannot change state from MOD_DoDebug to !MOD_DoDebug or vice-versa during the same link.
Arguments:
pmod - ptr to MOD
fDoDebug - TRUE if debug info needs to be done during Pass2
Return Value:
None.
--*/
{ PLIB plib; ENM_MOD enm_mod;
// already marked for doing pass2
if (FDoPass2PMOD(pmod)) { return; }
// mark for doing pass2
pmod->LnkFlags |= MOD_DoPass2;
if (fDoDebug) { pmod->LnkFlags |= MOD_DoDebug; }
// done if command line obj
if (!FIsLibPMOD(pmod)) { return; }
// mark MODs with the same name for Pass2 (DBI hack for import libs)
plib = pmod->plibBack;
// REVIEW: base reloc count is not accurate
InitEnmMod(&enm_mod, plib); while (FNextEnmMod(&enm_mod)) { if (!_tcsicmp(SzOrigFilePMOD(enm_mod.pmod),SzOrigFilePMOD(pmod))) { enm_mod.pmod->LnkFlags |= MOD_DoPass2;
CountBaseRelocsPMOD(enm_mod.pmod, &cReloc);
if (fDoDebug) { enm_mod.pmod->LnkFlags |= MOD_DoDebug; } } } EndEnmMod(&enm_mod); }
void DetermineTimeStamps ( VOID )
/*++
Routine Description:
Determine timestamps of all files.
Arguments:
None.
Return Value:
None.
--*/
{ PARGUMENT_LIST argument; DWORD i; struct _stat statfile;
for (i = 0, argument = FilenameArguments.First; i < FilenameArguments.Count; argument = argument->Next, i++) {
// determine current timestamp of file
if (_stat(argument->OriginalName, &statfile) == -1) { Fatal(NULL, CANTOPENFILE, argument->OriginalName); } argument->TimeStamp = statfile.st_mtime; } }
// assign the weak definition
void AssignWeakDefinition ( PEXTERNAL pext, PEXTERNAL pextWeakDefault, PST pst ) { // define the weak/lazy external to its "default"
assert(pext); assert(pextWeakDefault); if ((pextWeakDefault->Flags & EXTERN_DEFINED)) { PMOD pmod;
pext->ImageSymbol.Value = pextWeakDefault->ImageSymbol.Value; // + PsecPCON(pext->pextWeakDefault->pcon)->rva;
pext->ImageSymbol.SectionNumber = pextWeakDefault->ImageSymbol.SectionNumber; pext->ImageSymbol.Type = pextWeakDefault->ImageSymbol.Type; SetDefinedExt(pext, TRUE, pst); pext->Flags |= (EXTERN_DEFINED|EXTERN_DIRTY); pext->pcon = pextWeakDefault->pcon; pext->FinalValue = pextWeakDefault->FinalValue;
// chain up extern to mod defining it
pmod = PmodPCON(pext->pcon);
assert(pmod); if (pmod->pextFirstDefined) { pext->pextNextDefined = pmod->pextFirstDefined; } pmod->pextFirstDefined = pext; } }
// All existing weak & lazy externs can be resolved. The 'lazy' because
// ilink assumes libs haven't changed and so there is no need to search
// the libs.
void ResolveExistingWeakAndLazyExterns ( PIMAGE pimage ) { ENM_EXT_LIST enmExtList; PST pst = pimage->pst;
// walk the list of existing weak/lazy externs
InitEnmExtList(&enmExtList, plpextWeak); while (FNextEnmExtList(&enmExtList)) {
PEXTERNAL pext;
pext = enmExtList.pext;
// no longer referenced or marked as ignore
if (!FPextRef(pext) || (pext->Flags & EXTERN_IGNORE) ) continue;
// it is still weak/lazy
// if it is defined nothing to do (already been asigned to weak defn)
// else assign to weak defn
if (pext->Flags & (EXTERN_WEAK|EXTERN_LAZY)) { PEXTERNAL pextWeakDefault;
if (pext->Flags & EXTERN_DEFINED) { continue; }
pextWeakDefault = PextWeakDefaultFind(pext);
AssignWeakDefinition(pext, pextWeakDefault, pst); continue; }
// weak/lazy has changed from weak definition to strong - punt
#ifdef INSTRUMENT
{ char *szOutSymName; szOutSymName = SzOutputSymbolName(SzNamePext(pext, pst), TRUE); LogNoteEvent(Log, SZILINK, NULL, letypeEvent, "weak/lazy extern (w 2 s): %s", szOutSymName); if (szOutSymName != SzNamePext(pext, pst)) { free(szOutSymName); } } #endif // INSTRUMENT
errInc = errWeakExtern; return; } // end while
EndEnmExtList(&enmExtList); }
// assign weak externs to their default definitions
// new weak/lazy are permitted on an ilink
// change in state from weak to strong or vice-versa isn't allowed
void ResolveWeakExterns ( PIMAGE pimage, DWORD Type ) { WEAK_EXTERN_LIST *pwel; PST pst = pimage->pst;
// now walk the global list for any new weak/lazy externs
pwel = pwelHead; for (; pwel; pwel = pwel->pwelNext) {
// look at ones of interest
if (!(pwel->pext->Flags & Type)) { continue; }
// skip ones already done in the prior pass
if (pwel->pext->Flags & EXTERN_DEFINED) { continue; }
// rest are ones which haven't been handled yet; new are ok
if (pwel->pext->Flags & EXTERN_NEWFUNC || pwel->pext->Flags & EXTERN_NEWDATA) { AssignWeakDefinition(pwel->pext, pwel->pextWeakDefault, pst); continue; }
// weak and not new (=> a strong defn became a weak one)
errInc = errWeakExtern; return; } }
void RestoreWeakSymVals ( PIMAGE /* pimage */ )
/*++
Routine Description:
Restore weak sym values of externs that were modified just before emit to map file.
Arguments:
pimage - ptr to image.
Return Value:
None.
--*/
{ WEAK_EXTERN_LIST *pwel;
for (pwel = pwelHead; pwel != NULL; pwel = pwel->pwelNext) { if (pwel->pext->Flags & (EXTERN_WEAK | EXTERN_LAZY | EXTERN_ALIAS)) { assert(pwel->pextWeakDefault); pwel->pext->ImageSymbol.Value -= PsecPCON(pwel->pextWeakDefault->pcon)->rva; } } }
DWORD CThunks ( PIMAGE pimage )
/*++
Routine Description:
Returns count of thunks required.
Arguments:
pimage - ptr to image.
Return Value:
None.
--*/
{ PEXTERNAL pext; DWORD cext = 0UL;
// walk the external symbol table
InitEnumerateExternals(pimage->pst); while ((pext = PexternalEnumerateNext(pimage->pst)) != NULL) { // ignore undefined externs
if (!(pext->Flags & EXTERN_DEFINED) || !pext->pcon || FIsLibPCON(pext->pcon)) { continue; }
// check to see if this is a function
if (ISFCN(pext->ImageSymbol.Type)) { DBEXEC(DB_DUMPJMPTBL, DBPRINT("sym=%s\n", SzNamePext(pext, pimage->pst)));
cext++; } } TerminateEnumerateExternals(pimage->pst);
// done
return cext; }
PCON PconCreateJumpTable ( PIMAGE pimage )
/*++
Routine Description:
Creates a dummy pcon for the jump table.
Arguments:
pimage - ptr to image.
Return Value:
None.
--*/
{ PCON pcon = NULL; PSEC psecText; PGRP pgrpBase;
// get count of functions
cextFCNs = CThunks(pimage);
if (cextFCNs == 0) { return NULL; }
// find .text section
psecText = PsecFind(NULL, ".text", IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ, &pimage->secs, &pimage->ImgOptHdr); assert(psecText);
pgrpBase = psecText->pgrpNext; assert(pgrpBase); assert(pgrpBase->pconNext);
// create a pcon
pcon = (PCON) Calloc(1, sizeof(CON));
switch (pimage->ImgFileHdr.Machine) { case IMAGE_FILE_MACHINE_ALPHA : ilink_info = &ALPHA_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
case IMAGE_FILE_MACHINE_I386 : ilink_info = &I386_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
case IMAGE_FILE_MACHINE_R4000 : case IMAGE_FILE_MACHINE_R10000 : ilink_info = &MIPS_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
case IMAGE_FILE_MACHINE_MPPC_601 : ilink_info = &MPPC_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
default: assert(FALSE); break; } // fill in structure
pcon->cbPad = __min(cextFCNs * CbJumpEntry(), USHRT_MAX); pcon->cbRawData = cextFCNs * CbJumpEntry() + pcon->cbPad; pcon->pgrpBack = pgrpBase; pcon->pmodBack = pmodLinkerDefined; pcon->pconNext = NULL;
pmodLinkerDefined->icon++;
// attach the pcon at the front of the list
pcon->pconNext = pgrpBase->pconNext; pgrpBase->pconNext = pcon;
return(pcon); }
void BuildThunkMap ( PVOID pvThunkTable, DWORD **prgThunkMapOff, DWORD cThunk )
/*++
Routine Description:
Builds the thunk map o be passed onto dbi.
Arguments:
pimage - ptr to image.
pvThunkTable - ptr to raw thunk table
prgThunkMapOff - ptr to array of file offsets of jmp destinations in thunk table on return.
cThunk - count of thunks.
Return Value:
None.
--*/
{ BYTE *p; DWORD i;
// Alloc space for the array of offsets
*prgThunkMapOff = (DWORD *) PvAlloc(cThunk * (sizeof(DWORD)));
// Fill the array with the offset values
p = (BYTE *) pvThunkTable;
if (fPowerMac) { // The first entry in the Jump Table is not used
// for PowerMac. So get past it!
p += CbJumpEntry(); }
p += ilink_info->address_offset; // get past first opcode
for (i = 0; i < cThunk; i++) { LONG offset = *(LONG UNALIGNED *) p;
if (fPowerMac) { // If it is PowerMac, remove the opcode in the first 6 bits
SwapBytes(&offset, 4); offset = (offset << 6) >> 6; }
// Calculate file offset of jmp destination
(*prgThunkMapOff)[i] = pconJmpTbl->foRawDataDest + ((i+1) * CbJumpEntry()) + offset;
p += CbJumpEntry(); } }
void WriteJumpTable ( PIMAGE pimage, PCON pconJmpTbl, DWORD **prgThunkMapOff, DWORD *pcThunk )
/*++
Routine Description:
Builds the jump table & writes it out.
Arguments:
pimage - ptr to image.
pconJmpTbl - pcon to write out
prgThunkMap - ptr to array of addresses on return.
pcThunkAddr - on return has countof thunks.
Return Value:
None.
--*/
{ PVOID pvRaw = NULL; DWORD cfuncs, i; LONG offset; BYTE *p; PEXTERNAL pext;
// check for any thunks
if (!pconJmpTbl || !cextFCNs) { return; }
// allocate space for raw data
pvRaw = PvAllocZ(pconJmpTbl->cbRawData);
// hammer thunks into the space
switch (pimage->ImgFileHdr.Machine) { case IMAGE_FILE_MACHINE_ALPHA: ilink_info = &ALPHA_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
case IMAGE_FILE_MACHINE_I386: ilink_info = &I386_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
case IMAGE_FILE_MACHINE_MPPC_601: ilink_info = &MPPC_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
case IMAGE_FILE_MACHINE_R4000: case IMAGE_FILE_MACHINE_R10000: ilink_info = &MIPS_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
default: assert(FALSE); break; }
cfuncs = cextFCNs;
p = (BYTE *) pvRaw; for (i = 0; i < cfuncs; i++) { memcpy(p, ilink_info->pvJumpEntry, ilink_info->cbJumpEntry); p += ilink_info->cbJumpEntry; }
p = (BYTE *) pvRaw + ilink_info->address_offset;
if (fPowerMac) { // In PowerMac we never want to use the first entry
// because we rely on the offset being not zero
// for any of the function that goes thru jump table
p += CbJumpEntry();
// So the pad is decreased by that amount
pconJmpTbl->cbPad -= CbJumpEntry(); }
// Walk the external symbol table
InitEnumerateExternals(pimage->pst); cfuncs = 0; while ((pext = PexternalEnumerateNext(pimage->pst)) != NULL) { if (!(pext->Flags & EXTERN_DEFINED) || !pext->pcon || FIsLibPCON(pext->pcon)) { // Ignore undefined externs
continue; }
// check to see if this is a function
if (ISFCN(pext->ImageSymbol.Type)) { // Hammer in func addr
offset = (LONG)(pext->pcon->rva + pext->ImageSymbol.Value) - (LONG)(pconJmpTbl->rva + (p - (BYTE *) pvRaw) + (fPowerMac ? 0 : sizeof(LONG)) );
DBEXEC(DB_DUMPJMPTBL, DBPRINT("Offset= %.8lx, symval=%.8lx, pconrva=%.8lx, sym=%s\n", offset, pext->ImageSymbol.Value,pext->pcon->rva, SzNamePext(pext, pimage->pst)));
if (fPowerMac) { if (!TEST32MBCODERANGE(offset)) { // Bail out of linking because the offset is greater than 26 bits
Error(NULL, TOOFAR, SzNamePext(pext, pimage->pst)); } else { offset = DwSwap(PPC_BRANCH | (offset & PPC_ADDR_MASK)); } }
*(LONG UNALIGNED *) p = offset;
p += CbJumpEntry();
// store the offset in pconjmptbl for the symbol
pext->Offset = p - (BYTE *) pvRaw - CbJumpEntry(); // offset is to addr
assert(pext->Offset); cfuncs++; } } TerminateEnumerateExternals(pimage->pst);
// Just checking
assert(cfuncs == cextFCNs); if ((pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_R4000) && (pimage->Switch.Link.fPadMipsCode == TRUE)) { DWORD cbAdjust;
if (!ComputeTextPad(pconJmpTbl->rva, (DWORD *) pvRaw, pconJmpTbl->cbRawData, 4096L, &cbAdjust)) { // Cannot adjust text, we're in big trouble
FatalPcon(pconJmpTbl, TEXTPADFAILED, cbAdjust, pconJmpTbl->rva); } assert(cbAdjust == 0); }
// Pad the the remaining space with int3
p = (BYTE *) pvRaw + pconJmpTbl->cbRawData - pconJmpTbl->cbPad; memset(p, ilink_info->bPad, pconJmpTbl->cbPad);
// Build the array of addr to which the thunks point to
if (pimage->Switch.Link.DebugInfo != None) { *pcThunk = cfuncs; BuildThunkMap(pvRaw, prgThunkMapOff, *pcThunk); }
// Write out jump table
FileSeek(FileWriteHandle, pconJmpTbl->foRawDataDest, SEEK_SET); FileWrite(FileWriteHandle, pvRaw, pconJmpTbl->cbRawData); FileSeek(FileWriteHandle, 0, SEEK_SET);
DBEXEC(DB_DUMPJMPTBL, DBPRINT("cextFCNs= %.8lx, cfuncs= %.8lx\n", cextFCNs, cfuncs)); DBEXEC(DB_DUMPJMPTBL, DumpJmpTbl(pconJmpTbl, pvRaw));
FreePv(pvRaw); }
void UpdateJumpTable ( PIMAGE pimage, DWORD **prgThunkMapOff, DWORD *pcThunk )
/*++
Routine Description:
Updates the existing jmp table with new addr of old functions and adds entries for the new functions.
Is it faster to just write out the individual thunks?
Arguments:
pimage - ptr to image.
Return Value:
None.
--*/
{ PVOID pvRaw = NULL; LONG offset; BYTE *p; PEXTERNAL pext; BYTE *pvNew; // new thunks get written here
if (!pconJmpTbl) { return; }
// allocate space for raw data
pvRaw = PvAllocZ(pconJmpTbl->cbRawData);
// read in jump table
FileSeek(FileWriteHandle, pconJmpTbl->foRawDataDest, SEEK_SET); FileRead(FileWriteHandle, pvRaw, pconJmpTbl->cbRawData);
DBEXEC(DB_DUMPJMPTBL, DBPRINT("\n---BEFORE---\n")); DBEXEC(DB_DUMPJMPTBL, DumpJmpTbl(pconJmpTbl, pvRaw));
pvNew = (BYTE *) pvRaw + (pconJmpTbl->cbRawData - pconJmpTbl->cbPad);
// Walk the external symbol table
InitEnumerateExternals(pimage->pst); while ((pext = PexternalEnumerateNext(pimage->pst)) != NULL) { // consider only the dirty & new funcs
if (!(pext->Flags & EXTERN_DIRTY) && !(pext->Flags & EXTERN_NEWFUNC)) { continue; }
if (FIsLibPCON(pext->pcon)) { // We ignore lib functions
continue; }
// Check to see if this is a function
if (pext->Flags & EXTERN_NEWFUNC) { assert(!pext->Offset); pext->Flags &= ~(EXTERN_NEWFUNC);
// Check to see if we are about to run over jmp tbl space
if ((pvNew + ilink_info->cbJumpEntry) > (BYTE *) pvRaw + pconJmpTbl->cbRawData) { TerminateEnumerateExternals(pimage->pst); FreePv(pvRaw); errInc = errJmpTblOverflow; return; }
if (fPowerMac) { RESET_BIT (pext, sy_NEWSYMBOL); }
// Hammer in thunk & new func addr
memcpy(pvNew, ilink_info->pvJumpEntry, ilink_info->cbJumpEntry); pvNew += ilink_info->address_offset; offset = (LONG)(pext->pcon->rva + pext->ImageSymbol.Value) - (LONG)(pconJmpTbl->rva + (pvNew - (BYTE *) pvRaw) + (fPowerMac ? 0 : sizeof(LONG)) );
if (fPowerMac) { if (!TEST32MBCODERANGE(offset)) { // Bail out of linking because the offset is greater than 26 bits
Error(NULL, TOOFAR, SzNamePext(pext, pimage->pst)); } else { offset = DwSwap(PPC_BRANCH | (offset & PPC_ADDR_MASK)); } }
*(LONG UNALIGNED *) pvNew = offset; pvNew += 4;
// store the offset in pconjmptbl for the symbol
pext->Offset = pvNew - (BYTE *) pvRaw - CbJumpEntry() + ilink_info->address_offset; // +N to go past opcode
// reduce the pad available
pconJmpTbl->cbPad -= CbJumpEntry(); } else { // Old func whose addr has changed
assert(pext->Offset); pext->Flags &= ~(EXTERN_DIRTY);
// Hammer in new address
p = (BYTE *) pvRaw + pext->Offset;
offset = (LONG)(pext->pcon->rva + pext->ImageSymbol.Value) - (LONG)(pconJmpTbl->rva + (p - (BYTE *) pvRaw) + (fPowerMac ? 0 : sizeof(LONG)) );
if (fPowerMac) { if ( !TEST32MBCODERANGE(offset)) { // Bail out of linking because the offset is greater than 26 bits
Error(NULL, TOOFAR, SzNamePext(pext, pimage->pst)); } else { offset = (offset & PPC_ADDR_MASK) | PPC_BRANCH; SwapBytes(&offset, 4); } }
*(LONG UNALIGNED *) p = offset; } } TerminateEnumerateExternals(pimage->pst);
if ((pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_R4000) && (pimage->Switch.Link.fPadMipsCode == TRUE)) { DWORD cbAdjust; if (!ComputeTextPad(pconJmpTbl->rva, (DWORD *) pvRaw, pconJmpTbl->cbRawData, 4096L, &cbAdjust)) { // Cannot adjust text, we're in big trouble
FatalPcon(pconJmpTbl, TEXTPADFAILED, cbAdjust, pconJmpTbl->rva); } assert(cbAdjust == 0); }
// Write out jump table
FileSeek(FileWriteHandle, pconJmpTbl->foRawDataDest, SEEK_SET); FileWrite(FileWriteHandle, pvRaw, pconJmpTbl->cbRawData); FileSeek(FileWriteHandle, 0, SEEK_SET);
// Build array of addr for disasm support
if (pimage->Switch.Link.DebugInfo != None) { *pcThunk = ((BYTE *) pvNew - (BYTE *) pvRaw) / CbJumpEntry(); BuildThunkMap(pvRaw, prgThunkMapOff, *pcThunk); }
DBEXEC(DB_DUMPJMPTBL, DBPRINT("\n---AFTER---\n")); DBEXEC(DB_DUMPJMPTBL, DumpJmpTbl(pconJmpTbl, pvRaw));
FreePv(pvRaw); }
PMOD PmodFindPrevPMOD ( PMOD pmod )
/*++
Routine Description:
Finds the mod before this.
Arguments:
pmod - pmod
Return Value:
PMOD prior to this or NULL
--*/
{ ENM_MOD enm_mod; PMOD pmodP = NULL;
assert(pmod); // walk the list of pmods
InitEnmMod(&enm_mod, pmod->plibBack); while (FNextEnmMod(&enm_mod)) { if (enm_mod.pmod == pmod) { return pmodP; }
pmodP = enm_mod.pmod; } EndEnmMod(&enm_mod);
return(NULL); }
PCON PconFindPrevPCON ( PCON pcon )
/*++
Routine Description:
Finds the previous pcon.
Arguments:
pcon - pcon
Return Value:
None.
--*/
{ PGRP pgrp; PCON pconP = NULL; ENM_DST enm_dst;
assert(pcon); // start at the top of the group
pgrp = pcon->pgrpBack; InitEnmDst(&enm_dst, pgrp); while (FNextEnmDst(&enm_dst)) { if (enm_dst.pcon == pcon) { return pconP; }
pconP = enm_dst.pcon; } EndEnmDst(&enm_dst);
assert(0); return(NULL); }
PCON PconFindOldPMOD ( PMOD pmodO, PCON pconN )
/*++
Routine Description:
Finds a matching PCON in the old mod
Arguments:
pmodO - old mod
pconN - con from new mod
Return Value:
Matching PCON in old mod or NULL
--*/
{ PCON pcon; DWORD i;
if (!pmodO) { return NULL; }
assert(pmodO); // walk the list of pcons
for (i = 0; i < pmodO->ccon; i++) { pcon = RgconPMOD(pmodO) + i;
// seen already?
if (!pcon->foRawDataDest) { continue; }
// pcons match if they belong to same
// group & have same flags (turn off FIXED bit)
if (pcon->pgrpBack == pconN->pgrpBack && pcon->flags == pconN->flags) {
return(pcon); } }
// didn't find a match
return NULL; }
void ZeroPCONSpace ( PCON pcon )
/*++
Routine Description:
Zeros out space occupied by a pcon in the output file.
Arguments:
pcon - pcon to be zeroed out.
Return Value:
None.
--*/
{ PVOID pvRawData; assert(pcon);
// ignore PCONs that don't get written out to the output file.
if (pcon->flags & IMAGE_SCN_LNK_REMOVE || !pcon->cbRawData || FetchContent(pcon->flags) == IMAGE_SCN_CNT_UNINITIALIZED_DATA) { return; }
assert(pcon->foRawDataDest != 0); // allocate a chunk for pcon and set to either int3 or zero
pvRawData = PvAlloc(pcon->cbRawData); if (FetchContent(pcon->flags) == IMAGE_SCN_CNT_CODE) { memset(pvRawData, ilink_info->bPad, pcon->cbRawData); }
// zero out space in output file
FileSeek(FileWriteHandle, pcon->foRawDataDest, SEEK_SET); FileWrite(FileWriteHandle, pvRawData, pcon->cbRawData);
FreePv(pvRawData); }
void FreePCONSpace( PCON pconP, PCON pconC, PIMAGE pimage )
/*++
Routine Description:
Frees up space. Makes it pad of the prior pcon.
Arguments:
pconP - previous pcon
pconC - pcon to be free'd (space) up
pimage - ptr to IMAGE
Return Value:
None.
--*/
{ assert(pconC);
if (!(fPowerMac && (PgrpPCON(pconC) == pgrpPdata))) { // Pdata (C++ EH for PowerMac), do not zero out pcon
// because this is done in PDATAUpdate in ipdata.cpp
ZeroPCONSpace(pconC); }
if (pconP) { // A previous pcon exists
assert((pconP->flags & IMAGE_SCN_LNK_REMOVE) == 0);
pconP->cbRawData += pconC->cbRawData; pconP->cbPad += (WORD) pconC->cbRawData; pconP->pconNext = pconC->pconNext; } else { PCON pcon;
// This is the first pcon in the grp
// create a dummy PCON at the front of the group
// check to make sure that the first PCON in the image
// is pconC.
if (pconC != pconC->pgrpBack->pconNext) { pcon = pconC->pgrpBack->pconNext; while (pcon != pconC) { assert(pcon->flags & IMAGE_SCN_LNK_REMOVE); pcon = pcon->pconNext; } } pcon = (PCON) Calloc(1, sizeof(CON));
pcon->pgrpBack = pconC->pgrpBack; pcon->pmodBack = pmodLinkerDefined; pmodLinkerDefined->icon++;
pcon->cbPad = pcon->cbRawData = pconC->cbRawData; pcon->rva = pconC->rva; pcon->foRawDataDest = pconC->foRawDataDest;
pcon->pconNext = pconC->pconNext; // unused cons bypassed - that's ok
pconC->pgrpBack->pconNext = pcon; } }
void OverflowInPCON ( PCON pconN, PCON pconO, PIMAGE pimage )
/*++
Routine Description:
Handles the case where a CON has overflowed its pad.
Arguments:
pconN - new pcon of the changed module
pconO - old pcon
pimage - ptr to IMAGE
Return Value:
None.
--*/
{ PCON pconP;
// Free up the space occupied currently by the pcon
pconP = PconFindPrevPCON(pconO); while (pconP && ((pconP->flags & IMAGE_SCN_LNK_REMOVE) != 0)) { pconP = PconFindPrevPCON(pconP); }
FreePCONSpace(pconP, pconO, pimage);
// Find a slot for the new pcon
FindSlotForPCON(pconN);
// Mark pcon as seen
pconO->foRawDataDest = 0; }
void GrowPCON ( PCON pconN, PCON pconO )
/*++
Routine Description:
pcon has grown (incr/decr) to fit the current spot
Arguments:
pconN(ew) - pcon of the modified mod
pconO(ld) - pcon of the old mod
Return Value:
None.
--*/
{ PCON pconP;
// assign values
pconN->foRawDataDest = pconO->foRawDataDest; pconN->rva = pconO->rva; pconN->cbPad = (WORD) (pconO->cbRawData - pconN->cbRawData); pconN->cbRawData += pconN->cbPad;
// find prev pcon and chain up the
// new pcon into the image map
pconP = PconFindPrevPCON(pconO); if (pconP) { pconP->pconNext = pconN; } else { pconN->pgrpBack->pconNext = pconN; } pconN->pconNext = pconO->pconNext;
// mark old pcon as seen.
pconO->foRawDataDest = 0; }
void FindSlotForPCON ( PCON pcon )
/*++
Routine Description:
Moves pcon to the first available slot (alternative strategies? best fit?)
Assumes that pcons are in proper rva order.
Arguments:
pcon - pcon that is to be moved
Return Value:
None.
--*/
{ PGRP pgrp; PCON pconC; BOOL fFound = 0; DWORD rva, cbPad, cbRaw; ENM_DST enm_dst;
assert(pcon); assert(pcon->pgrpBack);
pgrp = pcon->pgrpBack;
// search within the group
InitEnmDst(&enm_dst, pgrp); while (FNextEnmDst(&enm_dst)) { pconC = enm_dst.pcon;
// skip unusable pcons
if (pconC->flags & IMAGE_SCN_LNK_REMOVE) { continue; }
// cannot use the pad of jmptbl pcon. It is committed space.
if (pconC == pconJmpTbl) { continue; }
if (fPowerMac) { // cannot use the pad of PowerMacLoader, TOC Table,
// TOC descriptors, Glue Code pcons. They are all committed space.
if (pconC == pconPowerMacLoader || pconC == pconTocTable || pconC == pconTocDescriptors || pconC == pconGlueCode || pconC == pconMppcFuncTable) { continue; } }
// do we have enough room for this pcon
if (pcon->cbRawData > pconC->cbPad ) { continue; }
// one more check to ensure padding requirements
cbRaw = pconC->cbRawData - pconC->cbPad; // raw data of existing PCON
rva = pconC->rva + cbRaw; cbPad = RvaAlign(rva, pcon->flags) - rva; // pad for PCON being inserted
if (pcon->cbRawData + cbPad > pconC->cbPad) { continue; }
// found a slot
fFound = 1;
// assign values for PCON being inserted. Note that the pad
// calculated above becomes part of the existing PCON.
assert ((pconC->flags & IMAGE_SCN_LNK_REMOVE) == 0); assert(pconC->rva != 0); pcon->rva = pconC->rva + cbRaw + cbPad; pcon->foRawDataDest = pconC->foRawDataDest + cbRaw + cbPad; pcon->cbPad = (WORD)((DWORD)pconC->cbPad - (cbPad + pcon->cbRawData)); pcon->cbRawData += pcon->cbPad; pcon->pconNext = pconC->pconNext; // update values for existing CON
pconC->cbPad = (WORD)cbPad; pconC->cbRawData = cbRaw + cbPad; pconC->pconNext = pcon;
break; } EndEnmDst(&enm_dst);
// done
if (!fFound) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeEvent, "failed to find a slot for pcon: %-8.8s", pcon->pgrpBack->szName); #endif // INSTRUMENT
errInc = errCalcPtrs; } }
void FreePMOD ( PIMAGE pimage, PMOD pmodO )
/*++
Routine Description:
Frees up space.
Arguments:
pmodO - old mod
Return Value:
None.
--*/
{ DWORD i; PCON pconP, pcon;
// free up space occupied by unseen pcons
for (i = 0; i < pmodO->ccon; i++) { pcon = RgconPMOD(pmodO) + i;
// Seen already?
if (!pcon->foRawDataDest) { continue; }
pconP = PconFindPrevPCON(pcon); while (pconP && ((pconP->flags & IMAGE_SCN_LNK_REMOVE) != 0)) { pconP = PconFindPrevPCON(pconP); }
FreePCONSpace(pconP, pcon, pimage);
if (fPowerMac && (PgrpPCON(pcon) == pgrpPdata)) { // Pdata (C++ EH for PowerMac), do not delete base relocs
continue; }
// zap any base relocs
if (fPowerMac) { CHAR szBuffer[12];
// Code is pure in PowerMac and so there won't be any
// relocations in .text and .drectve sections
if (!(pcon->flags & IMAGE_SCN_CNT_CODE || pcon->flags & IMAGE_SCN_LNK_INFO)) { AddArgumentToList(&ZappedBaseRelocList, SzDup(_itoa(pcon->rva, szBuffer, 10)), SzDup(_itoa(pcon->cbRawData - pcon->cbPad, szBuffer, 10)) ); } } else { // Intel, MIPS, and others
DeleteBaseRelocs(&pimage->bri, pcon->rva, pcon->cbRawData - pcon->cbPad); } }
// Free up memory occupied by PMOD (LATER)
}
void AllocSpaceForImportPCON ( PCON pcon )
/*++
Routine Description:
Allocates space for this .idata PCON.
Arguments:
pcon - ptr to a .idata pcon
Return Value:
None.
--*/
{ ENM_DST enm_dst; PPCON ppcon;
if (!pgrpIdata$4) { pgrpIdata$4 = PgrpFind(psecImportDescriptor, ".idata$4"); pgrpIdata$5 = PgrpFind(psecImportDescriptor, ".idata$5"); pgrpIdata$6 = PgrpFind(psecImportDescriptor, ".idata$6");
if (!pgrpIdata$4 || !pgrpIdata$5 || !pgrpIdata$6) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeEvent, "failed to find a slot for pcon: %-8.8s", pcon->pgrpBack->szName); #endif // INSTRUMENT
errInc = errCalcPtrs; return; } } // end if
assert(pgrpIdata$5); assert(pgrpIdata$6);
if (pcon->pgrpBack != pgrpIdata$4 && pcon->pgrpBack != pgrpIdata$5 && pcon->pgrpBack != pgrpIdata$6) {
// PCON belongs to groups other than .idata${4,5,6}
errInc = errCalcPtrs; return; }
// for .idata$6 pcon is inserted like any other pcon
if (pcon->pgrpBack == pgrpIdata$6) { FindSlotForPCON(pcon); return; }
// for .idata$5, .idata$4 pcons inserted in proper
// place (same DLL) and order (before NULL entry)
InitEnmDst(&enm_dst, pcon->pgrpBack); ppcon = &pcon->pgrpBack->pconNext; while (FNextEnmDst(&enm_dst)) { if (strcmp(SzObjNamePCON(enm_dst.pcon), SzObjNamePCON(pcon)) || enm_dst.pcon->cbPad < pcon->cbRawData) {
ppcon = &enm_dst.pcon->pconNext; continue; }
// fill in fields for new CON
pcon->rva = enm_dst.pcon->rva; pcon->foRawDataDest = enm_dst.pcon->foRawDataDest; pcon->pconNext = enm_dst.pcon;
// update existing pcon fields
enm_dst.pcon->foRawDataDest += pcon->cbRawData; enm_dst.pcon->rva += pcon->cbRawData; enm_dst.pcon->cbRawData -= pcon->cbRawData; enm_dst.pcon->cbPad -= pcon->cbRawData;
// update previous con's next ptr
(*ppcon) = pcon;
EndEnmDst(&enm_dst); return; } EndEnmDst(&enm_dst);
errInc = errCalcPtrs; }
void CalcPtrsPMOD ( PMOD pmodN, PMOD pmodO, PIMAGE pimage )
/*++
Routine Description:
Calculates new addresses for all the changed CONs.
If it is not possible to assign an address, function returns after setting the appropriate error value in errInc.
Arguments:
pmodN - Current module (modified)
pmodO - Previous module by the same name (if any)
pimage - ptr to EXE image
Return Value:
None.
--*/
{ PMOD pmodP; PCON pconN; PCON pconO; ENM_SRC enm_src; PEXTERNAL pext; DWORD AlphaBsrCount = 0;
SzComNamePMOD(pmodN, InternalError.CombinedFilenames);
DBEXEC(DB_INCRCALCPTRS, DBPRINT("\nMODULE = %s\n", InternalError.CombinedFilenames));
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeEvent, "mod: %s", InternalError.CombinedFilenames); #endif // INSTRUMENT
// for each pcon in new mod, find a matching con
// in the old module if possible
InitEnmSrc(&enm_src, pmodN); while (FNextEnmSrc(&enm_src)) { pconN = enm_src.pcon;
// Ignore ignoreable pcons. eg zero-sized pcons OR COMDATs not included;
// debug sections can be ignored as well.
if (pconN->flags & IMAGE_SCN_LNK_REMOVE || PsecPCON(pconN) == psecDebug) { continue; }
// Check to see if this pcon is an import data pcon
if (PsecPCON(pconN) == psecImportDescriptor) { AllocSpaceForImportPCON(pconN);
if (errInc != errNone) { return; }
continue; }
// Find a matching pcon
pconO = PconFindOldPMOD(pmodO, pconN);
if (!pconO) { // New pcon
DBEXEC(DB_INCRCALCPTRS, DBPRINT("NEW %.8s cb=%.8lx\n", pconN->pgrpBack->szName, pconN->cbRawData));
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeEvent, "new pcon %.8s cb=0x%.8lx", pconN->pgrpBack->szName, pconN->cbRawData); #endif // INSTRUMENT
FindSlotForPCON(pconN); } else { // Zap any relocs right away
if (fPowerMac) { CHAR szBuffer[12];
// Code is pure in PowerMac and so there won't be any
// relocations in .text and .drectve sections
if (!(pconO->flags & IMAGE_SCN_CNT_CODE || pconO->flags & IMAGE_SCN_LNK_INFO || (PgrpPCON(pconO) == pgrpPdata))) { AddArgumentToList(&ZappedBaseRelocList, SzDup(_itoa(pconO->rva, szBuffer, 10)), SzDup(_itoa(pconO->cbRawData - pconO->cbPad, szBuffer, 10)) ); } } else { // Intel, MIPS, and others
DeleteBaseRelocs(&pimage->bri, pconO->rva, pconO->cbRawData - pconO->cbPad); }
if (pconN->cbRawData <= pconO->cbRawData) { // Got enough room
DBEXEC(DB_INCRCALCPTRS, DBPRINT("GRW %.8s cb(o)=%.8lx cb(n)=%.8lx\n", pconN->pgrpBack->szName, pconO->cbRawData, pconN->cbRawData));
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeEvent, "pad %.8s cb(n):0x%.8lx cb(o):0x%.8lx cb(pad):0x%.4x", pconN->pgrpBack->szName, pconN->cbRawData, pconO->cbRawData-pconO->cbPad, pconO->cbPad); #endif // INSTRUMENT
GrowPCON(pconN, pconO); } else { // Not enough room
DBEXEC(DB_INCRCALCPTRS, DBPRINT("OVF %.8s cb(o)=%.8lx cb(n)=%.8lx\n", pconN->pgrpBack->szName, pconO->cbRawData, pconN->cbRawData));
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeEvent, "ovf %.8s cb(n):0x%.8lx cb(o):0x%.8lx cb(pad):0x%.4x", pconN->pgrpBack->szName, pconN->cbRawData, pconO->cbRawData-pconO->cbPad, pconO->cbPad); #endif // INSTRUMENT
OverflowInPCON(pconN, pconO, pimage); } }
if ((FetchContent(pconN->flags) == IMAGE_SCN_CNT_CODE) && (pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_R4000) && (pimage->Switch.Link.fPadMipsCode == TRUE) ) {
extern DWORD CheckMIPSCode(PCON);
DWORD cbAdjust = CheckMIPSCode(pconN);
if (cbAdjust != 0) { if (cbAdjust <= pconN->cbPad) { PCON pconP;
pconP = PconFindPrevPCON(pconN);
if (pconP) { pconN->cbPad -= cbAdjust; pconN->cbRawData -= cbAdjust; pconN->foRawDataDest += cbAdjust; pconN->rva += cbAdjust; pconP->cbPad += cbAdjust; pconP->cbRawData += cbAdjust; } else { DBEXEC(DB_INCRCALCPTRS, DBPRINT("CheckMIPSCode returned %d in first pcon\n", cbAdjust)); errInc = errPdata; }
} else { DBEXEC(DB_INCRCALCPTRS, DBPRINT("CheckMIPSCode returned %d\n", cbAdjust)); errInc = errPdata; } } }
// Check status
if (errInc != errNone) { return; } } EndEnmSrc(&enm_src);
// Alloc space for COMMON data
pext = pmodN->pextFirstDefined; while (pext) { if (pext->Flags & EXTERN_COMMON) { assert(pext->pcon); FindSlotForPCON(pext->pcon); }
pext = pext->pextNextDefined; }
// Link in the new module & cut out the old (cmdline objs)
if (!FIsLibPMOD(pmodN)) { pmodP = PmodFindPrevPMOD(pmodO);
if (pmodP) { pmodP->pmodNext = pmodN; } else { pmodO->plibBack->pmodNext = pmodN; }
pmodN->pmodNext = pmodO->pmodNext; pmodN->plibBack = pmodO->plibBack;
assert(pmodN->imod == 0);
pmodN->imod = pmodO->imod;
FreePMOD(pimage, pmodO); } else { // New objects (from lib search)
assert(pmodN->plibBack);
pmodN->imod = NewIModIdx();
if (pmodN->imod >= IMODIDXMAC) { Fatal(NULL, PDBLIMIT, NULL); } } }
void IncrCalcPtrsPMOD ( PMOD pmod, PLIB plib, PIMAGE pimage ) { PMOD pmodO;
// for modules from libs there is no existing MOD, we just add new MODs
if (plib) { pmodO = PmodFind(plib, SzOrigFilePMOD(pmod), FoMemberPMOD(pmod)); } else { pmodO = NULL; }
CalcPtrsPMOD(pmod, pmodO, pimage);
// check for failures
if (errInc != errNone) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeEvent, "failed calcptrs, file: %s", SzOrigFilePMOD(pmod)); LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeEnd, NULL); #endif // INSTRUMENT
return; }
// add it to the pass2 list
DoPass2PMOD(pmod, TRUE); // Set flag to tell them they have gone through pass1
pmod->LnkFlags |= MOD_DidPass1; }
void IncrCalcPtrs ( PIMAGE pimage )
/*++
Routine Description:
Calculates new addresses for all the changed mods.
If it is not possible to assign an address, function returns after setting the appropriate error value in errInc.
Arguments:
pimage - ptr to EXE image
Return Value:
None.
--*/
{ PMOD pmod; PMOD pmodNext; PLMOD plmod;
InternalError.Phase = "CalcPtrs"; InternalError.CombinedFilenames[0] = '\0';
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeBegin, NULL); #endif // INSTRUMENT
if (!fPowerMac) { // setup psec pointers
psecBaseReloc = PsecFindNoFlags(ReservedSection.BaseReloc.Name, &pimage->secs);
psecImportDescriptor = PsecFind(NULL, ".idata", ReservedSection.ImportDescriptor.Characteristics, &pimage->secs, &pimage->ImgOptHdr); }
// walk the list of modified cmdline objects
pmod = plibModObjs->pmodNext; while (pmod) { pmodNext = pmod->pmodNext;
IncrCalcPtrsPMOD(pmod, pimage->plibCmdLineObjs, pimage); if (errInc != errNone) { return; }
pmod = pmodNext; } plibModObjs->pmodNext = NULL;
// walk the list of new objs (added as aresult of lib srch)
plmod = plmodNewModsFromLibSrch; while (plmod) { IncrCalcPtrsPMOD(plmod->pmod, NULL, pimage); plmod = plmod->plmodNext; } FreePLMODList(&plmodNewModsFromLibSrch);
// check if there is enough space for base relocs
// For PowerMac this is done at the end in MppcUpdateRelocTable in ppc.c
fNoBaseRelocs = (pimage->bri.rgfoBlk == NULL); if (!fPowerMac && !pimage->Switch.Link.fFixed && pimage->bri.crelFree < pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) { errInc = errBaseReloc; #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZPASS1, letypeEvent, "not enough space for base relocs"); #endif // INSTRUMENT
}
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZCALCPTRS, letypeEnd, NULL); #endif // INSTRUMENT
}
void CollectWeakAndLazyExterns ( PMOD pmod )
/*++
Routine Description:
Collect any weak & lazy externs defined by this mod.
Arguments:
pmod - pmod to delete from list
Return Value:
None.
--*/
{ PEXTERNAL pext = pmod->pextFirstDefined;
while (pext) { if ((pext->Flags & (EXTERN_WEAK|EXTERN_LAZY)) && pext->Offset) { AddExtToList(plpextWeak, TRUE, pext); }
pext = pext->pextNextDefined; } }
void DeletePMODFromRefList ( PMODS pmods, PMOD pmod )
/*++
Routine Description:
Deletes PMODs representing the modified files from the specified list.
Arguments:
pmods - ptr to first chunk of PMODs
pmod - pmod to delete from list
Return Value:
None.
--*/
{ while (pmods) {
PMOD *rgpmod = RgpmodPMODS(pmods); DWORD i;
// is it referenced by a modified file?
for (i = 0; i < CPMODS; i++) { if (rgpmod[i] == pmod) { rgpmod[i] = NULL; return; } }
// next chunk
pmods = pmods->pmodsNext; } }
void DeleteReference ( PEXTERNAL pext, PMOD pmod )
/*++
Routine Description:
Deletes references to this symbol from modified files.
Arguments:
pext - ptr to external
pmod - ptr to MOD referencing this extern.
Return Value:
None.
--*/
{ assert(pext); assert(pext->pmodOnly);
if (pext->Flags & EXTERN_MULT_REFS) { DeletePMODFromRefList(pext->pmodsFirst, pmod); } else { assert(pext->pmodOnly == pmod); pext->pmodOnly = NULL; } }
void RemoveReferencesPMOD ( PMOD pmod )
/*++
Routine Description:
2) remove references by modified objs to any of the symbols.
Arguments:
pmod - ptr to a MOD
Return Value:
None.
--*/
{ ENM_EXT_LIST enmExtList;
InitEnmExtList(&enmExtList, pmod->plpextRef); while (FNextEnmExtList(&enmExtList)) {
if (!enmExtList.pext) { continue; // sometimes linker removes references (for bss)
}
DeleteReference(enmExtList.pext, pmod); } EndEnmExtList(&enmExtList); }
void MarkSymbolsUndefinedPMOD ( PIMAGE pimage, PMOD pmod )
/*++
Routine Description:
1) mark all symbols that were defined in the modified objs as UNDEFINED.
Arguments:
pimage - ptr to IMAGE
pmod - ptr to a MOD
Return Value:
None.
--*/
{ PEXTERNAL pext = pmod->pextFirstDefined;
while (pext) { PEXTERNAL pextNext = pext->pextNextDefined;
// if symbol was common free up space used
if (pext->Flags & EXTERN_COMMON) { assert(PmodPCON(pext->pcon) == pmodLinkerDefined);
// free up space here itself so that we don't have to save the list
FreePCONSpace(PconFindPrevPCON(pext->pcon), pext->pcon, pimage); pext->pcon = NULL; }
// mark symbol as undefined
SetDefinedExt(pext, FALSE, pimage->pst); pext->Flags &= ~(EXTERN_DEFINED | EXTERN_EMITTED);
pext = pextNext; } }
void MarkSymbols ( PIMAGE pimage )
/*++
Routine Description:
1) mark all symbols that were defined in the modified objs as UNDEFINED and 2) remove references by modified objs to any of the symbols.
Arguments:
pimage - ptr to EXE image
Return Value:
None.
--*/
{ ENM_MOD enm_mod;
// walk the cmdline objs.
InitEnmMod(&enm_mod, pimage->plibCmdLineObjs); while (FNextEnmMod(&enm_mod)) {
// gather all "weak" & "lazy" externs.
CollectWeakAndLazyExterns(enm_mod.pmod);
// if dirty, undef symbols & remove references
if (IsDirtyPMOD(enm_mod.pmod)) {
MarkSymbolsUndefinedPMOD(pimage, enm_mod.pmod);
RemoveReferencesPMOD(enm_mod.pmod); }
} EndEnmMod(&enm_mod);
// walk the external symbol table
// REVIEW: we should remove this walk altogether
if (fPowerMac) { PEXTERNAL pext;
InitEnumerateExternals(pimage->pst); while ((pext = PexternalEnumerateNext(pimage->pst)) != NULL) { // ignore whatever needs to be ignored
if ((pext->Flags & EXTERN_IGNORE) || !(pext->Flags & EXTERN_DEFINED) || !pext->pcon) { continue; }
// Reset bit for sy_TOCENTRYFIXEDUP so that it will be
// processed later
RESET_BIT(pext, sy_TOCENTRYFIXEDUP); } TerminateEnumerateExternals(pimage->pst); } }
ERRINC ChckExtSym ( const char *szSym, PIMAGE_SYMBOL psymObj, PEXTERNAL pext, BOOL fNewSymbol )
/*++
Routine Description:
Checks to see if the extern has changed address.
Arguments:
szSym = symbol name
pext - ptr to external (values represent prior link)
psymObj - symbol being processed
fNewSymbol - TRUE if new symbol
Return Value:
One of the values of ERRINC (errNone, errDataMoved, errJmpTblOverflow)
--*/
{ // is it a func?
if (ISFCN(psymObj->Type)) { // is it new? make sure we have room for one more thunk
if (fNewSymbol) { DBEXEC(DB_SYMPROCESS,DBPRINT("sym= %s (NEW func)\n", szSym)); pext->Flags |= EXTERN_NEWFUNC; // old function
} else { if (pext->ImageSymbol.Value != psymObj->Value) { DBEXEC(DB_SYMPROCESS,DBPRINT("sym= %s (func chng)", szSym)); DBEXEC(DB_SYMPROCESS,DBPRINT(" old addr= %.8lx, new addr= %.8lx\n", pext->ImageSymbol.Value, psymObj->Value)); } else { DBEXEC(DB_SYMPROCESS,DBPRINT("sym= %s (func unchng)\n", szSym)); } pext->Flags |= EXTERN_DIRTY;
// if extern has fixups to it that don't go thru the jump table,
// need to do a pass2 on all mods that reference it - add it to data list.
if (pext->Flags & EXTERN_FUNC_FIXUP) { AddToLext(&plextMovedData, pext); } }
// not a function (data)
} else { if (fNewSymbol) { DBEXEC(DB_SYMPROCESS,DBPRINT("sym= %s (NEW data)\n", szSym)); pext->Flags |= EXTERN_NEWDATA; // Log_ILOG1("NEW data sym........: %s", szSym);
// return(errInc = errDataMoved);
// not new? check to see if addr has changed.
} else { if (pext->ImageSymbol.Value != psymObj->Value) { DBEXEC(DB_SYMPROCESS,DBPRINT("sym= %s (data chng)", szSym)); DBEXEC(DB_SYMPROCESS,DBPRINT(" old addr= %.8lx, new addr= %.8lx\n", pext->ImageSymbol.Value, psymObj->Value)); #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZPASS1, letypeEvent, "data moved:%s", szSym); #endif // INSTRUMENT
// return(errInc = errDataMoved);
} else { DBEXEC(DB_SYMPROCESS,DBPRINT("sym= %s (data unchng)\n", szSym)); }
// add to list of data whose addresses may change.
AddToLext(&plextMovedData, pext); } }
return errNone; }
ERRINC ChckAbsSym ( const char *szSym, PIMAGE_SYMBOL psymObj, PEXTERNAL pext, BOOL fNewSymbol )
/*++
Routine Description:
Checks to see if the absolute sym has changed address.
Arguments:
szSym = symbol name
pext - ptr to external (values represent prior link)
psymObj - symbol being processed
fNewSymbol - TRUE if new symbol
Return Value:
One of the values of ERRINC (errNone, errDataMoved, errJmpTblOverflow)
--*/
{ // new symbol
if (fNewSymbol) { return errNone; }
// old symbol
if (psymObj->Value != pext->ImageSymbol.Value) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZPASS1, letypeEvent, "chng in abs value: %s", szSym); #endif // INSTRUMENT
return(errInc = errAbsolute); }
return(errNone); }
void IncrAllocCommonPMOD ( PMOD pmod, PIMAGE pimage ) { PEXTERNAL pext = pmod->pextFirstDefined;
// alloc a CON for each COMMON symbol
while (pext) { if (pext->Flags & EXTERN_COMMON) { assert(!pext->pcon); AllocateCommonPEXT(pimage, pext); AddToLext(&plextMovedData, pext); assert(pext->pcon); } pext = pext->pextNextDefined; } }
void IncrAllocateCommon ( PIMAGE pimage ) { PLMOD plmod; PMOD pmod, pmodNext;
// walk the list of modified cmd line objs
pmod = plibModObjs->pmodNext; while (pmod) { pmodNext = pmod->pmodNext;
IncrAllocCommonPMOD(pmod, pimage); pmod = pmodNext; }
// walk the list of new objs (added as aresult of lib srch)
plmod = plmodNewModsFromLibSrch; while (plmod) { IncrAllocCommonPMOD(plmod->pmod, pimage); plmod = plmod->plmodNext; } }
void InitPconJmpTbl ( PIMAGE pimage )
/*++
Routine Description:
Estimates count of new functions that can be added to jump table without overflow.
Arguments:
pimage - ptr to EXE image
Return Value:
None.
--*/
{ PSEC psecText; PGRP pgrpBase;
// Find .text section
psecText = PsecFind(NULL, ".text", IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ, &pimage->secs, &pimage->ImgOptHdr);
if (psecText == NULL) { pconJmpTbl = NULL; return; } assert(psecText);
pgrpBase = psecText->pgrpNext; assert(pgrpBase); assert(pgrpBase->pconNext);
// first pcon is jmp tbl
pconJmpTbl = pgrpBase->pconNext; }
void IncrPass1 ( PIMAGE pimage )
/*++
Routine Description:
Does an incremental Pass1.
Arguments:
pimage - ptr to EXE image
Return Value:
TRUE if it succeeded, FALSE on failure
--*/
{ DWORD i; PARGUMENT_LIST arg;
switch (pimage->ImgFileHdr.Machine) { case IMAGE_FILE_MACHINE_ALPHA: ilink_info = &ALPHA_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
case IMAGE_FILE_MACHINE_I386: ilink_info = &I386_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
case IMAGE_FILE_MACHINE_MPPC_601: ilink_info = &MPPC_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
case IMAGE_FILE_MACHINE_R4000: case IMAGE_FILE_MACHINE_R10000: ilink_info = &MIPS_ilink; cbJumpEntry = ilink_info->cbJumpEntry; break;
default: assert(FALSE); break; } // setup global ptrs to debug section (always)
psecDebug = PsecFindNoFlags(".debug", &pimage->secs); assert(psecDebug); pgrpCvSymbols = PgrpFind(psecDebug, ReservedSection.CvSymbols.Name); pgrpCvTypes = PgrpFind(psecDebug, ReservedSection.CvTypes.Name); pgrpCvPTypes = PgrpFind(psecDebug, ReservedSection.CvPTypes.Name); pgrpFpoData = PgrpFind(psecDebug, ReservedSection.FpoData.Name); psecException = PsecFindNoFlags(ReservedSection.Exception.Name, &pimage->secs);
// create a dummy library node for all modified command line objects
plibModObjs = PlibNew("inc_lib", 0L, &pimage->libs); assert(plibModObjs);
// exclude dummy library from library search
plibModObjs->flags |= (LIB_DontSearch | LIB_LinkerDefined);
// estimate how many new functions can be handled before overflow
InitPconJmpTbl(pimage);
// reset count of relocs
// For PowerMac this is done in ppc.c in MppcDoIncrInit
pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0;
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZPASS1, letypeBegin, NULL); #endif // INSTRUMENT
// make a pass over all modified files
for (i = 0, arg = ModFileList.First; i < ModFileList.Count; i++, arg = arg->Next) {
Pass1Arg(arg, pimage, plibModObjs);
if (errInc != errNone) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZPASS1, letypeEvent, "failed pass1, file: %s", arg->OriginalName); LogNoteEvent(Log, SZILINK, SZPASS1, letypeEnd, NULL); #endif // INSTRUMENT
return; } }
InternalError.CombinedFilenames[0] = '\0';
// Allocate CONs for EXTERN_COMMON symbols
// AllocateCommon(pimage);
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZPASS1, letypeEnd, NULL); #endif // INSTRUMENT
}
void CountBaseRelocsPMOD ( PMOD pmod, DWORD *pcReloc )
/*++
Routine Description:
Counts base reloc by counting relocs in each CON of pmod.
Arguments:
pmod - pointer to mod.
pcReloc - ptr to count of base relocs (approximate)
Return Value:
none.
--*/
{ (*pcReloc) += pmod->cReloc; }
BOOL FCheckRefByPMOD ( PEXTERNAL pext, PMOD pmod, PST pst, DWORD *pcReloc )
/*++
Routine Description:
Checks the reference by this mod.
Arguments:
pmod - ptr to a mod
Return Value:
FALSE if mod belongs to a lib.
--*/
{ BOOL fIsLib = FIsLibPMOD(pmod);
// if referenced by a lib - return
if (fIsLib) { #ifdef INSTRUMENT
const char *szSym = SzNamePext(pext, pst); LogNoteEvent(Log, SZILINK, "data-handling", letypeEvent, "%s ref by lib %s", szSym, SzOrigFilePMOD(pmod)); #endif // INSTRUMENT
return(FALSE); }
// is it referenced by a modified file?
if (!IsDirtyPMOD(pmod)) { #ifdef INSTRUMENT
const char *szSym = SzNamePext(pext, pst); LogNoteEvent(Log, SZILINK, "data-handling", letypeEvent, "%s ref by unmod. file %s", szSym, SzOrigFilePMOD(pmod)); #endif // INSTRUMENT
// add the module to the pass2 list. no need to do pass1 on it.
assert (!FDidPass1PMOD(pmod)); if (!FDidPass1PMOD(pmod)) { pmod->LnkFlags |= MOD_NoPass1; } DoPass2PMOD(pmod, FALSE);
// count number of base relocs
// This is no good for PowerMac
CountBaseRelocsPMOD(pmod, pcReloc); }
return(TRUE); }
BOOL FRefByModFilesOnly ( PEXTERNAL pext, PST pst, DWORD *pcReloc )
/*++
Routine Description:
checks if reference list is subset of modified list.
Arguments:
pext - extern (data).
pst - pointer to symbol table.
pcReloc - ptr to count of base relocs (approximate)
Return Value:
TRUE if reference list is a subset of modified list.
--*/
{ ENM_MOD_EXT enmModExt;
// no references
if (!pext->pmodsFirst) { return(TRUE); }
// walk the reference list
InitEnmModExt(&enmModExt, pext); while (FNextEnmModExt(&enmModExt)) { if (!enmModExt.pmod) { // For ilink we remove references
continue; }
if (!FCheckRefByPMOD(pext, enmModExt.pmod, pst, pcReloc)) { return(FALSE); } }
return(TRUE); }
void CheckIfMovedDataRefByModFiles( PST pst, DWORD *pcReloc )
/*++
Routine Description:
checks if public data moved is referenced only by some subset of modified files.
Arguments:
pst - pointer to symbol table.
pcReloc - On return will hold the count of base relocs on account of including new objs for pass2.
Return Value:
None. Sets errInc as appropriate.
--*/
{ LEXT *plext = plextMovedData;
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, "data-handling", letypeBegin, NULL); #endif // INSTRUMENT
// walk the public data list
while (plext) {
PEXTERNAL pext = plext->pext;
// if the address of extern data hasn't changed, go onto next
if (pext->FinalValue != (pext->ImageSymbol.Value+pext->pcon->rva)) { // data moved!
if (fPowerMac && READ_BIT(pext, sy_TOCALLOCATED)) { // Reset bit for sy_TOCENTRYFIXEDUP so that it will
// be fixed up in Pass2
RESET_BIT(pext, sy_TOCENTRYFIXEDUP); }
if (!FRefByModFilesOnly(pext, pst, pcReloc)) { // So ensure all references are subset of modified mods.
// if not pull in objs for a pass2.
errInc = errDataMoved; break; } #ifdef INSTRUMENT
{ const char *szSym = SzNamePext(pext, pst); LogNoteEvent(Log, SZILINK, "data-handling", letypeEvent, "%s ref by mod files", szSym); } #endif // INSTRUMENT
}
plext = plext->plextNext; }
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, "data-handling", letypeEnd, NULL); #endif // INSTRUMENT
}
void ReleaseMovedDataRefList ( PIMAGE pimage ) /* ++
++ */ { LEXT *plext = plextMovedData;
// free the list of data externs
while (plextMovedData) { if (fPowerMac) { MppcFixIncrDataMove(plext->pext, pimage); } plext = plextMovedData->plextNext; FreePv(plextMovedData); plextMovedData = plext; } }
void UpdateDebugDir ( PIMAGE pimage )
/*++
Routine Description:
Updates the debug directory
Arguments:
pimage - ptr to EXE image
Return Value:
None.
--*/
{ PGRP pgrp; PCON pcon; PSEC psec;
if (pimage->Switch.Link.DebugInfo == None) { // No debug info
return; }
// find the cv signature pcon.
psec = PsecFindNoFlags(".debug", &pimage->secs); assert(psec); pgrp = PgrpFind(psec, ".debug$H"); assert(pgrp); pcon = pgrp->pconNext; assert(pcon);
// update the directory (assumes pdbfilename remains the same).
FileSeek(FileWriteHandle, pcon->foRawDataDest, SEEK_SET); FileWrite(FileWriteHandle, &nb10i, sizeof(nb10i));
// update the fpo debug directory
if (pimage->fpoi.ifpoMax) { IMAGE_DEBUG_DIRECTORY debugDir;
FileSeek(FileWriteHandle, pimage->fpoi.foDebugDir, SEEK_SET); FileRead(FileWriteHandle, &debugDir, sizeof(IMAGE_DEBUG_DIRECTORY));
debugDir.SizeOfData = pimage->fpoi.ifpoMac * sizeof(FPO_DATA);
FileSeek(FileWriteHandle, -(LONG)sizeof(IMAGE_DEBUG_DIRECTORY), SEEK_CUR); FileWrite(FileWriteHandle, &debugDir, sizeof(IMAGE_DEBUG_DIRECTORY)); } }
void UpdateImgHdrsAndComment ( PIMAGE pimage, BOOL fStripRelocs )
/*++
Routine Description:
Updates the image headers
Arguments:
pimage - ptr to EXE image
fStripRelocs - TRUE if base relocs need to be stripped.
Return Value:
None.
--*/
{ // update image hdr timestamp
_tzset(); time((time_t *)&pimage->ImgFileHdr.TimeDateStamp);
// mark it as fixed (!!!TEMPORARY!!!)
if (fStripRelocs) { pimage->ImgFileHdr.Characteristics |= IMAGE_FILE_RELOCS_STRIPPED; }
// Is it a PE image.
if (pimage->Switch.Link.fPE) { // Update the stub if necessary
if (OAStub & OA_UPDATE) { FileSeek(FileWriteHandle, 0, SEEK_SET); FileWrite(FileWriteHandle, pimage->pbDosHeader, pimage->cbDosHeader); }
CoffHeaderSeek = pimage->cbDosHeader; }
// seek and write out updated image headers
FileSeek(FileWriteHandle, CoffHeaderSeek, SEEK_SET); WriteFileHeader(FileWriteHandle, &pimage->ImgFileHdr);
// Update optional header & write out as well
if (fStripRelocs) { pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0; pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0; }
if (pextGp) { pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_GLOBALPTR].VirtualAddress = pextGp->FinalValue; }
WriteOptionalHeader(FileWriteHandle, &pimage->ImgOptHdr, pimage->ImgFileHdr.SizeOfOptionalHeader);
// update the comment as necessary
if (OAComment == OA_UPDATE || OAComment == OA_ZERO) { DWORD cbComment = __max(pimage->SwitchInfo.cbComment, blkComment.cb); BYTE *buf;
buf = (BYTE *) PvAllocZ(cbComment);
if (OAComment != OA_ZERO) { memcpy(buf, blkComment.pb, blkComment.cb); } FileSeek(FileWriteHandle, pimage->ImgFileHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER), SEEK_CUR); FileWrite(FileWriteHandle, buf, cbComment);
FreePv(buf); } }
INT IncrBuildImage ( PPIMAGE ppimage )
/*++
Routine Description:
Main driving routine that does the incremental build.
Arguments:
pimage - ptr to EXE image
Return Value:
0 on success, -1 on failure
--*/
{ BOOL fLib; BOOL fNew; BOOL fDel; PIMAGE pimage = *ppimage;
// Instantiate value for linker defined mod
pmodLinkerDefined = pimage->pmodLinkerDefined;
// Init begins
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeBegin, NULL); #endif // INSTRUMENT
// Check to see if export object has changed
if (FExpFileChanged(&pimage->ExpInfo)) { errInc = errExports;
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEnd, NULL); #endif // INSTRUMENT
if (fTest) { PostNote(NULL, EXPORTSCHANGED); }
return CleanUp(ppimage); }
// Determine set of changed files
InitModFileList(pimage, &fLib, &fNew, &fDel);
// Open the EXE image
FileWriteHandle = FileOpen(OutFilename, O_RDWR | O_BINARY, 0); fOpenedOutFilename = TRUE;
// Any changes (LATER: should update the timestamp)
if (!ModFileList.Count) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "no mod changes"); #endif // INSTRUMENT
// Update the headers alone; timestamp and user values updated.
UpdateImgHdrsAndComment(pimage, FALSE);
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEnd, NULL); #endif // INSTRUMENT
errInc = errNoChanges; return CleanUp(ppimage); }
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "count of mod files: 0x%.4x", ModFileList.Count); #endif // INSTRUMENT
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEnd, NULL); #endif // INSTRUMENT
// New files OR deletion of objs OR mod of libs punt
if (fLib || fNew || fDel) { if (fNew) { errInc = errFileAdded;
if (fTest) { PostNote(NULL, OBJADDED); } } else if (fDel) { errInc = errFileDeleted;
if (fTest) { PostNote(NULL, OBJREMOVED); } } else if (fLib) { errInc = errLibChanged;
if (fTest) { PostNote(NULL, LIBCHANGED); } }
return CleanUp(ppimage); }
// Set up for cleanup
FilenameArguments = ModFileList;
if (pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_MPPC_601) { fPowerMac = TRUE;
// Do PowerMac Specific initialization
MppcDoIncrInit(pimage); }
// Make a pass over symbol table
DBEXEC(DB_SYMREFS, DumpReferences(pimage)); // DumpReferences() needs fixing.
MarkSymbols(*ppimage); DBEXEC(DB_SYMREFS, DumpReferences(pimage));
// Do an incr pass1
InternalError.Phase = "Pass1"; IncrPass1(pimage); if (errInc != errNone) { return CleanUp(ppimage); }
if ((pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_R4000) || (pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_R10000) || (pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_ALPHA)) {
// Find the GP symbol
pextGp = SearchExternSz(pimage->pst, "_gp"); }
// Resolve weak externs
ResolveExistingWeakAndLazyExterns(pimage); ResolveWeakExterns(pimage, EXTERN_WEAK); if (errInc != errNone) { return CleanUp(ppimage); }
// Check for unresolved externals
ProcessUndefinedExternals(pimage);
// Check to see if we need to search libraries
if (errInc == errUndefinedSyms) { errInc = errNone;
// Do the library search
ResolveExternalsInLibs(pimage); if (errInc != errNone && errInc != errUndefinedSyms) { return CleanUp(ppimage); } }
// Resolve weak/lazy externs
ResolveWeakExterns(pimage, (EXTERN_WEAK|EXTERN_LAZY|EXTERN_ALIAS)); if (errInc != errNone) { return CleanUp(ppimage); }
// Check to see if we have any undefined externals
PrintUndefinedExternals(pimage->pst);
// Fail link if we have undefined symbols as in full link
if (UndefinedSymbols && !(pimage->Switch.Link.Force & ftUnresolved)) { Fatal(OutFilename, UNDEFINEDEXTERNALS, UndefinedSymbols); }
// Encountered error or we were still left with undefined symbols (REVIEW?)
assert(errInc != errUndefinedSyms); if (errInc != errNone) { return CleanUp(ppimage); }
// Alloc common PCONs
IncrAllocateCommon(pimage);
// Check to see if all the mods included need to be included
if (!fPowerMac && CheckForUnrefLibMods(pimage)) { errInc = errLibRefSetChanged; return CleanUp(ppimage); }
// Check for any multiple definitions that may cause us to full-link
CheckForMultDefns(pimage, plpextMultDef); if (errInc != errNone) { return CleanUp(ppimage); }
// no output file generated if there are multiply defined symbols unless
// /FORCE:multiple was specified. In the latter case we need to do a
// non-incremental build. Otherwise on ilinks it will give us grief.
// if (fMultipleDefinitions && !(pimage->Switch.Link.Force & ftMultiple)) {
// Fatal(OutFilename, MULTIPLYDEFINEDSYMS);
// } else if (fMultipleDefinitions && (pimage->Switch.Link.Force & ftMultiple)) {
// Warning(OutFilename, CANNOTILINKINFUTURE);
// }
if (fPowerMac) { MppcCheckIncrTables();
if (errInc != errNone) { return CleanUp(ppimage); } }
// Calculate new addresses
InternalError.Phase = "CalcPtrs"; InternalError.CombinedFilenames[0] = '\0';
IncrCalcPtrs(pimage); if (errInc != errNone) { return CleanUp(ppimage); }
if (pextGp) { psecGp = PsecFind( NULL, ReservedSection.GpData.Name, ReservedSection.GpData.Characteristics, &pimage->secs, &pimage->ImgOptHdr); CalculateGpRange();
// Align the GP pointer on a quad-word. This is assumed for Alpha.
pextGp->FinalValue = pextGp->ImageSymbol.Value = ((rvaGp + rvaGpMax) / 2) & ~7; }
if (pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_ALPHA) { DWORD rvaCur = pimage->ImgOptHdr.BaseOfCode;
if (CalculateTextSectionSize(pimage, rvaCur) >= 0x400000) { fAlphaCheckLongBsr = TRUE; } }
if (fPowerMac) { CollectAndSort(pimage); }
// Check if data movement is a problem
CheckIfMovedDataRefByModFiles(pimage->pst, &cReloc); if (errInc != errNone) { return CleanUp(ppimage); }
if (!fPowerMac && !fNoBaseRelocs) { // alloc space for collecting the base relocs
// For PowerMac, this is done in MppcCheckIncrTables()
if (pimage->pdatai.ipdataMac) { unsigned long oldCrelFree = pimage->bri.crelFree; DeleteBaseRelocs(&pimage->bri, psecException->rva, pimage->pdatai.ipdataMac * sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)); cReloc += pimage->bri.crelFree - oldCrelFree; } pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size += cReloc; pbrCur = rgbr = (BASE_RELOC *) PvAlloc(pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size * sizeof(BASE_RELOC));
pbrEnd = rgbr + pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; }
if (pimage->fpoi.ifpoMax) { // Init fpo handler
FPOInit(pimage->fpoi.ifpoMax); }
if (pimage->pdatai.ipdataMax) { // Init pdata handler
PDATAInit(pimage->pdatai.ipdataMax); }
if (fPowerMac) { FixupEntryInitTerm(pextEntry, pimage); MppcIncrFixExportDescriptors(pimage); }
// Pass2 of changed mods
InternalError.Phase = "IncrPass2";
#ifdef INSTRUMENT
// log begin of pass2
LogNoteEvent(Log, SZILINK, SZPASS2, letypeBegin, NULL); #endif // INSTRUMENT
// Allocate space for CvInfo Structs: needed for linenum info
CvInfo = (PCVINFO) PvAllocZ(Cmod(pimage->libs.plibHead) * sizeof(CVINFO));
Pass2(pimage);
if (errInc != errNone) { return CleanUp(ppimage); }
ReleaseMovedDataRefList(pimage);
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZPASS2, letypeEnd, NULL); #endif // INSTRUMENT
// update fpo info
if (pimage->fpoi.ifpoMax) { WriteFpoRecords(&pimage->fpoi, pgrpFpoData->foRawData); if (errInc != errNone) { return CleanUp(ppimage); } }
// handle debug info
UpdateDebugDir(pimage); if (pimage->pdatai.ipdataMax) { if (fPowerMac) { assert(pgrpPdata); WritePdataRecords(&pimage->pdatai, pgrpPdata->foRawData); MppcFixCxxEHTableOnILink (pimage); } else { WritePdataRecords(&pimage->pdatai, psecException->foRawData); }
if (errInc != errNone) { return CleanUp(ppimage); }
pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = pimage->pdatai.ipdataMac * sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY);
assert(pimage->ImgOptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress == (fPowerMac ? pgrpPdata->rva : psecException->rva)); }
// map file changes (or regenerate a new one) (LATER)
// based relocs
InternalError.Phase = "EmitRelocations"; InternalError.CombinedFilenames[0] = '\0';
if (fPowerMac) { MppcUpdateRelocTable(pimage);
// Free the deleted Base Reloc List for PowerMac
FreeArgumentList(&ZappedBaseRelocList); } else if (!fNoBaseRelocs) { EmitRelocations(pimage); }
if (fAlphaCheckLongBsr) { assert(pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_ALPHA); EmitAlphaThunks(); }
if (errInc != errNone) { return CleanUp(ppimage); }
// Update image headers as needed
InternalError.Phase = "UpdateImgHdrsAndComment"; UpdateImgHdrsAndComment(pimage, FALSE);
return CleanUp(ppimage); }
INT CleanUp ( PPIMAGE ppimage )
/*++
Routine Description:
cleanup routine. errInc is global.
Arguments:
pimage - ptr to EXE image
Return Value:
0 on success, -1 on failure
--*/
{ BOOL fNotified = FALSE;
InternalError.Phase = "FinalPhase";
switch (errInc) {
case errOutOfDiskSpace: fNotified = TRUE; case errOutOfMemory: if (!fNotified) { if (fTest) { PostNote(NULL, INTLIMITEXCEEDED); }
fNotified = TRUE; } case errFpo: case errPdata: if (!fNotified) { if (fTest) { PostNote(NULL, PADEXHAUSTED); }
fNotified = TRUE; } case errTypes: if (!fNotified) { if (fTest) { PostNote(NULL, PRECOMPREQ); }
fNotified = TRUE; } case errDbiFormat: if (!fNotified) { if (fTest) { PostNote(NULL, DBIFORMAT); }
fNotified = TRUE; } case errDataMoved: if (!fNotified) { if (fTest) { PostNote(NULL, SYMREFSETCHNG); }
fNotified = TRUE; } case errCalcPtrs: if (!fNotified) { if (fTest) { PostNote(NULL, PADEXHAUSTED); }
fNotified = TRUE; }
// close up the image file
if (FileWriteHandle) { FileClose(FileWriteHandle, TRUE); FileWriteHandle = 0; }
case errUndefinedSyms: if (!fNotified) { if (fTest) { PostNote(NULL, SYMREFSETCHNG); }
fNotified = TRUE; } case errWeakExtern: if (!fNotified) { if (fTest) { PostNote(NULL, SYMREFSETCHNG); }
fNotified = TRUE; } case errCommonSym: if (!fNotified) { if (fTest) { PostNote(NULL, BSSCHNG); }
fNotified = TRUE; } case errAbsolute: if (!fNotified) { if (fTest) { PostNote(NULL, ABSSYMCHNG); }
fNotified = TRUE; } case errMultDefFound: if (!fNotified) { fNotified = TRUE; } case errLibRefSetChanged: if (!fNotified) { if (fTest) { PostNote(NULL, LIBREFSETCHNG); }
fNotified = TRUE; } case errComdat: if (!fNotified) { if (fTest) { PostNote(NULL, DIFFCOMDATS); }
fNotified = TRUE; } case errJmpTblOverflow: case errTocTblOverflow: case errDescOverflow: if (!fNotified) { if (fTest) { PostNote(NULL, PADEXHAUSTED, NULL, NULL); }
fNotified = TRUE; } case errDirectives: if (!fNotified) { if (fTest) { PostNote(NULL, DIFFDIRECTIVES, NULL, NULL); }
fNotified = TRUE; } case errBaseReloc: if (!fNotified) { if (fTest) { PostNote(NULL, PADEXHAUSTED); }
fNotified = TRUE; } case errTooManyChanges: case errFileAdded: case errFileDeleted: case errLibChanged: case errExports: #ifdef ILINKLOG
IlinkLog((UINT)-1); #endif // ILINKLOG
// restore state
fIncrDbFile = FALSE;
// close up the inc file & delete it
FreeImage(ppimage, TRUE); remove(szIncrDbFilename);
// remove any temporary files
FileCloseAll(); RemoveConvertTempFiles();
if (ModFileList.Count) { FreeArgumentList(&ModFileList); }
// return
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, NULL, letypeEvent, "failed"); #endif // INSTRUMENT
return -1;
case errNone: #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, NULL, letypeEvent, "success"); #endif // INSTRUMENT
case errNoChanges: FileClose(FileWriteHandle, TRUE); FileWriteHandle = 0; SaveImage(*ppimage); return 0;
default: assert(0); // uhhuh!
} // end switch
assert(0); // nahnah!
return(-1); }
#if DBG
void DumpJmpTbl ( PCON pcon, PVOID pvRaw )
/*++
Routine Description:
Dumps the jump table contents.
Arguments:
pvRaw - pointer to raw data
Return Value:
None.
--*/
{ BYTE *p;
DumpPCON(pcon);
p = (BYTE *) pvRaw;
DBPRINT("---------BEGIN OF JMP TBL--------------\n");
for (;pcon->cbRawData > (DWORD) (p - (BYTE *) pvRaw);) { if (fPowerMac) { DWORD offset = DwSwap(*(DWORD *) p);
// Opcode is in the first 6 bits for PowerMac
DBPRINT("OP= 0x%.02x, ADDR=0x%.8lx\n", (offset >> 24) & 0xFC, (offset << 6) >> 6); } else { DBPRINT("OP= 0x%.02x, ADDR=0x%.8lx\n", *p, *(LONG UNALIGNED *) (p+ilink_info->address_offset)); }
p += CbJumpEntry(); }
DBPRINT("----------END OF JMP TBL---------------\n"); }
void DumpReferences ( PIMAGE pimage )
/*++
Routine Description:
Dumps all references.
Arguments:
pimage - ptr to EXE image
Return Value:
None.
--*/
{ PEXTERNAL pext; INT i = 0;
DBPRINT("---------BEGIN OF SYMBOL REFERENCES--------------\n");
// walk the external symbol table
InitEnumerateExternals(pimage->pst); while ((pext = PexternalEnumerateNext(pimage->pst)) != NULL) { ENM_MOD_EXT enmModExt;
if ((pext->Flags & EXTERN_DEFINED) == 0) { // Ignore whatever needs to be ignored
continue; }
DBPRINT("sym= %s\n referenced by= ", SzNamePext(pext, pimage->pst));
// display references from modified files
InitEnmModExt(&enmModExt, pext); while (FNextEnmModExt(&enmModExt)) { PMOD pmod = enmModExt.pmod;
if (pmod == NULL) { continue; // for ilink we remove references
}
DBPRINT("%s ", FIsLibPMOD(pmod) ? pmod->plibBack->szName : SzOrigFilePMOD(pmod)); if (++i > 2) { DBPRINT("\n"); i = 0; } }
if (i) { DBPRINT("\n"); } } TerminateEnumerateExternals(pimage->pst);
DBPRINT("---------END OF SYMBOL REFERENCES--------------\n"); }
#endif // DBG
// REVIEW: assumes only one export object per DLL.
void SaveExpFileInfo ( PEXPINFO pei, const char *szFile, DWORD ts, BOOL fExpObj )
/*++
Routine Description:
Saves export object info (DEF file or export object).
Arguments:
pei - ptr to export info
szFile - name of file
ts - timestamp
fExpObj - TRUE if file is an export object
Return Value:
None.
--*/
{ assert(szFile);
// an .exp file wasn't used
if (!fExpObj) { struct _stat statfile;
szFile = Strdup(szFile); assert(!ts); if (_stat(szFile, &statfile) == -1) { Fatal(NULL, CANTOPENFILE, szFile); } ts = statfile.st_mtime; } assert(ts);
// fill in fields
pei->szExpFile = szFile; pei->tsExp = ts; }
void SaveExportInfo ( PIMAGE /* pimage */, const char *szDef, PEXPINFO pei )
/*++
Routine Description:
Saves any exports for the dll/exe into the incr db (private heap)
Arguments:
psecs - pointer to sections list in image map
szDef - name of DEF file
pei - ptr to export info
Return Value:
None.
--*/
{ if (pgrpExport->pconNext != NULL) { PMOD pmod;
// An export object was used
pmod = PmodPCON(pgrpExport->pconNext);
SaveExpFileInfo(pei, SzOrigFilePMOD(pmod), pmod->TimeStamp, 1); return; }
// DEF file was used
if (szDef && szDef[0] != '\0') { SaveExpFileInfo(pei, szDef, 0, 0); }
// save exports specified via cmd line only; directives checked separately
if (ExportSwitches.Count) { PARGUMENT_LIST parg; WORD i;
for (i = 0, parg = ExportSwitches.First; i < ExportSwitches.Count; i++, parg = parg->Next) {
if (parg->ModifiedName) { // This export was specified in a directive
continue; }
AddArgToListOnHeap(&pei->nlExports, parg); } } }
BOOL FExpFound ( PARGUMENT_LIST parg )
/*++
Routine Description:
Looks for the export in the current export list.
Arguments:
parg - ptr to an export entry.
Return Value:
TRUE if found else FALSE
--*/
{ if (FArgOnList(&ExportSwitches, parg)) { return(TRUE); }
// Not found
#ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, NULL, letypeEvent, "exports changed: %s not found", parg->OriginalName); #endif // INSTRUMENT
return(FALSE); }
BOOL FIsModFile ( PARGUMENT_LIST parg )
/*++
Routine Description:
Checks to see if this directive is from a modified file. REVIEW: currently works for objs only & not libs.
Arguments:
parg - ptr to an export entry.
Return Value:
TRUE if export belongs to a modified file.
--*/
{ PMOD pmod; char szBuf[_MAX_PATH * 2];
assert(parg); assert(parg->ModifiedName); pmod = plibModObjs->pmodNext; // walk the list of modified objs
while (pmod) {
// generate the combined name
SzComNamePMOD(pmod, szBuf);
// compare names
if (!_tcsicmp(szBuf, parg->ModifiedName)) { return 1; }
pmod = pmod->pmodNext; }
// didn't find mod
return 0; }
BOOL FExportsChanged ( PEXPINFO pei, BOOL fCmd )
/*++
Routine Description:
Checks to see if any exports have changed since last link. REVIEW: assumes changes in DEF/export file detected already.
Arguments:
pei - ptr to export info
fCmd - TRUE if test is for cmdline exports only.
Return Value:
TRUE if there were changes else FALSE
--*/
{ NAME_LIST nl; PARGUMENT_LIST parg; WORD i, cexp;
// no exp file was gen.(=> cmdline & directives ignored)
if (!pei->pmodGen && pei->szExpFile) { WarningIgnoredExports(pei->szExpFile); return(FALSE); }
nl = pei->nlExports;
// compare the two lists
cexp = 0; for (i = 0, parg = nl.First; i < nl.Count; i++, parg = parg->Next) {
// ignore directives when checking cmdline exports
if (fCmd && parg->ModifiedName) { continue; }
// if we are not checking cmdline exports, ignore them
if (!fCmd && !parg->ModifiedName) { ++cexp; // need to count
continue; }
// check directives of modified files only
if (parg->ModifiedName && !FIsModFile(parg)) { continue; }
cexp++;
if (!FExpFound(parg)) { return(TRUE); } }
// counts must be the same
if (cexp != ExportSwitches.Count) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, NULL, letypeEvent, "export count was 0x%.4x, is 0x%.4x", cexp, ExportSwitches.Count); #endif // INSTRUMENT
return(TRUE); }
return(FALSE); }
BOOL FGenFileModified ( const char *szOrig, const char *szNew, DWORD ts )
/*++
Routine Description:
Checks to see if linker generated files are same (name & ts).
Arguments:
szOrig - original name of file
szNew - newly specified name
ts - timestamp of original file
Return Value:
1 if changed else 0
--*/
{ struct _stat statfile;
if (_tcsicmp(szOrig, szNew)) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "file %s replaced by %s", szOrig, szNew); #endif // INSTRUMENT
return 1; }
if (_stat(szOrig, &statfile) == -1) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "file not accessible: %s", szNew); #endif // INSTRUMENT
return 1; }
if (ts != (DWORD)statfile.st_mtime) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "file modified: %s", szOrig); #endif // INSTRUMENT
return 1; }
return 0; // no changes
}
BOOL FExpFileChanged ( PEXPINFO pei )
/*++
Routine Description:
Checks to see if export-object/DEF-file was updated between links.
Caveat: assumes that if export object was used, DEF file is ignored.
Arguments:
pei - ptr to export info
Return Value:
1 if changed else 0
--*/
{ // an export object was used; if a new exports obj is added it will get detected
// as a new file being added
if (!pei->pmodGen && pei->szExpFile) { PARGUMENT_LIST parg;
parg = PargFindSz(pei->szExpFile, &FilenameArguments); if (!parg) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "exp obj %s was used, now not", pei->szExpFile); #endif // INSTRUMENT
return(TRUE); }
assert(parg); if (parg->TimeStamp != pei->tsExp) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "exp obj modified: %s", parg->OriginalName); #endif // INSTRUMENT
return(TRUE); }
return(FALSE); }
// an exports object was generated. check for any changes to it and the correspodning import lib if any
if (pei->pmodGen) { char szDrive[_MAX_DRIVE]; char szDir[_MAX_DIR]; char szFname[_MAX_FNAME]; char szExt[_MAX_EXT]; char szImplibFilename[_MAX_FNAME]; const char *szImplibT; PMOD pmod;
pmod = pei->pmodGen; if (FGenFileModified(SzOrigFilePMOD(pmod), SzOrigFilePMOD(pmod), pmod->TimeStamp)) { return(TRUE); }
assert(pei->szImpLib); assert(pei->tsImpLib); if ((szImplibT = ImplibFilename) == NULL) { _splitpath(OutFilename, szDrive, szDir, szFname, szExt); _makepath(szImplibFilename, szDrive, szDir, szFname, ".lib");
szImplibT = szImplibFilename; }
if (FGenFileModified(pei->szImpLib, szImplibT, pei->tsImpLib)) { return(TRUE); } }
// a DEF file was used previously;
if (pei->pmodGen && pei->szExpFile) { struct _stat statfile;
if (DefFilename == NULL || DefFilename[0] == '\0') { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, ".def file %s no longer used", DefFilename, 0); #endif // INSTRUMENT
return(TRUE); // DEF file no longer used
}
if (_tcsicmp(pei->szExpFile, DefFilename)) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, "file %s replaced by %s", pei->szExpFile, DefFilename); #endif // INSTRUMENT
return(TRUE); // DEF file replaced
}
if (_stat(pei->szExpFile, &statfile) == -1) { Fatal(NULL, CANTOPENFILE, DefFilename); // DEf file not found
}
if (pei->tsExp != (DWORD)statfile.st_mtime) { #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, SZINIT, letypeEvent, ".def file modified %s", pei->szExpFile, 0); #endif // INSTRUMENT
return(TRUE); }
return(FALSE); // no changes
}
// check if a DEF file was specified for the first time
if (DefFilename != NULL && DefFilename[0] != '\0') { return(TRUE); }
// neither was used (note: changes in export specs via
// cmdline get detected while pocessing cmdline options).
return(FALSE); }
void WriteFpoRecords ( FPOI *pfpoi, DWORD foFpo )
/*++
Routine Description:
Updates/writes out the fpo information.
Arguments:
pfpoi - ptr to fpo info.
foFpo - file ptr to fpo.
Return Value:
None.
--*/
{ BOOL fMapped; DWORD cbFpo = pfpoi->ifpoMax * sizeof(FPO_DATA); PVOID pvRawData = PbMappedRegion(FileWriteHandle, foFpo, cbFpo);
fMapped = pvRawData != NULL ? TRUE : FALSE;
// if not mapped, alloc space for fpo records
if (!fMapped) { pvRawData = PvAlloc(cbFpo);
// on an ilink read in fpo records
if (fIncrDbFile) { FileSeek(FileWriteHandle, foFpo, SEEK_SET); FileRead(FileWriteHandle, pvRawData, cbFpo); } }
pfpoi->rgfpo = (PFPO_DATA) pvRawData; if (!FPOUpdate(pfpoi)) {
// on a full build, no failures expected
if (!fIncrDbFile) { Fatal(NULL, INTERNAL_ERR); }
errInc = errFpo; #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, "fpo", letypeEvent, "fpo pad overflow"); #endif // INSTRUMENT
if (!fMapped) { FreePv(pvRawData); }
return; }
// zero out unused fpo entries (padding)
memset((BYTE *)pvRawData + (pfpoi->ifpoMac * sizeof(FPO_DATA)), 0, cbFpo - (pfpoi->ifpoMac * sizeof(FPO_DATA)));
// if not mapped, need to write out the updated fpo records
if (!fMapped) { FileSeek(FileWriteHandle, foFpo, SEEK_SET); FileWrite(FileWriteHandle, pvRawData, cbFpo); FreePv(pvRawData); } }
void WritePdataRecords ( PDATAI *ppdatai, DWORD foPdata )
/*++
Routine Description:
Updates/writes out the pdata information.
Arguments:
ppdatai - ptr to pdata info.
foPdata - file ptr to pdata.
Return Value:
None.
--*/
{ BOOL fMapped; DWORD cbPdata = ppdatai->ipdataMax * sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY); PVOID pvRawData = PbMappedRegion(FileWriteHandle, foPdata, cbPdata);
fMapped = (pvRawData != NULL);
// if not mapped, alloc space for Pdata records
if (!fMapped) { pvRawData = PvAlloc(cbPdata);
// on an ilink read in Pdata records
if (fIncrDbFile) { FileSeek(FileWriteHandle, foPdata, SEEK_SET); FileRead(FileWriteHandle, pvRawData, cbPdata); } }
ppdatai->rgpdata = (PIMAGE_RUNTIME_FUNCTION_ENTRY) pvRawData; if (!PDATAUpdate(ppdatai)) { // On a full build, no failures expected
if (!fIncrDbFile) { Fatal(NULL, INTERNAL_ERR); }
errInc = errPdata; #ifdef INSTRUMENT
LogNoteEvent(Log, SZILINK, "pdata", letypeEvent, "pdata pad overflow"); #endif // INSTRUMENT
if (!fMapped) { FreePv(pvRawData); }
return; }
// if not mapped, need to write out the updated fpo records
if (!fMapped) { FileSeek(FileWriteHandle, foPdata, SEEK_SET); FileWrite(FileWriteHandle, pvRawData, cbPdata); FreePv(pvRawData); } }
void SaveDirectiveSz ( const char *Sz, PST pstDirective, PMOD pmod )
/*++
Routine Description:
Save the directive.
Arguments:
Sz - directive string.
pstDirective - symbol table holding all directives.
pmod - ptr to mod that had the directives
Return Value:
None.
--*/
{ PEXTERNAL pext = LookupExternSz(pstDirective, Sz, NULL); PLEXT plextNew = (PLEXT) Malloc(sizeof(LEXT));
plextNew->pext = pext; plextNew->plextNext = pmod->plextDirectives; pmod->plextDirectives = plextNew; pmod->cDirectives++; }
BOOL FVerifyDirectivesPMOD ( PIMAGE pimage, PMOD pmod, PNAME_LIST pnl )
/*++
Routine Description:
Verifies that the directives for the given mod haven't changed.
Arguments:
pimage - ptr to image.
pmod - ptr to mod that had the directives
pnl - the new set of directives seen in this mod.
Return Value:
None.
--*/
{ WORD i; PARGUMENT_LIST pal; PMOD pmodO;
// For library modules, ensure no new directives encountered (no changes possible)
if (FIsLibPMOD(pmod)) {
for (i = 0, pal = pnl->First; i < pnl->Count; i++, pal = pal->Next) {
PEXTERNAL pext = SearchExternSz(pimage->pstDirective, pal->OriginalName); if (!pext) { errInc = errDirectives; return(FALSE); } } // end for
return(TRUE); }
pmodO = PmodFind(pimage->plibCmdLineObjs, SzOrigFilePMOD(pmod), 0); assert(pmodO); pmod->cDirectives = pmodO->cDirectives; pmod->plextDirectives = pmodO->plextDirectives; // check the count
if (pnl->Count != pmod->cDirectives) { errInc = errDirectives; return(FALSE); }
// compare individual directives
for (i = 0, pal = pnl->First; i < pnl->Count; i++, pal = pal->Next) {
PEXTERNAL pext = SearchExternSz(pimage->pstDirective, pal->OriginalName); if (!pext) { errInc = errDirectives; return(FALSE); }
if (!PlextFind(pmod->plextDirectives, pext)) { errInc = errDirectives; return(FALSE); } }
return(TRUE); }
void MarkExtern_FuncFixup ( PIMAGE_SYMBOL psym, PIMAGE pimage, PCON pcon )
/*++
Routine Description:
Mark the extern representing this symbol as having a non-lego kind of fixup (eg. func_sym+offset).
Arguments:
psym - ptr to symbols
pimage - ptr to image
pcon - contribution of the fixup
Return Value:
None.
--*/
{ PEXTERNAL pext; const char *szName;
// fetch the extern representing this sym. Note that the offsets to
// long names are into the image long name table & not the object's.
szName = SzNameSymPst((*psym), pimage->pst); pext = SearchExternSz(pimage->pst, szName);
assert(pext); assert(pext->pcon);
// If the fixup is in the same mod as the definition, nothing to do
if (PmodPCON(pext->pcon) == PmodPCON(pcon)) { return; }
// mark the extern
pext->Flags |= EXTERN_FUNC_FIXUP; }
//
// support routines for ensuring that all modules currently included in image
// as a result of library search are still required/accessed
//
// Algorithm: A garbage collection algorithm "mark & sweep" is used here
// to figure out if any mods are unreferenced.
//
static PLMOD plmodRefModList;
void WalkRefListPMOD ( PMOD pmod, PIMAGE pimage )
/*++
Routine Description:
walks the list of all references made by this MOD.
Arguments:
pmod - ptr to MOD
pimage - ptr to image
Return Value:
None.
--*/
{ ENM_EXT_LIST enmExtList;
pmod->fInclude = TRUE;
InitEnmExtList(&enmExtList, pmod->plpextRef); while (FNextEnmExtList(&enmExtList)) { PMOD pmodT;
if (!enmExtList.pext) { continue; // sometimes linker removes references (for bss)
}
if (enmExtList.pext->Flags & EXTERN_IGNORE) { continue; // ignorable syms eg. syms no longer in use
}
if (enmExtList.pext->ImageSymbol.SectionNumber == IMAGE_SYM_ABSOLUTE) { pmodT = FindPmodDefiningSym(pimage->psdAbsolute, enmExtList.pext); } else { pmodT = PmodPCON(enmExtList.pext->pcon); }
// if pmod is pmodLinkerDefined find actual mod defining it (for bss)
if (pmodT == pmodLinkerDefined) { pmodT = FindPmodDefiningSym(pimage->psdCommon, enmExtList.pext); if (!pmodT) { continue; // common was defined in a cmdline obj; we don't care
} }
// add the referenced MOD to list if it hasn't been processed
assert(pmodT); if (!pmodT->fInclude) { AddToPLMODList(&plmodRefModList, pmodT); } } EndEnmExtList(&enmExtList); }
void MarkAllRefPMODs ( PIMAGE pimage )
/*++
Routine Description:
marks all MODs in libs that are referenced
Arguments:
pimage - ptr to image
Return Value:
None.
--*/
{ ENM_MOD enm_mod; PLMOD plmod;
// Add all command line objs to list of referenced objs
InitEnmMod(&enm_mod, pimage->plibCmdLineObjs); while (FNextEnmMod(&enm_mod)) { AddToPLMODList(&plmodRefModList, enm_mod.pmod); } EndEnmMod(&enm_mod);
// Add the mod contributing the entrypoint
if (pimage->pmodEntryPoint) { AddToPLMODList(&plmodRefModList, pimage->pmodEntryPoint); }
// walk the list of MODs that are referenced
while (plmodRefModList) { // pop the mod from the list
plmod = plmodRefModList; plmodRefModList = plmodRefModList->plmodNext;
// walk its references
WalkRefListPMOD(plmod->pmod, pimage); FreePv(plmod); } }
// Currently this is to handle the case where objs from oldnames.lib
// always appear to be not referenced.
BOOL FHasSideEffectsPMOD( PMOD pmod ) { ENM_SRC enmSrc;
// For now even if 1 contrib is in the image, assume
// it could have side effects
InitEnmSrc(&enmSrc, pmod); while (FNextEnmSrc(&enmSrc)) { if (!(enmSrc.pcon->flags & IMAGE_SCN_LNK_REMOVE) && enmSrc.pcon->cbRawData) return(TRUE); }
return(FALSE); }
BOOL AnyUnrefLibMods( PIMAGE pimage )
/*++
Routine Description:
checks to see if there are any unreferenced lib mods
Arguments:
pimage - ptr to image
Return Value:
TRUE if there is even 1 mod not referenced anymore.
--*/
{ ENM_LIB enm_lib;
InitEnmLib(&enm_lib, pimage->libs.plibHead); while (FNextEnmLib(&enm_lib)) { ENM_MOD enm_mod;
if (enm_lib.plib->flags & LIB_DontSearch) { continue; }
InitEnmMod(&enm_mod, enm_lib.plib); while (FNextEnmMod(&enm_mod)) { if (!enm_mod.pmod->fInclude && FHasSideEffectsPMOD(enm_mod.pmod)) { EndEnmMod(&enm_mod); EndEnmLib(&enm_lib); return(TRUE); } } EndEnmMod(&enm_mod); } EndEnmLib(&enm_lib);
return(FALSE); }
BOOL CheckForUnrefLibMods ( PIMAGE pimage )
/*++
Routine Description:
checks to see if there are any unreferenced lib mods
Arguments:
pimage - ptr to image
Return Value:
TRUE if there is a library MOD no longer referenced
--*/
{ InternalError.Phase = "CheckForUnrefLibMods"; InternalError.CombinedFilenames[0] = '\0';
MarkAllRefPMODs(pimage); return AnyUnrefLibMods(pimage); }
//
// support for sym defn handling
//
void RecordSymDef ( SYM_DEF **ppsd, PEXTERNAL pext, PMOD pmod ) { PSYM_DEF psd = (*ppsd);
while (psd) { if (psd->pext == pext) { psd->pmod = pmod; // update pmod if already in list (for bss redfn)
return; } psd = psd->psdNext; }
psd = (SYM_DEF *) Malloc(sizeof(SYM_DEF)); psd->pext = pext; psd->pmod = pmod;
psd->psdNext = (*ppsd); (*ppsd) = psd; }
PMOD FindPmodDefiningSym ( SYM_DEF *psd, PEXTERNAL pext ) { while (psd) { if (psd->pext == pext) { return psd->pmod; } psd = psd->psdNext; }
return NULL; }
// functions for ensuring that multiple definitions in libs aren't a problem
//
// Algorithm: The linker builds up a list of externs. If any on this list have
// multiple definitions than the behavior of the linker could be different on
// a full build. The list comprises only the symbols that are referenced by cmdline
// objs, already defined in a lib that weren't referenced by cmdline objs till now.
// These externs potentially can cause differences in behavior if they have definitions
// in a library before the library they were found in.
BOOL IsSameDefn ( PEXTERNAL pext, DWORD foMember, PLIB plib ) { assert(FIsLibPCON(pext->pcon));
// convert foMember to value past arhive member (see ReadArchiveMemberHeader())
foMember = EvenByteAlign(foMember) + sizeof(IMAGE_ARCHIVE_MEMBER_HEADER);
// two defns are same if they came from same lib & mod
return(PmodPCON(pext->pcon)->plibBack == plib && PmodPCON(pext->pcon)->foMember == foMember); }
void MultDefFound ( PIMAGE pimage, PLIB plib, PLPEXT plpext ) { ENM_EXT_LIST enmExtList;
if (plib->szName != NULL) { // hack-o-rama: temp temp
char szFname[_MAX_FNAME]; char szExt[_MAX_EXT]; char szPath[_MAX_PATH];
_splitpath(plib->szName, NULL, NULL, szFname, szExt); strcpy(szPath, szFname); strcat(szPath, szExt);
if (!_ftcsicmp(szPath, "oldnames.lib")) { return; } }
// prep lib for searching
PrepLibForSearching(pimage, plib);
// walk the list of symbols that could potentially have multiple defns.
InitEnmExtList(&enmExtList, plpext); while (FNextEnmExtList(&enmExtList)) { const char *szSym; char **pszEntry; DWORD isz; BOOL fFound; WORD iszIntMem; DWORD iusOffIndex; DWORD foMember;
if (!enmExtList.pext) { continue; }
szSym = SzNamePext(enmExtList.pext, pimage->pst);
// search for the sym
if (plib->flags & LIB_NewIntMem) { pszEntry = (char **) bsearch(&szSym, plib->rgszSym, (size_t) plib->csymIntMem, sizeof(char *), Compare);
fFound = (pszEntry != NULL); } else { fFound = FALSE;
for (isz = 0; isz < plib->csymIntMem; isz++) { if (!strcmp(plib->rgszSym[isz], szSym)) { fFound = TRUE; break; } } }
if (!fFound) { continue; }
// get the offset of mod that defines it
if (plib->flags & LIB_NewIntMem) { iszIntMem = (WORD) (pszEntry - plib->rgszSym); iusOffIndex = plib->rgusOffIndex[iszIntMem]; foMember = plib->rgulSymMemOff[iusOffIndex]; } else { iszIntMem = (WORD) isz; foMember = sgetl(&plib->rgulSymMemOff[iszIntMem]); }
// does the current defn match with the defn just found?
if (!IsSameDefn(enmExtList.pext, foMember, plib)) { errInc = errMultDefFound;
if (fTest) { char *szOutput = SzOutputSymbolName(szSym, TRUE);
PostNote(NULL, MULTDEFNFOUND, szOutput);
if (szSym != szOutput) { FreePv(szOutput); } }
return; }
DelExtFromList(plpext, enmExtList.pext); } }
void CheckForMultDefns ( PIMAGE pimage, PLPEXT plpextMultDef ) { ENM_LIB enm_lib;
InternalError.Phase = "CheckForMultDefns"; InternalError.CombinedFilenames[0] = '\0';
// list empty
if (IsExtListEmpty(plpextMultDef)) { return; }
// search the libs for any multiple definitions
InitEnmLib(&enm_lib, pimage->libs.plibHead); while (FNextEnmLib(&enm_lib)) {
if (enm_lib.plib->flags & LIB_DontSearch) { continue; }
MultDefFound(pimage, enm_lib.plib, plpextMultDef);
// check if we hit a multiple defn
if (errInc != errNone) { return; }
// no more externs to lookup
if (IsExtListEmpty(plpextMultDef)) { return; } } }
BOOL NoRefsByCmdLineObjs ( PEXTERNAL pext ) { ENM_MOD_EXT enmModExt;
if (!pext->pmodOnly) { // No references
return(TRUE); }
// walk the list of referencing MODs kept at each sym
InitEnmModExt(&enmModExt, pext); while (FNextEnmModExt(&enmModExt)) {
if (!enmModExt.pmod) { continue; // for ilink we remove references
}
if (!FIsLibPMOD(enmModExt.pmod)) { return(FALSE); } }
return(TRUE); }
void AddExtToMultDefList ( PEXTERNAL pext, PIMAGE pimage ) { assert(pext->Flags & EXTERN_DEFINED);
// ignore absolutes (REVIEW: ignore?)
if (pext->ImageSymbol.SectionNumber == IMAGE_SYM_ABSOLUTE) { return; }
// ignore COMMON (don't give rise to multiple definition errors)
if (pext->Flags & EXTERN_COMMON) { return; }
if (fPowerMac && !pext->pcon) { // This could be a descriptor which just got created
// We are already making sure that there are no duplicates
char *szSym = SzNamePext(pext, pimage->pst); if (*szSym == '.') { return; } }
assert(pext->pcon);
if (FIsLibPMOD(PmodPCON(pext->pcon)) && NoRefsByCmdLineObjs(pext)) { AddExtToList(plpextMultDef, TRUE, pext); } }
|