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

951 lines
28 KiB

/***********************************************************************
* Microsoft (R) 32-Bit Incremental Linker
*
* Copyright (C) Microsoft Corp 1992-95. All rights reserved.
*
* File: vxd.cpp
*
* File Comments:
*
* Linker behavior specific to the LE file format.
*
***********************************************************************/
#include "link.h"
#include "image_.h"
#include <exe_vxd.h>
#define cbHdrVXDMax 0x1000
static struct e32_exe exe =
{
{E32MAGIC1, E32MAGIC2}, // e32_magic[2]
E32LEBO, // e32_border
E32LEWO, // e32_worder
E32LEVEL, // e32_level
E32CPU386, // e32_cpu
4 /* NE_DEV386 */, // e32_os
0, // e32_ver
E32NOTP, // e32_mflags
0, // e32_mpages
0, // e32_startobj TO BE SET
0, // e32_eip TO BE SET
0, // e32_stackobj
0, // e32_esp
0, // e32_pagesize
0, // e32_lastpagesize TO BE SET
0, // e32_fixupsize TO BE SET
0, // e32_fixupsum NEEDS SETTING?
0, // e32_ldrsize TO BE SET
0, // e32_ldrsum NEEDS SETTING?
0, // e32_objtab
0, // e32_objcnt
0, // e32_objmap
0, // e32_itermap
0, // e32_rsrctab
0, // e32_rsrccnt
0, // e32_restab TO BE SET
0, // e32_enttab TO BE SET
0, // e32_dirtab
0, // e32_dircnt
0, // e32_fpagetab TO BE SET
0, // e32_frectab TO BE SET
0, // e32_impmod TO BE SET
0, // e32_impmodcnt
0, // e32_impproc TO BE SET
0, // e32_pagesum
0, // e32_datapage TO BE SET
0, // e32_preload TO BE SET
0, // e32_nrestab TO BE SET
0, // e32_cbnrestab TO BE SET
0, // e32_nressum
0, // e32_autodata
0, // e32_debuginfo
0, // e32_debuglen
0, // e32_instpreload
0, // e32_instdemand
0, // e32_heapsize
{0}, // e32_res3
0, // e32_winresoff
0, // e32_winreslen
0, // Dev386_Device_ID
0x0400 // Dev386_DDK_Version
};
// VXD-specific image initialization (done after the general-purpose
// initialization performed by InitImage()).
void
InitImageVXD(PIMAGE pimage)
{
// Set default section alignment to 512
pimage->ImgOptHdr.SectionAlignment = SECTOR_SIZE;
// UNDONE: By setting FileAlignment to SectionAlignment, the first section is
// UNDONE: aligned on a SectionAlignment boundary. This isn't necessary and
// UNDONE: wastes disk space.
pimage->ImgOptHdr.FileAlignment = pimage->ImgOptHdr.SectionAlignment;
}
VXDRELOC *FindBaseReloc(
VXDPAGE *pvxdpage,
BYTE isecTarget,
DWORD ibTarget,
BYTE bType
)
/*++
Routine Description:
Locates the base relocation record in the given page group whose
section:offset "destination address" matches that given. The reloc
must be of the correct type to match.
Arguments:
pvxdpage - The page group in which to search
isecTarget - Section number to match
ibTarget - Offset to match
bType - Type of relocation to match
Return Value:
Pointer to the VXDRELOC, or NULL if not found.
--*/
{
VXDRELOC *pvxdreloc;
for (pvxdreloc = pvxdpage->pvxdreloc; pvxdreloc != NULL; pvxdreloc = pvxdreloc->pvxdrelocNext) {
if (pvxdreloc->ibTarget != ibTarget) {
continue;
}
if (pvxdreloc->isecTarget != isecTarget) {
continue;
}
if (pvxdreloc->bType != bType) {
continue;
}
if (pvxdreloc->cibSrc == 0xFF) {
// Reloc chain is maximum length. Don't add any more.
continue;
}
break;
}
return(pvxdreloc);
}
// CbHdr "method" -- computes the file header size. *ibHdrStart gets the
// file position of the header signature (this is not 0 in the case of
// files with DOS stubs).
DWORD
CbHdrVXD(PIMAGE pimage, DWORD *pibHdrStart, DWORD *pfoSectionHdrs)
{
*pibHdrStart = 0; // no DOS stub
*pfoSectionHdrs = pimage->cbDosHeader + sizeof(struct e32_exe);
// Set the starting position for the object pagemap ...
pimage->foHeaderCur = *pfoSectionHdrs
+ sizeof(struct o32_obj) *
pimage->ImgFileHdr.NumberOfSections;
pimage->foPageMapStart = pimage->foHeaderCur;
// For now, return a fixed value (& we will assert that the value is
// not exceeded). Later this should be replaced with a calculated
// upper bound based on the info we have available (which isn't much
// since Pass2 hasn't occurred yet).
return __max(pimage->foPageMapStart, cbHdrVXDMax);
}
// WriteSectionHeaderVXD: write an entry in the object table, at the current
// file position.
//
// The "SectionHeader" parameter is vestigial.
void
WriteSectionHeaderVXD (
PIMAGE pimage,
INT Handle,
PSEC psec,
PIMAGE_SECTION_HEADER /* SectionHeader */
)
{
struct o32_obj obj;
DWORD cpage;
DWORD foT;
DWORD ippage;
DWORD cppage;
struct o32_map map;
DWORD ipage;
if (pimage->cpage == 0) {
// Found the first page ...
pimage->foFirstPage = psec->foRawData;
}
obj.o32_size = psec->cbVirtualSize;
obj.o32_base = 0;
obj.o32_flags = 0;
if (psec->flags & IMAGE_SCN_MEM_READ) {
obj.o32_flags |= OBJREAD;
}
if (psec->flags & IMAGE_SCN_MEM_WRITE) {
obj.o32_flags |= OBJWRITE;
}
if (psec->flags & IMAGE_SCN_MEM_SHARED) {
obj.o32_flags |= OBJSHARED;
}
if (psec->flags & IMAGE_SCN_MEM_EXECUTE) {
obj.o32_flags |= OBJEXEC;
}
if (psec->flags & IMAGE_SCN_MEM_16BIT) {
obj.o32_flags |= OBJALIAS16;
} else {
obj.o32_flags |= OBJBIGDEF;
}
if (psec->flags & IMAGE_SCN_MEM_RESIDENT) {
obj.o32_flags |= OBJRESIDENT;
}
if (psec->fDiscardable) {
obj.o32_flags |= OBJDISCARD;
}
if (psec->fPreload) {
obj.o32_flags |= OBJPRELOAD;
}
if (psec->fIopl) {
obj.o32_flags |= OBJIOPL;
}
if (psec->fConforming) {
obj.o32_flags |= OBJCONFORM;
}
obj.o32_pagemap = pimage->cpage + 1;
obj.o32_mapsize = cpage = (psec->cbVirtualSize + pimage->ImgOptHdr.SectionAlignment - 1) / pimage->ImgOptHdr.SectionAlignment;
// Copy first 4 chars of class name to "reserved" field. Class name ends up
// as the segment name, so we just copy the beginning of that. This enables
// some tuning of the VxD loader.
obj.o32_reserved = 0;
strncpy((char *) &obj.o32_reserved, psec->szName, sizeof(obj.o32_reserved));
FileWrite(Handle, &obj, sizeof(obj));
// Set the "last page size" field in the header ... this will end up
// being set by the last section, since we see them in address order.
assert(cpage > 0);
exe.e32_lastpagesize = psec->cbVirtualSize - (cpage - 1) * pimage->ImgOptHdr.SectionAlignment;
if (psec->fPreload) {
exe.e32_preload += cpage;
}
foT = FileTell(Handle);
FileSeek(Handle, pimage->foHeaderCur, SEEK_SET);
ippage = 1 + (psec->foRawData - pimage->foFirstPage) / pimage->ImgOptHdr.SectionAlignment;
cppage = (psec->cbRawData + pimage->ImgOptHdr.SectionAlignment - 1) / pimage->ImgOptHdr.SectionAlignment;
map.o32_pageflags = VALID;
for (ipage = 0; ipage < cppage; ipage++, ippage++) {
PUTPAGEIDX(map, ippage);
FileWrite(Handle, &map, sizeof(map));
}
pimage->foHeaderCur += cppage * sizeof(map);
pimage->cpage += cppage;
FileSeek(Handle, foT, SEEK_SET); // restore file pos to after header
}
// Writes some stuff into the image header that goes after the object
// table and pagemap. Should be called after writing all the section
// headers but before calling WriteHeaderVXD().
void
WriteExtendedVXDHeader(PIMAGE pimage, INT fh)
{
BYTE cch;
char szFname[_MAX_FNAME];
WORD ich;
BYTE ZeroBuf[] = {0, 0, 0};
// Write the resident names table. Currently this always has one string
// in it, the name of the image.
//
if (szModuleName[0] != '\0') {
szFname[0] = '\0';
strcpy(szFname, szModuleName);
} else {
_splitpath(OutFilename, NULL, NULL, szFname, NULL);
}
cch = (BYTE) strlen(szFname);
for (ich = 0; ich < cch; ich++) {
szFname[ich] = (char) toupper(szFname[ich]);
}
// For some reason, the string must be terminated by 3 '\0' bytes, not one.
szFname[ich++] = '\0';
szFname[ich++] = '\0';
szFname[ich++] = '\0';
pimage->foResidentNames = pimage->foHeaderCur;
FileSeek(fh, pimage->foHeaderCur, SEEK_SET);
FileWrite(fh, &cch, sizeof(cch));
FileWrite(fh, szFname, ich);
pimage->foHeaderCur += sizeof(cch) + ich;
}
// Writes the entry table - should be called immediately after WriteExtendedVXDHeader().
void
WriteVXDEntryTable(PIMAGE pimage, INT Handle)
{
BYTE cchWrite;
PARGUMENT_LIST parg;
WORD i;
WORD j;
WORD numUndefs = 0;
PSHORT pEntrySec;
PEXTERNAL *pEntrySym;
struct b32_bundle EntryBundle;
struct e32_entry EntryPoint;
pEntrySec = (PSHORT) PvAlloc(sizeof(SHORT) * ExportSwitches.Count);
pEntrySym = (PEXTERNAL *) PvAlloc(sizeof(PSHORT) * ExportSwitches.Count);
pimage->foEntryTable = pimage->foHeaderCur;
FileSeek(Handle, pimage->foHeaderCur, SEEK_SET);
for (i = 0, parg = ExportSwitches.First;
i < ExportSwitches.Count;
i++, parg = parg->Next) {
char *nameBuf;
char *pComma;
BOOL fNewSymbol;
PEXTERNAL pExtSym;
nameBuf = (char *) PvAlloc(strlen(parg->OriginalName) + 1);
// Remove ",@xxx" from entry name
strcpy(nameBuf, parg->OriginalName);
if ((pComma = (char *) strchr(nameBuf,',')) != NULL) {
*pComma = '\0';
}
cchWrite = (BYTE) strlen(nameBuf);
// Check that it's a known symbol
fNewSymbol = FALSE;
pExtSym = LookupExternName(pimage->pst,
(SHORT) ((cchWrite > 8) ? LONGNAME : SHORTNAME),
nameBuf, &fNewSymbol);
if (fNewSymbol) {
// oops, never heard of it ...
pEntrySec[i] = -1;
Error(NULL, UNDEFINED, nameBuf);
numUndefs++;
} else {
// it's known; note the section# for bundling, save the symbol ptr for future reference
pEntrySec[i] = PsecPCON(pExtSym->pcon)->isec;
pEntrySym[i] = pExtSym;
}
FreePv(nameBuf);
}
if (numUndefs) {
Fatal(OutFilename, UNDEFINEDEXTERNALS, numUndefs);
}
EntryPoint.e32_flags = E32EXPORT | E32SHARED; // use default flags for each entry point
// consolidate all entry points with the same section #
for (i = 0, parg = ExportSwitches.First;
i < ExportSwitches.Count;
i++, parg = parg->Next) {
if (pEntrySec[i] != -1) { // i.e. if this section hasn't already been accounted for
EntryBundle.b32_cnt = 1;
EntryBundle.b32_type = ENTRY32;
EntryBundle.b32_obj = (WORD)pEntrySec[i];
for (j = (WORD) (i + 1); j < ExportSwitches.Count; j++) {
if (pEntrySec[j] == pEntrySec[i]) {
pEntrySec[j] = -1; // mark it as used
EntryBundle.b32_cnt++;
}
}
FileWrite(Handle, &EntryBundle, sizeof(EntryBundle));
for (j = 0; j < ExportSwitches.Count; j++) {
if (PsecPCON(pEntrySym[j]->pcon)->isec == pEntrySec[i]) {
EntryPoint.e32_variant.e32_fwd.value = 0; // pad end of union with 0's
EntryPoint.e32_variant.e32_offset.offset32 =
pEntrySym[j]->ImageSymbol.Value
+ pEntrySym[j]->pcon->rva
- PsecPCON(pEntrySym[j]->pcon)->rva;
FileWrite(Handle, &EntryPoint, 6);
}
}
pEntrySec[i] = -1; // mark it as used
}
}
pimage->foHeaderCur = FileTell(Handle);
FreePv(pEntrySec);
FreePv(pEntrySym);
}
void
WriteHeaderVXD(PIMAGE pimage, INT Handle)
{
PSEC psecLast;
ENM_SEC enmSec;
DWORD foNonResident;
WORD ibComment;
WORD i;
BYTE cchWrite;
PARGUMENT_LIST parg;
WORD numUndefs = 0;
BYTE ZeroBuf[] = {0, 0, 0};
WORD isymNonResident = 0;
PEXTERNAL pextDDB = NULL;
// Find the last section (this is where the non-resident name table
// will be).
psecLast = NULL;
InitEnmSec(&enmSec, &pimage->secs);
while (FNextEnmSec(&enmSec)) {
if (psecLast == NULL || enmSec.psec->isec > psecLast->isec) {
psecLast = enmSec.psec;
}
}
assert(psecLast != NULL);
foNonResident = Align(4, psecLast->foPad);
// Write the non-resident names table. This table contains an entry
// for each string specified via "-comment", followed by an entry for
// each exported entry point.
FileSeek(Handle, foNonResident, SEEK_SET);
ibComment = 0;
while (ibComment < blkComment.cb) {
WORD cch = (WORD) strlen((char *) &blkComment.pb[ibComment]);
if (cch < 0xff) {
cchWrite = (BYTE) cch;
} else {
cchWrite = 0xff; // quietly truncate comment string
}
FileWrite(Handle, &cchWrite, sizeof(BYTE));
FileWrite(Handle, &blkComment.pb[ibComment], cchWrite);
FileWrite(Handle, &isymNonResident, sizeof(isymNonResident));
isymNonResident++;
ibComment += (WORD) (cch + 1);
}
// Write the names of exported symbols
for (i = 0, parg = ExportSwitches.First;
i < ExportSwitches.Count;
i++, parg = parg->Next) {
char *szExport;
char *pchComma;
BOOL fNewSymbol;
PEXTERNAL pext;
szExport = (char *) PvAlloc(strlen(parg->OriginalName) + 1);
strcpy(szExport, parg->OriginalName);
if ((pchComma = strchr(szExport, ',')) != NULL) {
*pchComma = '\0';
}
cchWrite = (BYTE) strlen(szExport);
fNewSymbol = FALSE;
pext = LookupExternSz(pimage->pst, szExport, &fNewSymbol);
if ((pextDDB == NULL) && (pchComma != NULL)) {
pchComma++; // Skip the comma
if (*pchComma == '@') {
DWORD Ordinal;
pchComma++; // Skip the '@'
sscanf(pchComma, "%li", &Ordinal);
if (Ordinal == 1) {
pextDDB = pext;
}
}
}
if (fNewSymbol) {
numUndefs++;
} else {
FileWrite(Handle, &cchWrite, sizeof(BYTE));
FileWrite(Handle, szExport, cchWrite);
FileWrite(Handle, &isymNonResident, sizeof(isymNonResident));
isymNonResident++;
}
FreePv(szExport);
}
// Write one null to terminate the non-resident names table ...
FileWrite(Handle, ZeroBuf, sizeof(BYTE));
if (numUndefs) {
Fatal(OutFilename, UNDEFINEDEXTERNALS, numUndefs);
}
exe.e32_nrestab = foNonResident;
exe.e32_cbnrestab = FileTell(Handle) - foNonResident;
// Put out NB10 info last after the non-resident name table
if (pimage->Switch.Link.DebugInfo != None && pimage->Switch.Link.DebugType & CvDebug) {
IMAGE_DEBUG_DIRECTORY debugDirectory;
// assign address & offset for debug sections (debug dirs & .debug$H)
pconDebugDir->foRawDataDest = FileTell(Handle);
pconCvSignature->foRawDataDest = pconDebugDir->foRawDataDest + pconDebugDir->cbRawData;
// write out debug dir
debugDirectory.Characteristics = 0;
debugDirectory.TimeDateStamp = pimage->ImgFileHdr.TimeDateStamp;
debugDirectory.MajorVersion = debugDirectory.MinorVersion = 0;
debugDirectory.AddressOfRawData = 0;
debugDirectory.PointerToRawData = pconCvSignature->foRawDataDest;
debugDirectory.SizeOfData = pconCvSignature->cbRawData;
debugDirectory.Type = IMAGE_DEBUG_TYPE_CODEVIEW;
FileWrite(FileWriteHandle, &debugDirectory, sizeof(IMAGE_DEBUG_DIRECTORY));
// write out nb10 record
FileWrite(FileWriteHandle, &nb10i, sizeof(nb10i));
FileWrite(FileWriteHandle, PdbFilename, (DWORD)(strlen(PdbFilename)+1));
FreePv(PdbFilename);
exe.e32_debuginfo = pconDebugDir->foRawDataDest;
exe.e32_debuglen = pconDebugDir->cbRawData;
}
if (pimage->fDynamicVxd) {
exe.e32_mflags |= E32MODVDEVDYN;
} else {
exe.e32_mflags |= E32MODVDEV;
}
exe.e32_mpages = pimage->cpage;
if (pextEntry != NULL) {
// Set up entry point.
DWORD rva = pextEntry->ImageSymbol.Value + pextEntry->pcon->rva;
PSEC psec = PsecFindIsec(pextEntry->ImageSymbol.SectionNumber, &pimage->secs);
assert(rva >= psec->rva);
exe.e32_startobj = psec->isec;
exe.e32_eip = rva - psec->rva;
}
exe.e32_pagesize = pimage->ImgOptHdr.SectionAlignment;
exe.e32_objtab = sizeof(struct e32_exe);
exe.e32_objcnt = pimage->ImgFileHdr.NumberOfSections;
exe.e32_objmap = pimage->foPageMapStart - pimage->cbDosHeader;
exe.e32_restab = pimage->foResidentNames - pimage->cbDosHeader;
exe.e32_enttab = pimage->foEntryTable - pimage->cbDosHeader;
exe.e32_fpagetab = pimage->foFixupPageTable - pimage->cbDosHeader;
exe.e32_frectab = pimage->foFixupRecordTable - pimage->cbDosHeader;
exe.e32_impmod = pimage->foHeaderCur - pimage->cbDosHeader;
exe.e32_impproc = pimage->foHeaderCur - pimage->cbDosHeader;
exe.e32_fixupsize = exe.e32_impmod - exe.e32_fpagetab;
exe.e32_ldrsize = exe.e32_fpagetab - exe.e32_objtab;
exe.e32_datapage = pimage->foFirstPage;
if (pextDDB != NULL) {
struct VxD_Desc_Block ddb;
assert(pextDDB->Flags & EXTERN_DEFINED);
FileSeek(Handle,
pextDDB->pcon->foRawDataDest + pextDDB->ImageSymbol.Value,
SEEK_SET);
FileRead(Handle, &ddb, sizeof(struct VxD_Desc_Block));
exe.Dev386_Device_ID = ddb.DDB_Req_Device_Number;
exe.Dev386_DDK_Version = ddb.DDB_SDK_Version;
}
FileSeek(Handle, pimage->cbDosHeader, SEEK_SET); // BUG -- doesn't work if stub exists
FileWrite(Handle, &exe, sizeof(exe));
}
void
WriteVXDBaseRelocations (
PIMAGE pimage
)
/*++
Routine Description:
Writes VxD base relocations.
Arguments:
pimage - Pointer to the VxD image.
Return Value:
None.
--*/
{
VXDPAGE *rgvxdpage;
BYTE isecCur;
WORD ippageSec;
WORD ipageCur;
DWORD cbFixupEst;
BOOL fSpanPage;
BASE_RELOC *reloc;
VXDPAGE *pvxdpage;
DWORD ibPageCur;
VXDRELOC *pvxdreloc;
BYTE *pbFixups;
BYTE *pbOut;
DWORD cbFixup;
WORD ippage;
DWORD foHold;
// Allocate VXDPAGEs for all pages in the image
rgvxdpage = (VXDPAGE *) PvAllocZ(pimage->cpage * sizeof(VXDPAGE));
isecCur = 0; // Force new section to be recognized
cbFixupEst = 0;
// First relocation does not span a page
fSpanPage = FALSE;
for (reloc = rgbr; reloc < pbrCur;) {
BYTE isecSrc;
DWORD ibSrc;
BYTE isecTarget;
DWORD ibTarget;
BYTE bType;
SHORT ipageSrc;
isecSrc = VXD_UNPACK_SECTION(reloc->rva);
ibSrc = VXD_UNPACK_OFFSET(reloc->rva);
assert(isecSrc != 0);
isecTarget = VXD_UNPACK_SECTION(reloc->Value);
// ibTarget = VXD_UNPACK_OFFSET(reloc->Value);
struct _OFF{signed long off:24;}OFF;
ibTarget = OFF.off = VXD_UNPACK_OFFSET(reloc->Value);
assert(isecTarget != 0);
bType = (BYTE) ((reloc->Type == IMAGE_REL_BASED_VXD_RELATIVE) ? 8 : 7);
if (isecCur != isecSrc) {
PSEC psec;
assert(!fSpanPage);
isecCur = isecSrc;
psec = PsecFindIsec(isecCur, &pimage->secs);
assert(psec != NULL);
// Calculate zero-based physical page number
ippageSec = (WORD) ((psec->foRawData - pimage->foFirstPage) / pimage->ImgOptHdr.SectionAlignment);
assert(ippageSec < pimage->cpage);
ipageCur = 0xffff; // Force new page to be recognized
}
ipageSrc = (WORD) (ibSrc / pimage->ImgOptHdr.SectionAlignment);
if (fSpanPage) {
ipageSrc++;
// Span page code assumes no reloc spans two page boundaries
assert(pimage->ImgOptHdr.SectionAlignment >= sizeof(DWORD));
}
if (ipageCur != ipageSrc) {
// We've entered a new page; initialize a new base reloc group.
ipageCur = ipageSrc;
assert((DWORD) (ippageSec + ipageSrc) < pimage->cpage);
pvxdpage = rgvxdpage + ippageSec + ipageSrc;
ibPageCur = (DWORD) ipageSrc * pimage->ImgOptHdr.SectionAlignment;
}
pvxdreloc = FindBaseReloc(pvxdpage, isecTarget, ibTarget, bType);
if (pvxdreloc == NULL) {
// This is a new destination address.
pvxdreloc = (VXDRELOC *) PvAlloc(sizeof(VXDRELOC));
pvxdreloc->pvxdrelocNext = pvxdpage->pvxdreloc;
pvxdpage->pvxdreloc = pvxdreloc;
pvxdreloc->bType = bType;
pvxdreloc->isecTarget = isecTarget;
pvxdreloc->ibTarget = ibTarget;
pvxdreloc->cibSrc = 0;
pvxdreloc->cibAlloc = VXD_BLOCKSIZE;
pvxdreloc->pibSrc = (WORD *) PvAlloc(VXD_BLOCKSIZE * sizeof(WORD));
// Assume this fixup is not chained
cbFixupEst += (pvxdreloc->ibTarget >= 0x10000) ? 11 : 9;
} else {
if (pvxdreloc->cibSrc == 1) {
// Adjust the size for a chained relocation
cbFixupEst--;
}
// Each chained relocation requires a WORD for the page offset
cbFixupEst += sizeof(WORD);
if (pvxdreloc->cibSrc >= pvxdreloc->cibAlloc) {
WORD *pibNew;
// We've run out of room in the array of source
// addresses. Reallocate the array to make room.
pvxdreloc->cibAlloc += VXD_BLOCKSIZE;
pibNew = (WORD *) PvRealloc(pvxdreloc->pibSrc,
pvxdreloc->cibAlloc * sizeof(WORD));
pvxdreloc->pibSrc = pibNew;
}
}
pvxdreloc->pibSrc[pvxdreloc->cibSrc] = (WORD) (ibSrc - ibPageCur);
pvxdreloc->cibSrc++;
fSpanPage = ((ibSrc + sizeof(DWORD)) > (ibPageCur + pimage->ImgOptHdr.SectionAlignment));
if (!fSpanPage) {
// If this reloc spans a page, process it twice.
reloc++;
}
}
// Traverse the list of relocs and write them to the image file.
// Allocate a buffer into which to write the fixup data.
pbFixups = (BYTE *) PvAlloc(cbFixupEst);
// Skip over the fixup pages header, which takes one DWORD per page,
// plus one extra DWORD.
pbOut = pbFixups;
// Write the relocs for each page.
foHold = FileTell(FileWriteHandle);
// Prepare to write fixup page table as we build fixups in memory
FileSeek(FileWriteHandle, pimage->foHeaderCur, SEEK_SET);
pimage->foFixupPageTable = pimage->foHeaderCur;
pvxdpage = rgvxdpage;
for (ippage = 1; ippage <= pimage->cpage; ippage++, pvxdpage++) {
// Remember the file offset, relative to the start of all fixups,
// for the fixups associated with this page.
pvxdpage->fo = (DWORD) (pbOut - pbFixups);
cbFixup = (DWORD) (pbOut - pbFixups);
// Write entry for this page in the fixup page table
FileWrite(FileWriteHandle, &cbFixup, sizeof(DWORD));
// Write the fixups themselves.
for (pvxdreloc = pvxdpage->pvxdreloc; pvxdreloc != NULL; pvxdreloc = pvxdreloc->pvxdrelocNext) {
BOOL f32BitOff;
BYTE bFlags;
assert(pvxdreloc->cibSrc > 0);
if (pvxdreloc->cibSrc > 1) {
pvxdreloc->bType |= NRCHAIN;
}
f32BitOff = pvxdreloc->ibTarget >= 0x10000;
// UNDONE: There is no support for NRRORD relocations
bFlags = 0x00 /* UNDONE: NRRINT */;
if (f32BitOff) {
bFlags |= NR32BITOFF;
}
#if 0
// UNDONE: This can't occur because isecTarget is currently limited to a byte
if (pvxdreloc->isecTarget > 255) {
bFlags |= NR16OBJMOD;
}
#endif
*pbOut++ = pvxdreloc->bType;
*pbOut++ = bFlags;
if (pvxdreloc->cibSrc == 1) {
*(WORD UNALIGNED *) pbOut = pvxdreloc->pibSrc[0];
pbOut += sizeof(WORD);
} else {
*pbOut++ = pvxdreloc->cibSrc;
}
#if 0
// UNDONE: This can't occur because isecTarget is currently limited to a byte
if (pvxdreloc->isecTarget > 255) {
*(WORD UNALIGNED *) pbOut = pvxdreloc->isecTarget;
pbOut += sizeof(WORD);
} else
#endif
{
*pbOut++ = pvxdreloc->isecTarget;
}
if (f32BitOff) {
*(DWORD UNALIGNED *) pbOut = pvxdreloc->ibTarget;
pbOut += sizeof(DWORD);
} else {
*(WORD UNALIGNED *) pbOut = (WORD) pvxdreloc->ibTarget;
pbOut += sizeof(WORD);
}
// UNDONE: If relocation is additive, a WORD or DWORD is emitted here
if (pvxdreloc->cibSrc > 1) {
WORD i;
// UNDONE: Use memcpy
for (i = 0; i < pvxdreloc->cibSrc; i++) {
*(WORD UNALIGNED *) pbOut = pvxdreloc->pibSrc[i];
pbOut += sizeof(WORD);
}
}
}
}
cbFixup = (DWORD) (pbOut - pbFixups);
assert(cbFixup <= cbFixupEst);
// Write extra entry to fixup page table to identify end of last page
FileWrite(FileWriteHandle, &cbFixup, sizeof(DWORD));
// Now write the fixups themselves
pimage->foFixupRecordTable = FileTell(FileWriteHandle);
// see if we had enough space reserved else time to relink
// UNDONE: the way to handle this is to slide everything down in the image.
if ((pimage->foFixupRecordTable + (DWORD)cbFixup) >
pimage->ImgOptHdr.BaseOfCode) {
FreePv(pbFixups);
ExitProcess(SpawnFullBuildVXD(pimage->foFixupRecordTable + (DWORD)cbFixup));
}
FileWrite(FileWriteHandle, pbFixups, cbFixup);
// Finally, update the current header pointer. Then restore file pointer.
pimage->foHeaderCur = FileTell(FileWriteHandle);
FileSeek(FileWriteHandle, foHold, SEEK_SET);
// Free the memory we used:
FreePv(pbFixups);
pvxdpage = rgvxdpage;
for (ippage = 1; ippage <= pimage->cpage; ippage++, pvxdpage++) {
for (pvxdreloc = pvxdpage->pvxdreloc; pvxdreloc != NULL; ) {
VXDRELOC *pvxdrelocNext;
pvxdrelocNext = pvxdreloc->pvxdrelocNext;
FreePv(pvxdreloc->pibSrc);
FreePv(pvxdreloc);
pvxdreloc = pvxdrelocNext;
}
}
FreePv(rgvxdpage);
}