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

1098 lines
20 KiB

/***********************************************************************
* Microsoft (R) 32-Bit Incremental Linker
*
* Copyright (C) Microsoft Corp 1992-1996. All rights reserved.
*
* File: tce.cpp
*
* File Comments:
*
* Transitive Comdat Elimination (TCE).
*
***********************************************************************/
#include "link.h"
#define FPcodeSym(sym) ((sym).Type & IMAGE_SYM_TYPE_PCODE)
PCON pconHeadGraph;
PENT pentHeadImage;
STATIC LHEAP lheap; // local heap for TCE allocation
#define PvNew(cblk, cb) PvAllocLheap(&lheap, (cblk) * (cb))
char *strdup_TCE(const char *szIn)
{
char *szOut = (char *) PvNew(1, strlen(szIn) + 1);
strcpy(szOut, szIn);
return szOut;
}
void
Init_TCE(VOID)
/*++
Routine Description:
Initialize the TCE engine.
Return Value:
None.
--*/
{
// Initialize the local heap
InitLheap(&lheap);
}
void
Cleanup_TCE(VOID)
{
FreeLheap(&lheap);
}
void
InitNodPmod(
IN PMOD pmod)
/*++
Routine Description:
Create NOD array for a module
Arguments:
pmod - module node in image/driver map
Return Value:
pointer to a new TOD
--*/
{
pmod->rgnod = (PNOD) PvNew(pmod->ccon, sizeof(NOD));
}
__inline PNOD
PnodPcon(
PCON pcon)
{
PMOD pmod;
PCON rgcon;
PNOD pnod;
assert(pcon);
pmod = PmodPCON(pcon);
rgcon = RgconPMOD(pmod);
if ((pcon < rgcon) || (pcon > (rgcon + pmod->ccon))) {
// Individually allocated CONs are followed by their NODs
pnod = (PNOD) (pcon+1);
} else {
WORD icon;
icon = (WORD) (pcon - rgcon);
assert(pcon == RgconPMOD(pmod) + icon);
pnod = RgnodPMOD(pmod) + icon;
}
return(pnod);
}
void
InitNodPcon(
PCON pcon,
const char *sz,
BOOL fReferenced)
/*++
Routine Description:
Create a NOD, which is a node in the TCE graph.
Arguments:
pcon - contribution node in image/driver map corresponding to the NOD
to be created
sz - comdat name
--*/
{
PNOD pnod;
pnod = PnodPcon(pcon);
if (PmodPCON(pcon) != pmodLinkerDefined) {
// UNDONE: Is support for >= 64K relocs needed here?
pnod->cedg = CRelocSrcPCON(pcon);
if (pnod->cedg != 0) {
pnod->rgedg = (PEDG) PvNew(pnod->cedg, sizeof(EDG));
}
}
if (sz) {
pnod->sz = strdup_TCE(sz);
}
if (fReferenced) {
pcon->flags |= TCE_Referenced;
}
if (pconHeadGraph) {
pnod->pconNext = pconHeadGraph;
}
pconHeadGraph = pcon;
}
PEDG
PedgNew_TCE(
DWORD isym,
PCON pcon,
PCON pconTarget)
/*++
Routine Description:
Create a EDG, which is an edge in the TCE graph. If an edge is created
from an external reference, then pcon will be NULL and sz will refer to
the symbol name of the external. If the edge is not created from an
external then the target of the reloc is assumed to be a seciton and
sz will be NULL and pcon will be the pcon to resolve with.
Arguments:
sz - symbolic name of edge
pcon - CON to add edge to
pconTarget - contribution in image/driver map to resolve edge with
Return Value:
None.
--*/
{
PNOD pnod;
PEDG pedg;
pnod = PnodPcon(pcon);
while (pnod->iedg == pnod->cedg) {
if (pnod->cedg == 0) {
assert(pnod->rgedg == NULL);
// Allocate 8 EDGs
pnod->cedg = 8;
pnod->rgedg = (PEDG) PvNew(8, sizeof(EDG));
break;
}
if (pnod->pnodNext == NULL) {
// Allocate a new NOD and link to end of list
pnod->pnodNext = (PNOD) PvNew(1, sizeof(NOD));
}
pnod = pnod->pnodNext;
}
pedg = &pnod->rgedg[pnod->iedg];
pnod->iedg++;
if (pconTarget != NULL) {
pedg->pcon = pconTarget;
} else {
pedg->Sym.isym = isym;
}
pedg->NEPInfo.pconPcodeNEP = NULL;
return(pedg);
}
void
ProcessRelocForTCE(
PIMAGE pimage,
PCON pcon,
PIMAGE_SYMBOL rgsym,
PIMAGE_RELOCATION prel)
/*++
Routine Description:
Process a relocation for transitive comdat elimination. This involves
creating an edge in the TCE graph for each relocation.
Arguments:
pcon - contribution node in image/driver map
rgsym - COFF symbol table
prel - COFF relocation to process
Return Value:
None.
--*/
{
PMOD pmod;
DWORD isym;
PIMAGE_SYMBOL psym;
PEDG pedg;
SHORT isec;
pmod = PmodPCON(pcon);
isym = prel->SymbolTableIndex;
psym = &rgsym[isym];
if (psym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL ||
psym->StorageClass == IMAGE_SYM_CLASS_FAR_EXTERNAL ||
psym->StorageClass == IMAGE_SYM_CLASS_WEAK_EXTERNAL) {
pedg = PedgNew_TCE(isym, pcon, NULL);
if ((fM68K && !fRelFromMac68KPcode(prel->Type)) ||
(fPowerMac && !FRelFromPpcPcode(prel->Type))) {
pedg->NEPInfo.fFromNative = TRUE;
}
} else if ((isec = psym->SectionNumber) > 0) {
PCON pconTarget = PconPMOD(pmod, isec);
// If reloc is to a static pcode sym from native code, remember the NEP
// comdat since it must be colored.
if (FPcodeSym(*psym) &&
((fM68K && !fRelFromMac68KPcode(prel->Type)) ||
(fPowerMac && !FRelFromPpcPcode(prel->Type)))) {
PIMAGE_SYMBOL psymPcodeNEP = PsymAlternateStaticPcodeSym(pimage, pcon, TRUE, psym, FALSE);
PCON pconPcodeNEP = PconPMOD(pmod, psymPcodeNEP->SectionNumber);
// UNDONE: Why not create an edge to pconPcodeNEP and allow
// UNDONE: whatever mechanism that maps from the NEP to the
// UNDONE: pcode to drag in the pcode. If there is no such
// UNDONE: mechanism, something should be done to create an
// UNDONE: edge from the NEP to the pcode. This would allow
// UNDONE: the EDG structure to be shrunk by a DWORD.
pedg = PedgNew_TCE(0, pcon, pconTarget);
StorepconPcodeNEP(pedg, pconPcodeNEP);
} else if (pcon != pconTarget) {
PedgNew_TCE(0, pcon, pconTarget);
}
} else {
if (psym->StorageClass != IMAGE_SYM_CLASS_SECTION) {
// Unrecognized fixup (might be erroneous). Ignore here, handle
// elsewhere.
return;
}
// Fixup to an undefined section symbol. This is un-COFF-like
// (normally section symbols map 1-1 to the section headers, so
// they always are defined)
// but it can occur with imports because of the merging of
// objects with the same name. For this case we ignore the
// fixup because the import section will never be eliminated
// by TCE.
assert(psym->N.Name.Short &&
strncmp(SzNameSymPst(*psym, pimage->pst), ".idata", 6) == 0);
}
}
void
MakeEdgePextFromISym(
PMOD pmod)
/*++
Routine Description:
Update the EDG structure by validating the sz field of the sym union.
Before this function is called, the isym field is valid, which is the
index of the symbol in the symbol table. By storing the index and
later updating the string pointer we don't have multiple copies of
all the symbol strings lying around. For macword in pcode (when every
function generates three comdats) this will save and estimated 4 or 5
Meg of RAM.
Arguments:
pmod - module whose contributors' edges are to be updated
pst - external symbol table hash table
Return Value:
None.
--*/
{
ENM_SRC enm_src;
InitEnmSrc(&enm_src, pmod);
// For all cons in this module...
while (FNextEnmSrc(&enm_src)) {
PCON pcon;
PNOD pnod;
ENM_EDG enm_edg;
pcon = enm_src.pcon;
if (pcon->flags & IMAGE_SCN_LNK_REMOVE) {
// Ignore removed sections
continue;
}
pnod = PnodPcon(pcon);
// And for all edges in each con, validate the pext field of the
// Sym union.
InitEnmEdg(&enm_edg, pnod);
while (FNextEnmEdg(&enm_edg)) {
PEDG pedg;
pedg = enm_edg.pedg;
if (pedg->pcon == NULL) {
pedg->Sym.pext = rgpExternObj[pedg->Sym.isym];
assert(pedg->Sym.pext);
}
}
}
}
PENT
PentNew_TCE(
const char *sz,
PEXTERNAL pext,
PCON pcon,
PPENT ppentHead)
/*++
Routine Description:
Create a ENT, which is an entry point to the TCE graph. One and only one
of {sz, pext, pcon} must be !NULL.
Arguments:
sz - name of entry point to TCE graph
pext - external for entry point
pcon - contribution of entry point to graph
ppentHead - head entry point in list
Return Value:
None.
--*/
{
PENT pent;
assert(( sz && !pext && !pcon) ||
(!sz && pext && !pcon) ||
(!sz && !pext && pcon));
assert(ppentHead);
pent = (PENT) PvNew(1, sizeof(ENT));
if (sz) {
pent->sz = sz;
pent->e = TCE_sz;
} else if (pext) {
pent->pext = pext;
pent->e = TCE_ext;
} else if (pcon) {
pent->pcon = pcon;
pent->e = TCE_con;
}
if (*ppentHead) {
pent->pentNext = *ppentHead;
}
*ppentHead = pent;
return(pent);
}
void
CreateGraph_TCE(
PST pst)
/*++
Routine Description:
Walk the NOD adjacency list and resolve symbolic edges.
Arguments:
pst - external symbol table hash table
pcodHead - head CON in TCE graph
Return Value:
None.
--*/
{
ENM_NOD enm_nod;
InitEnmNod(&enm_nod, pconHeadGraph);
while (FNextEnmNod(&enm_nod)) {
PNOD pnod;
ENM_EDG enm_edg;
pnod = enm_nod.pnod;
InitEnmEdg(&enm_edg, pnod);
while (FNextEnmEdg(&enm_edg)) {
PEDG pedg;
pedg = enm_edg.pedg;
if (pedg->pcon == NULL) {
PEXTERNAL pext;
pext = pedg->Sym.pext;
assert(pext != NULL);
if ((pext->Flags & EXTERN_DEFINED) == 0) {
pedg->pcon = NULL;
} else {
pedg->pcon = pext->pcon;
// If reloc is to a pcode sym from native code, remember
// the NEP comdat since it must be colored.
if ((fM68K || fPowerMac) && pedg->NEPInfo.fFromNative) {
if (FPcodeSym(pext->ImageSymbol)) {
PEXTERNAL pextNEP = FindExtAlternatePcodeSym(pext, pst, FALSE);
if (pextNEP == NULL && fPowerMac && READ_BIT(pext, sy_WEAKEXT)) {
PEXTERNAL extSymWeakPtr = PextWeakDefaultFind(pext);
assert (extSymWeakPtr);
pextNEP = FindExtAlternatePcodeSym(extSymWeakPtr, pst, FALSE);
}
assert(pextNEP);
assert(pextNEP->pcon);
StorepconPcodeNEP(pedg, pextNEP->pcon);
} else {
pedg->NEPInfo.fFromNative = FALSE;
}
}
}
}
}
}
}
void
ColorPCON(
PCON pcon)
/*++
Routine Description:
Color a NOD's corresponding CON referenced and recurse through the graph.
Arguments:
pcon - node in TCE graph
Return Value:
None.
--*/
{
assert(pcon);
if ((pcon->flags & TCE_Referenced) == 0) {
PNOD pnod;
ENM_EDG enm_edg;
pcon->flags |= TCE_Referenced;
pnod = PnodPcon(pcon);
InitEnmEdg(&enm_edg, pnod);
while (FNextEnmEdg(&enm_edg)) {
PEDG pedg;
pedg = enm_edg.pedg;
if (pedg->pcon != NULL) {
ColorPCON(pedg->pcon);
}
if ((fM68K || fPowerMac) && (pedg->NEPInfo.pconPcodeNEP != NULL)) {
assert(fPCodeInApp);
ColorPCON(pedg->NEPInfo.pconPcodeNEP);
}
}
}
}
void
WalkGraphEntryPoints_TCE(
PENT pentHead,
PST pst)
/*++
Routine Description:
Walk the TCE graph starting at each entry point.
Arguments:
pent - set of entry points
Return Value:
None.
--*/
{
ENM_ENT enm_ent;
PEXTERNAL pext;
PENT pent;
PCON pconT;
InitEnmEnt(&enm_ent, pentHead);
while (FNextEnmEnt(&enm_ent)) {
pent = enm_ent.pent;
switch (pent->e) {
case TCE_con:
pconT = pent->pcon;
break;
case TCE_ext:
assert(pent->pext);
if (pent->pext->Flags & EXTERN_DEFINED) {
pconT = pent->pext->pcon;
} else {
// This only happens if -force so we are deliberately
// ignoring unresolved externals ...
pconT = NULL;
}
break;
case TCE_sz:
pext = SearchExternSz(pst, pent->sz);
assert(pext);
pconT = pext->pcon;
break;
default:
assert(0);
break;
}
if (pconT != NULL) {
ColorPCON(pconT);
}
}
}
BOOL
FDiscardPCON_TCE(
PCON pcon)
/*++
Routine Description:
Decide whether to discard a contribution.
Arguments:
pcon - contribution in image/driver map
Return Value:
!0 if we are to discard node, 0 otherwise.
--*/
{
if (pcon->flags & TCE_Referenced) {
return(0);
}
return(1);
}
void
DisplayDiscardedPcon(
PCON pcon,
PNOD pnod)
/*++
Routine Description:
Write to standard out what code will not be included in the image that
would have been had TCE not been invoked.
Arguments:
pnod - head node of TCE graph
Return Value:
None.
--*/
{
UINT MsgNumber;
const char *sz;
char *szOutput;
if (pnod == NULL) {
pnod = PnodPcon(pcon);
}
sz = pnod->sz;
if (sz != NULL) {
MsgNumber = TCESYM;
szOutput = SzOutputSymbolName(sz, TRUE);
} else if (PsecPCON(pcon) != psecDebug) {
MsgNumber = TCEGRP;
sz = szOutput = pcon->pgrpBack->szName;
} else {
szOutput = NULL;
}
if (szOutput != NULL) {
fputs(" ", stdout);
if (PmodPCON(pcon) != pmodLinkerDefined) {
char szBuf[_MAX_PATH * 2];
Message(MsgNumber, szOutput, SzComNamePMOD(PmodPCON(pcon), szBuf));
} else {
assert(MsgNumber == TCESYM);
Message(TCESYMNOMOD, sz);
}
if (szOutput != sz) {
FreePv(szOutput);
}
}
}
void
Verbose_TCE(
VOID)
/*++
Routine Description:
Write to standard out what code will not be included in the image that
would have been had TCE not been invoked.
Arguments:
pnod - head node of TCE graph
Return Value:
None.
--*/
{
ENM_NOD enm_nod;
InitEnmNod(&enm_nod, pconHeadGraph);
while (FNextEnmNod(&enm_nod)) {
PCON pcon;
PNOD pnod;
pcon = enm_nod.pcon;
pnod = enm_nod.pnod;
if (FDiscardPCON_TCE(pcon)) {
DisplayDiscardedPcon(pcon, pnod);
DBEXEC(DB_TCE_DISCARD, DumpPNOD_TCE(pcon, pnod));
}
}
}
/*++
Routine Description:
TCE adjacency list enumerator.
Arguments:
None.
Return Value:
None.
--*/
INIT_ENM(Nod, NOD, (ENM_NOD *penm, PCON pcon)) {
penm->pcon = pcon;
penm->pnod = NULL;
}
NEXT_ENM(Nod, NOD) {
if (penm->pnod != NULL) {
penm->pcon = penm->pnod->pconNext;
}
if (penm->pcon == NULL) {
return(FALSE);
}
penm->pnod = PnodPcon(penm->pcon);
return(TRUE);
}
END_ENM(Nod, NOD) {
}
DONE_ENM
/*++
Routine Description:
TCE graph adjacency edge enumerator.
Arguments:
None.
Return Value:
None.
--*/
INIT_ENM(Edg, EDG, (ENM_EDG *penm, PNOD pnod)) {
assert(penm);
penm->pnod = pnod;
penm->pedg = NULL;
penm->iedg = 0;
}
NEXT_ENM(Edg, EDG) {
assert(penm);
assert(penm->pnod);
assert(penm->pnod->iedg <= penm->pnod->cedg);
penm->pedg = NULL;
if (penm->iedg == penm->pnod->iedg) {
if (penm->pnod->pnodNext == NULL) {
return(FALSE);
}
penm->pnod = penm->pnod->pnodNext;
penm->iedg = 0;
}
if (penm->iedg < penm->pnod->iedg) {
penm->pedg = &penm->pnod->rgedg[penm->iedg];
penm->iedg++;
}
return(penm->pedg != NULL);
}
END_ENM(Edg, EDG) {
}
DONE_ENM
/*++
Routine Description:
TCE graph entry points enumerator.
Arguments:
None.
Return Value:
None.
--*/
INIT_ENM(Ent, ENT, (ENM_ENT *penm, PENT pent)) {
assert(penm);
penm->pentStart = pent;
penm->pent = NULL;
}
NEXT_ENM(Ent, ENT) {
assert(penm);
if (!penm->pent) {
penm->pent = penm->pentStart;
} else {
penm->pent = penm->pent->pentNext;
}
return(penm->pent != NULL);
}
END_ENM(Ent, ENT) {
}
DONE_ENM
void StorepconPcodeNEP(PEDG pedg, PCON pconPcodeNEP)
/*++
Routine Description:
Stores pconPcodeNEP in the pedg structure. This field will later be
used to color the NEP comdat if the pcode symbol is referenced from
native.
Arguments:
pedg - Points to the edg structure to be updated.
pconPcodeNEP - Points to the NEP comdat.
Return Value:
None.
--*/
{
assert(pedg);
assert(pconPcodeNEP);
pedg->NEPInfo.pconPcodeNEP = pconPcodeNEP;
}
#if DBG
const char *
SzRefPCON (
PCON pcon)
/*++
Routine Description:
Output either 'referenced' or 'not referenced'.
Arguments:
pcon - node in TCE graph to check
Return Value:
'referenced' if pnod was referenced, 'not referenced' otherwise.
--*/
{
static const char * const rgsz[2] = {
"R",
"N"};
assert(pcon);
return((pcon->flags & TCE_Referenced) ? rgsz[0] : rgsz[1]);
}
void
DumpPNOD_TCE(
PCON pcon,
PNOD pnod)
/*++
Routine Description:
Dump a PNOD to standard out. This routine is not #ifndef'ed since it is
used in a release build when -verbose and TCE are on.
Arguments:
pnod - node in TCE graph to dump
Return Value:
None.
--*/
{
assert(pcon != NULL);
assert(pnod != NULL);
DBPRINT("%s", SzRefPCON(pcon));
DBPRINT("|sec=%.8s", PsecPCON(pcon)->szName);
assert(pcon->pgrpBack);
DBPRINT("|grp=%.8s", pcon->pgrpBack->szName);
DBPRINT("|con=0x%p", pcon);
assert(PsecPCON(pcon));
DBPRINT("|mod=%s", SzObjNamePCON(pcon));
if (pnod->sz) {
DBPRINT("|%s", pnod->sz);
}
DBPRINT("\n");
}
void
DumpPEDG_TCE(
PEDG pedg)
/*++
Routine Description:
Dump a PEDG to standard out.
Arguments:
pedg - edge in TCE graph to dump
Return Value:
None.
--*/
{
assert(pedg != NULL);
if (pedg->pcon != NULL) {
DumpPNOD_TCE(pedg->pcon, PnodPcon(pedg->pcon));
}
}
void
DumpGraph_TCE(
VOID)
/*++
Routine Description:
Dump the TCE graph to standard out.
Arguments:
pconHead - root of adjacency list representation of TCE graph
Return Value:
None.
--*/
{
ENM_NOD enm_nod;
DBPRINT("Dump of comdat dependency graph for TCE\n");
DBPRINT("---------------------------------------\n");
InitEnmNod(&enm_nod, pconHeadGraph);
while (FNextEnmNod(&enm_nod)) {
PCON pcon;
PNOD pnod;
ENM_EDG enm_edg;
pcon = enm_nod.pcon;
pnod = enm_nod.pnod;
DBPRINT("PNOD|");
DumpPNOD_TCE(pcon, pnod);
InitEnmEdg(&enm_edg, pnod);
while (FNextEnmEdg(&enm_edg)) {
PEDG pedg;
pedg = enm_edg.pedg;
DBPRINT(" PEDG|");
DumpPEDG_TCE(pedg);
}
DBPRINT("\n");
}
fflush(stdout);
}
#endif // DBG