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.
2614 lines
79 KiB
2614 lines
79 KiB
/***********************************************************************
|
|
* Microsoft (R) 32-Bit Incremental Linker
|
|
*
|
|
* Copyright (C) Microsoft Corp 1992-1996. All rights reserved.
|
|
*
|
|
* File: lnkmain.cpp
|
|
*
|
|
* File Comments:
|
|
*
|
|
* Main entrypoint to the COFF Linker.
|
|
*
|
|
***********************************************************************/
|
|
|
|
#include "link.h"
|
|
|
|
|
|
extern PIMAGE pimageDeflib;
|
|
|
|
BOOL fIncrSwitchUsed; // User specified the incremental switch
|
|
BOOL fIncrSwitchValue; // User specified value
|
|
BOOL fExportDirective; // export directives were seen
|
|
BOOL fDbgImpLib = FALSE; // build a dbg implib
|
|
static DEBUG_TYPE dtUser; // User-specified debugtype
|
|
static int savArgc; // saved argc
|
|
static char **savArgv; // saved argv
|
|
DWORD cbHdrSize;
|
|
|
|
|
|
BOOL
|
|
FScanSwitches(const char *szOption)
|
|
{
|
|
WORD i;
|
|
PARGUMENT_LIST argument;
|
|
|
|
for (i = 0, argument = SwitchArguments.First;
|
|
i < SwitchArguments.Count;
|
|
i++, argument = argument->Next) {
|
|
if (_stricmp(szOption, argument->OriginalName) == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void
|
|
LinkerUsage(void)
|
|
{
|
|
if (fNeedBanner) {
|
|
PrintBanner();
|
|
}
|
|
|
|
// UNDONE: Options for unreleased products are supressed for the
|
|
// UNDONE: NT build. This is so an external release of the linker
|
|
// UNDONE: for NT 3.5 will not document unreleased products. No
|
|
// UNDONE: functionality is disabled in the NT version.
|
|
|
|
puts("usage: LINK [options] [files] [@commandfile]\n"
|
|
"\n"
|
|
" options:\n"
|
|
"\n"
|
|
" /ALIGN:#\n"
|
|
" /BASE:{address|@filename,key}\n"
|
|
" /COMMENT:comment\n"
|
|
" /DEBUG\n"
|
|
" /DEBUGTYPE:{CV|COFF|BOTH}\n"
|
|
" /DEF:filename\n"
|
|
" /DEFAULTLIB:library\n"
|
|
" /DLL\n"
|
|
" /DLLCHAR:X86THUNK\n"
|
|
" /DRIVER[:UPONLY]\n"
|
|
" /ENTRY:symbol\n"
|
|
" /EXETYPE:DYNAMIC\n"
|
|
" /EXPORT:symbol\n"
|
|
" /FIXED\n"
|
|
" /FORCE[:{MULTIPLE|UNRESOLVED}]\n"
|
|
" /GPSIZE:#\n"
|
|
" /HEAP:reserve[,commit]\n"
|
|
" /IMPORT:[symbol][,][LIB=container][,WEAK=1]\n" // PowerMac
|
|
" /IMPORT:[CURRENTVER=#][,][OLDCODEVER=#][,][OLDAPIVER=#]\n" // PowerMac
|
|
" /IMPLIB:filename\n"
|
|
" /INCLUDE:symbol\n"
|
|
" /INCREMENTAL:{YES|NO}\n"
|
|
" /MAC:{BUNDLE|NOBUNDLE|TYPE=xxxx|CREATOR=xxxx|INIT=symbol|TERM=symbol}\n"
|
|
#ifdef MFILE_PAD
|
|
" /MAC:{MFILEPAD|NOMFILEPAD}\n"
|
|
#endif
|
|
" /MACDATA:filename\n"
|
|
" /MACHINE:{IX86|MIPS|ALPHA|PPC|M68K|MPPC}\n"
|
|
" /MACRES:filename\n"
|
|
" /MAP[:filename]\n"
|
|
" /MERGE:from=to\n"
|
|
" /NODEFAULTLIB[:library]\n"
|
|
" /NOENTRY\n"
|
|
" /NOLOGO\n"
|
|
" /OPT:{REF|NOREF}\n"
|
|
" /ORDER:@filename\n"
|
|
" /OUT:filename\n"
|
|
" /PDB:{filename|NONE}\n"
|
|
" /PROFILE\n"
|
|
" /RELEASE\n"
|
|
" /SECTION:name,[E][R][W][S][D][K][L][P][X]\n"
|
|
" /SHARED\n" // PowerMac
|
|
" /STACK:reserve[,commit]\n"
|
|
" /STUB:filename\n"
|
|
" /SUBSYSTEM:{NATIVE|WINDOWS|CONSOLE|POSIX}[,#[.##]]\n"
|
|
" /SWAPRUN:{NET|CD}\n"
|
|
" /VERBOSE[:LIB]\n"
|
|
" /VERSION:#[.#]\n"
|
|
" /VXD\n"
|
|
" /WARN[:warninglevel]\n"
|
|
" /WS:AGGRESSIVE");
|
|
|
|
exit(USAGE);
|
|
}
|
|
|
|
|
|
BOOL
|
|
FIncrementalLinkSupported(PIMAGE pimage)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Validates that ilink is supported for the machine type.
|
|
If machine type is not already known, checks all command
|
|
line argument files for a machine type stamp. If no files
|
|
indicate machine type, then go ahead and try an ilink, but if
|
|
it later turns out we can't do ilink on this machine, Error.
|
|
|
|
Arguments:
|
|
|
|
pimage - Pointer to the image.
|
|
|
|
Return Value:
|
|
|
|
FALSE - Ilink not supported for machine..
|
|
TRUE - Ilink supported for machine (or still unknown).
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
PARGUMENT_LIST argument;
|
|
|
|
switch (pimage->ImgFileHdr.Machine) {
|
|
case IMAGE_FILE_MACHINE_UNKNOWN :
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_ALPHA :
|
|
case IMAGE_FILE_MACHINE_I386 :
|
|
case IMAGE_FILE_MACHINE_R4000:
|
|
case IMAGE_FILE_MACHINE_R10000:
|
|
cbExternal = offsetof(EXTERNAL, psecRef);
|
|
case IMAGE_FILE_MACHINE_MPPC_601:
|
|
return(TRUE);
|
|
|
|
default :
|
|
return(FALSE);
|
|
}
|
|
|
|
// Check all command line files for object files with machine type
|
|
|
|
for (i = 0, argument = FilenameArguments.First;
|
|
i < FilenameArguments.Count;
|
|
i++, argument = argument->Next) {
|
|
INT fhObj;
|
|
IMAGE_FILE_HEADER ImageFileHdr;
|
|
|
|
fhObj = FileOpen(argument->OriginalName, O_RDONLY | O_BINARY, 0);
|
|
|
|
if (IsArchiveFile(argument->OriginalName, fhObj)) {
|
|
FileClose(fhObj, FALSE);
|
|
continue;
|
|
}
|
|
|
|
// Could be an obj file, read header
|
|
|
|
FileSeek(fhObj, 0, SEEK_SET);
|
|
ReadFileHeader(fhObj, &ImageFileHdr);
|
|
FileClose(fhObj, FALSE);
|
|
|
|
switch (ImageFileHdr.Machine) {
|
|
case IMAGE_FILE_MACHINE_UNKNOWN :
|
|
// Keep looking
|
|
|
|
break;
|
|
|
|
case IMAGE_FILE_MACHINE_ALPHA :
|
|
case IMAGE_FILE_MACHINE_I386:
|
|
case IMAGE_FILE_MACHINE_R4000:
|
|
case IMAGE_FILE_MACHINE_R10000:
|
|
cbExternal = offsetof(EXTERNAL, psecRef);
|
|
case IMAGE_FILE_MACHINE_MPPC_601:
|
|
// Incremental link supported
|
|
|
|
return(TRUE);
|
|
|
|
default:
|
|
// Incremental link not supported
|
|
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
// still don't know whether machine is supported.
|
|
// try to ilink and error out later if not
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
void
|
|
ProcessLinkerSwitches (
|
|
PIMAGE pimage,
|
|
PCON pcon,
|
|
const char *szFilename
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Process all linker switches.
|
|
|
|
Arguments:
|
|
|
|
pimage - image
|
|
|
|
pcon - Non-NULL if switch is from a directive
|
|
|
|
szFilename - name of file in the case of switch is from directive
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
#define ImageFileHdr (pimage->ImgFileHdr)
|
|
#define ImageOptionalHdr (pimage->ImgOptHdr)
|
|
#define Switch (pimage->Switch)
|
|
#define SwitchInfo (pimage->SwitchInfo)
|
|
|
|
BOOL IsDirective;
|
|
DWORD i;
|
|
INT good_scan;
|
|
INT next;
|
|
DWORD major;
|
|
DWORD minor;
|
|
char fileKey[_MAX_PATH];
|
|
char *name;
|
|
char *token;
|
|
char *p;
|
|
PARGUMENT_LIST argument;
|
|
FILE *file_read_stream;
|
|
char *szReproOption;
|
|
PST pst = pimage->pst;
|
|
BOOL fAmountSet = FALSE;
|
|
|
|
IsDirective = (pcon != NULL);
|
|
|
|
for (i = 0, argument = SwitchArguments.First;
|
|
i < SwitchArguments.Count;
|
|
i++, argument = argument->Next,
|
|
(!IsDirective && (szReproDir != NULL)
|
|
? fprintf(pfileReproResponse, "/%s\n", szReproOption)
|
|
: 0), FreePv(szReproOption)) {
|
|
WORD iarpv;
|
|
DWORD dwVal;
|
|
char *szVal;
|
|
char szFname[_MAX_FNAME + _MAX_EXT];
|
|
char szExt[_MAX_EXT];
|
|
|
|
argument->parp = ParpParseSz(argument->OriginalName);
|
|
iarpv = 0; // we will gen warning if all val's not consumed
|
|
|
|
// The default is to copy the option verbatim to the repro directory,
|
|
// but the option-handling code may change this if the option contains
|
|
// a filename.
|
|
|
|
szReproOption = SzDup(argument->OriginalName);
|
|
|
|
if (!strcmp(argument->OriginalName, "?")) {
|
|
LinkerUsage();
|
|
assert(FALSE); // doesn't return
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "batch")) {
|
|
goto ProcessedArg; // quietly ignore -batch
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "comment")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
szVal = argument->parp->rgarpv[iarpv++].szVal;
|
|
|
|
IbAppendBlk(&blkComment, szVal, strlen(szVal) + 1);
|
|
SetOpt(SwitchInfo, OP_COMMENT);
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "exetype")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (!_stricmp(szVal, "dynamic")) {
|
|
pimage->fDynamicVxd = TRUE;
|
|
} else if (!_stricmp(szVal, "dev386")) {
|
|
// UNDONE: This is obsolete
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
// UNDONE: temp param for handling VxD header size problem
|
|
|
|
if (!_stricmp(argument->parp->szArg, "hdrsize")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
if (!FNumParp(argument->parp, iarpv++, &dwVal)) {
|
|
goto BadNum;
|
|
}
|
|
|
|
cbHdrSize = dwVal;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "mac")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
char *szKey = argument->parp->rgarpv[iarpv].szKeyword;
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (szKey != NULL && !_stricmp(szKey, "type")) {
|
|
Switch.Link.szMacType = szVal;
|
|
} else if (szKey != NULL && !_stricmp(szKey, "creator")) {
|
|
Switch.Link.szMacCreator = szVal;
|
|
} else if (szKey != NULL && !_stricmp(szKey, "init")) {
|
|
MppcSetInitRoutine(pimage, szVal);
|
|
SetOpt(SwitchInfo, OP_MACINIT);
|
|
if (IsDirective) {
|
|
SetOpt(SwitchInfo, OP_MACINITLIB);
|
|
}
|
|
} else if (szKey != NULL && !_stricmp(szKey, "term")) {
|
|
MppcSetTermRoutine(pimage, szVal);
|
|
SetOpt(SwitchInfo, OP_MACTERM);
|
|
if (IsDirective) {
|
|
SetOpt(SwitchInfo, OP_MACTERMLIB);
|
|
}
|
|
} else if (!_stricmp(szVal, "bundle")) {
|
|
Switch.Link.fMacBundle = TRUE;
|
|
} else if (!_stricmp(szVal, "nobundle")) {
|
|
Switch.Link.fMacBundle = FALSE;
|
|
#ifdef MFILE_PAD
|
|
} else if (!_stricmp(szVal, "mfilepad")) {
|
|
fMfilePad = TRUE;
|
|
SetOpt(SwitchInfo, OP_MFILEPAD);
|
|
} else if (!_stricmp(szVal, "nomfilepad")) {
|
|
fMfilePad = FALSE;
|
|
SetOpt(SwitchInfo, OP_MFILEPAD);
|
|
#endif
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "macres")) {
|
|
if (argument->OriginalName[7] == '\0') {
|
|
goto MissingVal;
|
|
}
|
|
|
|
// TODO: Use GetMacResourcePointer instead of FArgumentInList - ShankarV
|
|
if (!FArgumentInList(argument->OriginalName+7, &MacResourceList)) {
|
|
AddArgumentToList(&MacResourceList, argument->OriginalName+7,
|
|
"resntBinaryResource");
|
|
SetOpt(SwitchInfo, OP_MACRES);
|
|
}
|
|
argument->parp->carpv = ++iarpv; // only one arg
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "macdata")) {
|
|
if (argument->OriginalName[8] == '\0') {
|
|
goto MissingVal;
|
|
}
|
|
|
|
UseMacBinaryRes(argument->OriginalName+8, resntDataFork, -1);
|
|
argument->parp->carpv = ++iarpv; // assume only one arg
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "name")) {
|
|
|
|
if (!IsDirective || pimage->imaget != imagetVXD) {
|
|
Warning(szFilename, WARN_UNKNOWN_SWITCH, argument->OriginalName);
|
|
continue;
|
|
}
|
|
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
iarpv++;
|
|
|
|
szModuleName = argument->parp->rgarpv[0].szVal;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "nopack")) {
|
|
Switch.Link.fNoPack = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
#ifdef ILINKLOG
|
|
if (!_stricmp(argument->OriginalName, "noilinklog")) {
|
|
fIlinkLog = FALSE;
|
|
goto ProcessedArg;
|
|
}
|
|
#endif // ILINKLOG
|
|
|
|
if (!_stricmp(argument->OriginalName, "xoff")) {
|
|
fExceptionsOff = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "nologo")) {
|
|
fNeedBanner = FALSE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "profile")) {
|
|
Switch.Link.fProfile = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "test")) {
|
|
fTest = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "fullbuild")) {
|
|
Switch.Link.fNotifyFullBuild = FALSE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "export")) {
|
|
if (argument->OriginalName[6] != ':') {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
|
|
if (Tool == Librarian) {
|
|
assert(IsDirective);
|
|
ParseExportDirective(argument->OriginalName+7, pimageDeflib,
|
|
TRUE, szFilename);
|
|
} else if (Tool == Linker) {
|
|
if (!IsDirective || pimage->imaget == imagetVXD) {
|
|
AddArgumentToList(&ExportSwitches, argument->OriginalName+7, NULL);
|
|
} else {
|
|
fExportDirective = TRUE;
|
|
}
|
|
}
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "defaultlib:", 11 )) {
|
|
if (argument->OriginalName[11] == '\0') {
|
|
goto MissingVal;
|
|
}
|
|
|
|
if (Switch.Link.fNoDefaultLibs) {
|
|
// Skip all values
|
|
|
|
iarpv = argument->parp->carpv;
|
|
} else {
|
|
MakeDefaultLib(&argument->OriginalName[11], &pimage->libs);
|
|
argument->parp->carpv = ++iarpv; // assume only one arg
|
|
}
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "disallowlib")) {
|
|
if (argument->OriginalName[12] == '\0') {
|
|
goto MissingVal;
|
|
}
|
|
|
|
if (Switch.Link.fNoDefaultLibs) {
|
|
// Skip all values
|
|
|
|
iarpv = argument->parp->carpv;
|
|
} else {
|
|
ExcludeLib(&argument->OriginalName[12],
|
|
&pimage->libs,
|
|
pcon ? PmodPCON(pcon) : NULL);
|
|
argument->parp->carpv = ++iarpv; // assume only one arg
|
|
}
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "opt")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (_stricmp(szVal, "ref") == 0) {
|
|
Switch.Link.fTCE = TRUE;
|
|
fExplicitOptRef = TRUE;
|
|
} else if (_stricmp(szVal, "noref") == 0) {
|
|
Switch.Link.fTCE = FALSE;
|
|
fExplicitOptRef = TRUE;
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
next = 0;
|
|
if (!_stricmp(argument->parp->szArg, "nodefaultlib") ||
|
|
!_stricmp(argument->parp->szArg, "nod"))
|
|
{
|
|
if (Switch.Link.fNoDefaultLibs) {
|
|
argument->parp->carpv = ++iarpv; // assume only one arg
|
|
goto ProcessedArg; // redundant
|
|
}
|
|
|
|
next = !_stricmp(argument->parp->szArg, "nodefaultlib") ? 12 : 3;
|
|
|
|
if (argument->OriginalName[next] == ':' &&
|
|
argument->OriginalName[next+1] != '\0') {
|
|
// Lib name given ...
|
|
NoDefaultLib(&argument->OriginalName[next+1], &pimage->libs);
|
|
argument->parp->carpv = ++iarpv; // assume only one arg
|
|
} else {
|
|
// -defaultlib with no argument
|
|
Switch.Link.fNoDefaultLibs = TRUE;
|
|
NoDefaultLib(NULL, &pimage->libs);
|
|
}
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "out")) {
|
|
if (argument->OriginalName[3] != ':' ||
|
|
argument->OriginalName[4] == '\0') {
|
|
goto MissingVal;
|
|
}
|
|
|
|
argument->parp->carpv = ++iarpv; // only one arg
|
|
|
|
// If the user used the out switch, then ignore
|
|
// any directive that sets the filename.
|
|
|
|
if (Switch.Link.Out && IsDirective) {
|
|
_splitpath(OutFilename, NULL, NULL, szFname, szExt);
|
|
strcat(szFname, szExt);
|
|
|
|
// Warn if directive doesn't match with output filename
|
|
// For PowerMac the names can differ (internal name vs filename)
|
|
if (_tcsicmp(szFname, argument->OriginalName+4) &&
|
|
ImageFileHdr.Machine != IMAGE_FILE_MACHINE_MPPC_601) {
|
|
Warning(szFilename, OUTDRCTVDIFF,
|
|
argument->OriginalName+4, OutFilename);
|
|
}
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
OutFilename = argument->OriginalName+4;
|
|
Switch.Link.Out = TRUE;
|
|
|
|
if (szReproDir != NULL) {
|
|
_splitpath(OutFilename, NULL, NULL, szFname, szExt);
|
|
FreePv(szReproOption);
|
|
szReproOption = (char *)
|
|
PvAlloc(strlen("out:\".\\%s%s\"")+strlen(szFname)+strlen(szExt));
|
|
sprintf(szReproOption, "out:\".\\%s%s\"", szFname, szExt);
|
|
}
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "release")) {
|
|
Switch.Link.fChecksum = TRUE;
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "base")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
// If the user used the base switch, then ignore
|
|
// any directive that sets the base.
|
|
|
|
if (Switch.Link.Base && IsDirective) {
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (argument->parp->rgarpv[0].szVal[0] == '@') {
|
|
// Base file. First value is @filename ... expect a second
|
|
// value which is the key.
|
|
|
|
char *szBaseFile;
|
|
|
|
if (!FGotVal(argument->parp, 1)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
// Base values are in a command file, so open it.
|
|
|
|
szBaseFile = SzSearchEnv("LIB",
|
|
argument->parp->rgarpv[0].szVal+1,
|
|
".txt");
|
|
|
|
if (szReproDir != NULL) {
|
|
CopyFileToReproDir(szBaseFile, FALSE);
|
|
_splitpath(szBaseFile, NULL, NULL, szFname, szExt);
|
|
FreePv(szReproOption);
|
|
szReproOption = (char *)
|
|
PvAlloc(strlen("base:@\".\\%s%s,%s\"")+strlen(szFname)+
|
|
strlen(szExt)+strlen(argument->parp->rgarpv[1].szVal));
|
|
sprintf(szReproOption, "base:@\".\\%s%s,%s\"", szFname, szExt,
|
|
argument->parp->rgarpv[1].szVal);
|
|
}
|
|
|
|
|
|
if (!(file_read_stream = fopen(szBaseFile, "rt"))) {
|
|
Fatal(szFilename, CANTOPENFILE, szBaseFile);
|
|
}
|
|
|
|
// Read each key from command file until we find a match.
|
|
// fgets() fetches next argument from command file.
|
|
|
|
good_scan = 0;
|
|
|
|
while (!good_scan && fgets(fileKey, _MAX_PATH,
|
|
file_read_stream)) {
|
|
fileKey[strlen(fileKey)-1] = '\0'; // Replace \n with \0.
|
|
if ((p = strchr(fileKey, ';')) != NULL) {
|
|
*p = '\0';
|
|
}
|
|
token = strtok(fileKey, Delimiters);
|
|
while (token) {
|
|
if (_stricmp(token, argument->parp->rgarpv[1].szVal)) {
|
|
break;
|
|
}
|
|
token = strtok(NULL, Delimiters);
|
|
if (token && (good_scan = sscanf(token, "%li", &dwVal)) == 1) {
|
|
ImageOptionalHdr.ImageBase = dwVal;
|
|
token = strtok(NULL, Delimiters);
|
|
if (!token || sscanf(token, "%li", &VerifyImageSize) != 1) {
|
|
Fatal(szBaseFile, BADBASE, token);
|
|
}
|
|
break;
|
|
} else {
|
|
Fatal(szBaseFile, BADBASE, token);
|
|
}
|
|
}
|
|
}
|
|
fclose(file_read_stream);
|
|
|
|
if (!good_scan) {
|
|
Fatal(szBaseFile, KEYNOTFOUND,
|
|
argument->parp->rgarpv[1].szVal);
|
|
}
|
|
|
|
FreePv(szBaseFile);
|
|
Switch.Link.Base = TRUE;
|
|
iarpv = 2; // we processed 2 values
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
switch (argument->parp->carpv) {
|
|
case 1:
|
|
// BASED
|
|
|
|
if (!FNumParp(argument->parp, iarpv,
|
|
&ImageOptionalHdr.ImageBase)) {
|
|
goto BadNum;
|
|
}
|
|
iarpv++;
|
|
break;
|
|
|
|
case 2:
|
|
// BASE, SIZE
|
|
//
|
|
// Supported just to provide compatibility with the
|
|
// syntax of the base file.
|
|
|
|
if (!FNumParp(argument->parp, iarpv,
|
|
&ImageOptionalHdr.ImageBase)) {
|
|
goto BadNum;
|
|
}
|
|
|
|
iarpv++;
|
|
|
|
if (!FNumParp(argument->parp, iarpv,
|
|
&VerifyImageSize)) {
|
|
goto BadNum;
|
|
}
|
|
|
|
iarpv++;
|
|
break;
|
|
|
|
default:
|
|
Fatal(szFilename, BADBASE, argument->OriginalName+5);
|
|
}
|
|
Switch.Link.Base = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "Brepro")) {
|
|
fReproducible = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "pdb")) {
|
|
if (argument->OriginalName[4] == '\0') {
|
|
goto MissingVal;
|
|
}
|
|
|
|
szVal = argument->OriginalName+4;
|
|
|
|
if (!_stricmp(szVal, "none")) {
|
|
fPdb = FALSE;
|
|
} else {
|
|
PdbFilename = argument->OriginalName+4;
|
|
if (szReproDir != NULL) {
|
|
// NOTE: The actual file is copied in DeterminePDBFilename()
|
|
|
|
_splitpath(PdbFilename, NULL, NULL, szFname, szExt);
|
|
if ((szFname[0] != '\0') && (szExt[0] != '\0')) {
|
|
FreePv(szReproOption);
|
|
szReproOption = (char *)
|
|
PvAlloc(strlen(szFname)+strlen(szExt)+strlen("pdb:\".\\%s%s\""));
|
|
sprintf(szReproOption, "pdb:\".\\%s%s\"", szFname, szExt);
|
|
}
|
|
}
|
|
}
|
|
|
|
argument->parp->carpv = ++iarpv; // only one arg
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
next = 0;
|
|
if (!_stricmp(argument->parp->szArg, "debug")) {
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (!_stricmp(szVal, "mapped")) {
|
|
IncludeDebugSection = TRUE;
|
|
} else if (!_stricmp(szVal, "notmapped")) {
|
|
IncludeDebugSection = FALSE;
|
|
} else if (!_stricmp(szVal, "full")) {
|
|
Switch.Link.DebugInfo = Full;
|
|
fAmountSet = TRUE;
|
|
} else if (!_stricmp(szVal, "partial")) {
|
|
Switch.Link.DebugInfo = Partial;
|
|
fAmountSet = TRUE;
|
|
} else if (!_stricmp(szVal, "minimal")) {
|
|
Switch.Link.DebugInfo = Minimal;
|
|
fAmountSet = TRUE;
|
|
} else if (!_stricmp(szVal, "none")) {
|
|
Switch.Link.DebugInfo = None;
|
|
fAmountSet = TRUE;
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
|
|
if (!fAmountSet) {
|
|
// Just -debug, or -debug:notmapped etc. Default to "full".
|
|
|
|
Switch.Link.DebugInfo = Full;
|
|
}
|
|
|
|
fAmountSet = FALSE; // allow for -debug:none followed by -debug
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "merge")) {
|
|
char *pchEqu = _tcschr(argument->OriginalName + 6, '=');
|
|
|
|
if ((pchEqu == NULL) ||
|
|
(pchEqu == (argument->OriginalName + 6)) ||
|
|
(pchEqu[1] == '\0')) {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
|
|
*pchEqu++ = '\0';
|
|
|
|
if (!FValidSecName(argument->OriginalName + 6)) {
|
|
char *szSecName = SzDup(argument->OriginalName + 6);
|
|
|
|
*--pchEqu = '=';
|
|
Fatal(szFilename, INVALIDSECNAME, szSecName, argument->OriginalName);
|
|
}
|
|
|
|
if (!FValidSecName(pchEqu)) {
|
|
*--pchEqu = '=';
|
|
Fatal(szFilename, INVALIDSECNAME, ++pchEqu, argument->OriginalName);
|
|
}
|
|
|
|
AddArgumentToList(&MergeSwitches,
|
|
argument->OriginalName + 6,
|
|
pchEqu);
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "debugtype")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (!_stricmp(szVal, "coff")) {
|
|
dtUser = (DEBUG_TYPE) (dtUser | CoffDebug);
|
|
} else if (!_stricmp(szVal, "cv")) {
|
|
dtUser = (DEBUG_TYPE) (dtUser | CvDebug);
|
|
} else if (!_stricmp(szVal, "both")) {
|
|
dtUser = (DEBUG_TYPE) (dtUser | CoffDebug | CvDebug);
|
|
} else if (!_stricmp(szVal, "fpo")) {
|
|
dtUser = (DEBUG_TYPE) (dtUser | FpoDebug);
|
|
} else if (!_stricmp(szVal, "fixup")) {
|
|
dtUser = (DEBUG_TYPE) (dtUser | FixupDebug);
|
|
} else if (!_stricmp(szVal, "map")) {
|
|
// Force mapfile generation
|
|
|
|
Switch.Link.fMap = TRUE;
|
|
Switch.Link.fMapLines = TRUE;
|
|
SetOpt(SwitchInfo, OP_MAP);
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "entry")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
EntryPointName = SzDup(argument->parp->rgarpv[0].szVal);
|
|
|
|
SwitchInfo.szEntry = EntryPointName;
|
|
iarpv++;
|
|
SetOpt(SwitchInfo, OP_ENTRY);
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "force")) {
|
|
|
|
// can add other values for the /FORCE option in future
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (!_stricmp(szVal, "multiple")) {
|
|
Switch.Link.Force = (FORCE_TYPE) (Switch.Link.Force | ftMultiple);
|
|
} else if (!_stricmp(szVal, "unresolved")) {
|
|
Switch.Link.Force = (FORCE_TYPE) (Switch.Link.Force | ftUnresolved);
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
|
|
if (Switch.Link.Force == ftNone) {
|
|
// /FORCE was specfied without any values
|
|
|
|
Switch.Link.Force = (FORCE_TYPE) (Switch.Link.Force | ftUnresolved | ftMultiple);
|
|
}
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "fixed")) {
|
|
ImageFileHdr.Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;
|
|
Switch.Link.fFixed = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "map", 3)) {
|
|
Switch.Link.fMap = TRUE;
|
|
|
|
if (argument->OriginalName[3] != '\0') {
|
|
// require valid arg and set szInfoFilename
|
|
if ((*(argument->OriginalName+3) == ':') &&
|
|
(*(argument->OriginalName+4))) {
|
|
szInfoFilename = argument->OriginalName+4;
|
|
|
|
if (szReproDir != NULL) {
|
|
_splitpath(szInfoFilename, NULL, NULL, szFname, szExt);
|
|
FreePv(szReproOption);
|
|
szReproOption = (char *)
|
|
PvAlloc(strlen("map:\".\\%s%s\"")+strlen(szFname)+strlen(szExt));
|
|
sprintf(szReproOption, "map:\".\\%s%s\"", szFname, szExt);
|
|
}
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
|
|
SetOpt(SwitchInfo, OP_MAP);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
#ifndef IMAGE_DLLCHARACTERISTICS_X86_THUNK
|
|
#define IMAGE_DLLCHARACTERISTICS_X86_THUNK 0x0001 // Image is a Wx86 Thunk DLL
|
|
#endif
|
|
|
|
if (!_stricmp(argument->parp->szArg, "dllchar")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (!_stricmp(szVal, "x86thunk")) {
|
|
ImageOptionalHdr.DllCharacteristics = IMAGE_DLLCHARACTERISTICS_X86_THUNK;
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "dll", 3)) {
|
|
ImageFileHdr.Characteristics |= IMAGE_FILE_DLL;
|
|
|
|
fPowerMacBuildShared = TRUE;
|
|
|
|
if (argument->OriginalName[3] == ':') {
|
|
if (!_stricmp(argument->OriginalName+4, "system")) {
|
|
ImageFileHdr.Characteristics |= IMAGE_FILE_SYSTEM;
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (argument->OriginalName[3] != '\0') {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "incremental")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (_stricmp(szVal, "yes") == 0) {
|
|
fIncrSwitchValue = TRUE;
|
|
} else if (_stricmp(szVal, "no") == 0) {
|
|
fIncrSwitchValue = FALSE;
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
fIncrSwitchUsed = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "noentry")) {
|
|
Switch.Link.fNoEntry = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "implib:", 7)) {
|
|
ImplibFilename = &argument->OriginalName[7];
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "def:", 4)) {
|
|
if (argument->OriginalName[4] != '\0') {
|
|
DefFilename = &argument->OriginalName[4];
|
|
|
|
if (szReproDir != NULL) {
|
|
CopyFileToReproDir(DefFilename, FALSE);
|
|
_splitpath(DefFilename, NULL, NULL, szFname, szExt);
|
|
FreePv(szReproOption);
|
|
szReproOption = (char *)
|
|
PvAlloc(strlen("def:\".\\%s%s\"")+strlen(szFname)+strlen(szExt));
|
|
sprintf(szReproOption, "def:\".\\%s%s\"", szFname, szExt);
|
|
}
|
|
}
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "include:", 8)) {
|
|
PEXTERNAL pextInclude;
|
|
PLEXT plext;
|
|
|
|
SetOpt(SwitchInfo, OP_INCLUDE);
|
|
name = argument->OriginalName+8;
|
|
pextInclude = LookupExternSz(pst, name, NULL);
|
|
|
|
// ilink: don't save directives (list not reqd. since TCE not done for ilink)
|
|
if (!(fINCR && IsDirective)) {
|
|
plext = (PLEXT) PvAlloc(sizeof(LEXT));
|
|
plext->pext = pextInclude;
|
|
plext->plextNext = SwitchInfo.plextIncludes;
|
|
SwitchInfo.plextIncludes = plext;
|
|
}
|
|
|
|
// remember pmod that had the directive (used if pchsym isn't dfned)
|
|
if (IsDirective) {
|
|
assert(pcon);
|
|
AddReferenceExt(pextInclude, PmodPCON(pcon));
|
|
|
|
// ilink: keep the pext in the list of pexts referred to by this mod
|
|
if (fINCR) {
|
|
AddExtToModRefList(PmodPCON(pcon), pextInclude);
|
|
}
|
|
}
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "version:", 8)) {
|
|
if (!_strnicmp(argument->OriginalName+8, "liborder=before,", 16)) {
|
|
char *pchT;
|
|
|
|
pchT = argument->OriginalName+24;
|
|
|
|
for (;;) {
|
|
char *pchComma;
|
|
|
|
if ((pchComma = strchr(pchT, ',')) != NULL) {
|
|
*pchComma = '\0';
|
|
}
|
|
|
|
if (PlibFind(pchT, pimage->libs.plibHead, TRUE)) {
|
|
Warning(szFilename, BAD_LIBORDER, szFilename, pchT);
|
|
}
|
|
|
|
if (pchComma == NULL) {
|
|
break;
|
|
}
|
|
|
|
*pchComma = ',';
|
|
|
|
pchT = pchComma + 1;
|
|
}
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
minor = 0;
|
|
if ((p = strchr(argument->OriginalName+8, '.')) != NULL) {
|
|
if ((sscanf(++p, "%li", &minor) != 1) || minor > 0xffff) {
|
|
Warning(szFilename, INVALIDVERSIONSTAMP, argument->OriginalName+8);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
SetOpt(SwitchInfo, OP_MINIMGVER);
|
|
}
|
|
|
|
if ((sscanf(argument->OriginalName+8, "%li", &major) != 1) || major > 0xffff) {
|
|
Warning(szFilename, INVALIDVERSIONSTAMP, argument->OriginalName+8);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
SetOpt(SwitchInfo, OP_MAJIMGVER);
|
|
|
|
ImageOptionalHdr.MajorImageVersion = (WORD)major;
|
|
ImageOptionalHdr.MinorImageVersion = (WORD)minor;
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "osversion:", 10)) {
|
|
minor = 0;
|
|
if ((p = strchr(argument->OriginalName+10, '.')) != NULL) {
|
|
if ((sscanf(++p, "%li", &minor) != 1) || minor > 0xffff) {
|
|
Warning(szFilename, INVALIDVERSIONSTAMP, argument->OriginalName+10);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
SetOpt(SwitchInfo, OP_MINOSVER);
|
|
}
|
|
|
|
if ((sscanf(argument->OriginalName+10, "%li", &major) != 1) || major > 0xffff) {
|
|
Warning(szFilename, INVALIDVERSIONSTAMP, argument->OriginalName+10);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
SetOpt(SwitchInfo, OP_MAJOSVER);
|
|
|
|
ImageOptionalHdr.MajorOperatingSystemVersion = (WORD) major;
|
|
ImageOptionalHdr.MinorOperatingSystemVersion = (WORD) minor;
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
next = 0;
|
|
if (!_strnicmp(argument->OriginalName, "subsystem:", 10)) {
|
|
next = 10;
|
|
if (!_strnicmp(argument->OriginalName+10, "native", 6)) {
|
|
next += 6;
|
|
ImageOptionalHdr.Subsystem = IMAGE_SUBSYSTEM_NATIVE;
|
|
Switch.Link.fChecksum = TRUE; // Always checksum native images.
|
|
} else if (!_strnicmp(argument->OriginalName+10, "windows", 7)) {
|
|
next += 7;
|
|
ImageOptionalHdr.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
|
} else if (!_strnicmp(argument->OriginalName+10, "console", 7)) {
|
|
next += 7;
|
|
ImageOptionalHdr.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
|
|
} else if (!_strnicmp(argument->OriginalName+10, "posix", 5)) {
|
|
next += 5;
|
|
ImageOptionalHdr.Subsystem = IMAGE_SUBSYSTEM_POSIX_CUI;
|
|
} else if (!_strnicmp(argument->OriginalName+10, "mmosa", 5)) {
|
|
next += 5;
|
|
ImageOptionalHdr.Subsystem = IMAGE_SUBSYSTEM_MMOSA;
|
|
} else if (argument->OriginalName[next] != ',') {
|
|
Warning(szFilename, UNKNOWNSUBSYSTEM, argument->OriginalName+10);
|
|
}
|
|
|
|
SetOpt(SwitchInfo, OP_SUBSYSTEM);
|
|
|
|
if (argument->OriginalName[next] == ',') {
|
|
++next;
|
|
major = minor = 0;
|
|
if ((p = strchr(argument->OriginalName+next, '.')) != NULL) {
|
|
if ((sscanf(++p, "%li", &minor) != 1) || minor > 0xffff) {
|
|
Warning(szFilename, INVALIDVERSIONSTAMP, argument->OriginalName+next);
|
|
goto ProcessedAllVals;
|
|
}
|
|
}
|
|
|
|
if ((sscanf(argument->OriginalName+next, "%li", &major) != 1) || major > 0xffff) {
|
|
Warning(szFilename, INVALIDVERSIONSTAMP, argument->OriginalName+next);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (((ImageOptionalHdr.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI) ||
|
|
(ImageOptionalHdr.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI) ||
|
|
(ImageOptionalHdr.Subsystem == IMAGE_SUBSYSTEM_MMOSA)) &&
|
|
((major < 3) || ((major == 3) && (minor < 10)))) {
|
|
Warning(szFilename, INVALIDVERSIONSTAMP, argument->OriginalName+next);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
SetOpt(SwitchInfo, OP_SUBSYSVER);
|
|
ImageOptionalHdr.MajorSubsystemVersion = (WORD)major;
|
|
ImageOptionalHdr.MinorSubsystemVersion = (WORD)minor;
|
|
}
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "stack")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
// If the user used the stack switch, then ignore
|
|
// any directive that sets the stack.
|
|
|
|
if (Switch.Link.Stack && IsDirective) {
|
|
iarpv = 2; // ignore 2 or fewer args
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (argument->parp->rgarpv[iarpv].szVal[0] != '\0' &&
|
|
!FNumParp(argument->parp, iarpv,
|
|
&ImageOptionalHdr.SizeOfStackReserve))
|
|
{
|
|
goto BadNum;
|
|
}
|
|
iarpv++;
|
|
|
|
if (argument->parp->carpv >= 2 &&
|
|
argument->parp->rgarpv[iarpv].szVal[0] != '\0' &&
|
|
!FNumParp(argument->parp, iarpv,
|
|
&ImageOptionalHdr.SizeOfStackCommit))
|
|
{
|
|
goto BadNum;
|
|
}
|
|
iarpv++;
|
|
Switch.Link.Stack = TRUE;
|
|
ImageOptionalHdr.SizeOfStackCommit =
|
|
Align(sizeof(DWORD), ImageOptionalHdr.SizeOfStackCommit);
|
|
ImageOptionalHdr.SizeOfStackReserve =
|
|
Align(sizeof(DWORD), ImageOptionalHdr.SizeOfStackReserve);
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "heap")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
// If the user used the heap switch, then ignore
|
|
// any directive that sets the heap size.
|
|
|
|
if (Switch.Link.Heap && IsDirective) {
|
|
iarpv = 2; // ignore 2 or fewer args
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (argument->parp->rgarpv[iarpv].szVal[0] != '\0' &&
|
|
!FNumParp(argument->parp, iarpv,
|
|
&ImageOptionalHdr.SizeOfHeapReserve))
|
|
{
|
|
goto BadNum;
|
|
}
|
|
iarpv++;
|
|
|
|
if (argument->parp->carpv >= 2 &&
|
|
argument->parp->rgarpv[iarpv].szVal[1] != '\0' &&
|
|
!FNumParp(argument->parp, iarpv,
|
|
&ImageOptionalHdr.SizeOfHeapCommit))
|
|
{
|
|
goto BadNum;
|
|
}
|
|
iarpv++;
|
|
Switch.Link.Heap = TRUE;
|
|
|
|
ImageOptionalHdr.SizeOfHeapCommit =
|
|
Align(sizeof(DWORD), ImageOptionalHdr.SizeOfHeapCommit);
|
|
ImageOptionalHdr.SizeOfHeapReserve =
|
|
Align(sizeof(DWORD), ImageOptionalHdr.SizeOfHeapReserve);
|
|
|
|
if (ImageOptionalHdr.SizeOfHeapReserve <
|
|
ImageOptionalHdr.SizeOfHeapCommit)
|
|
{
|
|
// Reserve less than commit -- this causes NT to fail to load
|
|
// it, and it often happens when people use .def files left
|
|
// over from the 16-bit world, so we increase "reserve" to be
|
|
// the same as "commit".
|
|
|
|
ImageOptionalHdr.SizeOfHeapReserve =
|
|
ImageOptionalHdr.SizeOfHeapCommit;
|
|
}
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "machine")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
szVal = argument->parp->rgarpv[iarpv++].szVal;
|
|
|
|
if (!_stricmp(szVal, "I386") || !_stricmp(szVal, "IX86") || !_stricmp(szVal, "X86")) {
|
|
ImageFileHdr.Machine = IMAGE_FILE_MACHINE_I386;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(szVal, "MIPS")) {
|
|
ImageFileHdr.Machine = IMAGE_FILE_MACHINE_R4000;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(szVal, "MIPSR10")) {
|
|
ImageFileHdr.Machine = IMAGE_FILE_MACHINE_R10000;
|
|
Switch.Link.fPadMipsCode = FALSE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(szVal, "ALPHA") || !_stricmp(szVal, "ALPHA_AXP")) {
|
|
ImageFileHdr.Machine = IMAGE_FILE_MACHINE_ALPHA;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(szVal, "PPC")) {
|
|
ImageFileHdr.Machine = IMAGE_FILE_MACHINE_POWERPC;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(szVal, "M68K")) {
|
|
ImageFileHdr.Machine = IMAGE_FILE_MACHINE_M68K;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(szVal, "MPPC")) {
|
|
ImageFileHdr.Machine = IMAGE_FILE_MACHINE_MPPC_601;
|
|
fPowerMac = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
Warning(szFilename, UNKNOWNRESPONSE, argument->OriginalName+8, "IX86, MIPS, ALPHA, PPC, M68K, or MPPC");
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "align")) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
if (!FNumParp(argument->parp, iarpv++, &dwVal)) {
|
|
goto BadNum;
|
|
}
|
|
|
|
if (!dwVal || (dwVal & (dwVal - 1))) {
|
|
// Section alignment is not a power of 2
|
|
|
|
Warning(szFilename, BAD_ALIGN, dwVal);
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
SetOpt(SwitchInfo, OP_ALIGN);
|
|
ImageOptionalHdr.SectionAlignment = dwVal;
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "gpsize", 6)) {
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
if (!FNumParp(argument->parp, iarpv++, &dwVal)) {
|
|
goto BadNum;
|
|
}
|
|
|
|
SetOpt(SwitchInfo, OP_GPSIZE);
|
|
Switch.Link.GpSize = dwVal;
|
|
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "section:", 8)) {
|
|
if (argument->OriginalName[8] == '\0') {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
|
|
if (!IsDirective) {
|
|
SetOpt(SwitchInfo, OP_SECTION);
|
|
}
|
|
|
|
char *pb = _tcschr(argument->OriginalName+8, ',');
|
|
if (pb) {
|
|
size_t cch = pb - (argument->OriginalName+8);
|
|
char *szSecName = (char *) PvAlloc(cch + 1);
|
|
|
|
_tcsncpy(szSecName, argument->OriginalName+8, cch);
|
|
szSecName[cch] = '\0';
|
|
|
|
if (!FValidSecName(szSecName)) {
|
|
Fatal(szFilename, INVALIDSECNAME, szSecName, argument->OriginalName);
|
|
}
|
|
|
|
FreePv(szSecName);
|
|
}
|
|
|
|
AddArgument(&SectionNames, argument->OriginalName+8);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "verbose", 7)) {
|
|
|
|
// no arguments
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
|
|
if (argument->OriginalName[7] != '\0') {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
|
|
Verbose = TRUE;
|
|
fVerboseLib = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (_stricmp(szVal, "lib") == 0) {
|
|
fVerboseLib = TRUE;
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "rom")) {
|
|
Switch.Link.fROM = TRUE;
|
|
Switch.Link.fPE = FALSE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "stub:", 5)) {
|
|
if (argument->OriginalName[5] != '\0') {
|
|
FILE *StubFile;
|
|
LONG AlignedSize;
|
|
DWORD FileSize;
|
|
|
|
if (!(StubFile = fopen(argument->OriginalName+5, "rb"))) {
|
|
// Stub file not found using the specified path.
|
|
// If the path didn't specify a directory, try using the
|
|
// directory where the linker itself is.
|
|
|
|
char szDrive[_MAX_DRIVE];
|
|
char szDir[_MAX_DIR];
|
|
char szStubPath[_MAX_PATH];
|
|
|
|
_splitpath(argument->OriginalName+5,
|
|
szDrive, szDir, szFname, szExt);
|
|
if (szDrive[0] == '\0' && szDir[0] == '\0') {
|
|
_splitpath(_pgmptr, szDrive, szDir, NULL, NULL);
|
|
_makepath(szStubPath, szDrive, szDir, szFname, szExt);
|
|
StubFile = fopen(szStubPath, "rb");
|
|
}
|
|
}
|
|
|
|
if (StubFile == NULL) {
|
|
Fatal(szFilename, CANTOPENFILE, argument->OriginalName+5);
|
|
}
|
|
{
|
|
BYTE *pbDosHeader;
|
|
|
|
if ((FileSize = _filelength(_fileno(StubFile))) < 0) {
|
|
Fatal(szFilename, CANTREADFILE, argument->OriginalName+5);
|
|
}
|
|
|
|
// make sure the file is at least as large as a DOS header
|
|
if (FileSize < 0x40) {
|
|
Fatal(szFilename, BADSTUBFILE, argument->OriginalName+5);
|
|
}
|
|
|
|
if (pimage->imaget != imagetVXD) {
|
|
// Align the end to an 8 byte boundary
|
|
AlignedSize = Align(8, FileSize);
|
|
} else {
|
|
// 128-byte boundaries seem common for VxD stubs
|
|
AlignedSize = Align(0x80, FileSize);
|
|
}
|
|
|
|
pbDosHeader = (BYTE *) PvAlloc((size_t) AlignedSize + 4);
|
|
|
|
if (fread( pbDosHeader, 1, (size_t)FileSize, StubFile ) != FileSize) {
|
|
Fatal(szFilename, CANTREADFILE, argument->OriginalName+5);
|
|
}
|
|
|
|
fclose(StubFile);
|
|
|
|
// check for the MZ signature
|
|
if ((pbDosHeader[0] != 'M') || (pbDosHeader[1] != 'Z')) {
|
|
Fatal(szFilename, BADSTUBFILE, argument->OriginalName+5);
|
|
}
|
|
|
|
if (((PIMAGE_DOS_HEADER)pbDosHeader)->e_lfarlc < 0x40) {
|
|
// Ideally we would convert these to full headers
|
|
// but it's too late and I don't have the algorithm
|
|
// for doing it.
|
|
|
|
Warning(argument->OriginalName + 5, PARTIAL_DOS_HDR);
|
|
}
|
|
if (pimage->imaget != imagetVXD) {
|
|
|
|
// slam the PE00 at the end
|
|
pbDosHeader[AlignedSize] = 'P';
|
|
pbDosHeader[AlignedSize+1] = 'E';
|
|
pbDosHeader[AlignedSize+2] = '\0';
|
|
pbDosHeader[AlignedSize+3] = '\0';
|
|
}
|
|
|
|
// adjust the offset
|
|
*((LONG *)&pbDosHeader[0x3c]) = AlignedSize;
|
|
|
|
// set the global
|
|
pimage->pbDosHeader = pbDosHeader;
|
|
if (pimage->imaget != imagetVXD) {
|
|
pimage->cbDosHeader = AlignedSize + 4;
|
|
} else {
|
|
pimage->cbDosHeader = AlignedSize;
|
|
}
|
|
}
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
SetOpt(SwitchInfo, OP_STUB);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "order:@", 7)) {
|
|
Switch.Link.fOrder = TRUE;
|
|
OrderFilename = argument->OriginalName+7;
|
|
OrderInit();
|
|
|
|
if (szReproDir != NULL) {
|
|
CopyFileToReproDir(OrderFilename, FALSE);
|
|
_splitpath(OrderFilename, NULL, NULL, szFname, szExt);
|
|
FreePv(szReproOption);
|
|
szReproOption = (char *)
|
|
PvAlloc(strlen("order:@\".\\%s%s\"")+strlen(szFname)+strlen(szExt));
|
|
sprintf(szReproOption, "order:@\".\\%s%s\"", szFname, szExt);
|
|
}
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->parp->szArg, "vxd")) {
|
|
if (IsDirective && (pimage->imaget != imagetVXD)) {
|
|
// UNDONE: Temporary error ... see comment in LinkerMain about /VXD
|
|
|
|
Fatal(szFilename, VXD_NEEDED);
|
|
}
|
|
|
|
// VXD implies MACHINE:IX86
|
|
|
|
ImageFileHdr.Machine = IMAGE_FILE_MACHINE_I386;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "ignore:", 7)) {
|
|
char *pMinus;
|
|
int range, last;
|
|
|
|
token = strtok(argument->OriginalName+7, ",");
|
|
while (token) {
|
|
if ((pMinus = strchr(token,'-')) != NULL) {
|
|
*pMinus = '\0';
|
|
last = atoi(pMinus+1);
|
|
for (range = atoi(token); range <= last; range++) {
|
|
DisableWarning(range);
|
|
}
|
|
}
|
|
else {
|
|
DisableWarning(atoi(token));
|
|
}
|
|
token = strtok(NULL,",");
|
|
}
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "warn", 4)) {
|
|
char chWarn;
|
|
|
|
if (argument->OriginalName[4] == '\0') {
|
|
// no arg implies 2 (default is 1)
|
|
WarningLevel = 2;
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if ((argument->OriginalName[4] != ':') ||
|
|
((chWarn = argument->OriginalName[5]) < '0') ||
|
|
(chWarn > '3') ||
|
|
(argument->OriginalName[6] != '\0')) {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
|
|
WarningLevel = (WORD) (chWarn - '0');
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "verstamp")) {
|
|
// Undocumented switch used by the NT build
|
|
|
|
IbAppendBlk(&blkComment, szVersion, strlen(szVersion) + 1);
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "miscrdata")) {
|
|
// Undocumented switch used by the NT build
|
|
|
|
Switch.Link.fMiscInRData = TRUE;
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "optidata")) {
|
|
Switch.Link.fOptIdata = TRUE;
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "driver", 6)) {
|
|
Switch.Link.fDriver = TRUE;
|
|
if (argument->OriginalName[6] != '\0') {
|
|
if (!_strnicmp(&argument->OriginalName[6], ":uponly", 7)) {
|
|
ImageFileHdr.Characteristics |= IMAGE_FILE_UP_SYSTEM_ONLY;
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "nopagecode")) {
|
|
Switch.Link.fNoPagedCode = TRUE;
|
|
Switch.Link.fPadMipsCode = FALSE;
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "nor4kworkarounds")) {
|
|
Switch.Link.fPadMipsCode = FALSE;
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "swaprun:", 8)) {
|
|
if (argument->OriginalName[8] != '\0') {
|
|
if (!_strnicmp(&argument->OriginalName[8], "net", 3)) {
|
|
ImageFileHdr.Characteristics |= IMAGE_FILE_NET_RUN_FROM_SWAP;
|
|
} else {
|
|
if (!_strnicmp(&argument->OriginalName[8], "cd", 2)) {
|
|
ImageFileHdr.Characteristics |= IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP;
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
}
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_strnicmp(argument->OriginalName, "ws:", 3)) {
|
|
if (argument->OriginalName[3] != '\0') {
|
|
if (!_strnicmp(&argument->OriginalName[3], "aggressive", 10)) {
|
|
ImageFileHdr.Characteristics |= IMAGE_FILE_AGGRESIVE_WS_TRIM;
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
} else {
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->OriginalName);
|
|
}
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
// PowerMac
|
|
|
|
if (!_stricmp(argument->OriginalName, "shared")) {
|
|
fPowerMacBuildShared = TRUE;
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
// Implementation for WEAK and regular import for PowerMac
|
|
|
|
if (!_stricmp(argument->parp->szArg, "import")) {
|
|
BOOL fWeakImport = FALSE;
|
|
BOOL fCustomGlue = FALSE;
|
|
char *szFuncName = NULL;
|
|
char *szContainerName = NULL;
|
|
char *szLocalCurrentVer = NULL;
|
|
char *szLocalOldCodeVer = NULL;
|
|
char *szLocalOldAPIVer = NULL;
|
|
|
|
if (!FGotVal(argument->parp, iarpv)) {
|
|
goto MissingVal;
|
|
}
|
|
|
|
for (; iarpv < argument->parp->carpv; iarpv++) {
|
|
char *szKey = argument->parp->rgarpv[iarpv].szKeyword;
|
|
szVal = argument->parp->rgarpv[iarpv].szVal;
|
|
|
|
if (szKey != NULL && !_stricmp(szKey, "lib")) {
|
|
if (!szContainerName) {
|
|
szContainerName = szVal;
|
|
continue;
|
|
} else {
|
|
// lib= has been mentioned twice
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->parp->szArg);
|
|
}
|
|
} else if (szKey != NULL && !_stricmp(szKey, "weak")) {
|
|
fWeakImport = (!strcmp(szVal, "1")) ? TRUE : FALSE;
|
|
continue;
|
|
} else if (szKey != NULL && !_stricmp(szKey, "glue")) {
|
|
fCustomGlue = (!strcmp(szVal, "1")) ? TRUE : FALSE;
|
|
continue;
|
|
} else if (szKey != NULL && !_stricmp(szKey, "CURRENTVER")) {
|
|
if (_strspnp(szVal, "0123456789")) {
|
|
// Not appropriate number
|
|
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->parp->szArg);
|
|
} else {
|
|
szLocalCurrentVer = szVal;
|
|
continue;
|
|
}
|
|
} else if (szKey != NULL && !_stricmp(szKey, "OLDCODEVER")) {
|
|
if (_strspnp(szVal, "0123456789")) {
|
|
// Not appropriate number
|
|
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->parp->szArg);
|
|
} else {
|
|
szLocalOldCodeVer = szVal;
|
|
continue;
|
|
}
|
|
} else if (szKey != NULL && !_stricmp(szKey, "OLDAPIVER")) {
|
|
if (_strspnp(szVal, "0123456789")) {
|
|
// Not appropriate number
|
|
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->parp->szArg);
|
|
} else {
|
|
szLocalOldAPIVer = szVal;
|
|
continue;
|
|
}
|
|
} else if (!szFuncName) {
|
|
szFuncName = szVal;
|
|
continue;
|
|
} else {
|
|
// At least two function names have been specified
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->parp->szArg);
|
|
}
|
|
}
|
|
|
|
if (szLocalCurrentVer || szLocalOldCodeVer || szLocalOldAPIVer) {
|
|
AddVersionList(szContainerName, szLocalCurrentVer,
|
|
szLocalOldCodeVer, szLocalOldAPIVer);
|
|
}
|
|
|
|
if (fWeakImport) {
|
|
assert (fCustomGlue == FALSE);
|
|
if (szFuncName) {
|
|
// Add to Function list
|
|
|
|
AddArgument(&WeakImportsFunctionList, szFuncName);
|
|
} else if (szContainerName) {
|
|
// Add to Container list
|
|
|
|
AddArgument(&WeakImportsContainerList, szContainerName);
|
|
} else {
|
|
// Only WEAK attribute has been specified without function or container name
|
|
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->parp->szArg);
|
|
}
|
|
}
|
|
|
|
if (szFuncName && (szContainerName || fCustomGlue)) {
|
|
char *szTempFuncName = (char *) PvAlloc(strlen(szFuncName) + 2);
|
|
|
|
if (*szFuncName != '?') {
|
|
// It is not a C++ function, but a C function
|
|
// So decorate it with an '_'
|
|
|
|
strcpy(szTempFuncName, "_");
|
|
strcat(szTempFuncName, szFuncName);
|
|
} else {
|
|
strcpy(szTempFuncName, szFuncName);
|
|
}
|
|
|
|
if (pcon) {
|
|
if (!AddCmdLineImport(szTempFuncName, szContainerName, pcon, pimage)) {
|
|
Warning(szFilename, MACIMPORTSYMBOLNOTFOUND, szFuncName);
|
|
}
|
|
} else {
|
|
// Add to Command Line Imports list
|
|
|
|
AddArgumentToList(&MppcImportList, szTempFuncName, szContainerName);
|
|
}
|
|
|
|
}
|
|
|
|
if (!szLocalCurrentVer && !szLocalOldCodeVer && !szLocalOldAPIVer && !fWeakImport &&
|
|
!(szFuncName && (szContainerName || fCustomGlue))) {
|
|
// Neither weak attribute nor (both function and container names) have been specified
|
|
// the version #s have not been specified either
|
|
|
|
Fatal(szFilename, SWITCHSYNTAX, argument->parp->szArg);
|
|
}
|
|
|
|
goto ProcessedAllVals;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "dbgimplib")) {
|
|
fDbgImpLib = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "newglue")) {
|
|
Switch.Link.fNewGlue = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
if (!_stricmp(argument->OriginalName, "newrelocs")) {
|
|
Switch.Link.fNewRelocs = TRUE;
|
|
goto ProcessedArg;
|
|
}
|
|
|
|
#ifdef NT_BUILD
|
|
if (!_stricmp(argument->OriginalName, "calltree")) {
|
|
Switch.Link.fCallTree = TRUE;
|
|
// Force fixup debug
|
|
dtUser = (DEBUG_TYPE) (dtUser | FixupDebug);
|
|
goto ProcessedAllVals;
|
|
}
|
|
#endif
|
|
|
|
Warning(szFilename, WARN_UNKNOWN_SWITCH, argument->OriginalName);
|
|
continue;
|
|
|
|
MissingVal:
|
|
Fatal(szFilename, MISSING_SWITCH_VALUE, argument->OriginalName);
|
|
continue;
|
|
|
|
BadNum:
|
|
Fatal(szFilename, BAD_NUMBER, argument->OriginalName);
|
|
continue;
|
|
|
|
ProcessedArg:
|
|
if (argument->parp->carpv > iarpv) {
|
|
// There were extra values which were not processed by the
|
|
// option-specific handler, so give a warning.
|
|
|
|
Warning(szFilename, EXTRA_SWITCH_VALUE, argument->OriginalName);
|
|
}
|
|
ProcessedAllVals:; // ignores extra ... mainly used for handlers which
|
|
// haven't been updated to new scheme yet.
|
|
}
|
|
|
|
// set image base here since in some cases a DLL directive is seen
|
|
// after FPass1DefFile()
|
|
|
|
if (!Switch.Link.Base) {
|
|
// Set image base (to 1M) if not set by user in case of DLLs
|
|
|
|
ImageOptionalHdr.ImageBase = fDLL(pimage) ? 0x10000000 : 0x00400000;
|
|
}
|
|
|
|
#undef ImageFileHdr
|
|
#undef ImageOptionalHdr
|
|
#undef Switch
|
|
#undef SwitchInfo
|
|
}
|
|
|
|
|
|
void
|
|
CheckSwitchesForIncrementalLink(PIMAGE pimage)
|
|
{
|
|
#define Switch (pimage->Switch)
|
|
|
|
fINCR = fIncrSwitchValue;
|
|
|
|
if (pimage->imaget != imagetPE) {
|
|
// Turn off ilink for non-PE images (i.e. VXDs)
|
|
|
|
if (fINCR) {
|
|
Warning(NULL, SWITCH_IGNORED, "INCREMENTAL", "VXD");
|
|
|
|
fINCR = FALSE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (Switch.Link.fProfile) {
|
|
// Turn off ilink if user wants to profile
|
|
|
|
if (fINCR) {
|
|
Warning(NULL, SWITCH_IGNORED, "INCREMENTAL", "PROFILE");
|
|
|
|
fINCR = FALSE;
|
|
}
|
|
|
|
Switch.Link.fMap = TRUE;
|
|
|
|
return;
|
|
}
|
|
|
|
if (fPdb && (dtUser != CvDebug) && (dtUser != 0) && (Switch.Link.DebugInfo != None)) {
|
|
if (fINCR) {
|
|
// Incremental link isn't supported if non-CV format debugging is requested
|
|
|
|
Warning(NULL, SWITCH_IGNORED, "INCREMENTAL", "DEBUGTYPE");
|
|
|
|
fINCR = FALSE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!fIncrSwitchUsed) {
|
|
// Turn on ilink by default for debug builds
|
|
|
|
fINCR = (Switch.Link.DebugInfo != None);
|
|
}
|
|
|
|
if (!fINCR) {
|
|
return;
|
|
}
|
|
|
|
if (!fPdb && (Switch.Link.DebugInfo != None)) {
|
|
// Turn off ilink if /PDB:NONE & /DEBUG requested, can't handle debug info
|
|
|
|
if (fIncrSwitchUsed) {
|
|
Warning(NULL, SWITCH_IGNORED, "INCREMENTAL", "PDB");
|
|
}
|
|
|
|
fINCR = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (fExplicitOptRef && Switch.Link.fTCE) {
|
|
// Turn off ilink if tce was specified, interferes with ilink
|
|
|
|
if (fIncrSwitchUsed) {
|
|
Warning(NULL, SWITCH_IGNORED, "INCREMENTAL", "OPT");
|
|
}
|
|
|
|
fINCR = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (Switch.Link.fOrder) {
|
|
// Turn off ilink if /order was specified
|
|
|
|
if (fIncrSwitchUsed) {
|
|
Warning(NULL, SWITCH_IGNORED, "INCREMENTAL", "ORDER");
|
|
}
|
|
|
|
fINCR = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (Switch.Link.fMap) {
|
|
// Turn off ilink if /map was specified
|
|
|
|
if (fIncrSwitchUsed) {
|
|
Warning(NULL, SWITCH_IGNORED, "INCREMENTAL", "MAP");
|
|
}
|
|
|
|
fINCR = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (Switch.Link.fChecksum) {
|
|
// Turn off ilink if /release was specified
|
|
|
|
if (fIncrSwitchUsed) {
|
|
Warning(NULL, SWITCH_IGNORED, "INCREMENTAL", "RELEASE");
|
|
}
|
|
|
|
fINCR = FALSE;
|
|
return;
|
|
}
|
|
|
|
if (Switch.Link.Force != ftNone) {
|
|
// Turn off ilink if /FORCE was specified
|
|
// (ILK/EXE del if mul/undef present & no FORCE)
|
|
|
|
if (fIncrSwitchUsed) {
|
|
Warning(NULL, SWITCH_IGNORED, "INCREMENTAL", "FORCE");
|
|
}
|
|
|
|
fINCR = FALSE;
|
|
return;
|
|
}
|
|
|
|
// Verify possible to ilink for the machine.
|
|
|
|
if (!FIncrementalLinkSupported(pimage)) {
|
|
if (fIncrSwitchUsed) {
|
|
// UNDONE: There should be a warning
|
|
}
|
|
|
|
fINCR = FALSE;
|
|
return;
|
|
}
|
|
|
|
#undef Switch
|
|
}
|
|
|
|
|
|
void
|
|
FlushWorkingSet(void)
|
|
{
|
|
HINSTANCE hInstKernel32;
|
|
BOOL (WINAPI *pfnSetProcessWorkingSetSize)(HANDLE, DWORD, DWORD);
|
|
|
|
hInstKernel32 = GetModuleHandle("KERNEL32.DLL");
|
|
if (hInstKernel32 == NULL) {
|
|
return;
|
|
}
|
|
|
|
// Get the address
|
|
pfnSetProcessWorkingSetSize = (BOOL (WINAPI *)(HANDLE, DWORD, DWORD))
|
|
GetProcAddress(hInstKernel32, "SetProcessWorkingSetSize");
|
|
if (pfnSetProcessWorkingSetSize == NULL) {
|
|
return;
|
|
}
|
|
|
|
// Set working set size
|
|
|
|
(*pfnSetProcessWorkingSetSize)(GetCurrentProcess(), 0xFFFFFFFF, 0xFFFFFFFF);
|
|
}
|
|
|
|
|
|
INT
|
|
SpawnFullBuildVXD(DWORD cbHeaderSize)
|
|
{
|
|
const char **newargv;
|
|
int i;
|
|
int rc;
|
|
char rgch[64];
|
|
|
|
// Cleanup a bit
|
|
|
|
FileCloseAll();
|
|
RemoveConvertTempFiles();
|
|
|
|
sprintf(rgch, "/hdrsize:0x%lX", cbHeaderSize);
|
|
|
|
fflush(NULL);
|
|
|
|
assert(savArgc);
|
|
assert(savArgv);
|
|
newargv = (const char **) PvAlloc((savArgc+3) * sizeof(const char *));
|
|
|
|
for (i = 0; i < savArgc; i++) {
|
|
newargv[i] = savArgv[i];
|
|
}
|
|
|
|
newargv[savArgc] = rgch;
|
|
newargv[savArgc+1] = "/nologo";
|
|
newargv[savArgc+2] = NULL;
|
|
|
|
FlushWorkingSet();
|
|
|
|
if ((rc = _spawnv(P_WAIT, _pgmptr, newargv)) == -1) {
|
|
Fatal(NULL, SPAWNFAILED, _pgmptr);
|
|
}
|
|
|
|
FreePv(newargv);
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
INT
|
|
SpawnFullBuild(BOOL fIncrBuild)
|
|
{
|
|
const char **newargv;
|
|
int rc;
|
|
|
|
fflush(NULL);
|
|
|
|
assert(savArgc);
|
|
assert(savArgv);
|
|
newargv = (const char **) PvAlloc((2+4) * sizeof(const char *));
|
|
|
|
char *szargv0 = (char *) PvAlloc(strlen(savArgv[0])+3);
|
|
sprintf(szargv0, "\"%s\"", savArgv[0]);
|
|
newargv[0] = szargv0;
|
|
|
|
// second arg will be the entire command string except for argv[0]
|
|
char *szCmdLine = GetCommandLine();
|
|
|
|
char ch = ' ';
|
|
if (*szCmdLine == '"') { // in case argv[o] is a lfn, then it is quoted
|
|
ch = '"';
|
|
szCmdLine++; // skip over leading quote
|
|
}
|
|
|
|
// skip over argv[0]. Assumes that there are no embedded quotes.
|
|
while (*szCmdLine != ch) {
|
|
szCmdLine++;
|
|
} // end while
|
|
|
|
if (*szCmdLine == '"') { // skip over closing quote if applicable
|
|
szCmdLine++;
|
|
}
|
|
|
|
assert(*szCmdLine == ' '); // gotta have the blank char
|
|
newargv[1] = ++szCmdLine;
|
|
|
|
if (fIncrBuild) {
|
|
newargv[2] = "/incremental:yes";
|
|
} else {
|
|
newargv[2] = "/incremental:no";
|
|
}
|
|
|
|
newargv[3] = "/nologo";
|
|
newargv[4] = "/fullbuild";
|
|
newargv[5] = NULL;
|
|
|
|
FlushWorkingSet();
|
|
|
|
if ((rc = _spawnv(P_WAIT, _pgmptr, newargv)) == -1) {
|
|
Fatal(NULL, SPAWNFAILED, _pgmptr);
|
|
}
|
|
|
|
FreePv(newargv);
|
|
FreePv(szargv0);
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
void
|
|
ResetLibsAndMods(PIMAGE pimage)
|
|
{
|
|
ENM_LIB enm_lib;
|
|
|
|
InitEnmLib(&enm_lib, pimage->libs.plibHead);
|
|
while (FNextEnmLib(&enm_lib)) {
|
|
ENM_MOD enm_mod;
|
|
|
|
InitEnmMod(&enm_mod, enm_lib.plib);
|
|
while (FNextEnmMod(&enm_mod)) {
|
|
enm_mod.pmod->rgci = NULL; // causes section headers to be reread
|
|
enm_mod.pmod->fInclude = FALSE; // sets up for next ilink
|
|
}
|
|
|
|
if (enm_lib.plib->flags & LIB_DontSearch) {
|
|
continue;
|
|
}
|
|
|
|
enm_lib.plib->rgszSym = NULL;
|
|
}
|
|
EndEnmLib(&enm_lib);
|
|
}
|
|
|
|
|
|
void
|
|
SaveImage(PIMAGE pimage)
|
|
{
|
|
// reset libs so that they are preprocessed on ilink
|
|
ResetLibsAndMods(pimage);
|
|
|
|
// save away imodidx
|
|
pimage->imodidx = imodidx;
|
|
|
|
// save away pmodlinkerdefined
|
|
pimage->pmodLinkerDefined = pmodLinkerDefined;
|
|
|
|
// save away pmod that defines the entrypoint on full build
|
|
if (!fIncrDbFile) {
|
|
pimage->pmodEntryPoint = (pextEntry && pextEntry->pcon) ?
|
|
PmodPCON(pextEntry->pcon) : NULL;
|
|
}
|
|
|
|
// reset symbol table for insertions
|
|
AllowInserts(pimage->pst);
|
|
|
|
if (psecDebug) {
|
|
// Need to reset the flags
|
|
|
|
psecDebug->flagsOrig &= ~IMAGE_SCN_LNK_REMOVE;
|
|
}
|
|
|
|
SaveEXEInfo(OutFilename, pimage);
|
|
WriteIncrDbFile(pimage);
|
|
|
|
DBEXEC(DB_DUMPIMAGE, DumpImage(pimage));
|
|
}
|
|
|
|
|
|
INT
|
|
CvPackExe(void)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Packs the debug info in EXE.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Return value of _spawnv[p](). 0 on success & !0 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
const char *argv[4];
|
|
#if 1
|
|
char szOutArg[2+_MAX_PATH];
|
|
#endif
|
|
char szDrive[_MAX_DRIVE];
|
|
char szDir[_MAX_DIR];
|
|
char szCvpackPath[_MAX_PATH];
|
|
int rc;
|
|
|
|
fflush(NULL);
|
|
|
|
#if 1
|
|
strcpy(szOutArg, "\"");
|
|
strcat(szOutArg, OutFilename);
|
|
strcat(szOutArg, "\"");
|
|
#endif
|
|
|
|
argv[0] = "cvpack";
|
|
argv[1] = "/nologo";
|
|
#if 1
|
|
argv[2] = szOutArg;
|
|
#else
|
|
argv[2] = OutFilename;
|
|
#endif
|
|
argv[3] = NULL;
|
|
|
|
// Look for CVPACK.EXE in the directory from which we were loaded
|
|
|
|
_splitpath(_pgmptr, szDrive, szDir, NULL, NULL);
|
|
_makepath(szCvpackPath, szDrive, szDir, "cvpack", ".exe");
|
|
|
|
FlushWorkingSet();
|
|
|
|
rc = _spawnv(P_WAIT, szCvpackPath, argv);
|
|
|
|
if (rc == -1) {
|
|
// Run CVPACK.EXE from the path
|
|
|
|
rc = _spawnvp(P_WAIT, "cvpack.exe", argv);
|
|
}
|
|
|
|
return(rc);
|
|
}
|
|
|
|
|
|
MainFunc
|
|
LinkerMain(int Argc, char *Argv[])
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Linker entrypoint.
|
|
|
|
Arguments:
|
|
|
|
Argc - Standard C argument count.
|
|
|
|
Argv - Standard C argument strings.
|
|
|
|
Return Value:
|
|
|
|
0 Link was successful.
|
|
!0 Linker error index.
|
|
|
|
--*/
|
|
|
|
{
|
|
#define ImageOptionalHdr (pimage->ImgOptHdr)
|
|
#define Switch (pimage->Switch)
|
|
|
|
PIMAGE pimage;
|
|
IMAGET imaget;
|
|
INT rc;
|
|
BOOL fCvpack;
|
|
|
|
#ifdef ILINKLOG
|
|
{
|
|
extern DWORD dwBegin;
|
|
|
|
dwBegin = GetTickCount();
|
|
}
|
|
#endif // ILINKLOG
|
|
|
|
if (Argc < 2) {
|
|
LinkerUsage();
|
|
}
|
|
|
|
CheckForReproDir();
|
|
|
|
#ifdef INSTRUMENT
|
|
Log = LogOpen();
|
|
|
|
LogNoteEvent(Log, SZILINK, NULL, letypeBegin, NULL);
|
|
#endif // INSTRUMENT
|
|
|
|
ParseCommandLine(Argc, Argv, "LINK");
|
|
|
|
// Scan the command line to determine the basic image type. This
|
|
// means that the "VXD" option in the .DEF file isn't adequate by
|
|
// itself. You still must specify /VXD on the linker command line.
|
|
|
|
imaget = FScanSwitches("vxd") ? imagetVXD : imagetPE;
|
|
|
|
// scan the command line to see if /nologo is specified;
|
|
// ProcessLinkerSwitches() can invoke PrintBanner()
|
|
|
|
fNeedBanner = FScanSwitches("nologo") ? FALSE : TRUE;
|
|
|
|
// Initialize EXE image; image created in memory as fINCR is FALSE
|
|
|
|
InitImage(&pimage, imaget);
|
|
|
|
ProcessLinkerSwitches(pimage, NULL, NULL);
|
|
|
|
// Set the debugtype.
|
|
|
|
Switch.Link.DebugType = dtUser;
|
|
if (Switch.Link.DebugType == NoDebug) {
|
|
// Default is CV debug
|
|
|
|
Switch.Link.DebugType = CvDebug;
|
|
}
|
|
|
|
if (Switch.Link.DebugType & (CoffDebug | CvDebug)) {
|
|
// Always turn on FPO and misc. info if other info is being generated.
|
|
|
|
Switch.Link.DebugType = (DEBUG_TYPE) (Switch.Link.DebugType | FpoDebug | MiscDebug);
|
|
}
|
|
|
|
if (pimage->imaget == imagetVXD) {
|
|
// VXDs can only support CV debug
|
|
|
|
// UNDONE: Issue a warning is user specified debug type is disabled?
|
|
|
|
Switch.Link.DebugType = (DEBUG_TYPE) (Switch.Link.DebugType & CvDebug);
|
|
|
|
if (!fPdb || IncludeDebugSection) {
|
|
// UNDONE: Issue a warning?
|
|
|
|
Switch.Link.DebugType = NoDebug;
|
|
}
|
|
|
|
// UNDONE: There is probably a better place for this.
|
|
|
|
ImageOptionalHdr.Subsystem = IMAGE_SUBSYSTEM_NATIVE;
|
|
}
|
|
|
|
if (Switch.Link.DebugInfo == None) {
|
|
Switch.Link.DebugType = NoDebug;
|
|
}
|
|
|
|
// Check switches to decide if linking incremental or not.
|
|
|
|
CheckSwitchesForIncrementalLink(pimage);
|
|
|
|
if ((Switch.Link.DebugType & CvDebug) == 0) {
|
|
// If there is no CodeView debug info, there is no use for a PDB.
|
|
|
|
fPdb = FALSE;
|
|
}
|
|
|
|
if ((ImageOptionalHdr.Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) && fDLL(pimage)) {
|
|
// Set the default subsystem for a DLL to WINDOWS_GUI
|
|
|
|
// UNDONE: Why? Better to do this with import lib or C runtime
|
|
|
|
ImageOptionalHdr.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_GUI;
|
|
}
|
|
|
|
if (Switch.Link.fROM) {
|
|
// Never checksum ROM images
|
|
|
|
Switch.Link.fChecksum = FALSE;
|
|
}
|
|
|
|
if (fNeedBanner) {
|
|
PrintBanner();
|
|
}
|
|
|
|
savArgc = Argc;
|
|
savArgv = Argv;
|
|
|
|
if (fINCR) {
|
|
|
|
// Read in incr db file if possible
|
|
ReadIncrDbFile(&pimage);
|
|
|
|
if (fINCR) {
|
|
// Determine timestamps of all files if incr build is still on
|
|
|
|
DetermineTimeStamps();
|
|
}
|
|
}
|
|
|
|
if (!fExplicitOptRef) {
|
|
// -OPT:REF is the default for non-incremental non-debug links
|
|
|
|
Switch.Link.fTCE = !fINCR && (Switch.Link.DebugInfo == None);
|
|
}
|
|
|
|
if (fINCR && !fIncrDbFile && Switch.Link.fNotifyFullBuild && fTest) {
|
|
// Let user know that a full build has been kicked off
|
|
|
|
PostNote(NULL, FULLBUILD);
|
|
}
|
|
|
|
rc = -1;
|
|
|
|
if (fIncrDbFile) {
|
|
|
|
#ifdef ILINKLOG
|
|
wMachine = pimage->ImgFileHdr.Machine;
|
|
#endif // ILINKLOG
|
|
|
|
// Try an incremental link
|
|
|
|
rc = IncrBuildImage(&pimage);
|
|
|
|
if (rc == -1) {
|
|
// Incremental link failed, spawn a full link
|
|
|
|
return(SpawnFullBuild(TRUE));
|
|
}
|
|
}
|
|
|
|
if (!fINCR || (!fIncrDbFile && rc)) {
|
|
// Non incremental link OR a full ilink build (rc=0 after spawn)
|
|
|
|
rc = BuildImage(pimage, &fCvpack);
|
|
|
|
#ifdef ILINKLOG
|
|
wMachine = pimage->ImgFileHdr.Machine;
|
|
#endif // ILINKLOG
|
|
|
|
// If a full ilink save the image file
|
|
|
|
if (fINCR) {
|
|
SaveImage(pimage);
|
|
}
|
|
}
|
|
|
|
FileCloseAll();
|
|
RemoveConvertTempFiles();
|
|
|
|
|
|
if (fINCR) {
|
|
#ifdef INSTRUMENT
|
|
LogNoteEvent(Log, SZILINK, NULL, letypeEnd, NULL);
|
|
LogClose(Log);
|
|
#endif // INSTRUMENT
|
|
|
|
#ifdef ILINKLOG
|
|
IlinkLog((UINT)-1); // ilink success
|
|
#endif // ILINKLOG
|
|
|
|
if (fIncrDbFile) {
|
|
return(fTest ? 6 : 0);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
if (fCvpack) {
|
|
assert(rc == 0);
|
|
fflush(NULL);
|
|
|
|
if ((rc = CvPackExe()) != 0) {
|
|
Fatal(NULL, CVPACKERROR);
|
|
} else if (VerifyImageSize) {
|
|
FileReadHandle = FileOpen(OutFilename, O_RDONLY | O_BINARY, 0);
|
|
FileSeek(FileReadHandle, CoffHeaderSeek + sizeof(IMAGE_FILE_HEADER), SEEK_SET);
|
|
FileRead(FileReadHandle, &ImageOptionalHdr, pimage->ImgFileHdr.SizeOfOptionalHeader);
|
|
FileClose(FileReadHandle, TRUE);
|
|
}
|
|
}
|
|
|
|
if (VerifyImageSize && ImageOptionalHdr.SizeOfImage > VerifyImageSize) {
|
|
Warning(NULL, IMAGELARGERTHANKEY, ImageOptionalHdr.SizeOfImage, VerifyImageSize);
|
|
}
|
|
|
|
if (Switch.Link.fMap) {
|
|
EmitMap(pimage, OutFilename);
|
|
}
|
|
|
|
fclose(InfoStream);
|
|
|
|
if (szReproDir != NULL) {
|
|
CloseReproDir();
|
|
}
|
|
|
|
#ifdef INSTRUMENT
|
|
LogNoteEvent(Log, SZILINK, NULL, letypeEnd, NULL);
|
|
LogClose(Log);
|
|
#endif // INSTRUMENT
|
|
|
|
#ifdef ILINKLOG
|
|
IlinkLog((UINT)-1); // full link success
|
|
#endif // ILINKLOG
|
|
|
|
return(rc);
|
|
|
|
#undef ImageOptionalHdr
|
|
#undef Switch
|
|
}
|
|
|
|
|
|
void
|
|
BuildArgList (
|
|
PIMAGE pimage,
|
|
PCON pcon,
|
|
PNAME_LIST pnl,
|
|
char *Arguments
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds the list of args specified as a glob in the directives section.
|
|
|
|
Arguments:
|
|
|
|
pimage - ptr to image.
|
|
|
|
pmod - pmod that contained the directives.
|
|
|
|
pnl - ptr of list to add args to.
|
|
|
|
Arguments - A pointer to a string containing linker switches.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMOD pmod;
|
|
const char *szToken;
|
|
|
|
pmod = PmodPCON(pcon);
|
|
|
|
szToken = SzGetArgument(Arguments, NULL);
|
|
|
|
while (szToken) {
|
|
char c;
|
|
|
|
// Fetch first character of argument.
|
|
|
|
c = *szToken;
|
|
|
|
// If argument is a switch, then add it to
|
|
// the switch list (but don't include the switch character).
|
|
|
|
if (c == '/' || c == '-') {
|
|
char *szName;
|
|
|
|
if (szToken[1] == '?') {
|
|
szToken++;
|
|
}
|
|
|
|
szName = SzDup(szToken+1);
|
|
|
|
if (fINCR && !fIncrDbFile && !FIsLibPMOD(pmod)) {
|
|
// On full-links save all directives; currently no libs done
|
|
|
|
SaveDirectiveSz(szName, pimage->pstDirective, pmod);
|
|
}
|
|
|
|
AddArgument(pnl, szName);
|
|
}
|
|
|
|
szToken = SzGetArgument(NULL, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ApplyDirectives(
|
|
PIMAGE pimage,
|
|
PCON pcon,
|
|
char *Arguments
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Applys directives from object or library files in the form
|
|
of switches.
|
|
|
|
Arguments:
|
|
|
|
Arguments - A pointer to a string containing linker switches.
|
|
|
|
pst - external symbol table
|
|
|
|
Filename - name of the file that had the directives
|
|
|
|
pcon - pcon that is the .drectve section.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
char szComFileName[_MAX_PATH * 2];
|
|
|
|
SwitchArguments.First = SwitchArguments.Last = 0;
|
|
SwitchArguments.Count = 0;
|
|
|
|
BuildArgList(pimage, pcon, &SwitchArguments, Arguments);
|
|
|
|
SzComNamePMOD(PmodPCON(pcon), szComFileName);
|
|
|
|
ProcessLinkerSwitches(pimage, pcon, szComFileName);
|
|
}
|