mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-01-14 11:53:13 +01:00
3112 lines
95 KiB
C
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
|