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) { ...@@ -53,10 +53,8 @@ func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) {
line := 1 line := 1
file := 1 file := 1
dctxt.AddUint8(lines, 0) // start extended opcode // The linker will insert the DW_LNE_set_address once determined; therefore,
dwarf.Uleb128put(dctxt, lines, 1+int64(ctxt.Arch.PtrSize)) // it's omitted here.
dctxt.AddUint8(lines, dwarf.DW_LNE_set_address)
dctxt.AddAddress(lines, nil, pc)
// Generate the actual line information. // Generate the actual line information.
// We use the pcline and pcfile to generate this section, and it's suboptimal. // We use the pcline and pcfile to generate this section, and it's suboptimal.
......
...@@ -17,6 +17,7 @@ import ( ...@@ -17,6 +17,7 @@ import (
"cmd/internal/dwarf" "cmd/internal/dwarf"
"cmd/internal/obj" "cmd/internal/obj"
"cmd/internal/objabi" "cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys" "cmd/internal/sys"
"cmd/link/internal/sym" "cmd/link/internal/sym"
"fmt" "fmt"
...@@ -1000,96 +1001,6 @@ const ( ...@@ -1000,96 +1001,6 @@ const (
OPCODE_BASE = 11 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. * Walk prog table, emit line program and build DIE tree.
*/ */
...@@ -1155,34 +1066,40 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) { ...@@ -1155,34 +1066,40 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
ls.AddUint8(0) // standard_opcode_lengths[10] ls.AddUint8(0) // standard_opcode_lengths[10]
ls.AddUint8(0) // include_directories (empty) ls.AddUint8(0) // include_directories (empty)
// Create the file table. fileNums maps from global file // Copy over the file table.
// indexes (created by numberfile) to CU-local indexes. fileNums := make(map[string]int)
fileNums := make(map[int]int) for i, name := range unit.DWARFFileTable {
for _, s := range unit.Textp { // textp has been dead-code-eliminated already. if len(name) != 0 {
dsym := dwarfFuncSym(ctxt, s, dwarf.InfoPrefix, true) if strings.HasPrefix(name, src.FileSymPrefix) {
for _, f := range s.FuncInfo.File { name = name[len(src.FileSymPrefix):]
if _, ok := fileNums[int(f.Value)]; ok {
continue
} }
// File indexes are 1-based. name = expandGoroot(name)
fileNums[int(f.Value)] = len(fileNums) + 1 } else {
Addstring(ls, f.Name) // Can't have empty filenames, and having a unique filename is quite useful
ls.AddUint8(0) // for debugging.
ls.AddUint8(0) name = fmt.Sprintf("<missing>_%d", i)
ls.AddUint8(0)
} }
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++ { for ri := 0; ri < len(dsym.R); ri++ {
r := &dsym.R[ri] r := &dsym.R[ri]
if r.Type != objabi.R_DWARFFILEREF { if r.Type != objabi.R_DWARFFILEREF {
continue continue
} }
// A file that is only mentioned in an inlined subroutine will appear name := r.Sym.Name
// as a R_DWARFFILEREF but not in s.FuncInfo.File if _, ok := fileNums[name]; ok {
if _, ok := fileNums[int(r.Sym.Value)]; ok {
continue continue
} }
fileNums[int(r.Sym.Value)] = len(fileNums) + 1 fileNums[name] = len(fileNums) + 1
Addstring(ls, r.Sym.Name) dwarfctxt.AddString(ls, name)
ls.AddUint8(0) ls.AddUint8(0)
ls.AddUint8(0) ls.AddUint8(0)
ls.AddUint8(0) ls.AddUint8(0)
...@@ -1194,95 +1111,31 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) { ...@@ -1194,95 +1111,31 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
// terminate file_names. // terminate file_names.
headerend = ls.Size headerend = ls.Size
ls.AddUint8(0) // start extended opcode // Output the state machine for each function remaining.
dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize)) var lastAddr int64
ls.AddUint8(dwarf.DW_LNE_set_address) for _, s := range unit.Textp {
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 {
finddebugruntimepath(s) finddebugruntimepath(s)
pcfile.Init(s.FuncInfo.Pcfile.P) // Set the PC.
pcline.Init(s.FuncInfo.Pcline.P) ls.AddUint8(0)
dwarf.Uleb128put(dwarfctxt, ls, 1+int64(ctxt.Arch.PtrSize))
isStmtSym := dwarfFuncSym(ctxt, s, dwarf.IsStmtPrefix, false) ls.AddUint8(dwarf.DW_LNE_set_address)
if isStmtSym != nil && len(isStmtSym.P) > 0 { addr := ls.AddAddr(ctxt.Arch, s)
pcstmt.Init(isStmtSym.P) // Make sure the units are sorted.
} else { if addr < lastAddr {
// Assembly files lack a pcstmt section, we assume that every instruction Errorf(s, "address wasn't increasing %x < %x", addr, lastAddr)
// 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()
}
} }
if is_stmt == 0 && i < len(unit.Textp)-1 { lastAddr = addr
// If there is more than one function, ensure default value is established.
is_stmt = 1 // Output the line table.
ls.AddUint8(uint8(dwarf.DW_LNS_negate_stmt)) // 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) { ...@@ -1308,13 +1161,14 @@ func writelines(ctxt *Link, unit *sym.CompilationUnit, ls *sym.Symbol) {
// DIE flavors (ex: variables) then those DIEs would need to // DIE flavors (ex: variables) then those DIEs would need to
// be included below. // be included below.
missing := make(map[int]interface{}) missing := make(map[int]interface{})
s := unit.Textp[0]
for _, f := range unit.FuncDIEs { for _, f := range unit.FuncDIEs {
for ri := range f.R { for ri := range f.R {
r := &f.R[ri] r := &f.R[ri]
if r.Type != objabi.R_DWARFFILEREF { if r.Type != objabi.R_DWARFFILEREF {
continue continue
} }
idx, ok := fileNums[int(r.Sym.Value)] idx, ok := fileNums[r.Sym.Name]
if ok { if ok {
if int(int32(idx)) != idx { if int(int32(idx)) != idx {
Errorf(f, "bad R_DWARFFILEREF relocation: file index overflow") 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