2518 lines
71 KiB
C
2518 lines
71 KiB
C
/*
|
|
* Copyright Microsoft Corporation 1985-1987
|
|
*
|
|
* This Module contains Proprietary Information of Microsoft
|
|
* Corporation and should be treated as Confidential.
|
|
*/
|
|
|
|
/****************************************************************
|
|
* *
|
|
* NEWDEB.C *
|
|
* *
|
|
* Symbolic debugging support. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include <minlit.h> /* Basic types and constants */
|
|
#include <bndtrn.h> /* More types and constants */
|
|
#include <bndrel.h> /* Types and constants */
|
|
#include <lnkio.h> /* Linker input/output */
|
|
#if OIAPX286
|
|
#include <xenfmt.h> /* Xenix executable format defs. */
|
|
#endif
|
|
#if OEXE
|
|
#include <newexe.h> /* Segmented executable format */
|
|
#endif
|
|
#if EXE386
|
|
#include <exe386.h>
|
|
#endif
|
|
#include <lnkmsg.h> /* Error messages */
|
|
#include <extern.h> /* External function declarations */
|
|
#ifndef CVVERSION
|
|
#if OIAPX286
|
|
#define CVVERSION 0 /* Assume new CV exe format */
|
|
#else
|
|
#define CVVERSION 1 /* Assume new CV exe format */
|
|
#endif
|
|
#endif
|
|
#if (CPU8086 OR CPU286)
|
|
#define TFAR far
|
|
#else
|
|
#define TFAR
|
|
#endif
|
|
#include <newdeb.h> /* Symbolic debug types */
|
|
extern SEGTYPE segAdjCom; /* Segment moved by 0x100 in .com programs */
|
|
#if AUTOVM
|
|
BYTE FAR * NEAR FetchSym1(RBTYPE rb, WORD Dirty);
|
|
#define FETCHSYM FetchSym1
|
|
#define PROPSYMLOOKUP EnterName
|
|
#else
|
|
#define FETCHSYM FetchSym
|
|
#define PROPSYMLOOKUP EnterName
|
|
#endif
|
|
|
|
#define CVDEBUG FALSE
|
|
#define SRCDEBUG FALSE
|
|
|
|
#define DNT_START 64
|
|
#define Round2Dword(x) (((x) + 3L) & ~3L)
|
|
|
|
typedef struct raPair
|
|
{
|
|
DWORD raStart;
|
|
DWORD raEnd;
|
|
}
|
|
RAPAIR;
|
|
|
|
/*
|
|
* FUNCTION PROTOTYPES
|
|
*/
|
|
|
|
LOCAL WORD NEAR IsDebTyp(APROPSNPTR prop);
|
|
LOCAL WORD NEAR IsDebSym(APROPSNPTR prop);
|
|
LOCAL int NEAR OutLibSec(void);
|
|
void NEAR GetName(AHTEPTR ahte, BYTE *pBuf);
|
|
LOCAL DWORD NEAR OutSrcModule(CVSRC FAR *pSrcLines);
|
|
LOCAL void NEAR PutDnt(DNT *pDnt);
|
|
LOCAL WORD NEAR HasCVinfo(APROPFILEPTR apropFile);
|
|
LOCAL void NEAR OutModules(void);
|
|
LOCAL void NEAR Pad2Dword(void);
|
|
#if CVDEBUG
|
|
LOCAL void NEAR DumpDNT(DNT *pDnt);
|
|
#endif
|
|
|
|
extern long lfaBase; /* Base address */
|
|
extern int fSameComdat; /* Set if LINSYM to the same COMDAT */
|
|
|
|
/*
|
|
* CodeView signature - if changes notify the developers of the
|
|
* following programs:
|
|
* - QuickC
|
|
* - Resource Compiler - Windows and PM
|
|
* - CodeView and its utilities
|
|
*/
|
|
char szSignature[4] = "NB05";
|
|
|
|
RBTYPE rhteDebSrc; /* Class "DEBSRC" virt addr */
|
|
RBTYPE rhteDebSym; /* Class "DEBSYM" virt addr */
|
|
RBTYPE rhteDebTyp; /* Class "DEBTYP" virt addr */
|
|
RBTYPE rhteTypes;
|
|
RBTYPE rhteSymbols;
|
|
RBTYPE rhte0Types;
|
|
RBTYPE rhte0Symbols;
|
|
LOCAL SBTYPE sbLastModule; /* Name of THEADR last observed */
|
|
#if NOT CVVERSION
|
|
LOCAL long lfaDebHdr; /* Position of section table */
|
|
LOCAL long lfaSegMod;
|
|
#endif
|
|
LOCAL WORD dntMax; // DNT table size
|
|
LOCAL WORD dntMac; // Count of DNT entries in table
|
|
LOCAL DNT FAR *rgDnt; // Table of DNT entries
|
|
LOCAL DWORD FAR *fileBase; // Table of offsets to source file info
|
|
LOCAL RAPAIR FAR *raSeg; // Table of physical starting and ending offsets
|
|
// of the contribution to the logical segments
|
|
LOCAL WORD FAR *segNo; // Table of physical segment indicies
|
|
LOCAL WORD cMac; // Current number of elements in the above tables
|
|
|
|
#ifdef CVPACK_MONDO
|
|
#define CVPACK_SHARED 1
|
|
#define REVERSE_MODULE_ORDER_FOR_CVPACK 1
|
|
#else
|
|
#define CVPACK_SHARED 0
|
|
#define REVERSE_MODULE_ORDER_FOR_CVPACK 0
|
|
#endif
|
|
|
|
//these macros help to make the source not so cluttered with #ifdefs...
|
|
|
|
#if CVPACK_SHARED
|
|
|
|
#define IF_NOT_CVPACK_SHARED(x)
|
|
#define WriteCopy(x,y) WriteSave(TRUE, x, y)
|
|
#define WriteNocopy(x,y) WriteSave(FALSE, x, y)
|
|
#define FTELL_BSRUNFILE() lposCur
|
|
#define LINK_TRACE(x)
|
|
|
|
|
|
// cvpack might read parts of the header more than once, we use this
|
|
// constant to ensure that at least CB_HEADER_SAVE bytes are always
|
|
// available to be re-read by cvpack
|
|
|
|
#define CB_HEADER_SAVE 128
|
|
|
|
void WriteSave(FTYPE fCopy, void *pv, UINT cb);
|
|
void WriteFlushSignature(void);
|
|
void WriteFlushAll(void);
|
|
|
|
// cvpack cached blocks...
|
|
|
|
typedef struct _BL
|
|
{
|
|
long lpos; // position of this block in the file
|
|
BYTE * pb; // pointer to bytes in this block
|
|
} BL;
|
|
|
|
#define iblNil (-1)
|
|
|
|
static long lposCur; // current position in the file
|
|
static long lposMac; // size of the file
|
|
static long iblLim; // number of blocks used
|
|
static long iblCur; // current block we are reading
|
|
static long iblMac; // number of blocks allocated
|
|
static long cbRealBytes; // number of bytes actually written to the file
|
|
static int ichCur; // index within the current block
|
|
static int cbCur; // number of bytes left in the current block
|
|
|
|
static BL *rgbl; // array of buffered write blocks
|
|
|
|
// number of bytes in a particular block
|
|
|
|
__inline int CbIbl(int ibl)
|
|
{
|
|
// compute the difference between this block and the next block
|
|
// unless this is the last block then use lposMac
|
|
|
|
if (ibl == iblLim - 1)
|
|
return lposMac - rgbl[ibl].lpos;
|
|
else
|
|
return rgbl[ibl+1].lpos - rgbl[ibl].lpos;
|
|
}
|
|
|
|
#define C_BL_INIT 256
|
|
#else
|
|
#define IF_NOT_CVPACK_SHARED(x) x
|
|
#define WriteCopy(x,y) WriteExe(x,y)
|
|
#define WriteNocopy(x,y) WriteExe(x,y)
|
|
#define FTELL_BSRUNFILE() ftell(bsRunfile)
|
|
#define LINK_TRACE(x)
|
|
#endif
|
|
|
|
#if CVDEBUG
|
|
LOCAL void NEAR DumpDNT(DNT *pDnt)
|
|
{
|
|
if (pDnt == NULL)
|
|
return;
|
|
|
|
fprintf(stdout, "iMod = %d(0x%x)", pDnt->iMod, pDnt->iMod);
|
|
switch (pDnt->sst)
|
|
{
|
|
case SSTMODULES:
|
|
case SSTMODULES4:
|
|
fprintf(stdout, " SSTMODULES: ");
|
|
break;
|
|
|
|
case SSTTYPES:
|
|
case SSTTYPES4:
|
|
fprintf(stdout, " SSTYPES: ");
|
|
break;
|
|
|
|
case SSTPUBLICS:
|
|
case SSTPUBLICS4:
|
|
fprintf(stdout, " SSTPUBLICS: ");
|
|
break;
|
|
|
|
case SSTPUBLICSYM:
|
|
fprintf(stdout, " SSTPUBLICSYM: ");
|
|
break;
|
|
|
|
case SSTSYMBOLS:
|
|
case SSTSYMBOLS4:
|
|
fprintf(stdout, " SSTSYMBOLS: ");
|
|
break;
|
|
|
|
case SSTALIGNSYM:
|
|
fprintf(stdout, " SSTALIGNSYM: ");
|
|
break;
|
|
|
|
case SSTSRCLINES:
|
|
case SSTNSRCLINES:
|
|
case SSTSRCLNSEG:
|
|
fprintf(stdout, " SSTSRCLINES: ");
|
|
break;
|
|
|
|
case SSTSRCMODULE:
|
|
fprintf(stdout, " SSTSRCMODULE: ");
|
|
break;
|
|
|
|
case SSTLIBRARIES:
|
|
case SSTLIBRARIES4:
|
|
fprintf(stdout, " SSTLIBRARIES: ");
|
|
break;
|
|
|
|
case SSTGLOBALSYM:
|
|
fprintf(stdout, " SSTGLOBALSYM: ");
|
|
break;
|
|
|
|
case SSTGLOBALPUB:
|
|
fprintf(stdout, " SSTGLOBALPUB: ");
|
|
break;
|
|
|
|
case SSTGLOBALTYPES:
|
|
fprintf(stdout, " SSTGLOBALTYPES: ");
|
|
break;
|
|
|
|
case SSTMPC:
|
|
fprintf(stdout, " SSTMPC: ");
|
|
break;
|
|
|
|
case SSTSEGMAP:
|
|
fprintf(stdout, " SSTSEGMAP: ");
|
|
break;
|
|
|
|
case SSTSEGNAME:
|
|
fprintf(stdout, " SSTSEGNAME: ");
|
|
break;
|
|
|
|
case SSTIMPORTS:
|
|
fprintf(stdout, " SSTIMPORTS: ");
|
|
break;
|
|
|
|
default:
|
|
fprintf(stdout, " UNKNOWN !?!: ");
|
|
break;
|
|
}
|
|
fprintf(stdout, "file offset 0x%lx; size 0x%x\r\n",
|
|
lfaBase+pDnt->lfo, pDnt->cb);
|
|
}
|
|
#endif
|
|
|
|
#if SRCDEBUG
|
|
LOCAL void NEAR DumpSrcLines(DWORD vLines)
|
|
{
|
|
CVSRC cvSrc;
|
|
CVGSN cvGsn;
|
|
CVLINE cvLine;
|
|
DWORD curSrc;
|
|
DWORD curGsn;
|
|
DWORD curLine;
|
|
SBTYPE fileName;
|
|
DWORD i;
|
|
WORD j;
|
|
|
|
|
|
fprintf(stdout, "\r\nList at %lx\r\n\r\n", vLines);
|
|
for (curSrc = vLines; curSrc != 0L; curSrc = cvSrc.vpNext)
|
|
{
|
|
memcpy(&cvSrc, mapva(curSrc, FALSE), sizeof(CVSRC));
|
|
memcpy(fileName, mapva(cvSrc.vpFileName, FALSE), cvSrc.cbName);
|
|
fileName[cvSrc.cbName] = '\0';
|
|
fprintf(stdout, "'%s' --> code segments: %lu; source lines: %lu\r\n", fileName, cvSrc.cSegs, cvSrc.cLines);
|
|
|
|
for (curGsn = cvSrc.vpGsnFirst; curGsn != 0L; curGsn = cvGsn.vpNext)
|
|
{
|
|
memcpy(&cvGsn, mapva(curGsn, FALSE), sizeof(CVGSN));
|
|
fprintf(stdout, " Logical segment %d; source lines: %d; start: %lx; end: %lx\r\n", cvGsn.seg, cvGsn.cLines, cvGsn.raStart, cvGsn.raEnd);
|
|
|
|
for (curLine = cvGsn.vpLineFirst, i = 1L; curLine != 0L; curLine = cvLine.vpNext)
|
|
{
|
|
memcpy(&cvLine, mapva(curLine, FALSE), sizeof(CVLINE));
|
|
for (j = 0; j < cvLine.cPair; j++, i++)
|
|
fprintf(stdout, " %8lu: %u:%lx\r\n", i, cvLine.rgLn[j], cvLine.rgOff[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/****************************************************************
|
|
* *
|
|
* Initialize variables for symbolic debug processing. *
|
|
* Pass 1. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
void NEAR InitDeb1 (void)
|
|
{
|
|
#if ODOS3EXE
|
|
if (vfDSAlloc)
|
|
{
|
|
OutWarn(ER_dbgdsa);
|
|
vfDSAlloc = FALSE;
|
|
}
|
|
#endif
|
|
#if FEXEPACK
|
|
if (fExePack)
|
|
{
|
|
OutWarn(ER_dbgexe);
|
|
fExePack = FALSE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void InitDbRhte ()
|
|
{
|
|
PROPSYMLOOKUP((BYTE *) "\006DEBTYP", ATTRNIL, TRUE);
|
|
rhteDebTyp = vrhte;
|
|
PROPSYMLOOKUP((BYTE *) "\006DEBSYM", ATTRNIL, TRUE);
|
|
rhteDebSym = vrhte;
|
|
PROPSYMLOOKUP((BYTE *) "\006 TYPES", ATTRNIL, TRUE);
|
|
rhte0Types = vrhte;
|
|
PROPSYMLOOKUP((BYTE *) "\010 SYMBOLS", ATTRNIL, TRUE);
|
|
rhte0Symbols = vrhte;
|
|
PROPSYMLOOKUP((BYTE *) "\007$$TYPES", ATTRNIL, TRUE);
|
|
rhteTypes = vrhte;
|
|
PROPSYMLOOKUP((BYTE *) "\011$$SYMBOLS", ATTRNIL, TRUE);
|
|
rhteSymbols = vrhte;
|
|
}
|
|
|
|
|
|
LOCAL void NEAR Pad2Dword(void)
|
|
{
|
|
WORD cb; // Number of bytes to write
|
|
static DWORD dwZero;
|
|
|
|
// Calculate needed padding
|
|
|
|
cb = (WORD)(sizeof(DWORD)-((WORD) FTELL_BSRUNFILE() % sizeof(DWORD)));
|
|
|
|
if (cb != sizeof(DWORD))
|
|
WriteCopy(&dwZero, cb);
|
|
}
|
|
|
|
/*** GetName - get symbol associated with given property cell
|
|
*
|
|
* Purpose:
|
|
* Find the symbol which has given property.
|
|
*
|
|
* Input:
|
|
* - ahte - pointer to property cell
|
|
* - pBuf - pointer to ASCII buffer
|
|
*
|
|
* Output:
|
|
* No explicit value is passed. If symbol is found the it is
|
|
* copied into buffer
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* This functional duplicate of GetPropName, but we want to
|
|
* call both function as near.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void NEAR GetName(AHTEPTR ahte, BYTE *pBuf)
|
|
{
|
|
while(ahte->attr != ATTRNIL)
|
|
ahte = (AHTEPTR ) FETCHSYM(ahte->rhteNext, FALSE);
|
|
FMEMCPY((char FAR *) pBuf, ahte->cch, B2W(ahte->cch[0]) + 1);
|
|
if (B2W(pBuf[0]) < SBLEN)
|
|
pBuf[pBuf[0] + 1] = '\0';
|
|
else
|
|
pBuf[pBuf[0]] = '\0';
|
|
}
|
|
|
|
/*** DebPublic - prepare symbols for debugger
|
|
*
|
|
* Purpose:
|
|
* When the /CODEVIEW option is used then all PUBDEFs and COMDEFs
|
|
* defined in a given object file are linked into one list. This
|
|
* function adds one symbol to the list and updates the combined
|
|
* size of symbols
|
|
*
|
|
* Input:
|
|
* vrprop - virtual pointer to symbol descriptor
|
|
* rt - OMF record type
|
|
*
|
|
* Output:
|
|
* No explicit value is returned.
|
|
* Side effects:
|
|
* - symbol is attached to the module symbol list
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* Symbols are placed on the list in reverse order of their apperance
|
|
* in the object file.
|
|
*
|
|
*************************************************************************/
|
|
|
|
|
|
void DebPublic(RBTYPE vrprop, WORD rt)
|
|
{
|
|
APROPFILEPTR apropFile; // Pointer to file entry
|
|
APROPNAMEPTR apropName; // Real pointer to PUBDEF descriptor
|
|
APROPUNDEFPTR apropUndef; // Real pointer to COMDEF descriptor
|
|
APROPALIASPTR apropAlias; // Real pointer to ALIAS descriptor
|
|
RBTYPE symNext; // Virtual pointer to the next symbol
|
|
|
|
|
|
// Update the appropriate field in the current file symtab entry
|
|
|
|
apropFile = ((APROPFILEPTR ) FETCHSYM(vrpropFile, TRUE));
|
|
symNext = apropFile->af_publics;
|
|
apropFile->af_publics = vrprop;
|
|
apropName = (APROPNAMEPTR) FETCHSYM(vrprop, TRUE);
|
|
if (TYPEOF(rt) == PUBDEF)
|
|
apropName->an_sameMod = symNext;
|
|
else if (TYPEOF(rt) == COMDEF)
|
|
{
|
|
apropUndef = (APROPUNDEFPTR) apropName;
|
|
apropUndef->au_sameMod = symNext;
|
|
}
|
|
else if (TYPEOF(rt) == ALIAS)
|
|
{
|
|
apropAlias = (APROPALIASPTR) apropName;
|
|
apropAlias->al_sameMod = symNext;
|
|
}
|
|
}
|
|
|
|
|
|
LOCAL WORD NEAR IsDebTyp (prop)
|
|
APROPSNPTR prop; /* Pointer to segment record */
|
|
{
|
|
return(prop->as_attr == ATTRLSN && prop->as_rCla == rhteDebTyp);
|
|
}
|
|
|
|
LOCAL WORD NEAR IsDebSym (prop)
|
|
APROPSNPTR prop; /* Pointer to segment record */
|
|
{
|
|
return(prop->as_attr == ATTRLSN && prop->as_rCla == rhteDebSym);
|
|
}
|
|
|
|
|
|
/*** DoDebSrc - store source line information
|
|
*
|
|
* Purpose:
|
|
* Stores source line information from object file.
|
|
*
|
|
* Input:
|
|
* No explicit value is passed.
|
|
*
|
|
* Global variables:
|
|
* - vaCVMac - virtual pointer to the free space in the CV info buffer
|
|
*
|
|
* Output:
|
|
* Returns TRUE if the cv info has been stored in the VM ,or FALSE otherwise.
|
|
* Side effects:
|
|
* - source line information is stored in the VM
|
|
*
|
|
* Exceptions:
|
|
* More than 32Mb of CV information - dispaly error and quit
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
#pragma check_stack(on)
|
|
|
|
WORD DoDebSrc(void)
|
|
{
|
|
WORD cbRecSav; // LINNUM record size
|
|
APROPFILEPTR apropFile; // Current object file property cell
|
|
static SATYPE prevGsn = 0; // GSN of previous LINNUM record
|
|
GSNINFO gsnInfo; // GSN info for this LINNUM
|
|
static CVSRC FAR *pCurSrc; // Pointer to the current file source info
|
|
CVGSN FAR *pCurGsn; // Pointer to the current code segment descriptor
|
|
CVGSN FAR *pcvGsn; // Real pointer to the code segment descriptor
|
|
CVLINE FAR *pCurLine; // Pointer to the current offset/line pair bucket
|
|
RATYPE ra = 0; // Offset
|
|
WORD line; // Line number
|
|
RATYPE raPrev; // Offset of the previous line
|
|
WORD fChangeInSource;
|
|
WORD fComdatSplit;
|
|
DWORD gsnStart; // Start of this gsn
|
|
APROPSNPTR apropSn;
|
|
WORD align;
|
|
WORD threshold;
|
|
#if !defined( M_I386 ) && !defined( _WIN32 )
|
|
SBTYPE nameBuf;
|
|
#endif
|
|
|
|
|
|
cbRecSav = cbRec;
|
|
if (!GetGsnInfo(&gsnInfo))
|
|
return(FALSE);
|
|
|
|
// If LINNUM record is empty, don't do anything
|
|
|
|
if (cbRec == 1)
|
|
return(FALSE);
|
|
|
|
apropFile = (APROPFILEPTR ) FETCHSYM(vrpropFile, TRUE);
|
|
|
|
// If there is a new source file allocate new CVSRC structure
|
|
// and link it to the current object file descriptor
|
|
|
|
fChangeInSource = (WORD) (apropFile->af_Src == 0 || !SbCompare(sbModule, sbLastModule,TRUE));
|
|
|
|
if (fChangeInSource)
|
|
{
|
|
#if CVDEBUG
|
|
sbModule[sbModule[0]+1]='\0';
|
|
sbLastModule[sbLastModule[0]+1]='\0';
|
|
fprintf(stdout, "Change in source file; from '%s' to '%s'\r\n", &sbLastModule[1], &sbModule[1]);
|
|
#endif
|
|
// Search the list of CVSRC structures for this object
|
|
// file and find out if we heave already seen this source file
|
|
|
|
for (pCurSrc = apropFile->af_Src; pCurSrc;)
|
|
{
|
|
#if defined(M_I386) || defined( _WIN32 )
|
|
if (SbCompare(sbModule, pCurSrc->fname, TRUE))
|
|
#else
|
|
FMEMCPY((char FAR *) nameBuf, pCurSrc->fname, pCurSrc->fname[0] + 1);
|
|
if (SbCompare(sbModule, nameBuf, TRUE))
|
|
#endif
|
|
break;
|
|
else
|
|
pCurSrc = pCurSrc->next;
|
|
}
|
|
|
|
if (pCurSrc == NULL)
|
|
{
|
|
// New source file
|
|
|
|
pCurSrc = (CVSRC FAR *) GetMem(sizeof(CVSRC));
|
|
pCurSrc->fname = GetMem(sbModule[0] + 1);
|
|
FMEMCPY(pCurSrc->fname, (char FAR *) sbModule, sbModule[0] + 1);
|
|
|
|
if (apropFile->af_Src == NULL)
|
|
apropFile->af_Src = pCurSrc;
|
|
else
|
|
apropFile->af_SrcLast->next = pCurSrc;
|
|
|
|
apropFile->af_SrcLast = pCurSrc;
|
|
}
|
|
else
|
|
{
|
|
// We have already seen this source file
|
|
}
|
|
memcpy(sbLastModule, sbModule, B2W(sbModule[0]) + 1);
|
|
}
|
|
else
|
|
{
|
|
// Use descriptor set last time we changed source files
|
|
}
|
|
|
|
// Allocate the new CVGSN structure if any of the following is true
|
|
//
|
|
// - this is first batch of source lines
|
|
// - there is a change in GSNs
|
|
// - there is a change in source file
|
|
// - we have source lines for explicitly allocated COMDAT
|
|
// In this last case we assume that the begin portion of a
|
|
// given logical segment (gsn) has been filled with contributions
|
|
// from many object files. Because COMDATs are allocated after all
|
|
// the object files are read, then adding source
|
|
// lines of COMDAT to the source lines of preceeding LEDATA records
|
|
// will mask the contributions from other object files, as the picture
|
|
// below shows:
|
|
//
|
|
// +-------------+<--+
|
|
// | | |
|
|
// | LEDATA from | |
|
|
// | a.obj | |
|
|
// | | |
|
|
// +-------------+ |
|
|
// | | | Without splitting into fake CVGSN
|
|
// | LEDATA from | \ the source line for a.obj will
|
|
// | b.obj | / hide the LEDATA contribution from b.obj
|
|
// | | |
|
|
// +-------------+ |
|
|
// | | |
|
|
// | COMDAT from | |
|
|
// | a.obj | |
|
|
// | | |
|
|
// +-------------+<--+
|
|
// | |
|
|
// | COMDAT from |
|
|
// | b.obj |
|
|
// | |
|
|
// +-------------+
|
|
//
|
|
// This will be unnecessary only if COMDAT from a.obj immediately
|
|
// follows LEDATA from a.obj
|
|
|
|
fComdatSplit = FALSE;
|
|
pCurGsn = pCurSrc->pGsnLast;
|
|
if (pCurGsn)
|
|
{
|
|
// Assume we will be using the current CVGSN
|
|
|
|
if (gsnInfo.fComdat)
|
|
{
|
|
// Source lines from LINSYM - Calculate the threshold
|
|
|
|
apropSn = (APROPSNPTR ) FETCHSYM(mpgsnrprop[gsnInfo.gsn], FALSE);
|
|
if (gsnInfo.comdatAlign)
|
|
align = gsnInfo.comdatAlign;
|
|
else
|
|
align = (WORD) ((apropSn->as_tysn >> 2) & 7);
|
|
|
|
threshold = 1;
|
|
switch (align)
|
|
{
|
|
case ALGNWRD:
|
|
threshold = 2;
|
|
break;
|
|
#if OMF386
|
|
case ALGNDBL:
|
|
threshold = 4;
|
|
break;
|
|
#endif
|
|
case ALGNPAR:
|
|
threshold = 16;
|
|
break;
|
|
|
|
case ALGNPAG:
|
|
threshold = 256;
|
|
break;
|
|
}
|
|
|
|
// Check if we have to split CVGSN for this COMDAT
|
|
|
|
fComdatSplit = !fSameComdat &&
|
|
!(apropSn->as_fExtra & COMDAT_SEG) &&
|
|
(gsnInfo.comdatRa - pCurGsn->raEnd > threshold);
|
|
|
|
}
|
|
else
|
|
{
|
|
// Source lines from LINNUM
|
|
|
|
if (pCurGsn->flags & SPLIT_GSN)
|
|
{
|
|
// The LINNUM record following the LINSYM record that
|
|
// caused CVGSN split - we have to move back on CVGSN
|
|
// list until we find first CVGSN not marked as SPLIT_GSN
|
|
|
|
for (pcvGsn = pCurGsn->prev; pcvGsn != (CVGSN FAR *) pCurSrc;)
|
|
{
|
|
if (!(pcvGsn->flags & SPLIT_GSN))
|
|
break;
|
|
else
|
|
pcvGsn = pcvGsn->prev;
|
|
}
|
|
|
|
if (pcvGsn == (CVGSN FAR *) pCurSrc)
|
|
{
|
|
// There are only SPLIT_GSN on the list - make new CVGSN
|
|
|
|
prevGsn = 0;
|
|
}
|
|
else
|
|
{
|
|
// Use the first non SPLIT_GSN CVGSN as current one
|
|
|
|
pCurGsn = pcvGsn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((prevGsn == 0) ||
|
|
(mpgsnseg[gsnInfo.gsn] != mpgsnseg[prevGsn]) ||
|
|
fChangeInSource ||
|
|
fComdatSplit)
|
|
{
|
|
// Make new CVGSN
|
|
// Remember LOGICAL segment
|
|
|
|
pCurGsn = (CVGSN FAR *) GetMem(sizeof(CVGSN));
|
|
pCurGsn->seg = mpgsnseg[gsnInfo.gsn];
|
|
|
|
// The start and end offset will be derived from line number/offset pairs
|
|
|
|
pCurGsn->raStart = 0xffffffff;
|
|
if (fComdatSplit)
|
|
pCurGsn->flags |= SPLIT_GSN;
|
|
if (pCurSrc->pGsnFirst == NULL)
|
|
{
|
|
pCurSrc->pGsnFirst = pCurGsn;
|
|
pCurGsn->prev = (CVGSN FAR *) pCurSrc;
|
|
}
|
|
else
|
|
{
|
|
pCurSrc->pGsnLast->next = pCurGsn;
|
|
pCurGsn->prev = pCurSrc->pGsnLast;
|
|
}
|
|
pCurSrc->pGsnLast = pCurGsn;
|
|
pCurSrc->cSegs++;
|
|
#if CVDEBUG
|
|
sbModule[sbModule[0]+1] = '\0';
|
|
fprintf(stdout, "New code segment in '%s'; prevGsn = %x; newGsn = %x %s\r\n", &sbModule[1], prevGsn, gsnInfo.gsn, fComdatSplit ? "COMDAT split" : "");
|
|
#endif
|
|
prevGsn = gsnInfo.gsn;
|
|
}
|
|
|
|
// Get the offset/line bucket
|
|
|
|
if (pCurGsn->pLineFirst == NULL)
|
|
{
|
|
pCurLine = (CVLINE FAR *) GetMem(sizeof(CVLINE));
|
|
pCurGsn->pLineFirst = pCurLine;
|
|
pCurGsn->pLineLast = pCurLine;
|
|
}
|
|
else
|
|
pCurLine = pCurGsn->pLineLast;
|
|
|
|
// Fill in offset/line bucket
|
|
|
|
if (gsnInfo.fComdat)
|
|
gsnStart = gsnInfo.comdatRa;
|
|
else
|
|
gsnStart = mpgsndra[gsnInfo.gsn] - mpsegraFirst[pCurGsn->seg];
|
|
|
|
raPrev = 0xffff;
|
|
while (cbRec > 1) // While not at checksum
|
|
{
|
|
GetLineOff(&line, &ra);
|
|
|
|
ra += gsnStart;
|
|
|
|
// We have to eliminate line pairs with same ra (for MASM 5.1)
|
|
|
|
if(ra == raPrev)
|
|
continue;
|
|
raPrev = ra;
|
|
|
|
// Remember the smallest LOGICAL offset for source line
|
|
|
|
if (ra < pCurGsn->raStart)
|
|
pCurGsn->raStart = ra;
|
|
|
|
if (line != 0)
|
|
{
|
|
if (pCurLine->cPair >= CVLINEMAX)
|
|
{
|
|
pCurLine->next = (CVLINE FAR *) GetMem(sizeof(CVLINE));
|
|
pCurLine = pCurLine->next;
|
|
pCurGsn->pLineLast = pCurLine;
|
|
}
|
|
|
|
pCurLine->rgOff[pCurLine->cPair] = ra;
|
|
pCurLine->rgLn[pCurLine->cPair] = line;
|
|
pCurLine->cPair++;
|
|
pCurSrc->cLines++;
|
|
pCurGsn->cLines++;
|
|
}
|
|
}
|
|
|
|
// Remember last line LOGICAL offset
|
|
|
|
pCurGsn->raEnd = ra;
|
|
#if CVDEBUG
|
|
fprintf(stdout, "New source lines for the 0x%x logical code segment; lines %d\r\n start offset %x:%lx end offset %x:%lx; physical address of logical segment %x:%lx\r\n",
|
|
pCurGsn->seg, pCurGsn->cLines, pCurGsn->seg, pCurGsn->raStart, pCurGsn->seg, pCurGsn->raEnd, mpsegsa[pCurGsn->seg], mpsegraFirst[pCurGsn->seg]);
|
|
#endif
|
|
|
|
// If /LINENUMBERS and list file open, back up
|
|
|
|
if (vfLineNos && fLstFileOpen)
|
|
{
|
|
#if ALIGN_REC
|
|
pbRec += (cbRec - cbRecSav);
|
|
#else
|
|
fseek(bsInput, (long)cbRec - cbRecSav, 1);
|
|
#endif
|
|
cbRec = cbRecSav;
|
|
}
|
|
return(TRUE);
|
|
}
|
|
|
|
#pragma check_stack(off)
|
|
|
|
/*** CheckTables - check space in table used by OutSrcModule
|
|
*
|
|
* Purpose:
|
|
* While building the new source module subsection linker needs
|
|
* to store a lot of information about given source file. Since
|
|
* we can't predict how many source files were compiled to obtain
|
|
* this object module or to how many logical segments this object
|
|
* module contributes code we have to dynamically resize appropriate
|
|
* tables.
|
|
*
|
|
* Input:
|
|
* cFiles - number of source files compiled to produce
|
|
* this object module
|
|
* cSegs - number of logical segments this object module
|
|
* contributes to.
|
|
*
|
|
* Output:
|
|
* No explicit value is returned. As a side effect the following
|
|
* tables are allocated or reallocated:
|
|
*
|
|
* fileBase - table of offsets to source file info
|
|
* raSeg - table of physical starting and ending offsets
|
|
* of the contribution to the logical segments
|
|
* segNo - table of physical segment indicies
|
|
*
|
|
* Exceptions:
|
|
* Memory allocation problems - fatal error and exit.
|
|
*
|
|
* Notes:
|
|
* When we reallocated the tables we don't have to copy
|
|
* their old content, because it was used in the previous
|
|
* object module.
|
|
*
|
|
*************************************************************************/
|
|
|
|
LOCAL void NEAR CheckTables(WORD cFiles, WORD cSegs)
|
|
{
|
|
WORD cCur;
|
|
|
|
cCur = (WORD) (cFiles < cSegs ? cSegs : cFiles);
|
|
if (cCur > cMac)
|
|
{
|
|
// We have to reallocate tables or allocate for the first time
|
|
|
|
if (fileBase)
|
|
FFREE(fileBase);
|
|
if (raSeg)
|
|
FFREE(raSeg);
|
|
if (segNo)
|
|
FFREE(segNo);
|
|
|
|
fileBase = (DWORD FAR *) GetMem(cCur*sizeof(DWORD));
|
|
raSeg = (RAPAIR FAR *) GetMem(cCur*sizeof(RAPAIR));
|
|
segNo = (WORD FAR *) GetMem(cCur*sizeof(WORD));
|
|
cMac = cCur;
|
|
}
|
|
}
|
|
|
|
|
|
/*** OutSrcModule - write CV source module
|
|
*
|
|
* Purpose:
|
|
* Create the CV 4.00 format source module descrbing the source line
|
|
* number to addressing mapping information for one object file
|
|
*
|
|
* Input:
|
|
* - pSrcLines - the list of source file information blocks
|
|
*
|
|
* Output:
|
|
* Total size of the subsection in bytes.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
|
|
LOCAL DWORD NEAR OutSrcModule(CVSRC FAR *pSrcLines)
|
|
{
|
|
CVSRC FAR *pCurSrc; // Pointer to current source file
|
|
CVGSN FAR *pCurGsn; // Pointer to current code segment
|
|
CVLINE FAR *pLine; // Pointer to source line bucket
|
|
WORD cFiles; // Number of source files
|
|
WORD cSegs; // Number of code segments
|
|
WORD xFile;
|
|
WORD xSeg;
|
|
DWORD sizeTotal; // Size of source subsection
|
|
DWORD srcLnBase;
|
|
WORD counts[2];
|
|
CVLINE FAR *pTmp;
|
|
|
|
|
|
#if SRCDEBUG
|
|
DumpSrcLines(vaLines);
|
|
#endif
|
|
|
|
// Count total number of source files, total number of code segments
|
|
|
|
for (pCurSrc = pSrcLines, cFiles = 0, cSegs = 0; pCurSrc; cFiles++, pCurSrc = pCurSrc->next)
|
|
cSegs += pCurSrc->cSegs;
|
|
|
|
CheckTables(cFiles, cSegs);
|
|
sizeTotal = (DWORD) (2*sizeof(WORD) + cFiles*sizeof(DWORD) +
|
|
cSegs*(sizeof(raSeg[0]) + sizeof(WORD)));
|
|
sizeTotal = Round2Dword(sizeTotal);
|
|
|
|
// Make second pass througth the source files and fill in
|
|
// source module header
|
|
|
|
for (pCurSrc = pSrcLines, xFile = 0, xSeg = 0; xFile < cFiles && pCurSrc; xFile++, pCurSrc = pCurSrc->next)
|
|
{
|
|
fileBase[xFile] = sizeTotal;
|
|
|
|
// Add the size of this source file information:
|
|
//
|
|
// Source file header:
|
|
//
|
|
// +------+------+------------+--------------+------+-------------+
|
|
// | WORD | WORD | cSeg*DWORD | 2*cSeg*DWORD | BYTE | cbName*BYTE |
|
|
// +------+------+------------+--------------+------+-------------+
|
|
//
|
|
|
|
sizeTotal += (2*sizeof(WORD) +
|
|
pCurSrc->cSegs*(sizeof(DWORD) + sizeof(raSeg[0])) +
|
|
sizeof(BYTE) + pCurSrc->fname[0]);
|
|
|
|
// Pad to DWORD boundary
|
|
|
|
sizeTotal = Round2Dword(sizeTotal);
|
|
|
|
// Walk code segment list
|
|
|
|
for (pCurGsn = pCurSrc->pGsnFirst; pCurGsn; pCurGsn = pCurGsn->next, xSeg++)
|
|
{
|
|
raSeg[xSeg].raStart = pCurGsn->raStart;
|
|
raSeg[xSeg].raEnd = pCurGsn->raEnd;
|
|
segNo[xSeg] = pCurGsn->seg;
|
|
|
|
// Add size of the offset/line table
|
|
//
|
|
// +------+------+-------------+------------+
|
|
// | WORD | WORD | cLine*DWORD | cLine*WORD |
|
|
// +------+------+-------------+------------+
|
|
|
|
sizeTotal += (2*sizeof(WORD) +
|
|
pCurGsn->cLines*(sizeof(DWORD) + sizeof(WORD)));
|
|
|
|
// Pad to DWORD boundary
|
|
|
|
sizeTotal = Round2Dword(sizeTotal);
|
|
}
|
|
}
|
|
|
|
// Write source module header
|
|
|
|
counts[0] = cFiles;
|
|
counts[1] = cSegs;
|
|
WriteCopy(counts, sizeof(counts));
|
|
WriteCopy(fileBase, cFiles*sizeof(DWORD));
|
|
WriteCopy(raSeg, cSegs*sizeof(RAPAIR));
|
|
WriteCopy(segNo, cSegs*sizeof(WORD));
|
|
|
|
// Pad to DWORD boundary
|
|
|
|
Pad2Dword();
|
|
|
|
// Make third pass througth the source files and fill in
|
|
// the source file header and write offset/line pairs
|
|
|
|
for (pCurSrc = pSrcLines, srcLnBase = fileBase[0]; pCurSrc != NULL;
|
|
pCurSrc = pCurSrc->next, xFile++)
|
|
{
|
|
// Add the size of source file header:
|
|
//
|
|
// +------+------+------------+--------------+------+-------------+
|
|
// | WORD | WORD | cSeg*DWORD | 2*cSeg*DWORD | BYTE | cbName*BYTE |
|
|
// +------+------+------------+--------------+------+-------------+
|
|
//
|
|
|
|
srcLnBase += (2*sizeof(WORD) +
|
|
pCurSrc->cSegs*(sizeof(DWORD) + sizeof(raSeg[0])) +
|
|
sizeof(BYTE) + pCurSrc->fname[0]);
|
|
|
|
// Round to DWORD boundary
|
|
|
|
srcLnBase = Round2Dword(srcLnBase);
|
|
|
|
// Walk code segment list and store base offsets for source
|
|
// line offset/line pairs and record start/stop offsets of
|
|
// code segments
|
|
|
|
for (xSeg = 0, pCurGsn = pCurSrc->pGsnFirst; pCurGsn != NULL;
|
|
pCurGsn = pCurGsn->next, xSeg++)
|
|
{
|
|
fileBase[xSeg] = srcLnBase;
|
|
srcLnBase += (2*sizeof(WORD) +
|
|
pCurGsn->cLines*(sizeof(DWORD) + sizeof(WORD)));
|
|
|
|
// Round to DWORD boundary
|
|
|
|
srcLnBase = Round2Dword(srcLnBase);
|
|
raSeg[xSeg].raStart = pCurGsn->raStart;
|
|
raSeg[xSeg].raEnd = pCurGsn->raEnd;
|
|
}
|
|
|
|
// Write source file header
|
|
|
|
counts[0] = (WORD) pCurSrc->cSegs;
|
|
counts[1] = 0;
|
|
WriteCopy(counts, sizeof(counts));
|
|
WriteCopy(fileBase, pCurSrc->cSegs*sizeof(DWORD));
|
|
WriteCopy(raSeg, pCurSrc->cSegs*sizeof(RAPAIR));
|
|
WriteCopy(pCurSrc->fname, pCurSrc->fname[0] + 1);
|
|
|
|
// Pad to DWORD boundary
|
|
|
|
Pad2Dword();
|
|
|
|
// Walk code segment list and write offsets/line pairs
|
|
|
|
for (pCurGsn = pCurSrc->pGsnFirst; pCurGsn != NULL; pCurGsn = pCurGsn->next)
|
|
{
|
|
// Write segment index and number of offset/line pairs
|
|
|
|
counts[0] = pCurGsn->seg;
|
|
counts[1] = pCurGsn->cLines;
|
|
WriteCopy(counts, sizeof(counts));
|
|
|
|
// Write offsets
|
|
|
|
for (pLine = pCurGsn->pLineFirst; pLine != NULL; pLine = pLine->next)
|
|
WriteCopy(&(pLine->rgOff), pLine->cPair * sizeof(DWORD));
|
|
|
|
// Write line numbers
|
|
|
|
for (pLine = pCurGsn->pLineFirst; pLine != NULL; pLine = pLine->next)
|
|
WriteCopy(&(pLine->rgLn), pLine->cPair * sizeof(WORD));
|
|
|
|
// Pad to DWORD boundary
|
|
|
|
Pad2Dword();
|
|
|
|
// Free memory
|
|
|
|
for (pLine = pCurGsn->pLineFirst; pLine != NULL;)
|
|
{
|
|
pTmp = pLine->next;
|
|
FFREE(pLine);
|
|
pLine = pTmp;
|
|
}
|
|
}
|
|
}
|
|
return(sizeTotal);
|
|
}
|
|
|
|
|
|
/*** SaveCode - save code segment information in MODULES entry
|
|
*
|
|
* Purpose:
|
|
* For every module (.OBJ file) save the information about code segments
|
|
* this module contributes to. COMDATs are threated as contibutions to
|
|
* the logical segment, so each gets its entry in the CVCODE list attached
|
|
* to the given .OBJ file (module).
|
|
*
|
|
* Input:
|
|
* gsn - global segment index of logical segment to which this module
|
|
* contributes
|
|
* cb - size (in bytes) of contribution
|
|
* raInit - offset of the contribution inside the logical segment; this
|
|
* nonzero only for COMDATs.
|
|
*
|
|
* Output:
|
|
* No explicit value is returned. The list of CVCODE attached to the
|
|
* .OBJ file (module) is updated.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
void SaveCode(SNTYPE gsn, DWORD cb, DWORD raInit)
|
|
{
|
|
CVCODE FAR *pSegCur; // Pointer to the current code segment
|
|
APROPFILEPTR apropFile;
|
|
|
|
apropFile = (APROPFILEPTR) vrpropFile;
|
|
|
|
// Save code segment if module has CV info
|
|
|
|
pSegCur = (CVCODE FAR *) GetMem(sizeof(CVCODE));
|
|
|
|
// Store LOGICAL segment, offset and size of the contribution
|
|
|
|
pSegCur->seg = mpgsnseg[gsn];
|
|
if (raInit != 0xffffffffL)
|
|
pSegCur->ra = raInit;
|
|
else
|
|
pSegCur->ra = mpgsndra[gsn] - mpsegraFirst[mpgsnseg[gsn]];
|
|
pSegCur->cb = cb;
|
|
|
|
// Add to the CV code list
|
|
|
|
if (apropFile->af_Code == NULL)
|
|
apropFile->af_Code = pSegCur;
|
|
else
|
|
apropFile->af_CodeLast->next = pSegCur;
|
|
apropFile->af_CodeLast = pSegCur;
|
|
apropFile->af_cCodeSeg++;
|
|
}
|
|
|
|
/****************************************************************
|
|
* *
|
|
* DO SYMBOLIC DEBUG STUFF FOR MODULE JUST PROCESSED. *
|
|
* Pass 2. *
|
|
* *
|
|
****************************************************************/
|
|
|
|
void DebMd2(void)
|
|
{
|
|
APROPFILEPTR apropFile;
|
|
|
|
sbLastModule[0] = 0; /* Force recognition of new THEADR */
|
|
apropFile = (APROPFILEPTR) vrpropFile;
|
|
if (apropFile->af_cvInfo)
|
|
++segDebLast;
|
|
}
|
|
|
|
/*** PutDnt - store subsection directory entry in the table
|
|
*
|
|
* Purpose:
|
|
* Copy current subsection directory to the DNT table. If no more
|
|
* room in the table reallocate table doubling its size.
|
|
*
|
|
* Input:
|
|
* - pDnt - pointer to the current directory entry
|
|
*
|
|
* Output:
|
|
* No explicit value is returned.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
LOCAL void NEAR PutDnt(DNT *pDnt)
|
|
{
|
|
WORD newSize;
|
|
if (dntMac >= dntMax)
|
|
{
|
|
if(dntMax)
|
|
{
|
|
newSize = dntMax << 1;
|
|
#if defined(M_I386) || defined( _WIN32 )
|
|
{
|
|
BYTE *pb = REALLOC(rgDnt, newSize * sizeof(DNT));
|
|
if (!pb)
|
|
Fatal(ER_memovf);
|
|
rgDnt = (DNT *)pb;
|
|
}
|
|
#else
|
|
rgDnt = (DNT FAR *) _frealloc(rgDnt, newSize * sizeof(DNT));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
newSize = DNT_START;
|
|
rgDnt = (DNT*) GetMem ( newSize * sizeof(DNT) );
|
|
}
|
|
if (rgDnt == NULL)
|
|
Fatal(ER_memovf);
|
|
dntMax = newSize;
|
|
}
|
|
|
|
|
|
|
|
rgDnt[dntMac] = *pDnt;
|
|
dntMac++;
|
|
#if CVDEBUG
|
|
DumpDNT(pDnt);
|
|
#endif
|
|
}
|
|
|
|
#pragma check_stack(on)
|
|
|
|
/*** OutModule - write out module subsection
|
|
*
|
|
* Purpose:
|
|
* Write into the executable file the module subsections for all
|
|
* object files compiled with CV information. Only CV 4.0 format.
|
|
*
|
|
* Input:
|
|
* - apropFile - pointer to the current object file descriptor
|
|
*
|
|
* Output:
|
|
* No explicit value is retuned.
|
|
* Side effects:
|
|
* - module subsections in executable file
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
LOCAL DWORD NEAR OutModule(APROPFILEPTR apropFile)
|
|
{
|
|
SBTYPE sbName;
|
|
SSTMOD4 module;
|
|
CVCODE FAR *pSegCur;
|
|
CODEINFO codeOnt;
|
|
WORD cOnt;
|
|
|
|
|
|
module.ovlNo = (WORD) apropFile->af_iov;
|
|
module.iLib = (WORD) (apropFile->af_ifh + 1);
|
|
module.cSeg = apropFile->af_cCodeSeg;
|
|
module.style[0] = 'C';
|
|
module.style[1] = 'V';
|
|
|
|
// Get file name or library module name
|
|
|
|
if (apropFile->af_ifh != FHNIL && apropFile->af_rMod != RHTENIL)
|
|
GetName((AHTEPTR) apropFile->af_rMod, sbName);
|
|
else
|
|
GetName((AHTEPTR) apropFile, sbName);
|
|
|
|
#if CVDEBUG
|
|
sbName[sbName[0]+1] = '\0';
|
|
fprintf(stdout, "\r\nCV info for %s\r\n", &sbName[1]);
|
|
#endif
|
|
|
|
// Write sstModule header followed by the list of code contributions
|
|
|
|
WriteCopy(&module, sizeof(SSTMOD4));
|
|
pSegCur = apropFile->af_Code;
|
|
codeOnt.pad = 0;
|
|
for (cOnt = 0; cOnt < module.cSeg && pSegCur; cOnt++, pSegCur = pSegCur->next)
|
|
{
|
|
codeOnt.seg = pSegCur->seg;
|
|
codeOnt.off = pSegCur->ra;
|
|
codeOnt.cbOnt = pSegCur->cb;
|
|
WriteCopy(&codeOnt, sizeof(CODEINFO));
|
|
#if CVDEBUG
|
|
fprintf(stdout, " Logical segment %d; offset 0x%lx; size 0x%lx\r\n",
|
|
codeOnt.seg, codeOnt.off, codeOnt.cbOnt);
|
|
#endif
|
|
}
|
|
|
|
// Write object file name
|
|
|
|
WriteCopy(sbName, B2W(sbName[0]) + 1);
|
|
return(sizeof(SSTMOD4) + B2W(sbName[0]) + 1 + module.cSeg * sizeof(CODEINFO));
|
|
}
|
|
|
|
|
|
/*** OutPublics - write sstPublics subsection
|
|
*
|
|
* Purpose:
|
|
* Write sstPublics subsection of the CV information. The subsection
|
|
* conforms to the new CV 4.0 format.
|
|
*
|
|
* Input:
|
|
* - firstPub - virtual pointer to the list of public symbols defined
|
|
* in a given object module
|
|
*
|
|
* Output:
|
|
* Total size of the subsection in bytes.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
LOCAL DWORD NEAR OutPublics(RBTYPE firstPub)
|
|
{
|
|
PUB16 pub16; // CV public descriptor - 16-bit
|
|
PUB32 pub32; // CV public descriptor - 32-bit
|
|
APROPNAMEPTR apropPub; // Real pointer to public descriptor
|
|
APROPSNPTR apropSn; // Real pointer to segment descriptor
|
|
RBTYPE curPub; // Virtual pointer to the current public symbol
|
|
WORD f32Bit; // TRUE if public defined in 32-bit segment
|
|
DWORD sizeTotal; // Total size of subsection
|
|
SNTYPE seg; // Symbol base
|
|
RATYPE ra; // Symbol offset
|
|
WORD CVtype; // CV info type index
|
|
SBTYPE sbName; // Public symbol
|
|
char *pPub;
|
|
WORD len;
|
|
|
|
|
|
// Initialize
|
|
|
|
curPub = firstPub;
|
|
pub16.idx = S_PUB16;
|
|
pub32.idx = S_PUB32;
|
|
sizeTotal = 1L;
|
|
WriteCopy(&sizeTotal, sizeof(DWORD));// sstPublicSym signature
|
|
sizeTotal = sizeof(DWORD);
|
|
while (curPub != 0L)
|
|
{
|
|
f32Bit = FALSE;
|
|
apropPub = (APROPNAMEPTR) FETCHSYM(curPub, FALSE);
|
|
curPub = apropPub->an_sameMod;
|
|
if (apropPub->an_attr == ATTRALIAS)
|
|
apropPub = (APROPNAMEPTR) FETCHSYM(((APROPALIASPTR) apropPub)->al_sym, FALSE);
|
|
|
|
if (apropPub->an_attr != ATTRPNM)
|
|
continue;
|
|
|
|
ra = apropPub->an_ra;
|
|
if (apropPub->an_gsn) // If not absolute symbol
|
|
{
|
|
seg = mpgsnseg[apropPub->an_gsn];
|
|
// If this is a .com program and the segment is the one
|
|
// moved by 0x100, adjust accordingly the SegMap entry
|
|
if(seg == segAdjCom)
|
|
{
|
|
#if FALSE
|
|
GetName((AHTEPTR) apropPub, sbName);
|
|
sbName[sbName[0]+1] = '\0';
|
|
fprintf(stdout, "\r\nCorrecting public %s : %lx -> %lx", sbName+1, ra, ra+0x100);
|
|
fflush(stdout);
|
|
#endif
|
|
ra += 0x100;
|
|
}
|
|
|
|
CVtype = 0; // Should be this apropPub->an_CVtype
|
|
// but cvpack can't handle it.
|
|
#if O68K
|
|
if (iMacType == MAC_NONE)
|
|
#endif
|
|
ra -= mpsegraFirst[seg];
|
|
#if CVDEBUG
|
|
GetName((AHTEPTR) apropPub, sbName);
|
|
sbName[sbName[0]+1] = '\0';
|
|
fprintf(stdout, "'%s' --> logical address %2x:%lx; physical address %2x:%lx\r\n",
|
|
&sbName[1], seg, ra, mpsegsa[seg], apropPub->an_ra);
|
|
#endif
|
|
apropSn = (APROPSNPTR) FETCHSYM(mpgsnrprop[apropPub->an_gsn], FALSE);
|
|
#if EXE386
|
|
f32Bit = TRUE;
|
|
#else
|
|
f32Bit = (WORD) Is32BIT(apropSn->as_flags);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
seg = 0; // Else no base
|
|
CVtype = T_ABS; // CV absolute symbol type
|
|
f32Bit = (WORD) (ra > LXIVK);
|
|
}
|
|
|
|
GetName((AHTEPTR) apropPub, sbName);
|
|
|
|
if (f32Bit)
|
|
{
|
|
pub32.len = (WORD) (sizeof(PUB32) + B2W(sbName[0]) + 1 - sizeof(WORD));
|
|
pub32.off = ra;
|
|
pub32.seg = seg;
|
|
pub32.type = CVtype;
|
|
pPub = (char *) &pub32;
|
|
len = sizeof(PUB32);
|
|
}
|
|
else
|
|
{
|
|
pub16.len = (WORD) (sizeof(PUB16) + B2W(sbName[0]) + 1 - sizeof(WORD));
|
|
pub16.off = (WORD) ra;
|
|
pub16.seg = seg;
|
|
pub16.type = CVtype;
|
|
pPub = (char *) &pub16;
|
|
len = sizeof(PUB16);
|
|
}
|
|
WriteCopy(pPub, len);
|
|
|
|
// Output length-prefixed name
|
|
|
|
WriteCopy(sbName, sbName[0] + 1);
|
|
sizeTotal += (len + B2W(sbName[0]) + 1);
|
|
}
|
|
return(sizeTotal);
|
|
}
|
|
|
|
/*** OutSegMap - write segment map
|
|
*
|
|
* Purpose:
|
|
* This subsection was introduced in CV 4.0. This subsection
|
|
* maps the logical segments to physical segments. It also gives
|
|
* the names and sizes of each logical segment.
|
|
*
|
|
* Input:
|
|
* No explicit value is passed.
|
|
* Global variables:
|
|
* - mpsegsa - table mapping logical segment number to its physical
|
|
* segment number or address
|
|
* - mpsaflags - table mapping physicla segment index to its flags
|
|
* - mpseggsn - table mapping the logical segment index to its global
|
|
* segment index
|
|
* - mpgsnprop - table mapping global segment index to its symbol table
|
|
* descriptor
|
|
* - mpggrgsn - table mapping global group index to global segment index
|
|
* - mpggrrhte - table mapping global group index to group name
|
|
*
|
|
* Output:
|
|
* Function returns the size of segment map.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
LOCAL DWORD NEAR OutSegMap(void)
|
|
{
|
|
SEGTYPE seg; // Logical segment index
|
|
APROPSNPTR apropSn; // Real pointer to logical segment descriptor
|
|
SATYPE sa; // Physical segment index
|
|
WORD iName; // Index to free space in segment name table
|
|
DWORD sizeTotal; // Total size of the subsection
|
|
SEGINFO segInfo; // CV segment descriptor
|
|
SBTYPE segName; // Segment name
|
|
AHTEPTR ahte; // Real pointer to symbol table hash table
|
|
RBTYPE vpClass; // Virtual pointer to class descriptor
|
|
GRTYPE ggr; // Global group index
|
|
SATYPE saDGroup; // DGroup's sa
|
|
WORD counts[2];
|
|
|
|
iName = 0;
|
|
counts[0] = (WORD) (segLast + ggrMac - 1);
|
|
counts[1] = (WORD) segLast;
|
|
WriteCopy(counts, sizeof(counts));
|
|
sizeTotal = sizeof(counts);
|
|
|
|
saDGroup = mpsegsa[mpgsnseg[mpggrgsn[ggrDGroup]]];
|
|
|
|
// Write all logical segments
|
|
|
|
for (seg = 1; seg <= segLast; ++seg)// For all logical segments
|
|
{
|
|
memset(&segInfo, 0, sizeof(SEGINFO));
|
|
|
|
if (fNewExe)
|
|
segInfo.flags.fSel = TRUE;
|
|
|
|
sa = mpsegsa[seg];
|
|
|
|
if (fNewExe)
|
|
{
|
|
#if EXE386
|
|
segInfo.flags.f32Bit = TRUE;
|
|
#else
|
|
segInfo.flags.f32Bit = (WORD) (Is32BIT(mpsaflags[sa]));
|
|
#endif
|
|
|
|
if (IsDataFlg(mpsaflags[sa]))
|
|
{
|
|
segInfo.flags.fRead = TRUE;
|
|
#if EXE386
|
|
if (IsWRITEABLE(mpsaflags[sa]))
|
|
#else
|
|
if (!(mpsaflags[sa] & NSEXRD))
|
|
#endif
|
|
segInfo.flags.fWrite = TRUE;
|
|
}
|
|
else
|
|
{
|
|
segInfo.flags.fExecute = TRUE;
|
|
|
|
#if EXE386
|
|
if (IsREADABLE(mpsaflags[sa]))
|
|
#else
|
|
if (!(mpsaflags[sa] & NSEXRD))
|
|
#endif
|
|
segInfo.flags.fRead = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (mpsegFlags[seg] & FCODE)
|
|
{
|
|
segInfo.flags.fRead = TRUE;
|
|
segInfo.flags.fExecute = TRUE;
|
|
}
|
|
else
|
|
{
|
|
segInfo.flags.fRead = TRUE;
|
|
segInfo.flags.fWrite = TRUE;
|
|
}
|
|
}
|
|
|
|
// Look up segment definition
|
|
|
|
apropSn = (APROPSNPTR) FETCHSYM(mpgsnrprop[mpseggsn[seg]], FALSE);
|
|
vpClass = apropSn->as_rCla;
|
|
#if OVERLAYS
|
|
if (!fNewExe)
|
|
segInfo.ovlNbr = apropSn->as_iov;
|
|
#endif
|
|
// if segment doesn't belong to any group it's ggr is 0
|
|
if (apropSn->as_ggr != GRNIL)
|
|
segInfo.ggr = (WORD) (apropSn->as_ggr + segLast - 1);
|
|
|
|
// If segment is a DGROUP member, write DGROUP normalized address
|
|
|
|
if(apropSn->as_ggr == ggrDGroup)
|
|
{
|
|
segInfo.sa = saDGroup;
|
|
segInfo.phyOff = mpsegraFirst[seg] + ((sa - saDGroup) << 4);
|
|
}
|
|
else
|
|
{
|
|
segInfo.sa = sa;
|
|
segInfo.phyOff = mpsegraFirst[seg];
|
|
}
|
|
// If this is a .com program and the segment is the one moved by 0x100
|
|
// write all the publics at the original addresses, because the offset
|
|
// adjustment will be made in the SegMap table
|
|
if(seg == segAdjCom)
|
|
{
|
|
segInfo.phyOff -= 0x100;
|
|
}
|
|
segInfo.cbSeg = apropSn->as_cbMx;
|
|
GetName((AHTEPTR) apropSn, segName);
|
|
if (segName[0] != '\0')
|
|
{
|
|
segInfo.isegName = iName;
|
|
iName += (WORD) (B2W(segName[0]) + 1);
|
|
}
|
|
else
|
|
segInfo.isegName = 0xffff;
|
|
ahte = (AHTEPTR) FETCHSYM(vpClass, FALSE);
|
|
if (ahte->cch[0] != 0)
|
|
{
|
|
segInfo.iclassName = iName;
|
|
iName += (WORD) (B2W(ahte->cch[0]) + 1);
|
|
}
|
|
else
|
|
segInfo.iclassName = 0xffff;
|
|
WriteCopy(&segInfo, sizeof(SEGINFO));
|
|
sizeTotal += sizeof(SEGINFO);
|
|
}
|
|
|
|
// Write all groups
|
|
|
|
for (ggr = 1; ggr < ggrMac; ggr++)
|
|
{
|
|
memset(&segInfo, 0, sizeof(SEGINFO));
|
|
|
|
segInfo.flags.fGroup = TRUE;
|
|
|
|
if (fNewExe)
|
|
segInfo.flags.fSel = TRUE;
|
|
|
|
segInfo.sa = mpsegsa[mpgsnseg[mpggrgsn[ggr]]];
|
|
|
|
if (fNewExe)
|
|
segInfo.cbSeg = mpsacb[segInfo.sa];
|
|
else
|
|
{
|
|
segInfo.cbSeg = 0L;
|
|
if (mpggrgsn[ggr] != SNNIL)
|
|
{
|
|
// If group has members
|
|
|
|
for (seg = 1; seg <= segLast; seg++)
|
|
{
|
|
apropSn = (APROPSNPTR) FETCHSYM(mpgsnrprop[mpseggsn[seg]], FALSE);
|
|
if (apropSn->as_ggr == ggr)
|
|
{
|
|
segInfo.cbSeg += apropSn->as_cbMx;
|
|
#if OVERLAYS
|
|
segInfo.ovlNbr = apropSn->as_iov;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
segInfo.isegName = iName;
|
|
ahte = (AHTEPTR) FETCHSYM(mpggrrhte[ggr], FALSE);
|
|
iName += (WORD) (B2W(ahte->cch[0]) + 1);
|
|
segInfo.iclassName = 0xffff;
|
|
WriteCopy(&segInfo, sizeof(SEGINFO));
|
|
sizeTotal += sizeof(SEGINFO);
|
|
}
|
|
return(sizeTotal);
|
|
}
|
|
|
|
/*** OutSegNames - write segment name table
|
|
*
|
|
* Purpose:
|
|
* This subsection was introduced in CV 4.0.
|
|
* The segment name subsection contains all of the logical segment,
|
|
* class and group names. Each name is a zero terminated ASCII string.
|
|
*
|
|
* Input:
|
|
* No explicit value is passed.
|
|
* Global variables:
|
|
* - mpseggsn - table mapping the logical segment index to its global
|
|
* segment index
|
|
* - mpgsnprop - table mapping global segment index to its symbol table
|
|
* descriptor
|
|
* - mpggrrhte - table mapping global group index to group name
|
|
*
|
|
* Output:
|
|
* Function returns the size of segment name table.
|
|
*
|
|
* Exceptions:
|
|
* None.
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
LOCAL DWORD NEAR OutSegNames(void)
|
|
{
|
|
SEGTYPE seg; // Logical segment index
|
|
APROPSNPTR apropSn; // Real pointer to logical segment descriptor
|
|
DWORD sizeTotal; // Size of the segment name table
|
|
SBTYPE name; // A name
|
|
RBTYPE vpClass; // Virtual pointer to class descriptor
|
|
GRTYPE ggr; // Global group index
|
|
|
|
|
|
|
|
sizeTotal = 0L;
|
|
|
|
// Write names of all logical segments
|
|
|
|
for (seg = 1; seg <= segLast; ++seg)
|
|
{
|
|
// Look up segment definition
|
|
|
|
apropSn = (APROPSNPTR ) FETCHSYM(mpgsnrprop[mpseggsn[seg]], FALSE);
|
|
vpClass = apropSn->as_rCla;
|
|
GetName((AHTEPTR) apropSn, name);
|
|
WriteCopy(&name[1], B2W(name[0]) + 1);
|
|
sizeTotal += (B2W(name[0]) + 1);
|
|
GetName((AHTEPTR ) FETCHSYM(vpClass, FALSE), name);
|
|
WriteCopy(&name[1], B2W(name[0]) + 1);
|
|
sizeTotal += (B2W(name[0]) + 1);
|
|
}
|
|
|
|
// Write names of all groups
|
|
|
|
for (ggr = 1; ggr < ggrMac; ggr++)
|
|
{
|
|
GetName((AHTEPTR ) FETCHSYM(mpggrrhte[ggr], FALSE), name);
|
|
WriteCopy(&name[1], B2W(name[0]) + 1);
|
|
sizeTotal += (B2W(name[0]) + 1);
|
|
}
|
|
return(sizeTotal);
|
|
}
|
|
|
|
/*** OutSst - write subsections
|
|
*
|
|
* Purpose:
|
|
* For every object file with CV information write its sstModule,
|
|
* sstTypes, sstPublics, sstSymbols and sstSrcModule.
|
|
* Build subsection directory.
|
|
*
|
|
* Input:
|
|
* No explicit value is passed.
|
|
* Global variables used:
|
|
* - rprop1stFile - virtual pointer to the first object file descriptor
|
|
*
|
|
* Output:
|
|
* No explicit value is returned.
|
|
* Side effects:
|
|
* - subsections in the executable file
|
|
* - subsection directory in the VM
|
|
*
|
|
* Exceptions:
|
|
* I/O errors - display error message and quit
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
LOCAL void NEAR OutSst(void)
|
|
{
|
|
APROPFILEPTR apropFile; // Real pointer to file entry
|
|
RBTYPE rbFileNext; // Virtual pointer the next file descriptor
|
|
struct dnt dntCur; // Current subsection directory entry
|
|
CVINFO FAR *pCvInfo; // Pointer to the CV info descriptor
|
|
|
|
#if CVPACK_SHARED
|
|
#if REVERSE_MODULE_ORDER_FOR_CVPACK
|
|
|
|
RBTYPE rbFileCur;
|
|
RBTYPE rbFileLast;
|
|
|
|
// reverse module list in place
|
|
// I've been waiting all my life to actually *need* this code... [rm]
|
|
|
|
// this will cause us to write the modules tables in REVERSE order
|
|
// (this gives better swapping behaviour in the cvpack phase
|
|
// because the modules cvpack will visit first will be the ones that
|
|
// are still resident...
|
|
|
|
rbFileCur = rprop1stFile;
|
|
rbFileLast = NULL;
|
|
while (rbFileCur != NULL)
|
|
{
|
|
apropFile = (APROPFILEPTR ) FETCHSYM(rbFileCur, TRUE);
|
|
rbFileNext = apropFile->af_FNxt;// Get pointer to next file
|
|
apropFile->af_FNxt = rbFileLast;
|
|
rbFileLast = rbFileCur;
|
|
rbFileCur = rbFileNext;
|
|
}
|
|
rprop1stFile = rbFileLast;
|
|
#endif
|
|
|
|
#endif
|
|
|
|
rbFileNext = rprop1stFile;
|
|
dntCur.iMod = 1;
|
|
while (rbFileNext != NULL) // For every module
|
|
{
|
|
apropFile = (APROPFILEPTR ) FETCHSYM(rbFileNext, TRUE);
|
|
rbFileNext = apropFile->af_FNxt;// Get pointer to next file
|
|
|
|
// Skip this module if no debug info for it
|
|
|
|
if (!apropFile->af_cvInfo && !apropFile->af_publics && !apropFile->af_Src)
|
|
continue;
|
|
|
|
pCvInfo = apropFile->af_cvInfo;
|
|
|
|
// sstModules
|
|
|
|
dntCur.sst = SSTMODULES4;
|
|
dntCur.lfo = FTELL_BSRUNFILE() - lfaBase;
|
|
dntCur.cb = OutModule(apropFile);
|
|
PutDnt(&dntCur);
|
|
|
|
// sstTypes
|
|
|
|
if (pCvInfo && pCvInfo->cv_cbTyp > 0L)
|
|
{
|
|
Pad2Dword();
|
|
if (apropFile->af_flags & FPRETYPES)
|
|
dntCur.sst = SSTPRETYPES;
|
|
else
|
|
dntCur.sst = SSTTYPES4;
|
|
dntCur.lfo = FTELL_BSRUNFILE() - lfaBase;
|
|
dntCur.cb = pCvInfo->cv_cbTyp;
|
|
WriteNocopy(pCvInfo->cv_typ, pCvInfo->cv_cbTyp);
|
|
IF_NOT_CVPACK_SHARED(FFREE(pCvInfo->cv_typ));
|
|
PutDnt(&dntCur);
|
|
}
|
|
|
|
// sstPublics
|
|
|
|
if (apropFile->af_publics && !fSkipPublics)
|
|
{
|
|
Pad2Dword();
|
|
dntCur.sst = SSTPUBLICSYM;
|
|
dntCur.lfo = FTELL_BSRUNFILE() - lfaBase;
|
|
dntCur.cb = OutPublics(apropFile->af_publics);
|
|
PutDnt(&dntCur);
|
|
}
|
|
|
|
// sstSymbols
|
|
|
|
if (pCvInfo && pCvInfo->cv_cbSym > 0L)
|
|
{
|
|
Pad2Dword();
|
|
dntCur.sst = SSTSYMBOLS4;
|
|
dntCur.lfo = FTELL_BSRUNFILE() - lfaBase;
|
|
dntCur.cb = pCvInfo->cv_cbSym;
|
|
WriteNocopy(pCvInfo->cv_sym, pCvInfo->cv_cbSym);
|
|
IF_NOT_CVPACK_SHARED(FFREE(pCvInfo->cv_sym));
|
|
PutDnt(&dntCur);
|
|
}
|
|
|
|
// sstSrcModule
|
|
|
|
if (apropFile->af_Src)
|
|
{
|
|
Pad2Dword();
|
|
dntCur.sst = SSTSRCMODULE;
|
|
dntCur.lfo = FTELL_BSRUNFILE() - lfaBase;
|
|
dntCur.cb = OutSrcModule(apropFile->af_Src);
|
|
PutDnt(&dntCur);
|
|
}
|
|
|
|
dntCur.iMod++;
|
|
}
|
|
|
|
// sstLibraries
|
|
|
|
Pad2Dword();
|
|
dntCur.sst = SSTLIBRARIES4;
|
|
dntCur.iMod = (short) 0xffff;
|
|
dntCur.lfo = FTELL_BSRUNFILE() - lfaBase;
|
|
dntCur.cb = OutLibSec();
|
|
PutDnt(&dntCur);
|
|
|
|
// sstSegMap
|
|
|
|
Pad2Dword();
|
|
dntCur.sst = SSTSEGMAP;
|
|
dntCur.lfo = FTELL_BSRUNFILE() - lfaBase;
|
|
dntCur.cb = OutSegMap();
|
|
PutDnt(&dntCur);
|
|
|
|
// sstSegNames
|
|
|
|
Pad2Dword();
|
|
dntCur.sst = SSTSEGNAME;
|
|
dntCur.lfo = FTELL_BSRUNFILE() - lfaBase;
|
|
dntCur.cb = OutSegNames();
|
|
PutDnt(&dntCur);
|
|
FFREE(fileBase);
|
|
FFREE(raSeg);
|
|
FFREE(segNo);
|
|
}
|
|
|
|
#pragma check_stack(off)
|
|
|
|
/*
|
|
* OutLibSec : Output sstLibraries subsection to bsRunfile
|
|
*
|
|
* Path prefix is stripped from library name.
|
|
* If no libraries, don't output anything.
|
|
*
|
|
* Parameters: none
|
|
* Returns: Number of bytes in Libraries subsection
|
|
*/
|
|
LOCAL int NEAR OutLibSec ()
|
|
{
|
|
WORD ifh;
|
|
AHTEPTR ahte;
|
|
int cb = 0;
|
|
BYTE *pb;
|
|
|
|
if (ifhLibMac == 0)
|
|
return(0);
|
|
|
|
// Libraries subsection consists of a list of library
|
|
// names which will be indexed by library numbers in the
|
|
// sstModules. Those indexes are 1-based.
|
|
|
|
// cb == 0, use it to write a single byte
|
|
WriteCopy(&cb, 1); // 0th entry is null for now
|
|
|
|
cb++;
|
|
for (ifh = 0; ifh < ifhLibMac; ifh++)
|
|
{
|
|
if (mpifhrhte[ifh] != RHTENIL)
|
|
{
|
|
ahte = (AHTEPTR) FETCHSYM(mpifhrhte[ifh],FALSE);
|
|
#if OSXENIX
|
|
pb = GetFarSb(ahte->cch);
|
|
#else
|
|
pb = StripDrivePath(GetFarSb(ahte->cch));
|
|
#endif
|
|
}
|
|
else
|
|
pb = "";
|
|
WriteCopy(pb, pb[0] + 1);
|
|
cb += 1 + B2W(pb[0]);
|
|
}
|
|
return(cb);
|
|
}
|
|
|
|
/*** OutDntDir - write subsection directory
|
|
*
|
|
* Purpose:
|
|
* Write subsection directory
|
|
*
|
|
* Input:
|
|
* No explicit value is passed.
|
|
* Global variables:
|
|
* - dntPageMac - number of VM pages with DNTs
|
|
*
|
|
* Output:
|
|
* Function returns the size of the directory in bytes.
|
|
*
|
|
* Exceptions:
|
|
* I/O problems - display error message and quit
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
|
|
LOCAL DWORD NEAR OutDntDir(void)
|
|
{
|
|
DNTHDR hdr; // Directory header
|
|
|
|
|
|
hdr.cbDirHeader = sizeof(DNTHDR);
|
|
hdr.cbDirEntry = sizeof(DNT);
|
|
hdr.cDir = dntMac;
|
|
hdr.lfoDirNext = 0L;
|
|
hdr.flags = 0L;
|
|
|
|
// Write header
|
|
|
|
WriteCopy(&hdr, sizeof(DNTHDR));
|
|
|
|
// Write directory
|
|
|
|
WriteCopy((char FAR *) rgDnt, dntMac * sizeof(DNT));
|
|
FFREE(rgDnt);
|
|
return(sizeof(DNTHDR) + dntMac * sizeof(DNT));
|
|
}
|
|
|
|
/*** OutDebSection - allow debugging
|
|
*
|
|
* Purpose:
|
|
* Append to the executable file the CV information. ONLY CV 4.00
|
|
* format supported.
|
|
*
|
|
* Input:
|
|
* No explicit value is passed.
|
|
* Global variables:
|
|
* - too many to list
|
|
*
|
|
* Output:
|
|
* No explicit value is returned.
|
|
* Side effects:
|
|
* - what do you think ??
|
|
*
|
|
* Exceptions:
|
|
* I/O problems - display error message and quit
|
|
*
|
|
* Notes:
|
|
* None.
|
|
*
|
|
*************************************************************************/
|
|
|
|
|
|
void OutDebSections(void)
|
|
{
|
|
long lfaDir; // File address of Directory
|
|
DWORD dirSize; // Directory size
|
|
long tmp;
|
|
|
|
|
|
#if CVPACK_SHARED
|
|
long * plfoDir; // pointer to directory data
|
|
|
|
fseek(bsRunfile, 0L, 2); // Go to end of file
|
|
lfaBase = ftell(bsRunfile); // Remember base address
|
|
lposCur = lfaBase; // set current position...
|
|
|
|
WriteCopy(szSignature, sizeof(szSignature)); // Signature dword
|
|
WriteCopy(&tmp, sizeof(tmp)); // Skip lfoDir field
|
|
plfoDir = (long *)rgbl[iblLim-1].pb;// remember address of lfoDir
|
|
|
|
OutSst(); // Output subsections
|
|
|
|
lfaDir = lposCur; // Remember where directory starts
|
|
|
|
*plfoDir = lfaDir - lfaBase; // fix up lfoDir field
|
|
|
|
dirSize = OutDntDir(); // Output subsection directory
|
|
WriteCopy(szSignature, sizeof(szSignature));
|
|
// Signature dword
|
|
tmp = (lfaDir + dirSize + 2*sizeof(DWORD)) - lfaBase;
|
|
WriteCopy(&tmp, sizeof(long)); // Distance from EOF to base
|
|
|
|
// write out the bits that cvpack won't overwrite...
|
|
|
|
if (fCVpack)
|
|
WriteFlushSignature();
|
|
else
|
|
WriteFlushAll();
|
|
|
|
cbRealBytes = ftell(bsRunfile); // # of real bytes actually written
|
|
lposMac = lposCur;
|
|
iblCur = iblNil;
|
|
#else
|
|
if (fseek(bsRunfile, 0L, 2)) // Go to end of file
|
|
Fatal(ER_ioerr, strerror(errno));
|
|
lfaBase = FTELL_BSRUNFILE(); // Remember base address
|
|
WriteExe(szSignature, sizeof(szSignature));
|
|
// Signature dword
|
|
if (fseek(bsRunfile,4L,1)) // Skip lfoDir field
|
|
Fatal(ER_ioerr, strerror(errno));
|
|
OutSst(); // Output subsections
|
|
lfaDir = FTELL_BSRUNFILE(); // Remember where directory starts
|
|
if (fseek(bsRunfile, lfaBase + 4, 0)) // Go to lfoDir field
|
|
Fatal(ER_ioerr, strerror(errno));
|
|
tmp = lfaDir - lfaBase;
|
|
WriteExe(&tmp, sizeof(long)); // Fix it up
|
|
if (fseek(bsRunfile, lfaDir, 0)) // Go back to directory
|
|
Fatal(ER_ioerr, strerror(errno));
|
|
dirSize = OutDntDir(); // Output subsection directory
|
|
WriteExe(szSignature, sizeof(szSignature));
|
|
// Signature dword
|
|
tmp = (lfaDir + dirSize + 2*sizeof(DWORD)) - lfaBase;
|
|
WriteExe(&tmp, sizeof(long)); // Distance from EOF to base
|
|
if (fseek(bsRunfile, 0L, 2)) // Seek to EOF just in case
|
|
Fatal(ER_ioerr, strerror(errno));
|
|
#endif
|
|
}
|
|
|
|
#if CVPACK_SHARED
|
|
|
|
//
|
|
// write data to cvpack memory cache area
|
|
//
|
|
|
|
void
|
|
WriteSave(FTYPE fCopy, void *pb, UINT cb)
|
|
{
|
|
if (!rgbl)
|
|
{
|
|
rgbl = (BL *)GetMem(sizeof(BL) * C_BL_INIT);
|
|
iblMac = C_BL_INIT;
|
|
iblLim = 0;
|
|
}
|
|
|
|
// if this memory isn't going to stay around, then copy it
|
|
if (fCopy)
|
|
{
|
|
void *pbT = (void *)GetMem(cb);
|
|
memcpy(pbT, pb, cb);
|
|
pb = pbT;
|
|
}
|
|
|
|
if (iblLim == iblMac)
|
|
{
|
|
BL *rgblT;
|
|
|
|
rgblT = (BL *)GetMem(sizeof(BL) * iblMac * 2);
|
|
memcpy(rgblT, rgbl, sizeof(BL) * iblMac);
|
|
iblMac *= 2;
|
|
FFREE(rgbl);
|
|
rgbl = rgblT;
|
|
}
|
|
|
|
rgbl[iblLim].lpos = lposCur;
|
|
rgbl[iblLim].pb = pb;
|
|
iblLim++;
|
|
lposCur += cb;
|
|
}
|
|
|
|
// we want to write a few of the blocks because cvpack won't rewrite the
|
|
// first bit... [rm]
|
|
|
|
void WriteFlushSignature()
|
|
{
|
|
int ibl, cb;
|
|
|
|
// we know that the signature and offset are written in two pieces...
|
|
// if this changes we need to change the magic '2' below [rm]
|
|
|
|
for (ibl = 0; ibl < 2; ibl++)
|
|
{
|
|
cb = rgbl[ibl+1].lpos - rgbl[ibl].lpos;
|
|
WriteExe(rgbl[ibl].pb, cb);
|
|
}
|
|
}
|
|
|
|
void WriteFlushAll()
|
|
{
|
|
int ibl, cb;
|
|
|
|
for (ibl = 0; ibl < iblLim - 1; ibl++)
|
|
{
|
|
cb = rgbl[ibl+1].lpos - rgbl[ibl].lpos;
|
|
WriteExe(rgbl[ibl].pb, cb);
|
|
}
|
|
|
|
cb = lposCur - rgbl[ibl].lpos;
|
|
WriteExe(rgbl[ibl].pb, cb);
|
|
}
|
|
|
|
// the following are the various callback functions needed to support
|
|
// the cvpack library when we are attempting to not write out the
|
|
// unpacked types and symbols
|
|
|
|
#include <io.h>
|
|
|
|
extern int printf(char *,...);
|
|
|
|
int __cdecl
|
|
link_chsize (int fh, long size)
|
|
{
|
|
LINK_TRACE(printf("chsize(%06d, %08ld)\n", fh, size));
|
|
|
|
// we must keep track of the new size so that we will correctly
|
|
// process lseeks that are relative to the end of the file
|
|
|
|
lposMac = size;
|
|
|
|
return(_chsize(fh,size));
|
|
}
|
|
|
|
|
|
int __cdecl
|
|
link_close (int x)
|
|
{
|
|
LINK_TRACE(printf("close (%06d)\n", x));
|
|
|
|
return(_close(x));
|
|
}
|
|
|
|
void __cdecl
|
|
link_exit (int x)
|
|
{
|
|
LINK_TRACE(printf("exit (%06d)\n", x));
|
|
#if USE_REAL
|
|
RealMemExit();
|
|
#endif
|
|
exit(x);
|
|
}
|
|
|
|
long __cdecl
|
|
link_lseek (int fh, long lpos, int mode)
|
|
{
|
|
int ibl;
|
|
|
|
LINK_TRACE(printf("lseek (%d, %08ld, %2d)\n", fh, lpos, mode));
|
|
|
|
// if we have no cache blocks, just forward the request...
|
|
// this will happen on a /CvpackOnly invocation
|
|
|
|
if (rgbl == NULL)
|
|
return _lseek(fh, lpos, mode);
|
|
|
|
// adjust lpos so that we are always doing an absolute seek
|
|
|
|
if (mode == 1)
|
|
lpos = lposCur + lpos;
|
|
else if (mode == 2)
|
|
lpos = lposMac + lpos;
|
|
|
|
// check for a bogus seek
|
|
|
|
if (lpos > lposMac || lpos < 0)
|
|
{
|
|
// this used to be an internal error... but cvpack sometimes does
|
|
// try to seek beyond the end of the file when it is trying to
|
|
// distinguish a PE exe from an unsegmented DOS exe
|
|
// instead of panicing, we just return failure
|
|
|
|
return(-1);
|
|
}
|
|
|
|
// if we are in the midst of reading a block, then free that block
|
|
// cvpack never reads the same data twice
|
|
|
|
if (iblCur != iblNil)
|
|
{
|
|
// first check if we're in the header -- we might come back to that...
|
|
if (rgbl[iblCur].lpos > cbRealBytes + CB_HEADER_SAVE)
|
|
{
|
|
long lposCurMin, lposCurMac;
|
|
|
|
// check for a seek that is within the current bucket
|
|
// in case we're skipping within the current block
|
|
|
|
lposCurMin = rgbl[iblCur].lpos;
|
|
|
|
if (iblCur < iblLim)
|
|
lposCurMac = rgbl[iblCur+1].lpos;
|
|
else
|
|
lposCurMac = lposMac;
|
|
|
|
if (lpos < lposCurMin || lpos >= lposCurMac)
|
|
{
|
|
FFREE(rgbl[iblCur].pb);
|
|
rgbl[iblCur].pb = NULL;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// if this seek is not in the debug area of the .exe use the real lseek
|
|
|
|
if (lpos < cbRealBytes)
|
|
{
|
|
iblCur = iblNil;
|
|
lposCur = lpos;
|
|
return(_lseek(fh,lpos,0));
|
|
}
|
|
|
|
// see if we are searching forward (the normal case)
|
|
// if we are, search from the current block, otherwise search from
|
|
// the start (linear search but OK because cvpack doesn't
|
|
// jump around much, it just uses lseek to skip a few bytes here and
|
|
// there)
|
|
|
|
if (lpos > lposCur && iblCur != iblNil)
|
|
ibl = iblCur;
|
|
else
|
|
ibl = 0;
|
|
|
|
// set the current position
|
|
|
|
lposCur = lpos;
|
|
|
|
// loop through the buffered writes looking for the requested position
|
|
|
|
for (; ibl < iblLim - 1; ibl++)
|
|
{
|
|
if (lpos >= rgbl[ibl].lpos && lpos < rgbl[ibl+1].lpos)
|
|
break; // found bucket
|
|
}
|
|
|
|
// set the bucket number, offset within the bucket, and number of bytes
|
|
// left in the bucket
|
|
|
|
iblCur = ibl;
|
|
ichCur = lpos - rgbl[ibl].lpos;
|
|
cbCur = CbIbl(ibl) - ichCur;
|
|
|
|
// check to make sure we haven't seeked back to a buffer that we already
|
|
// freed...
|
|
|
|
ASSERT(rgbl[iblCur].pb != NULL);
|
|
|
|
// make sure we get the boundary case... if cvpack is requesting to go to
|
|
// the end of the data that we have written then we MUST seek because
|
|
// cvpack might be about to write out the packed stuff...
|
|
|
|
if (lposCur == cbRealBytes)
|
|
_lseek(fh, lpos, 0);
|
|
|
|
// we set up the current position earlier... return it now
|
|
|
|
return(lposCur);
|
|
}
|
|
|
|
|
|
int __cdecl
|
|
link_open (const char * x, int y)
|
|
{
|
|
LINK_TRACE(printf("open (%s, %06d)\n", x, y));
|
|
|
|
// setup the static variables to a safe state
|
|
// the current position is the start of file and there is no buffer
|
|
// active (iblCur = iblNil)
|
|
|
|
iblCur = iblNil;
|
|
lposCur = 0;
|
|
|
|
return(_open(x,y));
|
|
}
|
|
|
|
int __cdecl
|
|
link_read (int fh, char *pch, unsigned int cb)
|
|
{
|
|
int cbRem;
|
|
|
|
LINK_TRACE(printf("read (%d, %06u)\n", fh, cb));
|
|
|
|
if (rgbl == NULL)
|
|
return _read(fh, pch, cb);
|
|
|
|
// special case zero byte read, not really necessary but
|
|
// avoids any potential problems with trying to setup empty
|
|
// buffers etc. -- it should just fall out anyways but
|
|
// just to be safe [rm]
|
|
|
|
if (cb == 0)
|
|
return 0;
|
|
|
|
// if there is no buffer active, then just forward the read
|
|
// note that if we are invoked with /CvpackOnly this test will
|
|
// always succeed
|
|
|
|
if (iblCur == iblNil)
|
|
{
|
|
if (lposCur + ((long)(unsigned long)cb) < cbRealBytes)
|
|
{
|
|
lposCur += cb;
|
|
return(_read(fh,pch,cb));
|
|
}
|
|
else
|
|
{
|
|
int cbReal = cbRealBytes - lposCur;
|
|
|
|
if (_read(fh, pch, cbReal) != cbReal)
|
|
return -1;
|
|
|
|
if (link_lseek(fh, cbRealBytes, 0) != cbRealBytes)
|
|
return -1;
|
|
|
|
// set the number of bytes remaining to be read in
|
|
|
|
cbRem = cb - cbReal;
|
|
pch += cb - cbReal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// set the number of bytes remaining to be read in
|
|
cbRem = cb;
|
|
}
|
|
|
|
while (cbRem)
|
|
{
|
|
// check if the number of bytes we need to read is less than
|
|
// the number left in the current buffer
|
|
|
|
if (cbRem <= cbCur)
|
|
{
|
|
// we can read all the remaining bytes from the current buffer
|
|
// so do it. Copy bytes and adjust the number of bytes left
|
|
// in this buffer, the index into the buffer, and the current
|
|
// position in the file
|
|
|
|
memcpy(pch, rgbl[iblCur].pb+ichCur, cbRem);
|
|
cbCur -= cbRem;
|
|
ichCur += cbRem;
|
|
lposCur += cbRem;
|
|
|
|
#ifdef DUMP_CVPACK_BYTES
|
|
{
|
|
int i;
|
|
for (i=0;i<cb;i++)
|
|
{
|
|
if ((i&15) == 0)
|
|
printf("%04x: ", i);
|
|
printf("%02x ", pch[i]);
|
|
if ((i&15)==15)
|
|
printf("\n");
|
|
}
|
|
}
|
|
if ((i&15))
|
|
printf("\n");
|
|
#endif
|
|
return cb;
|
|
}
|
|
else
|
|
{
|
|
// in this case, the read is bigger than the current buffer
|
|
// we'll be reading the whole buffer and then moving to the
|
|
// next buffer
|
|
|
|
|
|
// first read in the rest of this buffer
|
|
|
|
memcpy(pch, rgbl[iblCur].pb+ichCur, cbCur);
|
|
|
|
// adjust the number of bytes remaining and the current file
|
|
// position...
|
|
|
|
pch += cbCur;
|
|
cbRem -= cbCur;
|
|
lposCur += cbCur;
|
|
|
|
// we won't be coming back to this buffer, so return it to the
|
|
// system and mark it as freed
|
|
|
|
// first check if we're in the header -- we might come back to that
|
|
if (rgbl[iblCur].lpos > cbRealBytes + CB_HEADER_SAVE)
|
|
{
|
|
FFREE(rgbl[iblCur].pb);
|
|
rgbl[iblCur].pb = NULL;
|
|
}
|
|
|
|
// move forward to the next bucket, if there are no more buckets
|
|
// then this is an ERROR -- we'll be returning the number of
|
|
// bytes that we managed to read
|
|
|
|
iblCur++;
|
|
if (iblCur == iblLim)
|
|
{
|
|
iblCur = iblNil;
|
|
break;
|
|
}
|
|
|
|
// check to make sure that we are not reading data that
|
|
// we've already freed (yipe!)
|
|
|
|
ASSERT(rgbl[iblCur].pb != NULL);
|
|
|
|
// check to make sure that the current position agrees with
|
|
// the position that this buffer is supposed to occur at
|
|
|
|
ASSERT(lposCur == rgbl[iblCur].lpos);
|
|
|
|
// ok, everything is safe now, set the index into the current
|
|
// buffer and the number of bytes left in the buffer, then
|
|
// run the loop again until we've read in all the bytes we need
|
|
|
|
ichCur = 0;
|
|
cbCur = CbIbl(iblCur);
|
|
}
|
|
}
|
|
|
|
// return the number of bytes we actually read
|
|
return cb - cbRem;
|
|
}
|
|
|
|
long __cdecl
|
|
link_tell (int x)
|
|
{
|
|
LINK_TRACE(printf("tell (%06d)\n", x));
|
|
|
|
if (iblCur != iblNil)
|
|
return(lposCur);
|
|
|
|
return(_tell(x));
|
|
}
|
|
|
|
|
|
int __cdecl
|
|
link_write (int x, const void * y, unsigned int z)
|
|
{
|
|
LINK_TRACE(printf("write (%06d,%08lx,%06u)\n", x, y, z));
|
|
|
|
return(_write(x,y,z));
|
|
}
|
|
|
|
#ifdef CVPACK_DEBUG_HELPER
|
|
void dumpstate()
|
|
{
|
|
printf("lposCur= %d\n", lposCur);
|
|
printf("iblCur = %d\n", iblCur);
|
|
printf("ichCur = %d\n", ichCur);
|
|
printf("cbReal = %d\n", cbRealBytes);
|
|
printf("lposMac= %d\n", lposMac);
|
|
}
|
|
#endif
|
|
|
|
#else
|
|
#ifdef CVPACK_MONDO
|
|
|
|
#include <io.h>
|
|
|
|
int __cdecl
|
|
link_chsize (int x, long y)
|
|
{
|
|
return(_chsize(x,y));
|
|
}
|
|
|
|
int __cdecl
|
|
link_close (int x)
|
|
{
|
|
return(_close(x));
|
|
}
|
|
|
|
void __cdecl
|
|
link_exit (int x)
|
|
{
|
|
#if USE_REAL
|
|
RealMemExit();
|
|
#endif
|
|
exit(x);
|
|
}
|
|
|
|
long __cdecl
|
|
link_lseek (int x, long y, int z)
|
|
{
|
|
return(_lseek(x,y,z));
|
|
}
|
|
|
|
|
|
int __cdecl
|
|
link_open (const char *x, int y)
|
|
{
|
|
return(_open(x,y));
|
|
}
|
|
|
|
int __cdecl
|
|
link_read (int x, void *y, unsigned int z)
|
|
{
|
|
return(_read(x,y,z));
|
|
}
|
|
|
|
long __cdecl
|
|
link_tell (int x)
|
|
{
|
|
return(_tell(x));
|
|
}
|
|
|
|
|
|
int __cdecl
|
|
link_write (int x, const void * y, unsigned int z)
|
|
{
|
|
return(_write(x,y,z));
|
|
}
|
|
#endif
|
|
#endif
|