244 changes: 142 additions & 102 deletions src/backend/dwarf.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "cv4.h"
#include "cgcv.h"
#include "dt.h"
#include "rtlsym.h"

#include "aa.h"
#include "tinfo.h"
Expand Down Expand Up @@ -70,12 +71,6 @@ static char __file__[] = __FILE__; // for tassert.h

#define OFFSET_FAC REGSIZE

#if TARGET_LINUX
const bool genEhFrame = true;
#else
const bool genEhFrame = false;
#endif

int dwarf_getsegment(const char *name, int align)
{
#if ELFOBJ
Expand All @@ -92,7 +87,7 @@ int dwarf_getsegment(const char *name, int align)
#define RELaddr 0 // straight address
#define RELrel 1 // relative to location to be fixed up

void dwarf_addrel(int seg, targ_size_t offset, int targseg, targ_size_t val = 0)
void dwarf_addrel(int seg, targ_size_t offset, int targseg, targ_size_t val)
{
#if ELFOBJ
ElfObj::addrel(seg, offset, I64 ? R_X86_64_32 : R_386_32, MAP_SEG2SYMIDX(targseg), val);
Expand Down Expand Up @@ -330,13 +325,6 @@ void dwarf_CFA_args_size(size_t sz)
cfa_buf.writeuLEB128(sz);
}

// .debug_frame
static IDXSEC debug_frame_secidx;

// .debug_str
static IDXSEC debug_str_secidx;
static Outbuffer *debug_str_buf;

// .debug_pubnames
static IDXSEC debug_pubnames_secidx;
static Outbuffer *debug_pubnames_buf;
Expand Down Expand Up @@ -576,59 +564,91 @@ void writeDebugFrameHeader(Outbuffer *buf)
* Append .eh_frame header to buf.
* Almost identical to .debug_frame
* Params:
* dfseg = SegData[] index for .eh_frame
* buf = write raw data here
* personality = "__dmd_personality_v0"
* See_Also:
* https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
*/
void writeEhFrameHeader(Outbuffer *buf)
static void writeEhFrameHeader(IDXSEC dfseg, Outbuffer *buf, Symbol *personality)
{
#pragma pack(1)
struct EhFrameHeader
/* Augmentation string:
* z = first character, means Augmentation Data field is present
* eh = EH Data field is present
* P = Augmentation Data contains 2 args:
* 1. encoding of 2nd arg
* 2. address of personality routine
* L = Augmentation Data contains 1 arg:
* 1. the encoding used for Augmentation Data in FDE
* Augmentation Data in FDE:
* 1. address of LSDA (gcc_except_table)
* R = Augmentation Data contains 1 arg:
* 1. encoding of addresses in FDE
* Non-EH code: "zR"
* EH code: "zPLR"
*/

const unsigned startsize = buf->size();

// Length of CIE, not including padding
const unsigned cielen = 4 + 4 + 1 +
(config.ehmethod == EH_DWARF ? 5 : 3) +
1 + 1 + 1 +
(config.ehmethod == EH_DWARF ? 8 : 2) +
5;

const unsigned pad = -cielen & (I64 ? 7 : 3); // pad to addressing unit size boundary
const unsigned length = cielen + pad - 4;

buf->reserve(length + 4);
buf->write32(length); // length of CIE, not including length and extended length fields
buf->write32(0); // CIE ID
buf->writeByten(1); // version
if (config.ehmethod == EH_DWARF)
buf->write("zPLR", 5); // Augmentation String
else
buf->writen("zR", 3);
// not present: EH Data: 4 bytes for I32, 8 bytes for I64
buf->writeByten(1); // code alignment factor
buf->writeByten(0x80 - OFFSET_FAC); // data alignment factor (I64 ? -8 : -4)
buf->writeByten(I64 ? 16 : 8); // return address register
if (config.ehmethod == EH_DWARF)
{
unsigned length;
unsigned CIE_id;
unsigned char version;
unsigned char augmentation[3];
unsigned char code_alignment_factor;
unsigned char data_alignment_factor;
unsigned char return_address_register;
unsigned char augmentation_length;
unsigned char address_pointer_encoding;
unsigned char opcodes[5];
};
#pragma pack()
static EhFrameHeader ehFrameHeader =
buf->writeByten(7); // Augmentation Length
buf->writeByten(DW_EH_PE_absptr | DW_EH_PE_udata4); // P: personality routing address encoding
ElfObj::reftoident(dfseg, buf->size(), personality, 0, CFoff); // PC_begin
buf->writeByten(DW_EH_PE_absptr | DW_EH_PE_udata4); // L: address encoding for LSDA in FDE
buf->writeByten(DW_EH_PE_pcrel | DW_EH_PE_sdata4); // R: encoding of addresses in FDE
}
else
{
0, // length
0, // CIE_id
1, // version
{ 'z','R',0 }, // augmentation
1, // code alignment factor
0x7C, // data alignment factor (-4)
8, // return address register
1, // augmentation_length
DW_EH_PE_pcrel | DW_EH_PE_sdata4, // address_pointer_encoding
{
DW_CFA_def_cfa, 4,4, // r4,4 [r7,8]
DW_CFA_offset +8,1, // r8,1 [r16,1]
}
};
buf->writeByten(1); // Augmentation Length
buf->writeByten(DW_EH_PE_pcrel | DW_EH_PE_sdata4); // R: encoding of addresses in FDE
}

if (I64)
{
ehFrameHeader.data_alignment_factor = 0x78; // (-8)
ehFrameHeader.return_address_register = 16;
ehFrameHeader.opcodes[1] = 7; // RSP
ehFrameHeader.opcodes[2] = 8;
ehFrameHeader.opcodes[3] = DW_CFA_offset + 16; // RIP
buf->writeByten(DW_CFA_def_cfa); // DEF_CFA r7,8
buf->writeByten(7);
buf->writeByten(8);

buf->writeByten(DW_CFA_offset + 16); // OFFSET r16,1
buf->writeByten(1);
}
assert(ehFrameHeader.data_alignment_factor == 0x80 - OFFSET_FAC);
else
{
buf->writeByten(DW_CFA_def_cfa); // DEF_CFA r4,4
buf->writeByten(4);
buf->writeByten(4);

unsigned pad = -sizeof(EhFrameHeader) & (I64 ? 7 : 3); // pad to addressing unit size boundary
ehFrameHeader.length = sizeof(EhFrameHeader) - 4 + pad;
assert(sizeof(EhFrameHeader) == 4 + 4 + 14);
buf->writeByten(DW_CFA_offset + 8); // OFFSET r8,1
buf->writeByten(1);
}

buf->reserve(sizeof(EhFrameHeader) + pad);
buf->writen(&ehFrameHeader, sizeof(EhFrameHeader));
for (unsigned i = 0; i < pad; ++i)
buf->writeByten(DW_CFA_nop);

assert(startsize + length + 4 == buf->size());
}

/*********************************************
Expand Down Expand Up @@ -666,7 +686,6 @@ void writeDebugFrameFDE(IDXSEC dfseg, Symbol *sfunc)
// Do we need this?
//debugFrameFDE.initial_location = sfunc->Soffset;

debug_frame_secidx = SegData[dfseg]->SDshtidx;
Outbuffer *debug_frame_buf = SegData[dfseg]->SDbuf;
unsigned debug_frame_buf_offset = debug_frame_buf->p - debug_frame_buf->buf;
debug_frame_buf->reserve(1000);
Expand Down Expand Up @@ -706,7 +725,6 @@ void writeDebugFrameFDE(IDXSEC dfseg, Symbol *sfunc)
// Do we need this?
//debugFrameFDE.initial_location = sfunc->Soffset;

debug_frame_secidx = SegData[dfseg]->SDshtidx;
Outbuffer *debug_frame_buf = SegData[dfseg]->SDbuf;
unsigned debug_frame_buf_offset = debug_frame_buf->p - debug_frame_buf->buf;
debug_frame_buf->reserve(1000);
Expand All @@ -729,60 +747,64 @@ void writeDebugFrameFDE(IDXSEC dfseg, Symbol *sfunc)
*/
void writeEhFrameFDE(IDXSEC dfseg, Symbol *sfunc)
{
unsigned CIE_offset = 0; // offset of enclosing CIE
const unsigned CIE_offset = 0; // offset of enclosing CIE
Outbuffer *buf = SegData[dfseg]->SDbuf;
unsigned offset = buf->p - buf->buf;

#pragma pack(1)
struct EhFrameFDE
{
unsigned length;
unsigned CIE_pointer;
unsigned PC_begin;
unsigned PC_range;
unsigned char augmentation_length;
};
#pragma pack()
assert(sizeof(EhFrameFDE) == 17);
const unsigned startsize = buf->size();

EhFrameFDE ehFrameFDE;
// Length of FDE, not including padding
const unsigned fdelen = 4 + 4 + 4 + 4 + (config.ehmethod == EH_DWARF ? 5 : 1) + cfa_buf.size();

// Pad to byte boundary
const unsigned pad = I64 ? 8 : 4;
for (unsigned n = (-(sizeof(EhFrameFDE) + cfa_buf.size()) & (pad - 1)); n; n--)
cfa_buf.writeByte(DW_CFA_nop);
const unsigned pad = -fdelen & (I64 ? 7 : 3); // pad to addressing unit size boundary
const unsigned length = fdelen + pad - 4;

ehFrameFDE.length = 13 + cfa_buf.size();
ehFrameFDE.CIE_pointer = (offset + 4) - CIE_offset;
ehFrameFDE.PC_begin = 0; /* maybe sfunc->Soffset */
ehFrameFDE.PC_range = sfunc->Ssize;
ehFrameFDE.augmentation_length = 0;
buf->write32(length); // Length (no Extended Length)
buf->write32((startsize + 4) - CIE_offset); // CIE Pointer
ElfObj::reftoident(dfseg, startsize + 8, sfunc, 0, CFpc32 | CFoff); // PC_begin
buf->write32(sfunc->Ssize); // PC Range
if (config.ehmethod == EH_DWARF)
{
buf->writeByten(4); // Augmentation Data Length
int etseg = dwarf_getsegment(except_table_name, 1);
Outbuffer *etbuf = SegData[etseg]->SDbuf;
buf->write32(0); // address of LSDA (".gcc_except_table")
dwarf_addrel(dfseg, buf->size() - 4, etseg, etbuf->size()); // and the fixup
}
else
buf->writeByten(0); // Augmentation Data Length

debug_frame_secidx = SegData[dfseg]->SDshtidx;
buf->reserve(1000);
buf->writen(&ehFrameFDE,sizeof(ehFrameFDE));
buf->write(&cfa_buf);

dwarf_addrel(dfseg,offset + 8,sfunc->Sseg); // offset of PC_begin
for (unsigned i = 0; i < pad; ++i)
buf->writeByten(DW_CFA_nop);

assert(startsize + length + 4 == buf->size());
}

void dwarf_initfile(const char *filename)
{
int seg = dwarf_getsegment(genEhFrame ? eh_frame_name : debug_frame_name, 1);
debug_frame_secidx = SegData[seg]->SDshtidx;
Outbuffer *debug_frame_buf = SegData[seg]->SDbuf;
debug_frame_buf->reserve(1000);
if (config.ehmethod == EH_DWARF)
{
dwarf_getsegment(except_table_name, 1);

if (genEhFrame)
writeEhFrameHeader(debug_frame_buf);
else
writeDebugFrameHeader(debug_frame_buf);
int seg = dwarf_getsegment(eh_frame_name, 1);
Outbuffer *buf = SegData[seg]->SDbuf;
buf->reserve(1000);
writeEhFrameHeader(seg, buf, getRtlsym(RTLSYM_PERSONALITY));
}
if (!config.fulltypes)
return;
if (config.ehmethod == EH_DM)
{
int seg = dwarf_getsegment(debug_frame_name, 1);
Outbuffer *buf = SegData[seg]->SDbuf;
buf->reserve(1000);
writeDebugFrameHeader(buf);
}

/* ======================================== */

seg = dwarf_getsegment(debug_str, 0);
debug_str_secidx = SegData[seg]->SDshtidx;
debug_str_buf = SegData[seg]->SDbuf;
int seg = dwarf_getsegment(debug_str, 0);
Outbuffer *debug_str_buf = SegData[seg]->SDbuf;
debug_str_buf->reserve(1000);

/* ======================================== */
Expand Down Expand Up @@ -1245,6 +1267,13 @@ void dwarf_func_term(Symbol *sfunc)
{
//printf("dwarf_func_term(sfunc = '%s')\n", sfunc->Sident);

if (config.ehmethod == EH_DWARF)
{
IDXSEC dfseg = dwarf_getsegment(eh_frame_name, 1);
writeEhFrameFDE(dfseg, sfunc);
}
if (!config.fulltypes)
return;
#if MARS
if (sfunc->Sflags & SFLnodebug)
return;
Expand All @@ -1255,12 +1284,7 @@ void dwarf_func_term(Symbol *sfunc)

unsigned funcabbrevcode;

if (genEhFrame)
{
IDXSEC dfseg = dwarf_getsegment(eh_frame_name, 1);
writeEhFrameFDE(dfseg, sfunc);
}
else
if (config.ehmethod == EH_DM)
{
IDXSEC dfseg = dwarf_getsegment(debug_frame_name, 1);
writeDebugFrameFDE(dfseg, sfunc);
Expand Down Expand Up @@ -2657,9 +2681,25 @@ unsigned dwarf_abbrev_code(unsigned char *data, size_t nbytes)
return *pcode;
}

/*****************************************************
* Write Dwarf-style exception tables.
* Params:
* sfunc = function to generate tables for
* startoffset = size of function prolog
* retoffset = offset from start of function to epilog
*/
void dwarf_except_gentables(Funcsym *sfunc, unsigned startoffset, unsigned retoffset)
{
int seg = dwarf_getsegment(except_table_name, 1);
Outbuffer *buf = SegData[seg]->SDbuf;
buf->reserve(100);
genDwarfEh(sfunc, seg, buf, usednteh & EHcleanup, startoffset, retoffset);
}

#else
void dwarf_CFA_set_loc(size_t location) { }
void dwarf_CFA_set_reg_offset(int reg, int offset) { }
void dwarf_CFA_offset(int reg, int offset) { }
void dwarf_except_gentables(Funcsym *sfunc, unsigned startoffset, unsigned retoffset) { }
#endif
#endif
4 changes: 4 additions & 0 deletions src/backend/dwarf.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ unsigned dwarf_abbrev_code(unsigned char *data, size_t nbytes);

int dwarf_regno(int reg);

void dwarf_addrel(int seg, targ_size_t offset, int targseg, targ_size_t val = 0);
void dwarf_except_gentables(Funcsym *sfunc, unsigned startoffset, unsigned retoffset);
void genDwarfEh(Funcsym *sfunc, int seg, Outbuffer *et, bool scancode, unsigned startoffset, unsigned retoffset);

#endif
40 changes: 28 additions & 12 deletions src/backend/dwarfeh.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
static char __file__[] = __FILE__; /* for tassert.h */
#include "tassert.h"

void dwarf_addrel(int seg, targ_size_t offset, int targseg, targ_size_t val = 0);

int actionTableInsert(Outbuffer *atbuf, int ttindex, int nextoffset);

unsigned long uLEB128(unsigned char **p);
Expand Down Expand Up @@ -72,7 +70,7 @@ struct DwEhTable
ptr = (DwEhTableEntry *)::realloc(ptr, capacity * sizeof(DwEhTableEntry));
assert(ptr);
}
memset(index(dim), 0, sizeof(DwEhTable));
memset(ptr + dim, 0, sizeof(DwEhTable));
return dim++;
}
};
Expand All @@ -86,14 +84,15 @@ static DwEhTable dwehtable;
* seg = .gcc_except_table segment
* et = buffer to insert table into
* scancode = true if there are destructors in the code (i.e. usednteh & EHcleanup)
* startoffset = size of function prolog
* retoffset = offset from start of function to epilog
*/

symbol *genDwarfEh(Symbol *sfunc, int seg, Outbuffer *et, bool scancode)
void genDwarfEh(Funcsym *sfunc, int seg, Outbuffer *et, bool scancode, unsigned startoffset, unsigned retoffset)
{
#ifdef DEBUG
unittest_dwarfeh();
#endif
assert(config.ehmethod == EH_DWARF);

/* LPstart = encoding of LPbase
* LPbase = landing pad base (normally omitted)
Expand All @@ -112,8 +111,10 @@ symbol *genDwarfEh(Symbol *sfunc, int seg, Outbuffer *et, bool scancode)
* Type Table
*/

et->reserve(100);
block *startblock = sfunc->Sfunc->Fstartblock;
//printf("genDwarfEh: func = %s, offset = x%x, startblock->Boffset = x%x\n", sfunc->Sident, sfunc->Soffset, startblock->Boffset);
//printf("genDwarfEh: func = %s, offset = x%x, startblock->Boffset = x%x, scancode = %d\n",
// sfunc->Sident, (int)sfunc->Soffset, (int)startblock->Boffset, scancode);

unsigned startsize = et->size();
assert((startsize & 3) == 0); // should be aligned
Expand All @@ -126,9 +127,18 @@ symbol *genDwarfEh(Symbol *sfunc, int seg, Outbuffer *et, bool scancode)
*/
int index = -1;
block *bprev = NULL;
// The first entry encompasses the entire function
{
unsigned i = deh->push();
DwEhTableEntry *d = deh->index(i);
d->start = startoffset;
d->end = retoffset;
d->lpad = 0; // no cleanup, no catches
index = i;
}
for (block *b = startblock; b; b = b->Bnext)
{
if (index >= 0 && b->Btry == bprev)
if (index > 0 && b->Btry == bprev)
{
DwEhTableEntry *d = deh->index(index);
d->end = b->Boffset;
Expand All @@ -150,15 +160,15 @@ symbol *genDwarfEh(Symbol *sfunc, int seg, Outbuffer *et, bool scancode)
unsigned *pat = bf->BS.BIJCATCH.actionTable;
unsigned length = pat[0];
assert(length);
unsigned offset = 0;
unsigned offset = -1;
for (unsigned u = length; u; --u)
{
/* Buy doing depth-first insertion into the Action Table,
* we can combine common tails.
*/
offset = actionTableInsert(&atbuf, pat[u], offset);
}
d->lpad = offset + 1;
d->action = offset + 1;
}
d->prev = index;
index = i;
Expand Down Expand Up @@ -194,6 +204,7 @@ symbol *genDwarfEh(Symbol *sfunc, int seg, Outbuffer *et, bool scancode)
assert(n == 0);
}
}
//printf("deh->dim = %d\n", (int)deh->dim);

/* Build Call Site Table
* Be sure to not generate empty entries,
Expand All @@ -202,18 +213,21 @@ symbol *genDwarfEh(Symbol *sfunc, int seg, Outbuffer *et, bool scancode)
* presumption that there may not
* be overlapping entries in the Call Site Table.
*/
unsigned end = 0;
assert(deh->dim);
unsigned end = deh->index(0)->start;
for (unsigned i = 0; i < deh->dim; ++i)
{
unsigned j = i;
do
{
DwEhTableEntry *d = deh->index(j);
//printf(" [%d] start=%x end=%x lpad=%x action=%x bcatch=%p prev=%d\n",
// j, d->start, d->end, d->lpad, d->action, d->bcatch, d->prev);
if (d->start <= end && end < d->end)
{
unsigned start = end;
unsigned dend = d->end;
if (j + 1 < deh->dim)
if (i + 1 < deh->dim)
{
DwEhTableEntry *dnext = deh->index(i + 1);
if (dnext->start < dend)
Expand All @@ -229,6 +243,7 @@ symbol *genDwarfEh(Symbol *sfunc, int seg, Outbuffer *et, bool scancode)
cstbuf.writeuLEB128(LandingPad);
unsigned ActionTable = d->action;
cstbuf.writeuLEB128(ActionTable);
//printf("\t%x %x %x %x\n", CallSiteStart, CallSiteRange, LandingPad, ActionTable);
}

end = dend;
Expand Down Expand Up @@ -295,7 +310,8 @@ symbol *genDwarfEh(Symbol *sfunc, int seg, Outbuffer *et, bool scancode)
for (int i = sfunc->Sfunc->typesTableDim; i--; )
{
Symbol *s = typesTable[i];
dwarf_addrel(seg, et->size(), s->Sseg, s->Soffset);
ElfObj::reftoident(seg, et->size(), s, 0, CFoff); //dwarf_addrel(seg, et->size(), s->Sseg, s->Soffset);
//et->write32(s->Soffset);
}
assert(TToffset == et->size() - startsize);
}
Expand Down
11 changes: 5 additions & 6 deletions src/backend/elfobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -853,8 +853,7 @@ Obj *Obj::init(Outbuffer *objbuf, const char *filename, const char *csegname)
elf_getsegment2(SHN_COM, STI_COM, 0);
assert(SegData[COMD]->SDseg == COMD);

if (config.fulltypes)
dwarf_initfile(filename);
dwarf_initfile(filename);
return obj;
}

Expand Down Expand Up @@ -2124,8 +2123,7 @@ void Obj::func_start(Symbol *sfunc)
Obj::pubdef(cseg, sfunc, Coffset);
sfunc->Soffset = Coffset;

if (config.fulltypes)
dwarf_func_start(sfunc);
dwarf_func_start(sfunc);
}

/*******************************
Expand All @@ -2142,8 +2140,7 @@ void Obj::func_term(Symbol *sfunc)
SymbolTable64[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset;
else
SymbolTable[sfunc->Sxtrnnum].st_size = Coffset - sfunc->Soffset;
if (config.fulltypes)
dwarf_func_term(sfunc);
dwarf_func_term(sfunc);
}

/********************************
Expand Down Expand Up @@ -2947,6 +2944,8 @@ int Obj::reftoident(int seg, targ_size_t offset, Symbol *s, targ_size_t val,
else if (segtyp == DATA)
{ // relocation from within DATA seg
relinfo = I64 ? R_X86_64_32 : R_386_32;
if (I64 && flags & CFpc32)
relinfo = R_X86_64_PC32;
}
else
{ // relocation from within CODE seg
Expand Down
1 change: 1 addition & 0 deletions src/backend/rtlsym.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ SYMBOL_MARS(D_HANDLER, FLfunc,FREGSAVED,"_d_framehandler", 0, tsclib) \
SYMBOL_MARS(D_LOCAL_UNWIND2, FLfunc,FREGSAVED,"_d_local_unwind2", 0, tsclib) \
SYMBOL_SCPP(LOCAL_UNWIND2, FLfunc,FREGSAVED,"_local_unwind2", 0, tsclib) \
SYMBOL_Z(UNWIND_RESUME, FLfunc,FREGSAVED,"_Unwind_Resume", SFLexit, tsclib) \
SYMBOL_Z(PERSONALITY, FLfunc,FREGSAVED,"__dmd_personality_v0", 0, tsclib) \
\
SYMBOL_Z(TLS_INDEX, FLextern,0,"_tls_index",0,tsint) \
SYMBOL_Z(TLS_ARRAY, FLextern,0,"_tls_array",0,tspvoid) \
Expand Down
1 change: 1 addition & 0 deletions src/s2ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,7 @@ class S2irVisitor : public Visitor
f->typesTable = (Symbol **)::realloc(f->typesTable, f->typesTableCapacity * sizeof(Symbol *));
assert(f->typesTable);
}
f->typesTableDim = j + 1;
f->typesTable[j] = catchtype;
}
bswitch->BS.Bswitch[1 + i] = 1 + j; // index starts at 1
Expand Down