Files
ldc/backend/elfobj.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

3112 lines
95 KiB
C

// Copyright (C) ?-1998 by Symantec
// Copyright (C) 2000-2010 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
/*
* This source file is made available for personal use
* only. The license is in /dmd/src/dmd/backendlicense.txt
* For any other uses, please contact Digital Mars.
*/
// Output to ELF object files
#if SCPP || MARS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#if __sun&&__SVR4
#include <alloca.h>
#endif
#include "cc.h"
#include "global.h"
#include "code.h"
#include "type.h"
#include "melf.h"
#include "outbuf.h"
#include "filespec.h"
#include "cv4.h"
#include "cgcv.h"
#include "dt.h"
#include "aa.h"
#include "tinfo.h"
#if ELFOBJ
#include "dwarf.h"
#include "aa.h"
#include "tinfo.h"
//#define DEBSYM 0x7E
static Outbuffer *fobjbuf;
regm_t BYTEREGS = BYTEREGS_INIT;
regm_t ALLREGS = ALLREGS_INIT;
static char __file__[] = __FILE__; // for tassert.h
#include "tassert.h"
#define MATCH_SECTION 1
#define DEST_LEN (IDMAX + IDOHD + 1)
char *obj_mangle2(Symbol *s,char *dest);
#if MARS
// C++ name mangling is handled by front end
#define cpp_mangle(s) ((s)->Sident)
#endif
/***************************************************
* Correspondence of relocation types
* 386 32 bit in 64 64 in 64
* RI_TYPE_SYM32 R_X86_64_32 R_X86_64_64
* RI_TYPE_GOTOFF R_X86_64_PC32 R_X86_64_
* RI_TYPE_GOTPC R_X86_64_ R_X86_64_
* RI_TYPE_GOT32 R_X86_64_ R_X86_64_
* RI_TYPE_TLS_GD R_X86_64_TLSGD R_X86_64_
* RI_TYPE_TLS_IE R_X86_64_GOTTPOFF R_X86_64_
* RI_TYPE_TLS_LE R_X86_64_TPOFF32 R_X86_64_
* RI_TYPE_PLT32 R_X86_64_PLT32 R_X86_64_
* RI_TYPE_PC32 R_X86_64_PC32 R_X86_64_
*/
/******************************************
*/
symbol *GOTsym; // global offset table reference
symbol *elfobj_getGOTsym()
{
if (!GOTsym)
{
GOTsym = symbol_name("_GLOBAL_OFFSET_TABLE_",SCglobal,tspvoid);
}
return GOTsym;
}
void elfobj_refGOTsym()
{
if (!GOTsym)
{
symbol *s = elfobj_getGOTsym();
objextern(s);
}
}
static void objfile_write(FILE *fd, void *buffer, unsigned len);
STATIC char * objmodtoseg (const char *modname);
STATIC void obj_browse_flush();
STATIC void objfixupp (struct FIXUP *);
STATIC void ledata_new (int seg,targ_size_t offset);
void obj_tlssections();
static IDXSYM elf_addsym(IDXSTR sym, targ_size_t val, unsigned sz,
unsigned typ,unsigned bind,IDXSEC sec);
static long elf_align(FILE *fd, targ_size_t size, long offset);
// The object file is built is several separate pieces
// Non-repeatable section types have single output buffers
// Pre-allocated buffers are defined for:
// Section Names string table
// Section Headers table
// Symbol table
// String table
// Notes section
// Comment data
// Section Names - String table for section names only
static Outbuffer *section_names;
#define SEC_NAMES_INIT 800
#define SEC_NAMES_INC 400
// Hash table for section_names
AArray *section_names_hashtable;
/* ====================== Cached Strings in section_names ================= */
struct TypeInfo_Idxstr : TypeInfo
{
const char* toString();
hash_t getHash(void *p);
int equals(void *p1, void *p2);
int compare(void *p1, void *p2);
size_t tsize();
void swap(void *p1, void *p2);
};
TypeInfo_Idxstr ti_idxstr;
const char* TypeInfo_Idxstr::toString()
{
return "IDXSTR";
}
hash_t TypeInfo_Idxstr::getHash(void *p)
{
IDXSTR a = *(IDXSTR *)p;
hash_t hash = 0;
for (const char *s = (char *)(section_names->buf + a);
*s;
s++)
{
hash = hash * 11 + *s;
}
return hash;
}
int TypeInfo_Idxstr::equals(void *p1, void *p2)
{
IDXSTR a1 = *(IDXSTR*)p1;
IDXSTR a2 = *(IDXSTR*)p2;
const char *s1 = (char *)(section_names->buf + a1);
const char *s2 = (char *)(section_names->buf + a2);
return strcmp(s1, s2) == 0;
}
int TypeInfo_Idxstr::compare(void *p1, void *p2)
{
IDXSTR a1 = *(IDXSTR*)p1;
IDXSTR a2 = *(IDXSTR*)p2;
const char *s1 = (char *)(section_names->buf + a1);
const char *s2 = (char *)(section_names->buf + a2);
return strcmp(s1, s2);
}
size_t TypeInfo_Idxstr::tsize()
{
return sizeof(IDXSTR);
}
void TypeInfo_Idxstr::swap(void *p1, void *p2)
{
assert(0);
}
/* ======================================================================== */
// String Table - String table for all other names
static Outbuffer *symtab_strings;
// Section Headers
Outbuffer *SECbuf; // Buffer to build section table in
#define SecHdrTab ((Elf32_Shdr *)SECbuf->buf)
#define GET_SECTION(secidx) (SecHdrTab + secidx)
#define GET_SECTION_NAME(secidx) (section_names->buf + SecHdrTab[secidx].sh_name)
// The relocation for text and data seems to get lost.
// Try matching the order gcc output them
// This means defining the sections and then removing them if they are
// not used.
static int section_cnt; // Number of sections in table
#define SHI_TEXT 1
#define SHI_RELTEXT 2
#define SHI_DATA 3
#define SHI_RELDATA 4
#define SHI_BSS 5
#define SHI_RODAT 6
#define SHI_STRINGS 7
#define SHI_SYMTAB 8
#define SHI_SECNAMES 9
#define SHI_COM 10
#define SHI_NOTE 11
IDXSYM *mapsec2sym;
#define S2S_INC 20
#define SymbolTable ((Elf32_Sym *)SYMbuf->buf)
#define SymbolTable64 ((Elf64_Sym *)SYMbuf->buf)
static int symbol_idx; // Number of symbols in symbol table
static int local_cnt; // Number of symbols with STB_LOCAL
#define STI_FILE 1 // Where file symbol table entry is
#define STI_TEXT 2
#define STI_DATA 3
#define STI_BSS 4
#define STI_GCC 5 // Where "gcc2_compiled" symbol is */
#define STI_RODAT 6 // Symbol for readonly data
#define STI_NOTE 7 // Where note symbol table entry is
#define STI_COM 8
// NOTE: There seems to be a requirement that the read-only data have the
// same symbol table index and section index. Use section NOTE as a place
// holder. When a read-only string section is required, swap to NOTE.
// Symbol Table
Outbuffer *SYMbuf; // Buffer to build symbol table in
// Notes data (note currently used)
static Outbuffer *note_data;
static IDXSEC secidx_note; // Final table index for note data
// Comment data for compiler version
static Outbuffer *comment_data;
static const char compiler[] = "\0Digital Mars C/C++"
VERSION
; // compiled by ...
// Each compiler segment is an elf section
// Predefined compiler segments CODE,DATA,CDATA,UDATA map to indexes
// into SegData[]
// An additionl index is reserved for comment data
// New compiler segments are added to end.
//
// There doesn't seem to be any way to get reserved data space in the
// same section as initialized data or code, so section offsets should
// be continuous when adding data. Fix-ups anywhere withing existing data.
#define COMD UDATA+1
#define OB_SEG_SIZ 10 // initial number of segments supported
#define OB_SEG_INC 10 // increment for additional segments
#define OB_CODE_STR 100000 // initial size for code
#define OB_CODE_INC 100000 // increment for additional code
#define OB_DATA_STR 100000 // initial size for data
#define OB_DATA_INC 100000 // increment for additional data
#define OB_CDATA_STR 1024 // initial size for data
#define OB_CDATA_INC 1024 // increment for additional data
#define OB_COMD_STR 256 // initial size for comments
// increment as needed
#define OB_XTRA_STR 250 // initial size for extra segments
#define OB_XTRA_INC 10000 // increment size
#define MAP_SEG2SECIDX(seg) (SegData[seg]->SDshtidx)
#define MAP_SEG2SYMIDX(seg) (SegData[seg]->SDsymidx)
#define MAP_SEG2SEC(seg) (&SecHdrTab[MAP_SEG2SECIDX(seg)])
#define MAP_SEG2TYP(seg) (MAP_SEG2SEC(seg)->sh_flags & SHF_EXECINSTR ? CODE : DATA)
seg_data **SegData;
int seg_count;
int seg_max;
int seg_tlsseg = UNKNOWN;
int seg_tlsseg_bss = UNKNOWN;
int elf_getsegment2(IDXSEC shtidx, IDXSYM symidx, IDXSEC relidx);
/*******************************
* Output a string into a string table
* Input:
* strtab = string table for entry
* str = string to add
*
* Returns index into the specified string table.
*/
IDXSTR elf_addstr(Outbuffer *strtab, const char *str)
{
//dbg_printf("elf_addstr(strtab = x%x str = '%s')\n",strtab,str);
IDXSTR idx = strtab->size(); // remember starting offset
strtab->writeString(str);
//dbg_printf("\tidx %d, new size %d\n",idx,strtab->size());
return idx;
}
/*******************************
* Find a string in a string table
* Input:
* strtab = string table for entry
* str = string to find
*
* Returns index into the specified string table or 0.
*/
static IDXSTR elf_findstr(Outbuffer *strtab, const char *str, const char *suffix)
{
//printf("elf_findstr(strtab = %p, str = %s, suffix = %s\n", strtab, str ? str : "", suffix ? suffix : "");
size_t len = strlen(str);
// Combine str~suffix and have buf point to the combination
#ifdef DEBUG
char tmpbuf[25]; // to exercise the alloca() code path
#else
char tmpbuf[1024]; // the alloca() code path is slow
#endif
const char *buf;
if (suffix)
{
size_t suffixlen = strlen(suffix);
if (len + suffixlen >= sizeof(tmpbuf))
{
buf = (char *)alloca(len + suffixlen + 1);
assert(buf);
}
else
{
buf = tmpbuf;
}
memcpy((char *)buf, str, len);
memcpy((char *)buf + len, suffix, suffixlen + 1);
len += suffixlen;
}
else
buf = str;
// Linear search, slow
const char *ent = (char *)strtab->buf+1;
const char *pend = ent+strtab->size() - 1;
while (ent + len < pend)
{
if (memcmp(buf, ent, len + 1) == 0)
return ent - (const char *)strtab->buf;
ent = (const char *)memchr(ent, 0, pend - ent);
ent += 1;
}
return 0; // never found match
}
/*******************************
* Output a mangled string into the symbol string table
* Input:
* str = string to add
*
* Returns index into the table.
*/
static IDXSTR elf_addmangled(Symbol *s)
{
//printf("elf_addmangled(%s)\n", s->Sident);
char dest[DEST_LEN];
char *destr;
const char *name;
int len;
IDXSTR namidx;
namidx = symtab_strings->size();
destr = obj_mangle2(s, dest);
name = destr;
if (CPP && name[0] == '_' && name[1] == '_')
{
if (strncmp(name,"__ct__",6) == 0)
name += 4;
#if 0
switch(name[2])
{
case 'c':
if (strncmp(name,"__ct__",6) == 0)
name += 4;
break;
case 'd':
if (strcmp(name,"__dl__FvP") == 0)
name = "__builtin_delete";
break;
case 'v':
//if (strcmp(name,"__vec_delete__FvPiUIPi") == 0)
//name = "__builtin_vec_del";
//else
//if (strcmp(name,"__vn__FPUI") == 0)
//name = "__builtin_vec_new";
break;
case 'n':
if (strcmp(name,"__nw__FPUI") == 0)
name = "__builtin_new";
break;
}
#endif
}
else if (tyfunc(s->ty()) && s->Sfunc && s->Sfunc->Fredirect)
name = s->Sfunc->Fredirect;
len = strlen(name);
symtab_strings->reserve(len+1);
strcpy((char *)symtab_strings->p,name);
symtab_strings->setsize(namidx+len+1);
if (destr != dest) // if we resized result
mem_free(destr);
//dbg_printf("\telf_addmagled symtab_strings %s namidx %d len %d size %d\n",name, namidx,len,symtab_strings->size());
return namidx;
}
/*******************************
* Output a symbol into the symbol table
* Input:
* stridx = string table index for name
* val = value associated with symbol
* sz = symbol size
* typ = symbol type
* bind = symbol binding
* segidx = segment index for segment where symbol is defined
*
* Returns the symbol table index for the symbol
*/
static IDXSYM elf_addsym(IDXSTR nam, targ_size_t val, unsigned sz,
unsigned typ, unsigned bind, IDXSEC sec)
{
//dbg_printf("elf_addsym(nam %d, val %d, sz %x, typ %x, bind %x, sec %d\n",
//nam,val,sz,typ,bind,sec);
/* We want globally defined data symbols to have a size because
* zero sized symbols break copy relocations for shared libraries.
*/
assert(!(sz == 0 && (bind == STB_GLOBAL || bind == STB_WEAK) &&
(typ == STT_OBJECT || typ == STT_TLS) &&
sec != SHT_UNDEF));
if (I64)
{
if (!SYMbuf)
{ SYMbuf = new Outbuffer(50 * sizeof(Elf64_Sym));
SYMbuf->reserve(100 * sizeof(Elf64_Sym));
}
Elf64_Sym sym;
sym.st_name = nam;
sym.st_value = val;
sym.st_size = sz;
sym.st_info = ELF_ST_INFO(bind,typ);
sym.st_other = 0;
sym.st_shndx = sec;
SYMbuf->write(&sym,sizeof(sym));
}
else
{
if (!SYMbuf)
{ SYMbuf = new Outbuffer(50 * sizeof(Elf32_Sym));
SYMbuf->reserve(100 * sizeof(Elf32_Sym));
}
Elf32_Sym sym;
sym.st_name = nam;
sym.st_value = val;
sym.st_size = sz;
sym.st_info = ELF_ST_INFO(bind,typ);
sym.st_other = 0;
sym.st_shndx = sec;
SYMbuf->write(&sym,sizeof(sym));
}
if (bind == STB_LOCAL)
local_cnt++;
//dbg_printf("\treturning symbol table index %d\n",symbol_idx);
return symbol_idx++;
}
/*******************************
* Create a new section header table entry.
*
* Input:
* name = section name
* suffix = suffix for name or NULL
* type = type of data in section sh_type
* flags = attribute flags sh_flags
* Output:
* section_cnt = assigned number for this section
* Note: Sections will be reordered on output
*/
static IDXSEC elf_newsection2(
elf_u32_f32 name,
elf_u32_f32 type,
elf_u32_f32 flags,
elf_add_f32 addr,
elf_off_f32 offset,
elf_u32_f32 size,
elf_u32_f32 link,
elf_u32_f32 info,
elf_u32_f32 addralign,
elf_u32_f32 entsize)
{
Elf32_Shdr sec;
sec.sh_name = name;
sec.sh_type = type;
sec.sh_flags = flags;
sec.sh_addr = addr;
sec.sh_offset = offset;
sec.sh_size = size;
sec.sh_link = link;
sec.sh_info = info;
sec.sh_addralign = addralign;
sec.sh_entsize = entsize;
if (!SECbuf)
{ SECbuf = new Outbuffer(4 * sizeof(Elf32_Shdr));
SECbuf->reserve(16 * sizeof(Elf32_Shdr));
}
SECbuf->write((void *)&sec, sizeof(sec));
return section_cnt++;
}
static IDXSEC elf_newsection(const char *name, const char *suffix,
elf_u32_f32 type, elf_u32_f32 flags)
{
// dbg_printf("elf_newsection(%s,%s,type %d, flags x%x)\n",
// name?name:"",suffix?suffix:"",type,flags);
IDXSTR namidx = section_names->size();
section_names->writeString(name);
if (suffix)
{ // Append suffix string
section_names->setsize(section_names->size() - 1); // back up over terminating 0
section_names->writeString(suffix);
}
IDXSTR *pidx = (IDXSTR *)section_names_hashtable->get(&namidx);
assert(!*pidx); // must not already exist
*pidx = namidx;
return elf_newsection2(namidx,type,flags,0,0,0,0,0,0,0);
}
/**************************
* Ouput read only data and generate a symbol for it.
*
*/
symbol *elf_sym_cdata(tym_t ty,char *p,int len)
{
symbol *s;
#if 0
if (OPT_IS_SET(OPTfwritable_strings))
{
alignOffset(DATA, tysize(ty));
s = symboldata(Doffset, ty);
SegData[DATA]->SDbuf->write(p,len);
s->Sseg = DATA;
s->Soffset = Doffset; // Remember its offset into DATA section
Doffset += len;
}
else
#endif
{
//printf("elf_sym_cdata(ty = %x, p = %x, len = %d, CDoffset = %x)\n", ty, p, len, CDoffset);
alignOffset(CDATA, tysize(ty));
s = symboldata(CDoffset, ty);
obj_bytes(CDATA, CDoffset, len, p);
s->Sseg = CDATA;
}
s->Sfl = /*(config.flags3 & CFG3pic) ? FLgotoff :*/ FLextern;
return s;
}
/**************************
* Ouput read only data for data
*
*/
int elf_data_cdata(char *p, int len, int *pseg)
{
int oldoff;
/*if (OPT_IS_SET(OPTfwritable_strings))
{
oldoff = Doffset;
SegData[DATA]->SDbuf->reserve(len);
SegData[DATA]->SDbuf->writen(p,len);
Doffset += len;
*pseg = DATA;
}
else*/
{
oldoff = CDoffset;
SegData[CDATA]->SDbuf->reserve(len);
SegData[CDATA]->SDbuf->writen(p,len);
CDoffset += len;
*pseg = CDATA;
}
return oldoff;
}
int elf_data_cdata(char *p, int len)
{
int pseg;
return elf_data_cdata(p, len, &pseg);
}
/******************************
* Perform initialization that applies to all .o output files.
* Called before any other obj_xxx routines
*/
void obj_init(Outbuffer *objbuf, const char *filename, const char *csegname)
{
//printf("obj_init()\n");
cseg = CODE;
fobjbuf = objbuf;
mapsec2sym = NULL;
note_data = NULL;
secidx_note = 0;
comment_data = NULL;
seg_tlsseg = UNKNOWN;
seg_tlsseg_bss = UNKNOWN;
GOTsym = NULL;
// Initialize buffers
if (symtab_strings)
symtab_strings->setsize(1);
else
{ symtab_strings = new Outbuffer(1024);
symtab_strings->reserve(2048);
symtab_strings->writeByte(0);
}
if (SECbuf)
SECbuf->setsize(0);
section_cnt = 0;
if (I64)
{
static char section_names_init64[] =
"\0.symtab\0.strtab\0.shstrtab\0.text\0.data\0.bss\0.note\0.comment\0.rodata\0.note.GNU-stack\0.rela.text\0.rela.data";
#define NAMIDX_NONE 0
#define NAMIDX_SYMTAB 1 // .symtab
#define NAMIDX_STRTAB 9 // .strtab
#define NAMIDX_SHSTRTAB 17 // .shstrtab
#define NAMIDX_TEXT 27 // .text
#define NAMIDX_DATA 33 // .data
#define NAMIDX_BSS 39 // .bss
#define NAMIDX_NOTE 44 // .note
#define NAMIDX_COMMENT 50 // .comment
#define NAMIDX_RODATA 59 // .rodata
#define NAMIDX_GNUSTACK 67 // .note.GNU-stack
#define NAMIDX_RELTEXT 83 // .rel.text and .rela.text
#define NAMIDX_RELDATA 93 // .rel.data
#define NAMIDX_RELDATA64 94 // .rela.data
if (section_names)
section_names->setsize(sizeof(section_names_init64));
else
{ section_names = new Outbuffer(512);
section_names->reserve(1024);
section_names->writen(section_names_init64, sizeof(section_names_init64));
}
if (section_names_hashtable)
delete section_names_hashtable;
section_names_hashtable = new AArray(&ti_idxstr, sizeof(IDXSTR));
// name,type,flags,addr,offset,size,link,info,addralign,entsize
elf_newsection2(0, SHT_NULL, 0, 0,0,0,0,0, 0,0);
elf_newsection2(NAMIDX_TEXT,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR,0,0,0,0,0, 4,0);
elf_newsection2(NAMIDX_RELTEXT,SHT_RELA, 0,0,0,0,SHI_SYMTAB, SHI_TEXT, 8,0x18);
elf_newsection2(NAMIDX_DATA,SHT_PROGDEF,SHF_ALLOC|SHF_WRITE, 0,0,0,0,0, 8,0);
elf_newsection2(NAMIDX_RELDATA64,SHT_RELA, 0,0,0,0,SHI_SYMTAB, SHI_DATA, 8,0x18);
elf_newsection2(NAMIDX_BSS, SHT_NOBITS,SHF_ALLOC|SHF_WRITE, 0,0,0,0,0, 16,0);
elf_newsection2(NAMIDX_RODATA,SHT_PROGDEF,SHF_ALLOC, 0,0,0,0,0, 16,0);
elf_newsection2(NAMIDX_STRTAB,SHT_STRTAB, 0, 0,0,0,0,0, 1,0);
elf_newsection2(NAMIDX_SYMTAB,SHT_SYMTAB, 0, 0,0,0,0,0, 8,0);
elf_newsection2(NAMIDX_SHSTRTAB,SHT_STRTAB, 0, 0,0,0,0,0, 1,0);
elf_newsection2(NAMIDX_COMMENT, SHT_PROGDEF,0, 0,0,0,0,0, 1,0);
elf_newsection2(NAMIDX_NOTE,SHT_NOTE, 0, 0,0,0,0,0, 1,0);
elf_newsection2(NAMIDX_GNUSTACK,SHT_PROGDEF,0, 0,0,0,0,0, 1,0);
IDXSTR namidx;
namidx = NAMIDX_TEXT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_RELTEXT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_DATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_RELDATA64; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_BSS; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_RODATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_STRTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_SYMTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_SHSTRTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_COMMENT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_NOTE; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_GNUSTACK; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
}
else
{
static char section_names_init[] =
"\0.symtab\0.strtab\0.shstrtab\0.text\0.data\0.bss\0.note\0.comment\0.rodata\0.note.GNU-stack\0.rel.text\0.rel.data";
if (section_names)
section_names->setsize(sizeof(section_names_init));
else
{ section_names = new Outbuffer(512);
section_names->reserve(100*1024);
section_names->writen(section_names_init, sizeof(section_names_init));
}
if (section_names_hashtable)
delete section_names_hashtable;
section_names_hashtable = new AArray(&ti_idxstr, sizeof(IDXSTR));
// name,type,flags,addr,offset,size,link,info,addralign,entsize
elf_newsection2(0, SHT_NULL, 0, 0,0,0,0,0, 0,0);
elf_newsection2(NAMIDX_TEXT,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR,0,0,0,0,0, 16,0);
elf_newsection2(NAMIDX_RELTEXT,SHT_REL, 0,0,0,0,SHI_SYMTAB, SHI_TEXT, 4,8);
elf_newsection2(NAMIDX_DATA,SHT_PROGDEF,SHF_ALLOC|SHF_WRITE, 0,0,0,0,0, 4,0);
elf_newsection2(NAMIDX_RELDATA,SHT_REL, 0,0,0,0,SHI_SYMTAB, SHI_DATA, 4,8);
elf_newsection2(NAMIDX_BSS, SHT_NOBITS,SHF_ALLOC|SHF_WRITE, 0,0,0,0,0, 32,0);
elf_newsection2(NAMIDX_RODATA,SHT_PROGDEF,SHF_ALLOC, 0,0,0,0,0, 1,0);
elf_newsection2(NAMIDX_STRTAB,SHT_STRTAB, 0, 0,0,0,0,0, 1,0);
elf_newsection2(NAMIDX_SYMTAB,SHT_SYMTAB, 0, 0,0,0,0,0, 4,0);
elf_newsection2(NAMIDX_SHSTRTAB,SHT_STRTAB, 0, 0,0,0,0,0, 1,0);
elf_newsection2(NAMIDX_COMMENT, SHT_PROGDEF,0, 0,0,0,0,0, 1,0);
elf_newsection2(NAMIDX_NOTE,SHT_NOTE, 0, 0,0,0,0,0, 1,0);
elf_newsection2(NAMIDX_GNUSTACK,SHT_PROGDEF,0, 0,0,0,0,0, 1,0);
IDXSTR namidx;
namidx = NAMIDX_TEXT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_RELTEXT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_DATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_RELDATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_BSS; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_RODATA; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_STRTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_SYMTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_SHSTRTAB; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_COMMENT; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_NOTE; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
namidx = NAMIDX_GNUSTACK; *(IDXSTR *)section_names_hashtable->get(&namidx) = namidx;
}
if (SYMbuf)
SYMbuf->setsize(0);
symbol_idx = 0;
local_cnt = 0;
// The symbols that every object file has
elf_addsym(0, 0, 0, STT_NOTYPE, STB_LOCAL, 0);
elf_addsym(0, 0, 0, STT_FILE, STB_LOCAL, SHT_ABS); // STI_FILE
elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_TEXT); // STI_TEXT
elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_DATA); // STI_DATA
elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_BSS); // STI_BSS
elf_addsym(0, 0, 0, STT_NOTYPE, STB_LOCAL, SHI_TEXT); // STI_GCC
elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_RODAT); // STI_RODAT
elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_NOTE); // STI_NOTE
elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, SHI_COM); // STI_COM
// Initialize output buffers for CODE, DATA and COMMENTS
// (NOTE not supported, BSS not required)
seg_count = 0;
elf_getsegment2(SHI_TEXT, STI_TEXT, SHI_RELTEXT);
assert(SegData[CODE]->SDseg == CODE);
elf_getsegment2(SHI_DATA, STI_DATA, SHI_RELDATA);
assert(SegData[DATA]->SDseg == DATA);
elf_getsegment2(SHI_RODAT, STI_RODAT, 0);
assert(SegData[CDATA]->SDseg == CDATA);
elf_getsegment2(SHI_BSS, STI_BSS, 0);
assert(SegData[UDATA]->SDseg == UDATA);
elf_getsegment2(SHI_COM, STI_COM, 0);
assert(SegData[COMD]->SDseg == COMD);
if (config.fulltypes)
dwarf_initfile(filename);
}
/**************************
* Initialize the start of object output for this particular .o file.
*
* Input:
* filename: Name of source file
* csegname: User specified default code segment name
*/
void obj_initfile(const char *filename, const char *csegname, const char *modname)
{
//dbg_printf("obj_initfile(filename = %s, modname = %s)\n",filename,modname);
IDXSTR name = elf_addstr(symtab_strings, filename);
if (I64)
SymbolTable64[STI_FILE].st_name = name;
else
SymbolTable[STI_FILE].st_name = name;
#if 0
// compiler flag for linker
if (I64)
SymbolTable64[STI_GCC].st_name = elf_addstr(symtab_strings,"gcc2_compiled.");
else
SymbolTable[STI_GCC].st_name = elf_addstr(symtab_strings,"gcc2_compiled.");
#endif
if (csegname && *csegname && strcmp(csegname,".text"))
{ // Define new section and make it the default for cseg segment
// NOTE: cseg is initialized to CODE
IDXSEC newsecidx;
Elf32_Shdr *newtextsec;
IDXSYM newsymidx;
SegData[cseg]->SDshtidx = newsecidx =
elf_newsection(csegname,0,SHT_PROGDEF,SHF_ALLOC|SHF_EXECINSTR);
newtextsec = &SecHdrTab[newsecidx];
newtextsec->sh_addralign = 4;
SegData[cseg]->SDsymidx =
elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, newsecidx);
}
if (config.fulltypes)
dwarf_initmodule(filename, modname);
}
/***************************
* Renumber symbols so they are
* ordered as locals, weak and then global
* Returns:
* sorted symbol table, caller must free with util_free()
*/
void *elf_renumbersyms()
{ void *symtab;
int nextlocal = 0;
int nextglobal = local_cnt;
SYMIDX *sym_map = (SYMIDX *)util_malloc(sizeof(SYMIDX),symbol_idx);
if (I64)
{
Elf64_Sym *oldsymtab = (Elf64_Sym *)SYMbuf->buf;
Elf64_Sym *symtabend = oldsymtab+symbol_idx;
symtab = util_malloc(sizeof(Elf64_Sym),symbol_idx);
Elf64_Sym *sl = (Elf64_Sym *)symtab;
Elf64_Sym *sg = sl + local_cnt;
int old_idx = 0;
for(Elf64_Sym *s = oldsymtab; s != symtabend; s++)
{ // reorder symbol and map new #s to old
int bind = ELF_ST_BIND(s->st_info);
if (bind == STB_LOCAL)
{
*sl++ = *s;
sym_map[old_idx] = nextlocal++;
}
else
{
*sg++ = *s;
sym_map[old_idx] = nextglobal++;
}
old_idx++;
}
}
else
{
Elf32_Sym *oldsymtab = (Elf32_Sym *)SYMbuf->buf;
Elf32_Sym *symtabend = oldsymtab+symbol_idx;
symtab = util_malloc(sizeof(Elf32_Sym),symbol_idx);
Elf32_Sym *sl = (Elf32_Sym *)symtab;
Elf32_Sym *sg = sl + local_cnt;
int old_idx = 0;
for(Elf32_Sym *s = oldsymtab; s != symtabend; s++)
{ // reorder symbol and map new #s to old
int bind = ELF_ST_BIND(s->st_info);
if (bind == STB_LOCAL)
{
*sl++ = *s;
sym_map[old_idx] = nextlocal++;
}
else
{
*sg++ = *s;
sym_map[old_idx] = nextglobal++;
}
old_idx++;
}
}
// Renumber the relocations
for (int i = 1; i <= seg_count; i++)
{ // Map indicies in the segment table
seg_data *pseg = SegData[i];
pseg->SDsymidx = sym_map[pseg->SDsymidx];
if (pseg->SDrel)
{
if (I64)
{
Elf64_Rela *rel = (Elf64_Rela *) pseg->SDrel->buf;
for (int r = 0; r < pseg->SDrelcnt; r++)
{
unsigned t = ELF64_R_TYPE(rel->r_info);
unsigned si = ELF64_R_SYM(rel->r_info);
assert(si < symbol_idx);
rel->r_info = ELF64_R_INFO(sym_map[si],t);
rel++;
}
}
else
{
Elf32_Rel *rel = (Elf32_Rel *) pseg->SDrel->buf;
assert(pseg->SDrelcnt == pseg->SDrel->size() / sizeof(Elf32_Rel));
for (int r = 0; r < pseg->SDrelcnt; r++)
{
unsigned t = ELF32_R_TYPE(rel->r_info);
unsigned si = ELF32_R_IDX(rel->r_info);
assert(si < symbol_idx);
rel->r_info = ELF32_R_INFO(sym_map[si],t);
rel++;
}
}
}
};
return symtab;
}
/***************************
* Fixup and terminate object file.
*/
void obj_termfile()
{
//dbg_printf("obj_termfile\n");
if (configv.addlinenumbers)
{
dwarf_termmodule();
}
}
/*********************************
* Terminate package.
*/
void obj_term()
{
//printf("obj_term()\n");
#if SCPP
if (!errcnt)
#endif
{
outfixlist(); // backpatches
}
if (configv.addlinenumbers)
{
dwarf_termfile();
}
#if SCPP
if (errcnt)
return;
#endif
// Write out the bytes for the header
static const char elf_string32[EI_NIDENT] =
{
ELFMAG0,ELFMAG1,ELFMAG2,ELFMAG3,
ELFCLASS32, // EI_CLASS
ELFDATA2LSB, // EI_DATA
EV_CURRENT, // EI_VERSION
ELFOSABI_LINUX,0, // EI_OSABI,EI_ABIVERSION
0,0,0,0,0,0,0
};
static const char elf_string64[EI_NIDENT] =
{
ELFMAG0,ELFMAG1,ELFMAG2,ELFMAG3,
ELFCLASS64, // EI_CLASS
ELFDATA2LSB, // EI_DATA
EV_CURRENT, // EI_VERSION
ELFOSABI_LINUX,0, // EI_OSABI,EI_ABIVERSION
0,0,0,0,0,0,0
};
fobjbuf->write(I64 ? elf_string64 : elf_string32, EI_NIDENT);
long foffset;
Elf32_Shdr *sechdr;
seg_data *seg;
void *symtab = elf_renumbersyms();
FILE *fd = NULL;
// Output the ELF Header
// The section header is build in the static variable elf_header
static Elf64_Ehdr elf_header =
{
ET_REL, // e_type
EM_X86_64, // e_machine
EV_CURRENT, // e_version
0, // e_entry
0, // e_phoff
0, // e_shoff
0, // e_flags
sizeof(Elf64_Ehdr) + EI_NIDENT, // e_ehsize
sizeof(Elf64_Phdr), // e_phentsize
0, // e_phnum
sizeof(Elf64_Shdr), // e_shentsize
0, // e_shnum
0 // e_shstrndx
};
int hdrsize = I64 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Hdr);
elf_header.e_shnum = section_cnt;
elf_header.e_shstrndx = SHI_SECNAMES;
fobjbuf->writezeros(hdrsize);
// Walk through sections determining size and file offsets
// Sections will be output in the following order
// Null segment
// For each Code/Data Segment
// code/data to load
// relocations without addens
// .bss
// notes
// comments
// section names table
// symbol table
// strings table
foffset = EI_NIDENT + hdrsize; // start after header
// section header table at end
//
// First output individual section data associate with program
// code and data
//
//printf("Setup offsets and sizes foffset %d\n\tsection_cnt %d, seg_count %d\n",foffset,section_cnt,seg_count);
for (int i=1; i<= seg_count; i++)
{
seg = SegData[i];
sechdr = MAP_SEG2SEC(i); // corresponding section
foffset = elf_align(fd,sechdr->sh_addralign,foffset);
if (i == UDATA) // 0, BSS never allocated
{ // but foffset as if it has
sechdr->sh_offset = foffset;
sechdr->sh_size = seg->SDoffset;
// accumulated size
continue;
}
else if (sechdr->sh_type == SHT_NOBITS) // .tbss never allocated
{
sechdr->sh_offset = foffset;
sechdr->sh_size = seg->SDoffset;
// accumulated size
continue;
}
else if (!seg->SDbuf)
continue; // For others leave sh_offset as 0
sechdr->sh_offset = foffset;
//printf("\tsection name %d,",sechdr->sh_name);
if (seg->SDbuf && seg->SDbuf->size())
{
//printf(" - size %d\n",seg->SDbuf->size());
sechdr->sh_size = seg->SDbuf->size();
fobjbuf->write(seg->SDbuf->buf, sechdr->sh_size);
foffset += sechdr->sh_size;
}
//printf(" assigned offset %d, size %d\n",foffset,sechdr->sh_size);
}
//
// Next output any notes or comments
//
if (note_data)
{
sechdr = &SecHdrTab[secidx_note]; // Notes
sechdr->sh_size = note_data->size();
sechdr->sh_offset = foffset;
fobjbuf->write(note_data->buf, sechdr->sh_size);
foffset += sechdr->sh_size;
}
if (comment_data)
{
sechdr = &SecHdrTab[SHI_COM]; // Comments
sechdr->sh_size = comment_data->size();
sechdr->sh_offset = foffset;
fobjbuf->write(comment_data->buf, sechdr->sh_size);
foffset += sechdr->sh_size;
}
//
// Then output string table for section names
//
sechdr = &SecHdrTab[SHI_SECNAMES]; // Section Names
sechdr->sh_size = section_names->size();
sechdr->sh_offset = foffset;
//dbg_printf("section names offset %d\n",foffset);
fobjbuf->write(section_names->buf, sechdr->sh_size);
foffset += sechdr->sh_size;
//
// Symbol table and string table for symbols next
//
//dbg_printf("output symbol table size %d\n",SYMbuf->size());
sechdr = &SecHdrTab[SHI_SYMTAB]; // Symbol Table
sechdr->sh_size = SYMbuf->size();
sechdr->sh_entsize = I64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
sechdr->sh_link = SHI_STRINGS;
sechdr->sh_info = local_cnt;
foffset = elf_align(fd,4,foffset);
sechdr->sh_offset = foffset;
fobjbuf->write(symtab, sechdr->sh_size);
foffset += sechdr->sh_size;
util_free(symtab);
//dbg_printf("output section strings size 0x%x,offset 0x%x\n",symtab_strings->size(),foffset);
sechdr = &SecHdrTab[SHI_STRINGS]; // Symbol Strings
sechdr->sh_size = symtab_strings->size();
sechdr->sh_offset = foffset;
fobjbuf->write(symtab_strings->buf, sechdr->sh_size);
foffset += sechdr->sh_size;
//
// Now the relocation data for program code and data sections
//
foffset = elf_align(fd,4,foffset);
//dbg_printf("output relocations size 0x%x, foffset 0x%x\n",section_names->size(),foffset);
for (int i=1; i<= seg_count; i++)
{
seg = SegData[i];
if (!seg->SDbuf)
{
// sechdr = &SecHdrTab[seg->SDrelidx];
// if (I64 && sechdr->sh_type == SHT_RELA)
// sechdr->sh_offset = foffset;
continue; // 0, BSS never allocated
}
if (seg->SDrel && seg->SDrel->size())
{
assert(seg->SDrelidx);
sechdr = &SecHdrTab[seg->SDrelidx];
sechdr->sh_size = seg->SDrel->size();
sechdr->sh_offset = foffset;
if (I64)
{
assert(seg->SDrelcnt == seg->SDrel->size() / sizeof(Elf64_Rela));
#ifdef DEBUG
for (size_t i = 0; i < seg->SDrelcnt; ++i)
{ Elf64_Rela *p = ((Elf64_Rela *)seg->SDrel->buf) + i;
if (ELF64_R_TYPE(p->r_info) == R_X86_64_64)
assert(*(Elf64_Xword *)(seg->SDbuf->buf + p->r_offset) == 0);
}
#endif
}
else
assert(seg->SDrelcnt == seg->SDrel->size() / sizeof(Elf32_Rel));
fobjbuf->write(seg->SDrel->buf, sechdr->sh_size);
foffset += sechdr->sh_size;
}
}
//
// Finish off with the section header table
//
elf_header.e_shoff = foffset; // remember location in elf header
//dbg_printf("output section header table\n");
// Output the completed Section Header Table
if (I64)
{ // Translate section headers to 64 bits
int sz = section_cnt * sizeof(Elf64_Shdr);
fobjbuf->reserve(sz);
for (int i = 0; i < section_cnt; i++)
{
Elf32_Shdr *p = SecHdrTab + i;
Elf64_Shdr s;
s.sh_name = p->sh_name;
s.sh_type = p->sh_type;
s.sh_flags = p->sh_flags;
s.sh_addr = p->sh_addr;
s.sh_offset = p->sh_offset;
s.sh_size = p->sh_size;
s.sh_link = p->sh_link;
s.sh_info = p->sh_info;
s.sh_addralign = p->sh_addralign;
s.sh_entsize = p->sh_entsize;
fobjbuf->write(&s, sizeof(s));
}
foffset += sz;
}
else
{
fobjbuf->write(SecHdrTab, section_cnt * sizeof(Elf32_Shdr));
foffset += section_cnt * sizeof(Elf32_Shdr);
}
//
// Now that we have correct offset to section header table, e_shoff,
// go back and re-output the elf header
//
fobjbuf->position(EI_NIDENT, hdrsize);
if (I64)
{
fobjbuf->write(&elf_header, hdrsize);
}
else
{ Elf32_Hdr h;
// Transfer to 32 bit header
h.e_type = elf_header.e_type;
h.e_machine = EM_386;
h.e_version = elf_header.e_version;
h.e_entry = elf_header.e_entry;
h.e_phoff = elf_header.e_phoff;
h.e_shoff = elf_header.e_shoff;
h.e_flags = elf_header.e_flags;
h.e_ehsize = sizeof(Elf32_Hdr) + EI_NIDENT;
h.e_phentsize = sizeof(elf_pht);
h.e_phnum = elf_header.e_phnum;
h.e_shentsize = sizeof(Elf32_Shdr);
h.e_shnum = elf_header.e_shnum;
h.e_shstrndx = elf_header.e_shstrndx;
fobjbuf->write(&h, hdrsize);
}
fobjbuf->position(foffset, 0);
fobjbuf->flush();
}
/*****************************
* Line number support.
*/
/***************************
* Record file and line number at segment and offset.
* The actual .debug_line segment is put out by dwarf_termfile().
* Input:
* cseg current code segment
*/
void objlinnum(Srcpos srcpos, targ_size_t offset)
{
if (srcpos.Slinnum == 0)
return;
#if 0
#if MARS || SCPP
printf("objlinnum(cseg=%d, offset=0x%lx) ", cseg, offset);
#endif
srcpos.print("");
#endif
#if MARS
if (!srcpos.Sfilename)
return;
#endif
#if SCPP
if (!srcpos.Sfilptr)
return;
sfile_debug(&srcpos_sfile(srcpos));
Sfile *sf = *srcpos.Sfilptr;
#endif
size_t i;
seg_data *seg = SegData[cseg];
// Find entry i in SDlinnum_data[] that corresponds to srcpos filename
for (i = 0; 1; i++)
{
if (i == seg->SDlinnum_count)
{ // Create new entry
if (seg->SDlinnum_count == seg->SDlinnum_max)
{ // Enlarge array
unsigned newmax = seg->SDlinnum_max * 2 + 1;
//printf("realloc %d\n", newmax * sizeof(linnum_data));
seg->SDlinnum_data = (linnum_data *)mem_realloc(
seg->SDlinnum_data, newmax * sizeof(linnum_data));
memset(seg->SDlinnum_data + seg->SDlinnum_max, 0,
(newmax - seg->SDlinnum_max) * sizeof(linnum_data));
seg->SDlinnum_max = newmax;
}
seg->SDlinnum_count++;
#if MARS
seg->SDlinnum_data[i].filename = srcpos.Sfilename;
#endif
#if SCPP
seg->SDlinnum_data[i].filptr = sf;
#endif
break;
}
#if MARS
if (seg->SDlinnum_data[i].filename == srcpos.Sfilename)
#endif
#if SCPP
if (seg->SDlinnum_data[i].filptr == sf)
#endif
break;
}
linnum_data *ld = &seg->SDlinnum_data[i];
// printf("i = %d, ld = x%x\n", i, ld);
if (ld->linoff_count == ld->linoff_max)
{
if (!ld->linoff_max)
ld->linoff_max = 8;
ld->linoff_max *= 2;
ld->linoff = (unsigned (*)[2])mem_realloc(ld->linoff, ld->linoff_max * sizeof(unsigned) * 2);
}
ld->linoff[ld->linoff_count][0] = srcpos.Slinnum;
ld->linoff[ld->linoff_count][1] = offset;
ld->linoff_count++;
}
/*******************************
* Set start address
*/
void obj_startaddress(Symbol *s)
{
//dbg_printf("obj_startaddress(Symbol *%s)\n",s->Sident);
//obj.startaddress = s;
}
/*******************************
* Output library name.
* Output:
*/
void obj_includelib(const char *name)
{
//dbg_printf("obj_includelib(name *%s)\n",name);
}
/**************************
* Embed string in executable.
*/
void obj_exestr(const char *p)
{
//dbg_printf("obj_exestr(char *%s)\n",p);
}
/**************************
* Embed string in obj.
*/
void obj_user(const char *p)
{
//dbg_printf("obj_user(char *%s)\n",p);
}
/*******************************
* Output a weak extern record.
*/
void obj_wkext(Symbol *s1,Symbol *s2)
{
//dbg_printf("obj_wkext(Symbol *%s,Symbol *s2)\n",s1->Sident,s2->Sident);
}
/*******************************
* Output file name record.
*
* Currently assumes that obj_filename will not be called
* twice for the same file.
*/
void obj_filename(const char *modname)
{
//dbg_printf("obj_filename(char *%s)\n",modname);
unsigned strtab_idx = elf_addstr(symtab_strings,modname);
elf_addsym(strtab_idx,0,0,STT_FILE,STB_LOCAL,SHT_ABS);
}
/*******************************
* Embed compiler version in .obj file.
*/
void obj_compiler()
{
//dbg_printf("obj_compiler\n");
comment_data = new Outbuffer();
comment_data->write(compiler,sizeof(compiler));
//dbg_printf("Comment data size %d\n",comment_data->size());
}
//#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 !=0 if leave space for static destructor
* seg 1: user
* 2: lib
* 3: compiler
*/
void obj_staticctor(Symbol *s,int dtor,int none)
{
// Static constructors and destructors
IDXSEC seg;
Outbuffer *buf;
//dbg_printf("obj_staticctor(%s) offset %x\n",s->Sident,s->Soffset);
//symbol_print(s);
s->Sseg = seg =
elf_getsegment(".ctors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4);
buf = SegData[seg]->SDbuf;
if (I64)
buf->write64(s->Soffset);
else
buf->write32(s->Soffset);
elf_addrel(seg,SegData[seg]->SDoffset,I64 ? R_X86_64_64 : RI_TYPE_SYM32,STI_TEXT,0);
SegData[seg]->SDoffset = buf->size();
}
/**************************************
* Symbol is the function that calls the static destructors.
* Put a pointer to it into a special segment that the exit code
* looks at.
* Input:
* s static destructor function
*/
void obj_staticdtor(Symbol *s)
{
IDXSEC seg;
Outbuffer *buf;
//dbg_printf("obj_staticdtor(%s) offset %x\n",s->Sident,s->Soffset);
//symbol_print(s);
seg = elf_getsegment(".dtors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, 4);
buf = SegData[seg]->SDbuf;
if (I64)
buf->write64(s->Soffset);
else
buf->write32(s->Soffset);
elf_addrel(seg,SegData[seg]->SDoffset,I64 ? R_X86_64_64 : RI_TYPE_SYM32,s->Sxtrnnum,0);
SegData[seg]->SDoffset = buf->size();
}
//#else
/***************************************
* Stuff pointer to function in its own segment.
* Used for static ctor and dtor lists.
*/
void obj_funcptr(Symbol *s)
{
//dbg_printf("obj_funcptr(%s) \n",s->Sident);
}
//#endif
/***************************************
* Stuff the following data in a separate segment:
* pointer to function
* pointer to ehsym
* length of function
*/
void obj_ehtables(Symbol *sfunc,targ_size_t size,Symbol *ehsym)
{
//dbg_printf("obj_ehtables(%s) \n",sfunc->Sident);
symbol *ehtab_entry = symbol_generate(SCstatic,type_alloc(TYint));
symbol_keep(ehtab_entry);
elf_getsegment(".deh_beg", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE);
int seg = elf_getsegment(".deh_eh", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE);
ehtab_entry->Sseg = seg;
Outbuffer *buf = SegData[seg]->SDbuf;
elf_getsegment(".deh_end", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE);
ehtab_entry->Stype->Tmangle = mTYman_c;
ehsym->Stype->Tmangle = mTYman_c;
assert(sfunc->Sxtrnnum && sfunc->Sseg);
assert(ehsym->Sxtrnnum && ehsym->Sseg);
if (I64)
{
elf_addrel(seg, buf->size(), R_X86_64_64, MAP_SEG2SYMIDX(sfunc->Sseg), sfunc->Soffset);
buf->write64(0);
elf_addrel(seg, buf->size(), R_X86_64_64, MAP_SEG2SYMIDX(ehsym->Sseg), ehsym->Soffset);
buf->write64(0);
buf->write64(sfunc->Ssize);
}
else
{
elf_addrel(seg, buf->size(), RI_TYPE_SYM32, MAP_SEG2SYMIDX(sfunc->Sseg), 0);
buf->write32(sfunc->Soffset);
elf_addrel(seg, buf->size(), RI_TYPE_SYM32, MAP_SEG2SYMIDX(ehsym->Sseg), 0);
buf->write32(ehsym->Soffset);
buf->write32(sfunc->Ssize);
}
}
/*********************************************
* Put out symbols that define the beginning/end of the .deh_eh section.
*/
void obj_ehsections()
{
int sec = elf_getsegment(".deh_beg", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE);
//obj_bytes(sec, 0, 4, NULL);
IDXSTR namidx = elf_addstr(symtab_strings,"_deh_beg");
elf_addsym(namidx, 0, 4, STT_OBJECT, STB_GLOBAL, MAP_SEG2SECIDX(sec));
//elf_addsym(namidx, 0, 4, STT_OBJECT, STB_GLOBAL, MAP_SEG2SECIDX(sec));
elf_getsegment(".deh_eh", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE);
sec = elf_getsegment(".deh_end", NULL, SHT_PROGDEF, SHF_ALLOC, NPTRSIZE);
namidx = elf_addstr(symtab_strings,"_deh_end");
elf_addsym(namidx, 0, 4, STT_OBJECT, STB_GLOBAL, MAP_SEG2SECIDX(sec));
obj_tlssections();
}
/*********************************************
* Put out symbols that define the beginning/end of the thread local storage sections.
*/
void obj_tlssections()
{
IDXSTR namidx;
int align = I64 ? 16 : 4;
int sec = elf_getsegment(".tdata", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, align);
obj_bytes(sec, 0, align, NULL);
namidx = elf_addstr(symtab_strings,"_tlsstart");
elf_addsym(namidx, 0, align, STT_TLS, STB_GLOBAL, MAP_SEG2SECIDX(sec));
elf_getsegment(".tdata.", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, align);
sec = elf_getsegment(".tcommon", NULL, SHT_NOBITS, SHF_ALLOC|SHF_WRITE|SHF_TLS, align);
namidx = elf_addstr(symtab_strings,"_tlsend");
elf_addsym(namidx, 0, align, STT_TLS, STB_GLOBAL, MAP_SEG2SECIDX(sec));
}
/*********************************
* 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
*/
STATIC void setup_comdat(Symbol *s)
{
const char *prefix;
int type;
int flags;
int align = 4;
//printf("obj_comdat(Symbol *%s\n",s->Sident);
//symbol_print(s);
symbol_debug(s);
if (tyfunc(s->ty()))
{
//s->Sfl = FLcode; // was FLoncecode
//prefix = ".gnu.linkonce.t"; // doesn't work, despite documentation
prefix = ".text."; // undocumented, but works
type = SHT_PROGDEF;
flags = SHF_ALLOC|SHF_EXECINSTR;
}
else if ((s->ty() & mTYLINK) == mTYthread)
{
/* Ensure that ".tdata" precedes any other .tdata. section, as the ld
* linker script fails to work right.
*/
if (I64)
align = 16;
elf_getsegment(".tdata", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, align);
s->Sfl = FLtlsdata;
prefix = ".tdata.";
type = SHT_PROGDEF;
flags = SHF_ALLOC|SHF_WRITE|SHF_TLS;
}
else
{
if (I64)
align = 16;
s->Sfl = FLdata;
//prefix = ".gnu.linkonce.d.";
prefix = ".data.";
type = SHT_PROGDEF;
flags = SHF_ALLOC|SHF_WRITE;
}
s->Sseg = elf_getsegment(prefix, cpp_mangle(s), type, flags, align);
// find or create new segment
SegData[s->Sseg]->SDsym = s;
}
int obj_comdat(Symbol *s)
{
setup_comdat(s);
if (s->Sfl == FLdata || s->Sfl == FLtlsdata)
{
objpubdef(s->Sseg,s,0);
searchfixlist(s); // backpatch any refs to this symbol
}
return s->Sseg;
}
int obj_comdatsize(Symbol *s, targ_size_t symsize)
{
setup_comdat(s);
if (s->Sfl == FLdata || s->Sfl == FLtlsdata)
{
objpubdefsize(s->Sseg,s,0,symsize);
searchfixlist(s); // backpatch any refs to this symbol
}
return s->Sseg;
}
/********************************
* Get a segment for a segment name.
* Input:
* name name of segment, if NULL then revert to default name
* suffix append to name
* align alignment
* Returns:
* segment index of found or newly created segment
*/
int elf_getsegment2(IDXSEC shtidx, IDXSYM symidx, IDXSEC relidx)
{
//printf("SegData = %p\n", SegData);
int seg = ++seg_count;
if (seg_count >= seg_max)
{ // need more room in segment table
seg_max += OB_SEG_INC;
SegData = (seg_data **)mem_realloc(SegData,seg_max * sizeof(seg_data *));
memset(&SegData[seg_count], 0, (seg_max - seg_count) * sizeof(seg_data *));
}
assert(seg_count < seg_max);
if (!SegData[seg])
{ SegData[seg] = (seg_data *)mem_calloc(sizeof(seg_data));
//printf("test2: SegData[%d] = %p\n", seg, SegData[seg]);
}
seg_data *pseg = SegData[seg];
pseg->SDseg = seg;
pseg->SDshtidx = shtidx;
pseg->SDoffset = 0;
if (pseg->SDbuf)
pseg->SDbuf->setsize(0);
else
{ if (SecHdrTab[shtidx].sh_type != SHT_NOBITS)
{ pseg->SDbuf = new Outbuffer(OB_XTRA_STR);
pseg->SDbuf->reserve(1024);
}
}
if (pseg->SDrel)
pseg->SDrel->setsize(0);
pseg->SDsymidx = symidx;
pseg->SDrelidx = relidx;
pseg->SDrelmaxoff = 0;
pseg->SDrelindex = 0;
pseg->SDrelcnt = 0;
pseg->SDshtidxout = 0;
pseg->SDsym = NULL;
pseg->SDaranges_offset = 0;
pseg->SDlinnum_count = 0;
return seg;
}
int elf_getsegment(const char *name, const char *suffix, int type, int flags,
int align)
{
//printf("elf_getsegment(%s,%s,flags %x, align %d)\n",name,suffix,flags,align);
// Add name~suffix to the section_names table
IDXSTR namidx = section_names->size();
section_names->writeString(name);
if (suffix)
{ // Append suffix string
section_names->setsize(section_names->size() - 1); // back up over terminating 0
section_names->writeString(suffix);
}
IDXSTR *pidx = (IDXSTR *)section_names_hashtable->get(&namidx);
if (*pidx)
{ // this section name already exists
section_names->setsize(namidx); // remove addition
namidx = *pidx;
for (int seg = CODE; seg <= seg_count; seg++)
{ // should be in segment table
if (MAP_SEG2SEC(seg)->sh_name == namidx)
{
return seg; // found section for segment
}
}
assert(0); // but it's not a segment
// FIX - should be an error message conflict with section names
}
*pidx = namidx;
//dbg_printf("\tNew segment - %d size %d\n", seg,SegData[seg]->SDbuf);
IDXSEC shtidx = elf_newsection2(namidx,type,flags,0,0,0,0,0,0,0);
SecHdrTab[shtidx].sh_addralign = align;
IDXSYM symidx = elf_addsym(0, 0, 0, STT_SECTION, STB_LOCAL, shtidx);
int seg = elf_getsegment2(shtidx, symidx, 0);
//printf("-elf_getsegment() = %d\n", seg);
return seg;
}
/********************************
* 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)
{
int seg;
const char *sfx;
//dbg_printf("obj_codeseg(%s,%x)\n",name,suffix);
sfx = (suffix) ? "_TEXT" : NULL;
if (!name) // returning to default code segment
{
if (cseg != CODE) // not the current default
{
SegData[cseg]->SDoffset = Coffset;
Coffset = SegData[CODE]->SDoffset;
cseg = CODE;
}
return cseg;
}
seg = elf_getsegment(name, sfx, SHT_PROGDEF, SHF_ALLOC|SHF_EXECINSTR, 4);
// find or create code segment
cseg = seg; // new code segment index
Coffset = 0;
return seg;
}
/*********************************
* Define segments for Thread Local Storage.
* Here's what the elf tls spec says:
* Field .tbss .tdata
* sh_name .tbss .tdata
* sh_type SHT_NOBITS SHT_PROGBITS
* sh_flags SHF_ALLOC|SHF_WRITE| SHF_ALLOC|SHF_WRITE|
* SHF_TLS SHF_TLS
* sh_addr virtual addr of section virtual addr of section
* sh_offset 0 file offset of initialization image
* sh_size size of section size of section
* sh_link SHN_UNDEF SHN_UNDEF
* sh_info 0 0
* sh_addralign alignment of section alignment of section
* sh_entsize 0 0
* We want _tlsstart and _tlsend to bracket all the D tls data.
* The default linker script (ld -verbose) says:
* .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
* .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
* so if we assign names:
* _tlsstart .tdata
* symbols .tdata.
* symbols .tbss
* _tlsend .tbss.
* this should work.
* Don't care about sections emitted by other languages, as we presume they
* won't be storing D gc roots in their tls.
* Output:
* seg_tlsseg set to segment number for TLS segment.
* Returns:
* segment for TLS segment
*/
seg_data *obj_tlsseg()
{
/* Ensure that ".tdata" precedes any other .tdata. section, as the ld
* linker script fails to work right.
*/
elf_getsegment(".tdata", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE|SHF_TLS, 4);
static const char tlssegname[] = ".tdata.";
//dbg_printf("obj_tlsseg(\n");
if (seg_tlsseg == UNKNOWN)
{
seg_tlsseg = elf_getsegment(tlssegname, NULL, SHT_PROGDEF,
SHF_ALLOC|SHF_WRITE|SHF_TLS, I64 ? 16 : 4);
}
return SegData[seg_tlsseg];
}
/*********************************
* Define segments for Thread Local Storage.
* Output:
* seg_tlsseg_bss set to segment number for TLS segment.
* Returns:
* segment for TLS segment
*/
seg_data *obj_tlsseg_bss()
{
static const char tlssegname[] = ".tbss";
//dbg_printf("obj_tlsseg_bss(\n");
if (seg_tlsseg_bss == UNKNOWN)
{
seg_tlsseg_bss = elf_getsegment(tlssegname, NULL, SHT_NOBITS,
SHF_ALLOC|SHF_WRITE|SHF_TLS, I64 ? 16 : 4);
}
return SegData[seg_tlsseg_bss];
}
/*******************************
* Output an alias definition record.
*/
void obj_alias(const char *n1,const char *n2)
{
dbg_printf("obj_alias(%s,%s)\n",n1,n2);
assert(0);
#if NOT_DONE
char *buffer = (char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD);
unsigned len = obj_namestring(buffer,n1);
len += obj_namestring(buffer + len,n2);
objrecord(ALIAS,buffer,len);
#endif
}
char *unsstr(unsigned value)
{
static char buffer[64];
sprintf(buffer, "%d", value);
return buffer;
}
/*******************************
* Mangle a name.
* Returns:
* mangled name
*/
char *obj_mangle2(Symbol *s,char *dest)
{
char *name;
//dbg_printf("obj_mangle('%s'), mangle = x%x\n",s->Sident,type_mangle(s->Stype));
symbol_debug(s);
assert(dest);
#if SCPP
name = CPP ? cpp_mangle(s) : s->Sident;
#elif MARS
name = cpp_mangle(s);
#else
name = s->Sident;
#endif
size_t len = strlen(name); // # of bytes in name
//dbg_printf("len %d\n",len);
switch (type_mangle(s->Stype))
{
case mTYman_pas: // if upper case
case mTYman_for:
if (len >= DEST_LEN)
dest = (char *)mem_malloc(len + 1);
memcpy(dest,name,len + 1); // copy in name and ending 0
for (int i = 0; 1; i++)
{ char c = dest[i];
if (!c)
break;
if (c >= 'a' && c <= 'z')
dest[i] = c + 'A' - 'a';
}
break;
case mTYman_std:
#if TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS
if (tyfunc(s->ty()) && !variadic(s->Stype))
#else
if (!(config.flags4 & CFG4oldstdmangle) &&
config.exe == EX_NT && tyfunc(s->ty()) &&
!variadic(s->Stype))
#endif
{
char *pstr = unsstr(type_paramsize(s->Stype));
size_t pstrlen = strlen(pstr);
size_t destlen = len + 1 + pstrlen + 1;
if (destlen > DEST_LEN)
dest = (char *)mem_malloc(destlen);
memcpy(dest,name,len);
dest[len] = '@';
memcpy(dest + 1 + len, pstr, pstrlen + 1);
break;
}
case mTYman_cpp:
case mTYman_c:
case mTYman_d:
case mTYman_sys:
case 0:
if (len >= DEST_LEN)
dest = (char *)mem_malloc(len + 1);
memcpy(dest,name,len+1);// copy in name and trailing 0
break;
default:
#ifdef DEBUG
printf("mangling %x\n",type_mangle(s->Stype));
symbol_print(s);
#endif
printf("%d\n", type_mangle(s->Stype));
assert(0);
}
//dbg_printf("\t %s\n",dest);
return dest;
}
/*******************************
* Export a function name.
*/
void obj_export(Symbol *s,unsigned argsize)
{
//dbg_printf("obj_export(%s,%d)\n",s->Sident,argsize);
}
/*******************************
* 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 = Offset(seg);
alignbytes = align(datasize, offset) - offset;
if (alignbytes)
obj_lidata(seg, offset, alignbytes);
sdata->Soffset = offset + alignbytes;
return seg;
}
/*******************************
* Update function info before codgen
*
* If code for this function is in a different segment
* than the current default in cseg, switch cseg to new segment.
*/
void elf_func_start(Symbol *sfunc)
{
//dbg_printf("elf_func_start(%s)\n",sfunc->Sident);
symbol_debug(sfunc);
if ((tybasic(sfunc->ty()) == TYmfunc) && (sfunc->Sclass == SCextern))
{ // create a new code segment
sfunc->Sseg =
elf_getsegment(".gnu.linkonce.t.", cpp_mangle(sfunc), SHT_PROGDEF, SHF_ALLOC|SHF_EXECINSTR,4);
}
else if (sfunc->Sseg == UNKNOWN)
sfunc->Sseg = CODE;
//dbg_printf("sfunc->Sseg %d CODE %d cseg %d Coffset %d\n",sfunc->Sseg,CODE,cseg,Coffset);
cseg = sfunc->Sseg;
assert(cseg == CODE || cseg > COMD);
objpubdef(cseg, sfunc, Coffset);
sfunc->Soffset = Coffset;
if (config.fulltypes)
dwarf_func_start(sfunc);
}
/*******************************
* Update function info after codgen
*/
void elf_func_term(Symbol *sfunc)
{
//dbg_printf("elf_func_term(%s) offset %x, Coffset %x symidx %d\n",
// sfunc->Sident, sfunc->Soffset,Coffset,sfunc->Sxtrnnum);
// fill in the function size
if (I64)
SymbolTable64[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset;
else
SymbolTable[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset;
if (config.fulltypes)
dwarf_func_term(sfunc);
}
/********************************
* Output a public definition.
* Input:
* seg = segment index that symbol is defined in
* s -> symbol
* offset = offset of name within segment
*/
void objpubdef(int seg, Symbol *s, targ_size_t offset)
{
const targ_size_t symsize=
tyfunc(s->ty()) ? Offset(s->Sseg) - offset : type_size(s->Stype);
objpubdefsize(seg, s, offset, symsize);
}
/********************************
* Output a public definition.
* Input:
* seg = segment index that symbol is defined in
* s -> symbol
* offset = offset of name within segment
* symsize size of symbol
*/
void objpubdefsize(int seg, Symbol *s, targ_size_t offset, targ_size_t symsize)
{
int bind;
switch (s->Sclass)
{
case SCglobal:
case SCinline:
bind = STB_GLOBAL;
break;
case SCcomdat:
case SCcomdef:
bind = STB_WEAK;
break;
default:
bind = STB_LOCAL;
break;
}
#if 0
//printf("\nobjpubdef(%d,%s,%d)\n",seg,s->Sident,offset);
//symbol_print(s);
#endif
symbol_debug(s);
IDXSTR namidx = elf_addmangled(s);
//printf("\tnamidx %d,section %d\n",namidx,MAP_SEG2SECIDX(seg));
if (tyfunc(s->ty()))
{
s->Sxtrnnum = elf_addsym(namidx, offset, symsize,
STT_FUNC, bind, MAP_SEG2SECIDX(seg));
}
else
{
const unsigned typ = (s->ty() & mTYthread) ? STT_TLS : STT_OBJECT;
s->Sxtrnnum = elf_addsym(namidx, offset, symsize,
typ, bind, MAP_SEG2SECIDX(seg));
}
fflush(NULL);
}
/*******************************
* Output an external symbol for name.
* Input:
* name Name to do EXTDEF on
* (Not to be mangled)
* Returns:
* Symbol table index of the definition
* NOTE: Numbers will not be linear.
*/
int objextern(const char *name)
{
//dbg_printf("objextdef('%s')\n",name);
assert(name);
IDXSTR namidx = elf_addstr(symtab_strings,name);
IDXSYM symidx = elf_addsym(namidx, 0, 0, STT_NOTYPE, STB_GLOBAL, SHT_UNDEF);
return symidx;
}
int objextdef(const char *name)
{
return objextern(name);
}
/*******************************
* Output an external for existing symbol.
* Input:
* s Symbol to do EXTDEF on
* (Name is to be mangled)
* Returns:
* Symbol table index of the definition
* NOTE: Numbers will not be linear.
*/
int objextern(Symbol *s)
{
int symtype,sectype;
unsigned size;
//dbg_printf("objextern('%s') %x\n",s->Sident,s->Svalue);
symbol_debug(s);
IDXSTR namidx = elf_addmangled(s);
#if SCPP
if (s->Sscope && !tyfunc(s->ty()))
{
symtype = STT_OBJECT;
sectype = SHT_COMMON;
size = type_size(s->Stype);
}
else
#endif
{
symtype = STT_NOTYPE;
sectype = SHT_UNDEF;
size = 0;
}
if (s->ty() & mTYthread)
{
//printf("objextern('%s') %x TLS\n",s->Sident,s->Svalue);
symtype = STT_TLS;
}
s->Sxtrnnum = elf_addsym(namidx, size, size, symtype,
/*(s->ty() & mTYweak) ? STB_WEAK : */STB_GLOBAL, sectype);
return s->Sxtrnnum;
}
/*******************************
* Output a common block definition.
* Input:
* p -> external identifier
* size size in bytes of each elem
* count number of elems
* Returns:
* Symbol table index for symbol
*/
int obj_comdef(Symbol *s,targ_size_t size,targ_size_t count)
{
//printf("obj_comdef('%s',%d,%d)\n",s->Sident,size,count);
symbol_debug(s);
int align = I64 ? 16 : 4;
if (s->ty() & mTYthread)
{
s->Sseg = elf_getsegment(".tbss.", cpp_mangle(s),
SHT_NOBITS, SHF_ALLOC|SHF_WRITE|SHF_TLS, align);
s->Sfl = FLtlsdata;
SegData[s->Sseg]->SDsym = s;
SegData[s->Sseg]->SDoffset += size * count;
objpubdef(s->Sseg, s, 0);
searchfixlist(s);
return s->Sseg;
}
else
{
s->Sseg = elf_getsegment(".bss.", cpp_mangle(s),
SHT_NOBITS, SHF_ALLOC|SHF_WRITE, align);
s->Sfl = FLudata;
SegData[s->Sseg]->SDsym = s;
SegData[s->Sseg]->SDoffset += size * count;
objpubdef(s->Sseg, s, 0);
searchfixlist(s);
return s->Sseg;
}
#if 0
IDXSTR namidx = elf_addmangled(s);
alignOffset(UDATA,size);
IDXSYM symidx = elf_addsym(namidx, SegData[UDATA]->SDoffset, size*count,
(s->ty() & mTYthread) ? STT_TLS : STT_OBJECT,
STB_WEAK, SHI_BSS);
//dbg_printf("\tobj_comdef returning symidx %d\n",symidx);
s->Sseg = UDATA;
s->Sfl = FLudata;
SegData[UDATA]->SDoffset += size * count;
return symidx;
#endif
}
int obj_comdef(Symbol *s, int flag, targ_size_t size, targ_size_t count)
{
return obj_comdef(s, size, count);
}
/***************************************
* 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);
}
/***************************************
* Output an iterated data block of 0s.
*
* For boundary alignment and initialization
*/
void obj_lidata(int seg,targ_size_t offset,targ_size_t count)
{
//printf("obj_lidata(%d,%x,%d)\n",seg,offset,count);
if (seg == UDATA || seg == UNKNOWN)
{ // Use SDoffset to record size of .BSS section
SegData[UDATA]->SDoffset += count;
}
else if (MAP_SEG2SEC(seg)->sh_type == SHT_NOBITS)
{ // Use SDoffset to record size of .TBSS section
SegData[seg]->SDoffset += count;
}
else
{
obj_bytes(seg, offset, count, NULL);
}
}
/***********************************
* Append byte to segment.
*/
void obj_write_byte(seg_data *pseg, unsigned byte)
{
obj_byte(pseg->SDseg, pseg->SDoffset, byte);
}
/************************************
* Output byte to object file.
*/
void obj_byte(int seg,targ_size_t offset,unsigned byte)
{
Outbuffer *buf = SegData[seg]->SDbuf;
int save = buf->size();
//dbg_printf("obj_byte(seg=%d, offset=x%lx, byte=x%x)\n",seg,offset,byte);
buf->setsize(offset);
buf->writeByte(byte);
if (save > offset+1)
buf->setsize(save);
else
SegData[seg]->SDoffset = offset+1;
//dbg_printf("\tsize now %d\n",buf->size());
}
/***********************************
* Append bytes to segment.
*/
void obj_write_bytes(seg_data *pseg, unsigned nbytes, void *p)
{
obj_bytes(pseg->SDseg, pseg->SDoffset, nbytes, p);
}
/************************************
* Output bytes to object file.
* Returns:
* nbytes
*/
unsigned obj_bytes(int seg, targ_size_t offset, unsigned nbytes, void *p)
{
#if 0
if (!(seg >= 0 && seg <= seg_count))
{ printf("obj_bytes: seg = %d, seg_count = %d\n", seg, seg_count);
*(char*)0=0;
}
#endif
assert(seg >= 0 && seg <= seg_count);
Outbuffer *buf = SegData[seg]->SDbuf;
if (buf == NULL)
{
//dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=%d, p=x%x)\n", seg, offset, nbytes, p);
//raise(SIGSEGV);
assert(buf != NULL);
}
int save = buf->size();
//dbg_printf("obj_bytes(seg=%d, offset=x%lx, nbytes=%d, p=x%x)\n",
//seg,offset,nbytes,p);
buf->setsize(offset);
buf->reserve(nbytes);
if (p)
{
buf->writen(p,nbytes);
}
else
{ // Zero out the bytes
buf->clearn(nbytes);
}
if (save > offset+nbytes)
buf->setsize(save);
else
SegData[seg]->SDoffset = offset+nbytes;
return nbytes;
}
/*******************************
* Output a relocation entry for a segment
* Input:
* seg = where the address is going
* offset = offset within seg
* type = ELF relocation type RI_TYPE_XXXX
* index = Related symbol table index
* val = addend or displacement from address
*/
int relcnt=0;
void elf_addrel(int seg, targ_size_t offset, unsigned type,
IDXSYM symidx, targ_size_t val)
{
seg_data *segdata;
Outbuffer *buf;
IDXSEC secidx;
//assert(val == 0);
relcnt++;
//dbg_printf("%d-elf_addrel(seg %d,offset x%x,type x%x,symidx %d,val %d)\n",
//relcnt,seg, offset, type, symidx,val);
assert(seg >= 0 && seg <= seg_count);
segdata = SegData[seg];
secidx = MAP_SEG2SECIDX(seg);
assert(secidx != 0);
if (segdata->SDrel == NULL)
segdata->SDrel = new Outbuffer();
if (segdata->SDrel->size() == 0)
{ IDXSEC relidx;
if (secidx == SHI_TEXT)
relidx = SHI_RELTEXT;
else if (secidx == SHI_DATA)
relidx = SHI_RELDATA;
else
{
// Get the section name, and make a copy because
// elf_newsection() may reallocate the string buffer.
char *section_name = (char *)GET_SECTION_NAME(secidx);
int len = strlen(section_name) + 1;
char *p = (char *)alloca(len);
memcpy(p, section_name, len);
relidx = elf_newsection(I64 ? ".rela" : ".rel", p, I64 ? SHT_RELA : SHT_REL, 0);
segdata->SDrelidx = relidx;
}
if (I64)
{
/* Note that we're using Elf32_Shdr here instead of Elf64_Shdr. This is to make
* the code a bit simpler. In obj_term(), we translate the Elf32_Shdr into the proper
* Elf64_Shdr.
*/
Elf32_Shdr *relsec = &SecHdrTab[relidx];
relsec->sh_link = SHI_SYMTAB;
relsec->sh_info = secidx;
relsec->sh_entsize = sizeof(Elf64_Rela);
relsec->sh_addralign = 8;
}
else
{
Elf32_Shdr *relsec = &SecHdrTab[relidx];
relsec->sh_link = SHI_SYMTAB;
relsec->sh_info = secidx;
relsec->sh_entsize = sizeof(Elf32_Rel);
relsec->sh_addralign = 4;
}
}
if (I64)
{
Elf64_Rela rel;
rel.r_offset = offset; // build relocation information
rel.r_info = ELF64_R_INFO(symidx,type);
rel.r_addend = val;
buf = segdata->SDrel;
buf->write(&rel,sizeof(rel));
segdata->SDrelcnt++;
if (offset >= segdata->SDrelmaxoff)
segdata->SDrelmaxoff = offset;
else
{ // insert numerically
Elf64_Rela *relbuf = (Elf64_Rela *)buf->buf;
int i = relbuf[segdata->SDrelindex].r_offset > offset ? 0 : segdata->SDrelindex;
while (i < segdata->SDrelcnt)
{
if (relbuf[i].r_offset > offset)
break;
i++;
}
assert(i != segdata->SDrelcnt); // slide greater offsets down
memmove(relbuf+i+1,relbuf+i,sizeof(Elf64_Rela) * (segdata->SDrelcnt - i - 1));
*(relbuf+i) = rel; // copy to correct location
segdata->SDrelindex = i; // next entry usually greater
}
}
else
{
Elf32_Rel rel;
rel.r_offset = offset; // build relocation information
rel.r_info = ELF32_R_INFO(symidx,type);
buf = segdata->SDrel;
buf->write(&rel,sizeof(rel));
segdata->SDrelcnt++;
if (offset >= segdata->SDrelmaxoff)
segdata->SDrelmaxoff = offset;
else
{ // insert numerically
Elf32_Rel *relbuf = (Elf32_Rel *)buf->buf;
int i = relbuf[segdata->SDrelindex].r_offset > offset ? 0 : segdata->SDrelindex;
while (i < segdata->SDrelcnt)
{
if (relbuf[i].r_offset > offset)
break;
i++;
}
assert(i != segdata->SDrelcnt); // slide greater offsets down
memmove(relbuf+i+1,relbuf+i,sizeof(Elf32_Rel) * (segdata->SDrelcnt - i - 1));
*(relbuf+i) = rel; // copy to correct location
segdata->SDrelindex = i; // next entry usually greater
}
}
}
/*******************************
* 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, CFoffset64
* 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)
{
Outbuffer *buf;
int save;
buf = SegData[seg]->SDbuf;
save = buf->size();
buf->setsize(offset);
#if 0
printf("reftodatseg(seg=%d, offset=x%llx, val=x%llx,data %x, flags %x)\n",
seg,(unsigned long long)offset,(unsigned long long)val,targetdatum,flags);
#endif
/*if (OPT_IS_SET(OPTfwritable_strings))
{
elf_addrel(seg,offset,RI_TYPE_SYM32,STI_DATA,0);
}
else*/
{
int relinfo;
targ_size_t v = 0;
if (I64)
{
if (flags & CFoffset64)
{
relinfo = R_X86_64_64;
elf_addrel(seg, offset, relinfo, STI_RODAT, val);
buf->write64(0);
if (save > offset + 8)
buf->setsize(save);
return;
}
else if (MAP_SEG2TYP(seg) == CODE && config.flags3 & CFG3pic)
{ relinfo = R_X86_64_PC32;
//v = -4L;
}
else if (MAP_SEG2SEC(targetdatum)->sh_flags & SHF_TLS)
relinfo = config.flags3 & CFG3pic ? R_X86_64_TLSGD : R_X86_64_TPOFF32;
else
relinfo = R_X86_64_32;
}
else
{
if (MAP_SEG2TYP(seg) == CODE && config.flags3 & CFG3pic)
relinfo = RI_TYPE_GOTOFF;
else if (MAP_SEG2SEC(targetdatum)->sh_flags & SHF_TLS)
relinfo = config.flags3 & CFG3pic ? RI_TYPE_TLS_GD : RI_TYPE_TLS_LE;
else
relinfo = RI_TYPE_SYM32;
}
elf_addrel(seg, offset, relinfo, STI_RODAT, v);
}
buf->write32(val);
if (save > offset + 4)
buf->setsize(save);
}
/*******************************
* 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)
{
Outbuffer *buf;
int save;
int segtyp = MAP_SEG2TYP(seg);
//dbg_printf("reftocodseg(seg=%d, offset=x%lx, val=x%lx )\n",seg,offset,val);
assert(seg > 0); // COMDATs not done yet
buf = SegData[seg]->SDbuf;
save = buf->size();
buf->setsize(offset);
#if 0
if (segtyp == CODE)
{
val = val - funcsym_p->Soffset;
elf_addrel(seg,offset,RI_TYPE_PC32,funcsym_p->Sxtrnnum,0);
}
else
#endif
{
val = val - funcsym_p->Soffset;
int relinfo;
targ_size_t v = 0;
if (I64)
relinfo = (config.flags3 & CFG3pic) ? R_X86_64_PC32 : R_X86_64_32;
else
relinfo = (config.flags3 & CFG3pic) ? RI_TYPE_GOTOFF : RI_TYPE_SYM32;
elf_addrel(seg,offset, relinfo, funcsym_p->Sxtrnnum, v);
}
buf->write32(val);
if (save > offset + 4)
buf->setsize(save);
}
/*******************************
* Refer to an identifier.
* Input:
* segtyp = 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
* CFoffset64: 64 bit fixup
* CFpc32: I64: PC relative 32 bit fixup
* Returns:
* number of bytes in reference (4 or 8)
*/
int reftoident(int seg, targ_size_t offset, Symbol *s, targ_size_t val,
int flags)
{
bool external = TRUE;
Outbuffer *buf;
elf_u32_f32 relinfo,refseg;
int segtyp = MAP_SEG2TYP(seg);
//assert(val == 0);
int retsize = (flags & CFoffset64) ? 8 : 4;
targ_size_t v = 0;
#if 0
printf("\nreftoident('%s' seg %d, offset x%llx, val x%llx, flags x%x)\n",
s->Sident,seg,offset,val,flags);
dbg_printf("Sseg = %d, Sxtrnnum = %d, retsize = %d\n",s->Sseg,s->Sxtrnnum,retsize);
symbol_print(s);
#endif
tym_t ty = s->ty();
if (s->Sxtrnnum)
{ // identifier is defined somewhere else
if (I64)
{
if (SymbolTable64[s->Sxtrnnum].st_shndx != SHT_UNDEF)
external = FALSE;
}
else
{
if (SymbolTable[s->Sxtrnnum].st_shndx != SHT_UNDEF)
external = FALSE;
}
}
switch (s->Sclass)
{
case SClocstat:
buf = SegData[seg]->SDbuf;
if (I64)
{
if (s->Sfl == FLtlsdata)
relinfo = config.flags3 & CFG3pic ? R_X86_64_TLSGD : R_X86_64_TPOFF32;
else
{ relinfo = config.flags3 & CFG3pic ? R_X86_64_PC32 : R_X86_64_32;
if (flags & CFpc32)
relinfo = R_X86_64_PC32;
if (relinfo == R_X86_64_PC32)
{
assert(retsize == 4);
if (val > 0xFFFFFFFF)
{ /* The value to be added is bigger than 32 bits, so we
* transfer it to the 64 bit addend of the fixup record
*/
v = val;
val = 0;
}
}
}
}
else
{
if (s->Sfl == FLtlsdata)
relinfo = config.flags3 & CFG3pic ? RI_TYPE_TLS_GD : RI_TYPE_TLS_LE;
else
relinfo = config.flags3 & CFG3pic ? RI_TYPE_GOTOFF : RI_TYPE_SYM32;
}
if (flags & CFoffset64 && relinfo == R_X86_64_32)
{
relinfo = R_X86_64_64;
elf_addrel(seg,offset,relinfo,STI_RODAT,val + s->Soffset);
buf->write64(0);
}
else
{
elf_addrel(seg,offset,relinfo,STI_RODAT,v);
if (retsize == 8)
buf->write64(val + s->Soffset);
else
buf->write32(val + s->Soffset);
}
break;
case SCcomdat:
case_SCcomdat:
case SCstatic:
#if 0
if ((s->Sflags & SFLthunk) && s->Soffset)
{ // A thunk symbol that has be defined
assert(s->Sseg == seg);
val = (s->Soffset+val) - (offset+4);
goto outaddrval;
}
// FALL_THROUGH
#endif
case SCextern:
case SCcomdef:
case_extern:
case SCglobal:
if (!s->Sxtrnnum)
{ // not in symbol table yet - class might change
//dbg_printf("\tadding %s to fixlist\n",s->Sident);
addtofixlist(s,offset,seg,val,flags);
return retsize;
}
else
{
int save;
buf = SegData[seg]->SDbuf;
save = buf->size();
buf->setsize(offset);
if (flags & CFselfrel)
{ // only for function references within code segments
if (!external && // local definition found
s->Sseg == seg && // within same code segment
(!(config.flags3 & CFG3pic) || // not position indp code
s->Sclass == SCstatic)) // or is pic, but declared static
{ // Can use PC relative
//dbg_printf("\tdoing PC relative\n");
val = (s->Soffset+val) - (offset+4);
}
else
{
//dbg_printf("\tadding relocation\n");
if (I64)
{ relinfo = config.flags3 & CFG3pic ? R_X86_64_PLT32 : R_X86_64_PC32;
elf_addrel(seg,offset, relinfo, s->Sxtrnnum, -4);
val = 0;
}
else
{ relinfo = config.flags3 & CFG3pic ? RI_TYPE_PLT32 : RI_TYPE_PC32;
elf_addrel(seg,offset, relinfo, s->Sxtrnnum, 0);
val = (targ_size_t)-4;
}
}
}
else
{ // code to code code to data, data to code, data to data refs
refseg = s->Sxtrnnum; // default to name symbol table entry
if (s->Sclass == SCstatic)
{ // offset into .data or .bss seg
refseg = MAP_SEG2SYMIDX(s->Sseg);
// use segment symbol table entry
val += s->Soffset;
if (!(config.flags3 & CFG3pic) || // all static refs from normal code
segtyp == DATA) // or refs from data from posi indp
{
if (I64)
relinfo = (flags & CFpc32) ? R_X86_64_PC32 : R_X86_64_32;
else
relinfo = RI_TYPE_SYM32;
}
else
{
relinfo = I64 ? R_X86_64_PC32 : RI_TYPE_GOTOFF;
}
}
else if (config.flags3 & CFG3pic && s == GOTsym)
{ // relocation for Gbl Offset Tab
relinfo = I64 ? R_X86_64_NONE : RI_TYPE_GOTPC;
}
else if (segtyp == DATA)
{ // relocation from within DATA seg
relinfo = I64 ? R_X86_64_32 : RI_TYPE_SYM32;
}
else
{ // relocation from within CODE seg
if (I64)
{ if (config.flags3 & CFG3pic)
relinfo = R_X86_64_GOTPCREL;
else
relinfo = (flags & CFpc32) ? R_X86_64_PC32 : R_X86_64_32;
}
else
relinfo = config.flags3 & CFG3pic ? RI_TYPE_GOT32 : RI_TYPE_SYM32;
}
if ((s->ty() & mTYLINK) & mTYthread)
{
if (I64)
{
if (config.flags3 & CFG3pic)
{
if (s->Sclass == SCstatic || s->Sclass == SClocstat)
relinfo = R_X86_64_TLSGD; // TLS_GD?
else
relinfo = R_X86_64_TLSGD;
}
else
{
if (s->Sclass == SCstatic || s->Sclass == SClocstat)
relinfo = R_X86_64_TPOFF32;
else
relinfo = R_X86_64_GOTTPOFF;
}
}
else
{
if (config.flags3 & CFG3pic)
{
if (s->Sclass == SCstatic)
relinfo = RI_TYPE_TLS_LE; // TLS_GD?
else
relinfo = RI_TYPE_TLS_IE;
}
else
{
if (s->Sclass == SCstatic)
relinfo = RI_TYPE_TLS_LE;
else
relinfo = RI_TYPE_TLS_IE;
}
}
}
targ_size_t v = 0;
if (flags & CFoffset64 && relinfo == R_X86_64_32)
{
// The value to be added must only reside in the 64 bit addend.
relinfo = R_X86_64_64;
v = val;
val = 0;
}
//printf("\t\t************* adding relocation\n");
if (I64 && retsize == 4)
{
assert(retsize == 4);
if (val > 0xFFFFFFFF)
{ /* The value to be added is bigger than 32 bits, so we
* transfer it to the 64 bit addend of the fixup record
*/
v = val;
val = 0;
}
}
#if 0
targ_size_t v = (relinfo == R_X86_64_PC32) ? -4 : 0;
if (relinfo == R_X86_64_PC32 && flags & CFaddend8)
v = -8;
#endif
assert(!(I64 && relinfo == R_X86_64_64) || val == 0);
elf_addrel(seg,offset,relinfo,refseg,v);
}
outaddrval:
if (retsize == 8)
buf->write64(val);
else
buf->write32(val);
if (save > offset + retsize)
buf->setsize(save);
}
break;
case SCsinline:
case SCeinline:
printf ("Undefined inline value <<fixme>>\n");
//warerr(WM_undefined_inline,s->Sident);
case SCinline:
if (tyfunc(ty))
{
s->Sclass = SCextern;
goto case_extern;
}
else if (config.flags2 & CFG2comdat)
goto case_SCcomdat; // treat as initialized common block
default:
#ifdef DEBUG
//symbol_print(s);
#endif
assert(0);
}
return retsize;
}
/*****************************************
* Generate far16 thunk.
* Input:
* s Symbol to generate a thunk for
*/
void obj_far16thunk(Symbol *s)
{
//dbg_printf("obj_far16thunk('%s')\n", s->Sident);
assert(0);
}
/**************************************
* Mark object file as using floating point.
*/
void obj_fltused()
{
//dbg_printf("obj_fltused()\n");
}
/************************************
* Close and delete .OBJ file.
*/
void objfile_delete()
{
//remove(fobjname); // delete corrupt output file
}
/**********************************
* Terminate.
*/
void objfile_term()
{
#if TERMCODE
mem_free(fobjname);
fobjname = NULL;
#endif
}
/**********************************
* Write to the object file
*/
void objfile_write(FILE *fd, void *buffer, unsigned len)
{
fobjbuf->write(buffer, len);
}
long elf_align(FILE *fd, targ_size_t size,long foffset)
{
long offset;
switch (size)
{
case 0:
case 1:
return foffset;
case 4:
offset = (foffset + 3) & ~3;
break;
case 8:
offset = (foffset + 7) & ~7;
break;
case 16:
offset = (foffset + 15) & ~15;
break;
case 32:
offset = (foffset + 31) & ~31;
break;
default:
dbg_printf("size was %d\n",(int)size);
assert(0);
break;
}
if (offset > foffset)
fobjbuf->writezeros(offset - foffset);
return offset;
}
/***************************************
* Stuff pointer to ModuleInfo in its own segment.
*/
#if MARS
void obj_moduleinfo(Symbol *scc)
{
// if (I64) return; // for now, until Phobos64 works
int codeOffset, refOffset;
/* Put in the ModuleReference. */
{
/* struct ModuleReference
* {
* void* next;
* ModuleReference* module;
* }
*/
const int seg = DATA;
alignOffset(seg, NPTRSIZE);
SegData[seg]->SDoffset = SegData[seg]->SDbuf->size();
refOffset = SegData[seg]->SDoffset;
SegData[seg]->SDbuf->writezeros(NPTRSIZE);
SegData[seg]->SDoffset += NPTRSIZE;
SegData[seg]->SDoffset += reftoident(seg, SegData[seg]->SDoffset, scc, 0, CFoffset64 | CFoff);
}
/* Constructor that links the ModuleReference to the head of
* the list pointed to by _Dmoduleref
*/
{
/* ret
* codeOffset:
* pushad
* mov EAX,&ModuleReference
* mov ECX,_DmoduleRef
* mov EDX,[ECX]
* mov [EAX],EDX
* mov [ECX],EAX
* popad
* ret
*/
const int seg = CODE;
Outbuffer *buf = SegData[seg]->SDbuf;
SegData[seg]->SDoffset = buf->size();
codeOffset = SegData[seg]->SDoffset;
if (I64 && config.flags3 & CFG3pic)
{ // LEA RAX,ModuleReference[RIP]
buf->writeByte(REX | REX_W);
buf->writeByte(0x8D);
buf->writeByte(modregrm(0,AX,5));
buf->write32(refOffset);
elf_addrel(seg, codeOffset + 3, R_X86_64_PC32, STI_DATA, -4);
// MOV RCX,_DmoduleRef@GOTPCREL[RIP]
buf->writeByte(REX | REX_W);
buf->writeByte(0x8B);
buf->writeByte(modregrm(0,CX,5));
buf->write32(0);
elf_addrel(seg, codeOffset + 10, R_X86_64_GOTPCREL, objextern("_Dmodule_ref"), -4);
}
else
{
const int reltype = I64 ? R_X86_64_32 : RI_TYPE_SYM32;
/* movl ModuleReference*, %eax */
buf->writeByte(0xB8);
buf->write32(refOffset);
elf_addrel(seg, codeOffset + 1, reltype, STI_DATA, 0);
/* movl _Dmodule_ref, %ecx */
buf->writeByte(0xB9);
buf->write32(0);
elf_addrel(seg, codeOffset + 6, reltype, objextern("_Dmodule_ref"), 0);
}
if (I64)
buf->writeByte(REX | REX_W);
buf->writeByte(0x8B); buf->writeByte(0x11); /* movl (%ecx), %edx */
if (I64)
buf->writeByte(REX | REX_W);
buf->writeByte(0x89); buf->writeByte(0x10); /* movl %edx, (%eax) */
if (I64)
buf->writeByte(REX | REX_W);
buf->writeByte(0x89); buf->writeByte(0x01); /* movl %eax, (%ecx) */
buf->writeByte(0xC3); /* ret */
SegData[seg]->SDoffset = buf->size();
}
/* Add reference to constructor into ".ctors" segment
*/
const int seg = elf_getsegment(".ctors", NULL, SHT_PROGDEF, SHF_ALLOC|SHF_WRITE, NPTRSIZE);
Outbuffer *buf = SegData[seg]->SDbuf;
if (I64)
{
elf_addrel(seg, SegData[seg]->SDoffset, R_X86_64_64, STI_TEXT, codeOffset);
buf->write64(0);
}
else
{
elf_addrel(seg, SegData[seg]->SDoffset, RI_TYPE_SYM32, STI_TEXT, 0);
buf->write32(codeOffset);
}
SegData[seg]->SDoffset += NPTRSIZE;
}
#endif
/*************************************
*/
void elfobj_gotref(symbol *s)
{
//printf("elfobj_gotref(%x '%s', %d)\n",s,s->Sident, s->Sclass);
switch(s->Sclass)
{
case SCstatic:
case SClocstat:
s->Sfl = FLgotoff;
break;
case SCextern:
case SCglobal:
case SCcomdat:
case SCcomdef:
s->Sfl = FLgot;
break;
default:
break;
}
}
#endif
#endif