diff options
author | Luuk van Dijk <lvd@golang.org> | 2010-10-24 23:07:52 +0200 |
---|---|---|
committer | Luuk van Dijk <lvd@golang.org> | 2010-10-24 23:07:52 +0200 |
commit | 5b4f1c6010ff26ac834f4f1c1b7f9565c1a279b4 (patch) | |
tree | 5ca2647f9054550cd9095d48fff4fb3f27bcab46 /src/cmd/ld | |
parent | 6978d7b85689cf2cf86463cdb7b6817bfdd82277 (diff) | |
download | go-5b4f1c6010ff26ac834f4f1c1b7f9565c1a279b4.tar.gz |
6l/8l: global and local variables and type info.
R=rsc
CC=golang-dev
http://codereview.appspot.com/2201044
Diffstat (limited to 'src/cmd/ld')
-rw-r--r-- | src/cmd/ld/dwarf.c | 1092 | ||||
-rw-r--r-- | src/cmd/ld/dwarf.h | 14 | ||||
-rw-r--r-- | src/cmd/ld/dwarf_defs.h | 16 | ||||
-rw-r--r-- | src/cmd/ld/elf.h | 3 |
4 files changed, 1039 insertions, 86 deletions
diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index cd9c82e02..7891f64c9 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -2,6 +2,15 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +// TODO: +// - eliminate DW_CLS_ if not used +// - package info in compilation units +// - assign global variables and types to their packages +// - (upstream) type info for C parts of runtime +// - 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. +// #include "l.h" #include "lib.h" #include "../ld/dwarf.h" @@ -17,10 +26,16 @@ static vlong abbrevo; static vlong abbrevsize; static vlong lineo; static vlong linesize; -static vlong infoo; +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; /* * Basic I/O @@ -39,7 +54,6 @@ addrput(vlong addr) } } - static int uleb128enc(uvlong v, char* dst) { @@ -106,17 +120,34 @@ struct DWAttrForm { uint8 form; }; -// index into the abbrevs table below. +// Index into the abbrevs table below. +// Keep in sync with ispubname() and ispubtype() below. enum { DW_ABRV_NULL, DW_ABRV_COMPUNIT, DW_ABRV_FUNCTION, + DW_ABRV_VARIABLE, + DW_ABRV_AUTO, + DW_ABRV_PARAM, + DW_ABRV_STRUCTFIELD, + 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; -struct DWAbbrev { +static struct DWAbbrev { uint8 tag; uint8 children; DWAttrForm attr[30]; @@ -135,10 +166,137 @@ struct DWAbbrev { }, /* FUNCTION */ { - DW_TAG_subprogram, DW_CHILDREN_no, + 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_addr, + 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 + }, + + /* 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 */ + { + DW_TAG_array_type, DW_CHILDREN_no, + 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, + 0, 0 + }, + + /* FUNCTYPE */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + 0, 0 + }, + + /* IFACETYPE */ + { + DW_TAG_interface_type, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + 0, 0 + }, + + /* MAPTYPE */ + { + DW_TAG_typedef, DW_CHILDREN_no, + DW_AT_name, DW_FORM_string, + 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 */ + // Children are data, len and cap of runtime::struct Slice. + { + DW_TAG_structure_type, DW_CHILDREN_yes, + DW_AT_name, DW_FORM_string, + DW_AT_byte_size, DW_FORM_udata, + 0, 0 + }, + + /* STRINGTYPE */ + // Children are str and len of runtime::struct String. + { + 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 }, }; @@ -154,8 +312,8 @@ writeabbrev(void) uleb128put(i); uleb128put(abbrevs[i].tag); cput(abbrevs[i].children); - // 0 is not a valid attr or form, so we can treat this as - // a string + // 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)); @@ -165,12 +323,29 @@ writeabbrev(void) } /* - * Debugging Information Entries and their attributes + * 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 all others, value is the whole thing and data is -// null. +// 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 { @@ -187,21 +362,20 @@ struct DWDie { 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 }; -// top level compilation unit DIE's -static DWDie *dwinfo; - -static DWDie* -newdie(DWDie *link, int abbrev) -{ - DWDie *die; +/* + * Root DIEs for compilation units, types and global variables. + */ - die = mal(sizeof *die); - die->abbrev = abbrev; - die->link = link; - return die; -} +static DWDie dwroot; +static DWDie dwtypes; +static DWDie dwglobals; static DWAttr* newattr(DWDie *die, uint8 attr, int cls, vlong value, char *data) @@ -218,6 +392,109 @@ newattr(DWDie *die, uint8 attr, int cls, vlong value, char *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*)); +} + +static DWDie* +find(DWDie *die, char* name) +{ + DWDie *a, *b; + int h; + + if (die->hash == nil) { + diag("lookup of %s in non-indexed DIE", name); + errorexit(); + } + + h = hashstr(name); + a = die->hash[h]; + + if (a == nil) + return nil; + + // AT_name always exists. + 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 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) { @@ -285,13 +562,24 @@ putattr(int form, int cls, vlong value, char *data) cput(value?1:0); break; - case DW_FORM_strp: // string - case DW_FORM_ref_addr: // reference - case DW_FORM_ref1: // reference + case DW_FORM_ref_addr: // reference to a DIE in the .info section + if (data == nil) { + diag("null dwarf 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("Unsupported atribute form %d / class %d", form, cls); @@ -330,6 +618,7 @@ putdies(DWDie* die) static void putdie(DWDie* die) { + die->offs = cpos() - infoo; uleb128put(die->abbrev); putattrs(die->abbrev, die->attr); if (abbrevs[die->abbrev].children) { @@ -344,8 +633,8 @@ reverselist(DWDie** list) DWDie *curr, * prev; curr = *list; - prev = 0; - while(curr) { + prev = nil; + while(curr != nil) { DWDie* next = curr->link; curr->link = prev; prev = curr; @@ -360,11 +649,397 @@ reversetree(DWDie** list) DWDie *die; reverselist(list); - if (*list != nil && abbrevs[(*list)->abbrev].children) - for (die = *list; die != nil; die = die->link) + 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); +} + +// 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, + KindFloat, + KindFloat32, + KindFloat64, + KindComplex, + KindComplex64, + KindComplex128, + KindArray, + KindChan, + KindFunc, + KindInterface, + KindMap, + KindPtr, + KindSlice, + KindString, + KindStruct, + KindUnsafePointer, + + KindNoPointers = 1<<7, +}; + +static Sym* +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].sym; + return nil; +} + +static uvlong +decode_inuxi(uchar* p, int sz) +{ + uvlong r; + uchar *inuxi; + int i; + + r = 0; + inuxi = nil; + switch (sz) { + case 2: + inuxi = inuxi2; + break; + case 4: + inuxi = inuxi4; + break; + case 8: + inuxi = inuxi8; + break; + default: + diag("decode inuxi %d", sz); + errorexit(); + } + for (i = 0; i < sz; i++) + r += p[i] << (8*inuxi[i]); + + return r; +} + +// 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 +static Sym* +decodetype_arrayelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8); // 0x1c / 0x30 +} + +// Type.PtrType.elem +static Sym* +decodetype_ptrelem(Sym *s) +{ + return decode_reloc(s, 5*PtrSize + 8); // 0x1c / 0x30 +} + +// Type.StructType.fields.Slice::len +static int +decodetype_structfieldcount(Sym *s) +{ + return decode_inuxi(s->p + 6*PtrSize + 8, 4); // 0x20 / 0x38 +} + +// Type.StructType.fields[]-> name, typ and offset. sizeof(structField) = 5*PtrSize +static uchar* +decodetype_structfieldname(Sym *s, int i) +{ + Sym *p; + p = decode_reloc(s, 6*PtrSize + 0x10 + i*5*PtrSize); // go.string."foo" 0x28 / 0x40 + if (p == nil) // embedded structs have a nil name. + return nil; + p = decode_reloc(p, 0); // string."foo" + if (p == nil) // shouldn't happen. + return nil; + return p->p; // the c-string +} + +static Sym* +decodetype_structfieldtype(Sym *s, int i) +{ + return decode_reloc(s, 8*PtrSize + 0x10 + i*5*PtrSize); // 0x30 / 0x50 +} + +static vlong +decodetype_structfieldoffs(Sym *s, int i) +{ + return decode_inuxi(s->p + 10*PtrSize + 0x10 + i*5*PtrSize, 4); // 0x38 / 0x60 +} + +// Define gotype, for composite ones recurse into constituents. +static DWDie* +defgotype(Sym *gotype) +{ + DWDie *die, *fld, *elem, *ptrelem; + Sym *s; + char *name, *ptrname, *f; + uint8 kind; + vlong bytesize; + int i, nfields; + + if (gotype == nil) + return find(&dwtypes, "<unspecified>"); // must be defined before + + if (strncmp("type.", gotype->name, 5) != 0) { + diag("Type name doesn't start with \".type\": %s", gotype->name); + return find(&dwtypes, "<unspecified>"); + } + name = gotype->name + 5; // Altenatively decode from Type.string + + die = find(&dwtypes, name); + if (die != nil) + return die; + + if (0 && debug['v'] > 2) { + print("new type: %s @0x%08x [%d]", gotype->name, gotype->value, gotype->size); + for (i = 0; i < gotype->size; ++i) { + if (!(i%8)) print("\n\t%04x ", i); + print("%02x ", gotype->p[i]); + } + print("\n"); + for (i = 0; i < gotype->nr; ++i) { + print("\t%02x %d %d %lld %s\n", + gotype->r[i].off, + gotype->r[i].siz, + gotype->r[i].type, + gotype->r[i].add, + gotype->r[i].sym->name); + } + } + + 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 KindFloat: + 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 KindComplex: + 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)); + break; + + case KindChan: + die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name); + // TODO: describe ../../pkg/runtime/chan.c::struct Hchan + break; + + case KindFunc: + die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name); + break; + + case KindInterface: + die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name); + break; + + case KindMap: + die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name); + // TODO: describe ../../pkg/runtime/hashmap.c::struct hash + 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); + fld = newdie(die, DW_ABRV_STRUCTFIELD, "data"); + // Synthesize *elemtype if not already exists. Maybe + // this should be named '<*T>' to not stand in the way + // of the real definition of *T. + s = decodetype_arrayelem(gotype); + elem = defgotype(s); + ptrname = strdup(s->name + 4); // skip "type" but leave the '.' + ptrname[0] = '*'; // .. to stuff in the '*' + ptrelem = find(&dwtypes, ptrname); + if (ptrelem == nil) { + ptrelem = newdie(&dwtypes, DW_ABRV_PTRTYPE, ptrname); + newrefattr(ptrelem, DW_AT_type, elem); + } else { + free(ptrname); + } + newrefattr(fld, DW_AT_type, ptrelem); + newmemberoffsetattr(fld, 0); + fld = newdie(die, DW_ABRV_STRUCTFIELD, "len"); + newrefattr(fld, DW_AT_type, find(&dwtypes, "<int32>")); + newmemberoffsetattr(fld, PtrSize); + fld = newdie(die, DW_ABRV_STRUCTFIELD, "cap"); + newrefattr(fld, DW_AT_type, find(&dwtypes, "<int32>")); + newmemberoffsetattr(fld, PtrSize + 4); + + break; + + case KindString: + die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0); + fld = newdie(die, DW_ABRV_STRUCTFIELD, "str"); + newrefattr(fld, DW_AT_type, find(&dwtypes, "<byte*>")); + newmemberoffsetattr(fld, 0); + fld = newdie(die, DW_ABRV_STRUCTFIELD, "len"); + newrefattr(fld, DW_AT_type, find(&dwtypes, "<int32>")); + newmemberoffsetattr(fld, PtrSize); + 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("definition of unknown kind %d: %s", kind, gotype->name); + die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name); + newrefattr(die, DW_AT_type, find(&dwtypes, "<unspecified>")); + } + + return die; + } + +// 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; + + if (gotype == nil) { + return; + } + + dv = nil; + + switch (t) { + default: + return; + case 'D': + case 'B': + dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s); + newattr(dv, DW_AT_location, DW_CLS_ADDRESS, v, 0); + 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. */ @@ -511,11 +1186,14 @@ checknesting(void) } } -/* find z and Z entries in the Auto list (of a Prog), and reset the history stack */ -static char * +/* + * 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) { - char *unitname; Linehist *lh; for (; a; a = a->link) @@ -531,8 +1209,6 @@ inithist(Auto *a) return 0; } - unitname = decodez(a->asym->name); - // Clear the history. clearhistfile(); includetop = 0; @@ -558,7 +1234,6 @@ inithist(Auto *a) checknesting(); includestack[includetop].file = f; includestack[includetop].line = 1; - } absline = a->aoffset; } else if (a->type == D_FILE1) { // 'Z' @@ -580,7 +1255,7 @@ inithist(Auto *a) linehist->file = includestack[includetop].file; linehist->line = includestack[includetop].line; } - return unitname; + return 1; } static Linehist * @@ -635,6 +1310,23 @@ putpclcdelta(vlong delta_pc, vlong 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); +} /* * Walk prog table, emit line program and build DIE tree. @@ -642,7 +1334,7 @@ putpclcdelta(vlong delta_pc, vlong delta_lc) // flush previous compilation unit. static void -flushunit(vlong pc, vlong unitstart) +flushunit(DWDie *dwinfo, vlong pc, vlong unitstart) { vlong here; @@ -669,12 +1361,13 @@ writelines(void) { Prog *q; Sym *s; - char *unitname; + Auto *a; vlong unitstart; vlong pc, epc, lc, llc, lline; int currfile; - int i; + int i, lang; Linehist *lh; + DWDie *dwinfo, *dwfunc, *dwvar; unitstart = -1; epc = pc = 0; @@ -682,32 +1375,39 @@ writelines(void) llc = 1; currfile = -1; lineo = cpos(); + dwinfo = nil; for(cursym = textp; cursym != nil; cursym = cursym->next) { s = cursym; // Look for history stack. If we find one, // we're entering a new compilation unit - if((unitname = inithist(s->autom)) != 0) { - flushunit(epc, unitstart); + + if (inithist(s->autom)) { + flushunit(dwinfo, epc, unitstart); unitstart = cpos(); + if(debug['v'] > 1) { - print("dwarf writelines found %s\n", unitname); + 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]); } - dwinfo = newdie(dwinfo, DW_ABRV_COMPUNIT); - newattr(dwinfo, DW_AT_name, DW_CLS_STRING, strlen(unitname), unitname); - newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, guesslang(unitname), 0); + + lang = guesslang(histfile[1]); + + 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 later. - WPUT(3); // version - LPUT(11); // header_length (*) + WPUT(3); // dwarf version (appendix F) + LPUT(11); // header_length (*), starting here. + cput(1); // minimum_instruction_length cput(1); // default_is_stmt cput(LINE_BASE); // line_base @@ -719,6 +1419,8 @@ writelines(void) cput(1); // standard_opcode_lengths[4] cput(0); // include_directories (empty) cput(0); // file_names (empty) (emitted by DW_LNE's below) + // header_length ends here. + for (i=1; i < histfilesize; i++) { cput(0); // start extended opcode uleb128put(1 + strlen(histfile[i]) + 4); @@ -745,11 +1447,12 @@ writelines(void) continue; } - dwinfo->child = newdie(dwinfo->child, DW_ABRV_FUNCTION); - newattr(dwinfo->child, DW_AT_name, DW_CLS_STRING, strlen(s->name), s->name); - newattr(dwinfo->child, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, 0); + 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(dwinfo->child, DW_AT_high_pc, DW_CLS_ADDRESS, epc, 0); + 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); for(q = s->text; q != P; q = q->link) { lh = searchhist(q->line); @@ -757,8 +1460,9 @@ writelines(void) diag("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", unitname, q); + // diag("instruction with line number past EOF in %s: %P", histfile[1], q); continue; } @@ -778,9 +1482,24 @@ writelines(void) lc = q->line; llc = lline; } + + for(a = s->autom; a; a = a->link) { + switch (a->type) { + case D_AUTO: + dwvar = newdie(dwfunc, DW_ABRV_AUTO, a->asym->name); + break; + case D_PARAM: + dwvar = newdie(dwfunc, DW_ABRV_PARAM, a->asym->name); + break; + default: + continue; + } + newcfaoffsetattr(dwvar, a->aoffset); + newrefattr(dwvar, DW_AT_type, defgotype(a->gotype)); + } } - flushunit(epc, unitstart); + flushunit(dwinfo, epc, unitstart); linesize = cpos() - lineo; } @@ -824,20 +1543,20 @@ writeframes(void) 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 - cput(0); // augmentation "" - uleb128put(1); // code_alignment_factor + 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 + 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 + uleb128put(-PtrSize / DATAALIGNMENTFACTOR); // at cfa - x*4 // 4 is to exclude the length field. pad = CIERESERVE + frameo + 4 - cpos(); @@ -866,7 +1585,6 @@ writeframes(void) 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; @@ -896,47 +1614,222 @@ writeframes(void) /* * Walk DWarfDebugInfoEntries, and emit .debug_info */ +enum +{ + COMPUNITHEADERSIZE = 4+2+4+1 +}; + static void writeinfo(void) { DWDie *compunit; - vlong unitstart; + vlong unitstart, here; - reversetree(&dwinfo); + fwdcount = 0; - infoo = cpos(); - - for (compunit = dwinfo; compunit; compunit = compunit->link) { + 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 - LPUT(0); // unit_length (*), will be filled in later. - WPUT(3); // version - LPUT(0); // debug_abbrev_offset (*) - cput(PtrSize); // address_size + // 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(); - vlong here = cpos(); + here = cpos(); seek(cout, unitstart, 0); - LPUT(here - unitstart - sizeof(int32)); + 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() +{ + 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(); - infosize = cpos() - infoo; + return sectionstart; } +/* + * 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; + + 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"); + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, "unsafe.Pointer"); + newrefattr(die, DW_AT_type, find(&dwtypes, "void")); + + die = newdie(&dwtypes, DW_ABRV_BASETYPE, "<int32>"); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_signed, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, 4, 0); + + die = newdie(&dwtypes, DW_ABRV_BASETYPE, "<byte>"); + newattr(die, DW_AT_encoding, DW_CLS_CONSTANT, DW_ATE_unsigned, 0); + newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, 1, 0); + + die = newdie(&dwtypes, DW_ABRV_PTRTYPE, "<byte*>"); + newrefattr(die, DW_AT_type, find(&dwtypes, "<byte>")); + + genasmsym(defdwsymb); + reversetree(&dwtypes.child); + reversetree(&dwglobals.child); + writeabbrev(); writelines(); writeframes(); + + reversetree(&dwroot.child); + movetomodule(&dwtypes); // TODO: put before functions + movetomodule(&dwglobals); + + + infoo = cpos(); writeinfo(); + infoe = cpos(); + + if (fwdcount > 0) { + if (debug['v']) + Bprint(&bso, "%5.2f dwarf pass 2.\n", cputime()); + seek(cout, infoo, 0); + writeinfo(); + if (fwdcount > 0) { + diag("unresolved references after first dwarf info pass"); + errorexit(); + } + if (infoe != cpos()) { + diag("inconsistent second dwarf info pass"); + errorexit(); + } + } + infosize = infoe - infoo; + + pubnameso = writepub(ispubname); + pubtypeso = writepub(ispubtype); + arangeso = writearanges(); + + pubnamessize = pubtypeso - pubnameso; + pubtypessize = arangeso - pubtypeso; + arangessize = cpos() - arangeso; } /* @@ -1004,6 +1897,30 @@ dwarfaddelfheaders(void) 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; + } } /* @@ -1021,23 +1938,48 @@ dwarfaddmachoheaders(void) // have to be page aligned in the file. fakestart = abbrevo & ~0xfff; - ms = newMachoSeg("__DWARF", 4); + ms = newMachoSeg("__DWARF", 7); ms->fileoffset = fakestart; - ms->filesize = abbrevo-fakestart + abbrevsize+linesize+framesize+infosize; + 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; + } } diff --git a/src/cmd/ld/dwarf.h b/src/cmd/ld/dwarf.h index 928aedd41..7881213c2 100644 --- a/src/cmd/ld/dwarf.h +++ b/src/cmd/ld/dwarf.h @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. - /* * Register 'f' symbol file fragments. Doing this while parsing the * .6 input saves a pass over the symbol table later. @@ -10,18 +9,19 @@ void dwarfaddfrag(int n, char* frag); /* - * Add the dwarf section names to the ELF - * s[ection]h[eader]str[ing]tab. - */ -void dwarfaddshstrings(Sym *shstrtab); - -/* * Emit debug_abbrevs, debug_info and debug_line sections to current * offset in cout. */ void dwarfemitdebugsections(void); /* + * Add the dwarf section names to the ELF + * s[ection]h[eader]str[ing]tab. Prerequisite for + * dwarfaddelfheaders(). + */ +void dwarfaddshstrings(Sym *shstrtab); + +/* * Add section headers pointing to the sections emitted in * dwarfemitdebugsections. */ diff --git a/src/cmd/ld/dwarf_defs.h b/src/cmd/ld/dwarf_defs.h index 3b54f77b0..0f1e5417c 100644 --- a/src/cmd/ld/dwarf_defs.h +++ b/src/cmd/ld/dwarf_defs.h @@ -55,6 +55,7 @@ enum DW_TAG_variant_part = 0x33, DW_TAG_variable = 0x34, DW_TAG_volatile_type = 0x35, + // Dwarf3 DW_TAG_dwarf_procedure = 0x36, DW_TAG_restrict_type = 0x37, DW_TAG_interface_type = 0x38, @@ -65,6 +66,12 @@ enum DW_TAG_imported_unit = 0x3d, DW_TAG_condition = 0x3f, DW_TAG_shared_type = 0x40, + // Dwarf4 + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + + // User defined DW_TAG_lo_user = 0x4080, DW_TAG_hi_user = 0xffff, @@ -84,7 +91,7 @@ enum DW_CLS_BLOCK, DW_CLS_CONSTANT, DW_CLS_FLAG, - DW_CLS_PTR, // lineptr, loclistptr, macptr, rangelistptr + DW_CLS_PTR, // lineptr, loclistptr, macptr, rangelistptr DW_CLS_REFERENCE, DW_CLS_STRING }; @@ -151,6 +158,7 @@ enum DW_AT_variable_parameter = 0x4b, // flag DW_AT_virtuality = 0x4c, // constant DW_AT_vtable_elem_location = 0x4d, // block, loclistptr + // Dwarf3 DW_AT_allocated = 0x4e, // block, constant, reference DW_AT_associated = 0x4f, // block, constant, reference DW_AT_data_location = 0x50, // block @@ -178,6 +186,7 @@ enum DW_AT_elemental = 0x66, // flag DW_AT_pure = 0x67, // flag DW_AT_recursive = 0x68, // flag + DW_AT_lo_user = 0x2000, // --- DW_AT_hi_user = 0x3fff, // --- @@ -358,6 +367,7 @@ enum DW_LANG_Fortran90 = 0x0008, DW_LANG_Pascal83 = 0x0009, DW_LANG_Modula2 = 0x000a, + // Dwarf3 DW_LANG_Java = 0x000b, DW_LANG_C99 = 0x000c, DW_LANG_Ada95 = 0x000d, @@ -367,7 +377,8 @@ enum DW_LANG_ObjC_plus_plus = 0x0011, DW_LANG_UPC = 0x0012, DW_LANG_D = 0x0013, - DW_LANG_Python = 0x0014, // DWARF4 + // Dwarf4 + DW_LANG_Python = 0x0014, DW_LANG_lo_user = 0x8000, DW_LANG_Go = 0x8015, // TODO(lvd) Temporary @@ -428,6 +439,7 @@ enum DW_LNS_set_basic_block = 0x07, DW_LNS_const_add_pc = 0x08, DW_LNS_fixed_advance_pc = 0x09, + // Dwarf3 DW_LNS_set_prologue_end = 0x0a, DW_LNS_set_epilogue_begin = 0x0b, DW_LNS_set_isa = 0x0c, diff --git a/src/cmd/ld/elf.h b/src/cmd/ld/elf.h index 41a6b3966..2ba6e53e6 100644 --- a/src/cmd/ld/elf.h +++ b/src/cmd/ld/elf.h @@ -975,5 +975,4 @@ void elfsetstring(char*, int); * May waste some. * On FreeBSD, cannot be larger than a page. */ -#define ELFRESERVE 2048 - +#define ELFRESERVE 3072 |