// 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 package ld import ( "cmd/internal/obj" "fmt" "os" "strings" ) /* * Offsets and sizes of the debug_* sections in the cout file. */ var abbrevo int64 var abbrevsize int64 var abbrevsym *LSym var abbrevsympos int64 var lineo int64 var linesize int64 var linesym *LSym var linesympos int64 var infoo int64 // also the base for DWDie->offs and reference attributes. var infosize int64 var infosym *LSym var infosympos int64 var frameo int64 var framesize int64 var framesym *LSym var framesympos int64 var pubnameso int64 var pubnamessize int64 var pubtypeso int64 var pubtypessize int64 var arangeso int64 var arangessize int64 var gdbscripto int64 var gdbscriptsize int64 var infosec *LSym var inforeloco int64 var inforelocsize int64 var arangessec *LSym var arangesreloco int64 var arangesrelocsize int64 var linesec *LSym var linereloco int64 var linerelocsize int64 var framesec *LSym var framereloco int64 var framerelocsize int64 var gdbscript string /* * Basic I/O */ func addrput(addr int64) { switch Thearch.Ptrsize { case 4: Thearch.Lput(uint32(addr)) case 8: Thearch.Vput(uint64(addr)) } } func uleb128enc(v uint64, dst []byte) int { var c uint8 length := uint8(0) for { c = uint8(v & 0x7f) v >>= 7 if v != 0 { c |= 0x80 } if dst != nil { dst[0] = byte(c) dst = dst[1:] } length++ if c&0x80 == 0 { break } } return int(length) } func sleb128enc(v int64, dst []byte) int { var c uint8 var s uint8 length := uint8(0) for { c = uint8(v & 0x7f) s = uint8(v & 0x40) v >>= 7 if (v != -1 || s == 0) && (v != 0 || s != 0) { c |= 0x80 } if dst != nil { dst[0] = byte(c) dst = dst[1:] } length++ if c&0x80 == 0 { break } } return int(length) } var encbuf [10]byte func uleb128put(v int64) { n := uleb128enc(uint64(v), encbuf[:]) Cwrite(encbuf[:n]) } func sleb128put(v int64) { n := sleb128enc(v, encbuf[:]) Cwrite(encbuf[:n]) } /* * Defining Abbrevs. This is hardcoded, and there will be * only a handful of them. The DWARF spec places no restriction on * the ordering of attributes in the Abbrevs and DIEs, and we will * always write them out in the order of declaration in the abbrev. */ type DWAttrForm struct { attr uint16 form uint8 } // Go-specific type attributes. const ( DW_AT_go_kind = 0x2900 DW_AT_go_key = 0x2901 DW_AT_go_elem = 0x2902 DW_AT_internal_location = 253 // params and locals; not emitted ) // Index into the abbrevs table below. // Keep in sync with ispubname() and ispubtype() below. // ispubtype considers >= NULLTYPE public const ( DW_ABRV_NULL = iota 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_BARE_PTRTYPE // only for void*, no DW_AT_type attr to please gdb 6. DW_ABRV_SLICETYPE DW_ABRV_STRINGTYPE DW_ABRV_STRUCTTYPE DW_ABRV_TYPEDECL DW_NABRV ) type DWAbbrev struct { tag uint8 children uint8 attr []DWAttrForm } var abbrevs = [DW_NABRV]DWAbbrev{ /* The mandatory DW_ABRV_NULL entry. */ {0, 0, []DWAttrForm{}}, /* COMPUNIT */ { DW_TAG_compile_unit, DW_CHILDREN_yes, []DWAttrForm{ {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}, {DW_AT_comp_dir, DW_FORM_string}, }, }, /* FUNCTION */ { DW_TAG_subprogram, DW_CHILDREN_yes, []DWAttrForm{ {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}, }, }, /* VARIABLE */ { DW_TAG_variable, DW_CHILDREN_no, []DWAttrForm{ {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}, }, }, /* AUTO */ { DW_TAG_variable, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_location, DW_FORM_block1}, {DW_AT_type, DW_FORM_ref_addr}, }, }, /* PARAM */ { DW_TAG_formal_parameter, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_location, DW_FORM_block1}, {DW_AT_type, DW_FORM_ref_addr}, }, }, /* STRUCTFIELD */ { DW_TAG_member, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_data_member_location, DW_FORM_block1}, {DW_AT_type, DW_FORM_ref_addr}, }, }, /* FUNCTYPEPARAM */ { DW_TAG_formal_parameter, DW_CHILDREN_no, // No name! []DWAttrForm{ {DW_AT_type, DW_FORM_ref_addr}, }, }, /* DOTDOTDOT */ { DW_TAG_unspecified_parameters, DW_CHILDREN_no, []DWAttrForm{}, }, /* ARRAYRANGE */ { DW_TAG_subrange_type, DW_CHILDREN_no, // No name! []DWAttrForm{ {DW_AT_type, DW_FORM_ref_addr}, {DW_AT_count, DW_FORM_udata}, }, }, // Below here are the types considered public by ispubtype /* NULLTYPE */ { DW_TAG_unspecified_type, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, }, }, /* BASETYPE */ { DW_TAG_base_type, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_encoding, DW_FORM_data1}, {DW_AT_byte_size, DW_FORM_data1}, {DW_AT_go_kind, DW_FORM_data1}, }, }, /* ARRAYTYPE */ // child is subrange with upper bound { DW_TAG_array_type, DW_CHILDREN_yes, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_type, DW_FORM_ref_addr}, {DW_AT_byte_size, DW_FORM_udata}, {DW_AT_go_kind, DW_FORM_data1}, }, }, /* CHANTYPE */ { DW_TAG_typedef, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_type, DW_FORM_ref_addr}, {DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_elem, DW_FORM_ref_addr}, }, }, /* FUNCTYPE */ { DW_TAG_subroutine_type, DW_CHILDREN_yes, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, // {DW_AT_type, DW_FORM_ref_addr}, {DW_AT_go_kind, DW_FORM_data1}, }, }, /* IFACETYPE */ { DW_TAG_typedef, DW_CHILDREN_yes, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_type, DW_FORM_ref_addr}, {DW_AT_go_kind, DW_FORM_data1}, }, }, /* MAPTYPE */ { DW_TAG_typedef, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_type, DW_FORM_ref_addr}, {DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_key, DW_FORM_ref_addr}, {DW_AT_go_elem, DW_FORM_ref_addr}, }, }, /* PTRTYPE */ { DW_TAG_pointer_type, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_type, DW_FORM_ref_addr}, {DW_AT_go_kind, DW_FORM_data1}, }, }, /* BARE_PTRTYPE */ { DW_TAG_pointer_type, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, }, }, /* SLICETYPE */ { DW_TAG_structure_type, DW_CHILDREN_yes, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_byte_size, DW_FORM_udata}, {DW_AT_go_kind, DW_FORM_data1}, {DW_AT_go_elem, DW_FORM_ref_addr}, }, }, /* STRINGTYPE */ { DW_TAG_structure_type, DW_CHILDREN_yes, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_byte_size, DW_FORM_udata}, {DW_AT_go_kind, DW_FORM_data1}, }, }, /* STRUCTTYPE */ { DW_TAG_structure_type, DW_CHILDREN_yes, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_byte_size, DW_FORM_udata}, {DW_AT_go_kind, DW_FORM_data1}, }, }, /* TYPEDECL */ { DW_TAG_typedef, DW_CHILDREN_no, []DWAttrForm{ {DW_AT_name, DW_FORM_string}, {DW_AT_type, DW_FORM_ref_addr}, }, }, } func writeabbrev() { abbrevo = Cpos() for i := 1; i < DW_NABRV; i++ { // See section 7.5.3 uleb128put(int64(i)) uleb128put(int64(abbrevs[i].tag)) Cput(abbrevs[i].children) for _, f := range abbrevs[i].attr { uleb128put(int64(f.attr)) uleb128put(int64(f.form)) } uleb128put(0) uleb128put(0) } Cput(0) abbrevsize = Cpos() - abbrevo } /* * Debugging Information Entries and their attributes. */ const ( HASHSIZE = 107 ) func dwarfhashstr(s string) uint32 { h := uint32(0) for s != "" { h = h + h + h + uint32(s[0]) s = s[1:] } 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. type DWAttr struct { link *DWAttr atr uint16 // DW_AT_ cls uint8 // DW_CLS_ value int64 data interface{} } type DWDie struct { abbrev int link *DWDie child *DWDie attr *DWAttr // offset into .debug_info section, i.e relative to // infoo. only valid after call to putdie() offs int64 hash []*DWDie // optional index of children by name, enabled by mkindex() hlink *DWDie // bucket chain in parent's index } /* * Root DIEs for compilation units, types and global variables. */ var dwroot DWDie var dwtypes DWDie var dwglobals DWDie func newattr(die *DWDie, attr uint16, cls int, value int64, data interface{}) *DWAttr { a := new(DWAttr) a.link = die.attr die.attr = a a.atr = attr a.cls = uint8(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. func getattr(die *DWDie, attr uint16) *DWAttr { if die.attr.atr == attr { return die.attr } a := die.attr b := a.link for 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. func newdie(parent *DWDie, abbrev int, name string) *DWDie { die := new(DWDie) die.abbrev = abbrev die.link = parent.child parent.child = die newattr(die, DW_AT_name, DW_CLS_STRING, int64(len(name)), name) if parent.hash != nil { h := int(dwarfhashstr(name)) die.hlink = parent.hash[h] parent.hash[h] = die } return die } func mkindex(die *DWDie) { die.hash = make([]*DWDie, HASHSIZE) } func walktypedef(die *DWDie) *DWDie { // Resolve typedef if present. if die.abbrev == DW_ABRV_TYPEDECL { for attr := die.attr; attr != nil; attr = attr.link { if attr.atr == DW_AT_type && attr.cls == DW_CLS_REFERENCE && attr.data != nil { return attr.data.(*DWDie) } } } return die } // Find child by AT_name using hashtable if available or linear scan // if not. func find(die *DWDie, name string) *DWDie { var a *DWDie var b *DWDie var die2 *DWDie var h int top: if die.hash == nil { for a = die.child; a != nil; a = a.link { if name == getattr(a, DW_AT_name).data { return a } } goto notfound } h = int(dwarfhashstr(name)) a = die.hash[h] if a == nil { goto notfound } if name == getattr(a, DW_AT_name).data { return a } // Move found ones to head of the list. b = a.hlink for b != nil { if name == getattr(b, DW_AT_name).data { a.hlink = b.hlink b.hlink = die.hash[h] die.hash[h] = b return b } a = b b = b.hlink } notfound: die2 = walktypedef(die) if die2 != die { die = die2 goto top } return nil } func mustFind(die *DWDie, name string) *DWDie { r := find(die, name) if r == nil { Exitf("dwarf find: %s %p has no %s", getattr(die, DW_AT_name).data, die, name) } return r } func adddwarfrel(sec *LSym, sym *LSym, offsetbase int64, siz int, addend int64) { r := Addrel(sec) r.Sym = sym r.Xsym = sym r.Off = int32(Cpos() - offsetbase) r.Siz = uint8(siz) r.Type = obj.R_ADDR r.Add = addend r.Xadd = addend if Iself && Thearch.Thechar == '6' { addend = 0 } if HEADTYPE == obj.Hdarwin { addend += sym.Value } switch siz { case 4: Thearch.Lput(uint32(addend)) case 8: Thearch.Vput(uint64(addend)) default: Diag("bad size in adddwarfrel") } } func newrefattr(die *DWDie, attr uint16, ref *DWDie) *DWAttr { if ref == nil { return nil } return newattr(die, attr, DW_CLS_REFERENCE, 0, ref) } var fwdcount int func putattr(abbrev int, form int, cls int, value int64, data interface{}) { switch form { case DW_FORM_addr: // address if Linkmode == LinkExternal { value -= (data.(*LSym)).Value adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) break } addrput(value) case DW_FORM_block1: // block if cls == DW_CLS_ADDRESS { Cput(uint8(1 + Thearch.Ptrsize)) Cput(DW_OP_addr) if Linkmode == LinkExternal { value -= (data.(*LSym)).Value adddwarfrel(infosec, data.(*LSym), infoo, Thearch.Ptrsize, value) break } addrput(value) break } value &= 0xff Cput(uint8(value)) p := data.([]byte) for i := 0; int64(i) < value; i++ { Cput(uint8(p[i])) } case DW_FORM_block2: // block value &= 0xffff Thearch.Wput(uint16(value)) p := data.([]byte) for i := 0; int64(i) < value; i++ { Cput(uint8(p[i])) } case DW_FORM_block4: // block value &= 0xffffffff Thearch.Lput(uint32(value)) p := data.([]byte) for i := 0; int64(i) < value; i++ { Cput(uint8(p[i])) } case DW_FORM_block: // block uleb128put(value) p := data.([]byte) for i := 0; int64(i) < value; i++ { Cput(uint8(p[i])) } case DW_FORM_data1: // constant Cput(uint8(value)) case DW_FORM_data2: // constant Thearch.Wput(uint16(value)) case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr if Linkmode == LinkExternal && cls == DW_CLS_PTR { adddwarfrel(infosec, linesym, infoo, 4, value) break } Thearch.Lput(uint32(value)) case DW_FORM_data8: // constant, {line,loclist,mac,rangelist}ptr Thearch.Vput(uint64(value)) case DW_FORM_sdata: // constant sleb128put(value) case DW_FORM_udata: // constant uleb128put(value) case DW_FORM_string: // string strnput(data.(string), int(value+1)) case DW_FORM_flag: // flag if value != 0 { Cput(1) } else { Cput(0) } // In DWARF 2 (which is what we claim to generate), // the ref_addr is the same size as a normal address. // In DWARF 3 it is always 32 bits, unless emitting a large // (> 4 GB of debug info aka "64-bit") unit, which we don't implement. case DW_FORM_ref_addr: // reference to a DIE in the .info section if data == nil { Diag("dwarf: null reference in %d", abbrev) if Thearch.Ptrsize == 8 { Thearch.Vput(0) // invalid dwarf, gdb will complain. } else { Thearch.Lput(0) // invalid dwarf, gdb will complain. } } else { off := (data.(*DWDie)).offs if off == 0 { fwdcount++ } if Linkmode == LinkExternal { adddwarfrel(infosec, infosym, infoo, Thearch.Ptrsize, off) break } addrput(off) } case DW_FORM_ref1, // reference within the compilation unit DW_FORM_ref2, // reference DW_FORM_ref4, // reference DW_FORM_ref8, // reference DW_FORM_ref_udata, // reference DW_FORM_strp, // string DW_FORM_indirect: // (see Section 7.5.3) fallthrough default: Exitf("dwarf: unsupported attribute form %d / class %d", form, cls) } } // 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. func putattrs(abbrev int, attr *DWAttr) { Outer: for _, f := range abbrevs[abbrev].attr { for ap := attr; ap != nil; ap = ap.link { if ap.atr == f.attr { putattr(abbrev, int(f.form), int(ap.cls), ap.value, ap.data) continue Outer } } putattr(abbrev, int(f.form), 0, 0, nil) } } func putdies(die *DWDie) { for ; die != nil; die = die.link { putdie(die) } } func putdie(die *DWDie) { die.offs = Cpos() - infoo uleb128put(int64(die.abbrev)) putattrs(die.abbrev, die.attr) if abbrevs[die.abbrev].children != 0 { putdies(die.child) Cput(0) } } func reverselist(list **DWDie) { curr := *list var prev *DWDie for curr != nil { var next *DWDie = curr.link curr.link = prev prev = curr curr = next } *list = prev } func reversetree(list **DWDie) { reverselist(list) for die := *list; die != nil; die = die.link { if abbrevs[die.abbrev].children != 0 { reversetree(&die.child) } } } func newmemberoffsetattr(die *DWDie, offs int32) { var block [20]byte i := 0 block[i] = DW_OP_plus_uconst i++ i += uleb128enc(uint64(offs), block[i:]) newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, int64(i), block[:i]) } // GDB doesn't like DW_FORM_addr for DW_AT_location, so emit a // location expression that evals to a const. func newabslocexprattr(die *DWDie, addr int64, sym *LSym) { newattr(die, DW_AT_location, DW_CLS_ADDRESS, addr, sym) // below } // Lookup predefined types func lookup_or_diag(n string) *LSym { s := Linkrlookup(Ctxt, n, 0) if s == nil || s.Size == 0 { Exitf("dwarf: missing type: %s", n) } return s } func dotypedef(parent *DWDie, name string, def *DWDie) { // Only emit typedefs for real names. if strings.HasPrefix(name, "map[") { return } if strings.HasPrefix(name, "struct {") { return } if strings.HasPrefix(name, "chan ") { return } if name[0] == '[' || name[0] == '*' { return } if def == nil { Diag("dwarf: bad def in dotypedef") } // The typedef entry must be created after the def, // so that future lookups will find the typedef instead // of the real definition. This hooks the typedef into any // circular definition loops, so that gdb can understand them. die := newdie(parent, DW_ABRV_TYPEDECL, name) newrefattr(die, DW_AT_type, def) } // Define gotype, for composite ones recurse into constituents. func defgotype(gotype *LSym) *DWDie { if gotype == nil { return mustFind(&dwtypes, "<unspecified>") } if !strings.HasPrefix(gotype.Name, "type.") { Diag("dwarf: type name doesn't start with \".type\": %s", gotype.Name) return mustFind(&dwtypes, "<unspecified>") } name := gotype.Name[5:] // could also decode from Type.string die := find(&dwtypes, name) if die != nil { return die } if false && Debug['v'] > 2 { fmt.Printf("new type: %v\n", gotype) } kind := decodetype_kind(gotype) bytesize := decodetype_size(gotype) switch kind { case obj.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) case obj.KindInt, obj.KindInt8, obj.KindInt16, obj.KindInt32, obj.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) case obj.KindUint, obj.KindUint8, obj.KindUint16, obj.KindUint32, obj.KindUint64, obj.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) case obj.KindFloat32, obj.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) case obj.KindComplex64, obj.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) case obj.KindArray: die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name) dotypedef(&dwtypes, name, die) 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") // use actual length not upper bound; correct for 0-length arrays. newattr(fld, DW_AT_count, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0) newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) case obj.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_go_elem, defgotype(s)) case obj.KindFunc: die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name) dotypedef(&dwtypes, name, die) newrefattr(die, DW_AT_type, mustFind(&dwtypes, "void")) nfields := decodetype_funcincount(gotype) var fld *DWDie var s *LSym 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) != 0 { 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))) } case obj.KindInterface: die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name) dotypedef(&dwtypes, name, die) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) nfields := int(decodetype_ifacemethodcount(gotype)) var s *LSym 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)) case obj.KindMap: die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name) s := decodetype_mapkey(gotype) newrefattr(die, DW_AT_go_key, defgotype(s)) s = decodetype_mapvalue(gotype) newrefattr(die, DW_AT_go_elem, defgotype(s)) case obj.KindPtr: die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name) dotypedef(&dwtypes, name, die) s := decodetype_ptrelem(gotype) newrefattr(die, DW_AT_type, defgotype(s)) case obj.KindSlice: die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name) dotypedef(&dwtypes, name, die) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) s := decodetype_arrayelem(gotype) newrefattr(die, DW_AT_go_elem, defgotype(s)) case obj.KindString: die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) case obj.KindStruct: die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name) dotypedef(&dwtypes, name, die) newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0) nfields := decodetype_structfieldcount(gotype) var f string var fld *DWDie var s *LSym for i := 0; i < nfields; i++ { f = decodetype_structfieldname(gotype, i) s = decodetype_structfieldtype(gotype, i) if f == "" { f = s.Name[5:] // skip "type." } fld = newdie(die, DW_ABRV_STRUCTFIELD, f) newrefattr(fld, DW_AT_type, defgotype(s)) newmemberoffsetattr(fld, int32(decodetype_structfieldoffs(gotype, i))) } case obj.KindUnsafePointer: die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name) default: Diag("dwarf: definition of unknown kind %d: %s", kind, gotype.Name) die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name) newrefattr(die, DW_AT_type, mustFind(&dwtypes, "<unspecified>")) } newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, int64(kind), 0) return die } // Find or construct *T given T. func defptrto(dwtype *DWDie) *DWDie { ptrname := fmt.Sprintf("*%s", getattr(dwtype, DW_AT_name).data) die := find(&dwtypes, ptrname) if die == nil { die = newdie(&dwtypes, DW_ABRV_PTRTYPE, 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. If except is one of // the top-level children, it will not be copied. func copychildrenexcept(dst *DWDie, src *DWDie, except *DWDie) { var c *DWDie var a *DWAttr for src = src.child; src != nil; src = src.link { if src == except { continue } c = newdie(dst, src.abbrev, getattr(src, DW_AT_name).data.(string)) for a = src.attr; a != nil; a = a.link { newattr(c, a.atr, int(a.cls), a.value, a.data) } copychildrenexcept(c, src, nil) } reverselist(&dst.child) } func copychildren(dst *DWDie, src *DWDie) { copychildrenexcept(dst, src, nil) } // Search children (assumed to have DW_TAG_member) for the one named // field and set its DW_AT_type to dwtype func substitutetype(structdie *DWDie, field string, dwtype *DWDie) { child := mustFind(structdie, field) if child == nil { return } a := getattr(child, DW_AT_type) if a != nil { a.data = dwtype } else { newrefattr(child, DW_AT_type, dwtype) } } func synthesizestringtypes(die *DWDie) { prototype := walktypedef(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) } } func synthesizeslicetypes(die *DWDie) { prototype := walktypedef(defgotype(lookup_or_diag("type.runtime.slice"))) if prototype == nil { return } var elem *DWDie for ; die != nil; die = die.link { if die.abbrev != DW_ABRV_SLICETYPE { continue } copychildren(die, prototype) elem = getattr(die, DW_AT_go_elem).data.(*DWDie) substitutetype(die, "array", defptrto(elem)) } } func mkinternaltypename(base string, arg1 string, arg2 string) string { var buf string if arg2 == "" { buf = fmt.Sprintf("%s<%s>", base, arg1) } else { buf = fmt.Sprintf("%s<%s,%s>", base, arg1, arg2) } n := buf return n } // synthesizemaptypes is way too closely married to runtime/hashmap.c const ( MaxKeySize = 128 MaxValSize = 128 BucketSize = 8 ) func synthesizemaptypes(die *DWDie) { hash := walktypedef(defgotype(lookup_or_diag("type.runtime.hmap"))) bucket := walktypedef(defgotype(lookup_or_diag("type.runtime.bmap"))) if hash == nil { return } var a *DWAttr var dwh *DWDie var dwhb *DWDie var dwhk *DWDie var dwhv *DWDie var fld *DWDie var indirect_key int var indirect_val int var keysize int var keytype *DWDie var t *DWDie var valsize int var valtype *DWDie for ; die != nil; die = die.link { if die.abbrev != DW_ABRV_MAPTYPE { continue } keytype = walktypedef(getattr(die, DW_AT_go_key).data.(*DWDie)) valtype = walktypedef(getattr(die, DW_AT_go_elem).data.(*DWDie)) // compute size info like hashmap.c does. a = getattr(keytype, DW_AT_byte_size) if a != nil { keysize = int(a.value) } else { keysize = Thearch.Ptrsize } a = getattr(valtype, DW_AT_byte_size) if a != nil { valsize = int(a.value) } else { valsize = Thearch.Ptrsize } indirect_key = 0 indirect_val = 0 if keysize > MaxKeySize { keysize = Thearch.Ptrsize indirect_key = 1 } if valsize > MaxValSize { valsize = Thearch.Ptrsize indirect_val = 1 } // Construct type to represent an array of BucketSize keys dwhk = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]key", getattr(keytype, DW_AT_name).data.(string), "")) newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(keysize), 0) t = keytype if indirect_key != 0 { t = defptrto(keytype) } newrefattr(dwhk, DW_AT_type, t) fld = newdie(dwhk, DW_ABRV_ARRAYRANGE, "size") newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) // Construct type to represent an array of BucketSize values dwhv = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, mkinternaltypename("[]val", getattr(valtype, DW_AT_name).data.(string), "")) newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize*int64(valsize), 0) t = valtype if indirect_val != 0 { t = defptrto(valtype) } newrefattr(dwhv, DW_AT_type, t) fld = newdie(dwhv, DW_ABRV_ARRAYRANGE, "size") newattr(fld, DW_AT_count, DW_CLS_CONSTANT, BucketSize, 0) newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) // Construct bucket<K,V> dwhb = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("bucket", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) // Copy over all fields except the field "data" from the generic bucket. // "data" will be replaced with keys/values below. copychildrenexcept(dwhb, bucket, find(bucket, "data")) fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys") newrefattr(fld, DW_AT_type, dwhk) newmemberoffsetattr(fld, BucketSize) fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values") newrefattr(fld, DW_AT_type, dwhv) newmemberoffsetattr(fld, BucketSize+BucketSize*int32(keysize)) fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "overflow") newrefattr(fld, DW_AT_type, defptrto(dwhb)) newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))) if Thearch.Regsize > Thearch.Ptrsize { fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "pad") newrefattr(fld, DW_AT_type, mustFind(&dwtypes, "uintptr")) newmemberoffsetattr(fld, BucketSize+BucketSize*(int32(keysize)+int32(valsize))+int32(Thearch.Ptrsize)) } newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize+BucketSize*int64(keysize)+BucketSize*int64(valsize)+int64(Thearch.Regsize), 0) // Construct hash<K,V> dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("hash", getattr(keytype, DW_AT_name).data.(string), getattr(valtype, DW_AT_name).data.(string))) copychildren(dwh, hash) substitutetype(dwh, "buckets", defptrto(dwhb)) substitutetype(dwh, "oldbuckets", defptrto(dwhb)) newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, getattr(hash, DW_AT_byte_size).value, nil) // make map type a pointer to hash<K,V> newrefattr(die, DW_AT_type, defptrto(dwh)) } } func synthesizechantypes(die *DWDie) { sudog := walktypedef(defgotype(lookup_or_diag("type.runtime.sudog"))) waitq := walktypedef(defgotype(lookup_or_diag("type.runtime.waitq"))) hchan := walktypedef(defgotype(lookup_or_diag("type.runtime.hchan"))) if sudog == nil || waitq == nil || hchan == nil { return } sudogsize := int(getattr(sudog, DW_AT_byte_size).value) var a *DWAttr var dwh *DWDie var dws *DWDie var dww *DWDie var elemsize int var elemtype *DWDie for ; die != nil; die = die.link { if die.abbrev != DW_ABRV_CHANTYPE { continue } elemtype = getattr(die, DW_AT_go_elem).data.(*DWDie) a = getattr(elemtype, DW_AT_byte_size) if a != nil { elemsize = int(a.value) } else { elemsize = Thearch.Ptrsize } // sudog<T> dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("sudog", getattr(elemtype, DW_AT_name).data.(string), "")) copychildren(dws, sudog) substitutetype(dws, "elem", elemtype) if elemsize > 8 { elemsize -= 8 } else { elemsize = 0 } newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, int64(sudogsize)+int64(elemsize), nil) // waitq<T> dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("waitq", getattr(elemtype, DW_AT_name).data.(string), "")) 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.(string), "")) copychildren(dwh, hchan) substitutetype(dwh, "recvq", dww) substitutetype(dwh, "sendq", dww) 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 func defdwsymb(sym *LSym, s string, t int, v int64, size int64, ver int, gotype *LSym) { if strings.HasPrefix(s, "go.string.") { return } if strings.HasPrefix(s, "runtime.gcbits.") { return } if strings.HasPrefix(s, "type.") && s != "type.*" && !strings.HasPrefix(s, "type..") { defgotype(sym) return } var dv *DWDie var dt *DWDie switch t { default: return case 'd', 'b', 'D', 'B': dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s) newabslocexprattr(dv, v, sym) if ver == 0 { newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0) } fallthrough case 'a', 'p': dt = defgotype(gotype) } if dv != nil { newrefattr(dv, DW_AT_type, dt) } } func movetomodule(parent *DWDie) { die := dwroot.child.child for die.link != nil { die = die.link } die.link = parent.child } // If the pcln table contains runtime/runtime.go, use that to set gdbscript path. func finddebugruntimepath(s *LSym) { if gdbscript != "" { return } var f *LSym var p string for i := 0; i < s.Pcln.Nfile; i++ { f = s.Pcln.File[i] _ = p if i := strings.Index(f.Name, "runtime/runtime.go"); i >= 0 { gdbscript = f.Name[:i] + "runtime/runtime-gdb.py" break } } } /* * Generate short opcodes when possible, long ones when necessary. * See section 6.2.5 */ const ( LINE_BASE = -1 LINE_RANGE = 4 OPCODE_BASE = 10 ) func putpclcdelta(delta_pc int64, delta_lc int64) { if LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE { var opcode int64 = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc) if OPCODE_BASE <= opcode && opcode < 256 { Cput(uint8(opcode)) return } } if delta_pc != 0 { Cput(DW_LNS_advance_pc) sleb128put(delta_pc) } Cput(DW_LNS_advance_line) sleb128put(delta_lc) Cput(DW_LNS_copy) } func newcfaoffsetattr(die *DWDie, offs int32) { var block [20]byte i := 0 block[i] = DW_OP_call_frame_cfa i++ if offs != 0 { block[i] = DW_OP_consts i++ i += sleb128enc(int64(offs), block[i:]) block[i] = DW_OP_plus i++ } newattr(die, DW_AT_location, DW_CLS_BLOCK, int64(i), block[:i]) } func mkvarname(name string, da int) string { buf := fmt.Sprintf("%s#%d", name, da) n := buf return n } /* * Walk prog table, emit line program and build DIE tree. */ // flush previous compilation unit. func flushunit(dwinfo *DWDie, pc int64, pcsym *LSym, unitstart int64, header_length int32) { if dwinfo != nil && pc != 0 { newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, pcsym) } if unitstart >= 0 { Cput(0) // start extended opcode uleb128put(1) Cput(DW_LNE_end_sequence) here := Cpos() Cseek(unitstart) Thearch.Lput(uint32(here - unitstart - 4)) // unit_length Thearch.Wput(2) // dwarf version Thearch.Lput(uint32(header_length)) // header length starting here Cseek(here) } } func getCompilationDir() string { if dir, err := os.Getwd(); err == nil { return dir } return "/" } func writelines() { if linesec == nil { linesec = Linklookup(Ctxt, ".dwarfline", 0) } linesec.R = linesec.R[:0] unitstart := int64(-1) headerend := int64(-1) epc := int64(0) var epcs *LSym lineo = Cpos() var dwinfo *DWDie flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) unitstart = Cpos() lang := DW_LANG_Go s := Ctxt.Textp dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, "go") newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, int64(lang), 0) newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart-lineo, 0) newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s) // OS X linker requires compilation dir or absolute path in comp unit name to output debug info. compDir := getCompilationDir() newattr(dwinfo, DW_AT_comp_dir, DW_CLS_STRING, int64(len(compDir)), compDir) // Write .debug_line Line Number Program Header (sec 6.2.4) // Fields marked with (*) must be changed for 64-bit dwarf Thearch.Lput(0) // unit_length (*), will be filled in by flushunit. Thearch.Wput(2) // dwarf version (appendix F) Thearch.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 & 0xFF) // line_base Cput(LINE_RANGE) // line_range Cput(OPCODE_BASE) // opcode_base 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(1) // standard_opcode_lengths[5] Cput(0) // standard_opcode_lengths[6] Cput(0) // standard_opcode_lengths[7] Cput(0) // standard_opcode_lengths[8] Cput(1) // standard_opcode_lengths[9] Cput(0) // include_directories (empty) files := make([]*LSym, Ctxt.Nhistfile) for f := Ctxt.Filesyms; f != nil; f = f.Next { files[f.Value-1] = f } for i := 0; int32(i) < Ctxt.Nhistfile; i++ { strnput(files[i].Name, len(files[i].Name)+4) } // 4 zeros: the string termination + 3 fields. Cput(0) // terminate file_names. headerend = Cpos() Cput(0) // start extended opcode uleb128put(1 + int64(Thearch.Ptrsize)) Cput(DW_LNE_set_address) pc := s.Value line := 1 file := 1 if Linkmode == LinkExternal { adddwarfrel(linesec, s, lineo, Thearch.Ptrsize, 0) } else { addrput(pc) } var a *Auto var da int var dt int var dwfunc *DWDie var dws **DWDie var dwvar *DWDie var n string var nn string var offs int64 var pcfile Pciter var pcline Pciter var varhash [HASHSIZE]*DWDie for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { s = Ctxt.Cursym dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s.Name) newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s) epc = s.Value + s.Size epcs = s newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, s) if s.Version == 0 { newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0) } if s.Pcln == nil { continue } finddebugruntimepath(s) pciterinit(Ctxt, &pcfile, &s.Pcln.Pcfile) pciterinit(Ctxt, &pcline, &s.Pcln.Pcline) epc = pc for pcfile.done == 0 && pcline.done == 0 { if epc-s.Value >= int64(pcfile.nextpc) { pciternext(&pcfile) continue } if epc-s.Value >= int64(pcline.nextpc) { pciternext(&pcline) continue } if int32(file) != pcfile.value { Cput(DW_LNS_set_file) uleb128put(int64(pcfile.value)) file = int(pcfile.value) } putpclcdelta(s.Value+int64(pcline.pc)-pc, int64(pcline.value)-int64(line)) pc = s.Value + int64(pcline.pc) line = int(pcline.value) if pcfile.nextpc < pcline.nextpc { epc = int64(pcfile.nextpc) } else { epc = int64(pcline.nextpc) } epc += s.Value } da = 0 dwfunc.hash = varhash[:] // enable indexing of children by name varhash = [HASHSIZE]*DWDie{} for a = s.Autom; a != nil; a = a.Link { switch a.Name { case obj.A_AUTO: dt = DW_ABRV_AUTO offs = int64(a.Aoffset) if !haslinkregister() { offs -= int64(Thearch.Ptrsize) } case obj.A_PARAM: dt = DW_ABRV_PARAM offs = int64(a.Aoffset) if haslinkregister() { offs += int64(Thearch.Ptrsize) } default: continue } if strings.Contains(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 if i := strings.LastIndex(n, "."); i >= 0 { n = n[i+1:] } dwvar = newdie(dwfunc, dt, n) newcfaoffsetattr(dwvar, int32(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, epcs, unitstart, int32(headerend-unitstart-10)) linesize = Cpos() - lineo } /* * Emit .debug_frame */ const ( CIERESERVE = 16 DATAALIGNMENTFACTOR = -4 ) func putpccfadelta(deltapc int64, cfa int64) { Cput(DW_CFA_def_cfa_offset_sf) sleb128put(cfa / DATAALIGNMENTFACTOR) if deltapc < 0x40 { Cput(uint8(DW_CFA_advance_loc + deltapc)) } else if deltapc < 0x100 { Cput(DW_CFA_advance_loc1) Cput(uint8(deltapc)) } else if deltapc < 0x10000 { Cput(DW_CFA_advance_loc2) Thearch.Wput(uint16(deltapc)) } else { Cput(DW_CFA_advance_loc4) Thearch.Lput(uint32(deltapc)) } } func writeframes() { if framesec == nil { framesec = Linklookup(Ctxt, ".dwarfframe", 0) } framesec.R = framesec.R[:0] frameo = Cpos() // Emit the CIE, Section 6.4.1 Thearch.Lput(CIERESERVE) // initial length, must be multiple of thearch.ptrsize Thearch.Lput(0xffffffff) // cid. Cput(3) // dwarf version (appendix F) Cput(0) // augmentation "" uleb128put(1) // code_alignment_factor sleb128put(DATAALIGNMENTFACTOR) // guess uleb128put(int64(Thearch.Dwarfreglr)) // return_address_register Cput(DW_CFA_def_cfa) uleb128put(int64(Thearch.Dwarfregsp)) // register SP (**ABI-dependent, defined in l.h) if haslinkregister() { uleb128put(int64(0)) // offset } else { uleb128put(int64(Thearch.Ptrsize)) // offset } Cput(DW_CFA_offset_extended) uleb128put(int64(Thearch.Dwarfreglr)) // return address if haslinkregister() { uleb128put(int64(0) / DATAALIGNMENTFACTOR) // at cfa - 0 } else { uleb128put(int64(-Thearch.Ptrsize) / DATAALIGNMENTFACTOR) // at cfa - x*4 } // 4 is to exclude the length field. pad := CIERESERVE + frameo + 4 - Cpos() if pad < 0 { Exitf("dwarf: CIERESERVE too small by %d bytes.", -pad) } strnput("", int(pad)) var fdeo int64 var fdesize int64 var nextpc uint32 var pcsp Pciter var s *LSym for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { s = Ctxt.Cursym if s.Pcln == nil { continue } fdeo = Cpos() // Emit a FDE, Section 6.4.1, starting wit a placeholder. Thearch.Lput(0) // length, must be multiple of thearch.ptrsize Thearch.Lput(0) // Pointer to the CIE above, at offset 0 addrput(0) // initial location addrput(0) // address range for pciterinit(Ctxt, &pcsp, &s.Pcln.Pcsp); pcsp.done == 0; pciternext(&pcsp) { nextpc = pcsp.nextpc // pciterinit goes up to the end of the function, // but DWARF expects us to stop just before the end. if int64(nextpc) == s.Size { nextpc-- if nextpc < pcsp.pc { continue } } if haslinkregister() { putpccfadelta(int64(nextpc)-int64(pcsp.pc), int64(pcsp.value)) } else { putpccfadelta(int64(nextpc)-int64(pcsp.pc), int64(Thearch.Ptrsize)+int64(pcsp.value)) } } fdesize = Cpos() - fdeo - 4 // exclude the length field. pad = Rnd(fdesize, int64(Thearch.Ptrsize)) - fdesize strnput("", int(pad)) fdesize += pad // Emit the FDE header for real, Section 6.4.1. Cseek(fdeo) Thearch.Lput(uint32(fdesize)) if Linkmode == LinkExternal { adddwarfrel(framesec, framesym, frameo, 4, 0) adddwarfrel(framesec, s, frameo, Thearch.Ptrsize, 0) } else { Thearch.Lput(0) addrput(s.Value) } addrput(s.Size) Cseek(fdeo + 4 + fdesize) } Cflush() framesize = Cpos() - frameo } /* * Walk DWarfDebugInfoEntries, and emit .debug_info */ const ( COMPUNITHEADERSIZE = 4 + 2 + 4 + 1 ) func writeinfo() { fwdcount = 0 if infosec == nil { infosec = Linklookup(Ctxt, ".dwarfinfo", 0) } infosec.R = infosec.R[:0] if arangessec == nil { arangessec = Linklookup(Ctxt, ".dwarfaranges", 0) } arangessec.R = arangessec.R[:0] var here int64 var unitstart int64 for compunit := dwroot.child; compunit != nil; 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. Thearch.Lput(0) // unit_length (*), will be filled in later. Thearch.Wput(2) // dwarf version (appendix F) // debug_abbrev_offset (*) if Linkmode == LinkExternal { adddwarfrel(infosec, abbrevsym, infoo, 4, 0) } else { Thearch.Lput(0) } Cput(uint8(Thearch.Ptrsize)) // address_size putdie(compunit) here = Cpos() Cseek(unitstart) Thearch.Lput(uint32(here - unitstart - 4)) // exclude the length field. Cseek(here) } Cflush() } /* * Emit .debug_pubnames/_types. _info must have been written before, * because we need die->offs and infoo/infosize; */ func ispubname(die *DWDie) bool { switch die.abbrev { case DW_ABRV_FUNCTION, DW_ABRV_VARIABLE: a := getattr(die, DW_AT_external) return a != nil && a.value != 0 } return false } func ispubtype(die *DWDie) bool { return die.abbrev >= DW_ABRV_NULLTYPE } func writepub(ispub func(*DWDie) bool) int64 { var die *DWDie var dwa *DWAttr var unitstart int64 var unitend int64 var here int64 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) Thearch.Lput(0) // unit_length (*), will be filled in later. Thearch.Wput(2) // dwarf version (appendix F) Thearch.Lput(uint32(unitstart)) // debug_info_offset (of the Comp unit Header) Thearch.Lput(uint32(unitend - unitstart)) // debug_info_length for die = compunit.child; die != nil; die = die.link { if !ispub(die) { continue } Thearch.Lput(uint32(die.offs - unitstart)) dwa = getattr(die, DW_AT_name) strnput(dwa.data.(string), int(dwa.value+1)) } Thearch.Lput(0) here = Cpos() Cseek(sectionstart) Thearch.Lput(uint32(here - sectionstart - 4)) // exclude the length field. Cseek(here) } return sectionstart } /* * emit .debug_aranges. _info must have been written before, * because we need die->offs of dw_globals. */ func writearanges() int64 { var b *DWAttr var e *DWAttr var value int64 sectionstart := Cpos() headersize := int(Rnd(4+2+4+1+1, int64(Thearch.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) Thearch.Lput(uint32(headersize) + 4*uint32(Thearch.Ptrsize) - 4) // unit_length (*) Thearch.Wput(2) // dwarf version (appendix F) value = compunit.offs - COMPUNITHEADERSIZE // debug_info_offset if Linkmode == LinkExternal { adddwarfrel(arangessec, infosym, sectionstart, 4, value) } else { Thearch.Lput(uint32(value)) } Cput(uint8(Thearch.Ptrsize)) // address_size Cput(0) // segment_size strnput("", headersize-(4+2+4+1+1)) // align to thearch.ptrsize if Linkmode == LinkExternal { adddwarfrel(arangessec, b.data.(*LSym), sectionstart, Thearch.Ptrsize, b.value-(b.data.(*LSym)).Value) } else { addrput(b.value) } addrput(e.value - b.value) addrput(0) addrput(0) } Cflush() return sectionstart } func writegdbscript() int64 { sectionstart := Cpos() if gdbscript != "" { Cput(1) // magic 1 byte? strnput(gdbscript, len(gdbscript)+1) Cflush() } return sectionstart } func align(size int64) { if HEADTYPE == obj.Hwindows { // Only Windows PE need section align. strnput("", int(Rnd(size, PEFILEALIGN)-size)) } } func writedwarfreloc(s *LSym) int64 { var i int var r *Reloc start := Cpos() for ri := 0; ri < len(s.R); ri++ { r = &s.R[ri] if Iself { i = Thearch.Elfreloc1(r, int64(r.Off)) } else if HEADTYPE == obj.Hdarwin { i = Thearch.Machoreloc1(r, int64(r.Off)) } else { i = -1 } if i < 0 { Diag("unsupported obj reloc %d/%d to %s", r.Type, r.Siz, r.Sym.Name) } } return start } func addmachodwarfsect(prev *Section, name string) *Section { sect := addsection(&Segdwarf, name, 04) sect.Extnum = prev.Extnum + 1 sym := Linklookup(Ctxt, name, 0) sym.Sect = sect return sect } /* * 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. * */ func Dwarfemitdebugsections() { if Debug['w'] != 0 { // disable dwarf return } if Linkmode == LinkExternal { if !Iself && HEADTYPE != obj.Hdarwin { return } if HEADTYPE == obj.Hdarwin { sect := Segdata.Sect // find the last section. for sect.Next != nil { sect = sect.Next } sect = addmachodwarfsect(sect, ".debug_abbrev") sect = addmachodwarfsect(sect, ".debug_line") sect = addmachodwarfsect(sect, ".debug_frame") sect = addmachodwarfsect(sect, ".debug_info") infosym = Linklookup(Ctxt, ".debug_info", 0) infosym.Hide = 1 abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0) abbrevsym.Hide = 1 linesym = Linklookup(Ctxt, ".debug_line", 0) linesym.Hide = 1 framesym = Linklookup(Ctxt, ".debug_frame", 0) framesym.Hide = 1 } } // For diagnostic messages. newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, int64(len("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") newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer") 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, int64(Thearch.Ptrsize), 0) newattr(die, DW_AT_go_kind, DW_CLS_CONSTANT, obj.KindUintptr, 0) // Needed by the prettyprinter code for interface inspection. defgotype(lookup_or_diag("type.runtime._type")) 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'] != 0 { fmt.Fprintf(&Bso, "%5.2f dwarf pass 2.\n", obj.Cputime()) } Cseek(infoo) writeinfo() if fwdcount > 0 { Exitf("dwarf: unresolved references after first dwarf info pass") } if infoe != Cpos() { Exitf("dwarf: inconsistent second dwarf info pass") } } 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) for Cpos()&7 != 0 { Cput(0) } if HEADTYPE != obj.Hdarwin { dwarfemitreloc() } } func dwarfemitreloc() { if Debug['w'] != 0 { // disable dwarf return } inforeloco = writedwarfreloc(infosec) inforelocsize = Cpos() - inforeloco align(inforelocsize) arangesreloco = writedwarfreloc(arangessec) arangesrelocsize = Cpos() - arangesreloco align(arangesrelocsize) linereloco = writedwarfreloc(linesec) linerelocsize = Cpos() - linereloco align(linerelocsize) framereloco = writedwarfreloc(framesec) framerelocsize = Cpos() - framereloco align(framerelocsize) } /* * Elf. */ const ( ElfStrDebugAbbrev = iota ElfStrDebugAranges ElfStrDebugFrame ElfStrDebugInfo ElfStrDebugLine ElfStrDebugLoc ElfStrDebugMacinfo ElfStrDebugPubNames ElfStrDebugPubTypes ElfStrDebugRanges ElfStrDebugStr ElfStrGDBScripts ElfStrRelDebugInfo ElfStrRelDebugAranges ElfStrRelDebugLine ElfStrRelDebugFrame NElfStrDbg ) var elfstrdbg [NElfStrDbg]int64 func dwarfaddshstrings(shstrtab *LSym) { if Debug['w'] != 0 { // 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") if Linkmode == LinkExternal { switch Thearch.Thechar { case '6', '7', '9': elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rela.debug_info") elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rela.debug_aranges") elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rela.debug_line") elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rela.debug_frame") default: elfstrdbg[ElfStrRelDebugInfo] = Addstring(shstrtab, ".rel.debug_info") elfstrdbg[ElfStrRelDebugAranges] = Addstring(shstrtab, ".rel.debug_aranges") elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rel.debug_line") elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rel.debug_frame") } infosym = Linklookup(Ctxt, ".debug_info", 0) infosym.Hide = 1 abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0) abbrevsym.Hide = 1 linesym = Linklookup(Ctxt, ".debug_line", 0) linesym.Hide = 1 framesym = Linklookup(Ctxt, ".debug_frame", 0) framesym.Hide = 1 } } // Add section symbols for DWARF debug info. This is called before // dwarfaddelfheaders. func dwarfaddelfsectionsyms() { if infosym != nil { infosympos = Cpos() putelfsectionsym(infosym, 0) } if abbrevsym != nil { abbrevsympos = Cpos() putelfsectionsym(abbrevsym, 0) } if linesym != nil { linesympos = Cpos() putelfsectionsym(linesym, 0) } if framesym != nil { framesympos = Cpos() putelfsectionsym(framesym, 0) } } func dwarfaddelfrelocheader(elfstr int, shdata *ElfShdr, off int64, size int64) { sh := newElfShdr(elfstrdbg[elfstr]) switch Thearch.Thechar { case '6', '7', '9': sh.type_ = SHT_RELA default: sh.type_ = SHT_REL } sh.entsize = uint64(Thearch.Ptrsize) * 2 if sh.type_ == SHT_RELA { sh.entsize += uint64(Thearch.Ptrsize) } sh.link = uint32(elfshname(".symtab").shnum) sh.info = uint32(shdata.shnum) sh.off = uint64(off) sh.size = uint64(size) sh.addralign = uint64(Thearch.Ptrsize) } func dwarfaddelfheaders() { if Debug['w'] != 0 { // disable dwarf return } sh := newElfShdr(elfstrdbg[ElfStrDebugAbbrev]) sh.type_ = SHT_PROGBITS sh.off = uint64(abbrevo) sh.size = uint64(abbrevsize) sh.addralign = 1 if abbrevsympos > 0 { putelfsymshndx(abbrevsympos, sh.shnum) } sh = newElfShdr(elfstrdbg[ElfStrDebugLine]) sh.type_ = SHT_PROGBITS sh.off = uint64(lineo) sh.size = uint64(linesize) sh.addralign = 1 if linesympos > 0 { putelfsymshndx(linesympos, sh.shnum) } shline := sh sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]) sh.type_ = SHT_PROGBITS sh.off = uint64(frameo) sh.size = uint64(framesize) sh.addralign = 1 if framesympos > 0 { putelfsymshndx(framesympos, sh.shnum) } shframe := sh sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]) sh.type_ = SHT_PROGBITS sh.off = uint64(infoo) sh.size = uint64(infosize) sh.addralign = 1 if infosympos > 0 { putelfsymshndx(infosympos, sh.shnum) } shinfo := sh if pubnamessize > 0 { sh := newElfShdr(elfstrdbg[ElfStrDebugPubNames]) sh.type_ = SHT_PROGBITS sh.off = uint64(pubnameso) sh.size = uint64(pubnamessize) sh.addralign = 1 } if pubtypessize > 0 { sh := newElfShdr(elfstrdbg[ElfStrDebugPubTypes]) sh.type_ = SHT_PROGBITS sh.off = uint64(pubtypeso) sh.size = uint64(pubtypessize) sh.addralign = 1 } var sharanges *ElfShdr if arangessize != 0 { sh := newElfShdr(elfstrdbg[ElfStrDebugAranges]) sh.type_ = SHT_PROGBITS sh.off = uint64(arangeso) sh.size = uint64(arangessize) sh.addralign = 1 sharanges = sh } if gdbscriptsize != 0 { sh := newElfShdr(elfstrdbg[ElfStrGDBScripts]) sh.type_ = SHT_PROGBITS sh.off = uint64(gdbscripto) sh.size = uint64(gdbscriptsize) sh.addralign = 1 } if inforelocsize != 0 { dwarfaddelfrelocheader(ElfStrRelDebugInfo, shinfo, inforeloco, inforelocsize) } if arangesrelocsize != 0 { dwarfaddelfrelocheader(ElfStrRelDebugAranges, sharanges, arangesreloco, arangesrelocsize) } if linerelocsize != 0 { dwarfaddelfrelocheader(ElfStrRelDebugLine, shline, linereloco, linerelocsize) } if framerelocsize != 0 { dwarfaddelfrelocheader(ElfStrRelDebugFrame, shframe, framereloco, framerelocsize) } } /* * Macho */ func dwarfaddmachoheaders(ms *MachoSeg) { if Debug['w'] != 0 { // disable dwarf return } // Zero vsize segments won't be loaded in memory, even so they // have to be page aligned in the file. fakestart := Rnd(int64(Segdwarf.Fileoff), 0x1000) addr := Segdata.Vaddr + Segdata.Length nsect := 4 if pubnamessize > 0 { nsect++ } if pubtypessize > 0 { nsect++ } if arangessize > 0 { nsect++ } if gdbscriptsize > 0 { nsect++ } if Linkmode != LinkExternal { ms = newMachoSeg("__DWARF", nsect) ms.fileoffset = uint64(fakestart) ms.filesize = Segdwarf.Filelen ms.vaddr = addr } msect := newMachoSect(ms, "__debug_abbrev", "__DWARF") msect.off = uint32(abbrevo) msect.size = uint64(abbrevsize) msect.addr = addr addr += msect.size msect.flag = 0x02000000 if abbrevsym != nil { abbrevsym.Value = int64(msect.addr) } msect = newMachoSect(ms, "__debug_line", "__DWARF") msect.off = uint32(lineo) msect.size = uint64(linesize) msect.addr = addr addr += msect.size msect.flag = 0x02000000 if linesym != nil { linesym.Value = int64(msect.addr) } if linerelocsize > 0 { msect.nreloc = uint32(len(linesec.R)) msect.reloc = uint32(linereloco) } msect = newMachoSect(ms, "__debug_frame", "__DWARF") msect.off = uint32(frameo) msect.size = uint64(framesize) msect.addr = addr addr += msect.size msect.flag = 0x02000000 if framesym != nil { framesym.Value = int64(msect.addr) } if framerelocsize > 0 { msect.nreloc = uint32(len(framesec.R)) msect.reloc = uint32(framereloco) } msect = newMachoSect(ms, "__debug_info", "__DWARF") msect.off = uint32(infoo) msect.size = uint64(infosize) msect.addr = addr addr += msect.size msect.flag = 0x02000000 if infosym != nil { infosym.Value = int64(msect.addr) } if inforelocsize > 0 { msect.nreloc = uint32(len(infosec.R)) msect.reloc = uint32(inforeloco) } if pubnamessize > 0 { msect := newMachoSect(ms, "__debug_pubnames", "__DWARF") msect.off = uint32(pubnameso) msect.size = uint64(pubnamessize) msect.addr = addr addr += msect.size msect.flag = 0x02000000 } if pubtypessize > 0 { msect := newMachoSect(ms, "__debug_pubtypes", "__DWARF") msect.off = uint32(pubtypeso) msect.size = uint64(pubtypessize) msect.addr = addr addr += msect.size msect.flag = 0x02000000 } if arangessize > 0 { msect := newMachoSect(ms, "__debug_aranges", "__DWARF") msect.off = uint32(arangeso) msect.size = uint64(arangessize) msect.addr = addr addr += msect.size msect.flag = 0x02000000 if arangesrelocsize > 0 { msect.nreloc = uint32(len(arangessec.R)) msect.reloc = uint32(arangesreloco) } } // TODO(lvd) fix gdb/python to load MachO (16 char section name limit) if gdbscriptsize > 0 { msect := newMachoSect(ms, "__debug_gdb_scripts", "__DWARF") msect.off = uint32(gdbscripto) msect.size = uint64(gdbscriptsize) msect.addr = addr addr += msect.size msect.flag = 0x02000000 } } /* * Windows PE */ func dwarfaddpeheaders() { if Debug['w'] != 0 { // 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) }