6502/vasm/dwarf.c

489 lines
15 KiB
C

/* dwarf.c - DWARF debugging sections */
/* (c) in 2018 by Frank Wille */
#include "vasm.h"
#include "dwarf.h"
#include "osdep.h"
struct DWinclude {
struct DWinclude *next;
char *name;
};
struct DWfile {
struct DWfile *next;
char *name;
int incidx;
};
static const unsigned char stdopclengths[] = {
0,1,1,1,1,0,0,0,1
};
static unsigned char dw2_compile_unit_abbrev[] = {
DW_TAG_compile_unit,
0, /* no children */
DW_AT_producer,DW_FORM_string,
DW_AT_language,DW_FORM_data2,
DW_AT_name,DW_FORM_string,
DW_AT_comp_dir,DW_FORM_string,
DW_AT_stmt_list,DW_FORM_data4,
DW_AT_low_pc,DW_FORM_addr,
DW_AT_high_pc,DW_FORM_addr,
0,0
};
static unsigned char dw3_compile_unit_abbrev[] = {
DW_TAG_compile_unit,
0, /* no children */
DW_AT_producer,DW_FORM_string,
DW_AT_language,DW_FORM_data2,
DW_AT_name,DW_FORM_string,
DW_AT_comp_dir,DW_FORM_string,
DW_AT_stmt_list,DW_FORM_data4,
DW_AT_ranges,DW_FORM_data4,
0,0
};
static struct DWinclude *first_dwinc;
static struct DWfile *first_dwfil;
static struct DWinclude *new_dwinc(char *name)
{
struct DWinclude *new = mymalloc(sizeof(struct DWinclude));
new->next = NULL;
new->name = name;
return new;
}
/* DWARF needs source file lists with the file name part only, but without
any path in it. Include paths are put into another list. */
static void make_file_lists(struct source_file *first_source)
{
struct source_file *srcnode;
struct include_path *incnode;
struct DWfile *newfil,*dwfil;
struct DWinclude *newinc,*dwinc;
char pathbuf[MAXPATHLEN];
char *filepart;
int include_idx = 0;
int file_idx = 0;
int i;
first_dwfil = NULL;
first_dwinc = NULL;
for (srcnode=first_source; srcnode; srcnode=srcnode->next) {
newfil = mymalloc(sizeof(struct DWfile));
newfil->next = NULL;
pathbuf[0] = '\0';
if ((incnode = srcnode->incpath) != NULL) {
if (incnode->compdir_based) {
if (compile_dir != NULL)
strcpy(pathbuf,compile_dir);
else
ierror(0);
}
strcat(pathbuf,incnode->path);
}
if ((filepart = get_filepart(srcnode->name)) != srcnode->name) {
/* add path part from source file name */
size_t len = filepart - srcnode->name;
char *p = strrchr(pathbuf,'\0');
memcpy(p,srcnode->name,len);
*(p+len) = '\0';
}
if (pathbuf[0]) {
char *newpath = remove_path_delimiter(pathbuf);
/* check if this path already exists in the list, otherwise add it */
if (first_dwinc) {
for (i=1,dwinc=first_dwinc; ; i++,dwinc=dwinc->next) {
if (!filenamecmp(newpath,dwinc->name)) {
myfree(newpath);
newfil->incidx = i; /* use existing include index */
break;
}
if (dwinc->next == NULL) {
if (include_idx != i)
ierror(0);
dwinc->next = new_dwinc(newpath);
newfil->incidx = ++include_idx; /* new include index */
break;
}
}
}
else {
first_dwinc = new_dwinc(newpath);
newfil->incidx = ++include_idx; /* should be index 1 */
}
}
else
newfil->incidx = 0; /* no path, file is in current work directory */
/* append new file node */
if (++file_idx != srcnode->index)
ierror(0);
newfil->name = filepart;
if (dwfil = first_dwfil) {
while (dwfil->next != NULL)
dwfil = dwfil->next;
dwfil->next = newfil;
}
else
first_dwfil = newfil;
}
}
void dwarf_init(struct dwarf_info *dinfo,
struct include_path *first_incpath,
struct source_file *first_source)
{
atom *a,*dinfoatom,*dabbratom,*drangatom,*dlineatom;
void *lengthptr;
symbol *tmpsym;
section *dsec;
struct DWinclude *dwinc;
struct DWfile *dwfil;
int i;
if (dinfo->version < 2)
ierror(0);
/* init dwarf_info for DWARF2 and CPU architecture */
dinfo->code_sections = 0;
dinfo->addr_len = bytespertaddr;
dinfo->min_inst_len = INST_ALIGN;
dinfo->default_is_stmt = 1;
dinfo->line_base = -8;
dinfo->line_range = 16;
dinfo->opcode_base = sizeof(stdopclengths) + 1;
dinfo->max_pcadvance = (255 - dinfo->opcode_base) / dinfo->line_range;
dinfo->max_lnadvance_hipc = 255 - dinfo->max_pcadvance * dinfo->line_range -
dinfo->opcode_base;
dinfo->asec = dsec = new_section(".debug_aranges","r",1);
/* compilation unit header */
a = add_data_atom(dsec,4,1,0); /* total range-table size */
dinfo->range_length = a->content.db->data;
add_data_atom(dsec,2,1,dinfo->version);
dinfoatom = add_data_atom(dsec,4,1,0);/* 32-bit reloc to .debug_info */
add_data_atom(dsec,1,1,dinfo->addr_len);
add_data_atom(dsec,1,1,0); /* segment-descriptor size - unused */
dsec = new_section(".debug_info","r",1);
/* make 32-bit reloc for info-start label */
tmpsym = new_tmplabel(dsec);
add_extnreloc(&dinfoatom->content.db->relocs,tmpsym,tmpsym->pc,
REL_ABS,0,32,0);
/* compilation unit header */
a = add_data_atom(dsec,4,1,0); /* total compilation unit size */
lengthptr = a->content.db->data;
add_data_atom(dsec,2,1,dinfo->version);
dabbratom = add_data_atom(dsec,4,1,0);/* 32-bit reloc to .debug_abbrev */
add_data_atom(dsec,1,1,dinfo->addr_len);
/* first DIE, using abbrev. no. 1 */
add_leb128_atom(dsec,1);
/* DW_TAG_compile_unit */
add_bytes_atom(dsec,dinfo->producer,strlen(dinfo->producer));
add_data_atom(dsec,1,1,' ');
add_string_atom(dsec,cpuname); /* vasm version and cpu-name */
add_data_atom(dsec,2,1,DW_LANG_ASSEMBLER);
add_string_atom(dsec,getdebugname());
add_string_atom(dsec,get_workdir());
dlineatom = add_data_atom(dsec,4,1,0);/* 32-bit reloc to .debug_line */
if (dinfo->version < 3) {
/* DWARF2:
low_pc/high_pc does not work for multiple sections, so the workaround
is to set low_pc=0 and high_pc=~0 to cover the whole address space. */
dinfo->lowpc_atom = add_data_atom(dsec,dinfo->addr_len,1,0);
dinfo->highpc_atom = add_data_atom(dsec,dinfo->addr_len,1,~0);
}
else {
/* address ranges defined in .debug_ranges since DWARF3 */
drangatom = add_data_atom(dsec,4,1,0);/* 32-bit reloc to .debug_ranges */
dinfo->lowpc_atom = dinfo->highpc_atom = NULL;
}
add_leb128_atom(dsec,0); /* no more DIEs */
setval(BIGENDIAN,lengthptr,4,dsec->pc-4);
dsec = new_section(".debug_abbrev","r",1);
/* make 32-bit reloc for abbreviations-start label */
tmpsym = new_tmplabel(dsec);
add_extnreloc(&dabbratom->content.db->relocs,tmpsym,tmpsym->pc,
REL_ABS,0,32,0);
add_leb128_atom(dsec,1); /* abbrev. no. 1 */
if (dinfo->version < 3)
add_bytes_atom(dsec,dw2_compile_unit_abbrev,
sizeof(dw2_compile_unit_abbrev));
else
add_bytes_atom(dsec,dw3_compile_unit_abbrev,
sizeof(dw3_compile_unit_abbrev));
add_leb128_atom(dsec,0); /* end of abbreviations */
if (dinfo->version >= 3) {
dinfo->rsec = dsec = new_section(".debug_ranges","r",2*dinfo->addr_len);
/* make 32-bit reloc for ranges-start label */
tmpsym = new_tmplabel(dsec);
add_extnreloc(&drangatom->content.db->relocs,tmpsym,tmpsym->pc,
REL_ABS,0,32,0);
/* @@@ highest and lowest possible address in first entry? */
add_data_atom(dsec,dinfo->addr_len,dinfo->addr_len,-1);
add_data_atom(dsec,dinfo->addr_len,dinfo->addr_len,0);
}
else
dinfo->rsec = NULL;
dinfo->lsec = dsec = new_section(".debug_line","r",1);
/* make 32-bit reloc for linedebug-start label */
tmpsym = new_tmplabel(dsec);
add_extnreloc(&dlineatom->content.db->relocs,tmpsym,tmpsym->pc,
REL_ABS,0,32,0);
/* make .debug_line prologue */
a = add_data_atom(dsec,4,1,0); /* line debug size for comp.unit */
dinfo->line_length = a->content.db->data;
add_data_atom(dsec,2,1,dinfo->version);
a = add_data_atom(dsec,4,1,0); /* byte-offset to statement program */
lengthptr = a->content.db->data;
add_data_atom(dsec,1,1,dinfo->min_inst_len);
add_data_atom(dsec,1,1,dinfo->default_is_stmt);
add_data_atom(dsec,1,1,dinfo->line_base);
add_data_atom(dsec,1,1,dinfo->line_range);
add_data_atom(dsec,1,1,dinfo->opcode_base);
/* define standard opcode lengths */
for (i=0; i<sizeof(stdopclengths); i++)
add_data_atom(dsec,1,1,stdopclengths[i]);
/* make lists of DWARF directories and files */
make_file_lists(first_source);
/* list of include directories, CWD-entry (index 0) is not written */
for (dwinc=first_dwinc; dwinc; dwinc=dwinc->next)
add_string_atom(dsec,dwinc->name);
add_data_atom(dsec,1,1,0); /* list is terminated by a 0-byte */
/* list of file names, with directory-index, last-modif.-time and size */
for (dwfil=first_dwfil; dwfil; dwfil=dwfil->next) {
add_string_atom(dsec,dwfil->name);
add_leb128_atom(dsec,dwfil->incidx);
add_leb128_atom(dsec,0); /* time */
add_leb128_atom(dsec,0); /* size */
}
add_data_atom(dsec,1,1,0); /* list is terminated by a 0-byte */
/* start of statement program */
setval(BIGENDIAN,lengthptr,4,dsec->pc-10);
dinfo->end_sequence = 1; /* we have to start a new sequence */
}
void dwarf_finish(struct dwarf_info *dinfo)
{
/* close .debug_aranges table with two NULL-entries and set its size */
add_data_atom(dinfo->asec,dinfo->addr_len,dinfo->addr_len,0);
add_data_atom(dinfo->asec,dinfo->addr_len,dinfo->addr_len,0);
setval(BIGENDIAN,dinfo->range_length,4,dinfo->asec->pc-4);
if (dinfo->rsec) {
/* close .debug_ranges table with two NULL-entries */
add_data_atom(dinfo->rsec,dinfo->addr_len,dinfo->addr_len,0);
add_data_atom(dinfo->rsec,dinfo->addr_len,dinfo->addr_len,0);
}
/* set .debug_line compilation unit size */
setval(BIGENDIAN,dinfo->line_length,4,dinfo->lsec->pc-4);
}
static void dwarf_set_address(struct dwarf_info *dinfo,symbol *sym)
{
static unsigned char opcode[3] = { 0,0,DW_LNE_set_address };
atom *a;
/* extended opcode to set address for current cpu including relocation */
opcode[1] = dinfo->addr_len + 1;
add_bytes_atom(dinfo->lsec,opcode,3);
a = add_data_atom(dinfo->lsec,dinfo->addr_len,1,sym->pc);
add_extnreloc(&a->content.db->relocs,sym,sym->pc,REL_ABS,
0,dinfo->addr_len<<3,0);
}
static void set_atom_label(atom *a,size_t addrlen,symbol *sym)
{
setval(BIGENDIAN,a->content.db->data,addrlen,sym->pc);
add_extnreloc(&a->content.db->relocs,sym,sym->pc,REL_ABS,0,addrlen<<3,0);
}
static void set_atom_val(atom *a,size_t len,taddr val)
{
setval(BIGENDIAN,a->content.db->data,len,val);
a->content.db->relocs = NULL; /* clear relocs */
}
void dwarf_end_sequence(struct dwarf_info *dinfo,section *sec)
{
if (!dinfo->end_sequence) {
static unsigned char opcode[3] = { 0,1,DW_LNE_end_sequence };
symbol *sym = new_tmplabel(sec); /* label at end of section */
atom *a;
dwarf_set_address(dinfo,sym);
add_bytes_atom(dinfo->lsec,opcode,3);
dinfo->end_sequence = 1;
/* enter section size for this sequence into the address-range table */
add_data_atom(dinfo->asec,dinfo->addr_len,dinfo->addr_len,sec->pc);
if (dinfo->highpc_atom) {
if (dinfo->code_sections > 1)
/* low_pc=0, high_pc=~0 for multiple sections as workaround */
set_atom_val(dinfo->highpc_atom,dinfo->addr_len,~0);
else
/* we can set high_pc, when there is just a single code section */
set_atom_label(dinfo->highpc_atom,dinfo->addr_len,sym);
}
if (dinfo->rsec) {
/* enter end-of-section label reference into the ranges table */
a = add_data_atom(dinfo->rsec,dinfo->addr_len,dinfo->addr_len,sym->pc);
add_extnreloc(&a->content.db->relocs,sym,sym->pc,REL_ABS,
0,dinfo->addr_len<<3,0);
}
}
}
void dwarf_line(struct dwarf_info *dinfo,section *sec,int file,int line)
{
if (file==0 || line==0)
ierror(0);
if (dinfo->end_sequence) {
symbol *sym;
atom *a;
/* start a new sequence, reset state machine */
dinfo->code_sections++;
dinfo->file = 1;
dinfo->line = 1;
dinfo->column = 0;
dinfo->is_stmt = dinfo->default_is_stmt;
dinfo->basic_block = 0;
dinfo->end_sequence = 0;
/* set label at start of the instruction's section */
sym = new_tmplabel(sec);
sym->pc = 0;
/* make new entry into the address-range table: .debug_aranges */
a = new_space_atom(number_expr(0),1,NULL);
a->align = 2 * dinfo->addr_len;
add_atom(dinfo->asec,a); /* align to double address-length */
a = add_data_atom(dinfo->asec,dinfo->addr_len,dinfo->addr_len,0);
add_extnreloc(&a->content.db->relocs,sym,sym->pc,REL_ABS,
0,dinfo->addr_len<<3,0);
if (dinfo->lowpc_atom) {
if (dinfo->code_sections > 1)
/* low_pc=0, high_pc=~0 for multiple sections as workaround */
set_atom_val(dinfo->lowpc_atom,dinfo->addr_len,0);
else
/* we can set low_pc, when there is just a single code section */
set_atom_label(dinfo->lowpc_atom,dinfo->addr_len,sym);
}
if (dinfo->rsec) {
/* make new entry into the ranges table: .debug_ranges */
a = add_data_atom(dinfo->rsec,dinfo->addr_len,dinfo->addr_len,0);
add_extnreloc(&a->content.db->relocs,sym,sym->pc,REL_ABS,
0,dinfo->addr_len<<3,0);
}
/* set relocatable address of first instruction, then advance line, etc.*/
dinfo->address = sec->pc;
dwarf_set_address(dinfo,new_tmplabel(sec));
if (file != dinfo->file) {
add_data_atom(dinfo->lsec,1,1,DW_LNS_set_file);
add_leb128_atom(dinfo->lsec,file);
dinfo->file = file;
}
if (line != dinfo->line) {
add_data_atom(dinfo->lsec,1,1,DW_LNS_advance_line);
add_leb128_atom(dinfo->lsec,line-dinfo->line);
dinfo->line = line;
}
add_data_atom(dinfo->lsec,1,1,DW_LNS_copy);
}
else {
int lineoffs = line - dinfo->line;
int instoffs = (sec->pc - dinfo->address) / dinfo->min_inst_len;
if (file != dinfo->file) {
add_data_atom(dinfo->lsec,1,1,DW_LNS_set_file);
add_leb128_atom(dinfo->lsec,file);
dinfo->file = file;
}
if (instoffs > dinfo->max_pcadvance) {
if (instoffs - dinfo->max_pcadvance <= dinfo->max_pcadvance) {
/* const_add_pc for up to twice the maximum special opcode advance */
add_data_atom(dinfo->lsec,1,1,DW_LNS_const_add_pc);
instoffs -= dinfo->max_pcadvance;
}
else {
/* advance address by standard opcode */
add_data_atom(dinfo->lsec,1,1,DW_LNS_advance_pc);
add_leb128_atom(dinfo->lsec,instoffs);
instoffs = 0;
}
}
if (lineoffs < dinfo->line_base ||
lineoffs >= dinfo->line_base + dinfo->line_range ||
(instoffs == dinfo->max_pcadvance &&
lineoffs > dinfo->line_base + dinfo->max_lnadvance_hipc)) {
/* we have to advance line by standard opcode */
add_data_atom(dinfo->lsec,1,1,DW_LNS_advance_line);
add_sleb128_atom(dinfo->lsec,lineoffs);
lineoffs = 0;
}
/* construct special opcode for simultaneous inst./pc-advancement */
add_data_atom(dinfo->lsec,1,1,
dinfo->opcode_base +
instoffs*dinfo->line_range +
(lineoffs-dinfo->line_base));
/* update line/address */
dinfo->address = sec->pc;
dinfo->line = line;
}
}