342 changes: 252 additions & 90 deletions src/backend/dwarf.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ 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 Down Expand Up @@ -141,6 +147,8 @@ void append_addr(Outbuffer *buf, targ_size_t addr)

// Dwarf Symbolic Debugging Information

// CFA = value of the stack pointer at the call site in the previous frame

struct CFA_reg
{
int offset; // offset from CFA
Expand Down Expand Up @@ -214,6 +222,13 @@ static CFA_state CFA_state_init_64 = // initial CFA state as defined by CI
static CFA_state CFA_state_current; // current CFA state
static Outbuffer cfa_buf; // CFA instructions

/***********************************
* Set the location, i.e. the offset from the start
* of the function. It must always be greater than
* the current location.
* Params:
* location = offset from the start of the function
*/
void dwarf_CFA_set_loc(size_t location)
{
assert(location >= CFA_state_current.location);
Expand All @@ -235,6 +250,12 @@ void dwarf_CFA_set_loc(size_t location)
CFA_state_current.location = location;
}

/*******************************************
* Set the frame register, and its offset.
* Params:
* reg = machine register
* offset = offset from frame register
*/
void dwarf_CFA_set_reg_offset(int reg, int offset)
{
int dw_reg = dwarf_regno(reg);
Expand Down Expand Up @@ -272,6 +293,12 @@ void dwarf_CFA_set_reg_offset(int reg, int offset)
CFA_state_current.offset = offset;
}

/***********************************************
* Set reg to be at offset from frame register.
* Params:
* reg = machine register
* offset = offset from frame register
*/
void dwarf_CFA_offset(int reg, int offset)
{
int dw_reg = dwarf_regno(reg);
Expand All @@ -292,6 +319,11 @@ void dwarf_CFA_offset(int reg, int offset)
CFA_state_current.regstates[dw_reg].offset = offset;
}

/**************************************
* Set total size of arguments pushed on the stack.
* Params:
* sz = total size
*/
void dwarf_CFA_args_size(size_t sz)
{
cfa_buf.writeByte(DW_CFA_GNU_args_size);
Expand Down Expand Up @@ -466,7 +498,7 @@ static DebugLineHeader debugline;
unsigned typidx_tab[TYMAX];

#if MACHOBJ
const char* debug_frame = "__debug_frame";
const char* debug_frame_name = "__debug_frame";
const char* debug_str = "__debug_str";
const char* debug_ranges = "__debug_ranges";
const char* debug_loc = "__debug_loc";
Expand All @@ -475,8 +507,9 @@ const char* debug_abbrev = "__debug_abbrev";
const char* debug_info = "__debug_info";
const char* debug_pubnames = "__debug_pubnames";
const char* debug_aranges = "__debug_aranges";
const char* eh_frame_name = "__eh_frame";
#elif ELFOBJ
const char* debug_frame = ".debug_frame";
const char* debug_frame_name = ".debug_frame";
const char* debug_str = ".debug_str";
const char* debug_ranges = ".debug_ranges";
const char* debug_loc = ".debug_loc";
Expand All @@ -485,9 +518,15 @@ const char* debug_abbrev = ".debug_abbrev";
const char* debug_info = ".debug_info";
const char* debug_pubnames = ".debug_pubnames";
const char* debug_aranges = ".debug_aranges";
const char* eh_frame_name = ".eh_frame";
#endif

void dwarf_initfile(const char *filename)
/*****************************************
* Append .debug_frame header to buf.
* Params:
* buf = write raw data here
*/
void writeDebugFrameHeader(Outbuffer *buf)
{
#pragma pack(1)
struct DebugFrameHeader
Expand Down Expand Up @@ -528,12 +567,214 @@ void dwarf_initfile(const char *filename)
}
assert(debugFrameHeader.data_alignment_factor == 0x80 - OFFSET_FAC);

int seg = dwarf_getsegment(debug_frame, 1);
buf->writen(&debugFrameHeader,debugFrameHeader.length + 4);
}

/*****************************************
* Append .eh_frame header to buf.
* Almost identical to .debug_frame
* Params:
* buf = write raw data here
*/
void writeEhFrameHeader(Outbuffer *buf)
{
#pragma pack(1)
struct EhFrameHeader
{
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 =
{
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]
}
};
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
}
assert(ehFrameHeader.data_alignment_factor == 0x80 - OFFSET_FAC);

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->reserve(sizeof(EhFrameHeader) + pad);
buf->writen(&ehFrameHeader, sizeof(EhFrameHeader));
for (unsigned i = 0; i < pad; ++i)
buf->writeByten(DW_CFA_nop);
}

/*********************************************
* Generate function's Frame Description Entry into .debug_frame
* Params:
* dfseg = SegData[] index for .debug_frame
* sfunc = the function
*/
void writeDebugFrameFDE(IDXSEC dfseg, Symbol *sfunc)
{
if (I64)
{
#pragma pack(1)
struct DebugFrameFDE
{
unsigned length;
unsigned CIE_pointer;
unsigned long long initial_location;
unsigned long long address_range;
};
#pragma pack()
static DebugFrameFDE debugFrameFDE =
{ 20, // length
0, // CIE_pointer
0, // initial_location
0, // address_range
};

// Pad to 8 byte boundary
for (unsigned n = (-cfa_buf.size() & 7); n; n--)
cfa_buf.writeByte(DW_CFA_nop);

debugFrameFDE.length = 20 + cfa_buf.size();
debugFrameFDE.address_range = sfunc->Ssize;
// 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);
debug_frame_buf->writen(&debugFrameFDE,sizeof(debugFrameFDE));
debug_frame_buf->write(&cfa_buf);

#if ELFOBJ
// Absolute address for debug_frame, relative offset for eh_frame
dwarf_addrel(dfseg,debug_frame_buf_offset + 4,dfseg);
#endif
dwarf_addrel64(dfseg,debug_frame_buf_offset + 8,sfunc->Sseg,0);
}
else
{
#pragma pack(1)
struct DebugFrameFDE
{
unsigned length;
unsigned CIE_pointer;
unsigned initial_location;
unsigned address_range;
};
#pragma pack()
static DebugFrameFDE debugFrameFDE =
{ 12, // length
0, // CIE_pointer
0, // initial_location
0, // address_range
};

// Pad to 4 byte boundary
for (unsigned n = (-cfa_buf.size() & 3); n; n--)
cfa_buf.writeByte(DW_CFA_nop);

debugFrameFDE.length = 12 + cfa_buf.size();
debugFrameFDE.address_range = sfunc->Ssize;
// 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);
debug_frame_buf->writen(&debugFrameFDE,sizeof(debugFrameFDE));
debug_frame_buf->write(&cfa_buf);

#if ELFOBJ
// Absolute address for debug_frame, relative offset for eh_frame
dwarf_addrel(dfseg,debug_frame_buf_offset + 4,dfseg);
#endif
dwarf_addrel(dfseg,debug_frame_buf_offset + 8,sfunc->Sseg);
}
}

/*********************************************
* Generate function's Frame Description Entry into .eh_frame
* Params:
* dfseg = SegData[] index for .eh_frame
* sfunc = the function
*/
void writeEhFrameFDE(IDXSEC dfseg, Symbol *sfunc)
{
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);

EhFrameFDE ehFrameFDE;

// 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);

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;

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
}

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);

debug_frame_buf->writen(&debugFrameHeader,debugFrameHeader.length + 4);
if (genEhFrame)
writeEhFrameHeader(debug_frame_buf);
else
writeDebugFrameHeader(debug_frame_buf);

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

Expand Down Expand Up @@ -1010,96 +1251,17 @@ void dwarf_func_term(Symbol *sfunc)
return;
#endif

unsigned funcabbrevcode;

/* Put out the start of the debug_frame entry for this function
*/
Outbuffer *debug_frame_buf;
unsigned debug_frame_buf_offset;
unsigned funcabbrevcode;

if (I64)
if (genEhFrame)
{
#pragma pack(1)
struct DebugFrameFDE
{
unsigned length;
unsigned CIE_pointer;
unsigned long long initial_location;
unsigned long long address_range;
};
#pragma pack()
static DebugFrameFDE debugFrameFDE =
{ 20, // length
0, // CIE_pointer
0, // initial_location
0, // address_range
};

// Pad to 8 byte boundary
int n;
for (n = (-cfa_buf.size() & 7); n; n--)
cfa_buf.writeByte(DW_CFA_nop);

debugFrameFDE.length = 20 + cfa_buf.size();
debugFrameFDE.address_range = sfunc->Ssize;
// Do we need this?
//debugFrameFDE.initial_location = sfunc->Soffset;

IDXSEC dfseg;
dfseg = dwarf_getsegment(debug_frame, 1);
debug_frame_secidx = SegData[dfseg]->SDshtidx;
debug_frame_buf = SegData[dfseg]->SDbuf;
debug_frame_buf_offset = debug_frame_buf->p - debug_frame_buf->buf;
debug_frame_buf->reserve(1000);
debug_frame_buf->writen(&debugFrameFDE,sizeof(debugFrameFDE));
debug_frame_buf->write(&cfa_buf);

#if ELFOBJ
dwarf_addrel(dfseg,debug_frame_buf_offset + 4,dfseg);
#endif
dwarf_addrel64(dfseg,debug_frame_buf_offset + 8,sfunc->Sseg,0);
IDXSEC dfseg = dwarf_getsegment(eh_frame_name, 1);
writeEhFrameFDE(dfseg, sfunc);
}
else
{
#pragma pack(1)
struct DebugFrameFDE
{
unsigned length;
unsigned CIE_pointer;
unsigned initial_location;
unsigned address_range;
};
#pragma pack()
static DebugFrameFDE debugFrameFDE =
{ 12, // length
0, // CIE_pointer
0, // initial_location
0, // address_range
};

// Pad to 4 byte boundary
int n;
for (n = (-cfa_buf.size() & 3); n; n--)
cfa_buf.writeByte(DW_CFA_nop);

debugFrameFDE.length = 12 + cfa_buf.size();
debugFrameFDE.address_range = sfunc->Ssize;
// Do we need this?
//debugFrameFDE.initial_location = sfunc->Soffset;

IDXSEC dfseg;
dfseg = dwarf_getsegment(debug_frame, 1);
debug_frame_secidx = SegData[dfseg]->SDshtidx;
debug_frame_buf = SegData[dfseg]->SDbuf;
debug_frame_buf_offset = debug_frame_buf->p - debug_frame_buf->buf;
debug_frame_buf->reserve(1000);
debug_frame_buf->writen(&debugFrameFDE,sizeof(debugFrameFDE));
debug_frame_buf->write(&cfa_buf);

#if ELFOBJ
dwarf_addrel(dfseg,debug_frame_buf_offset + 4,dfseg);
#endif
dwarf_addrel(dfseg,debug_frame_buf_offset + 8,sfunc->Sseg);
IDXSEC dfseg = dwarf_getsegment(debug_frame_name, 1);
writeDebugFrameFDE(dfseg, sfunc);
}

IDXSEC seg = sfunc->Sseg;
Expand Down
24 changes: 24 additions & 0 deletions src/backend/dwarf2.h
Original file line number Diff line number Diff line change
Expand Up @@ -500,4 +500,28 @@ enum
DW_CFA_hi_user = 0x3f,
};

enum
{
DW_EH_PE_FORMAT_MASK = 0x0F,
DW_EH_PE_APPL_MASK = 0x70,
DW_EH_PE_indirect = 0x80,

DW_EH_PE_omit = 0xFF,
DW_EH_PE_ptr = 0x00,
DW_EH_PE_uleb128 = 0x01,
DW_EH_PE_udata2 = 0x02,
DW_EH_PE_udata4 = 0x03,
DW_EH_PE_udata8 = 0x04,
DW_EH_PE_sleb128 = 0x09,
DW_EH_PE_sdata2 = 0x0A,
DW_EH_PE_sdata4 = 0x0B,
DW_EH_PE_sdata8 = 0x0C,

DW_EH_PE_absptr = 0x00,
DW_EH_PE_pcrel = 0x10,
DW_EH_PE_textrel = 0x20,
DW_EH_PE_datarel = 0x30,
DW_EH_PE_funcrel = 0x40,
DW_EH_PE_aligned = 0x50,
};