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