Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6139 lines
133 KiB

/***********************************************************************
* 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 *)&ltime);
char *szTime = ctime(&ltime);
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);
}
}