mirror of
https://github.com/xomboverlord/ldc.git
synced 2026-03-30 16:49:03 +02:00
2758 lines
84 KiB
C
2758 lines
84 KiB
C
|
|
// Copyright (c) 2009-2011 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright
|
|
// http://www.digitalmars.com
|
|
// License for redistribution is by either the Artistic License
|
|
// in artistic.txt, or the GNU General Public License in gpl.txt.
|
|
// See the included readme.txt for details.
|
|
|
|
|
|
#if SCPP || MARS
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
|
|
#if _WIN32 || linux
|
|
#include <malloc.h>
|
|
#endif
|
|
|
|
#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4
|
|
#include <signal.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if __APPLE__
|
|
#import <CoreServices/CoreServices.h>
|
|
|
|
const SInt32 MacOSX_10_5 = 0x1050;
|
|
const SInt32 MacOSX_10_6 = 0x1060;
|
|
#endif
|
|
|
|
#include "cc.h"
|
|
#include "global.h"
|
|
#include "code.h"
|
|
#include "type.h"
|
|
#include "mach.h"
|
|
#include "outbuf.h"
|
|
#include "filespec.h"
|
|
#include "cv4.h"
|
|
#include "cgcv.h"
|
|
#include "dt.h"
|
|
|
|
#include "aa.h"
|
|
#include "tinfo.h"
|
|
|
|
#if MACHOBJ
|
|
|
|
#if MARS
|
|
#include "mars.h"
|
|
#endif
|
|
|
|
#include "mach.h"
|
|
#include "dwarf.h"
|
|
|
|
// for x86_64
|
|
#define X86_64_RELOC_UNSIGNED 0
|
|
#define X86_64_RELOC_SIGNED 1
|
|
#define X86_64_RELOC_BRANCH 2
|
|
#define X86_64_RELOC_GOT_LOAD 3
|
|
#define X86_64_RELOC_GOT 4
|
|
#define X86_64_RELOC_SUBTRACTOR 5
|
|
#define X86_64_RELOC_SIGNED_1 6
|
|
#define X86_64_RELOC_SIGNED_2 7
|
|
#define X86_64_RELOC_SIGNED_4 8
|
|
|
|
static Outbuffer *fobjbuf;
|
|
|
|
regm_t BYTEREGS = BYTEREGS_INIT;
|
|
regm_t ALLREGS = ALLREGS_INIT;
|
|
|
|
static char __file__[] = __FILE__; // for tassert.h
|
|
#include "tassert.h"
|
|
|
|
#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
|
|
|
|
|
|
/******************************************
|
|
*/
|
|
|
|
symbol *GOTsym; // global offset table reference
|
|
|
|
symbol *elfobj_getGOTsym()
|
|
{
|
|
if (!GOTsym)
|
|
{
|
|
GOTsym = symbol_name("_GLOBAL_OFFSET_TABLE_",SCglobal,tspvoid);
|
|
}
|
|
return GOTsym;
|
|
}
|
|
|
|
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);
|
|
|
|
static long elf_align(targ_size_t size, long offset);
|
|
|
|
// The object file is built is several separate pieces
|
|
|
|
|
|
// String Table - String table for all other names
|
|
static Outbuffer *symtab_strings;
|
|
|
|
// Section Headers
|
|
Outbuffer *SECbuf; // Buffer to build section table in
|
|
#define SecHdrTab ((struct section *)SECbuf->buf)
|
|
#define SecHdrTab64 ((struct section_64 *)SECbuf->buf)
|
|
|
|
// 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 SEC_TAB_INIT 16 // Initial number of sections in buffer
|
|
#define SEC_TAB_INC 4 // Number of sections to increment buffer by
|
|
|
|
#define SYM_TAB_INIT 100 // Initial number of symbol entries in buffer
|
|
#define SYM_TAB_INC 50 // Number of symbols to increment buffer by
|
|
|
|
/* Three symbol tables, because the different types of symbols
|
|
* are grouped into 3 different types (and a 4th for comdef's).
|
|
*/
|
|
|
|
static Outbuffer *local_symbuf;
|
|
static Outbuffer *public_symbuf;
|
|
static Outbuffer *extern_symbuf;
|
|
|
|
struct Comdef { symbol *sym; targ_size_t size; int count; };
|
|
static Outbuffer *comdef_symbuf; // Comdef's are stored here
|
|
|
|
static Outbuffer *indirectsymbuf1; // indirect symbol table of Symbol*'s
|
|
static int jumpTableSeg; // segment index for __jump_table
|
|
|
|
static Outbuffer *indirectsymbuf2; // indirect symbol table of Symbol*'s
|
|
static int pointersSeg; // segment index for __pointers
|
|
|
|
/* If an objextdef() happens, set this to the string index,
|
|
* to be added last to the symbol table.
|
|
* Obviously, there can be only one.
|
|
*/
|
|
static IDXSTR extdef;
|
|
|
|
#if 0
|
|
#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_COM 8
|
|
#endif
|
|
|
|
// Each compiler segment is a section
|
|
// Predefined compiler segments CODE,DATA,CDATA,UDATA map to indexes
|
|
// into SegData[]
|
|
// New compiler segments are added to end.
|
|
|
|
/******************************
|
|
* Returns !=0 if this segment is a code segment.
|
|
*/
|
|
|
|
int seg_data::isCode()
|
|
{
|
|
if (I64)
|
|
{
|
|
//printf("SDshtidx = %d, x%x\n", SDshtidx, SecHdrTab64[SDshtidx].flags);
|
|
return strcmp(SecHdrTab64[SDshtidx].segname, "__TEXT") == 0;
|
|
}
|
|
else
|
|
{
|
|
//printf("SDshtidx = %d, x%x\n", SDshtidx, SecHdrTab[SDshtidx].flags);
|
|
return strcmp(SecHdrTab[SDshtidx].segname, "__TEXT") == 0;
|
|
}
|
|
}
|
|
|
|
|
|
seg_data **SegData;
|
|
int seg_count;
|
|
int seg_max;
|
|
int seg_tlsseg = UNKNOWN;
|
|
int seg_tlsseg_bss = UNKNOWN;
|
|
|
|
/*******************************************************
|
|
* Because the Mach-O relocations cannot be computed until after
|
|
* all the segments are written out, and we need more information
|
|
* than the Mach-O relocations provide, make our own relocation
|
|
* type. Later, translate to Mach-O relocation structure.
|
|
*/
|
|
|
|
struct Relocation
|
|
{ // Relocations are attached to the struct seg_data they refer to
|
|
targ_size_t offset; // location in segment to be fixed up
|
|
symbol *funcsym; // function in which offset lies, if any
|
|
symbol *targsym; // if !=NULL, then location is to be fixed up
|
|
// to address of this symbol
|
|
unsigned targseg; // if !=0, then location is to be fixed up
|
|
// to address of start of this segment
|
|
unsigned char rtype; // RELxxxx
|
|
#define RELaddr 0 // straight address
|
|
#define RELrel 1 // relative to location to be fixed up
|
|
short val; // 0, -1, -2, -4
|
|
};
|
|
|
|
|
|
/*******************************
|
|
* 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)
|
|
{
|
|
//printf("elf_addstr(strtab = %p str = '%s')\n",strtab,str);
|
|
IDXSTR idx = strtab->size(); // remember starting offset
|
|
strtab->writeString(str);
|
|
//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)
|
|
{
|
|
const char *ent = (char *)strtab->buf+1;
|
|
const char *pend = ent+strtab->size() - 1;
|
|
const char *s = str;
|
|
const char *sx = suffix;
|
|
int len = strlen(str);
|
|
|
|
if (suffix)
|
|
len += strlen(suffix);
|
|
|
|
while(ent < pend)
|
|
{
|
|
if(*ent == 0) // end of table entry
|
|
{
|
|
if(*s == 0 && !sx) // end of string - found a match
|
|
{
|
|
return ent - (const char *)strtab->buf - len;
|
|
}
|
|
else // table entry too short
|
|
{
|
|
s = str; // back to beginning of string
|
|
sx = suffix;
|
|
ent++; // start of next table entry
|
|
}
|
|
}
|
|
else if (*s == 0 && sx && *sx == *ent)
|
|
{ // matched first string
|
|
s = sx+1; // switch to suffix
|
|
ent++;
|
|
sx = NULL;
|
|
}
|
|
else // continue comparing
|
|
{
|
|
if (*ent == *s)
|
|
{ // Have a match going
|
|
ent++;
|
|
s++;
|
|
}
|
|
else // no match
|
|
{
|
|
while(*ent != 0) // skip to end of entry
|
|
ent++;
|
|
ent++; // start of next table entry
|
|
s = str; // back to beginning of string
|
|
sx = suffix;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
/**************************
|
|
* 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 (I64)
|
|
{
|
|
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);
|
|
s->Sseg = CDATA;
|
|
//objpubdef(CDATA, s, CDoffset);
|
|
obj_bytes(CDATA, CDoffset, len, p);
|
|
}
|
|
|
|
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 (I64)
|
|
{
|
|
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;
|
|
|
|
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 (!local_symbuf)
|
|
local_symbuf = new Outbuffer(sizeof(symbol *) * SYM_TAB_INIT);
|
|
local_symbuf->setsize(0);
|
|
|
|
if (!public_symbuf)
|
|
public_symbuf = new Outbuffer(sizeof(symbol *) * SYM_TAB_INIT);
|
|
public_symbuf->setsize(0);
|
|
|
|
if (!extern_symbuf)
|
|
extern_symbuf = new Outbuffer(sizeof(symbol *) * SYM_TAB_INIT);
|
|
extern_symbuf->setsize(0);
|
|
|
|
if (!comdef_symbuf)
|
|
comdef_symbuf = new Outbuffer(sizeof(symbol *) * SYM_TAB_INIT);
|
|
comdef_symbuf->setsize(0);
|
|
|
|
extdef = 0;
|
|
|
|
if (indirectsymbuf1)
|
|
indirectsymbuf1->setsize(0);
|
|
jumpTableSeg = 0;
|
|
|
|
if (indirectsymbuf2)
|
|
indirectsymbuf2->setsize(0);
|
|
pointersSeg = 0;
|
|
|
|
// Initialize segments for CODE, DATA, UDATA and CDATA
|
|
size_t struct_section_size = I64 ? sizeof(struct section_64) : sizeof(struct section);
|
|
if (SECbuf)
|
|
{
|
|
SECbuf->setsize(struct_section_size);
|
|
}
|
|
else
|
|
{
|
|
SECbuf = new Outbuffer(SYM_TAB_INC * struct_section_size);
|
|
SECbuf->reserve(SEC_TAB_INIT * struct_section_size);
|
|
// Ignore the first section - section numbers start at 1
|
|
SECbuf->writezeros(struct_section_size);
|
|
}
|
|
section_cnt = 1;
|
|
|
|
seg_count = 0;
|
|
int align = I64 ? 4 : 2; // align to 16 bytes for floating point
|
|
mach_getsegment("__text", "__TEXT", 2, S_REGULAR | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS);
|
|
mach_getsegment("__data", "__DATA", align, S_REGULAR); // DATA
|
|
mach_getsegment("__const", "__TEXT", 2, S_REGULAR); // CDATA
|
|
mach_getsegment("__bss", "__DATA", 4, S_ZEROFILL); // UDATA
|
|
|
|
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);
|
|
#if SCPP
|
|
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;
|
|
assert(!I64); // fix later
|
|
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);
|
|
}
|
|
#endif
|
|
if (config.fulltypes)
|
|
dwarf_initmodule(filename, modname);
|
|
}
|
|
|
|
/************************************
|
|
* Patch pseg/offset by adding in the vmaddr difference from
|
|
* pseg/offset to start of seg.
|
|
*/
|
|
|
|
int32_t *patchAddr(int seg, targ_size_t offset)
|
|
{
|
|
return(int32_t *)(fobjbuf->buf + SecHdrTab[SegData[seg]->SDshtidx].offset + offset);
|
|
}
|
|
|
|
int32_t *patchAddr64(int seg, targ_size_t offset)
|
|
{
|
|
return(int32_t *)(fobjbuf->buf + SecHdrTab64[SegData[seg]->SDshtidx].offset + offset);
|
|
}
|
|
|
|
void patch(seg_data *pseg, targ_size_t offset, int seg, targ_size_t value)
|
|
{
|
|
//printf("patch(offset = x%04x, seg = %d, value = x%llx)\n", (unsigned)offset, seg, value);
|
|
if (I64)
|
|
{
|
|
int32_t *p = (int32_t *)(fobjbuf->buf + SecHdrTab64[pseg->SDshtidx].offset + offset);
|
|
#if 0
|
|
printf("\taddr1 = x%llx\n\taddr2 = x%llx\n\t*p = x%llx\n\tdelta = x%llx\n",
|
|
SecHdrTab64[pseg->SDshtidx].addr,
|
|
SecHdrTab64[SegData[seg]->SDshtidx].addr,
|
|
*p,
|
|
SecHdrTab64[SegData[seg]->SDshtidx].addr -
|
|
(SecHdrTab64[pseg->SDshtidx].addr + offset));
|
|
#endif
|
|
*p += SecHdrTab64[SegData[seg]->SDshtidx].addr -
|
|
(SecHdrTab64[pseg->SDshtidx].addr - value);
|
|
}
|
|
else
|
|
{
|
|
int32_t *p = (int32_t *)(fobjbuf->buf + SecHdrTab[pseg->SDshtidx].offset + offset);
|
|
#if 0
|
|
printf("\taddr1 = x%x\n\taddr2 = x%x\n\t*p = x%x\n\tdelta = x%x\n",
|
|
SecHdrTab[pseg->SDshtidx].addr,
|
|
SecHdrTab[SegData[seg]->SDshtidx].addr,
|
|
*p,
|
|
SecHdrTab[SegData[seg]->SDshtidx].addr -
|
|
(SecHdrTab[pseg->SDshtidx].addr + offset));
|
|
#endif
|
|
*p += SecHdrTab[SegData[seg]->SDshtidx].addr -
|
|
(SecHdrTab[pseg->SDshtidx].addr - value);
|
|
}
|
|
}
|
|
|
|
/***************************
|
|
* Number symbols so they are
|
|
* ordered as locals, public and then extern/comdef
|
|
*/
|
|
|
|
void mach_numbersyms()
|
|
{
|
|
//printf("mach_numbersyms()\n");
|
|
int n = 0;
|
|
|
|
int dim;
|
|
dim = local_symbuf->size() / sizeof(symbol *);
|
|
for (int i = 0; i < dim; i++)
|
|
{ symbol *s = ((symbol **)local_symbuf->buf)[i];
|
|
s->Sxtrnnum = n;
|
|
n++;
|
|
}
|
|
|
|
dim = public_symbuf->size() / sizeof(symbol *);
|
|
for (int i = 0; i < dim; i++)
|
|
{ symbol *s = ((symbol **)public_symbuf->buf)[i];
|
|
s->Sxtrnnum = n;
|
|
n++;
|
|
}
|
|
|
|
dim = extern_symbuf->size() / sizeof(symbol *);
|
|
for (int i = 0; i < dim; i++)
|
|
{ symbol *s = ((symbol **)extern_symbuf->buf)[i];
|
|
s->Sxtrnnum = n;
|
|
n++;
|
|
}
|
|
|
|
dim = comdef_symbuf->size() / sizeof(Comdef);
|
|
for (int i = 0; i < dim; i++)
|
|
{ Comdef *c = ((Comdef *)comdef_symbuf->buf) + i;
|
|
c->sym->Sxtrnnum = n;
|
|
n++;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************
|
|
* 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 object file in the following order:
|
|
* header
|
|
* commands
|
|
* segment_command
|
|
* { sections }
|
|
* symtab_command
|
|
* dysymtab_command
|
|
* { segment contents }
|
|
* { relocations }
|
|
* symbol table
|
|
* string table
|
|
* indirect symbol table
|
|
*/
|
|
|
|
unsigned foffset;
|
|
unsigned headersize;
|
|
unsigned sizeofcmds;
|
|
|
|
// Write out the bytes for the header
|
|
if (I64)
|
|
{
|
|
mach_header_64 header;
|
|
|
|
header.magic = MH_MAGIC_64;
|
|
header.cputype = CPU_TYPE_X86_64;
|
|
header.cpusubtype = CPU_SUBTYPE_I386_ALL;
|
|
header.filetype = MH_OBJECT;
|
|
header.ncmds = 3;
|
|
header.sizeofcmds = sizeof(segment_command_64) +
|
|
(section_cnt - 1) * sizeof(struct section_64) +
|
|
sizeof(symtab_command) +
|
|
sizeof(dysymtab_command);
|
|
header.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
|
|
header.reserved = 0;
|
|
fobjbuf->write(&header, sizeof(header));
|
|
foffset = sizeof(header); // start after header
|
|
headersize = sizeof(header);
|
|
sizeofcmds = header.sizeofcmds;
|
|
|
|
// Write the actual data later
|
|
fobjbuf->writezeros(header.sizeofcmds);
|
|
foffset += header.sizeofcmds;
|
|
}
|
|
else
|
|
{
|
|
mach_header header;
|
|
|
|
header.magic = MH_MAGIC;
|
|
header.cputype = CPU_TYPE_I386;
|
|
header.cpusubtype = CPU_SUBTYPE_I386_ALL;
|
|
header.filetype = MH_OBJECT;
|
|
header.ncmds = 3;
|
|
header.sizeofcmds = sizeof(segment_command) +
|
|
(section_cnt - 1) * sizeof(struct section) +
|
|
sizeof(symtab_command) +
|
|
sizeof(dysymtab_command);
|
|
header.flags = MH_SUBSECTIONS_VIA_SYMBOLS;
|
|
fobjbuf->write(&header, sizeof(header));
|
|
foffset = sizeof(header); // start after header
|
|
headersize = sizeof(header);
|
|
sizeofcmds = header.sizeofcmds;
|
|
|
|
// Write the actual data later
|
|
fobjbuf->writezeros(header.sizeofcmds);
|
|
foffset += header.sizeofcmds;
|
|
}
|
|
|
|
struct segment_command segment_cmd;
|
|
struct segment_command_64 segment_cmd64;
|
|
struct symtab_command symtab_cmd;
|
|
struct dysymtab_command dysymtab_cmd;
|
|
|
|
memset(&segment_cmd, 0, sizeof(segment_cmd));
|
|
memset(&segment_cmd64, 0, sizeof(segment_cmd64));
|
|
memset(&symtab_cmd, 0, sizeof(symtab_cmd));
|
|
memset(&dysymtab_cmd, 0, sizeof(dysymtab_cmd));
|
|
|
|
if (I64)
|
|
{
|
|
segment_cmd64.cmd = LC_SEGMENT_64;
|
|
segment_cmd64.cmdsize = sizeof(segment_cmd64) +
|
|
(section_cnt - 1) * sizeof(struct section_64);
|
|
segment_cmd64.nsects = section_cnt - 1;
|
|
segment_cmd64.maxprot = 7;
|
|
segment_cmd64.initprot = 7;
|
|
}
|
|
else
|
|
{
|
|
segment_cmd.cmd = LC_SEGMENT;
|
|
segment_cmd.cmdsize = sizeof(segment_cmd) +
|
|
(section_cnt - 1) * sizeof(struct section);
|
|
segment_cmd.nsects = section_cnt - 1;
|
|
segment_cmd.maxprot = 7;
|
|
segment_cmd.initprot = 7;
|
|
}
|
|
|
|
symtab_cmd.cmd = LC_SYMTAB;
|
|
symtab_cmd.cmdsize = sizeof(symtab_cmd);
|
|
|
|
dysymtab_cmd.cmd = LC_DYSYMTAB;
|
|
dysymtab_cmd.cmdsize = sizeof(dysymtab_cmd);
|
|
|
|
/* If a __pointers section was emitted, need to set the .reserved1
|
|
* field to the symbol index in the indirect symbol table of the
|
|
* start of the __pointers symbols.
|
|
*/
|
|
if (pointersSeg)
|
|
{
|
|
seg_data *pseg = SegData[pointersSeg];
|
|
if (I64)
|
|
{
|
|
struct section_64 *psechdr = &SecHdrTab64[pseg->SDshtidx]; // corresponding section
|
|
psechdr->reserved1 = indirectsymbuf1
|
|
? indirectsymbuf1->size() / sizeof(Symbol *)
|
|
: 0;
|
|
}
|
|
else
|
|
{
|
|
struct section *psechdr = &SecHdrTab[pseg->SDshtidx]; // corresponding section
|
|
psechdr->reserved1 = indirectsymbuf1
|
|
? indirectsymbuf1->size() / sizeof(Symbol *)
|
|
: 0;
|
|
}
|
|
}
|
|
|
|
// Walk through sections determining size and file offsets
|
|
|
|
//
|
|
// First output individual section data associate with program
|
|
// code and data
|
|
//
|
|
foffset = elf_align(I64 ? 8 : 4, foffset);
|
|
if (I64)
|
|
segment_cmd64.fileoff = foffset;
|
|
else
|
|
segment_cmd.fileoff = foffset;
|
|
unsigned vmaddr = 0;
|
|
|
|
//printf("Setup offsets and sizes foffset %d\n\tsection_cnt %d, seg_count %d\n",foffset,section_cnt,seg_count);
|
|
// Zero filled segments go at the end, so go through segments twice
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
for (int seg = 1; seg <= seg_count; seg++)
|
|
{
|
|
seg_data *pseg = SegData[seg];
|
|
if (I64)
|
|
{
|
|
struct section_64 *psechdr = &SecHdrTab64[pseg->SDshtidx]; // corresponding section
|
|
|
|
// Do zero-fill the second time through this loop
|
|
if (i ^ (psechdr->flags == S_ZEROFILL))
|
|
continue;
|
|
|
|
int align = 1 << psechdr->align;
|
|
foffset = elf_align(align, foffset);
|
|
vmaddr = (vmaddr + align - 1) & ~(align - 1);
|
|
if (psechdr->flags == S_ZEROFILL)
|
|
{
|
|
psechdr->offset = 0;
|
|
psechdr->size = pseg->SDoffset; // accumulated size
|
|
}
|
|
else
|
|
{
|
|
psechdr->offset = foffset;
|
|
psechdr->size = 0;
|
|
//printf("\tsection name %s,", psechdr->sectname);
|
|
if (pseg->SDbuf && pseg->SDbuf->size())
|
|
{
|
|
//printf("\tsize %d\n", pseg->SDbuf->size());
|
|
psechdr->size = pseg->SDbuf->size();
|
|
fobjbuf->write(pseg->SDbuf->buf, psechdr->size);
|
|
foffset += psechdr->size;
|
|
}
|
|
}
|
|
psechdr->addr = vmaddr;
|
|
vmaddr += psechdr->size;
|
|
//printf(" assigned offset %d, size %d\n", foffset, psechdr->sh_size);
|
|
}
|
|
else
|
|
{
|
|
struct section *psechdr = &SecHdrTab[pseg->SDshtidx]; // corresponding section
|
|
|
|
// Do zero-fill the second time through this loop
|
|
if (i ^ (psechdr->flags == S_ZEROFILL))
|
|
continue;
|
|
|
|
int align = 1 << psechdr->align;
|
|
foffset = elf_align(align, foffset);
|
|
vmaddr = (vmaddr + align - 1) & ~(align - 1);
|
|
if (psechdr->flags == S_ZEROFILL)
|
|
{
|
|
psechdr->offset = 0;
|
|
psechdr->size = pseg->SDoffset; // accumulated size
|
|
}
|
|
else
|
|
{
|
|
psechdr->offset = foffset;
|
|
psechdr->size = 0;
|
|
//printf("\tsection name %s,", psechdr->sectname);
|
|
if (pseg->SDbuf && pseg->SDbuf->size())
|
|
{
|
|
//printf("\tsize %d\n", pseg->SDbuf->size());
|
|
psechdr->size = pseg->SDbuf->size();
|
|
fobjbuf->write(pseg->SDbuf->buf, psechdr->size);
|
|
foffset += psechdr->size;
|
|
}
|
|
}
|
|
psechdr->addr = vmaddr;
|
|
vmaddr += psechdr->size;
|
|
//printf(" assigned offset %d, size %d\n", foffset, psechdr->sh_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (I64)
|
|
{
|
|
segment_cmd64.vmsize = vmaddr;
|
|
segment_cmd64.filesize = foffset - segment_cmd64.fileoff;
|
|
/* Bugzilla 5331: Apparently having the filesize field greater than the vmsize field is an
|
|
* error, and is happening sometimes.
|
|
*/
|
|
if (segment_cmd64.filesize > vmaddr)
|
|
segment_cmd64.vmsize = segment_cmd64.filesize;
|
|
}
|
|
else
|
|
{
|
|
segment_cmd.vmsize = vmaddr;
|
|
segment_cmd.filesize = foffset - segment_cmd.fileoff;
|
|
/* Bugzilla 5331: Apparently having the filesize field greater than the vmsize field is an
|
|
* error, and is happening sometimes.
|
|
*/
|
|
if (segment_cmd.filesize > vmaddr)
|
|
segment_cmd.vmsize = segment_cmd.filesize;
|
|
}
|
|
|
|
// Put out relocation data
|
|
mach_numbersyms();
|
|
for (int seg = 1; seg <= seg_count; seg++)
|
|
{
|
|
seg_data *pseg = SegData[seg];
|
|
struct section *psechdr = NULL;
|
|
struct section_64 *psechdr64 = NULL;
|
|
if (I64)
|
|
{
|
|
psechdr64 = &SecHdrTab64[pseg->SDshtidx]; // corresponding section
|
|
//printf("psechdr->addr = x%llx\n", psechdr64->addr);
|
|
}
|
|
else
|
|
{
|
|
psechdr = &SecHdrTab[pseg->SDshtidx]; // corresponding section
|
|
//printf("psechdr->addr = x%x\n", psechdr->addr);
|
|
}
|
|
foffset = elf_align(I64 ? 8 : 4, foffset);
|
|
unsigned reloff = foffset;
|
|
unsigned nreloc = 0;
|
|
if (pseg->SDrel)
|
|
{ Relocation *r = (Relocation *)pseg->SDrel->buf;
|
|
Relocation *rend = (Relocation *)(pseg->SDrel->buf + pseg->SDrel->size());
|
|
for (; r != rend; r++)
|
|
{ symbol *s = r->targsym;
|
|
const char *rs = r->rtype == RELaddr ? "addr" : "rel";
|
|
//printf("%d:x%04llx : tseg %d tsym %s REL%s\n", seg, r->offset, r->targseg, s ? s->Sident : "0", rs);
|
|
relocation_info rel;
|
|
scattered_relocation_info srel;
|
|
if (s)
|
|
{
|
|
//printf("Relocation\n");
|
|
//symbol_print(s);
|
|
if (pseg->isCode())
|
|
{
|
|
if (I64)
|
|
{
|
|
rel.r_type = (r->rtype == RELrel)
|
|
? X86_64_RELOC_BRANCH
|
|
: X86_64_RELOC_SIGNED;
|
|
if (r->val == -1)
|
|
rel.r_type = X86_64_RELOC_SIGNED_1;
|
|
else if (r->val == -2)
|
|
rel.r_type = X86_64_RELOC_SIGNED_2;
|
|
if (r->val == -4)
|
|
rel.r_type = X86_64_RELOC_SIGNED_4;
|
|
|
|
if (s->Sclass == SCextern ||
|
|
s->Sclass == SCcomdef ||
|
|
s->Sclass == SCcomdat ||
|
|
s->Sclass == SCglobal)
|
|
{
|
|
if ((s->Sfl == FLfunc || s->Sfl == FLextern || s->Sclass == SCglobal || s->Sclass == SCcomdat || s->Sclass == SCcomdef) && r->rtype == RELaddr)
|
|
rel.r_type = X86_64_RELOC_GOT_LOAD;
|
|
rel.r_address = r->offset;
|
|
rel.r_symbolnum = s->Sxtrnnum;
|
|
rel.r_pcrel = 1;
|
|
rel.r_length = 2;
|
|
rel.r_extern = 1;
|
|
fobjbuf->write(&rel, sizeof(rel));
|
|
foffset += sizeof(rel);
|
|
nreloc++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
rel.r_address = r->offset;
|
|
rel.r_symbolnum = s->Sseg;
|
|
rel.r_pcrel = 1;
|
|
rel.r_length = 2;
|
|
rel.r_extern = 0;
|
|
fobjbuf->write(&rel, sizeof(rel));
|
|
foffset += sizeof(rel);
|
|
nreloc++;
|
|
|
|
int32_t *p = patchAddr64(seg, r->offset);
|
|
// Absolute address; add in addr of start of targ seg
|
|
//printf("*p = x%x, .addr = x%x, Soffset = x%x\n", *p, (int)SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr, (int)s->Soffset);
|
|
//printf("pseg = x%x, r->offset = x%x\n", (int)SecHdrTab64[pseg->SDshtidx].addr, (int)r->offset);
|
|
*p += SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr;
|
|
*p += s->Soffset;
|
|
*p -= SecHdrTab64[pseg->SDshtidx].addr + r->offset + 4;
|
|
//patch(pseg, r->offset, s->Sseg, s->Soffset);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (s->Sclass == SCextern ||
|
|
s->Sclass == SCcomdef ||
|
|
s->Sclass == SCcomdat)
|
|
{
|
|
rel.r_address = r->offset;
|
|
rel.r_symbolnum = s->Sxtrnnum;
|
|
rel.r_pcrel = 0;
|
|
rel.r_length = 2;
|
|
rel.r_extern = 1;
|
|
rel.r_type = GENERIC_RELOC_VANILLA;
|
|
if (I64)
|
|
{
|
|
rel.r_type = X86_64_RELOC_UNSIGNED;
|
|
rel.r_length = 3;
|
|
}
|
|
fobjbuf->write(&rel, sizeof(rel));
|
|
foffset += sizeof(rel);
|
|
nreloc++;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
rel.r_address = r->offset;
|
|
rel.r_symbolnum = s->Sseg;
|
|
rel.r_pcrel = 0;
|
|
rel.r_length = 2;
|
|
rel.r_extern = 0;
|
|
rel.r_type = GENERIC_RELOC_VANILLA;
|
|
if (I64)
|
|
{
|
|
rel.r_type = X86_64_RELOC_UNSIGNED;
|
|
rel.r_length = 3;
|
|
if (0 && s->Sseg != seg)
|
|
rel.r_type = X86_64_RELOC_BRANCH;
|
|
}
|
|
fobjbuf->write(&rel, sizeof(rel));
|
|
foffset += sizeof(rel);
|
|
nreloc++;
|
|
if (I64)
|
|
{
|
|
rel.r_length = 3;
|
|
int32_t *p = patchAddr64(seg, r->offset);
|
|
// Absolute address; add in addr of start of targ seg
|
|
*p += SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr + s->Soffset;
|
|
//patch(pseg, r->offset, s->Sseg, s->Soffset);
|
|
}
|
|
else
|
|
{
|
|
int32_t *p = patchAddr(seg, r->offset);
|
|
// Absolute address; add in addr of start of targ seg
|
|
*p += SecHdrTab[SegData[s->Sseg]->SDshtidx].addr + s->Soffset;
|
|
//patch(pseg, r->offset, s->Sseg, s->Soffset);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
else if (r->rtype == RELaddr && pseg->isCode())
|
|
{
|
|
int32_t *p = NULL;
|
|
int32_t *p64 = NULL;
|
|
if (I64)
|
|
p64 = patchAddr64(seg, r->offset);
|
|
else
|
|
p = patchAddr(seg, r->offset);
|
|
srel.r_scattered = 1;
|
|
|
|
srel.r_address = r->offset;
|
|
srel.r_length = 2;
|
|
if (I64)
|
|
{
|
|
srel.r_type = X86_64_RELOC_GOT;
|
|
srel.r_value = SecHdrTab64[SegData[r->targseg]->SDshtidx].addr + *p64;
|
|
//printf("SECTDIFF: x%llx + x%llx = x%x\n", SecHdrTab[SegData[r->targseg]->SDshtidx].addr, *p, srel.r_value);
|
|
}
|
|
else
|
|
{
|
|
srel.r_type = GENERIC_RELOC_LOCAL_SECTDIFF;
|
|
srel.r_value = SecHdrTab[SegData[r->targseg]->SDshtidx].addr + *p;
|
|
//printf("SECTDIFF: x%x + x%x = x%x\n", SecHdrTab[SegData[r->targseg]->SDshtidx].addr, *p, srel.r_value);
|
|
}
|
|
srel.r_pcrel = 0;
|
|
fobjbuf->write(&srel, sizeof(srel));
|
|
foffset += sizeof(srel);
|
|
nreloc++;
|
|
|
|
srel.r_address = 0;
|
|
srel.r_type = GENERIC_RELOC_PAIR;
|
|
srel.r_length = 2;
|
|
if (I64)
|
|
srel.r_value = SecHdrTab64[pseg->SDshtidx].addr +
|
|
r->funcsym->Slocalgotoffset + NPTRSIZE;
|
|
else
|
|
srel.r_value = SecHdrTab[pseg->SDshtidx].addr +
|
|
r->funcsym->Slocalgotoffset + NPTRSIZE;
|
|
srel.r_pcrel = 0;
|
|
fobjbuf->write(&srel, sizeof(srel));
|
|
foffset += sizeof(srel);
|
|
nreloc++;
|
|
|
|
// Recalc due to possible realloc of fobjbuf->buf
|
|
if (I64)
|
|
{
|
|
p64 = patchAddr64(seg, r->offset);
|
|
//printf("address = x%x, p64 = %p *p64 = x%llx\n", r->offset, p64, *p64);
|
|
*p64 += SecHdrTab64[SegData[r->targseg]->SDshtidx].addr -
|
|
(SecHdrTab64[pseg->SDshtidx].addr + r->funcsym->Slocalgotoffset + NPTRSIZE);
|
|
}
|
|
else
|
|
{
|
|
p = patchAddr(seg, r->offset);
|
|
//printf("address = x%x, p = %p *p = x%x\n", r->offset, p, *p);
|
|
*p += SecHdrTab[SegData[r->targseg]->SDshtidx].addr -
|
|
(SecHdrTab[pseg->SDshtidx].addr + r->funcsym->Slocalgotoffset + NPTRSIZE);
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
rel.r_address = r->offset;
|
|
rel.r_symbolnum = r->targseg;
|
|
rel.r_pcrel = (r->rtype == RELaddr) ? 0 : 1;
|
|
rel.r_length = 2;
|
|
rel.r_extern = 0;
|
|
rel.r_type = GENERIC_RELOC_VANILLA;
|
|
if (I64)
|
|
{
|
|
rel.r_type = X86_64_RELOC_UNSIGNED;
|
|
rel.r_length = 3;
|
|
if (0 && r->targseg != seg)
|
|
rel.r_type = X86_64_RELOC_BRANCH;
|
|
}
|
|
fobjbuf->write(&rel, sizeof(rel));
|
|
foffset += sizeof(rel);
|
|
nreloc++;
|
|
if (I64)
|
|
{
|
|
int32_t *p64 = patchAddr64(seg, r->offset);
|
|
//int64_t before = *p64;
|
|
if (rel.r_pcrel)
|
|
// Relative address
|
|
patch(pseg, r->offset, r->targseg, 0);
|
|
else
|
|
{ // Absolute address; add in addr of start of targ seg
|
|
//printf("*p = x%x, targ.addr = x%x\n", *p64, (int)SecHdrTab64[SegData[r->targseg]->SDshtidx].addr);
|
|
//printf("pseg = x%x, r->offset = x%x\n", (int)SecHdrTab64[pseg->SDshtidx].addr, (int)r->offset);
|
|
*p64 += SecHdrTab64[SegData[r->targseg]->SDshtidx].addr;
|
|
//*p64 -= SecHdrTab64[pseg->SDshtidx].addr;
|
|
}
|
|
//printf("%d:x%04x before = x%04llx, after = x%04llx pcrel = %d\n", seg, r->offset, before, *p64, rel.r_pcrel);
|
|
}
|
|
else
|
|
{
|
|
int32_t *p = patchAddr(seg, r->offset);
|
|
//int32_t before = *p;
|
|
if (rel.r_pcrel)
|
|
// Relative address
|
|
patch(pseg, r->offset, r->targseg, 0);
|
|
else
|
|
// Absolute address; add in addr of start of targ seg
|
|
*p += SecHdrTab[SegData[r->targseg]->SDshtidx].addr;
|
|
//printf("%d:x%04x before = x%04x, after = x%04x pcrel = %d\n", seg, r->offset, before, *p, rel.r_pcrel);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (nreloc)
|
|
{
|
|
if (I64)
|
|
{
|
|
psechdr64->reloff = reloff;
|
|
psechdr64->nreloc = nreloc;
|
|
}
|
|
else
|
|
{
|
|
psechdr->reloff = reloff;
|
|
psechdr->nreloc = nreloc;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Put out symbol table
|
|
foffset = elf_align(I64 ? 8 : 4, foffset);
|
|
symtab_cmd.symoff = foffset;
|
|
dysymtab_cmd.ilocalsym = 0;
|
|
dysymtab_cmd.nlocalsym = local_symbuf->size() / sizeof(symbol *);
|
|
dysymtab_cmd.iextdefsym = dysymtab_cmd.nlocalsym;
|
|
dysymtab_cmd.nextdefsym = public_symbuf->size() / sizeof(symbol *);
|
|
dysymtab_cmd.iundefsym = dysymtab_cmd.iextdefsym + dysymtab_cmd.nextdefsym;
|
|
int nexterns = extern_symbuf->size() / sizeof(symbol *);
|
|
int ncomdefs = comdef_symbuf->size() / sizeof(Comdef);
|
|
dysymtab_cmd.nundefsym = nexterns + ncomdefs;
|
|
symtab_cmd.nsyms = dysymtab_cmd.nlocalsym +
|
|
dysymtab_cmd.nextdefsym +
|
|
dysymtab_cmd.nundefsym;
|
|
fobjbuf->reserve(symtab_cmd.nsyms * (I64 ? sizeof(struct nlist_64) : sizeof(struct nlist)));
|
|
for (int i = 0; i < dysymtab_cmd.nlocalsym; i++)
|
|
{ symbol *s = ((symbol **)local_symbuf->buf)[i];
|
|
struct nlist_64 sym;
|
|
sym.n_un.n_strx = elf_addmangled(s);
|
|
sym.n_type = N_SECT;
|
|
sym.n_desc = 0;
|
|
if (s->Sclass == SCcomdat)
|
|
sym.n_desc = N_WEAK_DEF;
|
|
sym.n_sect = s->Sseg;
|
|
if (I64)
|
|
{
|
|
sym.n_value = s->Soffset + SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr;
|
|
fobjbuf->write(&sym, sizeof(sym));
|
|
}
|
|
else
|
|
{
|
|
struct nlist sym32;
|
|
sym32.n_un.n_strx = sym.n_un.n_strx;
|
|
sym32.n_value = s->Soffset + SecHdrTab[SegData[s->Sseg]->SDshtidx].addr;
|
|
sym32.n_type = sym.n_type;
|
|
sym32.n_desc = sym.n_desc;
|
|
sym32.n_sect = sym.n_sect;
|
|
fobjbuf->write(&sym32, sizeof(sym32));
|
|
}
|
|
}
|
|
for (int i = 0; i < dysymtab_cmd.nextdefsym; i++)
|
|
{ symbol *s = ((symbol **)public_symbuf->buf)[i];
|
|
|
|
//printf("Writing public symbol %d:x%x %s\n", s->Sseg, s->Soffset, s->Sident);
|
|
struct nlist_64 sym;
|
|
sym.n_un.n_strx = elf_addmangled(s);
|
|
sym.n_type = N_EXT | N_SECT;
|
|
sym.n_desc = 0;
|
|
if (s->Sclass == SCcomdat)
|
|
sym.n_desc = N_WEAK_DEF;
|
|
sym.n_sect = s->Sseg;
|
|
if (I64)
|
|
{
|
|
sym.n_value = s->Soffset + SecHdrTab64[SegData[s->Sseg]->SDshtidx].addr;
|
|
fobjbuf->write(&sym, sizeof(sym));
|
|
}
|
|
else
|
|
{
|
|
struct nlist sym32;
|
|
sym32.n_un.n_strx = sym.n_un.n_strx;
|
|
sym32.n_value = s->Soffset + SecHdrTab[SegData[s->Sseg]->SDshtidx].addr;
|
|
sym32.n_type = sym.n_type;
|
|
sym32.n_desc = sym.n_desc;
|
|
sym32.n_sect = sym.n_sect;
|
|
fobjbuf->write(&sym32, sizeof(sym32));
|
|
}
|
|
}
|
|
for (int i = 0; i < nexterns; i++)
|
|
{ symbol *s = ((symbol **)extern_symbuf->buf)[i];
|
|
struct nlist_64 sym;
|
|
sym.n_un.n_strx = elf_addmangled(s);
|
|
sym.n_value = s->Soffset;
|
|
sym.n_type = N_EXT | N_UNDF;
|
|
sym.n_desc = tyfunc(s->ty()) ? REFERENCE_FLAG_UNDEFINED_LAZY
|
|
: REFERENCE_FLAG_UNDEFINED_NON_LAZY;
|
|
sym.n_sect = 0;
|
|
if (I64)
|
|
fobjbuf->write(&sym, sizeof(sym));
|
|
else
|
|
{
|
|
struct nlist sym32;
|
|
sym32.n_un.n_strx = sym.n_un.n_strx;
|
|
sym32.n_value = sym.n_value;
|
|
sym32.n_type = sym.n_type;
|
|
sym32.n_desc = sym.n_desc;
|
|
sym32.n_sect = sym.n_sect;
|
|
fobjbuf->write(&sym32, sizeof(sym32));
|
|
}
|
|
}
|
|
for (int i = 0; i < ncomdefs; i++)
|
|
{ Comdef *c = ((Comdef *)comdef_symbuf->buf) + i;
|
|
struct nlist_64 sym;
|
|
sym.n_un.n_strx = elf_addmangled(c->sym);
|
|
sym.n_value = c->size * c->count;
|
|
sym.n_type = N_EXT | N_UNDF;
|
|
int align;
|
|
if (c->size < 2)
|
|
align = 0; // align is expressed as power of 2
|
|
else if (c->size < 4)
|
|
align = 1;
|
|
else if (c->size < 8)
|
|
align = 2;
|
|
else if (c->size < 16)
|
|
align = 3;
|
|
else
|
|
align = 4;
|
|
sym.n_desc = align << 8;
|
|
sym.n_sect = 0;
|
|
if (I64)
|
|
fobjbuf->write(&sym, sizeof(sym));
|
|
else
|
|
{
|
|
struct nlist sym32;
|
|
sym32.n_un.n_strx = sym.n_un.n_strx;
|
|
sym32.n_value = sym.n_value;
|
|
sym32.n_type = sym.n_type;
|
|
sym32.n_desc = sym.n_desc;
|
|
sym32.n_sect = sym.n_sect;
|
|
fobjbuf->write(&sym32, sizeof(sym32));
|
|
}
|
|
}
|
|
if (extdef)
|
|
{
|
|
struct nlist_64 sym;
|
|
sym.n_un.n_strx = extdef;
|
|
sym.n_value = 0;
|
|
sym.n_type = N_EXT | N_UNDF;
|
|
sym.n_desc = 0;
|
|
sym.n_sect = 0;
|
|
if (I64)
|
|
fobjbuf->write(&sym, sizeof(sym));
|
|
else
|
|
{
|
|
struct nlist sym32;
|
|
sym32.n_un.n_strx = sym.n_un.n_strx;
|
|
sym32.n_value = sym.n_value;
|
|
sym32.n_type = sym.n_type;
|
|
sym32.n_desc = sym.n_desc;
|
|
sym32.n_sect = sym.n_sect;
|
|
fobjbuf->write(&sym32, sizeof(sym32));
|
|
}
|
|
symtab_cmd.nsyms++;
|
|
}
|
|
foffset += symtab_cmd.nsyms * (I64 ? sizeof(struct nlist_64) : sizeof(struct nlist));
|
|
|
|
// Put out string table
|
|
foffset = elf_align(I64 ? 8 : 4, foffset);
|
|
symtab_cmd.stroff = foffset;
|
|
symtab_cmd.strsize = symtab_strings->size();
|
|
fobjbuf->write(symtab_strings->buf, symtab_cmd.strsize);
|
|
foffset += symtab_cmd.strsize;
|
|
|
|
// Put out indirectsym table, which is in two parts
|
|
foffset = elf_align(I64 ? 8 : 4, foffset);
|
|
dysymtab_cmd.indirectsymoff = foffset;
|
|
if (indirectsymbuf1)
|
|
{ dysymtab_cmd.nindirectsyms += indirectsymbuf1->size() / sizeof(Symbol *);
|
|
for (int i = 0; i < dysymtab_cmd.nindirectsyms; i++)
|
|
{ Symbol *s = ((Symbol **)indirectsymbuf1->buf)[i];
|
|
fobjbuf->write32(s->Sxtrnnum);
|
|
}
|
|
}
|
|
if (indirectsymbuf2)
|
|
{ int n = indirectsymbuf2->size() / sizeof(Symbol *);
|
|
dysymtab_cmd.nindirectsyms += n;
|
|
for (int i = 0; i < n; i++)
|
|
{ Symbol *s = ((Symbol **)indirectsymbuf2->buf)[i];
|
|
fobjbuf->write32(s->Sxtrnnum);
|
|
}
|
|
}
|
|
foffset += dysymtab_cmd.nindirectsyms * 4;
|
|
|
|
/* The correct offsets are now determined, so
|
|
* rewind and fix the header.
|
|
*/
|
|
fobjbuf->position(headersize, sizeofcmds);
|
|
if (I64)
|
|
{
|
|
fobjbuf->write(&segment_cmd64, sizeof(segment_cmd64));
|
|
fobjbuf->write(SECbuf->buf + sizeof(struct section_64), (section_cnt - 1) * sizeof(struct section_64));
|
|
}
|
|
else
|
|
{
|
|
fobjbuf->write(&segment_cmd, sizeof(segment_cmd));
|
|
fobjbuf->write(SECbuf->buf + sizeof(struct section), (section_cnt - 1) * sizeof(struct section));
|
|
}
|
|
fobjbuf->write(&symtab_cmd, sizeof(symtab_cmd));
|
|
fobjbuf->write(&dysymtab_cmd, sizeof(dysymtab_cmd));
|
|
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=x%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);
|
|
// Not supported by Mach-O
|
|
}
|
|
|
|
/*******************************
|
|
* Embed compiler version in .obj file.
|
|
*/
|
|
|
|
void obj_compiler()
|
|
{
|
|
//dbg_printf("obj_compiler\n");
|
|
}
|
|
|
|
//#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)
|
|
{
|
|
#if 0
|
|
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);
|
|
mach_addrel(seg, SegData[seg]->SDoffset, s, RELaddr);
|
|
SegData[seg]->SDoffset = buf->size();
|
|
#endif
|
|
}
|
|
|
|
/**************************************
|
|
* 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)
|
|
{
|
|
#if 0
|
|
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);
|
|
mach_addrel(seg, SegData[seg]->SDoffset, s, RELaddr);
|
|
SegData[seg]->SDoffset = buf->size();
|
|
#endif
|
|
}
|
|
|
|
//#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 (instance of struct FuncTable) 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);
|
|
|
|
/* BUG: this should go into a COMDAT if sfunc is in a COMDAT
|
|
* otherwise the duplicates aren't removed.
|
|
*/
|
|
|
|
int align = I64 ? 3 : 2; // align to NPTRSIZE
|
|
// The size is sizeof(struct FuncTable) in deh2.d
|
|
mach_getsegment("__deh_beg", "__DATA", align, S_COALESCED, 3 * NPTRSIZE);
|
|
int seg = mach_getsegment("__deh_eh", "__DATA", align, S_REGULAR);
|
|
mach_getsegment("__deh_end", "__DATA", align, S_COALESCED, NPTRSIZE);
|
|
|
|
Outbuffer *buf = SegData[seg]->SDbuf;
|
|
if (I64)
|
|
{ reftoident(seg, buf->size(), sfunc, 0, CFoff | CFoffset64);
|
|
reftoident(seg, buf->size(), ehsym, 0, CFoff | CFoffset64);
|
|
buf->write64(sfunc->Ssize);
|
|
}
|
|
else
|
|
{ reftoident(seg, buf->size(), sfunc, 0, CFoff);
|
|
reftoident(seg, buf->size(), ehsym, 0, CFoff);
|
|
buf->write32(sfunc->Ssize);
|
|
}
|
|
}
|
|
|
|
/*********************************************
|
|
* Put out symbols that define the beginning/end of the .deh_eh section.
|
|
* This gets called if this is the module with "main()" in it.
|
|
*/
|
|
|
|
void obj_ehsections()
|
|
{
|
|
//printf("obj_ehsections()\n");
|
|
#if 0
|
|
/* Determine Mac OSX version, and put out the sections slightly differently for each.
|
|
* This is needed because the linker on OSX 10.5 behaves differently than
|
|
* the linker on 10.6.
|
|
* See Bugzilla 3502 for more information.
|
|
*/
|
|
static SInt32 MacVersion;
|
|
if (!MacVersion)
|
|
Gestalt(gestaltSystemVersion, &MacVersion);
|
|
|
|
/* Exception handling sections
|
|
*/
|
|
// 12 is size of struct FuncTable in D runtime
|
|
mach_getsegment("__deh_beg", "__DATA", 2, S_COALESCED, 12);
|
|
int seg = mach_getsegment("__deh_eh", "__DATA", 2, S_REGULAR);
|
|
Outbuffer *buf = SegData[seg]->SDbuf;
|
|
buf->writezeros(12); // 12 is size of struct FuncTable in D runtime,
|
|
// this entry gets skipped over by __eh_finddata()
|
|
|
|
mach_getsegment("__deh_end", "__DATA", 2, S_COALESCED, 4);
|
|
|
|
/* Thread local storage sections
|
|
*/
|
|
mach_getsegment("__tls_beg", "__DATA", 2, S_COALESCED, 4);
|
|
mach_getsegment("__tls_data", "__DATA", 2, S_REGULAR, 4);
|
|
mach_getsegment("__tlscoal_nt", "__DATA", 4, S_COALESCED, 4);
|
|
mach_getsegment("__tls_end", "__DATA", 2, S_COALESCED, 4);
|
|
|
|
/* Module info sections
|
|
*/
|
|
mach_getsegment("__minfo_beg", "__DATA", 2, S_COALESCED, 4);
|
|
mach_getsegment("__minfodata", "__DATA", 2, S_REGULAR, 4);
|
|
mach_getsegment("__minfo_end", "__DATA", 2, S_COALESCED, 4);
|
|
#endif
|
|
}
|
|
|
|
/*********************************
|
|
* Setup for Symbol s to go into a COMDAT segment.
|
|
* Output (if s is a function):
|
|
* cseg segment index of new current code segment
|
|
* Coffset starting offset in cseg
|
|
* Returns:
|
|
* "segment index" of COMDAT
|
|
*/
|
|
|
|
int obj_comdat(Symbol *s)
|
|
{
|
|
const char *sectname;
|
|
const char *segname;
|
|
int align;
|
|
int flags;
|
|
|
|
//printf("obj_comdat(Symbol* %s)\n",s->Sident);
|
|
//symbol_print(s);
|
|
symbol_debug(s);
|
|
|
|
if (tyfunc(s->ty()))
|
|
{
|
|
sectname = "__textcoal_nt";
|
|
segname = "__TEXT";
|
|
align = 2; // 4 byte alignment
|
|
flags = S_COALESCED | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS;
|
|
s->Sseg = mach_getsegment(sectname, segname, align, flags);
|
|
}
|
|
else if ((s->ty() & mTYLINK) == mTYthread)
|
|
{
|
|
s->Sfl = FLtlsdata;
|
|
align = I64 ? 4 : 2; // align to 16 bytes for floating point
|
|
mach_getsegment("__tls_beg", "__DATA", align, S_COALESCED, 4);
|
|
mach_getsegment("__tls_data", "__DATA", align, S_REGULAR, 4);
|
|
s->Sseg = mach_getsegment("__tlscoal_nt", "__DATA", 4, S_COALESCED);
|
|
mach_getsegment("__tls_end", "__DATA", align, S_COALESCED, 4);
|
|
elf_data_start(s, 1 << align, s->Sseg);
|
|
}
|
|
else
|
|
{
|
|
s->Sfl = FLdata;
|
|
sectname = "__datacoal_nt";
|
|
segname = "__DATA";
|
|
align = 4; // 16 byte alignment
|
|
flags = S_COALESCED;
|
|
s->Sseg = mach_getsegment(sectname, segname, align, flags);
|
|
}
|
|
// find or create new segment
|
|
s->Soffset = SegData[s->Sseg]->SDoffset;
|
|
if (s->Sfl == FLdata || s->Sfl == FLtlsdata)
|
|
{ // Code symbols are 'published' by elf_func_start()
|
|
|
|
objpubdef(s->Sseg,s,s->Soffset);
|
|
searchfixlist(s); // backpatch any refs to this symbol
|
|
}
|
|
return s->Sseg;
|
|
}
|
|
|
|
/**********************************
|
|
* Get segment.
|
|
* Input:
|
|
* flags2 put out some data for this, so the linker will keep things in order
|
|
* align segment alignment as power of 2
|
|
* Returns:
|
|
* segment index of found or newly created segment
|
|
*/
|
|
|
|
int mach_getsegment(const char *sectname, const char *segname,
|
|
int align, int flags, int flags2)
|
|
{
|
|
assert(strlen(sectname) <= 16);
|
|
assert(strlen(segname) <= 16);
|
|
for (int seg = 1; seg <= seg_count; seg++)
|
|
{ seg_data *pseg = SegData[seg];
|
|
if (I64)
|
|
{
|
|
if (strncmp(SecHdrTab64[pseg->SDshtidx].sectname, sectname, 16) == 0 &&
|
|
strncmp(SecHdrTab64[pseg->SDshtidx].segname, segname, 16) == 0)
|
|
return seg; // return existing segment
|
|
}
|
|
else
|
|
{
|
|
if (strncmp(SecHdrTab[pseg->SDshtidx].sectname, sectname, 16) == 0 &&
|
|
strncmp(SecHdrTab[pseg->SDshtidx].segname, segname, 16) == 0)
|
|
return seg; // return existing segment
|
|
}
|
|
}
|
|
|
|
int seg = ++seg_count;
|
|
if (seg_count >= seg_max)
|
|
{ // need more room in segment table
|
|
seg_max += 10;
|
|
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])
|
|
{ seg_data *pseg = SegData[seg];
|
|
Outbuffer *b1 = pseg->SDbuf;
|
|
Outbuffer *b2 = pseg->SDrel;
|
|
memset(pseg, 0, sizeof(seg_data));
|
|
if (b1)
|
|
b1->setsize(0);
|
|
if (b2)
|
|
b2->setsize(0);
|
|
pseg->SDbuf = b1;
|
|
pseg->SDrel = b2;
|
|
}
|
|
else
|
|
{
|
|
seg_data *pseg = (seg_data *)mem_calloc(sizeof(seg_data));
|
|
SegData[seg] = pseg;
|
|
if (flags != S_ZEROFILL)
|
|
{ pseg->SDbuf = new Outbuffer(4096);
|
|
pseg->SDbuf->reserve(4096);
|
|
}
|
|
}
|
|
|
|
//dbg_printf("\tNew segment - %d size %d\n", seg,SegData[seg]->SDbuf);
|
|
seg_data *pseg = SegData[seg];
|
|
|
|
pseg->SDseg = seg;
|
|
pseg->SDoffset = 0;
|
|
|
|
if (I64)
|
|
{
|
|
struct section_64 *sec = (struct section_64 *)
|
|
SECbuf->writezeros(sizeof(struct section_64));
|
|
strncpy(sec->sectname, sectname, 16);
|
|
strncpy(sec->segname, segname, 16);
|
|
sec->align = align;
|
|
sec->flags = flags;
|
|
}
|
|
else
|
|
{
|
|
struct section *sec = (struct section *)
|
|
SECbuf->writezeros(sizeof(struct section));
|
|
strncpy(sec->sectname, sectname, 16);
|
|
strncpy(sec->segname, segname, 16);
|
|
sec->align = align;
|
|
sec->flags = flags;
|
|
}
|
|
|
|
pseg->SDshtidx = section_cnt++;
|
|
pseg->SDaranges_offset = 0;
|
|
pseg->SDlinnum_count = 0;
|
|
|
|
if (flags2)
|
|
{
|
|
/* If we don't write something to each seg, then the linker won't put
|
|
* them in this necessary order. In fact, it will ignore the segment entirely.
|
|
*/
|
|
static SInt32 MacVersion;
|
|
if (!MacVersion)
|
|
Gestalt(gestaltSystemVersion, &MacVersion);
|
|
|
|
if (flags == S_COALESCED)
|
|
{ type *t = type_fake(TYint);
|
|
t->Tmangle = mTYman_c;
|
|
symbol *s_deh_beg = symbol_name(sectname + 1, SCcomdat, t);
|
|
objpubdef(seg, s_deh_beg, 0);
|
|
}
|
|
if (MacVersion >= MacOSX_10_6)
|
|
obj_bytes(seg, 0, flags2, NULL); // 12 is size of struct FuncTable in D runtime
|
|
}
|
|
|
|
//printf("seg_count = %d\n", seg_count);
|
|
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)
|
|
{
|
|
//dbg_printf("obj_codeseg(%s,%x)\n",name,suffix);
|
|
#if 0
|
|
const char *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;
|
|
}
|
|
|
|
int 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;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*********************************
|
|
* Define segments for Thread Local Storage.
|
|
* Output:
|
|
* seg_tlsseg set to segment number for TLS segment.
|
|
* Returns:
|
|
* segment for TLS segment
|
|
*/
|
|
|
|
seg_data *obj_tlsseg()
|
|
{
|
|
//printf("obj_tlsseg(\n");
|
|
|
|
if (seg_tlsseg == UNKNOWN)
|
|
{
|
|
int align = I64 ? 4 : 2; // align to 16 bytes for floating point
|
|
mach_getsegment("__tls_beg", "__DATA", align, S_COALESCED, 4);
|
|
seg_tlsseg = mach_getsegment("__tls_data", "__DATA", align, S_REGULAR);
|
|
mach_getsegment("__tlscoal_nt", "__DATA", 4, S_COALESCED, 4);
|
|
mach_getsegment("__tls_end", "__DATA", align, S_COALESCED, 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()
|
|
{
|
|
/* Because Mach-O does not support tls, it's easier to support
|
|
* if we have all the tls in one segment.
|
|
*/
|
|
return obj_tlsseg();
|
|
}
|
|
|
|
|
|
/*******************************
|
|
* Output an alias definition record.
|
|
*/
|
|
|
|
void obj_alias(const char *n1,const char *n2)
|
|
{
|
|
//printf("obj_alias(%s,%s)\n",n1,n2);
|
|
assert(0);
|
|
#if NOT_DONE
|
|
unsigned len;
|
|
char *buffer;
|
|
|
|
buffer = (char *) alloca(strlen(n1) + strlen(n2) + 2 * ONS_OHD);
|
|
len = obj_namestring(buffer,n1);
|
|
len += obj_namestring(buffer + len,n2);
|
|
objrecord(ALIAS,buffer,len);
|
|
#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)
|
|
{
|
|
size_t len;
|
|
char *name;
|
|
|
|
//printf("obj_mangle(s = %p, '%s'), mangle = x%x\n",s,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
|
|
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
|
|
strupr(dest); // to upper case
|
|
break;
|
|
case mTYman_std:
|
|
#if TARGET_LINUX || TARGET_OSX || 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_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;
|
|
|
|
case mTYman_c:
|
|
if (len >= DEST_LEN - 1)
|
|
dest = (char *)mem_malloc(1 + len + 1);
|
|
dest[0] = '_';
|
|
memcpy(dest + 1,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;
|
|
//dbg_printf("elf_data_start(%s,size %d,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)
|
|
{
|
|
//printf("elf_func_start(%s)\n",sfunc->Sident);
|
|
symbol_debug(sfunc);
|
|
|
|
if (sfunc->Sseg == UNKNOWN)
|
|
sfunc->Sseg = CODE;
|
|
//printf("sfunc->Sseg %d CODE %d cseg %d Coffset x%x\n",sfunc->Sseg,CODE,cseg,Coffset);
|
|
cseg = sfunc->Sseg;
|
|
assert(cseg == CODE || cseg > UDATA);
|
|
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);
|
|
|
|
#if 0
|
|
// fill in the function size
|
|
if (I64)
|
|
SymbolTable64[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset;
|
|
else
|
|
SymbolTable[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset;
|
|
#endif
|
|
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)
|
|
{
|
|
#if 0
|
|
printf("objpubdef(%d:x%x s=%p, %s)\n", seg, offset, s, s->Sident);
|
|
//symbol_print(s);
|
|
#endif
|
|
symbol_debug(s);
|
|
|
|
s->Soffset = offset;
|
|
s->Sseg = seg;
|
|
switch (s->Sclass)
|
|
{
|
|
case SCglobal:
|
|
case SCinline:
|
|
public_symbuf->write(&s, sizeof(s));
|
|
break;
|
|
case SCcomdat:
|
|
case SCcomdef:
|
|
public_symbuf->write(&s, sizeof(s));
|
|
break;
|
|
default:
|
|
local_symbuf->write(&s, sizeof(s));
|
|
break;
|
|
}
|
|
//printf("%p\n", *(void**)public_symbuf->buf);
|
|
s->Sxtrnnum = 1;
|
|
}
|
|
|
|
/*******************************
|
|
* 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)
|
|
{
|
|
//printf("objextdef('%s')\n",name);
|
|
assert(name);
|
|
assert(extdef == 0);
|
|
extdef = elf_addstr(symtab_strings, name);
|
|
return 0;
|
|
}
|
|
|
|
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)
|
|
{
|
|
//printf("objextern('%s') %x\n",s->Sident,s->Svalue);
|
|
symbol_debug(s);
|
|
extern_symbuf->write(&s, sizeof(s));
|
|
s->Sxtrnnum = 1;
|
|
}
|
|
|
|
/*******************************
|
|
* 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', size=%d, count=%d)\n",s->Sident,size,count);
|
|
symbol_debug(s);
|
|
|
|
// can't have code or thread local comdef's
|
|
assert(!(s->ty() & (
|
|
#if TARGET_SEGMENTED
|
|
mTYcs |
|
|
#endif
|
|
mTYthread)));
|
|
|
|
struct Comdef comdef;
|
|
comdef.sym = s;
|
|
comdef.size = size;
|
|
comdef.count = count;
|
|
comdef_symbuf->write(&comdef, sizeof(comdef));
|
|
s->Sxtrnnum = 1;
|
|
return 0; // should return void
|
|
}
|
|
|
|
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);
|
|
size_t idx = SegData[seg]->SDshtidx;
|
|
if ((I64 ? SecHdrTab64[idx].flags : SecHdrTab[idx].flags) == S_ZEROFILL)
|
|
{ // Use SDoffset to record size of bss 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);
|
|
if (!buf) halt();
|
|
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;
|
|
}
|
|
|
|
/*********************************************
|
|
* Add a relocation entry for seg/offset.
|
|
*/
|
|
|
|
void mach_addrel(int seg, targ_size_t offset, symbol *targsym,
|
|
unsigned targseg, int rtype, int val)
|
|
{
|
|
Relocation rel;
|
|
rel.offset = offset;
|
|
rel.targsym = targsym;
|
|
rel.targseg = targseg;
|
|
rel.rtype = rtype;
|
|
rel.funcsym = funcsym_p;
|
|
rel.val = val;
|
|
seg_data *pseg = SegData[seg];
|
|
if (!pseg->SDrel)
|
|
pseg->SDrel = new Outbuffer();
|
|
pseg->SDrel->write(&rel, sizeof(rel));
|
|
}
|
|
|
|
/****************************************
|
|
* Sort the relocation entry buffer.
|
|
*/
|
|
|
|
#if __DMC__
|
|
static int __cdecl rel_fp(const void *e1, const void *e2)
|
|
{ Relocation *r1 = (Relocation *)e1;
|
|
Relocation *r2 = (Relocation *)e2;
|
|
|
|
return r1->offset - r2->offset;
|
|
}
|
|
#else
|
|
extern "C" {
|
|
static int rel_fp(const void *e1, const void *e2)
|
|
{ Relocation *r1 = (Relocation *)e1;
|
|
Relocation *r2 = (Relocation *)e2;
|
|
|
|
return r1->offset - r2->offset;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void mach_relsort(Outbuffer *buf)
|
|
{
|
|
qsort(buf->buf, buf->size() / sizeof(Relocation), sizeof(Relocation), &rel_fp);
|
|
}
|
|
|
|
/*******************************
|
|
* Output a relocation entry for a segment
|
|
* Input:
|
|
* seg = where the address is going
|
|
* offset = offset within seg
|
|
* type = ELF relocation type
|
|
* index = Related symbol table index
|
|
* val = addend or displacement from address
|
|
*/
|
|
|
|
void elf_addrel(int seg, targ_size_t offset, unsigned type,
|
|
IDXSYM symidx, targ_size_t val)
|
|
{
|
|
}
|
|
|
|
/*******************************
|
|
* Refer to address that is in the data segment.
|
|
* Input:
|
|
* seg:offset = the address being fixed up
|
|
* val = displacement from start of target segment
|
|
* targetdatum = target segment number (DATA, CDATA or UDATA, etc.)
|
|
* flags = CFoff, CFseg
|
|
* Example:
|
|
* int *abc = &def[3];
|
|
* to allocate storage:
|
|
* reftodatseg(DATA,offset,3 * sizeof(int *),UDATA);
|
|
*/
|
|
|
|
void reftodatseg(int seg,targ_size_t offset,targ_size_t val,
|
|
unsigned targetdatum,int flags)
|
|
{
|
|
Outbuffer *buf = SegData[seg]->SDbuf;
|
|
int save = buf->size();
|
|
buf->setsize(offset);
|
|
#if 0
|
|
printf("reftodatseg(seg:offset=%d:x%llx, val=x%llx, targetdatum %x, flags %x )\n",
|
|
seg,offset,val,targetdatum,flags);
|
|
#endif
|
|
if (SegData[seg]->isCode() && SegData[targetdatum]->isCode())
|
|
{
|
|
assert(0);
|
|
}
|
|
mach_addrel(seg, offset, NULL, targetdatum, RELaddr);
|
|
if (I64)
|
|
{
|
|
if (flags & CFoffset64)
|
|
{
|
|
buf->write64(val);
|
|
if (save > offset + 8)
|
|
buf->setsize(save);
|
|
return;
|
|
}
|
|
}
|
|
buf->write32(val);
|
|
if (save > offset + 4)
|
|
buf->setsize(save);
|
|
}
|
|
|
|
/*******************************
|
|
* Refer to address that is in the current function code (funcsym_p).
|
|
* 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)
|
|
{
|
|
//printf("reftocodseg(seg=%d, offset=x%lx, val=x%lx )\n",seg,(unsigned long)offset,(unsigned long)val);
|
|
assert(seg > 0);
|
|
Outbuffer *buf = SegData[seg]->SDbuf;
|
|
int save = buf->size();
|
|
buf->setsize(offset);
|
|
val -= funcsym_p->Soffset;
|
|
mach_addrel(seg, offset, funcsym_p, 0, RELaddr);
|
|
// if (I64)
|
|
// buf->write64(val);
|
|
// else
|
|
buf->write32(val);
|
|
if (save > offset + 4)
|
|
buf->setsize(save);
|
|
}
|
|
|
|
/*******************************
|
|
* Refer to an identifier.
|
|
* Input:
|
|
* seg = where the address is going (CODE or DATA)
|
|
* offset = offset within seg
|
|
* s -> Symbol table entry for identifier
|
|
* val = displacement from identifier
|
|
* flags = CFselfrel: self-relative
|
|
* CFseg: get segment
|
|
* CFoff: get offset
|
|
* CFpc32: [RIP] addressing, val is 0, -1, -2 or -4
|
|
* CFoffset64: 8 byte offset for 64 bit builds
|
|
* 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)
|
|
{
|
|
int retsize = (flags & CFoffset64) ? 8 : 4;
|
|
#if 0
|
|
dbg_printf("\nreftoident('%s' seg %d, offset x%llx, val x%llx, flags x%x)\n",
|
|
s->Sident,seg,(unsigned long long)offset,(unsigned long long)val,flags);
|
|
printf("retsize = %d\n", retsize);
|
|
//dbg_printf("Sseg = %d, Sxtrnnum = %d\n",s->Sseg,s->Sxtrnnum);
|
|
symbol_print(s);
|
|
#endif
|
|
assert(seg > 0);
|
|
if (s->Sclass != SClocstat && !s->Sxtrnnum)
|
|
{ // It may get defined later as public or local, so defer
|
|
addtofixlist(s, offset, seg, val, flags);
|
|
}
|
|
else
|
|
{
|
|
if (I64)
|
|
{
|
|
//if (s->Sclass != SCcomdat)
|
|
//val += s->Soffset;
|
|
int v = 0;
|
|
if (flags & CFpc32)
|
|
v = (int)val;
|
|
if (flags & CFselfrel)
|
|
{
|
|
mach_addrel(seg, offset, s, 0, RELrel, v);
|
|
}
|
|
else
|
|
{
|
|
mach_addrel(seg, offset, s, 0, RELaddr, v);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (SegData[seg]->isCode() && flags & CFselfrel)
|
|
{
|
|
if (!jumpTableSeg)
|
|
{
|
|
jumpTableSeg =
|
|
mach_getsegment("__jump_table", "__IMPORT", 0, S_SYMBOL_STUBS | S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE);
|
|
}
|
|
seg_data *pseg = SegData[jumpTableSeg];
|
|
if (I64)
|
|
SecHdrTab64[pseg->SDshtidx].reserved2 = 5;
|
|
else
|
|
SecHdrTab[pseg->SDshtidx].reserved2 = 5;
|
|
|
|
if (!indirectsymbuf1)
|
|
indirectsymbuf1 = new Outbuffer();
|
|
else
|
|
{ // Look through indirectsym to see if it is already there
|
|
int n = indirectsymbuf1->size() / sizeof(Symbol *);
|
|
Symbol **psym = (Symbol **)indirectsymbuf1->buf;
|
|
for (int i = 0; i < n; i++)
|
|
{ // Linear search, pretty pathetic
|
|
if (s == psym[i])
|
|
{ val = i * 5;
|
|
goto L1;
|
|
}
|
|
}
|
|
}
|
|
|
|
val = pseg->SDbuf->size();
|
|
static char halts[5] = { 0xF4,0xF4,0xF4,0xF4,0xF4 };
|
|
pseg->SDbuf->write(halts, 5);
|
|
|
|
// Add symbol s to indirectsymbuf1
|
|
indirectsymbuf1->write(&s, sizeof(Symbol *));
|
|
L1:
|
|
val -= offset + 4;
|
|
mach_addrel(seg, offset, NULL, jumpTableSeg, RELrel);
|
|
}
|
|
else if (SegData[seg]->isCode() &&
|
|
((s->Sclass != SCextern && SegData[s->Sseg]->isCode()) || s->Sclass == SClocstat || s->Sclass == SCstatic))
|
|
{
|
|
val += s->Soffset;
|
|
mach_addrel(seg, offset, NULL, s->Sseg, RELaddr);
|
|
}
|
|
else if (SegData[seg]->isCode() && !tyfunc(s->ty()))
|
|
{
|
|
if (!pointersSeg)
|
|
{
|
|
pointersSeg =
|
|
mach_getsegment("__pointers", "__IMPORT", 0, S_NON_LAZY_SYMBOL_POINTERS);
|
|
}
|
|
seg_data *pseg = SegData[pointersSeg];
|
|
|
|
if (!indirectsymbuf2)
|
|
indirectsymbuf2 = new Outbuffer();
|
|
else
|
|
{ // Look through indirectsym to see if it is already there
|
|
int n = indirectsymbuf2->size() / sizeof(Symbol *);
|
|
Symbol **psym = (Symbol **)indirectsymbuf2->buf;
|
|
for (int i = 0; i < n; i++)
|
|
{ // Linear search, pretty pathetic
|
|
if (s == psym[i])
|
|
{ val = i * 4;
|
|
goto L2;
|
|
}
|
|
}
|
|
}
|
|
|
|
val = pseg->SDbuf->size();
|
|
pseg->SDbuf->writezeros(NPTRSIZE);
|
|
|
|
// Add symbol s to indirectsymbuf2
|
|
indirectsymbuf2->write(&s, sizeof(Symbol *));
|
|
|
|
L2:
|
|
//printf("reftoident: seg = %d, offset = x%x, s = %s, val = x%x, pointersSeg = %d\n", seg, offset, s->Sident, val, pointersSeg);
|
|
mach_addrel(seg, offset, NULL, pointersSeg, RELaddr);
|
|
}
|
|
else
|
|
{ //val -= s->Soffset;
|
|
mach_addrel(seg, offset, s, 0, RELaddr);
|
|
}
|
|
}
|
|
|
|
Outbuffer *buf = SegData[seg]->SDbuf;
|
|
int save = buf->size();
|
|
buf->setsize(offset);
|
|
//printf("offset = x%llx, val = x%llx\n", offset, val);
|
|
if (retsize == 8)
|
|
buf->write64(val);
|
|
else
|
|
buf->write32(val);
|
|
if (save > offset + retsize)
|
|
buf->setsize(save);
|
|
}
|
|
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(targ_size_t size, long foffset)
|
|
{
|
|
long offset;
|
|
switch (size)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
return foffset;
|
|
case 2:
|
|
offset = (foffset + 1) & ~1;
|
|
break;
|
|
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 %lu\n",(unsigned long)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)
|
|
{
|
|
int align = I64 ? 4 : 2;
|
|
|
|
mach_getsegment("__minfo_beg", "__DATA", align, S_COALESCED, 4);
|
|
|
|
int seg = mach_getsegment("__minfodata", "__DATA", align, S_REGULAR);
|
|
//printf("obj_moduleinfo(%s) seg = %d:x%x\n", scc->Sident, seg, Offset(seg));
|
|
|
|
#if 0
|
|
type *t = type_fake(TYint);
|
|
t->Tmangle = mTYman_c;
|
|
char *p = (char *)malloc(5 + strlen(scc->Sident) + 1);
|
|
strcpy(p, "SUPER");
|
|
strcpy(p + 5, scc->Sident);
|
|
symbol *s_minfo_beg = symbol_name(p, SCglobal, t);
|
|
objpubdef(seg, s_minfo_beg, 0);
|
|
#endif
|
|
|
|
int flags = CFoff;
|
|
if (I64)
|
|
flags |= CFoffset64;
|
|
SegData[seg]->SDoffset += reftoident(seg, Offset(seg), scc, 0, flags);
|
|
|
|
mach_getsegment("__minfo_end", "__DATA", align, S_COALESCED, 4);
|
|
}
|
|
|
|
#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
|