Commit 8506b7d4 authored by Jeremy Faller's avatar Jeremy Faller

cmd/link: switch linker over to new debug lines from compiler

This switches the linker over to using the new debug_lines data
generated in the compiler.

Change-Id: If8362d6fcea7db60aaebab670ed6f702ab1c4908
Reviewed-on: https://go-review.googlesource.com/c/go/+/191968
Run-TryBot: Jeremy Faller <jeremy@golang.org>
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: default avatarAustin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 770a1354
......@@ -53,10 +53,8 @@ func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) {
line := 1
file := 1
dctxt.AddUint8(lines, 0) // start extended opcode
dwarf.Uleb128put(dctxt, lines, 1+int64(ctxt.Arch.PtrSize))
dctxt.AddUint8(lines, dwarf.DW_LNE_set_address)
dctxt.AddAddress(lines, nil, pc)
// The linker will insert the DW_LNE_set_address once determined; therefore,
// it's omitted here.
// Generate the actual line information.
// We use the pcline and pcfile to generate this section, and it's suboptimal.
......
......@@ -17,6 +17,7 @@ import (
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
"cmd/link/internal/sym"
"fmt"
......@@ -1000,96 +1001,6 @@ const (
OPCODE_BASE = 11
)
func putpclcdelta(linkctxt *Link, ctxt dwarf.Context, s *sym.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 -= (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))
}
s.AddUint8(dwarf.DW_LNS_const_add_pc)
} else if (1<<14) <= deltaPC && deltaPC < (1<<16) {
s.AddUint8(dwarf.DW_LNS_fixed_advance_pc)
s.AddUint16(linkctxt.Arch, uint16(deltaPC))
} else {
s.AddUint8(dwarf.DW_LNS_advance_pc)
dwarf.Uleb128put(ctxt, s, int64(deltaPC))
}
}
// Encode deltaLC.
if deltaLC != 0 {
s.AddUint8(dwarf.DW_LNS_advance_line)
dwarf.Sleb128put(ctxt, s, deltaLC)
}
// Output the special opcode.
s.AddUint8(uint8(opcode))
}
/*
* Walk prog table, emit line program and build DIE tree.
*/
......@@ -1155,34 +1066,40 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
ls.AddUint8(0) // standard_opcode_lengths[10]
ls.AddUint8(0) // include_directories (empty)
// Create the file table. fileNums maps from global file
// indexes (created by numberfile) to CU-local indexes.
fileNums := make(map[int]int)
for _, s := range unit.Textp { // textp has been dead-code-eliminated already.
dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true)
for _, f := range s.FuncInfo.File {
if _, ok := fileNums[int(f.Value)]; ok {
continue
// Copy over the file table.
fileNums := make(map[string]int)
for i, name := range unit.DWARFFileTable {
if len(name) != 0 {
if strings.HasPrefix(name, src.FileSymPrefix) {
name = name[len(src.FileSymPrefix):]
}
// File indexes are 1-based.
fileNums[int(f.Value)] = len(fileNums) + 1
Addstring(ls, f.Name)
ls.AddUint8(0)
ls.AddUint8(0)
ls.AddUint8(0)
name = expandGoroot(name)
} else {
// Can't have empty filenames, and having a unique filename is quite useful
// for debugging.
name = fmt.Sprintf("<missing>_%d", i)
}
fileNums[name] = i + 1
dwarfctxt.AddString(ls, name)
ls.AddUint8(0)
ls.AddUint8(0)
ls.AddUint8(0)
}
// Grab files for inlined functions.
// TODO: With difficulty, this could be moved into the compiler.
for _, s := range unit.Textp {
dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true)
for ri := 0; ri < len(dsym.R); ri++ {
r := &dsym.R[ri]
if r.Type != objabi.R_DWARFFILEREF {
continue
}
// A file that is only mentioned in an inlined subroutine will appear
// as a R_DWARFFILEREF but not in s.FuncInfo.File
if _, ok := fileNums[int(r.Sym.Value)]; ok {
name := r.Sym.Name
if _, ok := fileNums[name]; ok {
continue
}
fileNums[int(r.Sym.Value)] = len(fileNums) + 1
Addstring(ls, r.Sym.Name)
fileNums[name] = len(fileNums) + 1
dwarfctxt.AddString(ls, name)
ls.AddUint8(0)
ls.AddUint8(0)
ls.AddUint8(0)
......@@ -1194,95 +1111,31 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
// terminate file_names.
headerend = ls.Size
ls.AddUint8(0) // start extended opcode
dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize))
ls.AddUint8(dwarf.DW_LNE_set_address)
s := unit.Textp[0]
pc := s.Value
line := 1
file := 1
ls.AddAddr(ctxt.Arch, s)
pcfile := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
pcline := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
pcstmt := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
for i, s := range unit.Textp {
// Output the state machine for each function remaining.
var lastAddr int64
for _, s := range unit.Textp {
finddebugruntimepath(s)
pcfile.Init(s.FuncInfo.Pcfile.P)
pcline.Init(s.FuncInfo.Pcline.P)
isStmtSym := dwarfFuncSym(ctxt, s, dwarf.IsStmtPrefix, false)
if isStmtSym != nil && len(isStmtSym.P) > 0 {
pcstmt.Init(isStmtSym.P)
} else {
// Assembly files lack a pcstmt section, we assume that every instruction
// is a valid statement.
pcstmt.Done = true
pcstmt.Value = 1
}
var thispc uint32
// TODO this loop looks like it could exit with work remaining.
for !pcfile.Done && !pcline.Done {
// Only changed if it advanced
if int32(file) != pcfile.Value {
ls.AddUint8(dwarf.DW_LNS_set_file)
idx, ok := fileNums[int(pcfile.Value)]
if !ok {
Exitf("pcln table file missing from DWARF line table %q", s.Unit.Lib.Pkg)
}
dwarf.Uleb128put(dwarfctxt, ls, int64(idx))
file = int(pcfile.Value)
}
// Only changed if it advanced
if is_stmt != uint8(pcstmt.Value) {
new_stmt := uint8(pcstmt.Value)
switch new_stmt &^ 1 {
case obj.PrologueEnd:
ls.AddUint8(uint8(dwarf.DW_LNS_set_prologue_end))
case obj.EpilogueBegin:
// TODO if there is a use for this, add it.
// Don't forget to increase OPCODE_BASE by 1 and add entry for standard_opcode_lengths[11]
}
new_stmt &= 1
if is_stmt != new_stmt {
is_stmt = new_stmt
ls.AddUint8(uint8(dwarf.DW_LNS_negate_stmt))
}
}
// putpcldelta makes a row in the DWARF matrix, always, even if line is unchanged.
putpclcdelta(ctxt, dwarfctxt, ls, uint64(s.Value+int64(thispc)-pc), int64(pcline.Value)-int64(line))
pc = s.Value + int64(thispc)
line = int(pcline.Value)
// Take the minimum step forward for the three iterators
thispc = pcfile.NextPC
if pcline.NextPC < thispc {
thispc = pcline.NextPC
}
if !pcstmt.Done && pcstmt.NextPC < thispc {
thispc = pcstmt.NextPC
}
if pcfile.NextPC == thispc {
pcfile.Next()
}
if !pcstmt.Done && pcstmt.NextPC == thispc {
pcstmt.Next()
}
if pcline.NextPC == thispc {
pcline.Next()
}
// Set the PC.
ls.AddUint8(0)
dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize))
ls.AddUint8(dwarf.DW_LNE_set_address)
addr := ls.AddAddr(ctxt.Arch, s)
// Make sure the units are sorted.
if addr < lastAddr {
Errorf(s, "address wasn't increasing %x < %x", addr, lastAddr)
}
if is_stmt == 0 && i < len(unit.Textp)-1 {
// If there is more than one function, ensure default value is established.
is_stmt = 1
ls.AddUint8(uint8(dwarf.DW_LNS_negate_stmt))
lastAddr = addr
// Output the line table.
// TODO: Now that we have all the debug information in seperate
// symbols, it would make sense to use a rope, and concatenate them all
// together rather then the append() below. This would allow us to have
// the compiler emit the DW_LNE_set_address and a rope data structure
// to concat them all together in the output.
lines := dwarfFuncSym(ctxt, s, dwarf.DebugLinesPrefix, false)
if lines != nil {
ls.P = append(ls.P, lines.P...)
}
}
......@@ -1308,13 +1161,14 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
// DIE flavors (ex: variables) then those DIEs would need to
// be included below.
missing := make(map[int]interface{})
s := unit.Textp[0]
for _, f := range unit.FuncDIEs {
for ri := range f.R {
r := &f.R[ri]
if r.Type != objabi.R_DWARFFILEREF {
continue
}
idx, ok := fileNums[int(r.Sym.Value)]
idx, ok := fileNums[r.Sym.Name]
if ok {
if int(int32(idx)) != idx {
Errorf(f, "bad R_DWARFFILEREF relocation: file index overflow")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment