| // 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 appendUleb128(b []byte, v uint64) []byte { |
| for { |
| c := uint8(v & 0x7f) |
| v >>= 7 |
| if v != 0 { |
| c |= 0x80 |
| } |
| b = append(b, c) |
| if c&0x80 == 0 { |
| break |
| } |
| } |
| return b |
| } |
| |
| func appendSleb128(b []byte, v int64) []byte { |
| for { |
| c := uint8(v & 0x7f) |
| s := uint8(v & 0x40) |
| v >>= 7 |
| if (v != -1 || s == 0) && (v != 0 || s != 0) { |
| c |= 0x80 |
| } |
| b = append(b, c) |
| if c&0x80 == 0 { |
| break |
| } |
| } |
| return b |
| } |
| |
| var encbuf [10]byte |
| |
| func uleb128put(v int64) { |
| b := appendUleb128(encbuf[:0], uint64(v)) |
| Cwrite(b) |
| } |
| |
| func sleb128put(v int64) { |
| b := appendSleb128(encbuf[:0], v) |
| Cwrite(b) |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| // 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 map[string]*DWDie // optional index of DWAttr by name, enabled by mkindex() |
| } |
| |
| /* |
| * 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 { |
| parent.hash[name] = die |
| } |
| |
| return die |
| } |
| |
| func mkindex(die *DWDie) { |
| die.hash = make(map[string]*DWDie) |
| } |
| |
| 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 prev *DWDie |
| for ; die != prev; prev, die = die, walktypedef(die) { |
| if die.hash == nil { |
| for a := die.child; a != nil; a = a.link { |
| if name == getattr(a, DW_AT_name).data { |
| return a |
| } |
| } |
| continue |
| } |
| if a := die.hash[name]; a != nil { |
| return a |
| } |
| } |
| 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 |
| b := append(block[:0], DW_OP_plus_uconst) |
| b = appendUleb128(b, uint64(offs)) |
| newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, int64(len(b)), b) |
| } |
| |
| // 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) { |
| 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) { |
| 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.stringStructDWARF"))) |
| 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 |
| } |
| |
| 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 |
| } |
| |
| 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. |
| keysize, valsize := Thearch.Ptrsize, Thearch.Ptrsize |
| a := getattr(keytype, DW_AT_byte_size) |
| if a != nil { |
| keysize = int(a.value) |
| } |
| a = getattr(valtype, DW_AT_byte_size) |
| if a != nil { |
| valsize = int(a.value) |
| } |
| indirect_key, indirect_val := false, false |
| if keysize > MaxKeySize { |
| keysize = Thearch.Ptrsize |
| indirect_key = true |
| } |
| if valsize > MaxValSize { |
| valsize = Thearch.Ptrsize |
| indirect_val = true |
| } |
| |
| // 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 { |
| 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 { |
| 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) |
| |
| for ; die != nil; die = die.link { |
| if die.abbrev != DW_ABRV_CHANTYPE { |
| continue |
| } |
| elemsize := Thearch.Ptrsize |
| elemtype := getattr(die, DW_AT_go_elem).data.(*DWDie) |
| a := getattr(elemtype, DW_AT_byte_size) |
| if a != nil { |
| elemsize = int(a.value) |
| } |
| |
| // 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 |
| } |
| |
| for i := range s.Pcln.File { |
| f := s.Pcln.File[i] |
| 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 |
| b := append(block[:0], DW_OP_call_frame_cfa) |
| |
| if offs != 0 { |
| b = append(b, DW_OP_consts) |
| b = appendSleb128(b, int64(offs)) |
| b = append(b, DW_OP_plus) |
| } |
| |
| newattr(die, DW_AT_location, DW_CLS_BLOCK, int64(len(b)), b) |
| } |
| |
| 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 pcfile Pciter |
| var pcline Pciter |
| 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 |
| } |
| |
| var ( |
| dt, da int |
| offs int64 |
| ) |
| for _, a := range s.Autom { |
| 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) + Ctxt.FixedFrameSize() |
| |
| default: |
| continue |
| } |
| |
| if strings.Contains(a.Asym.Name, ".autotmp_") { |
| continue |
| } |
| var n string |
| 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. |
| 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 |
| dws := &dwfunc.child |
| for ; *dws != nil; dws = &(*dws).link { |
| if offs > getattr(*dws, DW_AT_internal_location).value { |
| break |
| } |
| } |
| dwvar.link = *dws |
| *dws = dwvar |
| |
| da++ |
| } |
| } |
| |
| flushunit(dwinfo, epc, epcs, unitstart, int32(headerend-unitstart-10)) |
| linesize = Cpos() - lineo |
| } |
| |
| /* |
| * Emit .debug_frame |
| */ |
| const ( |
| CIERESERVE = 16 |
| DATAALIGNMENTFACTOR = -4 |
| ) |
| |
| // appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice. |
| func appendPCDeltaCFA(b []byte, deltapc, cfa int64) []byte { |
| b = append(b, DW_CFA_def_cfa_offset_sf) |
| b = appendSleb128(b, cfa/DATAALIGNMENTFACTOR) |
| |
| switch { |
| case deltapc < 0x40: |
| b = append(b, uint8(DW_CFA_advance_loc+deltapc)) |
| case deltapc < 0x100: |
| b = append(b, DW_CFA_advance_loc1) |
| b = append(b, uint8(deltapc)) |
| case deltapc < 0x10000: |
| b = append(b, DW_CFA_advance_loc2) |
| b = Thearch.Append16(b, uint16(deltapc)) |
| default: |
| b = append(b, DW_CFA_advance_loc4) |
| b = Thearch.Append32(b, uint32(deltapc)) |
| } |
| return b |
| } |
| |
| 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 deltaBuf []byte |
| var pcsp Pciter |
| for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next { |
| s := Ctxt.Cursym |
| if s.Pcln == nil { |
| continue |
| } |
| |
| // Emit a FDE, Section 6.4.1. |
| // First build the section contents into a byte buffer. |
| deltaBuf = deltaBuf[:0] |
| 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() { |
| deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(pcsp.value)) |
| } else { |
| deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(Thearch.Ptrsize)+int64(pcsp.value)) |
| } |
| } |
| pad := int(Rnd(int64(len(deltaBuf)), int64(Thearch.Ptrsize))) - len(deltaBuf) |
| deltaBuf = append(deltaBuf, zeros[:pad]...) |
| |
| // Emit the FDE header, Section 6.4.1. |
| // 4 bytes: length, must be multiple of thearch.ptrsize |
| // 4 bytes: Pointer to the CIE above, at offset 0 |
| // ptrsize: initial location |
| // ptrsize: address range |
| Thearch.Lput(uint32(4 + 2*Thearch.Ptrsize + len(deltaBuf))) // length (excludes itself) |
| if Linkmode == LinkExternal { |
| adddwarfrel(framesec, framesym, frameo, 4, 0) // CIE offset |
| adddwarfrel(framesec, s, frameo, Thearch.Ptrsize, 0) // initial location |
| } else { |
| Thearch.Lput(0) // CIE offset |
| addrput(s.Value) // initial location |
| } |
| addrput(s.Size) // address range |
| |
| Cwrite(deltaBuf) |
| } |
| |
| 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] |
| |
| 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 { |
| sectionstart := Cpos() |
| |
| for compunit := dwroot.child; compunit != nil; compunit = compunit.link { |
| unitend := infoo + infosize |
| unitstart := compunit.offs - COMPUNITHEADERSIZE |
| if compunit.link != nil { |
| unitend = compunit.link.offs - COMPUNITHEADERSIZE |
| } |
| |
| // 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 { |
| sectionstart := Cpos() |
| // The first tuple is aligned to a multiple of the size of a single tuple |
| // (twice the size of an address) |
| headersize := int(Rnd(4+2+4+1+1, int64(Thearch.Ptrsize*2))) // 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 { |
| start := Cpos() |
| for ri := 0; ri < len(s.R); ri++ { |
| r := &s.R[ri] |
| i := -1 |
| if Iself { |
| i = Thearch.Elfreloc1(r, int64(r.Off)) |
| } else if HEADTYPE == obj.Hdarwin { |
| i = Thearch.Machoreloc1(r, int64(r.Off)) |
| } |
| 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.Attr |= AttrHidden |
| |
| abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0) |
| abbrevsym.Attr |= AttrHidden |
| |
| linesym = Linklookup(Ctxt, ".debug_line", 0) |
| linesym.Attr |= AttrHidden |
| |
| framesym = Linklookup(Ctxt, ".debug_frame", 0) |
| framesym.Attr |= AttrHidden |
| } |
| } |
| |
| // 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 '0', '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.Attr |= AttrHidden |
| |
| abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0) |
| abbrevsym.Attr |= AttrHidden |
| |
| linesym = Linklookup(Ctxt, ".debug_line", 0) |
| linesym.Attr |= AttrHidden |
| |
| framesym = Linklookup(Ctxt, ".debug_frame", 0) |
| framesym.Attr |= AttrHidden |
| } |
| } |
| |
| // 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 '0', '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) |
| } |