jwasm/omffixup.c
2014-06-18 19:16:56 +04:00

437 lines
16 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 fixups
*
****************************************************************************/
#include <stddef.h>
#include "globals.h"
#include "parser.h"
#include "segment.h"
#include "fixup.h"
#include "omfint.h"
#include "omfspec.h"
#include "myassert.h"
extern const char szNull[];
extern unsigned omf_GetGrpIdx( struct asym *sym );
/* logical data for fixup subrecord creation */
struct logref {
uint_8 frame; /* see enum frame_methods in omfspec.h */
uint_16 frame_datum; /* datum for certain frame methods */
uint_8 is_secondary; /* can write target in a secondary manner */
uint_8 target; /* see enum target_methods in omfspec.h */
uint_16 target_datum; /* datum for certain target methods */
int_32 target_offset; /* offset of target for target method */
};
static uint_8 *putIndex( uint_8 *p, uint_16 index )
/*************************************************/
{
if( index > 0x7f ) {
*p++ = 0x80 | ( index >> 8 );
}
*p++ = index;
return( p );
}
static uint_8 *put16( uint_8 *p, uint_16 value )
/**********************************************/
{
WriteU16( p, value );
return( p + sizeof( uint_16 ) );
}
static uint_8 *put32( uint_8 *p, uint_32 value )
/**********************************************/
{
WriteU32( p, value );
return( p + sizeof( uint_32 ) );
}
static uint_8 *putFrameDatum( uint_8 *p, uint_8 method, uint_16 datum )
/*********************************************************************/
{
switch( method ) {
case FRAME_SEG:
case FRAME_GRP:
case FRAME_EXT:
return( putIndex( p, datum ) );
#if 0 /* v2.12: FRAME_ABS is invalid according to TIS OMF docs. */
case FRAME_ABS:
return( put16( p, datum ) );
#endif
}
/* for FRAME_LOC & FRAME_TARG ( & FRAME_NONE ) there's no datum to write. */
return( p );
}
static uint_8 *putTargetDatum( uint_8 *p, uint_8 method, uint_16 datum )
/**********************************************************************/
{
#if 0 /* v2.12: JWasm won't use TARGE_ABSxx; also, it's not defined for FIXUP sub-records */
if( ( method & 0x03 ) == TARGET_ABSWD ) {
return( put16( p, datum ) );
}
#endif
return( putIndex( p, datum ) );
}
/* translate logref to FIXUP subrecord ( without Locat field ).
* fields written to buf:
* - uint_8 Fix Data (type of frame and target)
* - index Frame Datum (optional, index of a SEGDEF, GRPDEF or EXTDEF)
* - index Target Datum (index of a SEGDEF, GRPDEF or EXTDEF)
* - uint_16/32 Target Displacement (optional)
*
* type is FIX_GEN_INTEL or FIX_GEN_MS386
* v2.12: new function OmfFixGetFixModend() replaced code in writeModend(),
* TranslateLogref() has become static.
*/
static unsigned TranslateLogref( const struct logref *lr, uint_8 *buf, enum fixgen_types type )
/*********************************************************************************************/
{
uint_8 *p;
uint_8 target;
/**/myassert( lr != NULL );
/**/myassert( buf != NULL );
/**/myassert( type == FIX_GEN_INTEL || type == FIX_GEN_MS386 );
/*
* According to the discussion on p102 of the Intel OMF document, we
* cannot just arbitrarily write fixups without a displacment if their
* displacement field is 0. So we use the is_secondary field.
*/
target = lr->target;
if( lr->target_offset == 0 && lr->is_secondary ) {
target |= 0x04; /* P=1 -> no displacement field */
}
p = buf;
/* write the "Fix Data" field, FfffTPtt:
* F : 0 = frame method is defined in fff field ( F0-F5)
* 1 = frame is defined by a thread ( won't occur here )
* fff: frame method
* T : 0 = target is defined by tt
* 1 = target is defined by thread# in tt, P is used as bit 2 for method
* P : 0 = target displacement field is present
* 1 = no displacement field
* tt : target method
*/
*p++ = ( lr->frame << 4 ) | ( target );
p = putFrameDatum( p, lr->frame, lr->frame_datum );
p = putTargetDatum( p, target, lr->target_datum );
if( ( target & 0x04 ) == 0 ) {
if( type == FIX_GEN_MS386 ) {
p = put32( p, (uint_32)lr->target_offset );
} else {
p = put16( p, (uint_16)lr->target_offset );
}
}
return( p - buf );
}
#if 0 /* v2.11: obsolete */
static uint TranslatePhysref( const struct physref *ref, uint_8 *buf, enum fixgen_types type )
/********************************************************************************************/
{
uint_8 *p;
/**/myassert( ref != NULL );
/**/myassert( buf != NULL );
/**/myassert( type == FIX_GEN_INTEL || type == FIX_GEN_MS386 );
p = put16( buf, ref->frame );
p = put16( p, ref->offset );
return( p - buf );
}
/* used when the MODEND record is written.
* is_logical is always 1 then.
* v2.11: obsolete. TranslateLogref() is now called directly by writeModend() in omfint.c
*/
unsigned TranslateRef( const union logphys *ref, int is_logical, uint_8 *buf, enum fixgen_types type )
/****************************************************************************************************/
{
return( is_logical ? TranslateLogref( &ref->log, buf, type ) : TranslatePhysref( &ref->phys, buf, type ) );
}
#endif
/* generate start address subfield for MODEND */
unsigned OmfFixGenFixModend( const struct fixup *fixup, uint_8 *buf, uint_32 displ, enum fixgen_types type )
/**********************************************************************************************************/
{
struct asym *sym = fixup->sym;
struct logref lr;
lr.is_secondary = FALSE;
lr.target_offset = sym->offset + displ;
lr.frame_datum = fixup->frame_datum;
/* symbol is always a code label (near or far), internal or external */
/* now set Target and Frame */
if( sym->state == SYM_EXTERNAL ) {
DebugMsg(("omf_write_modend(%p): fixup->frame_type/datum=%u/%u, EXTERNAL sym=%s\n",
fixup, fixup->frame_type, fixup->frame_datum, sym->name));
lr.target = TARGET_EXT & TARGET_WITH_DISPL;
lr.target_datum = sym->ext_idx1;
if( fixup->frame_type == FRAME_GRP && fixup->frame_datum == 0 ) {
/* set the frame to the frame of the corresponding segment */
lr.frame_datum = omf_GetGrpIdx( sym );
}
} else { /* SYM_INTERNAL */
DebugMsg(("OmfFixGenFixModend(%p): fixup->frame_type/datum=%u/%u sym->name=%s state=%X segm=%s\n",
fixup, fixup->frame_type, fixup->frame_datum, sym->name, sym->state, sym->segment ? sym->segment->name : "NULL" ));
/**/myassert( sym->state == SYM_INTERNAL );
lr.target = TARGET_SEG & TARGET_WITH_DISPL;
lr.target_datum = GetSegIdx( sym->segment );
}
if( fixup->frame_type != FRAME_NONE && fixup->frame_type != FRAME_SEG ) {
lr.frame = (uint_8)fixup->frame_type;
} else {
lr.frame = FRAME_TARG;
}
return( TranslateLogref( &lr, buf, type ) );
}
/* fill a logref from a fixup's info */
static int omf_fill_logref( const struct fixup *fixup, struct logref *lr )
/************************************************************************/
{
struct asym *sym;
sym = fixup->sym; /* may be NULL! */
DebugMsg1(("omf_fill_logref: sym=%s, state=%d, fixup->type=%u\n",
sym ? sym->name : "NULL", sym ? sym->state : -1, fixup->type ));
/*------------------------------------*/
/* Determine the Target and the Frame */
/*------------------------------------*/
if( sym == NULL ) {
DebugMsg(("omf_fill_logref: sym is NULL, frame_type=%u\n", fixup->frame_type ));
if ( fixup->frame_type == FRAME_NONE ) /* v1.96: nothing to do without a frame */
return( 0 );
lr->target = fixup->frame_type;
lr->target_datum = fixup->frame_datum;
lr->frame = FRAME_TARG;
} else if( sym->state == SYM_UNDEFINED ) { /* shouldn't happen */
DebugMsg(("omf_fill_logref: sym->state is SYM_UNDEFINED\n" ));
EmitErr( SYMBOL_NOT_DEFINED, sym->name );
return( 0 );
} else if( sym->state == SYM_GRP ) {
DebugMsg1(("omf_fill_logref: sym->state is SYM_GRP\n" ));
lr->target = TARGET_GRP;
lr->target_datum = ((struct dsym *)sym)->e.grpinfo->grp_idx;
if( fixup->frame_type != FRAME_NONE ) {
lr->frame = fixup->frame_type;
lr->frame_datum = fixup->frame_datum;
} else {
lr->frame = FRAME_GRP;
lr->frame_datum = lr->target_datum;
}
} else if( sym->state == SYM_SEG ) {
DebugMsg1(("omf_fill_logref: sym->state is SYM_SEG %s\n" ));
lr->target = TARGET_SEG;
lr->target_datum = GetSegIdx( sym );
if( fixup->frame_type != FRAME_NONE ) {
lr->frame = fixup->frame_type;
lr->frame_datum = fixup->frame_datum;
} else {
lr->frame = FRAME_SEG;
lr->frame_datum = lr->target_datum;
}
} else {
/* symbol is a label */
lr->frame_datum = fixup->frame_datum;
if( sym->state == SYM_EXTERNAL ) {
DebugMsg1(("omf_fill_logref: sym->state is SYM_EXTERNAL, fixup->frame_type/datum=%u/%u\n",
fixup->frame_type, fixup->frame_datum ));
lr->target = TARGET_EXT;
lr->target_datum = sym->ext_idx1;
if( fixup->frame_type == FRAME_GRP && fixup->frame_datum == 0 ) {
/* set the frame to the frame of the corresponding segment */
lr->frame_datum = omf_GetGrpIdx( sym );
}
} else {
/* must be SYM_INTERNAL */
/**/myassert( sym->state == SYM_INTERNAL );
DebugMsg1(("omf_fill_logref: sym->state is SYM_INTERNAL, sym->segment=%s, fixup->frame/datum=%u/%u\n",
sym->segment ? sym->segment->name : "NULL", fixup->frame_type, fixup->frame_datum ));
/* v2.08: don't use info from assembly-time variables */
if ( sym->variable ) {
lr->target = ( fixup->frame_type == FRAME_GRP ? TARGET_GRP : TARGET_SEG );
lr->target_datum = fixup->frame_datum;
} else if ( sym->segment == NULL ) { /* shouldn't happen */
EmitErr( SEGMENT_MISSING_FOR_FIXUP, sym->name );
return ( 0 );
#if COMDATSUPP
} else if ( ( (struct dsym *)sym->segment)->e.seginfo->comdat_selection ) {
lr->target = TARGET_EXT;
lr->target_datum = ((struct dsym *)sym->segment)->e.seginfo->seg_idx;
lr->frame = FRAME_TARG;
return( 1 );
#endif
} else {
lr->target = TARGET_SEG;
lr->target_datum = GetSegIdx( sym->segment );
}
}
if( fixup->frame_type != FRAME_NONE ) {
lr->frame = (uint_8)fixup->frame_type;
} else {
lr->frame = FRAME_TARG;
}
}
/*--------------------*/
/* Optimize the fixup */
/*--------------------*/
if( lr->frame == ( lr->target - TARGET_SEG ) ) {
lr->frame = FRAME_TARG;
}
return( 1 );
}
/* translate a fixup into its binary representation,
* which is a "FIXUPP subrecord" according to OMF docs.
* structure:
* - WORD, Locat: 1MLLLLDD DDDDDDDD, is
* 1 = indicates FIXUP, no THREAD subrecord
* M = mode: 1=segment relative, 0=self relative
* L = location, see LOC_ entries in omfspec.h
* D = data record offset, 10 bits for range 0-3FFh
* - BYTE, Fix Data: FRRRTPGG, is
* F = 0=frame defined in fixup, 1=frame defined in frame thread
* R = Frame
* T = 0=target defined in fixup, 1=Target defined in target thread
* P = 0=target displacement is present
* G = lower bits of target method (F=0), target thread number (F=1)
* - void/BYTE/WORD, Frame Datum
* - BYTE/WORD, Target Datum
* - WORD/DWORD, Target Displacement
*/
unsigned OmfFixGenFix( const struct fixup *fixup, uint_32 start_loc, uint_8 *buf, enum fixgen_types type )
/********************************************************************************************************/
{
uint_8 locat1;
uint_8 self_relative = FALSE;
unsigned data_rec_offset;
struct logref lr;
/**/myassert( fixup != NULL );
/**/myassert( buf != NULL );
/**/myassert( type == FIX_GEN_INTEL || type == FIX_GEN_MS386 );
lr.is_secondary = TRUE;
lr.target_offset = 0;
switch( fixup->type ) {
case FIX_RELOFF8:
self_relative = TRUE;
/* no break */
case FIX_OFF8:
locat1 = ( LOC_OFFSET_LO << 2 );
break;
case FIX_RELOFF16:
self_relative = TRUE;
/* no break */
case FIX_OFF16:
locat1 = ( fixup->loader_resolved ? LOC_MS_LINK_OFFSET << 2 : LOC_OFFSET << 2 );
break;
case FIX_RELOFF32:
self_relative = TRUE;
/* no break */
case FIX_OFF32:
locat1 = ( fixup->loader_resolved ? LOC_MS_LINK_OFFSET_32 << 2 : LOC_MS_OFFSET_32 << 2 );
break;
case FIX_HIBYTE:
locat1 = ( LOC_OFFSET_HI << 2 );
break;
case FIX_SEG:
locat1 = ( LOC_BASE << 2 );
break;
case FIX_PTR16:
locat1 = ( LOC_BASE_OFFSET << 2 );
break;
case FIX_PTR32:
locat1 = ( LOC_MS_BASE_OFFSET_32 << 2 );
break;
default: /* shouldn't happen. Check for valid fixup has already happened */
EmitErr( UNSUPPORTED_FIXUP_TYPE,
ModuleInfo.fmtopt->formatname,
fixup->sym ? fixup->sym->name : szNull );
return( 0 );
}
locat1 |= self_relative ? 0x80 : 0xc0; /* bit 7: 1=is a fixup subrecord */
if ( omf_fill_logref( fixup, &lr ) == 0 )
return( 0 );
/* magnitude of fixup's position is 10! */
/**/myassert( fixup->locofs - start_loc < 1024 );
/* calculate the fixup's position in current LEDATA */
data_rec_offset = fixup->locofs - start_loc;
locat1 |= data_rec_offset >> 8;
*buf = locat1;
*(buf+1) = (uint_8)data_rec_offset;
return( 2 + TranslateLogref( &lr, buf+2, type ) );
}