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.
1870 lines
47 KiB
1870 lines
47 KiB
/***********************************************************************
|
|
* Microsoft (R) 32-Bit Incremental Linker
|
|
*
|
|
* Copyright (C) Microsoft Corp 1992-1996. All rights reserved.
|
|
*
|
|
* File: lnkp1.cpp
|
|
*
|
|
* File Comments:
|
|
*
|
|
* Pass 1 of the COFF Linker.
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include "link.h"
|
|
|
|
void
|
|
LookForMain(
|
|
PIMAGE pimage,
|
|
PEXTERNAL *ppextMain,
|
|
PEXTERNAL *ppextwMain
|
|
)
|
|
{
|
|
PST pst;
|
|
const char *szMain;
|
|
const char *szwMain;
|
|
|
|
pst = pimage->pst;
|
|
|
|
switch (pimage->ImgFileHdr.Machine) {
|
|
case IMAGE_FILE_MACHINE_I386 :
|
|
szMain = "_main";
|
|
szwMain = "_wmain";
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_R4000:
|
|
case IMAGE_FILE_MACHINE_R10000:
|
|
case IMAGE_FILE_MACHINE_ALPHA:
|
|
case IMAGE_FILE_MACHINE_POWERPC:
|
|
szMain = "main";
|
|
szwMain = "wmain";
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_M68K :
|
|
case IMAGE_FILE_MACHINE_MPPC_601 :
|
|
szMain = "_main";
|
|
szwMain = "_wmain";
|
|
break;
|
|
|
|
default :
|
|
assert(FALSE);
|
|
}
|
|
|
|
// Look for main() and wmain()
|
|
|
|
*ppextMain = SearchExternSz(pst, szMain);
|
|
*ppextwMain = SearchExternSz(pst, szwMain);
|
|
}
|
|
|
|
|
|
void
|
|
LookForWinMain(
|
|
PIMAGE pimage,
|
|
PEXTERNAL *ppextWinMain,
|
|
PEXTERNAL *ppextwWinMain
|
|
)
|
|
{
|
|
PST pst;
|
|
const char *szWinMain;
|
|
const char *szwWinMain;
|
|
|
|
pst = pimage->pst;
|
|
|
|
switch (pimage->ImgFileHdr.Machine) {
|
|
case IMAGE_FILE_MACHINE_I386 :
|
|
szWinMain = "_WinMain@16";
|
|
szwWinMain = "_wWinMain@16";
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_R4000:
|
|
case IMAGE_FILE_MACHINE_R10000:
|
|
case IMAGE_FILE_MACHINE_ALPHA:
|
|
case IMAGE_FILE_MACHINE_POWERPC:
|
|
szWinMain = "WinMain";
|
|
szwWinMain = "wWinMain";
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_M68K :
|
|
case IMAGE_FILE_MACHINE_MPPC_601 :
|
|
szWinMain = "_WinMain";
|
|
szwWinMain = "_wWinMain";
|
|
break;
|
|
|
|
default :
|
|
assert(FALSE);
|
|
}
|
|
|
|
// Look for WinMain() and wWinMain()
|
|
|
|
*ppextWinMain = SearchExternSz(pst, szWinMain);
|
|
*ppextwWinMain = SearchExternSz(pst, szwWinMain);
|
|
}
|
|
|
|
|
|
BOOL
|
|
FSetEntryPoint(
|
|
PIMAGE pimage
|
|
)
|
|
{
|
|
char *szEntryName;
|
|
|
|
assert(EntryPointName != NULL);
|
|
|
|
// Keep track of entrypointname for ilink
|
|
|
|
if (!pimage->SwitchInfo.szEntry) {
|
|
pimage->SwitchInfo.szEntry = Strdup(EntryPointName);
|
|
}
|
|
|
|
switch (pimage->ImgFileHdr.Machine) {
|
|
case IMAGE_FILE_MACHINE_I386 :
|
|
case IMAGE_FILE_MACHINE_M68K :
|
|
case IMAGE_FILE_MACHINE_MPPC_601 :
|
|
// Don't add leading underscore for decorated names or VXDs
|
|
|
|
if ((pimage->imaget != imagetVXD) &&
|
|
(EntryPointName[0] != '?')) {
|
|
szEntryName = (char *) PvAlloc(strlen(EntryPointName) + 2);
|
|
szEntryName[0] = '_';
|
|
strcpy(szEntryName + 1, EntryPointName);
|
|
break;
|
|
}
|
|
|
|
// Fall through
|
|
|
|
default :
|
|
szEntryName = EntryPointName;
|
|
break;
|
|
}
|
|
|
|
pextEntry = LookupExternSz(pimage->pst, szEntryName, NULL);
|
|
|
|
if (szEntryName != EntryPointName) {
|
|
FreePv(szEntryName);
|
|
}
|
|
|
|
// Don't add the entry point to the TCE head yet, because we may do
|
|
// a fuzzy match and switch the entry point extern later.
|
|
|
|
FreePv(EntryPointName);
|
|
|
|
return((pextEntry->Flags & EXTERN_DEFINED) == 0);
|
|
}
|
|
|
|
|
|
void
|
|
DoMachineDependentInit(
|
|
PIMAGE pimage
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fIlinkSupported = FALSE; // default
|
|
|
|
// Finish doing machine dependent initialization.
|
|
|
|
if (pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_UNKNOWN) {
|
|
// If we don't have a machine type yet, shamelessly default to host
|
|
|
|
pimage->ImgFileHdr.Machine = wDefaultMachine;
|
|
Warning(NULL, HOSTDEFAULT, szHostDefault);
|
|
}
|
|
|
|
#ifdef ILINKLOG
|
|
wMachine = pimage->ImgFileHdr.Machine;
|
|
#endif // ILINKLOG
|
|
|
|
switch (pimage->ImgFileHdr.Machine) {
|
|
case IMAGE_FILE_MACHINE_I386 :
|
|
I386LinkerInit(pimage, &fIlinkSupported);
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_R3000 :
|
|
// The MMOSA team needs to be able to create import libraries
|
|
// that are compatible with the R3000. This is undocumented.
|
|
|
|
// Fall through
|
|
|
|
case IMAGE_FILE_MACHINE_R4000 :
|
|
case IMAGE_FILE_MACHINE_R10000 :
|
|
MipsLinkerInit(pimage, &fIlinkSupported);
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_ALPHA :
|
|
AlphaLinkerInit(pimage, &fIlinkSupported);
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_POWERPC :
|
|
PpcLinkerInit(pimage, &fIlinkSupported);
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_M68K :
|
|
M68KLinkerInit(pimage, &fIlinkSupported);
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_MPPC_601 :
|
|
MPPCLinkerInit(pimage, &fIlinkSupported);
|
|
break;
|
|
|
|
default :
|
|
assert(FALSE);
|
|
}
|
|
|
|
if (fINCR && !fIlinkSupported) {
|
|
// Still trying to ilink an unsupported machine. Too late to
|
|
// punt ilink, so tell user to specify machine next time.
|
|
|
|
Fatal(NULL, NOMACHINESPECIFIED);
|
|
}
|
|
|
|
if (Tool == Linker && fImageMappedAsFile) {
|
|
// We created .bss as initialized data but we now want it to be
|
|
// uninitialized data. Zap the characteristics directly.
|
|
|
|
assert(psecCommon->flags & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
|
|
assert(psecCommon->flagsOrig & IMAGE_SCN_CNT_UNINITIALIZED_DATA);
|
|
|
|
psecCommon->flags &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA;
|
|
psecCommon->flags |= IMAGE_SCN_CNT_INITIALIZED_DATA;
|
|
psecCommon->flagsOrig &= ~IMAGE_SCN_CNT_UNINITIALIZED_DATA;
|
|
psecCommon->flagsOrig |= IMAGE_SCN_CNT_INITIALIZED_DATA;
|
|
|
|
// There should be no GRPs at this point
|
|
|
|
assert(psecCommon->pgrpNext == NULL);
|
|
}
|
|
|
|
if (Tool == Linker) {
|
|
// Set the entrypoint before processing object files. This makes
|
|
// the entrypoint the first unresolved external and helps to make
|
|
// the object with the entrypoint found before any other object
|
|
// that may contain definitions of the same symbols.
|
|
|
|
// UNDONE: This serves to correct a problem with the C runtime where
|
|
// UNDONE: mainCRTStartup and WinMainCRTStartup are defined in
|
|
// UNDONE: modules that have otherwise duplicate definitions.
|
|
|
|
if ((pextEntry == NULL) && (EntryPointName != NULL)) {
|
|
FSetEntryPoint(pimage);
|
|
}
|
|
}
|
|
|
|
fDidMachineDependentInit = TRUE;
|
|
}
|
|
|
|
|
|
void
|
|
ProcessLib(
|
|
PLIB plib,
|
|
PIMAGE pimage)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read in library information and add it to driver map LIB node.
|
|
|
|
Arguments:
|
|
|
|
plib - library to process
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
// Just seek past archive header
|
|
|
|
FileSeek(FileReadHandle, IMAGE_ARCHIVE_START_SIZE, SEEK_SET);
|
|
|
|
// Fill in relavent fields in plib regarding archive file
|
|
|
|
ReadSpecialLinkerInterfaceMembers(plib, pimage);
|
|
}
|
|
|
|
|
|
PMOD
|
|
PmodProcessObjectFile(
|
|
PARGUMENT_LIST Argument,
|
|
PIMAGE pimage,
|
|
PLIB plibCmdLineObjs,
|
|
WORD *pwMachine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds object to object filename list & sets/verifies that it is targetted
|
|
for the same machine. If no output file name is specified, then the
|
|
base name of the first object is selected as the base name of the
|
|
outpuft file.
|
|
|
|
Arguments:
|
|
|
|
Argument - The argument to process
|
|
|
|
MachineType - The machine type.
|
|
|
|
plibCmdLineObjs - dummy library for the command line modules
|
|
|
|
pwMachine - On return has the machine type
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
IMAGE_FILE_HEADER imFileHdr;
|
|
PMOD pmod;
|
|
BOOL fNewObj;
|
|
|
|
// Verify target machine type
|
|
|
|
*pwMachine = VerifyAnObject(Argument, pimage);
|
|
|
|
if (!fDidMachineDependentInit) {
|
|
DoMachineDependentInit(pimage);
|
|
}
|
|
|
|
// Create module node
|
|
|
|
FileSeek(FileReadHandle, 0L, SEEK_SET);
|
|
ReadFileHeader(FileReadHandle, &imFileHdr);
|
|
|
|
pmod = PmodNew(Argument->ModifiedName,
|
|
Argument->OriginalName,
|
|
0,
|
|
imFileHdr.PointerToSymbolTable,
|
|
imFileHdr.NumberOfSymbols,
|
|
imFileHdr.SizeOfOptionalHeader,
|
|
imFileHdr.Characteristics,
|
|
imFileHdr.NumberOfSections,
|
|
plibCmdLineObjs, &fNewObj);
|
|
|
|
pmod->HdrTimeStamp = imFileHdr.TimeDateStamp;
|
|
pmod->cbFile = FileLength(FileReadHandle);
|
|
|
|
if (!fNewObj) {
|
|
Warning(Argument->OriginalName, DUPLICATE_OBJECT);
|
|
return(NULL);
|
|
}
|
|
|
|
// Save timestamp
|
|
|
|
pmod->TimeStamp = Argument->TimeStamp;
|
|
|
|
if (pimage->Switch.Link.fTCE) {
|
|
// Allocate memory for TCE data structures
|
|
|
|
InitNodPmod(pmod);
|
|
}
|
|
|
|
return(pmod);
|
|
}
|
|
|
|
|
|
PMOD
|
|
PmodPreprocessFile(
|
|
PARGUMENT_LIST Argument,
|
|
PLIB plibCmdLineObjs,
|
|
PIMAGE pimage,
|
|
WORD *pwMachine)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
a) classify obj/archive by looking for signature.
|
|
if obj
|
|
1) check for omf and convert to COFF object.
|
|
2) check for resource and convert to COFF object
|
|
3) get machine type
|
|
4) do rest of obj processing.
|
|
else
|
|
1) add to archive filename list
|
|
|
|
Arguments:
|
|
|
|
Argument - The argument to process
|
|
|
|
plib - dummy library node for command line modules
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (IsArchiveFile(Argument->OriginalName, FileReadHandle)) {
|
|
PLIB plib;
|
|
|
|
plib = PlibNew(Argument->OriginalName, 0L, &pimage->libs);
|
|
|
|
// If this is the linker, delay lib processing until later.
|
|
|
|
if (Tool != Linker) {
|
|
FileSeek(FileReadHandle, 0L, SEEK_SET);
|
|
ProcessLib(plib, pimage);
|
|
}
|
|
|
|
plib->flags &= ~LIB_DontSearch; // turn this lib back on
|
|
plib->TimeStamp = Argument->TimeStamp;
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
if (FIsMacResFile(FileReadHandle)) {
|
|
UseMacBinaryRes(Argument->OriginalName, resntBinaryResource, FileReadHandle);
|
|
return(NULL);
|
|
}
|
|
|
|
// Do object file processing
|
|
|
|
return(PmodProcessObjectFile(Argument, pimage, plibCmdLineObjs, pwMachine));
|
|
}
|
|
|
|
|
|
void
|
|
SetDefaultOutFilename(PIMAGE pimage, ARGUMENT_LIST *parg)
|
|
{
|
|
char szFname[_MAX_FNAME+4];
|
|
|
|
assert(OutFilename == NULL);
|
|
|
|
_splitpath(parg->OriginalName, NULL, NULL, szFname, NULL);
|
|
|
|
if (Tool == Librarian) {
|
|
strcat(szFname, ".lib");
|
|
} else if (pimage->imaget == imagetVXD) {
|
|
strcat(szFname, ".vxd");
|
|
} else if (fDLL(pimage)) {
|
|
strcat(szFname, ".dll");
|
|
} else {
|
|
strcat(szFname, ".exe");
|
|
}
|
|
|
|
OutFilename = SzDup(szFname);
|
|
}
|
|
|
|
|
|
void
|
|
Pass1Arg(ARGUMENT_LIST *parg, PIMAGE pimage, PLIB plib)
|
|
{
|
|
PMOD pmod;
|
|
WORD wMachine = IMAGE_FILE_MACHINE_UNKNOWN;
|
|
|
|
_tcscpy(InternalError.CombinedFilenames, parg->OriginalName);
|
|
|
|
FileReadHandle = FileOpen(parg->ModifiedName, O_RDONLY | O_BINARY, 0);
|
|
|
|
pmod = PmodPreprocessFile(parg, plib, pimage, &wMachine);
|
|
|
|
if (pmod != NULL) {
|
|
BuildExternalSymbolTable(pimage, NULL, pmod, 0, wMachine);
|
|
|
|
if (fIncrDbFile && (errInc != errNone)) {
|
|
// check for ilink failures
|
|
|
|
return;
|
|
}
|
|
|
|
if (OutFilename == NULL) {
|
|
// Capture first object name for output filename.
|
|
|
|
SetDefaultOutFilename(pimage, parg);
|
|
}
|
|
}
|
|
|
|
FileClose(FileReadHandle, FALSE);
|
|
}
|
|
|
|
|
|
void
|
|
WarningNoObjectFiles(
|
|
PIMAGE pimage,
|
|
PLIB plib)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Warn about no object files and perform machine dependant initialization.
|
|
|
|
Arguments:
|
|
|
|
plib - driver map library parent of the command line modules
|
|
|
|
pst - external symbol table
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENM_MOD enm;
|
|
|
|
InitEnmMod(&enm, plib);
|
|
|
|
if (!FNextEnmMod(&enm)) {
|
|
Warning(NULL, NOOBJECTFILES);
|
|
|
|
DoMachineDependentInit(pimage);
|
|
}
|
|
|
|
EndEnmMod(&enm);
|
|
}
|
|
|
|
void
|
|
PrepLibForSearching (
|
|
IN PIMAGE pimage,
|
|
IN PLIB plib
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prepares lib for searching by reading in linker interface member(s).
|
|
|
|
Arguments:
|
|
|
|
pimage - ptr to IMAGE
|
|
|
|
plib - lib to prep.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (plib->szName != NULL && plib->rgszSym == NULL) {
|
|
INT fdSave = FileReadHandle;
|
|
|
|
FileReadHandle = FileOpen(plib->szName,
|
|
O_RDONLY | O_BINARY, 0);
|
|
ProcessLib(plib, pimage);
|
|
FileClose(FileReadHandle, FALSE);
|
|
|
|
FileReadHandle = fdSave;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ResolveExternalsInLibs(
|
|
PIMAGE pimage)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Search all the libraries for unresolved externals.
|
|
|
|
Arguments:
|
|
|
|
pst - external symbol table
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENM_LIB enm_lib;
|
|
BOOL fUnresolved;
|
|
PLIB plibLastProgress = NULL;
|
|
|
|
if (fVerboseLib) {
|
|
fputc('\n', stdout);
|
|
|
|
Message(SRCHLIBS);
|
|
}
|
|
|
|
// enumerate archives until all externs are
|
|
// resolved or until no new externs added
|
|
for (fUnresolved = TRUE; fUnresolved; ) {
|
|
LIB *plib;
|
|
BOOL fMoreLibs;
|
|
|
|
fMoreLibs = TRUE;
|
|
InitEnmLib(&enm_lib, pimage->libs.plibHead);
|
|
for (;;) {
|
|
// Try to find a LIB to link.
|
|
|
|
if (fMoreLibs && FNextEnmLib(&enm_lib)) {
|
|
plib = enm_lib.plib;
|
|
} else {
|
|
fMoreLibs = FALSE;
|
|
}
|
|
|
|
if (!fMoreLibs) {
|
|
PLIB plibNew;
|
|
|
|
if ((plibNew = PlibInstantiateDefaultLib(&pimage->libs)) != NULL) {
|
|
plib = plibNew;
|
|
} else {
|
|
// No more LIB's.
|
|
|
|
// Got to end of the list of libs. Proceed from the
|
|
// beginning, but only if we have a stopping point (i.e.
|
|
// some progress has been made during the current lap).
|
|
|
|
if (plibLastProgress == NULL) {
|
|
goto BreakFor;
|
|
}
|
|
|
|
// Go start over from the beginning
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If we're about to search the last lib that added new symbols,
|
|
// then we're stuck.
|
|
|
|
if (plib == plibLastProgress) {
|
|
goto BreakFor;
|
|
}
|
|
|
|
// Got a LIB.
|
|
|
|
assert(plib != NULL);
|
|
|
|
if (!(plib->flags & LIB_DontSearch)) {
|
|
BOOL fNewSymbol = FALSE;
|
|
|
|
PrepLibForSearching(pimage, plib);
|
|
|
|
SearchLib(pimage, plib, &fNewSymbol, &fUnresolved);
|
|
|
|
if (fIncrDbFile && errInc != errNone) {
|
|
goto BreakFor;
|
|
}
|
|
|
|
if (!fUnresolved) {
|
|
// Break out if all externs are resolved
|
|
|
|
if (fMoreLibs) {
|
|
EndEnmLib(&enm_lib);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (fNewSymbol) {
|
|
// Remember the last archive that added new symbols
|
|
|
|
plibLastProgress = plib;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
BreakFor:;
|
|
|
|
if (fVerboseLib) {
|
|
fputc('\n', stdout);
|
|
|
|
Message(DONESRCHLIBS);
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
CountNoDefaultLibOptions (
|
|
PIMAGE pimage
|
|
)
|
|
{
|
|
if (pimage->libs.fNoDefaultLibs) {
|
|
return 1;
|
|
} else {
|
|
DWORD count = 0;
|
|
DL *pdl;
|
|
|
|
for (pdl = pimage->libs.pdlFirst; pdl != NULL; pdl = pdl->pdlNext) {
|
|
// nodefaultlibs have the LIB_DontSearch bit set
|
|
|
|
if (pdl->flags & LIB_DontSearch) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
CountIncludes (
|
|
SWITCH_INFO *pSwitchInfo
|
|
)
|
|
{
|
|
DWORD count = 0;
|
|
PLEXT plext;
|
|
|
|
for (plext = pSwitchInfo->plextIncludes; plext != NULL; plext = plext->plextNext) {
|
|
count++;
|
|
}
|
|
|
|
if (pextEntry != NULL && (pextEntry->Flags & EXTERN_DEFINED)) {
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
void
|
|
WarningIgnoredExports(
|
|
const char *sz
|
|
)
|
|
{
|
|
// Warns of ignored exports. DOESN'T REPORT ABOUT NEW EXPORTS
|
|
|
|
if (DefFilename != NULL && DefFilename[0] != '\0') {
|
|
Warning(NULL, DEF_IGNORED, DefFilename);
|
|
}
|
|
|
|
if (ExportSwitches.Count) {
|
|
Warning(NULL, EXPORTS_IGNORED, sz);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
FPass1DefFile(PIMAGE pimage, const char *szDefFilename)
|
|
{
|
|
WORD i;
|
|
PARGUMENT_LIST parg;
|
|
char szDrive[_MAX_DRIVE];
|
|
char szDir[_MAX_DIR];
|
|
char szFname[_MAX_FNAME];
|
|
char szExt[_MAX_EXT];
|
|
char szExpFilename[_MAX_FNAME];
|
|
char szImplibFilename[_MAX_FNAME];
|
|
BLK blkArgs = {0}; // tmp storage area for argument strings
|
|
union _u {
|
|
const char *sz;
|
|
DWORD ib;
|
|
} *rguImplibArgs;
|
|
WORD cuAllocated;
|
|
WORD iu;
|
|
char *szImplibT;
|
|
const char *szMachine;
|
|
int rc;
|
|
ARGUMENT_LIST argNewObject;
|
|
PLEXT plext;
|
|
BOOL fAddObjs;
|
|
BOOL fLFN = FALSE; // for long filenames
|
|
|
|
// If necessary, generates a COFF object representing the contents of the
|
|
// .def file and/or -export options, adds it to the list of files to link,
|
|
// and does Pass1 on it.
|
|
//
|
|
// If szDefFilename is a string of 0 length then there was no .def file on
|
|
// the command line.
|
|
|
|
if (fINCR) {
|
|
SaveExportInfo(pimage, szDefFilename, &pimage->ExpInfo);
|
|
}
|
|
|
|
if ((ExportSwitches.Count == 0) &&
|
|
!fExportDirective &&
|
|
((szDefFilename == NULL) || (szDefFilename[0] == '\0')))
|
|
{
|
|
// No .def file and no exports
|
|
return FALSE;
|
|
}
|
|
|
|
if (pgrpExport->pconNext != NULL) {
|
|
// Already have an .edata section (which presumably came from an .exp
|
|
// file) so we won't handle new -exports or .def.
|
|
|
|
WarningIgnoredExports(SzObjNamePCON(pgrpExport->pconNext));
|
|
return(FALSE);
|
|
}
|
|
|
|
cuAllocated = (WORD) (FilenameArguments.Count + ExportSwitches.Count +
|
|
CountNoDefaultLibOptions(pimage) +
|
|
CountIncludes(&pimage->SwitchInfo) + 12); // guess
|
|
rguImplibArgs = (union _u *) PvAlloc(sizeof(union _u) * cuAllocated);
|
|
|
|
iu = 0;
|
|
|
|
// Spawn "link /lib /def" to generate an .exp file from the -export flags
|
|
// and .def file.
|
|
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "\"", 1);
|
|
IbAppendBlk(&blkArgs, _pgmptr, strlen(_pgmptr));
|
|
IbAppendBlk(&blkArgs, "\"", 2); // Include the terminating null
|
|
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/lib", sizeof("/lib"));
|
|
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/def:", sizeof("/def:") - 1);
|
|
|
|
if ((szDefFilename == NULL) || (szDefFilename[0] == '\0')) {
|
|
IbAppendBlk(&blkArgs, "", 1);
|
|
} else {
|
|
IbAppendBlk(&blkArgs, "\"", 1);
|
|
IbAppendBlk(&blkArgs, szDefFilename, strlen(szDefFilename));
|
|
IbAppendBlk(&blkArgs, "\"", 2);
|
|
}
|
|
|
|
if (OutFilename == NULL) {
|
|
Fatal(NULL, NOOUTPUTFILE);
|
|
}
|
|
|
|
_splitpath(OutFilename, szDrive, szDir, szFname, szExt);
|
|
|
|
// Generate the name to embed in the file if it has exports. This name
|
|
// will be overridden by the name in the .def file if any.
|
|
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/name:", sizeof("/name:") - 1);
|
|
IbAppendBlk(&blkArgs, "\"", 1);
|
|
IbAppendBlk(&blkArgs, szFname, strlen(szFname));
|
|
IbAppendBlk(&blkArgs, szExt, strlen(szExt));
|
|
IbAppendBlk(&blkArgs, "\"", 2);
|
|
|
|
if ((szImplibT = ImplibFilename) == NULL) {
|
|
// Select default name for the import library to generate
|
|
|
|
_makepath(szImplibFilename, szDrive, szDir, szFname, ".lib");
|
|
|
|
szImplibT = szImplibFilename;
|
|
}
|
|
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/out:", sizeof("/out:") - 1);
|
|
IbAppendBlk(&blkArgs, "\"", 1);
|
|
IbAppendBlk(&blkArgs, szImplibT, strlen(szImplibT));
|
|
IbAppendBlk(&blkArgs, "\"", 2);
|
|
|
|
for (i = 0, parg = ExportSwitches.First;
|
|
i < ExportSwitches.Count;
|
|
i++, parg = parg->Next) {
|
|
if (parg->ModifiedName != NULL) {
|
|
// The export came from a directive, so ignore it because
|
|
// "lib -def" will also see it.
|
|
|
|
continue;
|
|
}
|
|
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/export:",
|
|
sizeof("/export:") - 1);
|
|
IbAppendBlk(&blkArgs, parg->OriginalName,
|
|
strlen(parg->OriginalName) + 1);
|
|
}
|
|
|
|
// Add in the /INCLUDE options
|
|
for (plext = pimage->SwitchInfo.plextIncludes; plext != NULL; plext = plext->plextNext) {
|
|
char *szInclude;
|
|
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/include:", sizeof("/include:") -1);
|
|
szInclude = SzNamePext(plext->pext, pimage->pst);
|
|
IbAppendBlk(&blkArgs, szInclude, strlen(szInclude) + 1);
|
|
}
|
|
|
|
// Force the entry point to be included
|
|
|
|
if (pextEntry != NULL && (pextEntry->Flags & EXTERN_DEFINED)) {
|
|
char *szInclude;
|
|
|
|
szInclude = SzNamePext(pextEntry, pimage->pst);
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/include:", sizeof("/include:") -1);
|
|
IbAppendBlk(&blkArgs, szInclude, strlen(szInclude) + 1);
|
|
}
|
|
|
|
// Pass on any /NODEFAULTLIB options
|
|
|
|
if (pimage->libs.fNoDefaultLibs) {
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/nodefaultlib", sizeof("/nodefaultlib"));
|
|
} else {
|
|
DL *pdl;
|
|
|
|
for (pdl = pimage->libs.pdlFirst; pdl != NULL; pdl = pdl->pdlNext) {
|
|
// nodefaultlib has the LIB_DontSearch set
|
|
|
|
if (pdl->flags & LIB_DontSearch) {
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/nodefaultlib:", sizeof("/nodefaultlib:")-1);
|
|
IbAppendBlk(&blkArgs, pdl->szName, strlen(pdl->szName) + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Adding version support for PowerMac
|
|
if (fPowerMac &&
|
|
(dwMaxCurrentVer != 0 || dwMinOldCodeVer != UINT_MAX || dwMinOldAPIVer != UINT_MAX)) {
|
|
BOOL fComma = FALSE;
|
|
char szBuffer[11];
|
|
|
|
rguImplibArgs[iu++].ib =
|
|
IbAppendBlk(&blkArgs, "/import:", sizeof("/import:") - 1);
|
|
|
|
if (dwMaxCurrentVer != 0) {
|
|
fComma = TRUE;
|
|
_itoa(dwMaxCurrentVer, szBuffer, 10);
|
|
IbAppendBlk(&blkArgs, "currentver=", sizeof("currentver=") - 1);
|
|
IbAppendBlk(&blkArgs, szBuffer, strlen(szBuffer));
|
|
}
|
|
|
|
if (dwMinOldCodeVer != UINT_MAX) {
|
|
if (fComma) {
|
|
IbAppendBlk(&blkArgs, ",", 1);
|
|
} else {
|
|
fComma = TRUE;
|
|
}
|
|
_itoa(dwMinOldCodeVer, szBuffer, 10);
|
|
IbAppendBlk(&blkArgs, "oldcodever=", sizeof("oldcodever=") - 1);
|
|
IbAppendBlk(&blkArgs, szBuffer, strlen(szBuffer));
|
|
}
|
|
|
|
if (dwMinOldAPIVer != UINT_MAX) {
|
|
if (fComma) {
|
|
IbAppendBlk(&blkArgs, ",", 1);
|
|
}
|
|
_itoa(dwMinOldAPIVer, szBuffer, 10);
|
|
IbAppendBlk(&blkArgs, "oldapiver=", sizeof("oldapiver=") - 1);
|
|
IbAppendBlk(&blkArgs, szBuffer, strlen(szBuffer));
|
|
}
|
|
|
|
IbAppendBlk(&blkArgs, "", 1);
|
|
}
|
|
|
|
if (fPowerMac && FUsedOpt(pimage->SwitchInfo, OP_MACINIT)) {
|
|
const char *szTemp = pimage->SwitchInfo.szMacInit;
|
|
if (*szTemp == '_') {
|
|
szTemp++;
|
|
}
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/mac:init=",
|
|
sizeof("/mac:init=") - 1);
|
|
IbAppendBlk(&blkArgs, szTemp, strlen(szTemp) + 1);
|
|
}
|
|
|
|
if (fPowerMac && FUsedOpt(pimage->SwitchInfo, OP_MACTERM)) {
|
|
const char *szTemp = pimage->SwitchInfo.szMacTerm;
|
|
if (*szTemp == '_') {
|
|
szTemp++;
|
|
}
|
|
rguImplibArgs[iu++].ib = IbAppendBlk(&blkArgs, "/mac:term=",
|
|
sizeof("/mac:term=") - 1);
|
|
IbAppendBlk(&blkArgs, szTemp, strlen(szTemp) + 1);
|
|
}
|
|
|
|
fAddObjs = TRUE;
|
|
|
|
switch (pimage->ImgFileHdr.Machine) {
|
|
case IMAGE_FILE_MACHINE_R4000 :
|
|
case IMAGE_FILE_MACHINE_R10000 :
|
|
szMachine = "/machine:mips";
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_I386 :
|
|
szMachine = "/machine:ix86";
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_ALPHA :
|
|
szMachine = "/machine:alpha";
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_POWERPC :
|
|
szMachine = "/machine:ppc";
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_MPPC_601 :
|
|
szMachine = "/machine:mppc";
|
|
MppcSetExpFilename(szExpFilename);
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_M68K :
|
|
szMachine = "/machine:m68k";
|
|
fAddObjs = FALSE;
|
|
break;
|
|
|
|
default:
|
|
szMachine = NULL;
|
|
break;
|
|
}
|
|
|
|
// Add in the filenames
|
|
if (fAddObjs) {
|
|
for (i = 0, parg = FilenameArguments.First;
|
|
i < FilenameArguments.Count;
|
|
i++, parg = parg->Next) {
|
|
rguImplibArgs[iu].ib = IbAppendBlk(&blkArgs, "\"", 1);
|
|
IbAppendBlk(&blkArgs, parg->OriginalName, strlen(parg->OriginalName));
|
|
IbAppendBlk(&blkArgs, "\"", 2); // Include the terminating null
|
|
iu++;
|
|
}
|
|
}
|
|
|
|
// Convert ib's to sz's now that blkArgs is in its final location.
|
|
|
|
for (i = 0; i < iu; i++) {
|
|
rguImplibArgs[i].sz = (char *) (blkArgs.pb + rguImplibArgs[i].ib);
|
|
}
|
|
|
|
rguImplibArgs[iu++].sz = szMachine;
|
|
|
|
if (Verbose) {
|
|
rguImplibArgs[iu++].sz = "/verbose";
|
|
} else {
|
|
rguImplibArgs[iu++].sz = "/nologo";
|
|
}
|
|
|
|
if (fDbgImpLib && pimage->Switch.Link.DebugType & CvDebug) {
|
|
rguImplibArgs[iu++].sz = "/debugtype:cv";
|
|
}
|
|
|
|
if (pimage->imaget == imagetVXD) {
|
|
rguImplibArgs[iu++].sz = "/vxd";
|
|
}
|
|
|
|
_splitpath(szImplibT, szDrive, szDir, szFname, NULL);
|
|
_makepath(szExpFilename, szDrive, szDir, szFname, "exp");
|
|
|
|
|
|
rguImplibArgs[iu].sz = NULL;
|
|
|
|
if (Verbose) {
|
|
fputc('\n', stdout);
|
|
Message(GENEXPFILE);
|
|
|
|
// UNDONE: If a response file is used to pass arguments, this
|
|
// UNDONE: will come for free from the nested invocation of LINK
|
|
|
|
fputc('\n', stdout);
|
|
Message(GENEXPFILECMD);
|
|
for (i = 0; i < iu; i++) {
|
|
puts(rguImplibArgs[i].sz);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
fflush(NULL);
|
|
|
|
// Write a different response file for the implib.
|
|
// putenv isn't used because of a bug in the NT 3.5 C runtimes
|
|
|
|
SetEnvironmentVariable("LINK_REPRO_NAME", "\\deflib.rsp");
|
|
|
|
if ((rc = _spawnv(P_WAIT, _pgmptr, (const char * const *) rguImplibArgs)) != 0) {
|
|
Fatal(NULL, DEFLIB_FAILED);
|
|
}
|
|
|
|
if (Verbose) {
|
|
fputc('\n', stdout);
|
|
Message(ENDGENEXPFILE);
|
|
}
|
|
|
|
FreeBlk(&blkArgs);
|
|
FreePv(rguImplibArgs);
|
|
|
|
for (i = 0, parg = ExportSwitches.First;
|
|
i < ExportSwitches.Count;
|
|
i++, parg = parg->Next)
|
|
{
|
|
if (parg->ModifiedName != NULL) {
|
|
// The export came from a directive, so free name.
|
|
|
|
FreePv(parg->ModifiedName);
|
|
}
|
|
}
|
|
|
|
// We just created szExpFilename ... run pass1 on it.
|
|
|
|
argNewObject.OriginalName = argNewObject.ModifiedName = szExpFilename;
|
|
Pass1Arg(&argNewObject, pimage, pimage->plibCmdLineObjs);
|
|
|
|
InternalError.CombinedFilenames[0] = '\0';
|
|
|
|
if (fINCR) {
|
|
PMOD pmod;
|
|
struct _stat statfile;
|
|
|
|
// save the generated exp object info
|
|
pmod = PmodFind(pimage->plibCmdLineObjs, szExpFilename, 0UL);
|
|
assert(pmod);
|
|
if (_stat(SzOrigFilePMOD(pmod), &statfile) == -1) {
|
|
Fatal(NULL, CANTOPENFILE, SzOrigFilePMOD(pmod));
|
|
}
|
|
pmod->TimeStamp = statfile.st_mtime;
|
|
pimage->ExpInfo.pmodGen = pmod;
|
|
|
|
// save the import lib info
|
|
pimage->ExpInfo.szImpLib = Strdup(szImplibT);
|
|
if (_stat(szImplibT, &statfile) == -1) {
|
|
Fatal(NULL, CANTOPENFILE, szImplibT);
|
|
}
|
|
|
|
pimage->ExpInfo.tsImpLib = statfile.st_mtime;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#ifdef NT_BUILD
|
|
char const * const rgszCRTNames[] = {
|
|
"crtdll.lib",
|
|
"libc.lib",
|
|
"libcmt.lib",
|
|
"libcntpr.lib",
|
|
"msvcrt.lib",
|
|
"ntcrt.lib"
|
|
};
|
|
|
|
const int nCrtNames = sizeof(rgszCRTNames) / sizeof(char *);
|
|
|
|
#endif
|
|
|
|
|
|
void
|
|
WarningConflictingLibs(
|
|
PLIBS plibs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Warn about conflicting libs.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
`
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#ifdef NT_BUILD
|
|
ENM_LIB enm_lib;
|
|
PLIB plib, plibCrt = NULL;
|
|
int i;
|
|
|
|
InitEnmLib(&enm_lib, plibs->plibHead);
|
|
while (FNextEnmLib(&enm_lib)) {
|
|
plib = enm_lib.plib;
|
|
if (!(plib->flags & LIB_DontSearch)) {
|
|
char szName[_MAX_FNAME];
|
|
char szExt[_MAX_EXT];
|
|
char szLib[_MAX_PATH];
|
|
|
|
_splitpath(plib->szName, NULL, NULL, szName, szExt);
|
|
_makepath(szLib, NULL, NULL, szName, szExt);
|
|
|
|
for (i = 0; i < nCrtNames; i++) {
|
|
if (!_stricmp(szLib, rgszCRTNames[i])) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i != nCrtNames) {
|
|
if (plibCrt == NULL) {
|
|
plibCrt = plib;
|
|
} else {
|
|
printf("LINK : warning LNK9999: Run-time Libraries \"%s\" and \"%s\" are both specified and are incompatible",
|
|
plibCrt->szName, plib->szName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
EndEnmLib(&enm_lib);
|
|
#endif
|
|
|
|
if (plibs->fNoDefaultLibs) {
|
|
return;
|
|
}
|
|
|
|
DL *pdl;
|
|
for (pdl = plibs->pdlFirst; pdl != NULL; pdl = pdl->pdlNext) {
|
|
|
|
// ignore nodefaultlibs
|
|
if (pdl->flags & LIB_DontSearch) {
|
|
continue;
|
|
}
|
|
|
|
// ignore libs specified on cmdline
|
|
PLIB plib = FindLib(pdl->szName, plibs);
|
|
if (plib && !(plib->flags & LIB_Default)) {
|
|
continue;
|
|
}
|
|
|
|
// warn if defaultlib & excludelib
|
|
if ((pdl->flags & LIB_Default) && (pdl->flags & LIB_Exclude)) {
|
|
|
|
if (pdl->pmod) {
|
|
char szComFileName[_MAX_PATH * 2];
|
|
|
|
SzComNamePMOD(pdl->pmod, szComFileName);
|
|
Warning(szComFileName, CONFLICTINGLIB, pdl->szName);
|
|
} else {
|
|
Warning(NULL, CONFLICTINGLIB, pdl->szName);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
WarningNoModulesExtracted(
|
|
PLIB plibHead)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Warn about no modules extracted from a supplied library.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ENM_LIB enm_lib;
|
|
|
|
InitEnmLib(&enm_lib, plibHead);
|
|
while (FNextEnmLib(&enm_lib)) {
|
|
PLIB plib;
|
|
|
|
plib = enm_lib.plib;
|
|
assert(plib);
|
|
|
|
// make sure this isn't the dummy LIB for command line objects
|
|
if (plib->szName) {
|
|
if (!(plib->flags & LIB_Extract) &&
|
|
!(plib->flags & LIB_DontSearch)) {
|
|
Warning(NULL, NOMODULESEXTRACTED, plib->szName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
LocateUndefinedWeakExternals (
|
|
PIMAGE pimage,
|
|
DWORD Type
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Assigns all undefined weak externs to their default routines.
|
|
|
|
Arguments:
|
|
|
|
pst - Pointer to external structure to search for undefines in.
|
|
|
|
Type - combination of EXTERN_WEAK, EXTERN_LAZY, EXTERN_ALIAS
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
WEAK_EXTERN_LIST *pwel = pwelHead;
|
|
|
|
while (pwel) {
|
|
PEXTERNAL pext = pwel->pext;
|
|
PEXTERNAL pextWeakDefault = pwel->pextWeakDefault;
|
|
|
|
if ((pext->Flags & Type) &&
|
|
(pextWeakDefault->Flags & EXTERN_DEFINED)) {
|
|
// When syms are being emitted this routine is called and all values
|
|
// are messed up (for ilink) because the rva isn't included. Note that
|
|
// this doesn't affect calls during pass1 of non-ilink build (rva=0):azk:
|
|
|
|
pext->ImageSymbol.Value = PsecPCON(pextWeakDefault->pcon)->rva +
|
|
pextWeakDefault->ImageSymbol.Value;
|
|
|
|
pext->ImageSymbol.SectionNumber =
|
|
pextWeakDefault->ImageSymbol.SectionNumber;
|
|
pext->ImageSymbol.Type = pextWeakDefault->ImageSymbol.Type;
|
|
SetDefinedExt(pext, TRUE, pimage->pst);
|
|
pext->pcon = pextWeakDefault->pcon;
|
|
pext->FinalValue = pextWeakDefault->FinalValue;
|
|
|
|
if (pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_POWERPC) {
|
|
if (READ_BIT(pextWeakDefault, fImGlue)) {
|
|
SET_BIT(pext, fImGlue);
|
|
|
|
pext->dwRestoreToc = pextWeakDefault->dwRestoreToc;
|
|
}
|
|
}
|
|
|
|
if (fPowerMac) {
|
|
SET_BIT(pext, sy_WEAKEXT);
|
|
|
|
if (READ_BIT(pextWeakDefault, sy_TOCALLOCATED)) {
|
|
SET_BIT(pext, sy_TOCALLOCATED);
|
|
|
|
// I may be losing TOC entries here - ShankarV
|
|
|
|
pext->ibToc = pextWeakDefault->ibToc;
|
|
}
|
|
|
|
if (READ_BIT(pextWeakDefault, sy_CROSSTOCCALL)) {
|
|
SET_BIT(pext, sy_CROSSTOCCALL);
|
|
}
|
|
|
|
if (READ_BIT(pextWeakDefault, sy_TOCDESCRREL)) {
|
|
SET_BIT(pext, sy_TOCDESCRREL);
|
|
}
|
|
|
|
if (READ_BIT(pextWeakDefault, sy_ISDOTEXTERN)) {
|
|
SET_BIT(pext, sy_ISDOTEXTERN);
|
|
}
|
|
}
|
|
|
|
if (fINCR) {
|
|
// Chain up extern to MOD defining the extern.
|
|
|
|
PMOD pmod = PmodPCON(pext->pcon);
|
|
|
|
assert(pmod);
|
|
if (pmod->pextFirstDefined) {
|
|
pext->pextNextDefined = pmod->pextFirstDefined;
|
|
}
|
|
|
|
pmod->pextFirstDefined = pext;
|
|
}
|
|
}
|
|
|
|
pwel = pwel->pwelNext;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
FInferEntryPoint(
|
|
PIMAGE pimage
|
|
)
|
|
{
|
|
const char *sz;
|
|
BOOL fAmbiguousEntry;
|
|
|
|
assert(EntryPointName == NULL);
|
|
|
|
// No default entry point for ROM or VxD images
|
|
|
|
if (pimage->Switch.Link.fROM) {
|
|
// No default entry point for ROM images
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
if (pimage->imaget == imagetVXD) {
|
|
// No default entry point for VxD images
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
// Select a default entry point, depending on the subsystem
|
|
|
|
sz = NULL;
|
|
|
|
fAmbiguousEntry = FALSE;
|
|
|
|
if (!fDLL(pimage)) {
|
|
switch (pimage->ImgOptHdr.Subsystem) {
|
|
PEXTERNAL pext;
|
|
PEXTERNAL pextw;
|
|
|
|
case IMAGE_SUBSYSTEM_NATIVE :
|
|
sz = "NtProcessStartup";
|
|
break;
|
|
|
|
case IMAGE_SUBSYSTEM_WINDOWS_GUI :
|
|
LookForWinMain(pimage, &pext, &pextw);
|
|
|
|
// Assume ANSI entrypoint
|
|
|
|
sz = "WinMainCRTStartup";
|
|
|
|
if (pextw != NULL) {
|
|
if (pext != NULL) {
|
|
fAmbiguousEntry = TRUE;
|
|
} else {
|
|
sz = "wWinMainCRTStartup";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IMAGE_SUBSYSTEM_WINDOWS_CUI :
|
|
case IMAGE_SUBSYSTEM_MMOSA :
|
|
LookForMain(pimage, &pext, &pextw);
|
|
|
|
// Assume ANSI entrypoint
|
|
|
|
sz = "mainCRTStartup";
|
|
|
|
if (pextw != NULL) {
|
|
if (pext != NULL) {
|
|
fAmbiguousEntry = TRUE;
|
|
} else {
|
|
sz = "wmainCRTStartup";
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IMAGE_SUBSYSTEM_POSIX_CUI :
|
|
sz = "__PosixProcessStartup";
|
|
break;
|
|
|
|
default:
|
|
// Should have valid subsystem at this point
|
|
|
|
assert(FALSE);
|
|
break;
|
|
}
|
|
} else if (!pimage->Switch.Link.fNoEntry && !fPowerMac) {
|
|
// No default DLL entry point for PowerMac
|
|
|
|
if ((pimage->ImgOptHdr.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) ||
|
|
(pimage->ImgOptHdr.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI) ||
|
|
(pimage->ImgOptHdr.Subsystem == IMAGE_SUBSYSTEM_MMOSA)) {
|
|
switch (pimage->ImgFileHdr.Machine) {
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
// UNDONE: This name is decorated because fuzzy
|
|
// UNDONE: lookup doesn't find this symbol.
|
|
|
|
sz = "_DllMainCRTStartup@12";
|
|
break;
|
|
|
|
default :
|
|
sz = "_DllMainCRTStartup";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sz == NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
if (fAmbiguousEntry) {
|
|
Warning(NULL, ENTRY_AMBIGUOUS, sz);
|
|
}
|
|
|
|
EntryPointName = SzDup(sz);
|
|
|
|
return(FSetEntryPoint(pimage));
|
|
}
|
|
|
|
|
|
BOOL
|
|
FInferSubsystemAndEntry(PIMAGE pimage)
|
|
{
|
|
PST pst = pimage->pst;
|
|
PIMAGE_OPTIONAL_HEADER pImgOptHdr = &pimage->ImgOptHdr;
|
|
PEXTERNAL pextMain;
|
|
PEXTERNAL pextwMain;
|
|
PEXTERNAL pextWinMain;
|
|
PEXTERNAL pextwWinMain;
|
|
BOOL fConsole;
|
|
BOOL fWindows;
|
|
|
|
assert(fNeedSubsystem);
|
|
assert(pimage->ImgFileHdr.Machine != IMAGE_FILE_MACHINE_UNKNOWN);
|
|
assert(pImgOptHdr->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN);
|
|
|
|
// Look for main(), wmain(), WinMain(), and wWinMain()
|
|
|
|
LookForMain(pimage, &pextMain, &pextwMain);
|
|
LookForWinMain(pimage, &pextWinMain, &pextwWinMain);
|
|
|
|
// If any of these symbols are defined or referenced
|
|
// than we use that to infer the subsystem.
|
|
|
|
fConsole = (pextMain != NULL) || (pextwMain != NULL);
|
|
fWindows = (pextWinMain != NULL) || (pextwWinMain != NULL);
|
|
|
|
if (fConsole) {
|
|
if (fWindows) {
|
|
Warning(NULL, SUBSYSTEM_AMBIGUOUS);
|
|
}
|
|
|
|
pImgOptHdr->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
|
|
} else if (fWindows) {
|
|
pImgOptHdr->Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
|
} else {
|
|
// Still not found.
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
fNeedSubsystem = FALSE;
|
|
|
|
if (pextEntry != NULL) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// Set entry point default from subsystem
|
|
|
|
return(FInferEntryPoint(pimage));
|
|
}
|
|
|
|
|
|
void
|
|
SetDefaultSubsystemVersion(PIMAGE pimage)
|
|
{
|
|
WORD wMajor;
|
|
WORD wMinor;
|
|
|
|
switch (pimage->ImgOptHdr.Subsystem) {
|
|
case IMAGE_SUBSYSTEM_NATIVE :
|
|
case IMAGE_SUBSYSTEM_WINDOWS_GUI :
|
|
case IMAGE_SUBSYSTEM_WINDOWS_CUI :
|
|
case IMAGE_SUBSYSTEM_MMOSA :
|
|
wMajor = 4;
|
|
wMinor = 0;
|
|
break;
|
|
|
|
case IMAGE_SUBSYSTEM_POSIX_CUI :
|
|
wMajor = 19;
|
|
wMinor = 90;
|
|
break;
|
|
|
|
default :
|
|
wMajor = 0;
|
|
wMinor = 0;
|
|
break;
|
|
}
|
|
|
|
pimage->ImgOptHdr.MajorSubsystemVersion = wMajor;
|
|
pimage->ImgOptHdr.MinorSubsystemVersion = wMinor;
|
|
}
|
|
|
|
|
|
void
|
|
Pass1(
|
|
PIMAGE pimage)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
First pass of the linker. All objects section headers are read to
|
|
calculate unique section names, section sizes, and all defined
|
|
externs within each object are added to the extern table.
|
|
Then the libraries are searched against for any undefined externs
|
|
which will also calculate unique section names, section sizes and
|
|
defined externs.
|
|
|
|
Arguments:
|
|
|
|
pst - external symbol table
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PARGUMENT_LIST argument;
|
|
BOOL fFirstObj = 0;
|
|
DWORD i;
|
|
|
|
#ifdef INSTRUMENT
|
|
LogNoteEvent(Log, SZILINK, SZPASS1, letypeBegin, NULL);
|
|
#endif // INSTRUMENT
|
|
|
|
VERBOSE(fputc('\n', stdout); Message(STRTPASS1));
|
|
|
|
if (Tool == Linker && pimage->Switch.Link.Out && !fOpenedOutFilename) {
|
|
// If the output filename was set on the command line by the user, open
|
|
// it now. (If it is still the default then we wait to see if one of
|
|
// the .obj files will set it in a directive.)
|
|
//
|
|
// The advantage of opening it earlier is that we give an error message
|
|
// earlier (without taking the time to do Pass1) if the open fails,
|
|
// e.g. because the user still has a debugging session open to the .exe
|
|
// file.
|
|
|
|
CheckDupFilename(OutFilename, FilenameArguments.First);
|
|
FileWriteHandle = FileOpen(OutFilename,
|
|
O_RDWR | O_BINARY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
|
|
|
|
fOpenedOutFilename = TRUE;
|
|
|
|
fdExeFile = FileWriteHandle;
|
|
}
|
|
|
|
// Create a dummy library node for all command line objects
|
|
|
|
plibCmdLineObjs = PlibNew(NULL, 0L, &pimage->libs);
|
|
pimage->plibCmdLineObjs = plibCmdLineObjs;
|
|
|
|
// Exclude dummy library from library search
|
|
|
|
plibCmdLineObjs->flags |= (LIB_DontSearch | LIB_LinkerDefined);
|
|
|
|
for (i = 0, argument = FilenameArguments.First;
|
|
i < FilenameArguments.Count;
|
|
i++, argument = argument->Next) {
|
|
|
|
if (szReproDir != NULL) {
|
|
CopyFileToReproDir(argument->ModifiedName, TRUE);
|
|
}
|
|
|
|
Pass1Arg(argument, pimage, plibCmdLineObjs);
|
|
}
|
|
|
|
InternalError.CombinedFilenames[0] = '\0';
|
|
|
|
WarningNoObjectFiles(pimage, plibCmdLineObjs);
|
|
|
|
if (cextWeakOrLazy != 0) {
|
|
// Assign all weak externs to their default routine.
|
|
|
|
LocateUndefinedWeakExternals(pimage, EXTERN_WEAK);
|
|
}
|
|
|
|
if (fPowerMac && (Tool == Linker || Tool == Librarian)) {
|
|
// Sneaky peek ahead into /MAC:INIT and /MAC:TERM so that an extensive
|
|
// search during ResolveExternalsInLibs may be avoided later
|
|
|
|
// Force reference to Init and Term entry points before library search
|
|
|
|
if (FUsedOpt(pimage->SwitchInfo, OP_MACINIT)) {
|
|
LookupExternSz(pimage->pst, pimage->SwitchInfo.szMacInit, NULL);
|
|
}
|
|
|
|
if (FUsedOpt(pimage->SwitchInfo, OP_MACTERM)) {
|
|
LookupExternSz(pimage->pst, pimage->SwitchInfo.szMacTerm, NULL);
|
|
}
|
|
|
|
// WLM might have an entry point coming from a directive in a library
|
|
if (!EntryPointName) {
|
|
ResolveExternalsInLibs(pimage);
|
|
}
|
|
}
|
|
|
|
|
|
if (Tool == Linker) {
|
|
if ((pextEntry == NULL) && (EntryPointName != NULL)) {
|
|
FSetEntryPoint(pimage);
|
|
}
|
|
|
|
fNeedSubsystem =
|
|
(pimage->ImgOptHdr.Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) &&
|
|
!fM68K &&
|
|
!fPowerMac &&
|
|
!fDLL(pimage) &&
|
|
!pimage->Switch.Link.fROM;
|
|
|
|
// Infer a subsystem and possibly an entry point. This is done
|
|
// here because it may save one call to ResolveExternalsInLibs.
|
|
|
|
if (fNeedSubsystem) {
|
|
FInferSubsystemAndEntry(pimage);
|
|
} else if (pextEntry == NULL) {
|
|
FInferEntryPoint(pimage);
|
|
}
|
|
}
|
|
|
|
ResolveExternalsInLibs(pimage);
|
|
|
|
if (Tool == Linker) {
|
|
// Handle the .DEF file and build export table if any.
|
|
|
|
if (FPass1DefFile(pimage, DefFilename)) {
|
|
// Export table might introduced unresolved externals
|
|
|
|
ResolveExternalsInLibs(pimage);
|
|
}
|
|
|
|
// do we still need to infer a subsystem(.exp file may have given us a clue)
|
|
fNeedSubsystem =
|
|
(pimage->ImgOptHdr.Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) &&
|
|
!fM68K &&
|
|
!fPowerMac &&
|
|
!fDLL(pimage) &&
|
|
!pimage->Switch.Link.fROM;
|
|
|
|
if (fNeedSubsystem) {
|
|
// Infer a subsystem and possibly an entry point
|
|
|
|
if (FInferSubsystemAndEntry(pimage)) {
|
|
// New entry point is an unresolved external.
|
|
// Search for it and any symbols this introduces.
|
|
|
|
ResolveExternalsInLibs(pimage);
|
|
}
|
|
} else if (pextEntry == NULL) {
|
|
// subsystem known but need to infer a entrypoint
|
|
|
|
if (FInferEntryPoint(pimage)) {
|
|
// New entry point is an unresolved external.
|
|
// Search for it and any symbols this introduces.
|
|
|
|
ResolveExternalsInLibs(pimage);
|
|
}
|
|
}
|
|
|
|
if (fPowerMac) {
|
|
CreateEntryInitTermDescriptors(&pextEntry, pimage);
|
|
ResolveExternalsInLibs(pimage);
|
|
}
|
|
|
|
if ((pextEntry == NULL) && !fPowerMac && !fDLL(pimage) && pimage->imaget != imagetVXD) {
|
|
Fatal(NULL, MACNOENTRY);
|
|
}
|
|
|
|
if (pimage->ImgOptHdr.Subsystem != IMAGE_SUBSYSTEM_UNKNOWN) {
|
|
if ((pimage->ImgOptHdr.MajorSubsystemVersion == 0) &&
|
|
(pimage->ImgOptHdr.MinorSubsystemVersion == 0)) {
|
|
SetDefaultSubsystemVersion(pimage);
|
|
}
|
|
}
|
|
|
|
if (!fOpenedOutFilename) {
|
|
// We now know the output filename, if we didn't before.
|
|
|
|
if (OutFilename == NULL) {
|
|
Fatal(NULL, NOOUTPUTFILE);
|
|
}
|
|
|
|
CheckDupFilename(OutFilename, FilenameArguments.First);
|
|
|
|
FileWriteHandle = FileOpen(OutFilename,
|
|
O_RDWR | O_BINARY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
|
|
|
|
fOpenedOutFilename = TRUE;
|
|
}
|
|
|
|
fdExeFile = FileWriteHandle;
|
|
|
|
// image base cannot be less than 4M for win95
|
|
// by now we know target platform, subsystem etc.
|
|
if (pimage->Switch.Link.Base &&
|
|
pimage->ImgOptHdr.ImageBase < 0x400000 &&
|
|
pimage->ImgFileHdr.Machine == IMAGE_FILE_MACHINE_I386 &&
|
|
pimage->ImgOptHdr.Subsystem != IMAGE_SUBSYSTEM_UNKNOWN &&
|
|
pimage->ImgOptHdr.Subsystem != IMAGE_SUBSYSTEM_NATIVE) {
|
|
|
|
Warning(NULL, INVALIDWIN95BASE, pimage->ImgOptHdr.ImageBase);
|
|
|
|
}
|
|
}
|
|
|
|
// This is under -verbose for now but should be under -warn later ...
|
|
|
|
if (WarningLevel > 1) {
|
|
WarningNoModulesExtracted(pimage->libs.plibHead);
|
|
}
|
|
|
|
// check to see if any conflicting libs
|
|
if (Tool == Linker) {
|
|
WarningConflictingLibs(&pimage->libs);
|
|
}
|
|
|
|
if (cextWeakOrLazy != 0) {
|
|
// Assign all lazy externs to their default routines.
|
|
|
|
LocateUndefinedWeakExternals(pimage, EXTERN_LAZY | EXTERN_ALIAS);
|
|
}
|
|
|
|
if (cextWeakOrLazy != 0) {
|
|
// Assign all weak externs to their default routine. Need to do this a
|
|
// second time since some may have become defined by library searches.
|
|
|
|
LocateUndefinedWeakExternals(pimage, EXTERN_WEAK);
|
|
}
|
|
|
|
if (Tool == Linker) {
|
|
ENM_LIB enmLib;
|
|
|
|
// Free our allocated copies of .lib directories.
|
|
// The librarian needs them for fuzzy lookup.
|
|
|
|
InitEnmLib(&enmLib, pimage->libs.plibHead);
|
|
while (FNextEnmLib(&enmLib)) {
|
|
|
|
if (enmLib.plib == plibCmdLineObjs) {
|
|
continue;
|
|
}
|
|
|
|
FreePv(enmLib.plib->rgulSymMemOff);
|
|
FreePv(enmLib.plib->rgusOffIndex);
|
|
FreePv(enmLib.plib->rgbST);
|
|
FreePv(enmLib.plib->rgszSym);
|
|
FreePv(enmLib.plib->rgbLongFileNames);
|
|
|
|
enmLib.plib->rgulSymMemOff = NULL;
|
|
enmLib.plib->rgusOffIndex = NULL;
|
|
enmLib.plib->rgbST = NULL;
|
|
enmLib.plib->rgszSym = NULL;
|
|
enmLib.plib->rgbLongFileNames = NULL;
|
|
}
|
|
}
|
|
|
|
if (Verbose) {
|
|
fputc('\n', stdout);
|
|
Message(ENDPASS1);
|
|
fputc('\n', stdout);
|
|
}
|
|
|
|
if (fPowerMac || fM68K) {
|
|
PARGUMENT_LIST pal;
|
|
// Now we are sure that there won't be anymore resource files
|
|
// coming from drectves in the library or object modules
|
|
// It's time to get some space reserved for them.
|
|
// Remember that this would be processed only with the first
|
|
// full iLink and not subsequent iLinks.
|
|
for (i = 0, pal = MacResourceList.First;
|
|
i < MacResourceList.Count;
|
|
i++, pal = pal->Next) {
|
|
|
|
// Case sensitive comparison
|
|
if (!strcmp(pal->ModifiedName, "resntBinaryResource")) {
|
|
UseMacBinaryRes(pal->OriginalName, resntBinaryResource, -1);
|
|
} else {
|
|
// This could be resntDataFork
|
|
assert(strcmp(pal->ModifiedName, "resntBinaryResource"));
|
|
}
|
|
}
|
|
FreeArgumentList(&MacResourceList);
|
|
}
|
|
|
|
#ifdef INSTRUMENT
|
|
LogNoteEvent(Log, SZILINK, SZPASS1, letypeEnd, NULL);
|
|
#endif // INSTRUMENT
|
|
}
|