diff options
Diffstat (limited to 'src/cmd/ld/dwarf.c')
-rw-r--r-- | src/cmd/ld/dwarf.c | 2598 |
1 files changed, 0 insertions, 2598 deletions
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c deleted file mode 100644 index 1c10dc796..000000000 --- a/src/cmd/ld/dwarf.c +++ /dev/null @@ -1,2598 +0,0 @@ -// Copyright 2010 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// TODO/NICETOHAVE: -// - eliminate DW_CLS_ if not used -// - package info in compilation units -// - assign global variables and types to their packages -// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg -// ptype struct '[]uint8' and qualifiers need to be quoted away -// - lexical scoping is lost, so gdb gets confused as to which 'main.i' you mean. -// - file:line info for variables -// - make strings a typedef so prettyprinters can see the underlying string type -// -#include "l.h" -#include "lib.h" -#include "../ld/dwarf.h" -#include "../ld/dwarf_defs.h" -#include "../ld/elf.h" -#include "../ld/macho.h" -#include "../ld/pe.h" - -/* - * Offsets and sizes of the debug_* sections in the cout file. - */ - -static vlong abbrevo; -static vlong abbrevsize; -static vlong lineo; -static vlong linesize; -static vlong infoo; // also the base for DWDie->offs and reference attributes. -static vlong infosize; -static vlong frameo; -static vlong framesize; -static vlong pubnameso; -static vlong pubnamessize; -static vlong pubtypeso; -static vlong pubtypessize; -static vlong arangeso; -static vlong arangessize; -static vlong gdbscripto; -static vlong gdbscriptsize; - -static char gdbscript[1024]; - -/* - * Basic I/O - */ - -static void -addrput(vlong addr) -{ - switch(PtrSize) { - case 4: - LPUT(addr); - break; - case 8: - VPUT(addr); - break; - } -} - -static int -uleb128enc(uvlong v, char* dst) -{ - uint8 c, len; - - len = 0; - do { - c = v & 0x7f; - v >>= 7; - if (v) - c |= 0x80; - if (dst) - *dst++ = c; - len++; - } while (c & 0x80); - return len; -}; - -static int -sleb128enc(vlong v, char *dst) -{ - uint8 c, s, len; - - len = 0; - do { - c = v & 0x7f; - s = v & 0x40; - v >>= 7; - if ((v != -1 || !s) && (v != 0 || s)) - c |= 0x80; - if (dst) - *dst++ = c; - len++; - } while(c & 0x80); - return len; -} - -static void -uleb128put(vlong v) -{ - char buf[10]; - strnput(buf, uleb128enc(v, buf)); -} - -static void -sleb128put(vlong v) -{ - char buf[10]; - strnput(buf, sleb128enc(v, buf)); -} - -/* - * Defining Abbrevs. This is hardcoded, and there will be - * only a handful of them. The DWARF spec places no restriction on - * the ordering of atributes in the Abbrevs and DIEs, and we will - * always write them out in the order of declaration in the abbrev. - * This implementation relies on tag, attr < 127, so they serialize as - * a char. Higher numbered user-defined tags or attributes can be used - * for storing internal data but won't be serialized. - */ -typedef struct DWAttrForm DWAttrForm; -struct DWAttrForm { - uint8 attr; - uint8 form; -}; - -// Index into the abbrevs table below. -// Keep in sync with ispubname() and ispubtype() below. -// ispubtype considers >= NULLTYPE public -enum -{ - DW_ABRV_NULL, - DW_ABRV_COMPUNIT, - DW_ABRV_FUNCTION, - DW_ABRV_VARIABLE, - DW_ABRV_AUTO, - DW_ABRV_PARAM, - DW_ABRV_STRUCTFIELD, - DW_ABRV_FUNCTYPEPARAM, - DW_ABRV_DOTDOTDOT, - DW_ABRV_ARRAYRANGE, - DW_ABRV_NULLTYPE, - DW_ABRV_BASETYPE, - DW_ABRV_ARRAYTYPE, - DW_ABRV_CHANTYPE, - DW_ABRV_FUNCTYPE, - DW_ABRV_IFACETYPE, - DW_ABRV_MAPTYPE, - DW_ABRV_PTRTYPE, - DW_ABRV_SLICETYPE, - DW_ABRV_STRINGTYPE, - DW_ABRV_STRUCTTYPE, - DW_ABRV_TYPEDECL, - DW_NABRV -}; - -typedef struct DWAbbrev DWAbbrev; -static struct DWAbbrev { - uint8 tag; - uint8 children; - DWAttrForm attr[30]; -} abbrevs[DW_NABRV] = { - /* The mandatory DW_ABRV_NULL entry. */ - { 0 }, - /* COMPUNIT */ - { - DW_TAG_compile_unit, DW_CHILDREN_yes, - DW_AT_name, DW_FORM_string, - DW_AT_language, DW_FORM_data1, - DW_AT_low_pc, DW_FORM_addr, - DW_AT_high_pc, DW_FORM_addr, - DW_AT_stmt_list, DW_FORM_data4, - 0, 0 - }, - /* FUNCTION */ - { - DW_TAG_subprogram, DW_CHILDREN_yes, - DW_AT_name, DW_FORM_string, - DW_AT_low_pc, DW_FORM_addr, - DW_AT_high_pc, DW_FORM_addr, - DW_AT_external, DW_FORM_flag, - 0, 0 - }, - /* VARIABLE */ - { - DW_TAG_variable, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_location, DW_FORM_block1, - DW_AT_type, DW_FORM_ref_addr, - DW_AT_external, DW_FORM_flag, - 0, 0 - }, - /* AUTO */ - { - DW_TAG_variable, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_location, DW_FORM_block1, - DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, - /* PARAM */ - { - DW_TAG_formal_parameter, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_location, DW_FORM_block1, - DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, - /* STRUCTFIELD */ - { - DW_TAG_member, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_data_member_location, DW_FORM_block1, - DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, - /* FUNCTYPEPARAM */ - { - DW_TAG_formal_parameter, DW_CHILDREN_no, - // No name! - DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, - - /* DOTDOTDOT */ - { - DW_TAG_unspecified_parameters, DW_CHILDREN_no, - 0, 0 - }, - /* ARRAYRANGE */ - { - DW_TAG_subrange_type, DW_CHILDREN_no, - // No name! - DW_AT_type, DW_FORM_ref_addr, - DW_AT_upper_bound, DW_FORM_data1, - 0, 0 - }, - - // Below here are the types considered public by ispubtype - /* NULLTYPE */ - { - DW_TAG_unspecified_type, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - 0, 0 - }, - /* BASETYPE */ - { - DW_TAG_base_type, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_encoding, DW_FORM_data1, - DW_AT_byte_size, DW_FORM_data1, - 0, 0 - }, - /* ARRAYTYPE */ - // child is subrange with upper bound - { - DW_TAG_array_type, DW_CHILDREN_yes, - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref_addr, - DW_AT_byte_size, DW_FORM_udata, - 0, 0 - }, - - /* CHANTYPE */ - { - DW_TAG_typedef, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, - - /* FUNCTYPE */ - { - DW_TAG_subroutine_type, DW_CHILDREN_yes, - DW_AT_name, DW_FORM_string, -// DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, - - /* IFACETYPE */ - { - DW_TAG_typedef, DW_CHILDREN_yes, - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, - - /* MAPTYPE */ - { - DW_TAG_typedef, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, - - /* PTRTYPE */ - { - DW_TAG_pointer_type, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, - - /* SLICETYPE */ - { - DW_TAG_structure_type, DW_CHILDREN_yes, - DW_AT_name, DW_FORM_string, - DW_AT_byte_size, DW_FORM_udata, - 0, 0 - }, - - /* STRINGTYPE */ - { - DW_TAG_structure_type, DW_CHILDREN_yes, - DW_AT_name, DW_FORM_string, - DW_AT_byte_size, DW_FORM_udata, - 0, 0 - }, - - /* STRUCTTYPE */ - { - DW_TAG_structure_type, DW_CHILDREN_yes, - DW_AT_name, DW_FORM_string, - DW_AT_byte_size, DW_FORM_udata, - 0, 0 - }, - - /* TYPEDECL */ - { - DW_TAG_typedef, DW_CHILDREN_no, - DW_AT_name, DW_FORM_string, - DW_AT_type, DW_FORM_ref_addr, - 0, 0 - }, -}; - -static void -writeabbrev(void) -{ - int i, n; - - abbrevo = cpos(); - for (i = 1; i < DW_NABRV; i++) { - // See section 7.5.3 - uleb128put(i); - uleb128put(abbrevs[i].tag); - cput(abbrevs[i].children); - // 0 is not a valid attr or form, and DWAbbrev.attr is - // 0-terminated, so we can treat it as a string - n = strlen((char*)abbrevs[i].attr) / 2; - strnput((char*)abbrevs[i].attr, - (n+1) * sizeof(DWAttrForm)); - } - cput(0); - abbrevsize = cpos() - abbrevo; -} - -/* - * Debugging Information Entries and their attributes. - */ - -enum -{ - HASHSIZE = 107 -}; - -static uint32 -hashstr(char* s) -{ - uint32 h; - - h = 0; - while (*s) - h = h+h+h + *s++; - return h % HASHSIZE; -} - -// For DW_CLS_string and _block, value should contain the length, and -// data the data, for _reference, value is 0 and data is a DWDie* to -// the referenced instance, for all others, value is the whole thing -// and data is null. - -typedef struct DWAttr DWAttr; -struct DWAttr { - DWAttr *link; - uint8 atr; // DW_AT_ - uint8 cls; // DW_CLS_ - vlong value; - char *data; -}; - -typedef struct DWDie DWDie; -struct DWDie { - int abbrev; - DWDie *link; - DWDie *child; - DWAttr *attr; - // offset into .debug_info section, i.e relative to - // infoo. only valid after call to putdie() - vlong offs; - DWDie **hash; // optional index of children by name, enabled by mkindex() - DWDie *hlink; // bucket chain in parent's index -}; - -/* - * Root DIEs for compilation units, types and global variables. - */ - -static DWDie dwroot; -static DWDie dwtypes; -static DWDie dwglobals; - -static DWAttr* -newattr(DWDie *die, uint8 attr, int cls, vlong value, char *data) -{ - DWAttr *a; - - a = mal(sizeof *a); - a->link = die->attr; - die->attr = a; - a->atr = attr; - a->cls = cls; - a->value = value; - a->data = data; - return a; -} - -// Each DIE (except the root ones) has at least 1 attribute: its -// name. getattr moves the desired one to the front so -// frequently searched ones are found faster. -static DWAttr* -getattr(DWDie *die, uint8 attr) -{ - DWAttr *a, *b; - - if (die->attr->atr == attr) - return die->attr; - - a = die->attr; - b = a->link; - while (b != nil) { - if (b->atr == attr) { - a->link = b->link; - b->link = die->attr; - die->attr = b; - return b; - } - a = b; - b = b->link; - } - return nil; -} - -// Every DIE has at least a DW_AT_name attribute (but it will only be -// written out if it is listed in the abbrev). If its parent is -// keeping an index, the new DIE will be inserted there. -static DWDie* -newdie(DWDie *parent, int abbrev, char *name) -{ - DWDie *die; - int h; - - die = mal(sizeof *die); - die->abbrev = abbrev; - die->link = parent->child; - parent->child = die; - - newattr(die, DW_AT_name, DW_CLS_STRING, strlen(name), name); - - if (parent->hash) { - h = hashstr(name); - die->hlink = parent->hash[h]; - parent->hash[h] = die; - } - - return die; -} - -static void -mkindex(DWDie *die) -{ - die->hash = mal(HASHSIZE * sizeof(DWDie*)); -} - -// Find child by AT_name using hashtable if available or linear scan -// if not. -static DWDie* -find(DWDie *die, char* name) -{ - DWDie *a, *b; - int h; - - if (die->hash == nil) { - for (a = die->child; a != nil; a = a->link) - if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) - return a; - return nil; - } - - h = hashstr(name); - a = die->hash[h]; - - if (a == nil) - return nil; - - - if (strcmp(name, getattr(a, DW_AT_name)->data) == 0) - return a; - - // Move found ones to head of the list. - b = a->hlink; - while (b != nil) { - if (strcmp(name, getattr(b, DW_AT_name)->data) == 0) { - a->hlink = b->hlink; - b->hlink = die->hash[h]; - die->hash[h] = b; - return b; - } - a = b; - b = b->hlink; - } - return nil; -} - -static DWDie* -find_or_diag(DWDie *die, char* name) -{ - DWDie *r; - r = find(die, name); - if (r == nil) { - diag("dwarf find: %s has no %s", getattr(die, DW_AT_name)->data, name); - errorexit(); - } - return r; -} - -static DWAttr* -newrefattr(DWDie *die, uint8 attr, DWDie* ref) -{ - if (ref == nil) - return nil; - return newattr(die, attr, DW_CLS_REFERENCE, 0, (char*)ref); -} - -static int fwdcount; - -static void -putattr(int form, int cls, vlong value, char *data) -{ - switch(form) { - case DW_FORM_addr: // address - addrput(value); - break; - - case DW_FORM_block1: // block - value &= 0xff; - cput(value); - while(value--) - cput(*data++); - break; - - case DW_FORM_block2: // block - value &= 0xffff; - WPUT(value); - while(value--) - cput(*data++); - break; - - case DW_FORM_block4: // block - value &= 0xffffffff; - LPUT(value); - while(value--) - cput(*data++); - break; - - case DW_FORM_block: // block - uleb128put(value); - while(value--) - cput(*data++); - break; - - case DW_FORM_data1: // constant - cput(value); - break; - - case DW_FORM_data2: // constant - WPUT(value); - break; - - case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr - LPUT(value); - break; - - case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr - VPUT(value); - break; - - case DW_FORM_sdata: // constant - sleb128put(value); - break; - - case DW_FORM_udata: // constant - uleb128put(value); - break; - - case DW_FORM_string: // string - strnput(data, value+1); - break; - - case DW_FORM_flag: // flag - cput(value?1:0); - break; - - case DW_FORM_ref_addr: // reference to a DIE in the .info section - if (data == nil) { - diag("dwarf: null reference"); - LPUT(0); // invalid dwarf, gdb will complain. - } else { - if (((DWDie*)data)->offs == 0) - fwdcount++; - LPUT(((DWDie*)data)->offs); - } - break; - - case DW_FORM_ref1: // reference within the compilation unit - case DW_FORM_ref2: // reference - case DW_FORM_ref4: // reference - case DW_FORM_ref8: // reference - case DW_FORM_ref_udata: // reference - - case DW_FORM_strp: // string - case DW_FORM_indirect: // (see Section 7.5.3) - default: - diag("dwarf: unsupported attribute form %d / class %d", form, cls); - errorexit(); - } -} - -// Note that we can (and do) add arbitrary attributes to a DIE, but -// only the ones actually listed in the Abbrev will be written out. -static void -putattrs(int abbrev, DWAttr* attr) -{ - DWAttr *attrs[DW_AT_recursive + 1]; - DWAttrForm* af; - - memset(attrs, 0, sizeof attrs); - for( ; attr; attr = attr->link) - if (attr->atr < nelem(attrs)) - attrs[attr->atr] = attr; - - for(af = abbrevs[abbrev].attr; af->attr; af++) - if (attrs[af->attr]) - putattr(af->form, - attrs[af->attr]->cls, - attrs[af->attr]->value, - attrs[af->attr]->data); - else - putattr(af->form, 0, 0, 0); -} - -static void putdie(DWDie* die); - -static void -putdies(DWDie* die) -{ - for(; die; die = die->link) - putdie(die); -} - -static void -putdie(DWDie* die) -{ - die->offs = cpos() - infoo; - uleb128put(die->abbrev); - putattrs(die->abbrev, die->attr); - if (abbrevs[die->abbrev].children) { - putdies(die->child); - cput(0); - } -} - -static void -reverselist(DWDie** list) -{ - DWDie *curr, *prev; - - curr = *list; - prev = nil; - while(curr != nil) { - DWDie* next = curr->link; - curr->link = prev; - prev = curr; - curr = next; - } - *list = prev; -} - -static void -reversetree(DWDie** list) -{ - DWDie *die; - - reverselist(list); - for (die = *list; die != nil; die = die->link) - if (abbrevs[die->abbrev].children) - reversetree(&die->child); -} - -static void -newmemberoffsetattr(DWDie *die, int32 offs) -{ - char block[10]; - int i; - - i = 0; - if (offs != 0) { - block[i++] = DW_OP_consts; - i += sleb128enc(offs, block+i); - block[i++] = DW_OP_plus; - } - newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, i, mal(i)); - memmove(die->attr->data, block, i); -} - -// GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a -// location expression that evals to a const. -static void -newabslocexprattr(DWDie *die, vlong addr) -{ - char block[10]; - int i; - - i = 0; - block[i++] = DW_OP_constu; - i += uleb128enc(addr, block+i); - newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i)); - memmove(die->attr->data, block, i); -} - -// Decoding the type.* symbols. This has to be in sync with -// ../../pkg/runtime/type.go, or more specificaly, with what -// ../gc/reflect.c stuffs in these. - -enum { - KindBool = 1, - KindInt, - KindInt8, - KindInt16, - KindInt32, - KindInt64, - KindUint, - KindUint8, - KindUint16, - KindUint32, - KindUint64, - KindUintptr, - KindFloat32, - KindFloat64, - KindComplex64, - KindComplex128, - KindArray, - KindChan, - KindFunc, - KindInterface, - KindMap, - KindPtr, - KindSlice, - KindString, - KindStruct, - KindUnsafePointer, - - KindNoPointers = 1<<7, - - // size of Type interface header + CommonType structure. - CommonSize = 2*PtrSize+ 4*PtrSize + 8, -}; - -static Reloc* -decode_reloc(Sym *s, int32 off) -{ - int i; - - for (i = 0; i < s->nr; i++) - if (s->r[i].off == off) - return s->r + i; - return nil; -} - -static Sym* -decode_reloc_sym(Sym *s, int32 off) -{ - Reloc *r; - - r = decode_reloc(s,off); - if (r == nil) - return nil; - return r->sym; -} - -static uvlong -decode_inuxi(uchar* p, int sz) -{ - uint64 v; - uint32 l; - uchar *cast, *inuxi; - int i; - - v = l = 0; - cast = nil; - inuxi = nil; - switch (sz) { - case 2: - cast = (uchar*)&l; - inuxi = inuxi2; - break; - case 4: - cast = (uchar*)&l; - inuxi = inuxi4; - break; - case 8: - cast = (uchar*)&v; - inuxi = inuxi8; - break; - default: - diag("dwarf: decode inuxi %d", sz); - errorexit(); - } - for (i = 0; i < sz; i++) - cast[inuxi[i]] = p[i]; - if (sz == 8) - return v; - return l; -} - -// Type.commonType.kind -static uint8 -decodetype_kind(Sym *s) -{ - return s->p[3*PtrSize + 7] & ~KindNoPointers; // 0x13 / 0x1f -} - -// Type.commonType.size -static vlong -decodetype_size(Sym *s) -{ - return decode_inuxi(s->p + 2*PtrSize, PtrSize); // 0x8 / 0x10 -} - -// Type.ArrayType.elem and Type.SliceType.Elem -static Sym* -decodetype_arrayelem(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} - -static vlong -decodetype_arraylen(Sym *s) -{ - return decode_inuxi(s->p + CommonSize+PtrSize, PtrSize); -} - -// Type.PtrType.elem -static Sym* -decodetype_ptrelem(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} - -// Type.MapType.key, elem -static Sym* -decodetype_mapkey(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} -static Sym* -decodetype_mapvalue(Sym *s) -{ - return decode_reloc_sym(s, CommonSize+PtrSize); // 0x20 / 0x38 -} - -// Type.ChanType.elem -static Sym* -decodetype_chanelem(Sym *s) -{ - return decode_reloc_sym(s, CommonSize); // 0x1c / 0x30 -} - -// Type.FuncType.dotdotdot -static int -decodetype_funcdotdotdot(Sym *s) -{ - return s->p[CommonSize]; -} - -// Type.FuncType.in.len -static int -decodetype_funcincount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize+2*PtrSize, 4); -} - -static int -decodetype_funcoutcount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize+3*PtrSize + 2*4, 4); -} - -static Sym* -decodetype_funcintype(Sym *s, int i) -{ - Reloc *r; - - r = decode_reloc(s, CommonSize + PtrSize); - if (r == nil) - return nil; - return decode_reloc_sym(r->sym, r->add + i * PtrSize); -} - -static Sym* -decodetype_funcouttype(Sym *s, int i) -{ - Reloc *r; - - r = decode_reloc(s, CommonSize + 2*PtrSize + 2*4); - if (r == nil) - return nil; - return decode_reloc_sym(r->sym, r->add + i * PtrSize); -} - -// Type.StructType.fields.Slice::len -static int -decodetype_structfieldcount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize + PtrSize, 4); -} - -enum { - StructFieldSize = 5*PtrSize -}; -// Type.StructType.fields[]-> name, typ and offset. -static char* -decodetype_structfieldname(Sym *s, int i) -{ - Reloc *r; - - // go.string."foo" 0x28 / 0x40 - s = decode_reloc_sym(s, CommonSize + PtrSize + 2*4 + i*StructFieldSize); - if (s == nil) // embedded structs have a nil name. - return nil; - r = decode_reloc(s, 0); // s has a pointer to the string data at offset 0 - if (r == nil) // shouldn't happen. - return nil; - return (char*) r->sym->p + r->add; // the c-string -} - -static Sym* -decodetype_structfieldtype(Sym *s, int i) -{ - return decode_reloc_sym(s, CommonSize + PtrSize + 2*4 + i*StructFieldSize + 2*PtrSize); -} - -static vlong -decodetype_structfieldoffs(Sym *s, int i) -{ - return decode_inuxi(s->p + CommonSize + PtrSize + 2*4 + i*StructFieldSize + 4*PtrSize, 4); -} - -// InterfaceTYpe.methods.len -static vlong -decodetype_ifacemethodcount(Sym *s) -{ - return decode_inuxi(s->p + CommonSize + PtrSize, 4); -} - - -// Fake attributes for slices, maps and channel -enum { - DW_AT_internal_elem_type = 250, // channels and slices - DW_AT_internal_key_type = 251, // maps - DW_AT_internal_val_type = 252, // maps - DW_AT_internal_location = 253, // params and locals -}; - -static DWDie* defptrto(DWDie *dwtype); // below - -// Lookup predefined types -static Sym* -lookup_or_diag(char *n) -{ - Sym *s; - - s = rlookup(n, 0); - if (s == nil || s->size == 0) { - diag("dwarf: missing type: %s", n); - errorexit(); - } - return s; -} - -// Define gotype, for composite ones recurse into constituents. -static DWDie* -defgotype(Sym *gotype) -{ - DWDie *die, *fld; - Sym *s; - char *name, *f; - uint8 kind; - vlong bytesize; - int i, nfields; - - if (gotype == nil) - return find_or_diag(&dwtypes, "<unspecified>"); - - if (strncmp("type.", gotype->name, 5) != 0) { - diag("dwarf: type name doesn't start with \".type\": %s", gotype->name); - return find_or_diag(&dwtypes, "<unspecified>"); - } - name = gotype->name + 5; // could also decode from Type.string - - die = find(&dwtypes, name); - if (die != nil) - return die; - - if (0 && debug['v'] > 2) - print("new type: %Y\n", gotype); - - kind = decodetype_kind(gotype); - bytesize = decodetype_size(gotype); - - switch (kind) { - case KindBool: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_boolean, 0); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - break; - - case KindInt: - case KindInt8: - case KindInt16: - case KindInt32: - case KindInt64: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - break; - - case KindUint: - case KindUint8: - case KindUint16: - case KindUint32: - case KindUint64: - case KindUintptr: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - break; - - case KindFloat32: - case KindFloat64: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_float, 0); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - break; - - case KindComplex64: - case KindComplex128: - die = newdie(&dwtypes, DW_ABRV_BASETYPE, name); - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_complex_float, 0); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - break; - - case KindArray: - die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - s = decodetype_arrayelem(gotype); - newrefattr(die, DW_AT_type, defgotype(s)); - fld = newdie(die, DW_ABRV_ARRAYRANGE, "range"); - newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0); - newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr")); - break; - - case KindChan: - die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - s = decodetype_chanelem(gotype); - newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); - break; - - case KindFunc: - die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name); - newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "void")); - nfields = decodetype_funcincount(gotype); - for (i = 0; i < nfields; i++) { - s = decodetype_funcintype(gotype, i); - fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); - newrefattr(fld, DW_AT_type, defgotype(s)); - } - if (decodetype_funcdotdotdot(gotype)) - newdie(die, DW_ABRV_DOTDOTDOT, "..."); - nfields = decodetype_funcoutcount(gotype); - for (i = 0; i < nfields; i++) { - s = decodetype_funcouttype(gotype, i); - fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5); - newrefattr(fld, DW_AT_type, defptrto(defgotype(s))); - } - break; - - case KindInterface: - die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - nfields = decodetype_ifacemethodcount(gotype); - if (nfields == 0) - s = lookup_or_diag("type.runtime.eface"); - else - s = lookup_or_diag("type.runtime.iface"); - newrefattr(die, DW_AT_type, defgotype(s)); - break; - - case KindMap: - die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name); - s = decodetype_mapkey(gotype); - newrefattr(die, DW_AT_internal_key_type, defgotype(s)); - s = decodetype_mapvalue(gotype); - newrefattr(die, DW_AT_internal_val_type, defgotype(s)); - break; - - case KindPtr: - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); - s = decodetype_ptrelem(gotype); - newrefattr(die, DW_AT_type, defgotype(s)); - break; - - case KindSlice: - die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - s = decodetype_arrayelem(gotype); - newrefattr(die, DW_AT_internal_elem_type, defgotype(s)); - break; - - case KindString: - die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - break; - - case KindStruct: - die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); - nfields = decodetype_structfieldcount(gotype); - for (i = 0; i < nfields; i++) { - f = decodetype_structfieldname(gotype, i); - s = decodetype_structfieldtype(gotype, i); - if (f == nil) - f = s->name + 5; // skip "type." - fld = newdie(die, DW_ABRV_STRUCTFIELD, f); - newrefattr(fld, DW_AT_type, defgotype(s)); - newmemberoffsetattr(fld, decodetype_structfieldoffs(gotype, i)); - } - break; - - case KindUnsafePointer: - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name); - newrefattr(die, DW_AT_type, find(&dwtypes, "void")); - break; - - default: - diag("dwarf: definition of unknown kind %d: %s", kind, gotype->name); - die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name); - newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "<unspecified>")); - } - - return die; -} - -// Find or construct *T given T. -static DWDie* -defptrto(DWDie *dwtype) -{ - char ptrname[1024]; - DWDie *die; - - snprint(ptrname, sizeof ptrname, "*%s", getattr(dwtype, DW_AT_name)->data); - die = find(&dwtypes, ptrname); - if (die == nil) { - die = newdie(&dwtypes, DW_ABRV_PTRTYPE, - strcpy(mal(strlen(ptrname)+1), ptrname)); - newrefattr(die, DW_AT_type, dwtype); - } - return die; -} - -// Copies src's children into dst. Copies attributes by value. -// DWAttr.data is copied as pointer only. -static void -copychildren(DWDie *dst, DWDie *src) -{ - DWDie *c; - DWAttr *a; - - for (src = src->child; src != nil; src = src->link) { - c = newdie(dst, src->abbrev, getattr(src, DW_AT_name)->data); - for (a = src->attr; a != nil; a = a->link) - newattr(c, a->atr, a->cls, a->value, a->data); - copychildren(c, src); - } - reverselist(&dst->child); -} - -// Search children (assumed to have DW_TAG_member) for the one named -// field and set it's DW_AT_type to dwtype -static void -substitutetype(DWDie *structdie, char *field, DWDie* dwtype) -{ - DWDie *child; - DWAttr *a; - - child = find_or_diag(structdie, field); - if (child == nil) - return; - - a = getattr(child, DW_AT_type); - if (a != nil) - a->data = (char*) dwtype; - else - newrefattr(child, DW_AT_type, dwtype); -} - -static void -synthesizestringtypes(DWDie* die) -{ - DWDie *prototype; - - prototype = defgotype(lookup_or_diag("type.runtime._string")); - if (prototype == nil) - return; - - for (; die != nil; die = die->link) { - if (die->abbrev != DW_ABRV_STRINGTYPE) - continue; - copychildren(die, prototype); - } -} - -static void -synthesizeslicetypes(DWDie *die) -{ - DWDie *prototype, *elem; - - prototype = defgotype(lookup_or_diag("type.runtime.slice")); - if (prototype == nil) - return; - - for (; die != nil; die = die->link) { - if (die->abbrev != DW_ABRV_SLICETYPE) - continue; - copychildren(die, prototype); - elem = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; - substitutetype(die, "array", defptrto(elem)); - } -} - -static char* -mkinternaltypename(char *base, char *arg1, char *arg2) -{ - char buf[1024]; - char *n; - - if (arg2 == nil) - snprint(buf, sizeof buf, "%s<%s>", base, arg1); - else - snprint(buf, sizeof buf, "%s<%s,%s>", base, arg1, arg2); - n = mal(strlen(buf) + 1); - memmove(n, buf, strlen(buf)); - return n; -} - - -// synthesizemaptypes is way too closely married to runtime/hashmap.c -enum { - MaxValsize = 256 - 64 -}; - -static void -synthesizemaptypes(DWDie *die) -{ - - DWDie *hash, *hash_subtable, *hash_entry, - *dwh, *dwhs, *dwhe, *dwhash, *keytype, *valtype, *fld; - int hashsize, keysize, valsize, datsize, valsize_in_hash, datavo; - DWAttr *a; - - hash = defgotype(lookup_or_diag("type.runtime.hmap")); - hash_subtable = defgotype(lookup_or_diag("type.runtime.hash_subtable")); - hash_entry = defgotype(lookup_or_diag("type.runtime.hash_entry")); - - if (hash == nil || hash_subtable == nil || hash_entry == nil) - return; - - dwhash = (DWDie*)getattr(find_or_diag(hash_entry, "hash"), DW_AT_type)->data; - if (dwhash == nil) - return; - - hashsize = getattr(dwhash, DW_AT_byte_size)->value; - - for (; die != nil; die = die->link) { - if (die->abbrev != DW_ABRV_MAPTYPE) - continue; - - keytype = (DWDie*) getattr(die, DW_AT_internal_key_type)->data; - valtype = (DWDie*) getattr(die, DW_AT_internal_val_type)->data; - - a = getattr(keytype, DW_AT_byte_size); - keysize = a ? a->value : PtrSize; // We don't store size with Pointers - - a = getattr(valtype, DW_AT_byte_size); - valsize = a ? a->value : PtrSize; - - // This is what happens in hash_init and makemap_c - valsize_in_hash = valsize; - if (valsize > MaxValsize) - valsize_in_hash = PtrSize; - datavo = keysize; - if (valsize_in_hash >= PtrSize) - datavo = rnd(keysize, PtrSize); - datsize = datavo + valsize_in_hash; - if (datsize < PtrSize) - datsize = PtrSize; - datsize = rnd(datsize, PtrSize); - - // Construct struct hash_entry<K,V> - dwhe = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hash_entry", - getattr(keytype, DW_AT_name)->data, - getattr(valtype, DW_AT_name)->data)); - - fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "hash"); - newrefattr(fld, DW_AT_type, dwhash); - newmemberoffsetattr(fld, 0); - - fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "key"); - newrefattr(fld, DW_AT_type, keytype); - newmemberoffsetattr(fld, hashsize); - - fld = newdie(dwhe, DW_ABRV_STRUCTFIELD, "val"); - if (valsize > MaxValsize) - valtype = defptrto(valtype); - newrefattr(fld, DW_AT_type, valtype); - newmemberoffsetattr(fld, hashsize + datavo); - newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, nil); - - // Construct hash_subtable<hash_entry<K,V>> - dwhs = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hash_subtable", - getattr(keytype, DW_AT_name)->data, - getattr(valtype, DW_AT_name)->data)); - copychildren(dwhs, hash_subtable); - substitutetype(dwhs, "end", defptrto(dwhe)); - substitutetype(dwhs, "entry", dwhe); // todo: []hash_entry with dynamic size - newattr(dwhs, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hash_subtable, DW_AT_byte_size)->value, nil); - - // Construct hash<K,V> - dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hash", - getattr(keytype, DW_AT_name)->data, - getattr(valtype, DW_AT_name)->data)); - copychildren(dwh, hash); - substitutetype(dwh, "st", defptrto(dwhs)); - newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hash, DW_AT_byte_size)->value, nil); - - newrefattr(die, DW_AT_type, defptrto(dwh)); - } -} - -static void -synthesizechantypes(DWDie *die) -{ - DWDie *sudog, *waitq, *hchan, - *dws, *dww, *dwh, *elemtype; - DWAttr *a; - int elemsize, sudogsize; - - sudog = defgotype(lookup_or_diag("type.runtime.sudog")); - waitq = defgotype(lookup_or_diag("type.runtime.waitq")); - hchan = defgotype(lookup_or_diag("type.runtime.hchan")); - if (sudog == nil || waitq == nil || hchan == nil) - return; - - sudogsize = getattr(sudog, DW_AT_byte_size)->value; - - for (; die != nil; die = die->link) { - if (die->abbrev != DW_ABRV_CHANTYPE) - continue; - elemtype = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data; - a = getattr(elemtype, DW_AT_byte_size); - elemsize = a ? a->value : PtrSize; - - // sudog<T> - dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("sudog", - getattr(elemtype, DW_AT_name)->data, nil)); - copychildren(dws, sudog); - substitutetype(dws, "elem", elemtype); - newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, - sudogsize + (elemsize > 8 ? elemsize - 8 : 0), nil); - - // waitq<T> - dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, nil)); - copychildren(dww, waitq); - substitutetype(dww, "first", defptrto(dws)); - substitutetype(dww, "last", defptrto(dws)); - newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(waitq, DW_AT_byte_size)->value, nil); - - // hchan<T> - dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, nil)); - copychildren(dwh, hchan); - substitutetype(dwh, "recvq", dww); - substitutetype(dwh, "sendq", dww); - substitutetype(dwh, "free", defptrto(dws)); - newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hchan, DW_AT_byte_size)->value, nil); - - newrefattr(die, DW_AT_type, defptrto(dwh)); - } -} - -// For use with pass.c::genasmsym -static void -defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype) -{ - DWDie *dv, *dt; - - USED(size); - if (strncmp(s, "go.string.", 10) == 0) - return; - - if (strncmp(s, "type.", 5) == 0 && strcmp(s, "type.*") != 0) { - defgotype(sym); - return; - } - - dv = nil; - - switch (t) { - default: - return; - case 'd': - case 'b': - case 'D': - case 'B': - dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s); - newabslocexprattr(dv, v); - if (ver == 0) - newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0); - // fallthrough - case 'a': - case 'p': - dt = defgotype(gotype); - } - - if (dv != nil) - newrefattr(dv, DW_AT_type, dt); -} - -// TODO(lvd) For now, just append them all to the first compilation -// unit (that should be main), in the future distribute them to the -// appropriate compilation units. -static void -movetomodule(DWDie *parent) -{ - DWDie *die; - - for (die = dwroot.child->child; die->link != nil; die = die->link) /* nix */; - die->link = parent->child; -} - -/* - * Filename fragments for the line history stack. - */ - -static char **ftab; -static int ftabsize; - -void -dwarfaddfrag(int n, char *frag) -{ - int s; - - if (n >= ftabsize) { - s = ftabsize; - ftabsize = 1 + n + (n >> 2); - ftab = realloc(ftab, ftabsize * sizeof(ftab[0])); - memset(ftab + s, 0, (ftabsize - s) * sizeof(ftab[0])); - } - - if (*frag == '<') - frag++; - ftab[n] = frag; -} - -// Returns a malloc'ed string, piecewise copied from the ftab. -static char * -decodez(char *s) -{ - int len, o; - char *ss, *f; - char *r, *rb, *re; - - len = 0; - ss = s + 1; // first is 0 - while((o = ((uint8)ss[0] << 8) | (uint8)ss[1]) != 0) { - if (o < 0 || o >= ftabsize) { - diag("dwarf: corrupt z entry"); - return 0; - } - f = ftab[o]; - if (f == nil) { - diag("dwarf: corrupt z entry"); - return 0; - } - len += strlen(f) + 1; // for the '/' - ss += 2; - } - - if (len == 0) - return 0; - - r = malloc(len + 1); - rb = r; - re = rb + len + 1; - - s++; - while((o = ((uint8)s[0] << 8) | (uint8)s[1]) != 0) { - f = ftab[o]; - if (rb == r || rb[-1] == '/') - rb = seprint(rb, re, "%s", f); - else - rb = seprint(rb, re, "/%s", f); - s += 2; - } - return r; -} - -/* - * The line history itself - */ - -static char **histfile; // [0] holds "<eof>", DW_LNS_set_file arguments must be > 0. -static int histfilesize; -static int histfilecap; - -static void -clearhistfile(void) -{ - int i; - - // [0] holds "<eof>" - for (i = 1; i < histfilesize; i++) - free(histfile[i]); - histfilesize = 0; -} - -static int -addhistfile(char *zentry) -{ - char *fname; - - if (histfilesize == histfilecap) { - histfilecap = 2 * histfilecap + 2; - histfile = realloc(histfile, histfilecap * sizeof(char*)); - } - if (histfilesize == 0) - histfile[histfilesize++] = "<eof>"; - - fname = decodez(zentry); - if (fname == 0) - return -1; - // Don't fill with duplicates (check only top one). - if (strcmp(fname, histfile[histfilesize-1]) == 0) { - free(fname); - return histfilesize - 1; - } - histfile[histfilesize++] = fname; - return histfilesize - 1; -} - -// if the histfile stack contains ..../runtime/runtime_defs.go -// use that to set gdbscript -static void -finddebugruntimepath(void) -{ - int i, l; - char *c; - - for (i = 1; i < histfilesize; i++) { - if ((c = strstr(histfile[i], "runtime/runtime_defs.go")) != nil) { - l = c - histfile[i]; - memmove(gdbscript, histfile[i], l); - memmove(gdbscript + l, "runtime/runtime-gdb.py", strlen("runtime/runtime-gdb.py") + 1); - break; - } - } -} - -// Go's runtime C sources are sane, and Go sources nest only 1 level, -// so 16 should be plenty. -static struct { - int file; - vlong line; -} includestack[16]; -static int includetop; -static vlong absline; - -typedef struct Linehist Linehist; -struct Linehist { - Linehist *link; - vlong absline; - vlong line; - int file; -}; - -static Linehist *linehist; - -static void -checknesting(void) -{ - int i; - - if (includetop < 0) { - diag("dwarf: corrupt z stack"); - errorexit(); - } - if (includetop >= nelem(includestack)) { - diag("dwarf: nesting too deep"); - for (i = 0; i < nelem(includestack); i++) - diag("\t%s", histfile[includestack[i].file]); - errorexit(); - } -} - -/* - * Return false if the a->link chain contains no history, otherwise - * returns true and finds z and Z entries in the Auto list (of a - * Prog), and resets the history stack - */ -static int -inithist(Auto *a) -{ - Linehist *lh; - - for (; a; a = a->link) - if (a->type == D_FILE) - break; - if (a==nil) - return 0; - - // We have a new history. They are guaranteed to come completely - // at the beginning of the compilation unit. - if (a->aoffset != 1) { - diag("dwarf: stray 'z' with offset %d", a->aoffset); - return 0; - } - - // Clear the history. - clearhistfile(); - includetop = 0; - includestack[includetop].file = 0; - includestack[includetop].line = -1; - absline = 0; - while (linehist != nil) { - lh = linehist->link; - free(linehist); - linehist = lh; - } - - // Construct the new one. - for (; a; a = a->link) { - if (a->type == D_FILE) { // 'z' - int f = addhistfile(a->asym->name); - if (f < 0) { // pop file - includetop--; - checknesting(); - } else if(f != includestack[includetop].file) { // pushed a new file - includestack[includetop].line += a->aoffset - absline; - includetop++; - checknesting(); - includestack[includetop].file = f; - includestack[includetop].line = 1; - } - absline = a->aoffset; - } else if (a->type == D_FILE1) { // 'Z' - // We could just fixup the current - // linehist->line, but there doesn't appear to - // be a guarantee that every 'Z' is preceded - // by it's own 'z', so do the safe thing and - // update the stack and push a new Linehist - // entry - includestack[includetop].line = a->aoffset; - } else - continue; - if (linehist == 0 || linehist->absline != absline) { - Linehist* lh = malloc(sizeof *lh); - lh->link = linehist; - lh->absline = absline; - linehist = lh; - } - linehist->file = includestack[includetop].file; - linehist->line = includestack[includetop].line; - } - return 1; -} - -static Linehist * -searchhist(vlong absline) -{ - Linehist *lh; - - for (lh = linehist; lh; lh = lh->link) - if (lh->absline <= absline) - break; - return lh; -} - -static int -guesslang(char *s) -{ - if(strlen(s) >= 3 && strcmp(s+strlen(s)-3, ".go") == 0) - return DW_LANG_Go; - - return DW_LANG_C; -} - -/* - * Generate short opcodes when possible, long ones when neccesary. - * See section 6.2.5 - */ - -enum { - LINE_BASE = -1, - LINE_RANGE = 4, - OPCODE_BASE = 5 -}; - -static void -putpclcdelta(vlong delta_pc, vlong delta_lc) -{ - if (LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE) { - vlong opcode = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc); - if (OPCODE_BASE <= opcode && opcode < 256) { - cput(opcode); - return; - } - } - - if (delta_pc) { - cput(DW_LNS_advance_pc); - sleb128put(delta_pc); - } - - cput(DW_LNS_advance_line); - sleb128put(delta_lc); - cput(DW_LNS_copy); -} - -static void -newcfaoffsetattr(DWDie *die, int32 offs) -{ - char block[10]; - int i; - - i = 0; - - block[i++] = DW_OP_call_frame_cfa; - if (offs != 0) { - block[i++] = DW_OP_consts; - i += sleb128enc(offs, block+i); - block[i++] = DW_OP_plus; - } - newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i)); - memmove(die->attr->data, block, i); -} - -static char* -mkvarname(char* name, int da) -{ - char buf[1024]; - char *n; - - snprint(buf, sizeof buf, "%s#%d", name, da); - n = mal(strlen(buf) + 1); - memmove(n, buf, strlen(buf)); - return n; -} - -/* - * Walk prog table, emit line program and build DIE tree. - */ - -// flush previous compilation unit. -static void -flushunit(DWDie *dwinfo, vlong pc, vlong unitstart, int32 header_length) -{ - vlong here; - - if (dwinfo != nil && pc != 0) { - newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, 0); - } - - if (unitstart >= 0) { - cput(0); // start extended opcode - uleb128put(1); - cput(DW_LNE_end_sequence); - cflush(); - - here = cpos(); - seek(cout, unitstart, 0); - LPUT(here - unitstart - sizeof(int32)); // unit_length - WPUT(3); // dwarf version - LPUT(header_length); // header length starting here - cflush(); - seek(cout, here, 0); - } -} - -static void -writelines(void) -{ - Prog *q; - Sym *s; - Auto *a; - vlong unitstart, headerend, offs; - vlong pc, epc, lc, llc, lline; - int currfile; - int i, lang, da, dt; - Linehist *lh; - DWDie *dwinfo, *dwfunc, *dwvar, **dws; - DWDie *varhash[HASHSIZE]; - char *n, *nn; - - unitstart = -1; - headerend = -1; - pc = 0; - epc = 0; - lc = 1; - llc = 1; - currfile = -1; - lineo = cpos(); - dwinfo = nil; - - for(cursym = textp; cursym != nil; cursym = cursym->next) { - s = cursym; - if(s->text == P) - continue; - - // Look for history stack. If we find one, - // we're entering a new compilation unit - - if (inithist(s->autom)) { - flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10); - unitstart = cpos(); - - if(debug['v'] > 1) { - print("dwarf writelines found %s\n", histfile[1]); - Linehist* lh; - for (lh = linehist; lh; lh = lh->link) - print("\t%8lld: [%4lld]%s\n", - lh->absline, lh->line, histfile[lh->file]); - } - - lang = guesslang(histfile[1]); - finddebugruntimepath(); - - dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, strdup(histfile[1])); - newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT,lang, 0); - newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart - lineo, 0); - newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s->text->pc, 0); - - // Write .debug_line Line Number Program Header (sec 6.2.4) - // Fields marked with (*) must be changed for 64-bit dwarf - LPUT(0); // unit_length (*), will be filled in by flushunit. - WPUT(3); // dwarf version (appendix F) - LPUT(0); // header_length (*), filled in by flushunit. - // cpos == unitstart + 4 + 2 + 4 - cput(1); // minimum_instruction_length - cput(1); // default_is_stmt - cput(LINE_BASE); // line_base - cput(LINE_RANGE); // line_range - cput(OPCODE_BASE); // opcode_base (we only use 1..4) - cput(0); // standard_opcode_lengths[1] - cput(1); // standard_opcode_lengths[2] - cput(1); // standard_opcode_lengths[3] - cput(1); // standard_opcode_lengths[4] - cput(0); // include_directories (empty) - - for (i=1; i < histfilesize; i++) { - strnput(histfile[i], strlen(histfile[i]) + 4); - // 4 zeros: the string termination + 3 fields. - } - - cput(0); // terminate file_names. - headerend = cpos(); - - pc = s->text->pc; - epc = pc; - currfile = 1; - lc = 1; - llc = 1; - - cput(0); // start extended opcode - uleb128put(1 + PtrSize); - cput(DW_LNE_set_address); - addrput(pc); - } - if(s->text == nil) - continue; - - if (unitstart < 0) { - diag("dwarf: reachable code before seeing any history: %P", s->text); - continue; - } - - dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s->name); - newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, 0); - epc = s->value + s->size; - newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, 0); - if (s->version == 0) - newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0); - - if(s->text->link == nil) - continue; - - for(q = s->text; q != P; q = q->link) { - lh = searchhist(q->line); - if (lh == nil) { - diag("dwarf: corrupt history or bad absolute line: %P", q); - continue; - } - - if (lh->file < 1) { // 0 is the past-EOF entry. - // diag("instruction with line number past EOF in %s: %P", histfile[1], q); - continue; - } - - lline = lh->line + q->line - lh->absline; - if (debug['v'] > 1) - print("%6llux %s[%lld] %P\n", (vlong)q->pc, histfile[lh->file], lline, q); - - if (q->line == lc) - continue; - if (currfile != lh->file) { - currfile = lh->file; - cput(DW_LNS_set_file); - uleb128put(currfile); - } - putpclcdelta(q->pc - pc, lline - llc); - pc = q->pc; - lc = q->line; - llc = lline; - } - - da = 0; - dwfunc->hash = varhash; // enable indexing of children by name - memset(varhash, 0, sizeof varhash); - for(a = s->autom; a; a = a->link) { - switch (a->type) { - case D_AUTO: - dt = DW_ABRV_AUTO; - offs = a->aoffset - PtrSize; - break; - case D_PARAM: - dt = DW_ABRV_PARAM; - offs = a->aoffset; - break; - default: - continue; - } - if (strstr(a->asym->name, ".autotmp_")) - continue; - if (find(dwfunc, a->asym->name) != nil) - n = mkvarname(a->asym->name, da); - else - n = a->asym->name; - // Drop the package prefix from locals and arguments. - nn = strrchr(n, '.'); - if (nn) - n = nn + 1; - - dwvar = newdie(dwfunc, dt, n); - newcfaoffsetattr(dwvar, offs); - newrefattr(dwvar, DW_AT_type, defgotype(a->gotype)); - - // push dwvar down dwfunc->child to preserve order - newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, nil); - dwfunc->child = dwvar->link; // take dwvar out from the top of the list - for (dws = &dwfunc->child; *dws != nil; dws = &(*dws)->link) - if (offs > getattr(*dws, DW_AT_internal_location)->value) - break; - dwvar->link = *dws; - *dws = dwvar; - - da++; - } - - dwfunc->hash = nil; - } - - flushunit(dwinfo, epc, unitstart, headerend - unitstart - 10); - linesize = cpos() - lineo; -} - -/* - * Emit .debug_frame - */ -enum -{ - CIERESERVE = 16, - DATAALIGNMENTFACTOR = -4, // TODO -PtrSize? - FAKERETURNCOLUMN = 16 // TODO gdb6 doesnt like > 15? -}; - -static void -putpccfadelta(vlong deltapc, vlong cfa) -{ - if (deltapc < 0x40) { - cput(DW_CFA_advance_loc + deltapc); - } else if (deltapc < 0x100) { - cput(DW_CFA_advance_loc1); - cput(deltapc); - } else if (deltapc < 0x10000) { - cput(DW_CFA_advance_loc2); - WPUT(deltapc); - } else { - cput(DW_CFA_advance_loc4); - LPUT(deltapc); - } - - cput(DW_CFA_def_cfa_offset_sf); - sleb128put(cfa / DATAALIGNMENTFACTOR); -} - -static void -writeframes(void) -{ - Prog *p, *q; - Sym *s; - vlong fdeo, fdesize, pad, cfa, pc; - - frameo = cpos(); - - // Emit the CIE, Section 6.4.1 - LPUT(CIERESERVE); // initial length, must be multiple of PtrSize - LPUT(0xffffffff); // cid. - cput(3); // dwarf version (appendix F) - cput(0); // augmentation "" - uleb128put(1); // code_alignment_factor - sleb128put(DATAALIGNMENTFACTOR); // guess - uleb128put(FAKERETURNCOLUMN); // return_address_register - - cput(DW_CFA_def_cfa); - uleb128put(DWARFREGSP); // register SP (**ABI-dependent, defined in l.h) - uleb128put(PtrSize); // offset - - cput(DW_CFA_offset + FAKERETURNCOLUMN); // return address - uleb128put(-PtrSize / DATAALIGNMENTFACTOR); // at cfa - x*4 - - // 4 is to exclude the length field. - pad = CIERESERVE + frameo + 4 - cpos(); - if (pad < 0) { - diag("dwarf: CIERESERVE too small by %lld bytes.", -pad); - errorexit(); - } - strnput("", pad); - - for(cursym = textp; cursym != nil; cursym = cursym->next) { - s = cursym; - if(s->text == nil) - continue; - - fdeo = cpos(); - // Emit a FDE, Section 6.4.1, starting wit a placeholder. - LPUT(0); // length, must be multiple of PtrSize - LPUT(0); // Pointer to the CIE above, at offset 0 - addrput(0); // initial location - addrput(0); // address range - - cfa = PtrSize; // CFA starts at sp+PtrSize - p = s->text; - pc = p->pc; - - for(q = p; q->link != P; q = q->link) { - if (q->spadj == 0) - continue; - cfa += q->spadj; - putpccfadelta(q->link->pc - pc, cfa); - pc = q->link->pc; - } - - fdesize = cpos() - fdeo - 4; // exclude the length field. - pad = rnd(fdesize, PtrSize) - fdesize; - strnput("", pad); - fdesize += pad; - cflush(); - - // Emit the FDE header for real, Section 6.4.1. - seek(cout, fdeo, 0); - LPUT(fdesize); - LPUT(0); - addrput(p->pc); - addrput(s->size); - - cflush(); - seek(cout, fdeo + 4 + fdesize, 0); - } - - cflush(); - framesize = cpos() - frameo; -} - -/* - * Walk DWarfDebugInfoEntries, and emit .debug_info - */ -enum -{ - COMPUNITHEADERSIZE = 4+2+4+1 -}; - -static void -writeinfo(void) -{ - DWDie *compunit; - vlong unitstart, here; - - fwdcount = 0; - - for (compunit = dwroot.child; compunit; compunit = compunit->link) { - unitstart = cpos(); - - // Write .debug_info Compilation Unit Header (sec 7.5.1) - // Fields marked with (*) must be changed for 64-bit dwarf - // This must match COMPUNITHEADERSIZE above. - LPUT(0); // unit_length (*), will be filled in later. - WPUT(3); // dwarf version (appendix F) - LPUT(0); // debug_abbrev_offset (*) - cput(PtrSize); // address_size - - putdie(compunit); - - cflush(); - here = cpos(); - seek(cout, unitstart, 0); - LPUT(here - unitstart - 4); // exclude the length field. - cflush(); - seek(cout, here, 0); - } - -} - -/* - * Emit .debug_pubnames/_types. _info must have been written before, - * because we need die->offs and infoo/infosize; - */ -static int -ispubname(DWDie *die) { - DWAttr *a; - - switch(die->abbrev) { - case DW_ABRV_FUNCTION: - case DW_ABRV_VARIABLE: - a = getattr(die, DW_AT_external); - return a && a->value; - } - return 0; -} - -static int -ispubtype(DWDie *die) { - return die->abbrev >= DW_ABRV_NULLTYPE; -} - -static vlong -writepub(int (*ispub)(DWDie*)) -{ - DWDie *compunit, *die; - DWAttr *dwa; - vlong unitstart, unitend, sectionstart, here; - - sectionstart = cpos(); - - for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) { - unitstart = compunit->offs - COMPUNITHEADERSIZE; - if (compunit->link != nil) - unitend = compunit->link->offs - COMPUNITHEADERSIZE; - else - unitend = infoo + infosize; - - // Write .debug_pubnames/types Header (sec 6.1.1) - LPUT(0); // unit_length (*), will be filled in later. - WPUT(2); // dwarf version (appendix F) - LPUT(unitstart); // debug_info_offset (of the Comp unit Header) - LPUT(unitend - unitstart); // debug_info_length - - for (die = compunit->child; die != nil; die = die->link) { - if (!ispub(die)) continue; - LPUT(die->offs - unitstart); - dwa = getattr(die, DW_AT_name); - strnput(dwa->data, dwa->value + 1); - } - LPUT(0); - - cflush(); - here = cpos(); - seek(cout, sectionstart, 0); - LPUT(here - sectionstart - 4); // exclude the length field. - cflush(); - seek(cout, here, 0); - - } - - return sectionstart; -} - -/* - * emit .debug_aranges. _info must have been written before, - * because we need die->offs of dw_globals. - */ -static vlong -writearanges(void) -{ - DWDie *compunit; - DWAttr *b, *e; - int headersize; - vlong sectionstart; - - sectionstart = cpos(); - headersize = rnd(4+2+4+1+1, PtrSize); // don't count unit_length field itself - - for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) { - b = getattr(compunit, DW_AT_low_pc); - if (b == nil) - continue; - e = getattr(compunit, DW_AT_high_pc); - if (e == nil) - continue; - - // Write .debug_aranges Header + entry (sec 6.1.2) - LPUT(headersize + 4*PtrSize - 4); // unit_length (*) - WPUT(2); // dwarf version (appendix F) - LPUT(compunit->offs - COMPUNITHEADERSIZE); // debug_info_offset - cput(PtrSize); // address_size - cput(0); // segment_size - strnput("", headersize - (4+2+4+1+1)); // align to PtrSize - - addrput(b->value); - addrput(e->value - b->value); - addrput(0); - addrput(0); - } - cflush(); - return sectionstart; -} - -static vlong -writegdbscript(void) -{ - vlong sectionstart; - - sectionstart = cpos(); - - if (gdbscript[0]) { - cput(1); // magic 1 byte? - strnput(gdbscript, strlen(gdbscript)+1); - cflush(); - } - return sectionstart; -} - -static void -align(vlong size) -{ - if(HEADTYPE == Hwindows) // Only Windows PE need section align. - strnput("", rnd(size, PEFILEALIGN) - size); -} - -/* - * This is the main entry point for generating dwarf. After emitting - * the mandatory debug_abbrev section, it calls writelines() to set up - * the per-compilation unit part of the DIE tree, while simultaneously - * emitting the debug_line section. When the final tree contains - * forward references, it will write the debug_info section in 2 - * passes. - * - */ -void -dwarfemitdebugsections(void) -{ - vlong infoe; - DWDie* die; - - if(debug['w']) // disable dwarf - return; - - // For diagnostic messages. - newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, strlen("dwtypes"), "dwtypes"); - - mkindex(&dwroot); - mkindex(&dwtypes); - mkindex(&dwglobals); - - // Some types that must exist to define other ones. - newdie(&dwtypes, DW_ABRV_NULLTYPE, "<unspecified>"); - newdie(&dwtypes, DW_ABRV_NULLTYPE, "void"); - newrefattr(newdie(&dwtypes, DW_ABRV_PTRTYPE, "unsafe.Pointer"), - DW_AT_type, find(&dwtypes, "void")); - die = newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr"); // needed for array size - newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); - newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0); - - // Needed by the prettyprinter code for interface inspection. - defgotype(lookup_or_diag("type.runtime.commonType")); - defgotype(lookup_or_diag("type.runtime.InterfaceType")); - defgotype(lookup_or_diag("type.runtime.itab")); - - genasmsym(defdwsymb); - - writeabbrev(); - align(abbrevsize); - writelines(); - align(linesize); - writeframes(); - align(framesize); - - synthesizestringtypes(dwtypes.child); - synthesizeslicetypes(dwtypes.child); - synthesizemaptypes(dwtypes.child); - synthesizechantypes(dwtypes.child); - - reversetree(&dwroot.child); - reversetree(&dwtypes.child); - reversetree(&dwglobals.child); - - movetomodule(&dwtypes); - movetomodule(&dwglobals); - - infoo = cpos(); - writeinfo(); - infoe = cpos(); - pubnameso = infoe; - pubtypeso = infoe; - arangeso = infoe; - gdbscripto = infoe; - - if (fwdcount > 0) { - if (debug['v']) - Bprint(&bso, "%5.2f dwarf pass 2.\n", cputime()); - seek(cout, infoo, 0); - writeinfo(); - if (fwdcount > 0) { - diag("dwarf: unresolved references after first dwarf info pass"); - errorexit(); - } - if (infoe != cpos()) { - diag("dwarf: inconsistent second dwarf info pass"); - errorexit(); - } - } - infosize = infoe - infoo; - align(infosize); - - pubnameso = writepub(ispubname); - pubnamessize = cpos() - pubnameso; - align(pubnamessize); - - pubtypeso = writepub(ispubtype); - pubtypessize = cpos() - pubtypeso; - align(pubtypessize); - - arangeso = writearanges(); - arangessize = cpos() - arangeso; - align(arangessize); - - gdbscripto = writegdbscript(); - gdbscriptsize = cpos() - gdbscripto; - align(gdbscriptsize); -} - -/* - * Elf. - */ -enum -{ - ElfStrDebugAbbrev, - ElfStrDebugAranges, - ElfStrDebugFrame, - ElfStrDebugInfo, - ElfStrDebugLine, - ElfStrDebugLoc, - ElfStrDebugMacinfo, - ElfStrDebugPubNames, - ElfStrDebugPubTypes, - ElfStrDebugRanges, - ElfStrDebugStr, - ElfStrGDBScripts, - NElfStrDbg -}; - -vlong elfstrdbg[NElfStrDbg]; - -void -dwarfaddshstrings(Sym *shstrtab) -{ - if(debug['w']) // disable dwarf - return; - - elfstrdbg[ElfStrDebugAbbrev] = addstring(shstrtab, ".debug_abbrev"); - elfstrdbg[ElfStrDebugAranges] = addstring(shstrtab, ".debug_aranges"); - elfstrdbg[ElfStrDebugFrame] = addstring(shstrtab, ".debug_frame"); - elfstrdbg[ElfStrDebugInfo] = addstring(shstrtab, ".debug_info"); - elfstrdbg[ElfStrDebugLine] = addstring(shstrtab, ".debug_line"); - elfstrdbg[ElfStrDebugLoc] = addstring(shstrtab, ".debug_loc"); - elfstrdbg[ElfStrDebugMacinfo] = addstring(shstrtab, ".debug_macinfo"); - elfstrdbg[ElfStrDebugPubNames] = addstring(shstrtab, ".debug_pubnames"); - elfstrdbg[ElfStrDebugPubTypes] = addstring(shstrtab, ".debug_pubtypes"); - elfstrdbg[ElfStrDebugRanges] = addstring(shstrtab, ".debug_ranges"); - elfstrdbg[ElfStrDebugStr] = addstring(shstrtab, ".debug_str"); - elfstrdbg[ElfStrGDBScripts] = addstring(shstrtab, ".debug_gdb_scripts"); -} - -void -dwarfaddelfheaders(void) -{ - ElfShdr *sh; - - if(debug['w']) // disable dwarf - return; - - sh = newElfShdr(elfstrdbg[ElfStrDebugAbbrev]); - sh->type = SHT_PROGBITS; - sh->off = abbrevo; - sh->size = abbrevsize; - sh->addralign = 1; - - sh = newElfShdr(elfstrdbg[ElfStrDebugLine]); - sh->type = SHT_PROGBITS; - sh->off = lineo; - sh->size = linesize; - sh->addralign = 1; - - sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]); - sh->type = SHT_PROGBITS; - sh->off = frameo; - sh->size = framesize; - sh->addralign = 1; - - sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]); - sh->type = SHT_PROGBITS; - sh->off = infoo; - sh->size = infosize; - sh->addralign = 1; - - if (pubnamessize > 0) { - sh = newElfShdr(elfstrdbg[ElfStrDebugPubNames]); - sh->type = SHT_PROGBITS; - sh->off = pubnameso; - sh->size = pubnamessize; - sh->addralign = 1; - } - - if (pubtypessize > 0) { - sh = newElfShdr(elfstrdbg[ElfStrDebugPubTypes]); - sh->type = SHT_PROGBITS; - sh->off = pubtypeso; - sh->size = pubtypessize; - sh->addralign = 1; - } - - if (arangessize) { - sh = newElfShdr(elfstrdbg[ElfStrDebugAranges]); - sh->type = SHT_PROGBITS; - sh->off = arangeso; - sh->size = arangessize; - sh->addralign = 1; - } - - if (gdbscriptsize) { - sh = newElfShdr(elfstrdbg[ElfStrGDBScripts]); - sh->type = SHT_PROGBITS; - sh->off = gdbscripto; - sh->size = gdbscriptsize; - sh->addralign = 1; - } -} - -/* - * Macho - */ -void -dwarfaddmachoheaders(void) -{ - MachoSect *msect; - MachoSeg *ms; - vlong fakestart; - int nsect; - - if(debug['w']) // disable dwarf - return; - - // Zero vsize segments won't be loaded in memory, even so they - // have to be page aligned in the file. - fakestart = abbrevo & ~0xfff; - - nsect = 4; - if (pubnamessize > 0) - nsect++; - if (pubtypessize > 0) - nsect++; - if (arangessize > 0) - nsect++; - if (gdbscriptsize > 0) - nsect++; - - ms = newMachoSeg("__DWARF", nsect); - ms->fileoffset = fakestart; - ms->filesize = abbrevo-fakestart; - - msect = newMachoSect(ms, "__debug_abbrev"); - msect->off = abbrevo; - msect->size = abbrevsize; - ms->filesize += msect->size; - - msect = newMachoSect(ms, "__debug_line"); - msect->off = lineo; - msect->size = linesize; - ms->filesize += msect->size; - - msect = newMachoSect(ms, "__debug_frame"); - msect->off = frameo; - msect->size = framesize; - ms->filesize += msect->size; - - msect = newMachoSect(ms, "__debug_info"); - msect->off = infoo; - msect->size = infosize; - ms->filesize += msect->size; - - if (pubnamessize > 0) { - msect = newMachoSect(ms, "__debug_pubnames"); - msect->off = pubnameso; - msect->size = pubnamessize; - ms->filesize += msect->size; - } - - if (pubtypessize > 0) { - msect = newMachoSect(ms, "__debug_pubtypes"); - msect->off = pubtypeso; - msect->size = pubtypessize; - ms->filesize += msect->size; - } - - if (arangessize > 0) { - msect = newMachoSect(ms, "__debug_aranges"); - msect->off = arangeso; - msect->size = arangessize; - ms->filesize += msect->size; - } - - // TODO(lvd) fix gdb/python to load MachO (16 char section name limit) - if (gdbscriptsize > 0) { - msect = newMachoSect(ms, "__debug_gdb_scripts"); - msect->off = gdbscripto; - msect->size = gdbscriptsize; - ms->filesize += msect->size; - } -} - -/* - * Windows PE - */ -void -dwarfaddpeheaders(void) -{ - if(debug['w']) // disable dwarf - return; - - newPEDWARFSection(".debug_abbrev", abbrevsize); - newPEDWARFSection(".debug_line", linesize); - newPEDWARFSection(".debug_frame", framesize); - newPEDWARFSection(".debug_info", infosize); - newPEDWARFSection(".debug_pubnames", pubnamessize); - newPEDWARFSection(".debug_pubtypes", pubtypessize); - newPEDWARFSection(".debug_aranges", arangessize); - newPEDWARFSection(".debug_gdb_scripts", gdbscriptsize); -} |