func writeinfo(ctxt *Link, syms []*Symbol, funcs []*Symbol) []*Symbol { if infosec == nil { infosec = Linklookup(ctxt, ".debug_info", 0) } infosec.R = infosec.R[:0] infosec.Type = obj.SDWARFINFO infosec.Attr |= AttrReachable syms = append(syms, infosec) if arangessec == nil { arangessec = Linklookup(ctxt, ".dwarfaranges", 0) } arangessec.R = arangessec.R[:0] var dwarfctxt dwarf.Context = dwctxt{ctxt} for compunit := dwroot.Child; compunit != nil; compunit = compunit.Link { s := dtolsym(compunit.Sym) // 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. Adduint32(ctxt, s, 0) // unit_length (*), will be filled in later. Adduint16(ctxt, s, 2) // dwarf version (appendix F) // debug_abbrev_offset (*) adddwarfref(ctxt, s, abbrevsym, 4) Adduint8(ctxt, s, uint8(SysArch.PtrSize)) // address_size dwarf.Uleb128put(dwarfctxt, s, int64(compunit.Abbrev)) dwarf.PutAttrs(dwarfctxt, s, compunit.Abbrev, compunit.Attr) cu := []*Symbol{s} if funcs != nil { cu = append(cu, funcs...) funcs = nil } cu = putdies(ctxt, dwarfctxt, cu, compunit.Child) var cusize int64 for _, child := range cu { cusize += child.Size } cusize -= 4 // exclude the length field. setuint32(ctxt, s, 0, uint32(cusize)) newattr(compunit, dwarf.DW_AT_byte_size, dwarf.DW_CLS_CONSTANT, cusize, 0) syms = append(syms, cu...) } return syms }
func putdie(linkctxt *Link, ctxt dwarf.Context, syms []*Symbol, die *dwarf.DWDie) []*Symbol { s := dtolsym(die.Sym) if s == nil { s = syms[len(syms)-1] } else { if s.Attr.OnList() { log.Fatalf("symbol %s listed multiple times", s.Name) } s.Attr |= AttrOnList syms = append(syms, s) } dwarf.Uleb128put(ctxt, s, int64(die.Abbrev)) dwarf.PutAttrs(ctxt, s, die.Abbrev, die.Attr) if dwarf.HasChildren(die) { return putdies(linkctxt, ctxt, syms, die.Child) } return syms }
func writelines(ctxt *Link, syms []*Symbol) ([]*Symbol, []*Symbol) { var dwarfctxt dwarf.Context = dwctxt{ctxt} if linesec == nil { linesec = Linklookup(ctxt, ".debug_line", 0) } linesec.Type = obj.SDWARFSECT linesec.R = linesec.R[:0] ls := linesec syms = append(syms, ls) var funcs []*Symbol unitstart := int64(-1) headerstart := int64(-1) headerend := int64(-1) epc := int64(0) var epcs *Symbol var dwinfo *dwarf.DWDie lang := dwarf.DW_LANG_Go s := ctxt.Textp[0] dwinfo = newdie(ctxt, &dwroot, dwarf.DW_ABRV_COMPUNIT, "go", 0) newattr(dwinfo, dwarf.DW_AT_language, dwarf.DW_CLS_CONSTANT, int64(lang), 0) newattr(dwinfo, dwarf.DW_AT_stmt_list, dwarf.DW_CLS_PTR, 0, linesec) newattr(dwinfo, dwarf.DW_AT_low_pc, dwarf.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, dwarf.DW_AT_comp_dir, dwarf.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 unit_length_offset := ls.Size Adduint32(ctxt, ls, 0) // unit_length (*), filled in at end. unitstart = ls.Size Adduint16(ctxt, ls, 2) // dwarf version (appendix F) header_length_offset := ls.Size Adduint32(ctxt, ls, 0) // header_length (*), filled in at end. headerstart = ls.Size // cpos == unitstart + 4 + 2 + 4 Adduint8(ctxt, ls, 1) // minimum_instruction_length Adduint8(ctxt, ls, 1) // default_is_stmt Adduint8(ctxt, ls, LINE_BASE&0xFF) // line_base Adduint8(ctxt, ls, LINE_RANGE) // line_range Adduint8(ctxt, ls, OPCODE_BASE) // opcode_base Adduint8(ctxt, ls, 0) // standard_opcode_lengths[1] Adduint8(ctxt, ls, 1) // standard_opcode_lengths[2] Adduint8(ctxt, ls, 1) // standard_opcode_lengths[3] Adduint8(ctxt, ls, 1) // standard_opcode_lengths[4] Adduint8(ctxt, ls, 1) // standard_opcode_lengths[5] Adduint8(ctxt, ls, 0) // standard_opcode_lengths[6] Adduint8(ctxt, ls, 0) // standard_opcode_lengths[7] Adduint8(ctxt, ls, 0) // standard_opcode_lengths[8] Adduint8(ctxt, ls, 1) // standard_opcode_lengths[9] Adduint8(ctxt, ls, 0) // include_directories (empty) for _, f := range ctxt.Filesyms { Addstring(ctxt, ls, f.Name) Adduint8(ctxt, ls, 0) Adduint8(ctxt, ls, 0) Adduint8(ctxt, ls, 0) } // 4 zeros: the string termination + 3 fields. Adduint8(ctxt, ls, 0) // terminate file_names. headerend = ls.Size Adduint8(ctxt, ls, 0) // start extended opcode dwarf.Uleb128put(dwarfctxt, ls, 1+int64(SysArch.PtrSize)) Adduint8(ctxt, ls, dwarf.DW_LNE_set_address) pc := s.Value line := 1 file := 1 Addaddr(ctxt, ls, s) var pcfile Pciter var pcline Pciter for _, ctxt.Cursym = range ctxt.Textp { s := ctxt.Cursym epc = s.Value + s.Size epcs = s dsym := Linklookup(ctxt, dwarf.InfoPrefix+s.Name, int(s.Version)) dsym.Attr |= AttrHidden dsym.Type = obj.SDWARFINFO for _, r := range dsym.R { if r.Type == obj.R_DWARFREF && r.Sym.Size == 0 { if Buildmode == BuildmodeShared { // These type symbols may not be present in BuildmodeShared. Skip. continue } n := nameFromDIESym(r.Sym) defgotype(ctxt, Linklookup(ctxt, "type."+n, 0)) } } funcs = append(funcs, dsym) if s.FuncInfo == nil { continue } finddebugruntimepath(s) pciterinit(ctxt, &pcfile, &s.FuncInfo.Pcfile) pciterinit(ctxt, &pcline, &s.FuncInfo.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 { Adduint8(ctxt, ls, dwarf.DW_LNS_set_file) dwarf.Uleb128put(dwarfctxt, ls, int64(pcfile.value)) file = int(pcfile.value) } putpclcdelta(ctxt, dwarfctxt, ls, 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 } } Adduint8(ctxt, ls, 0) // start extended opcode dwarf.Uleb128put(dwarfctxt, ls, 1) Adduint8(ctxt, ls, dwarf.DW_LNE_end_sequence) newattr(dwinfo, dwarf.DW_AT_high_pc, dwarf.DW_CLS_ADDRESS, epc+1, epcs) setuint32(ctxt, ls, unit_length_offset, uint32(ls.Size-unitstart)) setuint32(ctxt, ls, header_length_offset, uint32(headerend-headerstart)) return syms, funcs }
func writeframes(ctxt *Link, syms []*Symbol) []*Symbol { var dwarfctxt dwarf.Context = dwctxt{ctxt} if framesec == nil { framesec = Linklookup(ctxt, ".debug_frame", 0) } framesec.Type = obj.SDWARFSECT framesec.R = framesec.R[:0] fs := framesec syms = append(syms, fs) // Emit the CIE, Section 6.4.1 cieReserve := uint32(16) if haslinkregister(ctxt) { cieReserve = 32 } Adduint32(ctxt, fs, cieReserve) // initial length, must be multiple of thearch.ptrsize Adduint32(ctxt, fs, 0xffffffff) // cid. Adduint8(ctxt, fs, 3) // dwarf version (appendix F) Adduint8(ctxt, fs, 0) // augmentation "" dwarf.Uleb128put(dwarfctxt, fs, 1) // code_alignment_factor dwarf.Sleb128put(dwarfctxt, fs, dataAlignmentFactor) // all CFI offset calculations include multiplication with this factor dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfreglr)) // return_address_register Adduint8(ctxt, fs, dwarf.DW_CFA_def_cfa) // Set the current frame address.. dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfregsp)) // ...to use the value in the platform's SP register (defined in l.go)... if haslinkregister(ctxt) { dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...plus a 0 offset. Adduint8(ctxt, fs, dwarf.DW_CFA_same_value) // The platform's link register is unchanged during the prologue. dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfreglr)) Adduint8(ctxt, fs, dwarf.DW_CFA_val_offset) // The previous value... dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfregsp)) // ...of the platform's SP register... dwarf.Uleb128put(dwarfctxt, fs, int64(0)) // ...is CFA+0. } else { dwarf.Uleb128put(dwarfctxt, fs, int64(SysArch.PtrSize)) // ...plus the word size (because the call instruction implicitly adds one word to the frame). Adduint8(ctxt, fs, dwarf.DW_CFA_offset_extended) // The previous value... dwarf.Uleb128put(dwarfctxt, fs, int64(Thearch.Dwarfreglr)) // ...of the return address... dwarf.Uleb128put(dwarfctxt, fs, int64(-SysArch.PtrSize)/dataAlignmentFactor) // ...is saved at [CFA - (PtrSize/4)]. } // 4 is to exclude the length field. pad := int64(cieReserve) + 4 - fs.Size if pad < 0 { Exitf("dwarf: cieReserve too small by %d bytes.", -pad) } Addbytes(ctxt, fs, zeros[:pad]) var deltaBuf []byte var pcsp Pciter for _, ctxt.Cursym = range ctxt.Textp { s := ctxt.Cursym if s.FuncInfo == 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.FuncInfo.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(ctxt) { // TODO(bryanpkc): This is imprecise. In general, the instruction // that stores the return address to the stack frame is not the // same one that allocates the frame. if pcsp.value > 0 { // The return address is preserved at (CFA-frame_size) // after a stack frame has been allocated. deltaBuf = append(deltaBuf, dwarf.DW_CFA_offset_extended_sf) deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(Thearch.Dwarfreglr)) deltaBuf = dwarf.AppendSleb128(deltaBuf, -int64(pcsp.value)/dataAlignmentFactor) } else { // The return address is restored into the link register // when a stack frame has been de-allocated. deltaBuf = append(deltaBuf, dwarf.DW_CFA_same_value) deltaBuf = dwarf.AppendUleb128(deltaBuf, uint64(Thearch.Dwarfreglr)) } deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(pcsp.value)) } else { deltaBuf = appendPCDeltaCFA(deltaBuf, int64(nextpc)-int64(pcsp.pc), int64(SysArch.PtrSize)+int64(pcsp.value)) } } pad := int(Rnd(int64(len(deltaBuf)), int64(SysArch.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 Adduint32(ctxt, fs, uint32(4+2*SysArch.PtrSize+len(deltaBuf))) // length (excludes itself) if Linkmode == LinkExternal { adddwarfref(ctxt, fs, framesec, 4) } else { Adduint32(ctxt, fs, 0) // CIE offset } Addaddr(ctxt, fs, s) adduintxx(ctxt, fs, uint64(s.Size), SysArch.PtrSize) // address range Addbytes(ctxt, fs, deltaBuf) } return syms }
func putpclcdelta(linkctxt *Link, ctxt dwarf.Context, s *Symbol, deltaPC uint64, deltaLC int64) { // Choose a special opcode that minimizes the number of bytes needed to // encode the remaining PC delta and LC delta. var opcode int64 if deltaLC < LINE_BASE { if deltaPC >= PC_RANGE { opcode = OPCODE_BASE + (LINE_RANGE * PC_RANGE) } else { opcode = OPCODE_BASE + (LINE_RANGE * int64(deltaPC)) } } else if deltaLC < LINE_BASE+LINE_RANGE { if deltaPC >= PC_RANGE { opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * PC_RANGE) if opcode > 255 { opcode -= LINE_RANGE } } else { opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * int64(deltaPC)) } } else { if deltaPC <= PC_RANGE { opcode = OPCODE_BASE + (LINE_RANGE - 1) + (LINE_RANGE * int64(deltaPC)) if opcode > 255 { opcode = 255 } } else { // Use opcode 249 (pc+=23, lc+=5) or 255 (pc+=24, lc+=1). // // Let x=deltaPC-PC_RANGE. If we use opcode 255, x will be the remaining // deltaPC that we need to encode separately before emitting 255. If we // use opcode 249, we will need to encode x+1. If x+1 takes one more // byte to encode than x, then we use opcode 255. // // In all other cases x and x+1 take the same number of bytes to encode, // so we use opcode 249, which may save us a byte in encoding deltaLC, // for similar reasons. switch deltaPC - PC_RANGE { // PC_RANGE is the largest deltaPC we can encode in one byte, using // DW_LNS_const_add_pc. // // (1<<16)-1 is the largest deltaPC we can encode in three bytes, using // DW_LNS_fixed_advance_pc. // // (1<<(7n))-1 is the largest deltaPC we can encode in n+1 bytes for // n=1,3,4,5,..., using DW_LNS_advance_pc. case PC_RANGE, (1 << 7) - 1, (1 << 16) - 1, (1 << 21) - 1, (1 << 28) - 1, (1 << 35) - 1, (1 << 42) - 1, (1 << 49) - 1, (1 << 56) - 1, (1 << 63) - 1: opcode = 255 default: opcode = OPCODE_BASE + LINE_RANGE*PC_RANGE - 1 // 249 } } } if opcode < OPCODE_BASE || opcode > 255 { panic(fmt.Sprintf("produced invalid special opcode %d", opcode)) } // Subtract from deltaPC and deltaLC the amounts that the opcode will add. deltaPC -= uint64((opcode - OPCODE_BASE) / LINE_RANGE) deltaLC -= int64((opcode-OPCODE_BASE)%LINE_RANGE + LINE_BASE) // Encode deltaPC. if deltaPC != 0 { if deltaPC <= PC_RANGE { // Adjust the opcode so that we can use the 1-byte DW_LNS_const_add_pc // instruction. opcode -= LINE_RANGE * int64(PC_RANGE-deltaPC) if opcode < OPCODE_BASE { panic(fmt.Sprintf("produced invalid special opcode %d", opcode)) } Adduint8(linkctxt, s, dwarf.DW_LNS_const_add_pc) } else if (1<<14) <= deltaPC && deltaPC < (1<<16) { Adduint8(linkctxt, s, dwarf.DW_LNS_fixed_advance_pc) Adduint16(linkctxt, s, uint16(deltaPC)) } else { Adduint8(linkctxt, s, dwarf.DW_LNS_advance_pc) dwarf.Uleb128put(ctxt, s, int64(deltaPC)) } } // Encode deltaLC. if deltaLC != 0 { Adduint8(linkctxt, s, dwarf.DW_LNS_advance_line) dwarf.Sleb128put(ctxt, s, deltaLC) } // Output the special opcode. Adduint8(linkctxt, s, uint8(opcode)) }