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.
951 lines
28 KiB
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);
|
|
}
|