Files
ldc/backend/cgobj.c
Alexey Prokhin caad8cde58 Squashed 'dmd2/' content from commit 10017d5
git-subtree-dir: dmd2
git-subtree-split: 10017d50eaaff4ecdc37a0153b6c37ea0b004c81
2012-04-05 11:10:48 +04:00

3681 lines
102 KiB
C

// Copyright (C) 1984-1998 by Symantec
// Copyright (C) 2000-2011 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
// Written by Walter Bright
/*
* This source file is made available for personal use
* only. The license is in /dmd/src/dmd/backendlicense.txt
* or /dm/src/dmd/backendlicense.txt
* For any other uses, please contact Digital Mars.
*/
#if !HTOD && (SCPP || MARS)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <ctype.h>
#include <direct.h>
#include "filespec.h"
#include "cc.h"
#include "global.h"
#include "cgcv.h"
#include "code.h"
#include "type.h"
#include "outbuf.h"
#include "md5.h"
#if MARS
struct Loc
{
char *filename;
unsigned linnum;
Loc(int x)
{
linnum = x;
filename = NULL;
}
};
void error(Loc loc, const char *format, ...);
#endif
#if OMFOBJ
static char __file__[] = __FILE__; // for tassert.h
#include "tassert.h"
#define MULTISCOPE 1 /* account for bug in MultiScope debugger
where it cannot handle a line number
with multiple offsets. We use a bit vector
to filter out the extra offsets.
*/
#define TOOFFSET(a,b) (I32 ? TOLONG(a,b) : TOWORD(a,b))
/**************************
* Record types:
*/
#define RHEADR 0x6E
#define REGINT 0x70
#define REDATA 0x72
#define RIDATA 0x74
#define OVLDEF 0x76
#define ENDREC 0x78
#define BLKDEF 0x7A
#define BLKEND 0x7C
//#define DEBSYM 0x7E
#define THEADR 0x80
#define LHEADR 0x82
#define PEDATA 0x84
#define PIDATA 0x86
#define COMENT 0x88
#define MODEND 0x8A
#define EXTDEF 0x8C
#define TYPDEF 0x8E
#define PUBDEF 0x90
#define PUB386 0x91
#define LOCSYM 0x92
#define LINNUM 0x94
#define LNAMES 0x96
#define SEGDEF 0x98
#define SEG386 0x99
#define GRPDEF 0x9A
#define FIXUPP 0x9C
#define FIX386 0x9D
#define LEDATA 0xA0
#define LED386 0xA1
#define LIDATA 0xA2
#define LID386 0xA3
#define LIBHED 0xA4
#define LIBNAM 0xA6
#define LIBLOC 0xA8
#define LIBDIC 0xAA
#define COMDEF 0xB0
#define LEXTDEF 0xB4
#define LPUBDEF 0xB6
#define LCOMDEF 0xB8
#define CEXTDEF 0xBC
#define COMDAT 0xC2
#define LINSYM 0xC4
#define ALIAS 0xC6
#define LLNAMES 0xCA
// Some definitions for .OBJ files. Trial and error to determine which
// one to use when. Page #s refer to Intel spec on .OBJ files.
// Values for LOCAT byte: (pg. 71)
#define LOCATselfrel 0x8000
#define LOCATsegrel 0xC000
// OR'd with one of the following:
#define LOClobyte 0x0000
#define LOCbase 0x0800
#define LOChibyte 0x1000
#define LOCloader_resolved 0x1400
// Unfortunately, the fixup stuff is different for EASY OMF and Microsoft
#define EASY_LOCoffset 0x1400 // 32 bit offset
#define EASY_LOCpointer 0x1800 // 48 bit seg/offset
#define LOC32offset 0x2400
#define LOC32tlsoffset 0x2800
#define LOC32pointer 0x2C00
#define LOC16offset 0x0400
#define LOC16pointer 0x0C00
#define LOCxx 0x3C00
// FDxxxx are constants for the FIXDAT byte in fixup records (pg. 72)
#define FD_F0 0x00 // segment index
#define FD_F1 0x10 // group index
#define FD_F2 0x20 // external index
#define FD_F4 0x40 // canonic frame of LSEG that contains Location
#define FD_F5 0x50 // Target determines the frame
#define FD_T0 0 // segment index
#define FD_T1 1 // group index
#define FD_T2 2 // external index
#define FD_T4 4 // segment index, 0 displacement
#define FD_T5 5 // group index, 0 displacement
#define FD_T6 6 // external index, 0 displacement
/***************
* Fixup list.
*/
struct FIXUP
{
struct FIXUP *FUnext;
targ_size_t FUoffset; // offset from start of ledata
unsigned short FUlcfd; // LCxxxx | FDxxxx
unsigned short FUframedatum;
unsigned short FUtargetdatum;
};
#define list_fixup(fl) ((struct FIXUP *)list_ptr(fl))
#define seg_is_comdat(seg) ((seg) < 0)
/*****************************
* Ledata records
*/
#define LEDATAMAX (1024-14)
struct Ledatarec
{
char header[14]; // big enough to handle COMDAT header
char data[LEDATAMAX];
int lseg; // segment value
unsigned i; // number of bytes in data
targ_size_t offset; // segment offset of start of data
struct FIXUP *fixuplist; // fixups for this ledata
// For COMDATs
unsigned char flags; // flags byte of COMDAT
unsigned char alloctyp; // allocation type of COMDAT
unsigned char align; // align type
int typidx;
int pubbase;
int pubnamidx;
};
/*****************************
* For defining segments.
*/
#define SEG_ATTR(A,C,B,P) (((A) << 5) | ((C) << 2) | ((B) << 1) | (P))
// Segment alignment A
#define SEG_ALIGN0 0 // absolute segment
#define SEG_ALIGN1 1 // byte align
#define SEG_ALIGN2 2 // word align
#define SEG_ALIGN16 3 // paragraph align
#define SEG_ALIGN4K 4 // 4Kb page align
#define SEG_ALIGN4 5 // dword align
// Segment combine types C
#define SEG_C_ABS 0
#define SEG_C_PUBLIC 2
#define SEG_C_STACK 5
#define SEG_C_COMMON 6
// Segment type P
#define USE16 0
#define USE32 1
#define USE32_CODE (4+2) // use32 + execute/read
#define USE32_DATA (4+3) // use32 + read/write
/*****************************
* Line number support.
*/
#define LINNUMMAX 512
struct Linnum
{
#if MARS
const char *filename; // source file name
#else
Sfile *filptr; // file pointer
#endif
int cseg; // our internal segment number
int seg; // segment/public index
int i; // used in data[]
char data[LINNUMMAX]; // linnum/offset data
};
#define LINRECMAX (2 + 255 * 2) // room for 255 line numbers
/************************************
* State of object file.
*/
struct Objstate
{
const char *modname;
char *csegname;
Outbuffer *buf; // output buffer
int fdsegattr; // far data segment attribute
int csegattr; // code segment attribute
int lastfardatasegi; // SegData[] index of last far data seg
int LOCoffset;
int LOCpointer;
int mlidata;
int mpubdef;
int mfixupp;
int mmodend;
int lnameidx; // index of next LNAMES record
int segidx; // index of next SEGDEF record
int extidx; // index of next EXTDEF record
int pubnamidx; // index of COMDAT public name index
Symbol *startaddress; // if !NULL, then Symbol is start address
#ifdef DEBUG
int fixup_count;
#endif
size_t ledatai; // max index used in ledatas[]
// Line numbers
list_t linnum_list;
char *linrec; // line number record
unsigned linreci; // index of next avail in linrec[]
unsigned linrecheader; // size of line record header
unsigned linrecnum; // number of line record entries
list_t linreclist; // list of line records
int mlinnum;
int recseg;
int term;
#if MULTISCOPE
vec_t linvec; // bit vector of line numbers used
vec_t offvec; // and offsets used
#endif
int fisegi; // SegData[] index of FI segment
#if MARS
int fmsegi; // SegData[] of FM segment
#endif
int tlssegi; // SegData[] of tls segment
int fardataidx;
char pubdata[1024];
int pubdatai;
char extdata[1024];
int extdatai;
// For obj_far16thunk
int code16segi; // SegData[] index
targ_size_t CODE16offset;
int fltused;
int nullext;
};
Ledatarec **ledatas;
size_t ledatamax;
seg_data **SegData;
static int seg_count;
static int seg_max;
static Objstate obj;
STATIC void obj_defaultlib();
STATIC void objheader (char *csegname);
STATIC char * objmodtoseg (const char *modname);
STATIC void obj_browse_flush();
STATIC int obj_newfarseg (targ_size_t size,int);
STATIC void linnum_flush(void);
STATIC void linnum_term(void);
STATIC void objsegdef (int attr,targ_size_t size,int segnamidx,int classnamidx);
STATIC void obj_modend();
STATIC void objfixupp (struct FIXUP *);
STATIC void outextdata();
STATIC void outpubdata();
STATIC Ledatarec *ledata_new(int seg,targ_size_t offset);
char *id_compress(char *id, int idlen);
/*******************************
* Output an object file data record.
* Input:
* rectyp = record type
* record -> the data
* reclen = # of bytes in record
*/
void objrecord(unsigned rectyp,const char *record,unsigned reclen)
{ Outbuffer *o = obj.buf;
//printf("rectyp = x%x, record[0] = x%x, reclen = x%x\n",rectyp,record[0],reclen);
o->reserve(reclen + 4);
o->writeByten(rectyp);
o->writeWordn(reclen + 1); // record length includes checksum
o->writen(record,reclen);
o->writeByten(0); // use 0 for checksum
}
/**************************
* Insert an index number.
* Input:
* p -> where to put the 1 or 2 byte index
* index = the 15 bit index
* Returns:
* # of bytes stored
*/
extern void error(const char *filename, unsigned linnum, const char *format, ...);
extern void fatal();
void too_many_symbols()
{
#if SCPP
err_fatal(EM_too_many_symbols, 0x7FFF);
#else // MARS
error(NULL, 0, "more than %d symbols in object file", 0x7FFF);
fatal();
#endif
}
#if !DEBUG && TX86 && __INTSIZE == 4 && !defined(_MSC_VER)
__declspec(naked) int __pascal insidx(char *p,unsigned index)
{
#undef AL
#undef AH
#undef DL
#undef DH
_asm
{
mov EAX,index - 4[ESP]
mov ECX,p - 4[ESP]
cmp EAX,0x7F
jae L1
mov [ECX],AL
mov EAX,1
ret 8
L1:
cmp EAX,0x7FFF
ja L2
mov 1[ECX],AL
or EAX,0x8000
mov [ECX],AH
mov EAX,2
ret 8
}
L2:
too_many_symbols();
}
#else
__inline int insidx(char *p,unsigned index)
{
//if (index > 0x7FFF) printf("index = x%x\n",index);
/* OFM spec says it could be <=0x7F, but that seems to cause
* "library is corrupted" messages. Unverified. See Bugzilla 3601
*/
if (index < 0x7F)
{ *p = index;
return 1;
}
else if (index <= 0x7FFF)
{
*(p + 1) = index;
*p = (index >> 8) | 0x80;
return 2;
}
else
{ too_many_symbols();
return 0;
}
}
#endif
/**************************
* Insert a type index number.
* Input:
* p -> where to put the 1 or 2 byte index
* index = the 15 bit index
* Returns:
* # of bytes stored
*/
__inline int instypidx(char *p,unsigned index)
{
if (index <= 127)
{ *p = index;
return 1;
}
else if (index <= 0x7FFF)
{ *(p + 1) = index;
*p = (index >> 8) | 0x80;
return 2;
}
else // overflow
{ *p = 0; // the linker ignores this field anyway
return 1;
}
}
/****************************
* Read index.
*/
#define getindex(p) ((*(p) & 0x80) \
? ((*(unsigned char *)(p) & 0x7F) << 8) | *((unsigned char *)(p) + 1) \
: *(unsigned char *)(p))
/*****************************
* Returns:
* # of bytes stored
*/
#define ONS_OHD 4 // max # of extra bytes added by obj_namestring()
STATIC int obj_namestring(char *p,const char *name)
{ unsigned len;
len = strlen(name);
if (len > 255)
{ p[0] = 0xFF;
p[1] = 0;
#ifdef DEBUG
assert(len <= 0xFFFF);
#endif
TOWORD(p + 2,len);
memcpy(p + 4,name,len);
len += ONS_OHD;
}
else
{ p[0] = len;
memcpy(p + 1,name,len);
len++;
}
return len;
}
/******************************
* Allocate a new segment.
* Return index for the new segment.
*/
seg_data *getsegment()
{
int seg = ++seg_count;
if (seg_count == seg_max)
{
seg_max += 10;
SegData = (seg_data **)mem_realloc(SegData, seg_max * sizeof(seg_data *));
memset(&SegData[seg_count], 0, 10 * sizeof(seg_data *));
}
assert(seg_count < seg_max);
if (SegData[seg])
memset(SegData[seg], 0, sizeof(seg_data));
else
SegData[seg] = (seg_data *)mem_calloc(sizeof(seg_data));
seg_data *pseg = SegData[seg];
pseg->SDseg = seg;
pseg->segidx = 0;
return pseg;
}
/******************************
* Perform initialization that applies to all .obj output files.
* Input:
* filename source file name
* csegname code segment name (can be NULL)
*/
void obj_init(Outbuffer *objbuf, const char *filename, const char *csegname)
{
memset(&obj,0,sizeof(obj));
obj.buf = objbuf;
obj.buf->reserve(40000);
obj.lastfardatasegi = -1;
obj.mlidata = LIDATA;
obj.mpubdef = PUBDEF;
obj.mfixupp = FIXUPP;
obj.mmodend = MODEND;
obj.mlinnum = LINNUM;
// Reset for different OBJ file formats
if (I32)
{ if (config.flags & CFGeasyomf)
{ obj.LOCoffset = EASY_LOCoffset;
obj.LOCpointer = EASY_LOCpointer;
}
else
{
obj.mlidata = LID386;
obj.mpubdef = PUB386;
obj.mfixupp = FIX386;
obj.mmodend = MODEND + 1;
obj.LOCoffset = LOC32offset;
obj.LOCpointer = LOC32pointer;
}
obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
obj.csegattr = SEG_ATTR(SEG_ALIGN4, SEG_C_PUBLIC,0,USE32);
}
else
{
obj.LOCoffset = LOC16offset;
obj.LOCpointer = LOC16pointer;
obj.fdsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE16);
obj.csegattr = SEG_ATTR(SEG_ALIGN2, SEG_C_PUBLIC,0,USE16);
}
if (config.flags4 & CFG4speed && // if optimized for speed
config.target_cpu == TARGET_80486)
// 486 is only CPU that really benefits from alignment
obj.csegattr = I32 ? SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE32)
: SEG_ATTR(SEG_ALIGN16, SEG_C_PUBLIC,0,USE16);
if (!SegData)
{ seg_max = UDATA + 10;
SegData = (seg_data **)mem_calloc(seg_max * sizeof(seg_data *));
}
for (int i = 0; i < seg_max; i++)
{
if (SegData[i])
memset(SegData[i], 0, sizeof(seg_data));
else
SegData[i] = (seg_data *)mem_calloc(sizeof(seg_data));
}
SegData[CODE]->SDseg = CODE;
SegData[DATA]->SDseg = DATA;
SegData[CDATA]->SDseg = CDATA;
SegData[UDATA]->SDseg = UDATA;
SegData[CODE]->segidx = CODE;
SegData[DATA]->segidx = DATA;
SegData[CDATA]->segidx = CDATA;
SegData[UDATA]->segidx = UDATA;
seg_count = UDATA;
if (config.fulltypes)
{
SegData[DEBSYM]->SDseg = DEBSYM;
SegData[DEBTYP]->SDseg = DEBTYP;
SegData[DEBSYM]->segidx = DEBSYM;
SegData[DEBTYP]->segidx = DEBTYP;
seg_count = DEBTYP;
}
obj_theadr(filename);
obj.modname = filename;
if (!csegname || !*csegname) // if no code seg name supplied
obj.csegname = objmodtoseg(obj.modname); // generate one
else
obj.csegname = mem_strdup(csegname); // our own copy
objheader(obj.csegname);
objseggrp(0,0,0,0); // obj seg and grp info
ledata_new(cseg,0); // so ledata is never NULL
if (config.fulltypes) // if full typing information
cv_init(); // initialize debug output code
}
/**************************
* Initialize the start of object output for this particular .obj file.
*/
void obj_initfile(const char *filename,const char *csegname, const char *modname)
{
}
/***************************
* Fixup and terminate object file.
*/
void obj_termfile()
{
}
/*********************************
* Terminate package.
*/
void obj_term()
{
//printf("obj_term()\n");
list_t dl;
unsigned long size;
#if SCPP
if (!errcnt)
#endif
{ obj_defaultlib();
outfixlist(); // backpatches
}
if (config.fulltypes)
cv_term(); // write out final debug info
outextdata(); // finish writing EXTDEFs
outpubdata(); // finish writing PUBDEFs
// Put out LEDATA records and associated fixups
for (size_t i = 0; i < obj.ledatai; i++)
{ Ledatarec *d = ledatas[i];
if (d->i) // if any data in this record
{ // Fill in header
int headersize;
int rectyp;
assert(d->lseg > 0 && d->lseg <= seg_count);
int lseg = SegData[d->lseg]->segidx;
char header[sizeof(d->header)];
if (seg_is_comdat(lseg)) // if COMDAT
{
header[0] = d->flags | (d->offset ? 1 : 0); // continuation flag
header[1] = d->alloctyp;
header[2] = d->align;
TOOFFSET(header + 3,d->offset);
headersize = 3 + intsize;
headersize += instypidx(header + headersize,d->typidx);
if ((header[1] & 0x0F) == 0)
{ // Group index
header[headersize] = (d->pubbase == DATA) ? 1 : 0;
headersize++;
// Segment index
headersize += insidx(header + headersize,d->pubbase);
}
headersize += insidx(header + headersize,d->pubnamidx);
rectyp = I32 ? COMDAT + 1 : COMDAT;
}
else
{
rectyp = LEDATA;
headersize = insidx(header,lseg);
if (intsize == LONGSIZE || d->offset & ~0xFFFFL)
{ if (!(config.flags & CFGeasyomf))
rectyp++;
TOLONG(header + headersize,d->offset);
headersize += 4;
}
else
{
TOWORD(header + headersize,d->offset);
headersize += 2;
}
}
assert(headersize <= sizeof(d->header));
// Right-justify data in d->header[]
memcpy(d->header + sizeof(d->header) - headersize,header,headersize);
//printf("objrecord(rectyp=x%02x, d=%p, p=%p, size = %d)\n",
//rectyp,d,d->header + (sizeof(d->header) - headersize),d->i + headersize);
objrecord(rectyp,d->header + (sizeof(d->header) - headersize),
d->i + headersize);
objfixupp(d->fixuplist);
}
}
#if TERMCODE
//list_free(&obj.ledata_list,mem_freefp);
#endif
linnum_term();
obj_modend();
size = obj.buf->size();
obj.buf->setsize(0); // rewind file
obj_theadr(obj.modname);
objheader(obj.csegname);
mem_free(obj.csegname);
objseggrp(SegData[CODE]->SDoffset,Doffset,0,SegData[UDATA]->SDoffset); // do real sizes
// Update any out-of-date far segment sizes
for (size_t i = 0; i <= seg_count; i++)
{ seg_data *f = SegData[i];
if (f->isfarseg && f->origsize != f->SDoffset)
{ obj.buf->setsize(f->seek);
objsegdef(f->attr,f->SDoffset,f->lnameidx,f->classidx);
}
}
//mem_free(obj.farseg);
//printf("Ledata max = %d\n", obj.ledatai);
#if 0
printf("Max # of fixups = %d\n",obj.fixup_count);
#endif
obj.buf->setsize(size);
}
/*****************************
* Line number support.
*/
/***************************
* Record line number linnum at offset.
* Input:
* cseg current code segment (negative for COMDAT segments)
* pubnamidx
* obj.mlinnum LINNUM or LINSYM
*/
void objlinnum(Srcpos srcpos,targ_size_t offset)
{
unsigned linnum = srcpos.Slinnum;
#if 0
#if MARS || SCPP
printf("objlinnum(cseg=%d, offset=0x%lx) ", cseg, offset);
#endif
srcpos.print("");
#endif
char linos2 = config.exe == EX_OS2 && !seg_is_comdat(SegData[cseg]->segidx);
#if MARS
if (!obj.term &&
(seg_is_comdat(SegData[cseg]->segidx) || (srcpos.Sfilename && srcpos.Sfilename != obj.modname)))
#else
if (!srcpos.Sfilptr)
return;
sfile_debug(&srcpos_sfile(srcpos));
if (!obj.term && (!(srcpos_sfile(srcpos).SFflags & SFtop) || (seg_is_comdat(SegData[cseg]->segidx) && !obj.term)))
#endif
{ // Not original source file, or a COMDAT.
// Save data away and deal with it at close of compile.
// It is done this way because presumably 99% of the lines
// will be in the original source file, so we wish to minimize
// memory consumption and maximize speed.
list_t ll;
struct Linnum *ln;
if (linos2)
return; // BUG: not supported under OS/2
for (ll = obj.linnum_list; 1; ll = list_next(ll))
{
if (!ll)
{
ln = (struct Linnum *) mem_calloc(sizeof(struct Linnum));
#if MARS
ln->filename = srcpos.Sfilename;
#else
ln->filptr = *srcpos.Sfilptr;
#endif
ln->cseg = cseg;
ln->seg = obj.pubnamidx;
list_prepend(&obj.linnum_list,ln);
break;
}
ln = (Linnum *)list_ptr(ll);
if (
#if MARS
(ln->filename == srcpos.Sfilename) &&
#endif
#if SCPP
(ln->filptr == *srcpos.Sfilptr) &&
#endif
ln->cseg == cseg &&
ln->i < LINNUMMAX - 6)
break;
}
TOWORD(&ln->data[ln->i],linnum);
TOOFFSET(&ln->data[ln->i + 2],offset);
ln->i += 2 + intsize;
}
else
{
if (linos2 && obj.linreci > LINRECMAX - 8)
obj.linrec = NULL; // allocate a new one
else if (cseg != obj.recseg)
linnum_flush();
if (!obj.linrec) // if not allocated
{ obj.linrec = (char *) mem_calloc(LINRECMAX);
obj.linrec[0] = 0; // base group / flags
obj.linrecheader = 1 + insidx(obj.linrec + 1,seg_is_comdat(SegData[cseg]->segidx) ? obj.pubnamidx : SegData[cseg]->segidx);
obj.linreci = obj.linrecheader;
obj.recseg = cseg;
#if MULTISCOPE
if (!obj.linvec)
{ obj.linvec = vec_calloc(1000);
obj.offvec = vec_calloc(1000);
}
#endif
if (linos2)
{
if (!obj.linreclist) // if first line number record
obj.linreci += 8; // leave room for header
list_append(&obj.linreclist,obj.linrec);
}
// Select record type to use
obj.mlinnum = seg_is_comdat(SegData[cseg]->segidx) ? LINSYM : LINNUM;
if (I32 && !(config.flags & CFGeasyomf))
obj.mlinnum++;
}
else if (obj.linreci > LINRECMAX - (2 + intsize))
{ objrecord(obj.mlinnum,obj.linrec,obj.linreci); // output data
obj.linreci = obj.linrecheader;
if (seg_is_comdat(SegData[cseg]->segidx)) // if LINSYM record
obj.linrec[0] |= 1; // continuation bit
}
#if MULTISCOPE
if (linnum >= vec_numbits(obj.linvec))
obj.linvec = vec_realloc(obj.linvec,linnum + 1000);
if (offset >= vec_numbits(obj.offvec))
{
#if __INTSIZE == 2
unsigned newsize = (unsigned)offset * 2;
if (offset >= 0x8000)
{ newsize = 0xFF00;
assert(offset < newsize);
}
if (newsize != vec_numbits(obj.offvec))
obj.offvec = vec_realloc(obj.offvec,newsize);
#else
if (offset < 0xFF00) // otherwise we overflow ph_malloc()
obj.offvec = vec_realloc(obj.offvec,offset * 2);
#endif
}
if (
#if 1 // disallow multiple offsets per line
!vec_testbit(linnum,obj.linvec) && // if linnum not already used
#endif
// disallow multiple lines per offset
(offset >= 0xFF00 || !vec_testbit(offset,obj.offvec))) // and offset not already used
#endif
{
#if MULTISCOPE
vec_setbit(linnum,obj.linvec); // mark linnum as used
if (offset < 0xFF00)
vec_setbit(offset,obj.offvec); // mark offset as used
#endif
TOWORD(obj.linrec + obj.linreci,linnum);
if (linos2)
{ obj.linrec[obj.linreci + 2] = 1; // source file index
TOLONG(obj.linrec + obj.linreci + 4,offset);
obj.linrecnum++;
obj.linreci += 8;
}
else
{
TOOFFSET(obj.linrec + obj.linreci + 2,offset);
obj.linreci += 2 + intsize;
}
}
}
}
/***************************
* Flush any pending line number records.
*/
STATIC void linnum_flush()
{
if (obj.linreclist)
{ list_t list;
size_t len;
obj.linrec = (char *) list_ptr(obj.linreclist);
TOWORD(obj.linrec + 6,obj.linrecnum);
list = obj.linreclist;
while (1)
{ obj.linrec = (char *) list_ptr(list);
list = list_next(list);
if (list)
{ objrecord(obj.mlinnum,obj.linrec,LINRECMAX);
mem_free(obj.linrec);
}
else
{ objrecord(obj.mlinnum,obj.linrec,obj.linreci);
break;
}
}
list_free(&obj.linreclist,FPNULL);
// Put out File Names Table
TOLONG(obj.linrec + 2,0); // record no. of start of source (???)
TOLONG(obj.linrec + 6,obj.linrecnum); // number of primary source records
TOLONG(obj.linrec + 10,1); // number of source and listing files
len = obj_namestring(obj.linrec + 14,obj.modname);
assert(14 + len <= LINRECMAX);
objrecord(obj.mlinnum,obj.linrec,14 + len);
mem_free(obj.linrec);
obj.linrec = NULL;
}
else if (obj.linrec) // if some line numbers to send
{ objrecord(obj.mlinnum,obj.linrec,obj.linreci);
mem_free(obj.linrec);
obj.linrec = NULL;
}
#if MULTISCOPE
vec_clear(obj.linvec);
vec_clear(obj.offvec);
#endif
}
/*************************************
* Terminate line numbers.
*/
STATIC void linnum_term()
{ list_t ll;
#if SCPP
Sfile *lastfilptr = NULL;
#endif
#if MARS
const char *lastfilename = NULL;
#endif
int csegsave = cseg;
linnum_flush();
obj.term = 1;
while (obj.linnum_list)
{ struct Linnum *ln;
unsigned u;
Srcpos srcpos;
targ_size_t offset;
ll = obj.linnum_list;
ln = (struct Linnum *) list_ptr(ll);
#if SCPP
Sfile *filptr = ln->filptr;
if (filptr != lastfilptr)
{ obj_theadr(filptr->SFname);
lastfilptr = filptr;
}
#endif
#if MARS
const char *filename = ln->filename;
if (filename != lastfilename)
{
if (filename)
obj_theadr(filename);
lastfilename = filename;
}
#endif
while (1)
{
cseg = ln->cseg;
assert(cseg > 0);
obj.pubnamidx = ln->seg;
#if MARS
srcpos.Sfilename = ln->filename;
#else
srcpos.Sfilptr = &ln->filptr;
#endif
for (u = 0; u < ln->i; )
{
srcpos.Slinnum = *(unsigned short *)&ln->data[u];
u += 2;
if (I32)
offset = *(unsigned long *)&ln->data[u];
else
offset = *(unsigned short *)&ln->data[u];
objlinnum(srcpos,offset);
u += intsize;
}
linnum_flush();
ll = list_next(ll);
list_subtract(&obj.linnum_list,ln);
mem_free(ln);
L1:
if (!ll)
break;
ln = (struct Linnum *) list_ptr(ll);
#if SCPP
if (filptr != ln->filptr)
#else
if (filename != ln->filename)
#endif
{ ll = list_next(ll);
goto L1;
}
}
}
cseg = csegsave;
assert(cseg > 0);
#if MULTISCOPE
vec_free(obj.linvec);
vec_free(obj.offvec);
#endif
}
/*******************************
* Set start address
*/
void obj_startaddress(Symbol *s)
{
obj.startaddress = s;
}
/*******************************
* Output DOSSEG coment record.
*/
void obj_dosseg()
{ static const char dosseg[] = { 0x80,0x9E };
objrecord(COMENT,dosseg,sizeof(dosseg));
}
/*******************************
* Embed comment record.
*/
STATIC void obj_comment(unsigned char x, const char *string, size_t len)
{
char __ss *library;
library = (char __ss *) alloca(2 + len);
library[0] = 0;
library[1] = x;
memcpy(library + 2,string,len);
objrecord(COMENT,library,len + 2);
}
/*******************************
* Output library name.
* Output:
* name is modified
*/
void obj_includelib(const char *name)
{ const char *p;
size_t len = strlen(name);
p = filespecdotext(name);
if (!filespeccmp(p,".lib"))
len -= strlen(p); // lop off .LIB extension
obj_comment(0x9F, name, len);
}
/**************************
* Embed string in executable.
*/
void obj_exestr(const char *p)
{
obj_comment(0xA4,p, strlen(p));
}
/**************************
* Embed string in obj.
*/
void obj_user(const char *p)
{
obj_comment(0xDF,p, strlen(p));
}
/*********************************
* Put out default library name.
*/
STATIC void obj_defaultlib()
{
char library[4]; // default library
static const char model[MEMMODELS] = "SMCLV";
#if MARS
memcpy(library,"SM?",4);
#else
memcpy(library,"SD?",4);
#endif
switch (config.exe)
{
case EX_OS2:
library[2] = 'F';
case EX_OS1:
library[1] = 'O';
break;
case EX_NT:
#if MARS
library[1] = 'M';
#else
library[1] = 'N';
#endif
library[2] = (config.flags4 & CFG4dllrtl) ? 'D' : 'N';
break;
case EX_DOSX:
case EX_PHARLAP:
library[2] = 'X';
break;
default:
library[2] = model[config.memmodel];
if (config.wflags & WFwindows)
library[1] = 'W';
break;
}
if (!(config.flags2 & CFG2nodeflib))
{
obj_includelib(configv.deflibname ? configv.deflibname : library);
}
}
/*******************************
* Output a weak extern record.
* s1 is the weak extern, s2 is its default resolution.
*/
void obj_wkext(Symbol *s1,Symbol *s2)
{ char buffer[2+2+2];
int i;
int x2;
printf("obj_wkext(%s)\n", s1->Sident);
if (s2)
x2 = s2->Sxtrnnum;
else
{
if (!obj.nullext)
{
obj.nullext = objextdef("__nullext");
}
x2 = obj.nullext;
}
outextdata();
buffer[0] = 0x80;
buffer[1] = 0xA8;
i = 2;
i += insidx(&buffer[2],s1->Sxtrnnum);
i += insidx(&buffer[i],x2);
objrecord(COMENT,buffer,i);
}
/*******************************
* Output a lazy extern record.
* s1 is the lazy extern, s2 is its default resolution.
*/
void obj_lzext(Symbol *s1,Symbol *s2)
{ char buffer[2+2+2];
int i;
outextdata();
buffer[0] = 0x80;
buffer[1] = 0xA9;
i = 2;
i += insidx(&buffer[2],s1->Sxtrnnum);
i += insidx(&buffer[i],s2->Sxtrnnum);
objrecord(COMENT,buffer,i);
}
/*******************************
* Output an alias definition record.
*/
void obj_alias(const char *n1,const char *n2)
{ unsigned len;
char *buffer;
buffer = (char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD);
len = obj_namestring(buffer,n1);
len += obj_namestring(buffer + len,n2);
objrecord(ALIAS,buffer,len);
}
/*******************************
* Output module name record.
*/
void obj_theadr(const char *modname)
{
//printf("obj_theadr(%s)\n", modname);
// Convert to absolute file name, so debugger can find it anywhere
char absname[260];
if (config.fulltypes &&
modname[0] != '\\' && modname[0] != '/' && !(modname[0] && modname[1] == ':'))
{
if (getcwd(absname, sizeof(absname)))
{
int len = strlen(absname);
if(absname[len - 1] != '\\' && absname[len - 1] != '/')
absname[len++] = '\\';
strcpy(absname + len, modname);
modname = absname;
}
}
char *theadr = (char *)alloca(ONS_OHD + strlen(modname));
int i = obj_namestring(theadr,modname);
objrecord(THEADR,theadr,i); // module name record
}
/*******************************
* Embed compiler version in .obj file.
*/
void obj_compiler()
{
static const char compiler[] = "\0\xDBDigital Mars C/C++"
VERSION
; // compiled by ...
objrecord(COMENT,compiler,sizeof(compiler) - 1);
}
/*******************************
* Output header stuff for object files.
* Input:
* csegname Name to use for code segment (NULL if use default)
*/
STATIC void objheader(char *csegname)
{
char *nam;
static char lnames[] =
"\0\06DGROUP\05_TEXT\04CODE\05_DATA\04DATA\05CONST\04_BSS\03BSS\
\07$$TYPES\06DEBTYP\011$$SYMBOLS\06DEBSYM";
#define DATACLASS 6 // data class lname index
#define BSSCLASS 9 // BSS class lname index
// Include debug segment names if inserting type information
int lnamesize = config.fulltypes ? sizeof(lnames) - 1 : sizeof(lnames) - 1 - 32;
int texti = 8; // index of _TEXT
static char comment[] = {0,0x9D,'0','?','O'}; // memory model
static char model[MEMMODELS] = "smclv";
static char exten[] = {0,0xA1,1,'C','V'}; // extended format
static char pmdeb[] = {0x80,0xA1,1,'H','L','L',0}; // IBM PM debug format
if (I32)
{ if (config.flags & CFGeasyomf)
{
// Indicate we're in EASY OMF (hah!) format
static const char easy_omf[] = { 0x80,0xAA,'8','0','3','8','6' };
objrecord(COMENT,easy_omf,sizeof(easy_omf));
}
}
// Send out a comment record showing what memory model was used
comment[2] = config.target_cpu + '0';
comment[3] = model[config.memmodel];
if (I32)
{ if (config.exe == EX_NT)
comment[3] = 'n';
else if (config.exe == EX_OS2)
comment[3] = 'f';
else
comment[3] = 'x';
}
objrecord(COMENT,comment,sizeof(comment));
// Send out comment indicating we're using extensions to .OBJ format
if (config.exe == EX_OS2)
objrecord(COMENT,pmdeb,sizeof(pmdeb));
else
objrecord(COMENT,exten,sizeof(exten));
// Change DGROUP to FLAT if we are doing flat memory model
// (Watch out, objheader() is called twice!)
if (config.exe & EX_flat)
{
if (lnames[2] != 'F') // do not do this twice
{ memcpy(lnames + 1,"\04FLAT",5);
memmove(lnames + 6,lnames + 8,sizeof(lnames) - 8);
}
lnamesize -= 2;
texti -= 2;
}
// Put out segment and group names
if (csegname)
{ char *p;
size_t i;
// Replace the module name _TEXT with the new code segment name
i = strlen(csegname);
p = (char *)alloca(lnamesize + i - 5);
memcpy(p,lnames,8);
p[texti] = i;
texti++;
memcpy(p + texti,csegname,i);
memcpy(p + texti + i,lnames + texti + 5,lnamesize - (texti + 5));
objrecord(LNAMES,p,lnamesize + i - 5);
}
else
objrecord(LNAMES,lnames,lnamesize);
}
/********************************
* Convert module name to code segment name.
* Output:
* mem_malloc'd code seg name
*/
STATIC char * objmodtoseg(const char *modname)
{ char *csegname = NULL;
if (LARGECODE) // if need to add in module name
{ int i;
char *m;
static const char suffix[] = "_TEXT";
// Prepend the module name to the beginning of the _TEXT
m = filespecgetroot(filespecname(modname));
strupr(m);
i = strlen(m);
csegname = (char *)mem_malloc(i + sizeof(suffix));
strcpy(csegname,m);
strcat(csegname,suffix);
mem_free(m);
}
return csegname;
}
/*********************************
* Put out a segment definition.
*/
STATIC void objsegdef(int attr,targ_size_t size,int segnamidx,int classnamidx)
{
unsigned reclen;
char sd[1+4+2+2+2+1];
//printf("objsegdef(attr=x%x, size=x%x, segnamidx=x%x, classnamidx=x%x)\n",
//attr,size,segnamidx,classnamidx);
sd[0] = attr;
if (attr & 1 || config.flags & CFGeasyomf)
{ TOLONG(sd + 1,size); // store segment size
reclen = 5;
}
else
{
#ifdef DEBUG
assert(size <= 0xFFFF);
#endif
TOWORD(sd + 1,size);
reclen = 3;
}
reclen += insidx(sd + reclen,segnamidx); // segment name index
reclen += insidx(sd + reclen,classnamidx); // class name index
sd[reclen] = 1; // overlay name index
reclen++;
if (attr & 1) // if USE32
{
if (config.flags & CFGeasyomf)
{ // Translate to Pharlap format
sd[0] &= ~1; // turn off P bit
// Translate A: 4->6
attr &= SEG_ATTR(7,0,0,0);
if (attr == SEG_ATTR(4,0,0,0))
sd[0] ^= SEG_ATTR(4 ^ 6,0,0,0);
// 2 is execute/read
// 3 is read/write
// 4 is use32
sd[reclen] = (classnamidx == 4) ? (4+2) : (4+3);
reclen++;
}
}
else // 16 bit segment
{
#if MARS
assert(0);
#else
if (size & ~0xFFFFL)
{ if (size == 0x10000) // if exactly 64Kb
sd[0] |= 2; // set "B" bit
else
synerr(EM_seg_gt_64k,size); // segment exceeds 64Kb
}
//printf("attr = %x\n", attr);
#endif
}
#ifdef DEBUG
assert(reclen <= sizeof(sd));
#endif
objrecord(SEGDEF + (sd[0] & 1),sd,reclen);
}
/*********************************
* Output segment and group definitions.
* Input:
* codesize size of code segment
* datasize size of initialized data segment
* cdatasize size of initialized const data segment
* udatasize size of uninitialized data segment
*/
void objseggrp(targ_size_t codesize,targ_size_t datasize,
targ_size_t cdatasize,targ_size_t udatasize)
{
int dsegattr;
int dsymattr;
// Group into DGROUP the segments CONST, _BSS and _DATA
// For FLAT model, it's just GROUP FLAT
static const char grpdef[] = {2,0xFF,2,0xFF,3,0xFF,4};
objsegdef(obj.csegattr,codesize,3,4); // seg _TEXT, class CODE
#if MARS
dsegattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
#else
dsegattr = I32
? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
: SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
#endif
objsegdef(dsegattr,datasize,5,DATACLASS); // seg _DATA, class DATA
objsegdef(dsegattr,cdatasize,7,7); // seg CONST, class CONST
objsegdef(dsegattr,udatasize,8,9); // seg _BSS, class BSS
obj.lnameidx = 10; // next lname index
obj.segidx = 5; // next segment index
if (config.fulltypes)
{
dsymattr = I32
? SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE32)
: SEG_ATTR(SEG_ALIGN1,SEG_C_ABS,0,USE16);
if (config.exe & EX_flat)
{ // IBM's version of CV uses dword aligned segments
dsymattr = SEG_ATTR(SEG_ALIGN4,SEG_C_ABS,0,USE32);
}
else if (config.fulltypes == CV4)
{ // Always use 32 bit segments
dsymattr |= USE32;
assert(!(config.flags & CFGeasyomf));
}
objsegdef(dsymattr,SegData[DEBSYM]->SDoffset,0x0C,0x0D);
objsegdef(dsymattr,SegData[DEBTYP]->SDoffset,0x0A,0x0B);
obj.lnameidx += 4; // next lname index
obj.segidx += 2; // next segment index
}
objrecord(GRPDEF,grpdef,(config.exe & EX_flat) ? 1 : sizeof(grpdef));
#if 0
// Define fixup threads, we don't use them
{ static const char thread[] = { 0,3,1,2,2,1,3,4,0x40,1,0x45,1 };
objrecord(obj.mfixupp,thread,sizeof(thread));
}
// This comment appears to indicate that no more PUBDEFs, EXTDEFs,
// or COMDEFs are coming.
{ static const char cv[] = {0,0xA2,1};
objrecord(COMENT,cv,sizeof(cv));
}
#endif
}
//#if NEWSTATICDTOR
/**************************************
* Symbol is the function that calls the static constructors.
* Put a pointer to it into a special segment that the startup code
* looks at.
* Input:
* s static constructor function
* dtor number of static destructors
* seg 1: user
* 2: lib
* 3: compiler
*/
void obj_staticctor(Symbol *s,int dtor,int seg)
{
// We need to always put out the segments in triples, so that the
// linker will put them in the correct order.
static char lnamector[] = "\05XIFCB\04XIFU\04XIFL\04XIFM\05XIFCE";
static char lnamedtor[] = "\04XOFB\03XOF\04XOFE";
static char lnamedtorf[] = "\03XOB\02XO\03XOE";
symbol_debug(s);
// Determine if near or far function
assert(I32 || tyfarfunc(s->ty()));
// Put out LNAMES record
objrecord(LNAMES,lnamector,sizeof(lnamector) - 1);
int dsegattr = I32
? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
: SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
for (int i = 0; i < 5; i++)
{ int sz;
sz = (i == seg) ? 4 : 0;
// Put out segment definition record
objsegdef(dsegattr,sz,obj.lnameidx,DATACLASS);
if (i == seg)
{
seg_data *pseg = getsegment();
pseg->segidx = obj.segidx;
reftoident(pseg->SDseg,0,s,0,0); // put out function pointer
}
obj.segidx++;
obj.lnameidx++;
}
if (dtor)
{ // Leave space in XOF segment so that __fatexit() can insert a
// pointer to the static destructor in XOF.
// Put out LNAMES record
if (LARGEDATA)
objrecord(LNAMES,lnamedtorf,sizeof(lnamedtorf) - 1);
else
objrecord(LNAMES,lnamedtor,sizeof(lnamedtor) - 1);
// Put out beginning segment
objsegdef(dsegattr,0,obj.lnameidx,BSSCLASS);
// Put out segment definition record
objsegdef(dsegattr,4 * dtor,obj.lnameidx + 1,BSSCLASS);
// Put out ending segment
objsegdef(dsegattr,0,obj.lnameidx + 2,BSSCLASS);
obj.lnameidx += 3; // for next time
obj.segidx += 3;
}
}
//#else
/***************************************
* Stuff pointer to function in its own segment.
* Used for static ctor and dtor lists.
*/
void obj_funcptr(Symbol *s)
{
// We need to always put out the segments in triples, so that the
// linker will put them in the correct order.
static char lnames[4][5+4+5] =
{ "\03XIB\02XI\03XIE", // near constructor
"\03XCB\02XC\03XCE", // near destructor
"\04XIFB\03XIF\04XIFE", // far constructor
"\04XCFB\03XCF\04XCFE", // far destructor
};
// Size of each of the above strings
static int lnamesize[4] = { 4+3+4,4+3+4,5+4+5,5+4+5 };
int dsegattr;
int i;
symbol_debug(s);
#ifdef DEBUG
assert(memcmp(s->Sident,"_ST",3) == 0);
#endif
// Determine if constructor or destructor
// _STI... is a constructor, _STD... is a destructor
i = s->Sident[3] == 'D';
// Determine if near or far function
if (tyfarfunc(s->Stype->Tty))
i += 2;
// Put out LNAMES record
objrecord(LNAMES,lnames[i],lnamesize[i]);
dsegattr = I32
? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
: SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
// Put out beginning segment
objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
obj.segidx++;
// Put out segment definition record
// size is NPTRSIZE or FPTRSIZE
objsegdef(dsegattr,(i & 2) + tysize[TYnptr],obj.lnameidx + 1,DATACLASS);
seg_data *pseg = getsegment();
pseg->segidx = obj.segidx;
reftoident(pseg->SDseg,0,s,0,0); // put out function pointer
obj.segidx++;
// Put out ending segment
objsegdef(dsegattr,0,obj.lnameidx + 2,DATACLASS);
obj.segidx++;
obj.lnameidx += 3; // for next time
}
//#endif
/***************************************
* Stuff pointer to function in its own segment.
* Used for static ctor and dtor lists.
*/
void obj_ehtables(Symbol *sfunc,targ_size_t size,Symbol *ehsym)
{
// We need to always put out the segments in triples, so that the
// linker will put them in the correct order.
static char lnames[] =
{ "\03FIB\02FI\03FIE" // near constructor
};
int i;
int dsegattr;
targ_size_t offset;
symbol_debug(sfunc);
if (obj.fisegi == 0)
{
// Put out LNAMES record
objrecord(LNAMES,lnames,sizeof(lnames) - 1);
dsegattr = I32
? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
: SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
// Put out beginning segment
objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
obj.lnameidx++;
obj.segidx++;
// Put out segment definition record
obj.fisegi = obj_newfarseg(0,DATACLASS);
objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
SegData[obj.fisegi]->attr = dsegattr;
assert(SegData[obj.fisegi]->segidx == obj.segidx);
// Put out ending segment
objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS);
obj.lnameidx += 2; // for next time
obj.segidx += 2;
}
offset = SegData[obj.fisegi]->SDoffset;
offset += reftoident(obj.fisegi,offset,sfunc,0,LARGECODE ? CFoff | CFseg : CFoff); // put out function pointer
offset += reftoident(obj.fisegi,offset,ehsym,0,0); // pointer to data
obj_bytes(obj.fisegi,offset,intsize,&size); // size of function
SegData[obj.fisegi]->SDoffset = offset + intsize;
}
/***************************************
* Append pointer to ModuleInfo to "FM" segment.
* The FM segment is bracketed by the empty FMB and FME segments.
*/
#if MARS
void obj_moduleinfo(Symbol *scc)
{
// We need to always put out the segments in triples, so that the
// linker will put them in the correct order.
static char lnames[] =
{ "\03FMB\02FM\03FME"
};
symbol_debug(scc);
if (obj.fmsegi == 0)
{
// Put out LNAMES record
objrecord(LNAMES,lnames,sizeof(lnames) - 1);
int dsegattr = I32
? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
: SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
// Put out beginning segment
objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
obj.lnameidx++;
obj.segidx++;
// Put out segment definition record
obj.fmsegi = obj_newfarseg(0,DATACLASS);
objsegdef(dsegattr,0,obj.lnameidx,DATACLASS);
SegData[obj.fmsegi]->attr = dsegattr;
assert(SegData[obj.fmsegi]->segidx == obj.segidx);
// Put out ending segment
objsegdef(dsegattr,0,obj.lnameidx + 1,DATACLASS);
obj.lnameidx += 2; // for next time
obj.segidx += 2;
}
targ_size_t offset = SegData[obj.fmsegi]->SDoffset;
offset += reftoident(obj.fmsegi,offset,scc,0,LARGECODE ? CFoff | CFseg : CFoff); // put out function pointer
SegData[obj.fmsegi]->SDoffset = offset;
}
#endif
/*********************************
* Setup for Symbol s to go into a COMDAT segment.
* Output (if s is a function):
* cseg segment index of new current code segment
* Coffset starting offset in cseg
* Returns:
* "segment index" of COMDAT (which will be a negative value to
* distinguish it from regular segments).
*/
int obj_comdat(Symbol *s)
{ char lnames[IDMAX+IDOHD+1]; // +1 to allow room for strcpy() terminating 0
char cextdef[2+2];
char __ss *p;
size_t lnamesize;
unsigned ti;
int isfunc;
tym_t ty;
symbol_debug(s);
ty = s->ty();
isfunc = tyfunc(ty) != 0;
// Put out LNAME for name of Symbol
lnamesize = obj_mangle(s,lnames);
objrecord((s->Sclass == SCstatic ? LLNAMES : LNAMES),lnames,lnamesize);
// Put out CEXTDEF for name of Symbol
outextdata();
p = cextdef;
p += insidx(p,obj.lnameidx++);
ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0;
p += instypidx(p,ti);
objrecord(CEXTDEF,cextdef,p - cextdef);
s->Sxtrnnum = ++obj.extidx;
seg_data *pseg = getsegment();
pseg->segidx = -obj.extidx;
assert(pseg->SDseg > 0);
// Start new LEDATA record for this COMDAT
Ledatarec *lr = ledata_new(pseg->SDseg,0);
lr->typidx = ti;
lr->pubnamidx = obj.lnameidx - 1;
if (isfunc)
{ lr->pubbase = SegData[cseg]->segidx;
if (s->Sclass == SCcomdat || s->Sclass == SCinline)
lr->alloctyp = 0x10 | 0x00; // pick any instance | explicit allocation
cseg = lr->lseg;
assert(cseg > 0 && cseg <= seg_count);
obj.pubnamidx = obj.lnameidx - 1;
Coffset = 0;
if (tyfarfunc(ty) && strcmp(s->Sident,"main") == 0)
lr->alloctyp |= 1; // because MS does for unknown reasons
}
else
{ unsigned char atyp;
switch (ty & mTYLINK)
{ case 0:
case mTYnear: lr->pubbase = DATA;
#if 0
atyp = 0; // only one instance is allowed
#else
atyp = 0x10; // pick any (also means it is
// not searched for in a library)
#endif
break;
#if TARGET_SEGMENTED
case mTYcs: lr->flags |= 0x08; // data in code seg
atyp = 0x11; break;
case mTYfar: atyp = 0x12; break;
#endif
case mTYthread: lr->pubbase = obj_tlsseg()->segidx;
atyp = 0x10; // pick any (also means it is
// not searched for in a library)
break;
default: assert(0);
}
lr->alloctyp = atyp;
}
if (s->Sclass == SCstatic)
lr->flags |= 0x04; // local bit (make it an "LCOMDAT")
return pseg->SDseg;
}
/**********************************
* Reset code seg to existing seg.
* Used after a COMDAT for a function is done.
*/
void obj_setcodeseg(int seg,targ_size_t offset)
{
assert(0 < seg && seg <= seg_count);
cseg = seg;
Coffset = offset;
}
/********************************
* Define a new code segment.
* Input:
* name name of segment, if NULL then revert to default
* suffix 0 use name as is
* 1 append "_TEXT" to name
* Output:
* cseg segment index of new current code segment
* Coffset starting offset in cseg
* Returns:
* segment index of newly created code segment
*/
int obj_codeseg(char *name,int suffix)
{
if (!name)
{
if (cseg != CODE)
{
cseg = CODE;
}
return cseg;
}
// Put out LNAMES record
size_t lnamesize = strlen(name) + suffix * 5;
char *lnames = (char *) alloca(1 + lnamesize + 1);
lnames[0] = lnamesize;
assert(lnamesize <= (255 - 2 - sizeof(int)*3));
strcpy(lnames + 1,name);
if (suffix)
strcat(lnames + 1,"_TEXT");
objrecord(LNAMES,lnames,lnamesize + 1);
cseg = obj_newfarseg(0,4);
SegData[cseg]->attr = obj.csegattr;
SegData[cseg]->segidx = obj.segidx;
assert(cseg > 0);
obj.segidx++;
Coffset = 0;
objsegdef(obj.csegattr,0,obj.lnameidx++,4);
return cseg;
}
/*********************************
* Define segment for Thread Local Storage.
* Output:
* tlsseg set to segment number for TLS segment.
* Returns:
* segment for TLS segment
*/
seg_data *obj_tlsseg_bss() { return obj_tlsseg(); }
seg_data *obj_tlsseg()
{ //static char tlssegname[] = "\04$TLS\04$TLS";
//static char tlssegname[] = "\05.tls$\03tls";
static const char tlssegname[] = "\05.tls$\03tls\04.tls\010.tls$ZZZ";
if (obj.tlssegi == 0)
{ int segattr;
objrecord(LNAMES,tlssegname,sizeof(tlssegname) - 1);
#if MARS
segattr = SEG_ATTR(SEG_ALIGN16,SEG_C_PUBLIC,0,USE32);
#else
segattr = I32
? SEG_ATTR(SEG_ALIGN4,SEG_C_PUBLIC,0,USE32)
: SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
#endif
// Put out beginning segment (.tls)
objsegdef(segattr,0,obj.lnameidx + 2,obj.lnameidx + 1);
obj.segidx++;
// Put out .tls$ segment definition record
obj.tlssegi = obj_newfarseg(0,obj.lnameidx + 1);
objsegdef(segattr,0,obj.lnameidx,obj.lnameidx + 1);
SegData[obj.tlssegi]->attr = segattr;
SegData[obj.tlssegi]->segidx = obj.segidx;
// Put out ending segment (.tls$ZZZ)
objsegdef(segattr,0,obj.lnameidx + 3,obj.lnameidx + 1);
obj.lnameidx += 4;
obj.segidx += 2;
}
return SegData[obj.tlssegi];
}
/********************************
* Define a far data segment.
* Input:
* name Name of module
* size Size of the segment to be created
* Returns:
* segment index of far data segment created
* *poffset start of the data for the far data segment
*/
int obj_fardata(char *name,targ_size_t size,targ_size_t *poffset)
{
static char fardataclass[] = "\010FAR_DATA";
int len;
int i;
char *buffer;
// See if we can use existing far segment, and just bump its size
i = obj.lastfardatasegi;
if (i != -1
&& (intsize != 2 || (unsigned long) SegData[i]->SDoffset + size < 0x8000)
)
{ *poffset = SegData[i]->SDoffset; // BUG: should align this
SegData[i]->SDoffset += size;
return i;
}
// No. We need to build a new far segment
if (obj.fardataidx == 0) // if haven't put out far data lname
{ // Put out class lname
objrecord(LNAMES,fardataclass,sizeof(fardataclass) - 1);
obj.fardataidx = obj.lnameidx++;
}
// Generate name based on module name
name = strupr(filespecgetroot(filespecname(obj.modname)));
// Generate name for this far segment
len = 1 + strlen(name) + 3 + 5 + 1;
buffer = (char *)alloca(len);
sprintf(buffer + 1,"%s%d_DATA",name,obj.segidx);
len = strlen(buffer + 1);
buffer[0] = len;
assert(len <= 255);
objrecord(LNAMES,buffer,len + 1);
mem_free(name);
// Construct a new SegData[] entry
obj.lastfardatasegi = obj_newfarseg(size,obj.fardataidx);
// Generate segment definition
objsegdef(obj.fdsegattr,size,obj.lnameidx++,obj.fardataidx);
obj.segidx++;
*poffset = 0;
return SegData[obj.lastfardatasegi]->SDseg;
}
/************************************
* Remember where we put a far segment so we can adjust
* its size later.
* Input:
* obj.segidx
* lnameidx
* Returns:
* index of SegData[]
*/
STATIC int obj_newfarseg(targ_size_t size,int classidx)
{
seg_data *f = getsegment();
f->isfarseg = true;
f->seek = obj.buf->size();
f->attr = obj.fdsegattr;
f->origsize = size;
f->SDoffset = size;
f->segidx = obj.segidx;
f->lnameidx = obj.lnameidx;
f->classidx = classidx;
return f->SDseg;
}
/******************************
* Convert reference to imported name.
*/
void obj_import(elem *e)
{
#if MARS
assert(0);
#else
Symbol *s;
Symbol *simp;
elem_debug(e);
if ((e->Eoper == OPvar || e->Eoper == OPrelconst) &&
(s = e->EV.sp.Vsym)->ty() & mTYimport &&
(s->Sclass == SCextern || s->Sclass == SCinline)
)
{ char *name;
char *p;
size_t len;
char buffer[IDMAX + IDOHD + 1];
// Create import name
len = obj_mangle(s,buffer);
if (buffer[0] == (char)0xFF && buffer[1] == 0)
{ name = buffer + 4;
len -= 4;
}
else
{ name = buffer + 1;
len -= 1;
}
if (config.flags4 & CFG4underscore)
{ p = (char *) alloca(5 + len + 1);
memcpy(p,"_imp_",5);
memcpy(p + 5,name,len);
p[5 + len] = 0;
}
else
{ p = (char *) alloca(6 + len + 1);
memcpy(p,"__imp_",6);
memcpy(p + 6,name,len);
p[6 + len] = 0;
}
simp = scope_search(p,SCTglobal);
if (!simp)
{ type *t;
simp = scope_define(p,SCTglobal,SCextern);
simp->Ssequence = 0;
simp->Sfl = FLextern;
simp->Simport = s;
t = newpointer(s->Stype);
t->Tmangle = mTYman_c;
t->Tcount++;
simp->Stype = t;
}
assert(!e->EV.sp.Voffset);
if (e->Eoper == OPrelconst)
{
e->Eoper = OPvar;
e->EV.sp.Vsym = simp;
}
else // OPvar
{
e->Eoper = OPind;
e->E1 = el_var(simp);
e->E2 = NULL;
}
}
#endif
}
/*******************************
* Mangle a name.
* Returns:
* length of mangled name
*/
size_t obj_mangle(Symbol *s,char *dest)
{ size_t len;
size_t ilen;
char *name;
char *name2 = NULL;
//printf("obj_mangle('%s'), mangle = x%x\n",s->Sident,type_mangle(s->Stype));
#if SCPP
name = CPP ? cpp_mangle(s) : s->Sident;
#elif MARS
name = cpp_mangle(s);
#else
name = s->Sident;
#endif
len = strlen(name); // # of bytes in name
// Use as max length the max length lib.exe can handle
// Use 5 as length of _ + @nnn
#define LIBIDMAX ((512 - 0x25 - 3 - 4) - 5)
#define LIBIDMAX 128
if (len > LIBIDMAX)
//if (len > IDMAX)
{
size_t len2;
// Attempt to compress the name
name2 = id_compress(name, len);
len2 = strlen(name2);
#if MARS
if (len2 > LIBIDMAX) // still too long
{
/* Form md5 digest of the name and store it in the
* last 32 bytes of the name.
*/
MD5_CTX mdContext;
MD5Init(&mdContext);
MD5Update(&mdContext, (unsigned char *)name, len);
MD5Final(&mdContext);
memcpy(name2, name, LIBIDMAX - 32);
for (int i = 0; i < 16; i++)
{ unsigned char c = mdContext.digest[i];
unsigned char c1 = (c >> 4) & 0x0F;
unsigned char c2 = c & 0x0F;
c1 += (c1 < 10) ? '0' : 'A' - 10;
name2[LIBIDMAX - 32 + i * 2] = c1;
c2 += (c2 < 10) ? '0' : 'A' - 10;
name2[LIBIDMAX - 32 + i * 2 + 1] = c2;
}
name = name2;
len = LIBIDMAX;
name[len] = 0;
//printf("name = '%s', len = %d, strlen = %d\n", name, len, strlen(name));
}
#else
if (len2 > IDMAX) // still too long
{
#if SCPP
synerr(EM_identifier_too_long, name, len - IDMAX, IDMAX);
#elif MARS
// error(0, "identifier %s is too long by %d characters", name, len - IDMAX);
#else
assert(0);
#endif
len = IDMAX;
}
#endif
else
{
name = name2;
len = len2;
}
}
ilen = len;
if (ilen > (255-2-sizeof(int)*3))
dest += 3;
switch (type_mangle(s->Stype))
{ case mTYman_pas: // if upper case
case mTYman_for:
memcpy(dest + 1,name,len); // copy in name
dest[1 + len] = 0;
strupr(dest + 1); // to upper case
break;
#if SCPP || MARS
case mTYman_cpp:
#if NEWMANGLE
memcpy(dest + 1,name,len);
break;
#endif
#endif
case mTYman_std:
if (!(config.flags4 & CFG4oldstdmangle) &&
config.exe == EX_NT && tyfunc(s->ty()) &&
!variadic(s->Stype))
{
dest[1] = '_';
memcpy(dest + 2,name,len);
dest[1 + 1 + len] = '@';
itoa(type_paramsize(s->Stype),dest + 3 + len,10);
len = strlen(dest + 1);
assert(isdigit(dest[len]));
break;
}
case mTYman_c:
if (config.flags4 & CFG4underscore)
{
dest[1] = '_'; // leading _ in name
memcpy(&dest[2],name,len); // copy in name
len++;
break;
}
case mTYman_d:
case mTYman_sys:
memcpy(dest + 1, name, len); // no mangling
dest[1 + len] = 0;
break;
default:
#ifdef DEBUG
symbol_print(s);
#endif
assert(0);
}
if (ilen > (255-2-sizeof(int)*3))
{ dest -= 3;
dest[0] = 0xFF;
dest[1] = 0;
#ifdef DEBUG
assert(len <= 0xFFFF);
#endif
TOWORD(dest + 2,len);
len += 4;
}
else
{ *dest = len;
len++;
}
if (name2)
free(name2);
assert(len <= IDMAX + IDOHD);
return len;
}
/*******************************
* Export a function name.
*/
void obj_export(Symbol *s,unsigned argsize)
{ char *coment;
size_t len;
coment = (char *) alloca(4 + 1 + (IDMAX + IDOHD) + 1); // allow extra byte for mangling
len = obj_mangle(s,&coment[4]);
assert(len <= IDMAX + IDOHD);
coment[1] = 0xA0; // comment class
coment[2] = 2; // why??? who knows
if (argsize >= 64) // we only have a 5 bit field
argsize = 0; // hope we don't need callgate
coment[3] = (argsize + 1) >> 1; // # words on stack
coment[4 + len] = 0; // no internal name
objrecord(COMENT,coment,4 + len + 1); // module name record
}
/*******************************
* Update data information about symbol
* align for output and assign segment
* if not already specified.
*
* Input:
* sdata data symbol
* datasize output size
* seg default seg if not known
* Returns:
* actual seg
*/
int elf_data_start(Symbol *sdata, targ_size_t datasize, int seg)
{
targ_size_t alignbytes;
//printf("elf_data_start(%s,size %llx,seg %d)\n",sdata->Sident,datasize,seg);
//symbol_print(sdata);
if (sdata->Sseg == UNKNOWN) // if we don't know then there
sdata->Sseg = seg; // wasn't any segment override
else
seg = sdata->Sseg;
targ_size_t offset = SegData[seg]->SDoffset;
alignbytes = align(datasize, offset) - offset;
if (alignbytes)
obj_lidata(seg, offset, alignbytes);
sdata->Soffset = offset + alignbytes;
return seg;
}
/********************************
* Output a public definition.
* Input:
* seg = segment index that symbol is defined in
* s -> symbol
* offset = offset of name
*/
STATIC void outpubdata()
{
if (obj.pubdatai)
{ objrecord(obj.mpubdef,obj.pubdata,obj.pubdatai);
obj.pubdatai = 0;
}
}
void objpubdef(int seg,Symbol *s,targ_size_t offset)
{ unsigned reclen,len;
char *p;
unsigned ti;
int idx = SegData[seg]->segidx;
if (obj.pubdatai + 1 + (IDMAX + IDOHD) + 4 + 2 > sizeof(obj.pubdata) ||
idx != getindex(obj.pubdata + 1))
outpubdata();
if (obj.pubdatai == 0)
{
obj.pubdata[0] = (seg == DATA || seg == UDATA) ? 1 : 0; // group index
obj.pubdatai += 1 + insidx(obj.pubdata + 1,idx); // segment index
}
p = &obj.pubdata[obj.pubdatai];
len = obj_mangle(s,p); // mangle in name
reclen = len + intsize;
p += len;
TOOFFSET(p,offset);
p += intsize;
ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0;
reclen += instypidx(p,ti);
obj.pubdatai += reclen;
}
/*******************************
* Output an external definition.
* Input:
* name -> external identifier
* Returns:
* External index of the definition (1,2,...)
*/
STATIC void outextdata()
{
if (obj.extdatai)
{ objrecord(EXTDEF,obj.extdata,obj.extdatai);
obj.extdatai = 0;
}
}
int objextdef(const char *name)
{ unsigned len;
char *e;
//dbg_printf("objextdef('%s')\n",name);
assert(name);
len = strlen(name); // length of identifier
if (obj.extdatai + len + ONS_OHD + 1 > sizeof(obj.extdata))
outextdata();
e = &obj.extdata[obj.extdatai];
len = obj_namestring(e,name);
e[len] = 0; // typidx = 0
obj.extdatai += len + 1;
assert(obj.extdatai <= sizeof(obj.extdata));
return ++obj.extidx;
}
/*******************************
* Output an external definition.
* Input:
* s Symbol to do EXTDEF on
* Returns:
* External index of the definition (1,2,...)
*/
int objextern(Symbol *s)
{
//dbg_printf("objextern('%s')\n",s->Sident);
symbol_debug(s);
if (obj.extdatai + (IDMAX + IDOHD) + 3 > sizeof(obj.extdata))
outextdata();
char *e = &obj.extdata[obj.extdatai];
unsigned len = obj_mangle(s,e);
e[len] = 0; // typidx = 0
obj.extdatai += len + 1;
s->Sxtrnnum = ++obj.extidx;
return obj.extidx;
}
/*******************************
* Output a common block definition.
* Input:
* p -> external identifier
* flag TRUE: in default data segment
* FALSE: not in default data segment
* size size in bytes of each elem
* count number of elems
* Returns:
* External index of the definition (1,2,...)
*/
// Helper for obj_comdef()
static unsigned storelength(unsigned long length,unsigned i)
{
obj.extdata[i] = length;
if (length >= 128) // Microsoft docs say 129, but their linker
// won't take >=128, so accommodate it
{ obj.extdata[i] = 129;
#ifdef DEBUG
assert(length <= 0xFFFF);
#endif
TOWORD(obj.extdata + i + 1,length);
if (length >= 0x10000)
{ obj.extdata[i] = 132;
obj.extdata[i + 3] = length >> 16;
// Only 386 can generate lengths this big
if (I32 && length >= 0x1000000)
{ obj.extdata[i] = 136;
obj.extdata[i + 4] = length >> 24;
i += 4;
}
else
i += 3;
}
else
i += 2;
}
return i + 1; // index past where we stuffed length
}
int obj_comdef(Symbol *s,int flag,targ_size_t size,targ_size_t count)
{ register unsigned i;
unsigned long length;
unsigned ti;
//dbg_printf("obj_comdef('%s',%d,%d,%d)\n",s->Sident,flag,size,count);
outextdata(); // borrow the extdata[] storage
i = obj_mangle(s,obj.extdata);
ti = (config.fulltypes == CVOLD) ? cv_typidx(s->Stype) : 0;
i += instypidx(obj.extdata + i,ti);
if (flag) // if in default data segment
{
//printf("NEAR comdef\n");
obj.extdata[i] = 0x62;
length = (unsigned long) size * count;
assert(I32 || length <= 0x10000);
i = storelength(length,i + 1);
}
else
{
//printf("FAR comdef\n");
obj.extdata[i] = 0x61;
i = storelength((unsigned long) size,i + 1);
i = storelength((unsigned long) count,i);
}
assert(i <= arraysize(obj.extdata));
objrecord(COMDEF,obj.extdata,i);
return ++obj.extidx;
}
/***************************************
* Append an iterated data block of 0s.
* (uninitialized data only)
*/
void obj_write_zeros(seg_data *pseg, targ_size_t count)
{
obj_lidata(pseg->SDseg, pseg->SDoffset, count);
//pseg->SDoffset += count;
}
/***************************************
* Output an iterated data block of 0s.
* (uninitialized data only)
*/
void obj_lidata(int seg,targ_size_t offset,targ_size_t count)
{ int i;
unsigned reclen;
static char zero[20];
char data[20];
char __ss *di;
//printf("obj_lidata(seg = %d, offset = x%x, count = %d)\n", seg, offset, count);
SegData[seg]->SDoffset += count;
if (seg == UDATA)
return;
int idx = SegData[seg]->segidx;
Lagain:
if (count <= sizeof(zero)) // if shorter to use ledata
{
obj_bytes(seg,offset,count,zero);
return;
}
if (seg_is_comdat(idx))
{
while (count > sizeof(zero))
{
obj_bytes(seg,offset,sizeof(zero),zero);
offset += sizeof(zero);
count -= sizeof(zero);
}
obj_bytes(seg,offset,count,zero);
return;
}
i = insidx(data,idx);
di = data + i;
TOOFFSET(di,offset);
if (config.flags & CFGeasyomf)
{
if (count >= 0x8000) // repeat count can only go to 32k
{
TOWORD(di + 4,(unsigned short)(count / 0x8000));
TOWORD(di + 4 + 2,1); // 1 data block follows
TOWORD(di + 4 + 2 + 2,0x8000); // repeat count
TOWORD(di + 4 + 2 + 2 + 2,0); // block count
TOWORD(di + 4 + 2 + 2 + 2 + 2,1); // 1 byte of 0
reclen = i + 4 + 5 * 2;
objrecord(obj.mlidata,data,reclen);
offset += (count & ~0x7FFFL);
count &= 0x7FFF;
goto Lagain;
}
else
{
TOWORD(di + 4,(unsigned short)count); // repeat count
TOWORD(di + 4 + 2,0); // block count
TOWORD(di + 4 + 2 + 2,1); // 1 byte of 0
reclen = i + 4 + 2 + 2 + 2;
objrecord(obj.mlidata,data,reclen);
}
}
else
{
TOOFFSET(di + intsize,count);
TOWORD(di + intsize * 2,0); // block count
TOWORD(di + intsize * 2 + 2,1); // repeat 1 byte of 0s
reclen = i + (I32 ? 12 : 8);
objrecord(obj.mlidata,data,reclen);
}
assert(reclen <= sizeof(data));
}
/****************************
* Output a MODEND record.
*/
STATIC void obj_modend()
{
if (obj.startaddress)
{ char mdata[10];
int i;
unsigned framedatum,targetdatum;
unsigned char fd;
targ_size_t offset;
int external; // !=0 if identifier is defined externally
tym_t ty;
Symbol *s = obj.startaddress;
// Turn startaddress into a fixup.
// Borrow heavilly from reftoident()
symbol_debug(s);
offset = 0;
ty = s->ty();
switch (s->Sclass)
{
case SCcomdat:
case_SCcomdat:
case SCextern:
case SCcomdef:
if (s->Sxtrnnum) // identifier is defined somewhere else
external = s->Sxtrnnum;
else
{
Ladd:
s->Sclass = SCextern;
external = objextern(s);
outextdata();
}
break;
case SCinline:
if (config.flags2 & CFG2comdat)
goto case_SCcomdat; // treat as initialized common block
case SCsinline:
case SCstatic:
case SCglobal:
if (s->Sseg == UNKNOWN)
goto Ladd;
if (seg_is_comdat(SegData[s->Sseg]->segidx)) // if in comdat
goto case_SCcomdat;
case SClocstat:
external = 0; // identifier is static or global
// and we know its offset
offset += s->Soffset;
break;
default:
#ifdef DEBUG
//symbol_print(s);
#endif
assert(0);
}
if (external)
{ fd = FD_T2;
targetdatum = external;
switch (s->Sfl)
{
case FLextern:
if (!(ty & (
#if TARGET_SEGMENTED
mTYcs |
#endif
mTYthread)))
goto L1;
case FLfunc:
#if TARGET_SEGMENTED
case FLfardata:
case FLcsdata:
#endif
case FLtlsdata:
if (config.exe & EX_flat)
{ fd |= FD_F1;
framedatum = 1;
}
else
{
//case FLtlsdata:
fd |= FD_F2;
framedatum = targetdatum;
}
break;
default:
goto L1;
}
}
else
{
fd = FD_T0; // target is always a segment
targetdatum = SegData[s->Sseg]->segidx;
assert(targetdatum != -1);
switch (s->Sfl)
{
case FLextern:
if (!(ty & (
#if TARGET_SEGMENTED
mTYcs |
#endif
mTYthread)))
goto L1;
case FLfunc:
#if TARGET_SEGMENTED
case FLfardata:
case FLcsdata:
#endif
case FLtlsdata:
if (config.exe & EX_flat)
{ fd |= FD_F1;
framedatum = 1;
}
else
{
//case FLtlsdata:
fd |= FD_F0;
framedatum = targetdatum;
}
break;
default:
L1:
fd |= FD_F1;
framedatum = DGROUPIDX;
//if (flags == CFseg)
{ fd = FD_F1 | FD_T1; // target is DGROUP
targetdatum = DGROUPIDX;
}
break;
}
}
// Write the fixup into mdata[]
mdata[0] = 0xC1;
mdata[1] = fd;
i = 2 + insidx(&mdata[2],framedatum);
i += insidx(&mdata[i],targetdatum);
TOOFFSET(mdata + i,offset);
objrecord(obj.mmodend,mdata,i + intsize); // write mdata[] to .OBJ file
}
else
{ static const char modend[] = {0};
objrecord(MODEND,modend,sizeof(modend));
}
}
/****************************
* Output the fixups in list fl.
*/
STATIC void objfixupp(struct FIXUP *f)
{
unsigned i,j,k;
targ_size_t locat;
struct FIXUP *fn;
#if 1 // store in one record
char data[1024];
i = 0;
for (; f; f = fn)
{ unsigned char fd;
if (i >= sizeof(data) - (3 + 2 + 2)) // if not enough room
{ objrecord(obj.mfixupp,data,i);
i = 0;
}
//printf("f = %p, offset = x%x\n",f,f->FUoffset);
assert(f->FUoffset < 1024);
locat = (f->FUlcfd & 0xFF00) | f->FUoffset;
data[i+0] = locat >> 8;
data[i+1] = locat;
data[i+2] = fd = f->FUlcfd;
k = i;
i += 3 + insidx(&data[i+3],f->FUframedatum);
//printf("FUframedatum = x%x\n", f->FUframedatum);
if ((fd >> 4) == (fd & 3) && f->FUframedatum == f->FUtargetdatum)
{
data[k + 2] = (fd & 15) | FD_F5;
}
else
{ i += insidx(&data[i],f->FUtargetdatum);
//printf("FUtargetdatum = x%x\n", f->FUtargetdatum);
}
//printf("[%d]: %02x %02x %02x\n", k, data[k + 0] & 0xFF, data[k + 1] & 0xFF, data[k + 2] & 0xFF);
fn = f->FUnext;
mem_ffree(f);
}
assert(i <= sizeof(data));
if (i)
objrecord(obj.mfixupp,data,i);
#else // store in multiple records
for (; fl; fl = list_next(fl))
{
char data[7];
assert(f->FUoffset < 1024);
locat = (f->FUlcfd & 0xFF00) | f->FUoffset;
data[0] = locat >> 8;
data[1] = locat;
data[2] = f->FUlcfd;
i = 3 + insidx(&data[3],f->FUframedatum);
i += insidx(&data[i],f->FUtargetdatum);
objrecord(obj.mfixupp,data,i);
}
#endif
}
/***************************
* Add a new fixup to the fixup list.
* Write things out if we overflow the list.
*/
STATIC void addfixup(Ledatarec *lr, targ_size_t offset,unsigned lcfd,
unsigned framedatum,unsigned targetdatum)
{ struct FIXUP *f;
assert(offset < 0x1024);
#ifdef DEBUG
assert(targetdatum <= 0x7FFF);
assert(framedatum <= 0x7FFF);
#endif
f = (struct FIXUP *) mem_fmalloc(sizeof(struct FIXUP));
//printf("f = %p, offset = x%x\n",f,offset);
f->FUoffset = offset;
f->FUlcfd = lcfd;
f->FUframedatum = framedatum;
f->FUtargetdatum = targetdatum;
f->FUnext = lr->fixuplist; // link f into list
lr->fixuplist = f;
#ifdef DEBUG
obj.fixup_count++; // gather statistics
#endif
}
/*********************************
* Open up a new ledata record.
* Input:
* seg segment number data is in
* offset starting offset of start of data for this record
*/
STATIC Ledatarec *ledata_new(int seg,targ_size_t offset)
{
//printf("ledata_new(seg = %d, offset = x%lx)\n",seg,offset);
assert(seg > 0 && seg <= seg_count);
if (obj.ledatai == ledatamax)
{
size_t o = ledatamax;
ledatamax = o * 2 + 100;
ledatas = (Ledatarec **)mem_realloc(ledatas, ledatamax * sizeof(Ledatarec *));
memset(ledatas + o, 0, (ledatamax - o) * sizeof(Ledatarec *));
}
Ledatarec *lr = ledatas[obj.ledatai];
if (!lr)
{ lr = (Ledatarec *) mem_malloc(sizeof(Ledatarec));
ledatas[obj.ledatai] = lr;
}
memset(lr, 0, sizeof(Ledatarec));
ledatas[obj.ledatai] = lr;
obj.ledatai++;
lr->lseg = seg;
lr->offset = offset;
if (seg_is_comdat(SegData[seg]->segidx) && offset) // if continuation of an existing COMDAT
{
Ledatarec *d = SegData[seg]->ledata;
if (d)
{
if (d->lseg == seg) // found existing COMDAT
{ lr->flags = d->flags;
lr->alloctyp = d->alloctyp;
lr->align = d->align;
lr->typidx = d->typidx;
lr->pubbase = d->pubbase;
lr->pubnamidx = d->pubnamidx;
}
}
}
SegData[seg]->ledata = lr;
return lr;
}
/***********************************
* Append byte to segment.
*/
void obj_write_byte(seg_data *pseg, unsigned byte)
{
obj_byte(pseg->SDseg, pseg->SDoffset, byte);
pseg->SDoffset++;
}
/************************************
* Output byte to object file.
*/
void obj_byte(int seg,targ_size_t offset,unsigned byte)
{ unsigned i;
Ledatarec *lr = SegData[seg]->ledata;
if (!lr)
goto L2;
if (
lr->i > LEDATAMAX - 1 || // if it'll overflow
offset < lr->offset || // underflow
offset > lr->offset + lr->i
)
{
// Try to find an existing ledata
for (size_t i = obj.ledatai; i; )
{ Ledatarec *d = ledatas[--i];
if (seg == d->lseg && // segments match
offset >= d->offset &&
offset + 1 <= d->offset + LEDATAMAX &&
offset <= d->offset + d->i
)
{
lr = SegData[seg]->ledata = d;
goto L1;
}
}
L2:
lr = ledata_new(seg,offset);
L1: ;
}
i = offset - lr->offset;
if (lr->i <= i)
lr->i = i + 1;
lr->data[i] = byte; // 1st byte of data
}
/***********************************
* Append bytes to segment.
*/
void obj_write_bytes(seg_data *pseg, unsigned nbytes, void *p)
{
obj_bytes(pseg->SDseg, pseg->SDoffset, nbytes, p);
pseg->SDoffset += nbytes;
}
/************************************
* Output bytes to object file.
* Returns:
* nbytes
*/
unsigned obj_bytes(int seg,targ_size_t offset,unsigned nbytes, void *p)
{ unsigned n = nbytes;
//dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=x%x, p=%p)\n",seg,offset,nbytes,p);
Ledatarec *lr = SegData[seg]->ledata;
if (!lr)
lr = ledata_new(seg, offset);
L1:
if (
lr->i + nbytes > LEDATAMAX || // or it'll overflow
offset < lr->offset || // underflow
offset > lr->offset + lr->i
)
{
while (nbytes)
{ obj_byte(seg,offset,*(char *)p);
offset++;
((char *)p)++;
nbytes--;
lr = SegData[seg]->ledata;
if (lr->i + nbytes <= LEDATAMAX)
goto L1;
}
}
else
{
unsigned i = offset - lr->offset;
if (lr->i < i + nbytes)
lr->i = i + nbytes;
memcpy(lr->data + i,p,nbytes);
}
return n;
}
/************************************
* Output word of data. (Two words if segment:offset pair.)
* Input:
* seg CODE, DATA, CDATA, UDATA
* offset offset of start of data
* data word of data
* lcfd LCxxxx | FDxxxx
* if (FD_F2 | FD_T6)
* idx1 = external Symbol #
* else
* idx1 = frame datum
* idx2 = target datum
*/
void objledata(int seg,targ_size_t offset,targ_size_t data,
unsigned lcfd,unsigned idx1,unsigned idx2)
{ unsigned i;
unsigned size; // number of bytes to output
#if TARGET_SEGMENTED
unsigned ptrsize = tysize[TYfptr];
#else
unsigned ptrsize = I64 ? 10 : 6;
#endif
if ((lcfd & LOCxx) == obj.LOCpointer)
size = ptrsize;
else if ((lcfd & LOCxx) == LOCbase)
size = 2;
else
size = tysize[TYnptr];
Ledatarec *lr = SegData[seg]->ledata;
if (!lr)
lr = ledata_new(seg, offset);
assert(seg == lr->lseg);
if (
lr->i + size > LEDATAMAX || // if it'll overflow
offset < lr->offset || // underflow
offset > lr->offset + lr->i
)
{
// Try to find an existing ledata
//dbg_printf("seg = %d, offset = x%lx, size = %d\n",seg,offset,size);
for (size_t i = obj.ledatai; i; )
{ Ledatarec *d = ledatas[--i];
//dbg_printf("d: seg = %d, offset = x%lx, i = x%x\n",d->lseg,d->offset,d->i);
if (seg == d->lseg && // segments match
offset >= d->offset &&
offset + size <= d->offset + LEDATAMAX &&
offset <= d->offset + d->i
)
{
//dbg_printf("match\n");
lr = SegData[seg]->ledata = d;
goto L1;
}
}
lr = ledata_new(seg,offset);
L1: ;
}
i = offset - lr->offset;
if (lr->i < i + size)
lr->i = i + size;
if (size == 2 || !I32)
TOWORD(lr->data + i,data);
else
TOLONG(lr->data + i,data);
if (size == ptrsize) // if doing a seg:offset pair
TOWORD(lr->data + i + tysize[TYnptr],0); // segment portion
addfixup(lr, offset - lr->offset,lcfd,idx1,idx2);
}
/************************************
* Output long word of data.
* Input:
* seg CODE, DATA, CDATA, UDATA
* offset offset of start of data
* data long word of data
* Present only if size == 2:
* lcfd LCxxxx | FDxxxx
* if (FD_F2 | FD_T6)
* idx1 = external Symbol #
* else
* idx1 = frame datum
* idx2 = target datum
*/
void obj_long(int seg,targ_size_t offset,unsigned long data,
unsigned lcfd,unsigned idx1,unsigned idx2)
{
#if TARGET_SEGMENTED
unsigned sz = tysize[TYfptr];
#else
unsigned sz = I64 ? 10 : 6;
#endif
Ledatarec *lr = SegData[seg]->ledata;
if (!lr)
lr = ledata_new(seg, offset);
if (
lr->i + sz > LEDATAMAX || // if it'll overflow
offset < lr->offset || // underflow
offset > lr->offset + lr->i
)
lr = ledata_new(seg,offset);
unsigned i = offset - lr->offset;
if (lr->i < i + sz)
lr->i = i + sz;
TOLONG(lr->data + i,data);
if (I32) // if 6 byte far pointers
TOWORD(lr->data + i + LONGSIZE,0); // fill out seg
addfixup(lr, offset - lr->offset,lcfd,idx1,idx2);
}
/*******************************
* Refer to address that is in the data segment.
* Input:
* seg = where the address is going
* offset = offset within seg
* val = displacement from address
* targetdatum = DATA, CDATA or UDATA, depending where the address is
* flags = CFoff, CFseg
* Example:
* int *abc = &def[3];
* to allocate storage:
* reftodatseg(DATA,offset,3 * sizeof(int *),UDATA);
*/
void reftodatseg(int seg,targ_size_t offset,targ_size_t val,
unsigned targetdatum,int flags)
{
assert(flags);
if (flags == 0 || flags & CFoff)
{
// The frame datum is always 1, which is DGROUP
objledata(seg,offset,val,
LOCATsegrel | obj.LOCoffset | FD_F1 | FD_T4,DGROUPIDX,SegData[targetdatum]->segidx);
offset += intsize;
}
if (flags & CFseg)
{
#if 0
if (config.wflags & WFdsnedgroup)
warerr(WM_ds_ne_dgroup);
#endif
objledata(seg,offset,0,
LOCATsegrel | LOCbase | FD_F1 | FD_T5,DGROUPIDX,DGROUPIDX);
}
}
/*******************************
* Refer to address that is in a far segment.
* Input:
* seg = where the address is going
* offset = offset within seg
* val = displacement from address
* farseg = far segment index
* flags = CFoff, CFseg
*/
void reftofarseg(int seg,targ_size_t offset,targ_size_t val,
int farseg,int flags)
{
assert(flags);
int idx = SegData[farseg]->segidx;
if (flags == 0 || flags & CFoff)
{
objledata(seg,offset,val,
LOCATsegrel | obj.LOCoffset | FD_F0 | FD_T4,idx,idx);
offset += intsize;
}
if (flags & CFseg)
{
objledata(seg,offset,0,
LOCATsegrel | LOCbase | FD_F0 | FD_T4,idx,idx);
}
}
/*******************************
* Refer to address that is in the code segment.
* Only offsets are output, regardless of the memory model.
* Used to put values in switch address tables.
* Input:
* seg = where the address is going (CODE or DATA)
* offset = offset within seg
* val = displacement from start of this module
*/
void reftocodseg(int seg,targ_size_t offset,targ_size_t val)
{ unsigned framedatum;
unsigned lcfd;
int idx = SegData[cseg]->segidx;
if (seg_is_comdat(idx)) // if comdat
{ idx = -idx;
framedatum = idx;
lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F2 | FD_T6);
}
else if (config.exe & EX_flat)
{ framedatum = 1;
lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F1 | FD_T4);
}
else
{ framedatum = idx;
lcfd = (LOCATsegrel | obj.LOCoffset) | (FD_F0 | FD_T4);
}
objledata(seg,offset,val,lcfd,framedatum,idx);
}
/*******************************
* Refer to an identifier.
* Input:
* seg = where the address is going (CODE or DATA)
* offset = offset within seg
* s -> Symbol table entry for identifier
* val = displacement from identifier
* flags = CFselfrel: self-relative
* CFseg: get segment
* CFoff: get offset
* Returns:
* number of bytes in reference (2 or 4)
* Example:
* extern int def[];
* int *abc = &def[3];
* to allocate storage:
* reftodatseg(DATA,offset,3 * sizeof(int *),UDATA);
*/
int reftoident(int seg,targ_size_t offset,Symbol *s,targ_size_t val,
int flags)
{
unsigned targetdatum; // which datum the symbol is in
unsigned framedatum;
int lc;
int external; // !=0 if identifier is defined externally
int numbytes;
tym_t ty;
#if 0
printf("reftoident('%s' seg %d, offset x%lx, val x%lx, flags x%x)\n",
s->Sident,seg,offset,val,flags);
printf("Sseg = %d, Sxtrnnum = %d\n",s->Sseg,s->Sxtrnnum);
symbol_print(s);
#endif
assert(seg > 0);
ty = s->ty();
while (1)
{
switch (flags & (CFseg | CFoff))
{ case 0:
// Select default
flags |= CFoff;
if (tyfunc(ty))
{
if (tyfarfunc(ty))
flags |= CFseg;
}
else // DATA
{
if (LARGEDATA)
flags |= CFseg;
}
continue;
case CFoff:
if (I32)
{
#if 1
if (ty & mTYthread)
{ lc = LOC32tlsoffset;
}
else
#endif
lc = obj.LOCoffset;
}
else
{
// The 'loader_resolved' offset is required for VCM
// and Windows support. A fixup of this type is
// relocated by the linker to point to a 'thunk'.
lc = (tyfarfunc(ty)
&& !(flags & CFselfrel))
? LOCloader_resolved : obj.LOCoffset;
}
numbytes = tysize[TYnptr];
break;
case CFseg:
lc = LOCbase;
numbytes = 2;
break;
case CFoff | CFseg:
lc = obj.LOCpointer;
#if TARGET_SEGMENTED
numbytes = tysize[TYfptr];
#else
numbytes = I64 ? 10 : 6;
#endif
break;
}
break;
}
switch (s->Sclass)
{
case SCcomdat:
case_SCcomdat:
case SCextern:
case SCcomdef:
if (s->Sxtrnnum) // identifier is defined somewhere else
{ external = s->Sxtrnnum;
#ifdef DEBUG
if (external > obj.extidx)
symbol_print(s);
#endif
assert(external <= obj.extidx);
}
else
{ // Don't know yet, worry about it later
Ladd:
addtofixlist(s,offset,seg,val,flags);
return numbytes;
}
break;
case SCinline:
if (config.flags2 & CFG2comdat)
goto case_SCcomdat; // treat as initialized common block
case SCsinline:
case SCstatic:
case SCglobal:
if (s->Sseg == UNKNOWN)
goto Ladd;
if (seg_is_comdat(SegData[s->Sseg]->segidx))
goto case_SCcomdat;
case SClocstat:
external = 0; // identifier is static or global
// and we know its offset
if (flags & CFoff)
val += s->Soffset;
break;
default:
#ifdef DEBUG
symbol_print(s);
#endif
assert(0);
}
lc |= (flags & CFselfrel) ? LOCATselfrel : LOCATsegrel;
if (external)
{ lc |= FD_T6;
targetdatum = external;
switch (s->Sfl)
{
case FLextern:
if (!(ty & (
#if TARGET_SEGMENTED
mTYcs |
#endif
mTYthread)))
goto L1;
case FLfunc:
#if TARGET_SEGMENTED
case FLfardata:
case FLcsdata:
#endif
case FLtlsdata:
if (config.exe & EX_flat)
{ lc |= FD_F1;
framedatum = 1;
}
else
{
//case FLtlsdata:
lc |= FD_F2;
framedatum = targetdatum;
}
break;
default:
goto L1;
}
}
else
{
lc |= FD_T4; // target is always a segment
targetdatum = SegData[s->Sseg]->segidx;
assert(s->Sseg != UNKNOWN);
switch (s->Sfl)
{
case FLextern:
if (!(ty & (
#if TARGET_SEGMENTED
mTYcs |
#endif
mTYthread)))
goto L1;
case FLfunc:
#if TARGET_SEGMENTED
case FLfardata:
case FLcsdata:
#endif
case FLtlsdata:
if (config.exe & EX_flat)
{ lc |= FD_F1;
framedatum = 1;
}
else
{
//case FLtlsdata:
lc |= FD_F0;
framedatum = targetdatum;
}
break;
default:
L1:
lc |= FD_F1;
framedatum = DGROUPIDX;
if (flags == CFseg)
{ lc = LOCATsegrel | LOCbase | FD_F1 | FD_T5;
targetdatum = DGROUPIDX;
}
#if 0
if (flags & CFseg && config.wflags & WFdsnedgroup)
warerr(WM_ds_ne_dgroup);
#endif
break;
}
}
objledata(seg,offset,val,lc,framedatum,targetdatum);
return numbytes;
}
/*****************************************
* Generate far16 thunk.
* Input:
* s Symbol to generate a thunk for
*/
void obj_far16thunk(Symbol *s)
{
static unsigned char cod32_1[] =
{
0x55, // PUSH EBP
0x8B,0xEC, // MOV EBP,ESP
0x83,0xEC,0x04, // SUB ESP,4
0x53, // PUSH EBX
0x57, // PUSH EDI
0x56, // PUSH ESI
0x06, // PUSH ES
0x8C,0xD2, // MOV DX,SS
0x80,0xE2,0x03, // AND DL,3
0x80,0xCA,0x07, // OR DL,7
0x89,0x65,0xFC, // MOV -4[EBP],ESP
0x8C,0xD0, // MOV AX,SS
0x66,0x3D, // 0x00,0x00 */ /* CMP AX,seg FLAT:_DATA
};
static unsigned char cod32_2[] =
{ 0x0F,0x85,0x10,0x00,0x00,0x00, // JNE L1
0x8B,0xC4, // MOV EAX,ESP
0x66,0x3D,0x00,0x08, // CMP AX,2048
0x0F,0x83,0x04,0x00,0x00,0x00, // JAE L1
0x66,0x33,0xC0, // XOR AX,AX
0x94, // XCHG ESP,EAX
// L1:
0x55, // PUSH EBP
0x8B,0xC4, // MOV EAX,ESP
0x16, // PUSH SS
0x50, // PUSH EAX
0x8D,0x75,0x08, // LEA ESI,8[EBP]
0x81,0xEC,0x00,0x00,0x00,0x00, // SUB ESP,numparam
0x8B,0xFC, // MOV EDI,ESP
0xB9,0x00,0x00,0x00,0x00, // MOV ECX,numparam
0x66,0xF3,0xA4, // REP MOVSB
0x8B,0xC4, // MOV EAX,ESP
0xC1,0xC8,0x10, // ROR EAX,16
0x66,0xC1,0xE0,0x03, // SHL AX,3
0x0A,0xC2, // OR AL,DL
0xC1,0xC0,0x10, // ROL EAX,16
0x50, // PUSH EAX
0x66,0x0F,0xB2,0x24,0x24, // LSS SP,[ESP]
0x66,0xEA, // 0,0,0,0, */ /* JMPF L3
};
static unsigned char cod32_3[] =
{ // L2:
0xC1,0xE0,0x10, // SHL EAX,16
0x0F,0xAC,0xD0,0x10, // SHRD EAX,EDX,16
0x0F,0xB7,0xE4, // MOVZX ESP,SP
0x0F,0xB2,0x24,0x24, // LSS ESP,[ESP]
0x5D, // POP EBP
0x8B,0x65,0xFC, // MOV ESP,-4[EBP]
0x07, // POP ES
0x5E, // POP ESI
0x5F, // POP EDI
0x5B, // POP EBX
0xC9, // LEAVE
0xC2,0x00,0x00 // RET numparam
};
unsigned numparam = 24;
targ_size_t L2offset;
int idx;
s->Sclass = SCstatic;
s->Sseg = cseg; // identifier is defined in code segment
s->Soffset = Coffset;
// Store numparam into right places
assert((numparam & 0xFFFF) == numparam); // 2 byte value
TOWORD(&cod32_2[32],numparam);
TOWORD(&cod32_2[32 + 7],numparam);
TOWORD(&cod32_3[sizeof(cod32_3) - 2],numparam);
//------------------------------------------
// Generate CODE16 segment if it isn't there already
if (obj.code16segi == 0)
{
// Define CODE16 segment for far16 thunks
static char lname[] = { "\06CODE16" };
// Put out LNAMES record
objrecord(LNAMES,lname,sizeof(lname) - 1);
obj.code16segi = obj_newfarseg(0,4);
obj.CODE16offset = 0;
// class CODE
unsigned attr = SEG_ATTR(SEG_ALIGN2,SEG_C_PUBLIC,0,USE16);
SegData[obj.code16segi]->attr = attr;
objsegdef(attr,0,obj.lnameidx++,4);
obj.segidx++;
}
//------------------------------------------
// Output the 32 bit thunk
obj_bytes(cseg,Coffset,sizeof(cod32_1),cod32_1);
Coffset += sizeof(cod32_1);
// Put out fixup for SEG FLAT:_DATA
objledata(cseg,Coffset,0,LOCATsegrel|LOCbase|FD_F1|FD_T4,
DGROUPIDX,DATA);
Coffset += 2;
obj_bytes(cseg,Coffset,sizeof(cod32_2),cod32_2);
Coffset += sizeof(cod32_2);
// Put out fixup to CODE16 part of thunk
objledata(cseg,Coffset,obj.CODE16offset,LOCATsegrel|LOC16pointer|FD_F0|FD_T4,
SegData[obj.code16segi]->segidx,
SegData[obj.code16segi]->segidx);
Coffset += 4;
L2offset = Coffset;
obj_bytes(cseg,Coffset,sizeof(cod32_3),cod32_3);
Coffset += sizeof(cod32_3);
s->Ssize = Coffset - s->Soffset; // size of thunk
//------------------------------------------
// Output the 16 bit thunk
obj_byte(obj.code16segi,obj.CODE16offset++,0x9A); // CALLF function
// Make function external
idx = objextern(s); // use Pascal name mangling
// Output fixup for function
objledata(obj.code16segi,obj.CODE16offset,0,LOCATsegrel|LOC16pointer|FD_F2|FD_T6,
idx,idx);
obj.CODE16offset += 4;
obj_bytes(obj.code16segi,obj.CODE16offset,3,"\x66\x67\xEA"); // JMPF L2
obj.CODE16offset += 3;
objledata(obj.code16segi,obj.CODE16offset,L2offset,
LOCATsegrel | LOC32pointer | FD_F1 | FD_T4,
DGROUPIDX,
SegData[cseg]->segidx);
obj.CODE16offset += 6;
SegData[obj.code16segi]->SDoffset = obj.CODE16offset;
}
/**************************************
* Mark object file as using floating point.
*/
void obj_fltused()
{
if (!obj.fltused)
{
obj.fltused = 1;
if (!(config.flags3 & CFG3wkfloat))
objextdef("__fltused");
}
}
/****************************************
* Find longest match of pattern[] in dict[].
*/
static int longest_match(char *dict, int dlen, char *pattern, int plen,
int *pmatchoff, int *pmatchlen)
{
int matchlen = 0;
int matchoff;
int i;
int j;
for (i = 0; i < dlen; i++)
{
if (dict[i] == pattern[0])
{
for (j = 1; 1; j++)
{
if (i + j == dlen || j == plen)
break;
if (dict[i + j] != pattern[j])
break;
}
if (j >= matchlen)
{
matchlen = j;
matchoff = i;
}
}
}
if (matchlen > 1)
{
*pmatchlen = matchlen;
*pmatchoff = matchoff;
return 1; // found a match
}
return 0; // no match
}
/******************************************
* Compress an identifier.
* Format: if ASCII, then it's just the char
* if high bit set, then it's a length/offset pair
* Returns:
* malloc'd compressed identifier
*/
char *id_compress(char *id, int idlen)
{
int i;
int count = 0;
char *p;
p = (char *)malloc(idlen + 1);
for (i = 0; i < idlen; i++)
{
int matchoff;
int matchlen;
int j = 0;
if (i > 1023)
j = i - 1023;
if (longest_match(id + j, i - j, id + i, idlen - i, &matchoff, &matchlen))
{ int off;
matchoff += j;
off = i - matchoff;
//printf("matchoff = %3d, matchlen = %2d, off = %d\n", matchoff, matchlen, off);
assert(off >= matchlen);
if (off <= 8 && matchlen <= 8)
{
p[count] = 0xC0 | ((off - 1) << 3) | (matchlen - 1);
count++;
i += matchlen - 1;
continue;
}
else if (matchlen > 2 && off < 1024)
{
if (matchlen >= 1024)
matchlen = 1023; // longest representable match
p[count + 0] = 0x80 | ((matchlen >> 4) & 0x38) | ((off >> 7) & 7);
p[count + 1] = 0x80 | matchlen;
p[count + 2] = 0x80 | off;
count += 3;
i += matchlen - 1;
continue;
}
}
p[count] = id[i];
count++;
}
p[count] = 0;
//printf("old size = %d, new size = %d\n", idlen, count);
return p;
}
#endif
#endif