mirror of
https://github.com/NishiOwO/JWasm.git
synced 2025-04-21 16:54:39 +00:00
1684 lines
58 KiB
C
1684 lines
58 KiB
C
/****************************************************************************
|
|
*
|
|
* Open Watcom Project
|
|
*
|
|
* Portions Copyright (c) 1983-2002 Sybase, Inc. All Rights Reserved.
|
|
*
|
|
* ========================================================================
|
|
*
|
|
* This file contains Original Code and/or Modifications of Original
|
|
* Code as defined in and that are subject to the Sybase Open Watcom
|
|
* Public License version 1.0 (the 'License'). You may not use this file
|
|
* except in compliance with the License. BY USING THIS FILE YOU AGREE TO
|
|
* ALL TERMS AND CONDITIONS OF THE LICENSE. A copy of the License is
|
|
* provided with the Original Code and Modifications, and is also
|
|
* available at www.sybase.com/developer/opensource.
|
|
*
|
|
* The Original Code and all software distributed under the License are
|
|
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
|
* EXPRESS OR IMPLIED, AND SYBASE AND ALL CONTRIBUTORS HEREBY DISCLAIM
|
|
* ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
|
|
* NON-INFRINGEMENT. Please see the License for the specific language
|
|
* governing rights and limitations under the License.
|
|
*
|
|
* ========================================================================
|
|
*
|
|
* Description: handle OMF output format.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <limits.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "globals.h"
|
|
#include "memalloc.h"
|
|
#include "parser.h"
|
|
#include "segment.h"
|
|
#include "mangle.h"
|
|
#include "extern.h"
|
|
#include "fixup.h"
|
|
#include "omf.h"
|
|
#include "omfint.h"
|
|
#include "omfspec.h"
|
|
#include "fastpass.h"
|
|
#include "myassert.h"
|
|
#include "tokenize.h" /* needed because of StringBufferEnd usage */
|
|
#include "input.h"
|
|
#include "linnum.h"
|
|
|
|
#define TRUNCATE 1
|
|
#define MULTIHDR 1 /* write muliple THEADR records (Masm compatible) */
|
|
#define WRITEIMPDEF 0 /* write IMPDEF coment records for OPTION DLLIMPORT */
|
|
|
|
#define MANGLE_BYTES 8 /* extra size required for name decoration */
|
|
#define MAX_ID_LEN_OMF 247
|
|
#define MAX_LNAME_SIZE 1024
|
|
#define MAX_EXT_LENGTH 1020 /* max length ( in chars ) of EXTDEF */
|
|
#define MAX_PUB_LENGTH 1024 /* max length of PUBDEF record */
|
|
|
|
|
|
#if TRUNCATE
|
|
#if defined(__UNIX__) || defined(__CYGWIN__) || defined(__DJGPP__)
|
|
#include <unistd.h>
|
|
#else
|
|
#include <io.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__UNIX__) || defined(__CYGWIN__)
|
|
#define _stat stat
|
|
#endif
|
|
|
|
#define TruncRec(objr) (void)( (objr)->length = (objr)->curoff )
|
|
|
|
enum {
|
|
TIME_SEC_B = 0,
|
|
TIME_SEC_F = 0x001f,
|
|
TIME_MIN_B = 5,
|
|
TIME_MIN_F = 0x07e0,
|
|
TIME_HOUR_B = 11,
|
|
TIME_HOUR_F = 0xf800
|
|
};
|
|
|
|
enum {
|
|
DATE_DAY_B = 0,
|
|
DATE_DAY_F = 0x001f,
|
|
DATE_MON_B = 5,
|
|
DATE_MON_F = 0x01e0,
|
|
DATE_YEAR_B = 9,
|
|
DATE_YEAR_F = 0xfe00
|
|
};
|
|
|
|
union DOS_DATETIME {
|
|
struct {
|
|
unsigned short time;
|
|
unsigned short date;
|
|
} dos;
|
|
time_t timet;
|
|
};
|
|
|
|
extern void cv_write_debug_tables( struct dsym *, struct dsym *, void * );
|
|
extern void SortSegments( int );
|
|
|
|
extern struct qdesc LinnumQueue; /* queue of line_num_info items */
|
|
extern const char szNull[];
|
|
|
|
/* LastCodeBufSize stores the size of the code buffer AFTER it has been written in omf_write_ledata().
|
|
* This allows to get the content bytes for the listing.
|
|
* This is a rather hackish "design" and is to be improved.
|
|
*/
|
|
int_32 LastCodeBufSize;
|
|
|
|
static uint_32 seg_pos; /* file pos of SEGDEF record(s) */
|
|
static uint_32 public_pos; /* file pos of PUBDEF record(s) */
|
|
static uint_32 end_of_header; /* file pos of "end of header" */
|
|
|
|
/* v2.12: moved from inside omf_write_lnames() */
|
|
static int startitem;
|
|
static int startext;
|
|
|
|
#if MULTIHDR
|
|
static unsigned ln_srcfile; /* last file for which line numbers have been written */
|
|
#endif
|
|
static uint_8 ln_is32; /* last mode for which line numbers have been written */
|
|
static uint_16 ln_size; /* size of line number info */
|
|
|
|
/* CodeView symbolic debug info */
|
|
enum dbgseg_index {
|
|
DBGS_SYMBOLS,
|
|
DBGS_TYPES,
|
|
DBGS_MAX
|
|
};
|
|
|
|
struct dbg_section {
|
|
const char *name;
|
|
const char *cname;
|
|
};
|
|
|
|
static const struct dbg_section SymDebParm[DBGS_MAX] = {
|
|
{ "$$SYMBOLS", "DEBSYM" },
|
|
{ "$$TYPES", "DEBTYP" },
|
|
};
|
|
|
|
static struct dsym *SymDebSeg[DBGS_MAX];
|
|
|
|
static void omf_InitRec( struct omf_rec *obj, uint_8 command )
|
|
/************************************************************/
|
|
{
|
|
obj->length = 0;
|
|
obj->curoff = 0;
|
|
obj->data = NULL;
|
|
obj->command = command;
|
|
obj->is_32 = 0;
|
|
DebugMsg1(("omf_InitRec(%p, %X)\n", obj, command ));
|
|
return;
|
|
}
|
|
|
|
static time_t timet2dostime( time_t x )
|
|
/*************************************/
|
|
{
|
|
struct tm * ltime;
|
|
union DOS_DATETIME dt;
|
|
|
|
ltime = localtime( &x );
|
|
dt.dos.date = (( ltime->tm_year - 80 ) << DATE_YEAR_B )
|
|
| (( ltime->tm_mon + 1 ) << DATE_MON_B )
|
|
| (( ltime->tm_mday ) << DATE_DAY_B );
|
|
dt.dos.time = (( ltime->tm_hour ) << TIME_HOUR_B )
|
|
| (( ltime->tm_min ) << TIME_MIN_B )
|
|
| (( ltime->tm_sec / 2 ) << TIME_SEC_B );
|
|
return( dt.timet );
|
|
}
|
|
|
|
static void Put8( struct omf_rec *objr, uint_8 value )
|
|
/****************************************************/
|
|
{
|
|
/**/myassert( objr != NULL && objr->data != NULL );
|
|
objr->data[ objr->curoff++ ] = value;
|
|
}
|
|
|
|
static void Put16( struct omf_rec *objr, uint_16 value )
|
|
/******************************************************/
|
|
{
|
|
/**/myassert( objr != NULL && objr->data != NULL );
|
|
|
|
WriteU16( objr->data + objr->curoff, value );
|
|
objr->curoff += sizeof( uint_16 );
|
|
}
|
|
|
|
static void Put32( struct omf_rec *objr, uint_32 value )
|
|
/******************************************************/
|
|
{
|
|
/**/myassert( objr != NULL && objr->data != NULL );
|
|
|
|
WriteU32( objr->data + objr->curoff, value );
|
|
objr->curoff += sizeof( uint_32 );
|
|
}
|
|
|
|
#if 0
|
|
static void PutEither( struct omf_rec *objr, uint_32 data )
|
|
/*********************************************************/
|
|
{
|
|
/**/myassert( objr != NULL && objr->data != NULL );
|
|
if( objr->is_32 ) {
|
|
WriteU32( objr->data + objr->curoff, data );
|
|
objr->curoff += sizeof( uint_32 );
|
|
} else {
|
|
WriteU16( objr->data + objr->curoff, data );
|
|
objr->curoff += sizeof( uint_16 );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* index in OMF is limited to 0-7FFFh
|
|
* index values 0-7Fh are stored in 1 byte.
|
|
* index values 80h-7FFFh are stored in 2 bytes, the high
|
|
* order byte first ( with bit 7=1 ).
|
|
*/
|
|
|
|
static void PutIndex( struct omf_rec *objr, unsigned idx )
|
|
/********************************************************/
|
|
{
|
|
/**/myassert( objr != NULL && objr->data != NULL && idx <= 0x7FFF );
|
|
if( idx > 0x7f ) {
|
|
objr->data[objr->curoff++] = ( idx >> 8 ) | 0x80;
|
|
}
|
|
objr->data[objr->curoff++] = idx & 0xff;
|
|
}
|
|
|
|
static void PutData( struct omf_rec *objr, const uint_8 *data, size_t len )
|
|
/*************************************************************************/
|
|
{
|
|
/**/myassert( objr != NULL && objr->data != NULL );
|
|
memcpy( objr->data + objr->curoff, data, len );
|
|
objr->curoff += len;
|
|
}
|
|
|
|
static void PutName( struct omf_rec *objr, const char *name, size_t len )
|
|
/***********************************************************************/
|
|
{
|
|
/**/myassert( objr != NULL && objr->data != NULL );
|
|
#if MAX_ID_LEN > MAX_ID_LEN_OMF
|
|
if ( len > MAX_ID_LEN_OMF ) {
|
|
EmitWarn( 1, IDENTIFIER_TOO_LONG );
|
|
len = MAX_ID_LEN_OMF;
|
|
}
|
|
#endif
|
|
objr->data[objr->curoff++] = len;
|
|
PutData( objr, (uint_8 *)name, len );
|
|
}
|
|
|
|
static void AttachData( struct omf_rec *objr, uint_8 *data, size_t len )
|
|
/**********************************************************************/
|
|
{
|
|
/**/myassert( objr->data == NULL );
|
|
objr->data = data;
|
|
objr->length = len;
|
|
}
|
|
|
|
#if 0
|
|
static void AllocData( struct omf_rec *objr, size_t len )
|
|
/*******************************************************/
|
|
{
|
|
/**/myassert( objr->data == NULL );
|
|
objr->data = LclAlloc( len );
|
|
objr->length = len;
|
|
}
|
|
#endif
|
|
|
|
/* return a group's index */
|
|
|
|
unsigned omf_GetGrpIdx( struct asym *sym )
|
|
/****************************************/
|
|
{
|
|
return( sym ? ((struct dsym *)sym)->e.grpinfo->grp_idx : 0 );
|
|
}
|
|
|
|
/*
|
|
* write OMF comment records about data in code.
|
|
*/
|
|
|
|
void omf_OutSelect( bool is_data )
|
|
/********************************/
|
|
{
|
|
struct omf_rec obj;
|
|
uint_32 currofs;
|
|
int sel_idx;
|
|
static uint_32 sel_start; /* start offset of data items */
|
|
unsigned char buffer[12]; /* max is 11 ( see below ) */
|
|
|
|
if( is_data ) {
|
|
/* do nothing if it isn't the first data item or
|
|
* if current segment isn't code
|
|
*/
|
|
if( CurrSeg->e.seginfo->data_in_code ||
|
|
( CurrSeg->e.seginfo->segtype != SEGTYPE_CODE ) )
|
|
return;
|
|
sel_start = GetCurrOffset();
|
|
CurrSeg->e.seginfo->data_in_code = TRUE;
|
|
DebugMsg(("omf_OutSelect: data in code segment (%s), starting at %" I32_SPEC "X\n", CurrSeg->sym.name, sel_start ));
|
|
} else if ( CurrSeg->e.seginfo->data_in_code ) { /* data items written? */
|
|
|
|
CurrSeg->e.seginfo->data_in_code = FALSE;
|
|
|
|
if( write_to_file == TRUE ) {
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = CMT_TNP;
|
|
obj.d.coment.cmt_class = CMT_DISASM_DIRECTIVE;
|
|
|
|
sel_idx = GetSegIdx( &CurrSeg->sym );
|
|
|
|
//AllocData( objr, 11 ); /* 11 = 1 + 2 + 4 + 4 */
|
|
AttachData( &obj, buffer, 11 ); /* 11 = 1 + 2 + 4 + 4 */
|
|
currofs = GetCurrOffset();
|
|
DebugMsg(("omf_OutSelect: writing coment record about data in code: start=%" I32_SPEC "X curofs=%" I32_SPEC "X\n", sel_start, currofs ));
|
|
if( ( sel_start > 0xffffUL ) || ( currofs > 0xffffUL ) ) {
|
|
Put8( &obj, DDIR_SCAN_TABLE_32 );
|
|
PutIndex( &obj, sel_idx );
|
|
Put32( &obj, sel_start );
|
|
Put32( &obj, currofs );
|
|
} else {
|
|
Put8( &obj, DDIR_SCAN_TABLE );
|
|
PutIndex( &obj, sel_idx );
|
|
Put16( &obj, sel_start );
|
|
Put16( &obj, currofs );
|
|
}
|
|
TruncRec( &obj );
|
|
omf_write_record( &obj );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* write line number debug info.
|
|
* since v2.11, it's ensured that the size of this info will be < 1024.
|
|
* note the item structure:
|
|
* uint_16 line_number;
|
|
* union {
|
|
* uint_16 ofs16;
|
|
* uint_32 ofs32;
|
|
* };
|
|
* v2.11: create 16- or 32-bit data variant here!
|
|
*/
|
|
|
|
static void omf_write_linnum( uint_8 is32 )
|
|
/*****************************************/
|
|
{
|
|
unsigned ofssize = ( is32 ? sizeof( uint_32) : sizeof( uint_16 ) );
|
|
unsigned size;
|
|
uint_8 *data;
|
|
struct line_num_info *node;
|
|
struct line_num_info *next;
|
|
struct omf_rec obj;
|
|
|
|
for( node = LinnumQueue.head, data = StringBufferEnd; node; node = next ) {
|
|
next = node->next;
|
|
*(uint_16 *)data = node->number;
|
|
data += sizeof( uint_16 );
|
|
*(uint_32 *)data = node->offset;
|
|
data += ofssize;
|
|
LclFree( node );
|
|
}
|
|
LinnumQueue.head = NULL;
|
|
|
|
size = (char *)data - StringBufferEnd;
|
|
if( size ) {
|
|
omf_InitRec( &obj, CMD_LINNUM );
|
|
obj.is_32 = is32;
|
|
AttachData( &obj, StringBufferEnd, size );
|
|
obj.d.linnum.base.grp_idx = omf_GetGrpIdx( GetGroup( &CurrSeg->sym ) ); /* fixme ? */
|
|
obj.d.linnum.base.seg_idx = CurrSeg->e.seginfo->seg_idx;
|
|
obj.d.linnum.base.frame = 0; /* field not used here */
|
|
omf_write_record( &obj );
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void omf_write_fixupp( struct dsym *seg, char is32 )
|
|
/*********************************************************/
|
|
{
|
|
uint_8 *data;
|
|
unsigned size;
|
|
struct fixup *fix;
|
|
enum fixup_types type = ( is32 ? FIX_GEN_MS386 : FIX_GEN_INTEL );
|
|
struct omf_rec obj;
|
|
|
|
fix = seg->e.seginfo->FixupList.head;
|
|
while ( fix ) {
|
|
for( data = StringBufferEnd, size = 0; fix; fix = fix->nextrlc ) {
|
|
switch( fix->type ) {
|
|
case FIX_RELOFF32:
|
|
case FIX_OFF32:
|
|
case FIX_PTR32:
|
|
if ( !is32 ) continue;
|
|
break;
|
|
default:
|
|
if ( is32 ) continue;
|
|
break;
|
|
}
|
|
if ( size > 1020 - FIX_GEN_MAX )
|
|
break;
|
|
data += OmfFixGenFix( fix, seg->e.seginfo->start_loc, data, type );
|
|
size = (char *)data - StringBufferEnd;
|
|
}
|
|
if ( size ) {
|
|
omf_InitRec( &obj, CMD_FIXUPP );
|
|
obj.is_32 = is32;
|
|
AttachData( &obj, StringBufferEnd, size );
|
|
omf_write_record( &obj );
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint_8 get_omfalign( uint_8 alignment );
|
|
|
|
/* write an LEDATA record, optionally write fixups */
|
|
|
|
static void omf_write_ledata( struct dsym *seg )
|
|
/**********************************************/
|
|
{
|
|
struct omf_rec obj;
|
|
int_32 size;
|
|
|
|
size = seg->e.seginfo->current_loc - seg->e.seginfo->start_loc;
|
|
DebugMsg1(( "omf_write_ledata enter, buffer=%p start ofs=%" I32_SPEC "X, size=%" I32_SPEC "X\n",
|
|
seg->e.seginfo->CodeBuffer, seg->e.seginfo->start_loc, size ));
|
|
if( size > 0 && write_to_file == TRUE ) {
|
|
LastCodeBufSize = size;
|
|
#if COMDATSUPP
|
|
if ( seg->e.seginfo->comdat_selection ) {
|
|
/* if the COMDAT symbol has been referenced in a FIXUPP,
|
|
* a CEXTDEF has to be written.
|
|
*/
|
|
if ( seg->sym.used ) {
|
|
omf_InitRec( &obj, CMD_CEXTDEF );
|
|
AttachData( &obj, StringBufferEnd, 2 * sizeof( uint_16 ) );
|
|
PutIndex( &obj, seg->e.seginfo->comdat_idx ); /* Index */
|
|
PutIndex( &obj, 0 ); /* Type */
|
|
TruncRec( &obj );
|
|
omf_write_record( &obj );
|
|
if ( seg->e.seginfo->seg_idx == 0 )
|
|
seg->e.seginfo->seg_idx = startext++;
|
|
}
|
|
|
|
omf_InitRec( &obj, CMD_COMDAT );
|
|
AttachData( &obj, seg->e.seginfo->CodeBuffer, size );
|
|
if( seg->e.seginfo->start_loc > 0xffffUL )
|
|
obj.is_32 = 1;
|
|
obj.d.comdat.flags = 0;
|
|
/* low 4-bits is allocation type */
|
|
if ( seg->e.seginfo->segtype == SEGTYPE_CODE ) {
|
|
obj.d.comdat.attributes = ( ModuleInfo.model == MODEL_FLAT ? COMDAT_CODE32 : COMDAT_FAR_CODE );
|
|
} else {
|
|
obj.d.comdat.attributes = ( ModuleInfo.model == MODEL_FLAT ? COMDAT_DATA32 : COMDAT_FAR_DATA );
|
|
}
|
|
obj.d.comdat.align = get_omfalign( seg->e.seginfo->alignment );
|
|
obj.d.comdat.offset = seg->e.seginfo->start_loc;
|
|
obj.d.comdat.type_idx = 0;
|
|
obj.d.comdat.public_lname_idx = seg->e.seginfo->comdat_idx;
|
|
/* todo: error if comdat_idx is 0 */
|
|
} else {
|
|
#endif
|
|
omf_InitRec( &obj, CMD_LEDATA );
|
|
AttachData( &obj, seg->e.seginfo->CodeBuffer, size );
|
|
obj.d.ledata.idx = seg->e.seginfo->seg_idx;
|
|
obj.d.ledata.offset = seg->e.seginfo->start_loc;
|
|
if( obj.d.ledata.offset > 0xffffUL )
|
|
obj.is_32 = 1;
|
|
#if COMDATSUPP
|
|
}
|
|
#endif
|
|
omf_write_record( &obj );
|
|
|
|
/* process Fixup, if any */
|
|
if( seg->e.seginfo->FixupList.head != NULL ) {
|
|
#if FASTMEM==0
|
|
struct fixup *fix;
|
|
struct fixup *next;
|
|
#endif
|
|
omf_write_fixupp( seg, 0 );
|
|
omf_write_fixupp( seg, 1 );
|
|
#if FASTMEM==0
|
|
for( fix = seg->e.seginfo->FixupList.head; fix; ) {
|
|
next = fix->nextrlc;
|
|
LclFree( fix );
|
|
fix = next;
|
|
}
|
|
#endif
|
|
seg->e.seginfo->FixupList.head = seg->e.seginfo->FixupList.tail = NULL;
|
|
}
|
|
}
|
|
seg->e.seginfo->start_loc = seg->e.seginfo->current_loc;
|
|
}
|
|
|
|
/*
|
|
* flush current segment.
|
|
* write_to_file is always TRUE here
|
|
*/
|
|
|
|
void omf_FlushCurrSeg( void )
|
|
/***************************/
|
|
{
|
|
//unsigned i;
|
|
//unsigned size;
|
|
|
|
DebugMsg1(( "omf_FlushCurrSeg() enter, CurrSeg=%s, currsrc=%u\n", CurrSeg ? CurrSeg->sym.name : "NULL", get_curr_srcfile() ));
|
|
|
|
omf_write_ledata( CurrSeg );
|
|
/* add line numbers if debugging info is desired */
|
|
//if( write_to_file && Options.line_numbers ) {
|
|
if( Options.line_numbers ) {
|
|
omf_write_linnum( ln_is32 );
|
|
ln_size = 0;
|
|
}
|
|
//if ( Options.no_comment_data_in_code_records == FALSE )
|
|
// omf_OutSelect( FALSE );
|
|
return;
|
|
}
|
|
|
|
/* Write a THEADR record.
|
|
*/
|
|
static void omf_write_theadr( const char *name )
|
|
/**********************************************/
|
|
{
|
|
struct omf_rec obj;
|
|
unsigned len;
|
|
//const struct fname_item *fn;
|
|
|
|
DebugMsg1(("omf_write_theadr(%s) enter\n", name));
|
|
|
|
omf_InitRec( &obj, CMD_THEADR );
|
|
|
|
/* v2.08: use the name given at the cmdline, that's what Masm does.
|
|
* Masm emits either a relative or a full path, depending on what
|
|
* was given as filename!
|
|
*/
|
|
len = strlen( name );
|
|
AttachData( &obj, StringBufferEnd, len + 1 );
|
|
PutName( &obj, name, len );
|
|
omf_write_record( &obj );
|
|
|
|
DebugMsg1(("omf_write_theadr() exit\n"));
|
|
}
|
|
|
|
/* v2.11: check if
|
|
* - source file is changing
|
|
* - offset magnitude is changing
|
|
* - size of line number info exceeds 1024
|
|
* if at least one of these conditions are met AND there are linnum items
|
|
* in the queue, then flush the current LEDATA buffer.
|
|
* If source file changed, write a THEADR record for the new source file.
|
|
* ( Masm also emits THEADR records, but more frequently -
|
|
* it doesn't care if linnum items are written or have been written. )
|
|
*
|
|
* This function is called by AddLinnumDataRef(), that is, whenever a linnum
|
|
* info is about to be written.
|
|
*/
|
|
|
|
void omf_check_flush( const struct line_num_info *curr )
|
|
/******************************************************/
|
|
{
|
|
uint_8 is_32;
|
|
uint_16 size;
|
|
#if MULTIHDR
|
|
if ( curr->srcfile != ln_srcfile ) {
|
|
if ( LinnumQueue.head )
|
|
omf_FlushCurrSeg();
|
|
/* todo: for Borland, there's a COMENT ( CMT_SRCFILE ) that could be written
|
|
* instead of THEADR.
|
|
*/
|
|
omf_write_theadr( GetFName( curr->srcfile )->fname );
|
|
ln_srcfile = curr->srcfile;
|
|
return;
|
|
}
|
|
#endif
|
|
/* if there's a change in offset magnitude ( 16 -> 32 or 32 -> 16 ),
|
|
* do flush ( Masm compatible ).
|
|
*/
|
|
is_32 = ( curr->offset > 0xffff ? TRUE : FALSE );
|
|
if ( ln_is32 != is_32 ) {
|
|
if ( LinnumQueue.head )
|
|
omf_FlushCurrSeg();
|
|
ln_is32 = is_32;
|
|
return;
|
|
}
|
|
/* line number item consists of 16-bit line# and 16- or 32-bit offset */
|
|
size = sizeof( uint_16 ) + ( is_32 ? sizeof( uint_32 ) : sizeof( uint_16 ) );
|
|
/* if the size of the linnum data exceeds 1016,
|
|
* do flush ( Masm compatible ).
|
|
*/
|
|
if ( ln_size + size > 1024 - 8 ) {
|
|
if ( LinnumQueue.head )
|
|
omf_FlushCurrSeg();
|
|
}
|
|
ln_size += size;
|
|
return;
|
|
};
|
|
|
|
/*------------------------------------------------------*/
|
|
|
|
/* write end of pass 1 record. */
|
|
|
|
static void omf_end_of_pass1( void )
|
|
/**********************************/
|
|
{
|
|
struct omf_rec obj;
|
|
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = 0x00;
|
|
obj.d.coment.cmt_class = CMT_MS_END_PASS_1;
|
|
AttachData( &obj, (uint_8 *)"\x001", 1 );
|
|
omf_write_record( &obj );
|
|
}
|
|
|
|
/* called when a new path is started
|
|
* the OMF "path 2" records (LEDATA, FIXUP, LINNUM ) are written in all passes.
|
|
*/
|
|
|
|
void omf_set_filepos( void )
|
|
/**************************/
|
|
{
|
|
DebugMsg1(( "omf_set_filepos: reset file pos to %X\n", end_of_header ));
|
|
#if MULTIHDR
|
|
#endif
|
|
fseek( CurrFile[OBJ], end_of_header, SEEK_SET );
|
|
}
|
|
|
|
static void omf_write_dosseg( void )
|
|
/**********************************/
|
|
{
|
|
struct omf_rec obj;
|
|
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = CMT_TNP;
|
|
obj.d.coment.cmt_class = CMT_DOSSEG;
|
|
AttachData( &obj, (uint_8 *)"", 0 );
|
|
omf_write_record( &obj );
|
|
}
|
|
|
|
static void omf_write_lib( void )
|
|
/*******************************/
|
|
{
|
|
struct omf_rec obj;
|
|
struct qitem *curr;
|
|
struct qitem *next;
|
|
char *name;
|
|
|
|
DebugMsg1(("omf_write_lib() enter\n"));
|
|
for( curr = ModuleInfo.g.LibQueue.head; curr; curr = next ) {
|
|
next = curr->next;
|
|
name = curr->value;
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = CMT_TNP;
|
|
obj.d.coment.cmt_class = CMT_DEFAULT_LIBRARY;
|
|
AttachData( &obj, (uint_8 *)name, strlen( name ) );
|
|
omf_write_record( &obj );
|
|
}
|
|
DebugMsg1(("omf_write_lib() exit\n"));
|
|
}
|
|
|
|
#if DLLIMPORT && WRITEIMPDEF /* writing import records in OMF not supported yet */
|
|
|
|
void omf_write_import( void )
|
|
/***************************/
|
|
{
|
|
struct dsym *imp;
|
|
int len;
|
|
struct omf_rec obj;
|
|
uint_8 *data;
|
|
|
|
/* don't do anything if -Fd isn't set or if -Fd was given with a file name */
|
|
if ( (!Options.write_impdef) || Options.names[OPTN_LNKDEF_FN] )
|
|
return;
|
|
for ( imp = SymTables[TAB_EXT].head; imp; imp = imp->next ) {
|
|
if ( imp->sym.isproc && ( imp->sym.weak == FALSE || imp->sym.iat_used == TRUE ) ) {
|
|
if ( imp->sym.dll && *imp->sym.dll->name ) {
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = 0x00;
|
|
obj.d.coment.cmt_class = CMT_OMF_EXT;
|
|
|
|
/* structure of IMPDEF "comment":
|
|
* type db CMT_EXT_IMPDEF (=01)
|
|
* ordinal_flag db ? ;0=import by name
|
|
* int_name_len db ?
|
|
* int_name db int_name_len dup (?)
|
|
* mod_name_len db ?
|
|
* mod_name db mod_name_len dup (?)
|
|
* union
|
|
* entry_ident dw ? ;if by ordinal
|
|
* struct ;if by name
|
|
* imp_name_len db ? ;may be 0
|
|
* imp_name db imp_name_len dup (?)
|
|
* ends
|
|
* ends
|
|
*/
|
|
|
|
data = StringBufferEnd;
|
|
len = Mangle( &imp->sym, data+3 );
|
|
AttachData( &obj, data, len + strlen( imp->sym.dll->name ) + 5 ); /* 2 bytes prefix, 2*len prefix, 1 for imp_name_len */
|
|
|
|
Put8( &obj, CMT_EXT_IMPDEF );
|
|
Put8( &obj, 0 ); /* import by name */
|
|
Put8( &obj, len );
|
|
obj.curoff += len;
|
|
PutName( &obj, imp->sym.dll->name, strlen( imp->sym.dll->name ) );
|
|
Put8( &obj, 0 );
|
|
omf_write_record( &obj );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void omf_write_export( void )
|
|
/**********************************/
|
|
{
|
|
uint_8 parmcnt;
|
|
struct dsym *dir;
|
|
struct dsym *parm;
|
|
struct omf_rec obj;
|
|
int len;
|
|
uint_8 *data;
|
|
|
|
#if DLLIMPORT && WRITEIMPDEF /* writing import records in OMF not supported yet */
|
|
omf_write_import();
|
|
#endif
|
|
|
|
for( dir = SymTables[TAB_PROC].head; dir != NULL; dir = dir->nextproc ) {
|
|
if( dir->e.procinfo->isexport ) {
|
|
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = 0x00;
|
|
obj.d.coment.cmt_class = CMT_OMF_EXT;
|
|
data = StringBufferEnd;
|
|
|
|
/* structure of EXPDEF "comment":
|
|
* type db CMT_EXT_EXPDEF (=02)
|
|
* exported_flag db ?
|
|
* ex_name_len db ?
|
|
* exported_name db ex_name_len dup (?)
|
|
* int_name_len db 0 ;always 0
|
|
* ;internal_name db int_name_len dup (?)
|
|
* ;ordinal dw ? ;optional
|
|
*/
|
|
if ( Options.no_export_decoration == FALSE )
|
|
len = Mangle( &dir->sym, data+3 );
|
|
else {
|
|
strcpy( data+3, dir->sym.name );
|
|
len = dir->sym.name_size;
|
|
}
|
|
/* v2.11: case mapping was missing */
|
|
if ( ModuleInfo.convert_uppercase )
|
|
_strupr( data+3 );
|
|
#if MAX_ID_LEN > 255
|
|
if ( len > 255 )
|
|
len = 255; /* restrict name to 255 chars */
|
|
#endif
|
|
AttachData( &obj, data, len + 4 );
|
|
Put8( &obj, CMT_EXT_EXPDEF );
|
|
/* write the "Exported Flag" byte:
|
|
* bits 0-4: parameter count
|
|
* bit 5: no data (entry doesn't use initialized data )
|
|
* bit 6: resident (name should be kept resident)
|
|
* bit 7: ordinal ( if 1, 2 byte index must follow name)
|
|
*/
|
|
for ( parm = dir->e.procinfo->paralist, parmcnt = 0; parm; parm = parm->nextparam, parmcnt++ );
|
|
parmcnt &= 0x1F; /* ensure bits 5-7 are still 0 */
|
|
Put8( &obj, parmcnt ); /* v2.01: changed from fix 0x00 */
|
|
Put8( &obj, len );
|
|
obj.curoff += len;
|
|
Put8( &obj, 0 );
|
|
omf_write_record( &obj );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* write OMF GRPDEF records
|
|
*/
|
|
|
|
static void omf_write_grpdef( void )
|
|
/**********************************/
|
|
{
|
|
struct dsym *curr;
|
|
struct dsym *segminfo;
|
|
struct seg_item *seg;
|
|
struct omf_rec grp;
|
|
//char writeseg;
|
|
|
|
DebugMsg1(("omf_write_grpdef enter\n"));
|
|
//line_num = LineNumber;
|
|
|
|
/* size of group records may exceed 1024! */
|
|
for( curr = SymTables[TAB_GRP].head; curr; curr = curr->next ) {
|
|
|
|
omf_InitRec( &grp, CMD_GRPDEF );
|
|
|
|
grp.d.grpdef.idx = curr->e.grpinfo->grp_idx;
|
|
|
|
/* we might need:
|
|
* - 1 or 2 bytes for the group name index
|
|
* - 2 or 3 bytes for each segment in the group
|
|
*/
|
|
AttachData( &grp, StringBufferEnd, 2 + 3 * curr->e.grpinfo->numseg );
|
|
/* v2.01: the LName index of the group may be > 0xff */
|
|
/* v2.03: use the group index directly */
|
|
PutIndex( &grp, curr->e.grpinfo->lname_idx );
|
|
|
|
for( seg = curr->e.grpinfo->seglist; seg; seg = seg->next ) {
|
|
//writeseg = TRUE;
|
|
segminfo = (struct dsym *)(seg->seg);
|
|
Put8( &grp, GRP_SEGIDX );
|
|
PutIndex( &grp, segminfo->e.seginfo->seg_idx );
|
|
/* truncate the group record if it comes near output buffer limit! */
|
|
if ( grp.curoff > OBJ_BUFFER_SIZE - 10 ) {
|
|
EmitWarn( 2, GROUP_DEFINITION_TOO_LARGE, curr->sym.name );
|
|
break;
|
|
}
|
|
}
|
|
TruncRec( &grp );
|
|
omf_write_record( &grp );
|
|
}
|
|
DebugMsg1(("omf_write_grpdef exit\n"));
|
|
}
|
|
|
|
static uint_8 get_omfalign( uint_8 alignment )
|
|
/********************************************/
|
|
{
|
|
switch ( alignment ) {
|
|
case 1: return( SEGDEF_ALIGN_WORD );
|
|
case 2: return( SEGDEF_ALIGN_DWORD );
|
|
case 4: return( SEGDEF_ALIGN_PARA );
|
|
case 8: return( SEGDEF_ALIGN_PAGE );
|
|
#if PAGE4K /* PharLab peculiarity; this is invalid for MS OMF */
|
|
case 12:
|
|
if ( Parse_Pass == PASS_1 )
|
|
EmitWarn( 2, NO_4KPAGE_ALIGNED_SEGMENTS_IN_MS386 );
|
|
return( SEGDEF_ALIGN_4KPAGE );
|
|
#endif
|
|
case MAX_SEGALIGNMENT: return( SEGDEF_ALIGN_ABS );
|
|
}
|
|
/* value 0 is byte alignment, anything elso is "unexpected" */
|
|
/**/myassert( alignment == 0 );
|
|
return( SEGDEF_ALIGN_BYTE );
|
|
}
|
|
|
|
/* write segment table.
|
|
* This is done after pass 1.
|
|
* There might exist entries of undefined segments in
|
|
* the segment list!
|
|
*/
|
|
|
|
static void omf_write_segdef( void )
|
|
/**********************************/
|
|
{
|
|
struct dsym *curr;
|
|
struct omf_rec obj;
|
|
|
|
DebugMsg1(("omf_write_segdef enter\n"));
|
|
|
|
for( curr = SymTables[TAB_SEG].head; curr; curr = curr->next ) {
|
|
|
|
#if COMDATSUPP
|
|
if ( curr->e.seginfo->comdat_selection )
|
|
continue;
|
|
#endif
|
|
omf_InitRec( &obj, CMD_SEGDEF );
|
|
if ( curr->e.seginfo->Ofssize > USE16 ) {
|
|
obj.is_32 = ( ( curr->e.seginfo->force32 || ( curr->sym.max_offset >= 0x10000 ) ) ? 1 : 0 );
|
|
} else {
|
|
obj.is_32 = 0;
|
|
}
|
|
|
|
obj.d.segdef.idx = curr->e.seginfo->seg_idx;
|
|
obj.d.segdef.use_32 = ( curr->e.seginfo->Ofssize > USE16 ? 1 : 0 );
|
|
obj.d.segdef.align = get_omfalign( curr->e.seginfo->alignment );
|
|
obj.d.segdef.combine = curr->e.seginfo->combine;
|
|
obj.d.segdef.abs.frame = curr->e.seginfo->abs_frame;
|
|
obj.d.segdef.abs.offset = curr->e.seginfo->abs_offset;
|
|
obj.d.segdef.seg_length = curr->sym.max_offset;
|
|
obj.d.segdef.seg_lname_idx = curr->e.seginfo->lname_idx;
|
|
obj.d.segdef.class_lname_idx = ( curr->e.seginfo->clsym ? curr->e.seginfo->clsym->class_lname_idx : 1 );
|
|
obj.d.segdef.ovl_lname_idx = 1;
|
|
|
|
omf_write_record( &obj );
|
|
DebugMsg1(("omf_write_segdef(%s): len=%" I32_SPEC "X use32=%u align=%u comb=%u seg_lname=%u class_lname=%u ovl_lname=%u\n",
|
|
curr->sym.name,
|
|
obj.d.segdef.seg_length,
|
|
obj.d.segdef.use_32,
|
|
obj.d.segdef.align,
|
|
obj.d.segdef.combine,
|
|
obj.d.segdef.seg_lname_idx,
|
|
obj.d.segdef.class_lname_idx,
|
|
obj.d.segdef.ovl_lname_idx
|
|
));
|
|
/* write a comment for the linker.
|
|
* this is something not done by Masm, it has
|
|
* been inherited from Wasm.
|
|
*/
|
|
if( curr->e.seginfo->segtype == SEGTYPE_CODE && Options.no_opt_farcall == FALSE ) {
|
|
uint_8 buffer[4];
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = CMT_TNP;
|
|
obj.d.coment.cmt_class = CMT_LINKER_DIRECTIVE;
|
|
AttachData( &obj, buffer, 3 );
|
|
Put8( &obj, LDIR_OPT_FAR_CALLS );
|
|
PutIndex( &obj, curr->e.seginfo->seg_idx );
|
|
/* v2.04: added. cut off the 3. byte if not needed */
|
|
TruncRec( &obj );
|
|
omf_write_record( &obj );
|
|
}
|
|
}
|
|
DebugMsg1(("omf_write_segdef exit\n"));
|
|
}
|
|
|
|
/* the lnames are stored in a queue. read
|
|
* the items one by one and take care that
|
|
* the record size doesn't exceed 1024 bytes.
|
|
*/
|
|
|
|
static void omf_write_lnames( void )
|
|
/**********************************/
|
|
{
|
|
int size;
|
|
int items;
|
|
unsigned char *p;
|
|
//void *pv = NULL;
|
|
struct qnode *curr;
|
|
struct asym *sym;
|
|
struct omf_rec obj;
|
|
unsigned char buffer[MAX_LNAME_SIZE];
|
|
|
|
DebugMsg1(("omf_write_lnames() enter\n"));
|
|
p = buffer;
|
|
*p++ = NULLC; /* start with the NULL entry */
|
|
items = 1;
|
|
startitem = 1;
|
|
|
|
for ( curr = ModuleInfo.g.LnameQueue.head; ; curr = curr->next ) {
|
|
//sym = GetLnameData( &pv );
|
|
sym = ( curr ? (struct asym *)(curr->elmt) : NULL );
|
|
size = p - buffer;
|
|
/* v2.04: changed extra bytes from 1 to 4 (CMD, RECLEN, CHKSUM) */
|
|
//if ( sym == NULL || ( ( size + sym->name_size + 1 ) > MAX_LNAME_SIZE )) {
|
|
if ( sym == NULL || ( ( size + sym->name_size + 4 ) > MAX_LNAME_SIZE )) {
|
|
if( size ) {
|
|
omf_InitRec( &obj, CMD_LNAMES );
|
|
/* first_idx and num_names are NOT
|
|
* written to the LNAMES record!
|
|
* In fact, they aren't used at all.
|
|
*/
|
|
obj.d.lnames.first_idx = startitem;
|
|
obj.d.lnames.num_names = items;
|
|
AttachData( &obj, buffer, size );
|
|
omf_write_record( &obj );
|
|
startitem = items;
|
|
}
|
|
if ( sym == NULL )
|
|
break;
|
|
p = buffer;
|
|
}
|
|
*p++ = (char)sym->name_size;
|
|
/* copy 1 byte more - the NULLC - for _strupr() */
|
|
memcpy( p, sym->name, sym->name_size + 1 );
|
|
/* lnames are converted for casemaps ALL and NOTPUBLIC */
|
|
if ( ModuleInfo.case_sensitive == FALSE )
|
|
_strupr( p );
|
|
DebugMsg1(("omf_write_lnames: %u=%s\n", items, p ));
|
|
p += sym->name_size; /* overwrite the null char */
|
|
items++;
|
|
/* v2.12: lname_idx fields now set in OMF only */
|
|
switch ( sym->state ) {
|
|
case SYM_SEG: ((struct dsym *)sym)->e.seginfo->lname_idx = items; break;
|
|
case SYM_GRP: ((struct dsym *)sym)->e.grpinfo->lname_idx = items; break;
|
|
default: sym->class_lname_idx = items; break;
|
|
}
|
|
};
|
|
|
|
DebugMsg1(("omf_write_lnames() exit, items=%u\n", items ));
|
|
}
|
|
|
|
struct readext {
|
|
struct dsym *p;
|
|
uint_16 index;
|
|
uint_8 method;
|
|
};
|
|
|
|
/* read items for EXTDEF records.
|
|
* there are 2 sources:
|
|
* - the AltQueue of weak externals
|
|
* - the TAB_EXT queue of externals
|
|
* v2.09: index (ext_idx1, ext_idx2 ) is now set inside this function.
|
|
*/
|
|
|
|
static struct asym *GetExt( struct readext *r )
|
|
/*********************************************/
|
|
{
|
|
struct asym *sym;
|
|
if ( r->method == 0 ) {
|
|
for ( ; r->p; ) {
|
|
sym = (struct asym *)r->p;
|
|
r->p = r->p->next;
|
|
if ( sym->iscomm == TRUE )
|
|
continue;
|
|
if ( sym->altname && sym->altname->included == FALSE ) {
|
|
/**/ myassert( r->index ); /* overflow occured? */
|
|
sym->altname->ext_idx2 = r->index++;
|
|
sym->altname->included = TRUE;
|
|
return( sym->altname );
|
|
}
|
|
}
|
|
r->method++;
|
|
r->p = SymTables[TAB_EXT].head;
|
|
}
|
|
for ( ; r->p; ) {
|
|
sym = (struct asym *)r->p;
|
|
r->p = r->p->next;
|
|
if ( sym->iscomm == TRUE || sym->weak == TRUE )
|
|
continue;
|
|
/**/ myassert( r->index ); /* overflow occured? */
|
|
sym->ext_idx1 = r->index++;
|
|
//r->p->sym.included = TRUE;
|
|
return( sym );
|
|
}
|
|
return( NULL );
|
|
}
|
|
|
|
/* write EXTDEF records.
|
|
* this is done once, after pass 1.
|
|
* v2.09: external index is now set here.
|
|
*/
|
|
|
|
static uint_16 omf_write_extdef( void )
|
|
/*************************************/
|
|
{
|
|
struct omf_rec obj;
|
|
struct asym *sym;
|
|
struct dsym *symext;
|
|
unsigned rec_size;
|
|
unsigned len;
|
|
struct readext r;
|
|
char data[MAX_EXT_LENGTH];
|
|
unsigned char buffer[MAX_ID_LEN + MANGLE_BYTES + 1];
|
|
|
|
DebugMsg1(("omf_write_extdef enter\n"));
|
|
|
|
r.p = SymTables[TAB_EXT].head;
|
|
r.index = 1;
|
|
r.method = 0;
|
|
obj.d.extdef.first_idx = 0;
|
|
|
|
/* scan the EXTERN/EXTERNDEF items */
|
|
|
|
sym = GetExt( &r );
|
|
while ( sym ) {
|
|
for ( rec_size = 0, obj.d.extdef.num_names = 0; sym; sym = GetExt( &r ) ) {
|
|
//DebugMsg(("omf_write_extdef: %s, weak=%u, used=%u\n", curr->sym.name, curr->sym.weak, curr->sym.used ));
|
|
DebugMsg1(("omf_write_extdef: %s\n", sym->name));
|
|
len = Mangle( sym, buffer );
|
|
#if MAX_ID_LEN > 255
|
|
if ( len > 255 )
|
|
len = 255; /* length is 1 byte only */
|
|
#endif
|
|
if ( ModuleInfo.convert_uppercase )
|
|
_strupr( buffer );
|
|
|
|
if( rec_size + len + 2 >= MAX_EXT_LENGTH ) {
|
|
break;
|
|
}
|
|
obj.d.extdef.num_names++;
|
|
|
|
data[rec_size++] = (char)len;
|
|
memcpy( data + rec_size, buffer, len );
|
|
rec_size += len;
|
|
data[rec_size++] = 0; /* for the type index */
|
|
}
|
|
if ( rec_size ) {
|
|
DebugMsg1(("omf_write_extdef: write record, names=%u, size=%u, MAX=%u\n", obj.d.extdef.num_names, rec_size, MAX_EXT_LENGTH ));
|
|
omf_InitRec( &obj, CMD_EXTDEF );
|
|
AttachData( &obj, (uint_8 *)data, rec_size );
|
|
omf_write_record( &obj );
|
|
obj.d.extdef.first_idx += obj.d.extdef.num_names;
|
|
}
|
|
}
|
|
|
|
/* v2.04: write WKEXT coment records.
|
|
* those items are defined via "EXTERN (altname)" syntax.
|
|
* After the records have been written, the indices in
|
|
* altname are no longer needed.
|
|
*/
|
|
|
|
for ( symext = SymTables[TAB_EXT].head; symext; symext = symext->next ) {
|
|
if ( symext->sym.iscomm == FALSE && symext->sym.altname ) {
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = CMT_TNP;
|
|
obj.d.coment.cmt_class = CMT_WKEXT;
|
|
AttachData( &obj, buffer, 4 );
|
|
PutIndex( &obj, symext->sym.ext_idx1 );
|
|
PutIndex( &obj, symext->sym.altname->ext_idx2 );
|
|
TruncRec( &obj );
|
|
omf_write_record( &obj );
|
|
}
|
|
}
|
|
/* v2.05: reset the indices - this must be done only after ALL WKEXT
|
|
* records have been written!
|
|
*/
|
|
for ( symext = SymTables[TAB_EXT].head; symext; symext = symext->next ) {
|
|
/* v2.09: don't touch the index if the alternate name is an external
|
|
* - else an invalid object file will be created!
|
|
*/
|
|
if ( symext->sym.iscomm == FALSE && symext->sym.altname && symext->sym.altname->state != SYM_EXTERNAL )
|
|
symext->sym.altname->ext_idx = 0;
|
|
}
|
|
/* v2.09: write COMM items here. This allows to handle
|
|
* the external index field entirely in omf.c
|
|
* v2.11: removed again, since omf_write_extdef() is no
|
|
* longer called externally.
|
|
*/
|
|
//omf_write_comdef( r.index );
|
|
|
|
DebugMsg1(("omf_write_extdef exit, index=%u\n", r.index ));
|
|
return( r.index );
|
|
}
|
|
|
|
#define THREE_BYTE_MAX ( (1UL << 24) - 1 )
|
|
|
|
static int get_size_of_comdef_number( uint_32 value )
|
|
/***************************************************/
|
|
{
|
|
/* The spec allows up to 128 in a one byte size field, but lots
|
|
of software has problems with that, so we'll restrict ourselves
|
|
to 127.
|
|
*/
|
|
if( value < 128 ) {
|
|
return( 1 ); /* 1 byte value */
|
|
} else if( value <= USHRT_MAX ) {
|
|
return( 3 ); /* 1 byte flag + 2 byte value */
|
|
} else if( value <= THREE_BYTE_MAX ) {
|
|
return( 4 ); /* 1 byte flag + 3 byte value */
|
|
} else { /* if( value <= ULONG_MAX ) */
|
|
return( 5 ); /* 1 byte flag + 4 byte value */
|
|
}
|
|
}
|
|
|
|
/* for COMDEF: write item size (or number of items) */
|
|
|
|
static unsigned put_comdef_number( uint_8 *buffer, uint_32 value )
|
|
/****************************************************************/
|
|
{
|
|
unsigned i;
|
|
unsigned symsize;
|
|
|
|
symsize = get_size_of_comdef_number( value );
|
|
switch( symsize ) {
|
|
case 1: *buffer = value; break;
|
|
case 3: *buffer++ = COMDEF_LEAF_2; break; /* 0x81 */
|
|
case 4: *buffer++ = COMDEF_LEAF_3; break; /* 0x84 */
|
|
case 5: *buffer++ = COMDEF_LEAF_4; break; /* 0x88 */
|
|
}
|
|
|
|
for( i = 1; i < symsize; i++ ) {
|
|
*buffer++ = value % ( UCHAR_MAX + 1 );
|
|
value >>= 8;
|
|
}
|
|
return( symsize );
|
|
}
|
|
|
|
/* write OMF COMDEF records.
|
|
* this is done once, after pass 1.
|
|
* v2.09: external index is now set inside this function.
|
|
* important: the size of the communal variables must be known.
|
|
* If the size is the difference of two code labels, it might
|
|
* change in subsequent passes. Both Masm and JWasm won't adjust
|
|
* the size then!
|
|
*/
|
|
|
|
static uint_16 omf_write_comdef( uint_16 index )
|
|
/**********************************************/
|
|
{
|
|
struct omf_rec obj;
|
|
struct dsym *curr;
|
|
unsigned num;
|
|
unsigned recsize;
|
|
unsigned numsize;
|
|
unsigned symsize;
|
|
uint_32 varsize;
|
|
unsigned start = 0; /* record's start index (not used) */
|
|
char buffer[MAX_ID_LEN + MANGLE_BYTES + 1];
|
|
char data[MAX_EXT_LENGTH];
|
|
char number[16];
|
|
|
|
DebugMsg1(("omf_write_comdef enter\n"));
|
|
curr = SymTables[TAB_EXT].head;
|
|
while ( curr ) {
|
|
for( num = 0, recsize = 0; curr != NULL ; curr = curr->next ) {
|
|
if ( curr->sym.iscomm == FALSE )
|
|
continue;
|
|
symsize = Mangle( &curr->sym, buffer );
|
|
#if MAX_ID_LEN > 255
|
|
if ( symsize > 255 )
|
|
symsize = 255; /* length is 1 byte only */
|
|
#endif
|
|
/* v2.11: case mapping was missing */
|
|
if ( ModuleInfo.convert_uppercase )
|
|
_strupr( buffer );
|
|
|
|
varsize = SizeFromMemtype( curr->sym.mem_type, ModuleInfo.Ofssize, curr->sym.type );
|
|
|
|
/**/ myassert( index );
|
|
curr->sym.ext_idx = index++; /* v2.09: set external index here */
|
|
|
|
DebugMsg1(("omf_write_comdef: %s, size=%u, sym.total_size=%u, sym.total_length=%u, sym.isfar=%u\n",
|
|
curr->sym.name, varsize, curr->sym.total_size, curr->sym.total_length, curr->sym.isfar ));
|
|
if ( varsize == 0 )
|
|
varsize = curr->sym.total_size / curr->sym.total_length;
|
|
|
|
numsize = 1;
|
|
if ( curr->sym.isfar == TRUE ) {
|
|
number[0] = COMDEF_FAR; /* 0x61 */
|
|
numsize += put_comdef_number( &number[1], curr->sym.total_length );
|
|
numsize += put_comdef_number( &number[numsize], varsize );
|
|
DebugMsg(("omf_write_comdef: numsize=%u, length=%u, varsize=%u\n",
|
|
numsize, curr->sym.total_length, varsize ));
|
|
} else {
|
|
number[0] = COMDEF_NEAR; /* 0x62 */
|
|
numsize += put_comdef_number( &number[1], curr->sym.total_length * varsize );
|
|
DebugMsg1(("omf_write_comdef: numsize=%u, value=%u\n",
|
|
numsize, curr->sym.total_length * varsize ));
|
|
}
|
|
/* make sure the record's size doesn't exceed 1024.
|
|
* 2 = 1 (name len) + 1 (type index)
|
|
*/
|
|
if ( ( recsize + symsize + numsize + 2 ) > MAX_EXT_LENGTH )
|
|
break;
|
|
|
|
/* copy name ( including size prefix ), type, number */
|
|
data[recsize++] = (char)symsize;
|
|
memcpy( data + recsize, buffer, symsize );
|
|
recsize += symsize;
|
|
data[recsize++] = 0; /* for the type index */
|
|
memcpy( data + recsize, number, numsize );
|
|
recsize += numsize;
|
|
|
|
num++;
|
|
|
|
} /* end for */
|
|
|
|
if( num > 0 ) {
|
|
omf_InitRec( &obj, CMD_COMDEF );
|
|
obj.d.comdef.first_idx = start; /* unused */
|
|
AttachData( &obj, (uint_8 *)data, recsize );
|
|
obj.d.comdef.num_names = num; /* unused */
|
|
omf_write_record( &obj );
|
|
start += num;
|
|
}
|
|
}
|
|
DebugMsg1(("omf_write_comdef exit\n"));
|
|
return( index );
|
|
}
|
|
|
|
static time_t GetFileTimeStamp( const char *filename )
|
|
/****************************************************/
|
|
{
|
|
struct _stat statbuf;
|
|
|
|
if( _stat( filename, &statbuf ) != 0 ) {
|
|
DebugMsg(("GetFileTimeStamp(%s): _stat() did return 0\n", filename ));
|
|
return( 0 );
|
|
}
|
|
return( statbuf.st_mtime );
|
|
}
|
|
|
|
/*
|
|
* write COMENT dependency records (CMT_DEPENDENCY) for debugging
|
|
* if line numbers are on; this is a Borland/OW thing.
|
|
*/
|
|
static ret_code omf_write_autodep( void )
|
|
/***************************************/
|
|
{
|
|
struct omf_rec obj;
|
|
struct fname_item *curr;
|
|
char *p = StringBufferEnd;
|
|
unsigned int len;
|
|
unsigned idx;
|
|
|
|
DebugMsg(("omf_write_autodep() enter\n"));
|
|
for( idx = 0, curr = ModuleInfo.g.FNames; idx < ModuleInfo.g.cnt_fnames; idx++, curr++ ) {
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = CMT_TNP;
|
|
obj.d.coment.cmt_class = CMT_DEPENDENCY; /* 0xE9 */
|
|
|
|
len = strlen( curr->fname );
|
|
#if MAX_STRING_LEN > 255
|
|
if ( len > 255 )
|
|
len = 255; /* length is 1 byte only */
|
|
#endif
|
|
/* v2.11: field mtime removed, timestamp read when needed */
|
|
//*((time_t *)p) = timet2dostime( curr->mtime );
|
|
*((time_t *)p) = timet2dostime( GetFileTimeStamp( curr->fname ) );
|
|
*(p + 4) = (unsigned char)len;
|
|
memcpy( p + 5, curr->fname, len );
|
|
AttachData( &obj, (uint_8 *)p, len + 5 );
|
|
DebugMsg(("omf_write_autodep(): write record: file=%s, time=%X\n", curr->fname, *((time_t *)p) ));
|
|
omf_write_record( &obj );
|
|
}
|
|
/* one NULL dependency record must be on the end */
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = CMT_TNP;
|
|
obj.d.coment.cmt_class = CMT_DEPENDENCY;
|
|
AttachData( &obj, (uint_8 *)"", 0 );
|
|
omf_write_record( &obj );
|
|
DebugMsg(("omf_write_autodep() exit\n"));
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
static void omf_write_alias( void )
|
|
/*********************************/
|
|
{
|
|
struct omf_rec obj;
|
|
char *p;
|
|
uint_8 len1;
|
|
uint_8 len2;
|
|
//bool first = TRUE;
|
|
struct dsym *curr;
|
|
char tmp[MAX_ID_LEN + MANGLE_BYTES + 1];
|
|
unsigned char buff[2*MAX_ID_LEN_OMF + 2];
|
|
|
|
for( curr = SymTables[TAB_ALIAS].head; curr; curr = curr->next ) {
|
|
|
|
/* output an alias record for this alias */
|
|
/* v2.10: use the decorated names */
|
|
//len1 = curr->sym.name_size;
|
|
len1 = Mangle( &curr->sym, tmp );
|
|
#if MAX_ID_LEN > MAX_ID_LEN_OMF
|
|
if ( len1 > MAX_ID_LEN_OMF )
|
|
len1 = MAX_ID_LEN_OMF;
|
|
#endif
|
|
p = buff;
|
|
*p++ = len1;
|
|
memcpy( p, tmp, len1 );
|
|
p += len1;
|
|
|
|
//len2 = curr->sym.substitute->name_size;
|
|
len2 = Mangle( curr->sym.substitute, tmp );
|
|
#if MAX_ID_LEN > MAX_ID_LEN_OMF
|
|
if ( len2 > MAX_ID_LEN_OMF )
|
|
len2 = MAX_ID_LEN_OMF;
|
|
#endif
|
|
*p++ = len2;
|
|
memcpy( p, tmp, len2 );
|
|
|
|
omf_InitRec( &obj, CMD_ALIAS );
|
|
AttachData( &obj, buff, len1 + len2 + 2 );
|
|
omf_write_record( &obj );
|
|
//first = FALSE;
|
|
}
|
|
}
|
|
|
|
/* the PUBDEF record consists of:
|
|
* - base_info ( segment, group, frame ), max size is 4
|
|
* - items
|
|
* - 1 byte name len
|
|
* - name[]
|
|
* - offset ( 2/4 bytes )
|
|
* - type ( index field, here always 0 )
|
|
*/
|
|
static ret_code omf_write_pubdef( void )
|
|
/**************************************/
|
|
{
|
|
struct qnode *q;
|
|
|
|
DebugMsg1(("omf_write_pub enter\n"));
|
|
|
|
/* v2.07: struct pubdef_data has been modified to match
|
|
* the data to be written to the object module more closely.
|
|
* This fixed a possible overrun if too many publics were written.
|
|
* v2.11: now the data matches exactly the OMF PUBDEF record
|
|
* and is just attached.
|
|
*/
|
|
|
|
q = ModuleInfo.g.PubQueue.head;
|
|
while ( q ) {
|
|
struct asym *curr_seg;
|
|
uint_8 *data;
|
|
unsigned size;
|
|
uint_8 curr32;
|
|
uint_8 is32;
|
|
|
|
for ( size = 0, data = StringBufferEnd; q; q = q->next ) {
|
|
unsigned recsize;
|
|
unsigned len;
|
|
struct asym *sym;
|
|
sym = q->sym;
|
|
#if COMDATSUPP
|
|
/* COMDAT symbol? Then write an LNAME record */
|
|
if ( sym->segment && ((struct dsym *)sym->segment)->e.seginfo->comdat_selection ) {
|
|
struct dsym *seg = (struct dsym *)sym->segment;
|
|
if ( seg->e.seginfo->comdat_idx == 0 ) {
|
|
struct omf_rec obj;
|
|
seg->e.seginfo->comdat_idx = ++startitem;
|
|
seg->sym.used = sym->used;
|
|
omf_InitRec( &obj, CMD_LNAMES );
|
|
len = Mangle( sym, StringBufferEnd + 1 );
|
|
*StringBufferEnd = len;
|
|
if ( ModuleInfo.case_sensitive == FALSE )
|
|
_strupr( StringBufferEnd + 1 );
|
|
AttachData( &obj, StringBufferEnd, len + 1 );
|
|
omf_write_record( &obj );
|
|
} else if ( Parse_Pass == PASS_1 ) {
|
|
/* ??? */
|
|
}
|
|
continue;
|
|
}
|
|
#endif
|
|
/* for constants, Masm checks if the value will fit in a 16-bit field,
|
|
* either signed ( -32768 ... 32767 ) or unsigned ( 0 ... 65635 ).
|
|
* As a result, the following code:
|
|
* E1 equ 32768
|
|
* E2 equ -32768
|
|
* PUBLIC E1, E2
|
|
* will store both equates with value 8000h in the 16-bit PUBDEF record!!!
|
|
* JWasm behaves differently, resulting in negative values to be stored as 32-bit.
|
|
*/
|
|
#if 1
|
|
is32 = ( sym->offset > 0xffffUL ? 1 : 0 );
|
|
#else
|
|
is32 = ( sym->offset > 0xffff || sym->offset < -32768 ? 1 : 0 ); /* this is what Masm does */
|
|
#endif
|
|
/* check if public fits in current record yet.
|
|
* 4 bytes omf record overhead, 4 for base info, 1+1+4/2 for name_size, type & offset
|
|
*/
|
|
recsize = size + sym->name_size + MANGLE_BYTES + 4+4+1+1+( is32 ? 4 : 2 );
|
|
/* exit loop if segment or offset magnitude changes, or record becomes too big */
|
|
if( size && ( sym->segment != curr_seg || is32 != curr32 || recsize > MAX_PUB_LENGTH ) )
|
|
break;
|
|
len = Mangle( sym, data + 1 );
|
|
#if MAX_ID_LEN > MAX_ID_LEN_OMF
|
|
if ( len > 255 )
|
|
len = 255; /* length is 1 byte only */
|
|
#endif
|
|
if ( ModuleInfo.convert_uppercase )
|
|
_strupr( data + 1 );
|
|
curr_seg = sym->segment;
|
|
curr32 = is32;
|
|
|
|
*data = len;
|
|
data += len + 1;
|
|
if ( curr32 ) {
|
|
*(uint_32 *)data = sym->offset;
|
|
data += sizeof( uint_32 );
|
|
} else {
|
|
*(uint_16 *)data = sym->offset;
|
|
data += sizeof( uint_16 );
|
|
}
|
|
*data++ = 0; /* type field */
|
|
size = (char *)data - StringBufferEnd;
|
|
}
|
|
if ( size ) {
|
|
struct omf_rec obj;
|
|
omf_InitRec( &obj, CMD_PUBDEF );
|
|
AttachData( &obj, StringBufferEnd, size );
|
|
obj.is_32 = curr32;
|
|
if( curr_seg == NULL ) { /* absolute symbol, no segment */
|
|
obj.d.pubdef.base.grp_idx = 0;
|
|
obj.d.pubdef.base.seg_idx = 0;
|
|
} else {
|
|
obj.d.pubdef.base.seg_idx = GetSegIdx( curr_seg );
|
|
obj.d.pubdef.base.grp_idx = omf_GetGrpIdx( GetGroup( curr_seg ) );
|
|
}
|
|
obj.d.pubdef.base.frame = 0;
|
|
omf_write_record( &obj );
|
|
}
|
|
}
|
|
|
|
DebugMsg1(("omf_write_pub exit\n"));
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
static void omf_write_modend( struct fixup *fixup, uint_32 displ )
|
|
/****************************************************************/
|
|
{
|
|
struct omf_rec obj;
|
|
uint_8 buffer[FIX_GEN_MODEND_MAX];
|
|
|
|
DebugMsg(("omf_write_modend( fixup=%p, displ=%" I32_SPEC "X)\n", fixup, displ ));
|
|
|
|
omf_InitRec( &obj, CMD_MODEND );
|
|
|
|
if( fixup == NULL ) {
|
|
obj.d.modend.main_module = FALSE;
|
|
obj.d.modend.start_addrs = FALSE;
|
|
} else {
|
|
obj.d.modend.start_addrs = TRUE;
|
|
obj.d.modend.main_module = TRUE;
|
|
obj.is_32 = ( GetSymOfssize( fixup->sym ) > USE16 ? 1 : 0 ); /* USE16 or USE32 */
|
|
AttachData( &obj, buffer, 0 );
|
|
obj.length = OmfFixGenFixModend( fixup, buffer, displ, obj.is_32 ? FIX_GEN_MS386 : FIX_GEN_INTEL );
|
|
}
|
|
omf_write_record( &obj );
|
|
}
|
|
|
|
/* this callback function is called during codeview debug info generation */
|
|
|
|
static uint_8 *omf_cv_flushfunc( struct dsym *seg, uint_8 *curr, unsigned size, void *pv )
|
|
/****************************************************************************************/
|
|
{
|
|
uint_8 *buffer = seg->e.seginfo->CodeBuffer;
|
|
|
|
if ( ( curr - buffer ) && ( ( curr - buffer ) + size ) > ( 1024 - 8 ) ) {
|
|
seg->e.seginfo->current_loc = seg->e.seginfo->start_loc + ( curr - buffer );
|
|
omf_write_ledata( seg );
|
|
return( buffer );
|
|
}
|
|
return( curr );
|
|
}
|
|
|
|
/*
|
|
* If -Zi is set, a comment class
|
|
* A1 record (MS extensions present) is written.
|
|
*
|
|
* Additionally, segments $$SYMBOLS, $$TYPES are added to the segment table
|
|
*/
|
|
static void omf_write_header_dbgcv( void )
|
|
/****************************************/
|
|
{
|
|
int i;
|
|
struct omf_rec obj;
|
|
|
|
omf_InitRec( &obj, CMD_COMENT );
|
|
obj.d.coment.attr = 0x00;
|
|
obj.d.coment.cmt_class = CMT_MS_OMF; /* MS extensions present */
|
|
AttachData( &obj, "\001CV", 3 );
|
|
omf_write_record( &obj );
|
|
for ( i = 0; i < DBGS_MAX; i++ ) {
|
|
if ( SymDebSeg[i] = (struct dsym *)CreateIntSegment( SymDebParm[i].name, SymDebParm[i].cname, 0, USE32, TRUE ) ) {
|
|
SymDebSeg[i]->e.seginfo->force32 = TRUE; /* without this a 32-bit segdef is emitted only if segsize > 64kB */
|
|
SymDebSeg[i]->e.seginfo->flushfunc = omf_cv_flushfunc;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* write contents of segments $$SYMBOLS and $$TYPES */
|
|
|
|
static void omf_write_debug_tables( void )
|
|
/****************************************/
|
|
{
|
|
if ( SymDebSeg[DBGS_SYMBOLS] && SymDebSeg[DBGS_TYPES] ) {
|
|
SymDebSeg[DBGS_SYMBOLS]->e.seginfo->CodeBuffer = CurrSource;
|
|
SymDebSeg[DBGS_TYPES]->e.seginfo->CodeBuffer = CurrSource + 1024;
|
|
cv_write_debug_tables( SymDebSeg[DBGS_SYMBOLS], SymDebSeg[DBGS_TYPES], NULL );
|
|
}
|
|
}
|
|
|
|
/* write OMF module.
|
|
* this is called after the last pass.
|
|
* since the OMF records are written "on the fly",
|
|
* the "normal" section contents are already written at this time.
|
|
*/
|
|
|
|
static ret_code omf_write_module( struct module_info *modinfo )
|
|
/*************************************************************/
|
|
{
|
|
#if TRUNCATE
|
|
int fh;
|
|
uint_32 size;
|
|
#endif
|
|
/* -if Zi is set, write symbols and types */
|
|
if ( Options.debug_symbols )
|
|
omf_write_debug_tables();
|
|
omf_write_modend( modinfo->g.start_fixup, modinfo->g.start_displ );
|
|
#if FASTMEM==0
|
|
LclFree( modinfo->g.start_fixup );
|
|
#endif
|
|
|
|
#if TRUNCATE
|
|
/* under some very rare conditions, the object
|
|
* module might become shorter! Hence the file
|
|
* must be truncated now. The problem is that there
|
|
* is no stream function for this task.
|
|
* the final solution will be to save the segment contents
|
|
* in buffers and write the object module once everything
|
|
* is done ( as it is done for the other formats already).
|
|
* v2.03: most likely no longer necessary, since the file
|
|
* won't become shorter anymore.
|
|
*/
|
|
size = ftell( CurrFile[OBJ] );
|
|
#if defined(__UNIX__) || defined(__CYGWIN__) || defined(__DJGPP__)
|
|
fh = fileno( CurrFile[OBJ] );
|
|
if ( ftruncate( fh, size ) ); /* gcc warns if return value of ftruncate() is "ignored" */
|
|
#elif defined(__BORLANDC__)
|
|
fh = _fileno( CurrFile[OBJ] );
|
|
chsize( fh, size );
|
|
#else
|
|
fh = _fileno( CurrFile[OBJ] );
|
|
_chsize( fh, size );
|
|
#endif
|
|
|
|
#endif
|
|
|
|
/* write SEGDEF records. Since these records contain the segment's length,
|
|
* the records have to be written again after the final assembly pass.
|
|
*/
|
|
fseek( CurrFile[OBJ] , seg_pos, SEEK_SET );
|
|
omf_write_segdef();
|
|
/* write PUBDEF records. Since the final value of offsets isn't known after
|
|
* the first pass, this has to be called again after the final pass.
|
|
*/
|
|
fseek( CurrFile[OBJ], public_pos, SEEK_SET);
|
|
omf_write_pubdef();
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* write OMF header info after pass 1 */
|
|
|
|
static ret_code omf_write_header_initial( struct module_info *modinfo )
|
|
/*********************************************************************/
|
|
{
|
|
uint_16 ext_idx;
|
|
|
|
if ( write_to_file == FALSE )
|
|
return( NOT_ERROR );
|
|
|
|
omf_write_theadr( CurrFName[ASM] ); /* write THEADR record, main src filename */
|
|
/* v2.11: coment record "ms extensions present" now written here */
|
|
if ( Options.debug_symbols ) /* -Zi option set? */
|
|
omf_write_header_dbgcv();
|
|
/* if( Options.no_dependencies == FALSE ) */
|
|
if( Options.line_numbers )
|
|
omf_write_autodep(); /* write dependency COMENT records ( known by Borland & OW ) */
|
|
if( ModuleInfo.segorder == SEGORDER_DOSSEG )
|
|
omf_write_dosseg(); /* write dosseg COMENT records */
|
|
else if( ModuleInfo.segorder == SEGORDER_ALPHA )
|
|
SortSegments( 1 );
|
|
omf_write_lib(); /* write default lib COMENT records */
|
|
omf_write_lnames(); /* write LNAMES records */
|
|
|
|
/* write SEGDEF records. Since these records contain the segment's length,
|
|
* the records have to be written again after the final assembly pass.
|
|
* hence the start position of those records has to be saved.
|
|
*/
|
|
seg_pos = ftell( CurrFile[OBJ] );
|
|
omf_write_segdef();
|
|
omf_write_grpdef(); /* write GRPDEF records */
|
|
ext_idx = omf_write_extdef(); /* write EXTDEF records */
|
|
startext = omf_write_comdef( ext_idx ); /* write COMDEF records */
|
|
omf_write_alias(); /* write ALIAS records */
|
|
|
|
/* write PUBDEF records. Since the final value of offsets isn't known after
|
|
* the first pass, this has to be called again after the final pass.
|
|
*/
|
|
public_pos = ftell( CurrFile[OBJ] );
|
|
omf_write_pubdef();
|
|
omf_write_export(); /* write export COMENT records */
|
|
|
|
/* (optionally) write end-of-pass-one COMENT record
|
|
* v2.10: don't write record if starting address is present.
|
|
* the TIS OMF spec v1.1. warns that this
|
|
* comment record is NOT to be present if
|
|
* the MODEND record contains a starting address!
|
|
*/
|
|
if ( !ModuleInfo.g.start_fixup )
|
|
omf_end_of_pass1();
|
|
end_of_header = ftell( CurrFile[OBJ] );
|
|
return( NOT_ERROR );
|
|
}
|
|
|
|
/* init. called once per module */
|
|
|
|
void omf_init( struct module_info *modinfo )
|
|
/******************************************/
|
|
{
|
|
DebugMsg(("omf_init enter\n"));
|
|
modinfo->g.WriteModule = omf_write_module;
|
|
modinfo->g.Pass1Checks = omf_write_header_initial;
|
|
SymDebSeg[DBGS_SYMBOLS] = NULL;
|
|
SymDebSeg[DBGS_TYPES] = NULL;
|
|
#if MULTIHDR
|
|
ln_srcfile = modinfo->srcfile;
|
|
#endif
|
|
ln_size = 0;
|
|
return;
|
|
}
|