mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/compile: output DWARF lexical blocks for local variables
Change compiler and linker to emit DWARF lexical blocks in .debug_info section when compiling with -N -l. Version of debug_info is updated from DWARF v2 to DWARF v3 since version 2 does not allow lexical blocks with discontinuous PC ranges. Remaining open problems: - scope information is removed from inlined functions - variables records do not have DW_AT_start_scope attributes so a variable will shadow other variables with the same name as soon as its containing scope begins, even before its declaration. Updates #6913. Updates #12899. Change-Id: Idc6808788512ea20e7e45bcf782453acb416fb49 Reviewed-on: https://go-review.googlesource.com/40095 Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Matthew Dempsky <mdempsky@google.com> Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
This commit is contained in:
parent
0f0a51f1d1
commit
2ad41a3090
22 changed files with 1021 additions and 111 deletions
|
|
@ -5,19 +5,21 @@
|
|||
package gc
|
||||
|
||||
import (
|
||||
"cmd/compile/internal/syntax"
|
||||
"cmd/compile/internal/types"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// function literals aka closures
|
||||
func closurehdr(ntype *Node) {
|
||||
n := nod(OCLOSURE, nil, nil)
|
||||
func (p *noder) funcLit(expr *syntax.FuncLit) *Node {
|
||||
ntype := p.typeExpr(expr.Type)
|
||||
|
||||
n := p.nod(expr, OCLOSURE, nil, nil)
|
||||
n.Func.SetIsHiddenClosure(Curfn != nil)
|
||||
n.Func.Ntype = ntype
|
||||
n.Func.Depth = funcdepth
|
||||
n.Func.Outerfunc = Curfn
|
||||
|
||||
funchdr(n)
|
||||
old := p.funchdr(n, expr.Pos())
|
||||
|
||||
// steal ntype's argument names and
|
||||
// leave a fresh copy in their place.
|
||||
|
|
@ -25,8 +27,8 @@ func closurehdr(ntype *Node) {
|
|||
// refer to the variables in the external
|
||||
// function declared below; see walkclosure.
|
||||
n.List.Set(ntype.List.Slice())
|
||||
|
||||
n.Rlist.Set(ntype.Rlist.Slice())
|
||||
|
||||
ntype.List.Set(nil)
|
||||
ntype.Rlist.Set(nil)
|
||||
for _, n1 := range n.List.Slice() {
|
||||
|
|
@ -48,23 +50,23 @@ func closurehdr(ntype *Node) {
|
|||
}
|
||||
ntype.Rlist.Append(nod(ODCLFIELD, name, n2.Right))
|
||||
}
|
||||
}
|
||||
|
||||
func closurebody(body []*Node) *Node {
|
||||
body := p.stmts(expr.Body.List)
|
||||
|
||||
lineno = Ctxt.PosTable.XPos(expr.Body.Rbrace)
|
||||
if len(body) == 0 {
|
||||
body = []*Node{nod(OEMPTY, nil, nil)}
|
||||
}
|
||||
|
||||
func_ := Curfn
|
||||
func_.Nbody.Set(body)
|
||||
func_.Func.Endlineno = lineno
|
||||
funcbody(func_)
|
||||
n.Nbody.Set(body)
|
||||
n.Func.Endlineno = lineno
|
||||
p.funcbody(n, expr.Body.Rbrace, old)
|
||||
|
||||
// closure-specific variables are hanging off the
|
||||
// ordinary ones in the symbol table; see oldname.
|
||||
// unhook them.
|
||||
// make the list of pointers for the closure call.
|
||||
for _, v := range func_.Func.Cvars.Slice() {
|
||||
for _, v := range n.Func.Cvars.Slice() {
|
||||
// Unlink from v1; see comment in syntax.go type Param for these fields.
|
||||
v1 := v.Name.Defn
|
||||
v1.Name.Param.Innermost = v.Name.Param.Outer
|
||||
|
|
@ -100,7 +102,7 @@ func closurebody(body []*Node) *Node {
|
|||
v.Name.Param.Outer = oldname(v.Sym)
|
||||
}
|
||||
|
||||
return func_
|
||||
return n
|
||||
}
|
||||
|
||||
func typecheckclosure(func_ *Node, top int) {
|
||||
|
|
@ -227,7 +229,11 @@ func makeclosure(func_ *Node) *Node {
|
|||
|
||||
xfunc.Nbody.Set(func_.Nbody.Slice())
|
||||
xfunc.Func.Dcl = append(func_.Func.Dcl, xfunc.Func.Dcl...)
|
||||
xfunc.Func.Parents = func_.Func.Parents
|
||||
xfunc.Func.Marks = func_.Func.Marks
|
||||
func_.Func.Dcl = nil
|
||||
func_.Func.Parents = nil
|
||||
func_.Func.Marks = nil
|
||||
if xfunc.Nbody.Len() == 0 {
|
||||
Fatalf("empty body - won't generate any code")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,6 +216,9 @@ var flagDWARF bool
|
|||
// when the race detector is enabled.
|
||||
var instrumenting bool
|
||||
|
||||
// Whether we are tracking lexical scopes for DWARF.
|
||||
var trackScopes bool
|
||||
|
||||
var debuglive int
|
||||
|
||||
var Ctxt *obj.Link
|
||||
|
|
|
|||
|
|
@ -380,6 +380,8 @@ func Main(archInit func(*Arch)) {
|
|||
Debug['l'] = 1 - Debug['l']
|
||||
}
|
||||
|
||||
trackScopes = flagDWARF && Debug['l'] == 0 && Debug['N'] != 0
|
||||
|
||||
Widthptr = thearch.LinkArch.PtrSize
|
||||
Widthreg = thearch.LinkArch.RegSize
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,62 @@ type noder struct {
|
|||
linknames []linkname
|
||||
pragcgobuf string
|
||||
err chan syntax.Error
|
||||
scope ScopeID
|
||||
}
|
||||
|
||||
func (p *noder) funchdr(n *Node, pos src.Pos) ScopeID {
|
||||
old := p.scope
|
||||
p.scope = 0
|
||||
funchdr(n)
|
||||
return old
|
||||
}
|
||||
|
||||
func (p *noder) funcbody(n *Node, pos src.Pos, old ScopeID) {
|
||||
funcbody(n)
|
||||
p.scope = old
|
||||
}
|
||||
|
||||
func (p *noder) openScope(pos src.Pos) {
|
||||
types.Markdcl()
|
||||
|
||||
if trackScopes {
|
||||
Curfn.Func.Parents = append(Curfn.Func.Parents, p.scope)
|
||||
p.scope = ScopeID(len(Curfn.Func.Parents))
|
||||
|
||||
p.markScope(pos)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *noder) closeScope(pos src.Pos) {
|
||||
types.Popdcl()
|
||||
|
||||
if trackScopes {
|
||||
p.scope = Curfn.Func.Parents[p.scope-1]
|
||||
|
||||
p.markScope(pos)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *noder) markScope(pos src.Pos) {
|
||||
xpos := Ctxt.PosTable.XPos(pos)
|
||||
if i := len(Curfn.Func.Marks); i > 0 && Curfn.Func.Marks[i-1].Pos == xpos {
|
||||
Curfn.Func.Marks[i-1].Scope = p.scope
|
||||
} else {
|
||||
Curfn.Func.Marks = append(Curfn.Func.Marks, Mark{xpos, p.scope})
|
||||
}
|
||||
}
|
||||
|
||||
// closeAnotherScope is like closeScope, but it reuses the same mark
|
||||
// position as the last closeScope call. This is useful for "for" and
|
||||
// "if" statements, as their implicit blocks always end at the same
|
||||
// position as an explicit block.
|
||||
func (p *noder) closeAnotherScope() {
|
||||
types.Popdcl()
|
||||
|
||||
if trackScopes {
|
||||
p.scope = Curfn.Func.Parents[p.scope-1]
|
||||
Curfn.Func.Marks[len(Curfn.Func.Marks)-1].Scope = p.scope
|
||||
}
|
||||
}
|
||||
|
||||
// linkname records a //go:linkname directive.
|
||||
|
|
@ -326,8 +382,9 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
|
|||
declare(f.Func.Nname, PFUNC)
|
||||
}
|
||||
|
||||
funchdr(f)
|
||||
oldScope := p.funchdr(f, fun.Pos())
|
||||
|
||||
endPos := fun.Pos()
|
||||
if fun.Body != nil {
|
||||
if f.Noescape() {
|
||||
yyerrorl(f.Pos, "can only use //go:noescape with external func implementations")
|
||||
|
|
@ -339,6 +396,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
|
|||
}
|
||||
f.Nbody.Set(body)
|
||||
|
||||
endPos = fun.Body.Rbrace
|
||||
lineno = Ctxt.PosTable.XPos(fun.Body.Rbrace)
|
||||
f.Func.Endlineno = lineno
|
||||
} else {
|
||||
|
|
@ -347,8 +405,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) *Node {
|
|||
}
|
||||
}
|
||||
|
||||
funcbody(f)
|
||||
|
||||
p.funcbody(f, endPos, oldScope)
|
||||
return f
|
||||
}
|
||||
|
||||
|
|
@ -439,10 +496,7 @@ func (p *noder) expr(expr syntax.Expr) *Node {
|
|||
case *syntax.KeyValueExpr:
|
||||
return p.nod(expr, OKEY, p.expr(expr.Key), p.wrapname(expr.Value, p.expr(expr.Value)))
|
||||
case *syntax.FuncLit:
|
||||
closurehdr(p.typeExpr(expr.Type))
|
||||
body := p.stmts(expr.Body.List)
|
||||
lineno = Ctxt.PosTable.XPos(expr.Body.Rbrace)
|
||||
return p.setlineno(expr, closurebody(body))
|
||||
return p.funcLit(expr)
|
||||
case *syntax.ParenExpr:
|
||||
return p.nod(expr, OPAREN, p.expr(expr.X), nil)
|
||||
case *syntax.SelectorExpr:
|
||||
|
|
@ -774,14 +828,14 @@ func (p *noder) stmt(stmt syntax.Stmt) *Node {
|
|||
}
|
||||
|
||||
func (p *noder) blockStmt(stmt *syntax.BlockStmt) []*Node {
|
||||
types.Markdcl()
|
||||
p.openScope(stmt.Pos())
|
||||
nodes := p.stmts(stmt.List)
|
||||
types.Popdcl()
|
||||
p.closeScope(stmt.Rbrace)
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node {
|
||||
types.Markdcl()
|
||||
p.openScope(stmt.Pos())
|
||||
n := p.nod(stmt, OIF, nil, nil)
|
||||
if stmt.Init != nil {
|
||||
n.Ninit.Set1(p.stmt(stmt.Init))
|
||||
|
|
@ -798,12 +852,12 @@ func (p *noder) ifStmt(stmt *syntax.IfStmt) *Node {
|
|||
n.Rlist.Set1(e)
|
||||
}
|
||||
}
|
||||
types.Popdcl()
|
||||
p.closeAnotherScope()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *noder) forStmt(stmt *syntax.ForStmt) *Node {
|
||||
types.Markdcl()
|
||||
p.openScope(stmt.Pos())
|
||||
var n *Node
|
||||
if r, ok := stmt.Init.(*syntax.RangeClause); ok {
|
||||
if stmt.Cond != nil || stmt.Post != nil {
|
||||
|
|
@ -832,12 +886,12 @@ func (p *noder) forStmt(stmt *syntax.ForStmt) *Node {
|
|||
}
|
||||
}
|
||||
n.Nbody.Set(p.blockStmt(stmt.Body))
|
||||
types.Popdcl()
|
||||
p.closeAnotherScope()
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node {
|
||||
types.Markdcl()
|
||||
p.openScope(stmt.Pos())
|
||||
n := p.nod(stmt, OSWITCH, nil, nil)
|
||||
if stmt.Init != nil {
|
||||
n.Ninit.Set1(p.stmt(stmt.Init))
|
||||
|
|
@ -850,18 +904,21 @@ func (p *noder) switchStmt(stmt *syntax.SwitchStmt) *Node {
|
|||
if tswitch != nil && (tswitch.Op != OTYPESW || tswitch.Left == nil) {
|
||||
tswitch = nil
|
||||
}
|
||||
n.List.Set(p.caseClauses(stmt.Body, tswitch, stmt.Rbrace))
|
||||
|
||||
n.List.Set(p.caseClauses(stmt.Body, tswitch))
|
||||
|
||||
types.Popdcl()
|
||||
p.closeScope(stmt.Rbrace)
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node) []*Node {
|
||||
func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node, rbrace src.Pos) []*Node {
|
||||
var nodes []*Node
|
||||
for _, clause := range clauses {
|
||||
for i, clause := range clauses {
|
||||
p.lineno(clause)
|
||||
types.Markdcl()
|
||||
if i > 0 {
|
||||
p.closeScope(clause.Pos())
|
||||
}
|
||||
p.openScope(clause.Pos())
|
||||
|
||||
n := p.nod(clause, OXCASE, nil, nil)
|
||||
if clause.Cases != nil {
|
||||
n.List.Set(p.exprList(clause.Cases))
|
||||
|
|
@ -875,32 +932,40 @@ func (p *noder) caseClauses(clauses []*syntax.CaseClause, tswitch *Node) []*Node
|
|||
}
|
||||
n.Xoffset = int64(types.Block)
|
||||
n.Nbody.Set(p.stmts(clause.Body))
|
||||
types.Popdcl()
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
if len(clauses) > 0 {
|
||||
p.closeScope(rbrace)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func (p *noder) selectStmt(stmt *syntax.SelectStmt) *Node {
|
||||
n := p.nod(stmt, OSELECT, nil, nil)
|
||||
n.List.Set(p.commClauses(stmt.Body))
|
||||
n.List.Set(p.commClauses(stmt.Body, stmt.Rbrace))
|
||||
return n
|
||||
}
|
||||
|
||||
func (p *noder) commClauses(clauses []*syntax.CommClause) []*Node {
|
||||
func (p *noder) commClauses(clauses []*syntax.CommClause, rbrace src.Pos) []*Node {
|
||||
var nodes []*Node
|
||||
for _, clause := range clauses {
|
||||
for i, clause := range clauses {
|
||||
p.lineno(clause)
|
||||
types.Markdcl()
|
||||
if i > 0 {
|
||||
p.closeScope(clause.Pos())
|
||||
}
|
||||
p.openScope(clause.Pos())
|
||||
|
||||
n := p.nod(clause, OXCASE, nil, nil)
|
||||
if clause.Comm != nil {
|
||||
n.List.Set1(p.stmt(clause.Comm))
|
||||
}
|
||||
n.Xoffset = int64(types.Block)
|
||||
n.Nbody.Set(p.stmts(clause.Body))
|
||||
types.Popdcl()
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
if len(clauses) > 0 {
|
||||
p.closeScope(rbrace)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -299,13 +299,15 @@ func compileFunctions() {
|
|||
}
|
||||
}
|
||||
|
||||
func debuginfo(fnsym *obj.LSym, curfn interface{}) []*dwarf.Var {
|
||||
func debuginfo(fnsym *obj.LSym, curfn interface{}) []dwarf.Scope {
|
||||
fn := curfn.(*Node)
|
||||
if expect := fn.Func.Nname.Sym.Linksym(); fnsym != expect {
|
||||
Fatalf("unexpected fnsym: %v != %v", fnsym, expect)
|
||||
}
|
||||
|
||||
var vars []*dwarf.Var
|
||||
var dwarfVars []*dwarf.Var
|
||||
var varScopes []ScopeID
|
||||
|
||||
for _, n := range fn.Func.Dcl {
|
||||
if n.Op != ONAME { // might be OTYPE or OLITERAL
|
||||
continue
|
||||
|
|
@ -353,18 +355,26 @@ func debuginfo(fnsym *obj.LSym, curfn interface{}) []*dwarf.Var {
|
|||
}
|
||||
|
||||
typename := dwarf.InfoPrefix + gotype.Name[len("type."):]
|
||||
vars = append(vars, &dwarf.Var{
|
||||
dwarfVars = append(dwarfVars, &dwarf.Var{
|
||||
Name: n.Sym.Name,
|
||||
Abbrev: abbrev,
|
||||
Offset: int32(offs),
|
||||
Type: Ctxt.Lookup(typename),
|
||||
})
|
||||
|
||||
var scope ScopeID
|
||||
if !n.Name.Captured() && !n.Name.Byval() {
|
||||
// n.Pos of captured variables is their first
|
||||
// use in the closure but they should always
|
||||
// be assigned to scope 0 instead.
|
||||
// TODO(mdempsky): Verify this.
|
||||
scope = findScope(fn.Func.Marks, n.Pos)
|
||||
}
|
||||
|
||||
// Stable sort so that ties are broken with declaration order.
|
||||
sort.Stable(dwarf.VarsByOffset(vars))
|
||||
varScopes = append(varScopes, scope)
|
||||
}
|
||||
|
||||
return vars
|
||||
return assembleScopes(fnsym, fn, dwarfVars, varScopes)
|
||||
}
|
||||
|
||||
// fieldtrack adds R_USEFIELD relocations to fnsym to record any
|
||||
|
|
|
|||
177
src/cmd/compile/internal/gc/scope.go
Normal file
177
src/cmd/compile/internal/gc/scope.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
package gc
|
||||
|
||||
import (
|
||||
"cmd/internal/dwarf"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/src"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// See golang.org/issue/20390.
|
||||
func xposBefore(p, q src.XPos) bool {
|
||||
return Ctxt.PosTable.Pos(p).Before(Ctxt.PosTable.Pos(q))
|
||||
}
|
||||
|
||||
func findScope(marks []Mark, pos src.XPos) ScopeID {
|
||||
i := sort.Search(len(marks), func(i int) bool {
|
||||
return xposBefore(pos, marks[i].Pos)
|
||||
})
|
||||
if i == 0 {
|
||||
return 0
|
||||
}
|
||||
return marks[i-1].Scope
|
||||
}
|
||||
|
||||
func assembleScopes(fnsym *obj.LSym, fn *Node, dwarfVars []*dwarf.Var, varScopes []ScopeID) []dwarf.Scope {
|
||||
// Initialize the DWARF scope tree based on lexical scopes.
|
||||
dwarfScopes := make([]dwarf.Scope, 1+len(fn.Func.Parents))
|
||||
for i, parent := range fn.Func.Parents {
|
||||
dwarfScopes[i+1].Parent = int32(parent)
|
||||
}
|
||||
|
||||
scopeVariables(dwarfVars, varScopes, dwarfScopes)
|
||||
scopePCs(fnsym, fn.Func.Marks, dwarfScopes)
|
||||
return compactScopes(dwarfScopes)
|
||||
}
|
||||
|
||||
// scopeVariables assigns DWARF variable records to their scopes.
|
||||
func scopeVariables(dwarfVars []*dwarf.Var, varScopes []ScopeID, dwarfScopes []dwarf.Scope) {
|
||||
sort.Stable(varsByScopeAndOffset{dwarfVars, varScopes})
|
||||
|
||||
i0 := 0
|
||||
for i := range dwarfVars {
|
||||
if varScopes[i] == varScopes[i0] {
|
||||
continue
|
||||
}
|
||||
dwarfScopes[varScopes[i0]].Vars = dwarfVars[i0:i]
|
||||
i0 = i
|
||||
}
|
||||
if i0 < len(dwarfVars) {
|
||||
dwarfScopes[varScopes[i0]].Vars = dwarfVars[i0:]
|
||||
}
|
||||
}
|
||||
|
||||
// A scopedPCs represents a non-empty half-open interval of PCs that
|
||||
// share a common source position.
|
||||
type scopedPCs struct {
|
||||
start, end int64
|
||||
pos src.XPos
|
||||
scope ScopeID
|
||||
}
|
||||
|
||||
// scopePCs assigns PC ranges to their scopes.
|
||||
func scopePCs(fnsym *obj.LSym, marks []Mark, dwarfScopes []dwarf.Scope) {
|
||||
// If there aren't any child scopes (in particular, when scope
|
||||
// tracking is disabled), we can skip a whole lot of work.
|
||||
if len(marks) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Break function text into scopedPCs.
|
||||
var pcs []scopedPCs
|
||||
p0 := fnsym.Func.Text
|
||||
for p := fnsym.Func.Text; p != nil; p = p.Link {
|
||||
if p.Pos == p0.Pos {
|
||||
continue
|
||||
}
|
||||
if p0.Pc < p.Pc {
|
||||
pcs = append(pcs, scopedPCs{start: p0.Pc, end: p.Pc, pos: p0.Pos})
|
||||
}
|
||||
p0 = p
|
||||
}
|
||||
if p0.Pc < fnsym.Size {
|
||||
pcs = append(pcs, scopedPCs{start: p0.Pc, end: fnsym.Size, pos: p0.Pos})
|
||||
}
|
||||
|
||||
// Sort PCs by source position, and walk in parallel with
|
||||
// scope marks to assign a lexical scope to each PC interval.
|
||||
sort.Sort(pcsByPos(pcs))
|
||||
var marki int
|
||||
var scope ScopeID
|
||||
for i := range pcs {
|
||||
for marki < len(marks) && !xposBefore(pcs[i].pos, marks[marki].Pos) {
|
||||
scope = marks[marki].Scope
|
||||
marki++
|
||||
}
|
||||
pcs[i].scope = scope
|
||||
}
|
||||
|
||||
// Re-sort to create sorted PC ranges for each DWARF scope.
|
||||
sort.Sort(pcsByPC(pcs))
|
||||
for _, pc := range pcs {
|
||||
r := &dwarfScopes[pc.scope].Ranges
|
||||
if i := len(*r); i > 0 && (*r)[i-1].End == pc.start {
|
||||
(*r)[i-1].End = pc.end
|
||||
} else {
|
||||
*r = append(*r, dwarf.Range{Start: pc.start, End: pc.end})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func compactScopes(dwarfScopes []dwarf.Scope) []dwarf.Scope {
|
||||
// Forward pass to collapse empty scopes into parents.
|
||||
remap := make([]int32, len(dwarfScopes))
|
||||
j := int32(1)
|
||||
for i := 1; i < len(dwarfScopes); i++ {
|
||||
s := &dwarfScopes[i]
|
||||
s.Parent = remap[s.Parent]
|
||||
if len(s.Vars) == 0 {
|
||||
dwarfScopes[s.Parent].UnifyRanges(s)
|
||||
remap[i] = s.Parent
|
||||
continue
|
||||
}
|
||||
remap[i] = j
|
||||
dwarfScopes[j] = *s
|
||||
j++
|
||||
}
|
||||
dwarfScopes = dwarfScopes[:j]
|
||||
|
||||
// Reverse pass to propagate PC ranges to parent scopes.
|
||||
for i := len(dwarfScopes) - 1; i > 0; i-- {
|
||||
s := &dwarfScopes[i]
|
||||
dwarfScopes[s.Parent].UnifyRanges(s)
|
||||
}
|
||||
|
||||
return dwarfScopes
|
||||
}
|
||||
|
||||
type pcsByPC []scopedPCs
|
||||
|
||||
func (s pcsByPC) Len() int { return len(s) }
|
||||
func (s pcsByPC) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s pcsByPC) Less(i, j int) bool {
|
||||
return s[i].start < s[j].start
|
||||
}
|
||||
|
||||
type pcsByPos []scopedPCs
|
||||
|
||||
func (s pcsByPos) Len() int { return len(s) }
|
||||
func (s pcsByPos) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s pcsByPos) Less(i, j int) bool {
|
||||
return xposBefore(s[i].pos, s[j].pos)
|
||||
}
|
||||
|
||||
type varsByScopeAndOffset struct {
|
||||
vars []*dwarf.Var
|
||||
scopes []ScopeID
|
||||
}
|
||||
|
||||
func (v varsByScopeAndOffset) Len() int {
|
||||
return len(v.vars)
|
||||
}
|
||||
|
||||
func (v varsByScopeAndOffset) Less(i, j int) bool {
|
||||
if v.scopes[i] != v.scopes[j] {
|
||||
return v.scopes[i] < v.scopes[j]
|
||||
}
|
||||
return v.vars[i].Offset < v.vars[j].Offset
|
||||
}
|
||||
|
||||
func (v varsByScopeAndOffset) Swap(i, j int) {
|
||||
v.vars[i], v.vars[j] = v.vars[j], v.vars[i]
|
||||
v.scopes[i], v.scopes[j] = v.scopes[j], v.scopes[i]
|
||||
}
|
||||
407
src/cmd/compile/internal/gc/scope_test.go
Normal file
407
src/cmd/compile/internal/gc/scope_test.go
Normal file
|
|
@ -0,0 +1,407 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
package gc_test
|
||||
|
||||
import (
|
||||
"cmd/internal/objfile"
|
||||
"debug/dwarf"
|
||||
"internal/testenv"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testline struct {
|
||||
// line is one line of go source
|
||||
line string
|
||||
|
||||
// scopes is a list of scope IDs of all the lexical scopes that this line
|
||||
// of code belongs to.
|
||||
// Scope IDs are assigned by traversing the tree of lexical blocks of a
|
||||
// function in pre-order
|
||||
// Scope IDs are function specific, i.e. scope 0 is always the root scope
|
||||
// of the function that this line belongs to. Empty scopes are not assigned
|
||||
// an ID (because they are not saved in debug_info).
|
||||
// Scope 0 is always omitted from this list since all lines always belong
|
||||
// to it.
|
||||
scopes []int
|
||||
|
||||
// vars is the list of variables that belong in scopes[len(scopes)-1].
|
||||
// Local variables are prefixed with "var ", formal parameters with "arg ".
|
||||
// Must be ordered alphabetically.
|
||||
// Set to nil to skip the check.
|
||||
vars []string
|
||||
}
|
||||
|
||||
var testfile = []testline{
|
||||
{line: "package main"},
|
||||
{line: "func f1(x int) { }"},
|
||||
{line: "func f2(x int) { }"},
|
||||
{line: "func f3(x int) { }"},
|
||||
{line: "func f4(x int) { }"},
|
||||
{line: "func f5(x int) { }"},
|
||||
{line: "func f6(x int) { }"},
|
||||
{line: "func gret1() int { return 2 }"},
|
||||
{line: "func gretbool() bool { return true }"},
|
||||
{line: "func gret3() (int, int, int) { return 0, 1, 2 }"},
|
||||
{line: "var v = []int{ 0, 1, 2 }"},
|
||||
{line: "var ch = make(chan int)"},
|
||||
{line: "var floatch = make(chan float64)"},
|
||||
{line: "var iface interface{}"},
|
||||
{line: "func TestNestedFor() {", vars: []string{"var a int"}},
|
||||
{line: " a := 0"},
|
||||
{line: " f1(a)"},
|
||||
{line: " for i := 0; i < 5; i++ {", scopes: []int{1}, vars: []string{"var i int"}},
|
||||
{line: " f2(i)", scopes: []int{1}},
|
||||
{line: " for i := 0; i < 5; i++ {", scopes: []int{1, 2}, vars: []string{"var i int"}},
|
||||
{line: " f3(i)", scopes: []int{1, 2}},
|
||||
{line: " }"},
|
||||
{line: " f4(i)", scopes: []int{1}},
|
||||
{line: " }"},
|
||||
{line: " f5(a)"},
|
||||
{line: "}"},
|
||||
{line: "func TestOas2() {", vars: []string{}},
|
||||
{line: " if a, b, c := gret3(); a != 1 {", scopes: []int{1}, vars: []string{"var a int", "var b int", "var c int"}},
|
||||
{line: " f1(a)", scopes: []int{1}},
|
||||
{line: " f1(b)", scopes: []int{1}},
|
||||
{line: " f1(c)", scopes: []int{1}},
|
||||
{line: " }"},
|
||||
{line: " for i, x := range v {", scopes: []int{2}, vars: []string{"var i int", "var x int"}},
|
||||
{line: " f1(i)", scopes: []int{2}},
|
||||
{line: " f1(x)", scopes: []int{2}},
|
||||
{line: " }"},
|
||||
{line: " if a, ok := <- ch; ok {", scopes: []int{3}, vars: []string{"var a int", "var ok bool"}},
|
||||
{line: " f1(a)", scopes: []int{3}},
|
||||
{line: " }"},
|
||||
{line: " if a, ok := iface.(int); ok {", scopes: []int{4}, vars: []string{"var a int", "var ok bool"}},
|
||||
{line: " f1(a)", scopes: []int{4}},
|
||||
{line: " }"},
|
||||
{line: "}"},
|
||||
{line: "func TestIfElse() {"},
|
||||
{line: " if x := gret1(); x != 0 {", scopes: []int{1}, vars: []string{"var x int"}},
|
||||
{line: " a := 0", scopes: []int{1, 2}, vars: []string{"var a int"}},
|
||||
{line: " f1(a); f1(x)", scopes: []int{1, 2}},
|
||||
{line: " } else {"},
|
||||
{line: " b := 1", scopes: []int{1, 3}, vars: []string{"var b int"}},
|
||||
{line: " f1(b); f1(x+1)", scopes: []int{1, 3}},
|
||||
{line: " }"},
|
||||
{line: "}"},
|
||||
{line: "func TestSwitch() {", vars: []string{}},
|
||||
{line: " switch x := gret1(); x {", scopes: []int{1}, vars: []string{"var x int"}},
|
||||
{line: " case 0:", scopes: []int{1, 2}},
|
||||
{line: " i := x + 5", scopes: []int{1, 2}, vars: []string{"var i int"}},
|
||||
{line: " f1(x); f1(i)", scopes: []int{1, 2}},
|
||||
{line: " case 1:", scopes: []int{1, 3}},
|
||||
{line: " j := x + 10", scopes: []int{1, 3}, vars: []string{"var j int"}},
|
||||
{line: " f1(x); f1(j)", scopes: []int{1, 3}},
|
||||
{line: " case 2:", scopes: []int{1, 4}},
|
||||
{line: " k := x + 2", scopes: []int{1, 4}, vars: []string{"var k int"}},
|
||||
{line: " f1(x); f1(k)", scopes: []int{1, 4}},
|
||||
{line: " }"},
|
||||
{line: "}"},
|
||||
{line: "func TestTypeSwitch() {", vars: []string{}},
|
||||
{line: " switch x := iface.(type) {"},
|
||||
{line: " case int:", scopes: []int{1}},
|
||||
{line: " f1(x)", scopes: []int{1}, vars: []string{"var x int"}},
|
||||
{line: " case uint8:", scopes: []int{2}},
|
||||
{line: " f1(int(x))", scopes: []int{2}, vars: []string{"var x uint8"}},
|
||||
{line: " case float64:", scopes: []int{3}},
|
||||
{line: " f1(int(x)+1)", scopes: []int{3}, vars: []string{"var x float64"}},
|
||||
{line: " }"},
|
||||
{line: "}"},
|
||||
{line: "func TestSelectScope() {"},
|
||||
{line: " select {"},
|
||||
{line: " case i := <- ch:", scopes: []int{1}},
|
||||
{line: " f1(i)", scopes: []int{1}, vars: []string{"var i int"}},
|
||||
{line: " case f := <- floatch:", scopes: []int{2}},
|
||||
{line: " f1(int(f))", scopes: []int{2}, vars: []string{"var f float64"}},
|
||||
{line: " }"},
|
||||
{line: "}"},
|
||||
{line: "func TestBlock() {", vars: []string{"var a int"}},
|
||||
{line: " a := 1"},
|
||||
{line: " {"},
|
||||
{line: " b := 2", scopes: []int{1}, vars: []string{"var b int"}},
|
||||
{line: " f1(b)", scopes: []int{1}},
|
||||
{line: " f1(a)", scopes: []int{1}},
|
||||
{line: " }"},
|
||||
{line: "}"},
|
||||
{line: "func TestDiscontiguousRanges() {", vars: []string{"var a int"}},
|
||||
{line: " a := 0"},
|
||||
{line: " f1(a)"},
|
||||
{line: " {"},
|
||||
{line: " b := 0", scopes: []int{1}, vars: []string{"var b int"}},
|
||||
{line: " f2(b)", scopes: []int{1}},
|
||||
{line: " if gretbool() {", scopes: []int{1}},
|
||||
{line: " c := 0", scopes: []int{1, 2}, vars: []string{"var c int"}},
|
||||
{line: " f3(c)", scopes: []int{1, 2}},
|
||||
{line: " } else {"},
|
||||
{line: " c := 1.1", scopes: []int{1, 3}, vars: []string{"var c float64"}},
|
||||
{line: " f4(int(c))", scopes: []int{1, 3}},
|
||||
{line: " }"},
|
||||
{line: " f5(b)", scopes: []int{1}},
|
||||
{line: " }"},
|
||||
{line: " f6(a)"},
|
||||
{line: "}"},
|
||||
{line: "func TestClosureScope() {", vars: []string{"var a int", "var b int", "var f func(int)"}},
|
||||
{line: " a := 1; b := 1"},
|
||||
{line: " f := func(c int) {", scopes: []int{0}, vars: []string{"arg c int", "var &b *int", "var a int", "var d int"}},
|
||||
{line: " d := 3"},
|
||||
{line: " f1(c); f1(d)"},
|
||||
{line: " if e := 3; e != 0 {", scopes: []int{1}, vars: []string{"var e int"}},
|
||||
{line: " f1(e)", scopes: []int{1}},
|
||||
{line: " f1(a)", scopes: []int{1}},
|
||||
{line: " b = 2", scopes: []int{1}},
|
||||
{line: " }"},
|
||||
{line: " }"},
|
||||
{line: " f(3); f1(b)"},
|
||||
{line: "}"},
|
||||
{line: "func main() {"},
|
||||
{line: " TestNestedFor()"},
|
||||
{line: " TestOas2()"},
|
||||
{line: " TestIfElse()"},
|
||||
{line: " TestSwitch()"},
|
||||
{line: " TestTypeSwitch()"},
|
||||
{line: " TestSelectScope()"},
|
||||
{line: " TestBlock()"},
|
||||
{line: " TestDiscontiguousRanges()"},
|
||||
{line: " TestClosureScope()"},
|
||||
{line: "}"},
|
||||
}
|
||||
|
||||
const detailOutput = false
|
||||
|
||||
// Compiles testfile checks that the description of lexical blocks emitted
|
||||
// by the linker in debug_info, for each function in the main package,
|
||||
// corresponds to what we expect it to be.
|
||||
func TestScopeRanges(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
dir, err := ioutil.TempDir("", "TestScopeRanges")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
src, f := gobuild(t, dir, testfile)
|
||||
defer f.Close()
|
||||
|
||||
// the compiler uses forward slashes for paths even on windows
|
||||
src = strings.Replace(src, "\\", "/", -1)
|
||||
|
||||
pcln, err := f.PCLineTable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dwarfData, err := f.DWARF()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dwarfReader := dwarfData.Reader()
|
||||
|
||||
lines := make(map[line][]*lexblock)
|
||||
|
||||
for {
|
||||
entry, err := dwarfReader.Next()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if entry == nil {
|
||||
break
|
||||
}
|
||||
|
||||
if entry.Tag != dwarf.TagSubprogram {
|
||||
continue
|
||||
}
|
||||
|
||||
name, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok || !strings.HasPrefix(name, "main.Test") {
|
||||
continue
|
||||
}
|
||||
|
||||
var scope lexblock
|
||||
ctxt := scopexplainContext{
|
||||
dwarfData: dwarfData,
|
||||
dwarfReader: dwarfReader,
|
||||
scopegen: 1,
|
||||
}
|
||||
|
||||
readScope(&ctxt, &scope, entry)
|
||||
|
||||
scope.markLines(pcln, lines)
|
||||
}
|
||||
|
||||
anyerror := false
|
||||
for i := range testfile {
|
||||
tgt := testfile[i].scopes
|
||||
out := lines[line{src, i + 1}]
|
||||
|
||||
if detailOutput {
|
||||
t.Logf("%s // %v", testfile[i].line, out)
|
||||
}
|
||||
|
||||
scopesok := checkScopes(tgt, out)
|
||||
if !scopesok {
|
||||
t.Logf("mismatch at line %d %q: expected: %v got: %v\n", i, testfile[i].line, tgt, scopesToString(out))
|
||||
}
|
||||
|
||||
varsok := true
|
||||
if testfile[i].vars != nil {
|
||||
if len(out) > 0 {
|
||||
varsok = checkVars(testfile[i].vars, out[len(out)-1].vars)
|
||||
if !varsok {
|
||||
t.Logf("variable mismatch at line %d %q for scope %d: expected: %v got: %v\n", i, testfile[i].line, out[len(out)-1].id, testfile[i].vars, out[len(out)-1].vars)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anyerror = anyerror || !scopesok || !varsok
|
||||
}
|
||||
|
||||
if anyerror {
|
||||
t.Fatalf("mismatched output")
|
||||
}
|
||||
}
|
||||
|
||||
func scopesToString(v []*lexblock) string {
|
||||
r := make([]string, len(v))
|
||||
for i, s := range v {
|
||||
r[i] = strconv.Itoa(s.id)
|
||||
}
|
||||
return "[ " + strings.Join(r, ", ") + " ]"
|
||||
}
|
||||
|
||||
func checkScopes(tgt []int, out []*lexblock) bool {
|
||||
if len(out) > 0 {
|
||||
// omit scope 0
|
||||
out = out[1:]
|
||||
}
|
||||
if len(tgt) != len(out) {
|
||||
return false
|
||||
}
|
||||
for i := range tgt {
|
||||
if tgt[i] != out[i].id {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func checkVars(tgt, out []string) bool {
|
||||
if len(tgt) != len(out) {
|
||||
return false
|
||||
}
|
||||
for i := range tgt {
|
||||
if tgt[i] != out[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type lexblock struct {
|
||||
id int
|
||||
ranges [][2]uint64
|
||||
vars []string
|
||||
scopes []lexblock
|
||||
}
|
||||
|
||||
type line struct {
|
||||
file string
|
||||
lineno int
|
||||
}
|
||||
|
||||
type scopexplainContext struct {
|
||||
dwarfData *dwarf.Data
|
||||
dwarfReader *dwarf.Reader
|
||||
scopegen int
|
||||
lines map[line][]int
|
||||
}
|
||||
|
||||
// readScope reads the DW_TAG_lexical_block or the DW_TAG_subprogram in
|
||||
// entry and writes a description in scope.
|
||||
// Nested DW_TAG_lexical_block entries are read recursively.
|
||||
func readScope(ctxt *scopexplainContext, scope *lexblock, entry *dwarf.Entry) {
|
||||
var err error
|
||||
scope.ranges, err = ctxt.dwarfData.Ranges(entry)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for {
|
||||
e, err := ctxt.dwarfReader.Next()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
switch e.Tag {
|
||||
case 0:
|
||||
sort.Strings(scope.vars)
|
||||
return
|
||||
case dwarf.TagFormalParameter:
|
||||
typ, err := ctxt.dwarfData.Type(e.Val(dwarf.AttrType).(dwarf.Offset))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
scope.vars = append(scope.vars, "arg "+e.Val(dwarf.AttrName).(string)+" "+typ.String())
|
||||
case dwarf.TagVariable:
|
||||
typ, err := ctxt.dwarfData.Type(e.Val(dwarf.AttrType).(dwarf.Offset))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
scope.vars = append(scope.vars, "var "+e.Val(dwarf.AttrName).(string)+" "+typ.String())
|
||||
case dwarf.TagLexDwarfBlock:
|
||||
scope.scopes = append(scope.scopes, lexblock{id: ctxt.scopegen})
|
||||
ctxt.scopegen++
|
||||
readScope(ctxt, &scope.scopes[len(scope.scopes)-1], e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// markLines marks all lines that belong to this scope with this scope
|
||||
// Recursively calls markLines for all children scopes.
|
||||
func (scope *lexblock) markLines(pcln objfile.Liner, lines map[line][]*lexblock) {
|
||||
for _, r := range scope.ranges {
|
||||
for pc := r[0]; pc < r[1]; pc++ {
|
||||
file, lineno, _ := pcln.PCToLine(pc)
|
||||
l := line{file, lineno}
|
||||
if len(lines[l]) == 0 || lines[l][len(lines[l])-1] != scope {
|
||||
lines[l] = append(lines[l], scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := range scope.scopes {
|
||||
scope.scopes[i].markLines(pcln, lines)
|
||||
}
|
||||
}
|
||||
|
||||
func gobuild(t *testing.T, dir string, testfile []testline) (string, *objfile.File) {
|
||||
src := filepath.Join(dir, "test.go")
|
||||
dst := filepath.Join(dir, "out.o")
|
||||
|
||||
f, err := os.Create(src)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for i := range testfile {
|
||||
f.Write([]byte(testfile[i].line))
|
||||
f.Write([]byte{'\n'})
|
||||
}
|
||||
f.Close()
|
||||
|
||||
cmd := exec.Command(testenv.GoToolPath(t), "build", "-gcflags=-N -l", "-o", dst, src)
|
||||
if b, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Logf("build: %s\n", string(b))
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pkg, err := objfile.Open(dst)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return src, pkg
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ func TestSizeof(t *testing.T) {
|
|||
_32bit uintptr // size on 32bit platforms
|
||||
_64bit uintptr // size on 64bit platforms
|
||||
}{
|
||||
{Func{}, 100, 168},
|
||||
{Func{}, 124, 216},
|
||||
{Name{}, 36, 56},
|
||||
{Param{}, 28, 56},
|
||||
{Node{}, 76, 128},
|
||||
|
|
|
|||
|
|
@ -357,6 +357,15 @@ type Func struct {
|
|||
Cvars Nodes // closure params
|
||||
Dcl []*Node // autodcl for this func/closure
|
||||
Inldcl Nodes // copy of dcl for use in inlining
|
||||
|
||||
// Parents records the parent scope of each scope within a
|
||||
// function. The root scope (0) has no parent, so the i'th
|
||||
// scope's parent is stored at Parents[i-1].
|
||||
Parents []ScopeID
|
||||
|
||||
// Marks records scope boundary changes.
|
||||
Marks []Mark
|
||||
|
||||
Closgen int
|
||||
Outerfunc *Node // outer function (for closure)
|
||||
FieldTrack map[*types.Sym]struct{}
|
||||
|
|
@ -380,6 +389,19 @@ type Func struct {
|
|||
flags bitset8
|
||||
}
|
||||
|
||||
// A Mark represents a scope boundary.
|
||||
type Mark struct {
|
||||
// Pos is the position of the token that marks the scope
|
||||
// change.
|
||||
Pos src.XPos
|
||||
|
||||
// Scope identifies the innermost scope to the right of Pos.
|
||||
Scope ScopeID
|
||||
}
|
||||
|
||||
// A ScopeID represents a lexical scope within a function.
|
||||
type ScopeID int32
|
||||
|
||||
const (
|
||||
funcDupok = 1 << iota // duplicate definitions ok
|
||||
funcWrapper // is method wrapper
|
||||
|
|
|
|||
|
|
@ -8,14 +8,19 @@
|
|||
package dwarf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// InfoPrefix is the prefix for all the symbols containing DWARF info entries.
|
||||
const InfoPrefix = "go.info."
|
||||
|
||||
// RangePrefix is the prefix for all the symbols containing DWARF range lists.
|
||||
const RangePrefix = "go.range."
|
||||
|
||||
// Sym represents a symbol.
|
||||
type Sym interface {
|
||||
Len() int64
|
||||
}
|
||||
|
||||
// A Var represents a local variable or a function parameter.
|
||||
|
|
@ -23,9 +28,62 @@ type Var struct {
|
|||
Name string
|
||||
Abbrev int // Either DW_ABRV_AUTO or DW_ABRV_PARAM
|
||||
Offset int32
|
||||
Scope int32
|
||||
Type Sym
|
||||
}
|
||||
|
||||
// A Scope represents a lexical scope. All variables declared within a
|
||||
// scope will only be visible to instructions covered by the scope.
|
||||
// Lexical scopes are contiguous in source files but can end up being
|
||||
// compiled to discontiguous blocks of instructions in the executable.
|
||||
// The Ranges field lists all the blocks of instructions that belong
|
||||
// in this scope.
|
||||
type Scope struct {
|
||||
Parent int32
|
||||
Ranges []Range
|
||||
Vars []*Var
|
||||
}
|
||||
|
||||
// A Range represents a half-open interval [Start, End).
|
||||
type Range struct {
|
||||
Start, End int64
|
||||
}
|
||||
|
||||
// UnifyRanges merges the list of ranges of c into the list of ranges of s
|
||||
func (s *Scope) UnifyRanges(c *Scope) {
|
||||
out := make([]Range, 0, len(s.Ranges)+len(c.Ranges))
|
||||
|
||||
i, j := 0, 0
|
||||
for {
|
||||
var cur Range
|
||||
if i < len(s.Ranges) && j < len(c.Ranges) {
|
||||
if s.Ranges[i].Start < c.Ranges[j].Start {
|
||||
cur = s.Ranges[i]
|
||||
i++
|
||||
} else {
|
||||
cur = c.Ranges[j]
|
||||
j++
|
||||
}
|
||||
} else if i < len(s.Ranges) {
|
||||
cur = s.Ranges[i]
|
||||
i++
|
||||
} else if j < len(c.Ranges) {
|
||||
cur = c.Ranges[j]
|
||||
j++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
if n := len(out); n > 0 && cur.Start <= out[n-1].End {
|
||||
out[n-1].End = cur.End
|
||||
} else {
|
||||
out = append(out, cur)
|
||||
}
|
||||
}
|
||||
|
||||
s.Ranges = out
|
||||
}
|
||||
|
||||
// A Context specifies how to add data to a Sym.
|
||||
type Context interface {
|
||||
PtrSize() int
|
||||
|
|
@ -156,6 +214,8 @@ const (
|
|||
DW_ABRV_VARIABLE
|
||||
DW_ABRV_AUTO
|
||||
DW_ABRV_PARAM
|
||||
DW_ABRV_LEXICAL_BLOCK_RANGES
|
||||
DW_ABRV_LEXICAL_BLOCK_SIMPLE
|
||||
DW_ABRV_STRUCTFIELD
|
||||
DW_ABRV_FUNCTYPEPARAM
|
||||
DW_ABRV_DOTDOTDOT
|
||||
|
|
@ -247,6 +307,25 @@ var abbrevs = [DW_NABRV]dwAbbrev{
|
|||
},
|
||||
},
|
||||
|
||||
/* LEXICAL_BLOCK_RANGES */
|
||||
{
|
||||
DW_TAG_lexical_block,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_ranges, DW_FORM_data4}, // replace with DW_FORM_sec_offset in DWARFv4.
|
||||
},
|
||||
},
|
||||
|
||||
/* LEXICAL_BLOCK_SIMPLE */
|
||||
{
|
||||
DW_TAG_lexical_block,
|
||||
DW_CHILDREN_yes,
|
||||
[]dwAttrForm{
|
||||
{DW_AT_low_pc, DW_FORM_addr},
|
||||
{DW_AT_high_pc, DW_FORM_addr},
|
||||
},
|
||||
},
|
||||
|
||||
/* STRUCTFIELD */
|
||||
{
|
||||
DW_TAG_member,
|
||||
|
|
@ -525,8 +604,8 @@ func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, da
|
|||
ctxt.AddInt(s, 2, value)
|
||||
|
||||
case DW_FORM_data4: // constant, {line,loclist,mac,rangelist}ptr
|
||||
if cls == DW_CLS_PTR { // DW_AT_stmt_list
|
||||
ctxt.AddSectionOffset(s, 4, data, 0)
|
||||
if cls == DW_CLS_PTR { // DW_AT_stmt_list and DW_AT_ranges
|
||||
ctxt.AddSectionOffset(s, 4, data, value)
|
||||
break
|
||||
}
|
||||
ctxt.AddInt(s, 4, value)
|
||||
|
|
@ -555,15 +634,13 @@ func putattr(ctxt Context, s Sym, abbrev int, form int, cls int, value int64, da
|
|||
ctxt.AddInt(s, 1, 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
|
||||
// In DWARF 3 the ref_addr 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 {
|
||||
return fmt.Errorf("dwarf: null reference in %d", abbrev)
|
||||
} else {
|
||||
ctxt.AddSectionOffset(s, ctxt.PtrSize(), data, 0)
|
||||
ctxt.AddSectionOffset(s, 4, data, 0)
|
||||
}
|
||||
|
||||
case DW_FORM_ref1, // reference within the compilation unit
|
||||
|
|
@ -606,7 +683,7 @@ func HasChildren(die *DWDie) bool {
|
|||
|
||||
// PutFunc writes a DIE for a function to s.
|
||||
// It also writes child DIEs for each variable in vars.
|
||||
func PutFunc(ctxt Context, s Sym, name string, external bool, startPC Sym, size int64, vars []*Var) {
|
||||
func PutFunc(ctxt Context, s, ranges Sym, name string, external bool, startPC Sym, size int64, scopes []Scope) error {
|
||||
Uleb128put(ctxt, s, DW_ABRV_FUNCTION)
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, 0, startPC)
|
||||
|
|
@ -616,16 +693,56 @@ func PutFunc(ctxt Context, s Sym, name string, external bool, startPC Sym, size
|
|||
ev = 1
|
||||
}
|
||||
putattr(ctxt, s, DW_ABRV_FUNCTION, DW_FORM_flag, DW_CLS_FLAG, ev, 0)
|
||||
names := make(map[string]bool)
|
||||
if len(scopes) > 0 {
|
||||
var encbuf [20]byte
|
||||
for _, v := range vars {
|
||||
var n string
|
||||
if names[v.Name] {
|
||||
n = fmt.Sprintf("%s#%d", v.Name, len(names))
|
||||
} else {
|
||||
n = v.Name
|
||||
if putscope(ctxt, s, ranges, startPC, 0, scopes, encbuf[:0]) < int32(len(scopes)) {
|
||||
return errors.New("multiple toplevel scopes")
|
||||
}
|
||||
names[n] = true
|
||||
}
|
||||
|
||||
Uleb128put(ctxt, s, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func putscope(ctxt Context, s, ranges Sym, startPC Sym, curscope int32, scopes []Scope, encbuf []byte) int32 {
|
||||
for _, v := range scopes[curscope].Vars {
|
||||
putvar(ctxt, s, v, encbuf)
|
||||
}
|
||||
this := curscope
|
||||
curscope++
|
||||
for curscope < int32(len(scopes)) {
|
||||
scope := scopes[curscope]
|
||||
if scope.Parent != this {
|
||||
return curscope
|
||||
}
|
||||
|
||||
if len(scope.Ranges) == 1 {
|
||||
Uleb128put(ctxt, s, DW_ABRV_LEXICAL_BLOCK_SIMPLE)
|
||||
putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].Start, startPC)
|
||||
putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_SIMPLE, DW_FORM_addr, DW_CLS_ADDRESS, scope.Ranges[0].End, startPC)
|
||||
} else {
|
||||
Uleb128put(ctxt, s, DW_ABRV_LEXICAL_BLOCK_RANGES)
|
||||
putattr(ctxt, s, DW_ABRV_LEXICAL_BLOCK_RANGES, DW_FORM_data4, DW_CLS_PTR, ranges.Len(), ranges)
|
||||
|
||||
ctxt.AddAddress(ranges, nil, -1)
|
||||
ctxt.AddAddress(ranges, startPC, 0)
|
||||
for _, r := range scope.Ranges {
|
||||
ctxt.AddAddress(ranges, nil, r.Start)
|
||||
ctxt.AddAddress(ranges, nil, r.End)
|
||||
}
|
||||
ctxt.AddAddress(ranges, nil, 0)
|
||||
ctxt.AddAddress(ranges, nil, 0)
|
||||
}
|
||||
|
||||
curscope = putscope(ctxt, s, ranges, startPC, curscope, scopes, encbuf)
|
||||
|
||||
Uleb128put(ctxt, s, 0)
|
||||
}
|
||||
return curscope
|
||||
}
|
||||
|
||||
func putvar(ctxt Context, s Sym, v *Var, encbuf []byte) {
|
||||
n := v.Name
|
||||
|
||||
Uleb128put(ctxt, s, int64(v.Abbrev))
|
||||
putattr(ctxt, s, v.Abbrev, DW_FORM_string, DW_CLS_STRING, int64(len(n)), n)
|
||||
|
|
@ -637,8 +754,6 @@ func PutFunc(ctxt Context, s Sym, name string, external bool, startPC Sym, size
|
|||
}
|
||||
putattr(ctxt, s, v.Abbrev, DW_FORM_block1, DW_CLS_BLOCK, int64(len(loc)), loc)
|
||||
putattr(ctxt, s, v.Abbrev, DW_FORM_ref_addr, DW_CLS_REFERENCE, 0, v.Type)
|
||||
}
|
||||
Uleb128put(ctxt, s, 0)
|
||||
}
|
||||
|
||||
// VarsByOffset attaches the methods of sort.Interface to []*Var,
|
||||
|
|
|
|||
|
|
@ -120,7 +120,8 @@ func (s *LSym) WriteInt(ctxt *Link, off int64, siz int, i int64) {
|
|||
// WriteAddr writes an address of size siz into s at offset off.
|
||||
// rsym and roff specify the relocation for the address.
|
||||
func (s *LSym) WriteAddr(ctxt *Link, off int64, siz int, rsym *LSym, roff int64) {
|
||||
if siz != ctxt.Arch.PtrSize {
|
||||
// Allow 4-byte addresses for DWARF.
|
||||
if siz != ctxt.Arch.PtrSize && siz != 4 {
|
||||
ctxt.Diag("WriteAddr: bad address size %d in %s", siz, s.Name)
|
||||
}
|
||||
s.prepwrite(ctxt, off, siz)
|
||||
|
|
|
|||
|
|
@ -329,7 +329,10 @@ type FuncInfo struct {
|
|||
Text *Prog
|
||||
Autom []*Auto
|
||||
Pcln Pcln
|
||||
|
||||
dwarfSym *LSym
|
||||
dwarfRangesSym *LSym
|
||||
|
||||
GCArgs LSym
|
||||
GCLocals LSym
|
||||
}
|
||||
|
|
@ -490,7 +493,7 @@ type Link struct {
|
|||
InlTree InlTree // global inlining tree used by gc/inl.go
|
||||
Imports []string
|
||||
DiagFunc func(string, ...interface{})
|
||||
DebugInfo func(fn *LSym, curfn interface{}) []*dwarf.Var // if non-nil, curfn is a *gc.Node
|
||||
DebugInfo func(fn *LSym, curfn interface{}) []dwarf.Scope // if non-nil, curfn is a *gc.Node
|
||||
Errors int
|
||||
|
||||
Framepointer_enabled bool
|
||||
|
|
|
|||
|
|
@ -447,10 +447,14 @@ func (c dwCtxt) SymValue(s dwarf.Sym) int64 {
|
|||
return 0
|
||||
}
|
||||
func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
|
||||
rsym := data.(*LSym)
|
||||
ls := s.(*LSym)
|
||||
size := c.PtrSize()
|
||||
if data != nil {
|
||||
rsym := data.(*LSym)
|
||||
ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
|
||||
} else {
|
||||
ls.WriteInt(c.Link, ls.Size, size, value)
|
||||
}
|
||||
}
|
||||
func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
|
||||
ls := s.(*LSym)
|
||||
|
|
@ -460,27 +464,35 @@ func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
|
|||
r.Type = objabi.R_DWARFREF
|
||||
}
|
||||
|
||||
// dwarfSym returns the DWARF symbol for TEXT symbol.
|
||||
func (ctxt *Link) dwarfSym(s *LSym) *LSym {
|
||||
// dwarfSym returns the DWARF symbols for TEXT symbol.
|
||||
func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfRangesSym *LSym) {
|
||||
if s.Type != objabi.STEXT {
|
||||
ctxt.Diag("dwarfSym of non-TEXT %v", s)
|
||||
}
|
||||
if s.Func.dwarfSym == nil {
|
||||
s.Func.dwarfSym = ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name)
|
||||
s.Func.dwarfRangesSym = ctxt.LookupDerived(s, dwarf.RangePrefix+s.Name)
|
||||
}
|
||||
return s.Func.dwarfSym
|
||||
return s.Func.dwarfSym, s.Func.dwarfRangesSym
|
||||
}
|
||||
|
||||
// populateDWARF fills in the DWARF Debugging Information Entry for TEXT symbol s.
|
||||
// The DWARF symbol must already have been initialized in InitTextSym.
|
||||
func (s *LSym) Len() int64 {
|
||||
return s.Size
|
||||
}
|
||||
|
||||
// populateDWARF fills in the DWARF Debugging Information Entries for TEXT symbol s.
|
||||
// The DWARFs symbol must already have been initialized in InitTextSym.
|
||||
func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym) {
|
||||
dsym := ctxt.dwarfSym(s)
|
||||
dsym, drsym := ctxt.dwarfSym(s)
|
||||
if dsym.Size != 0 {
|
||||
ctxt.Diag("makeFuncDebugEntry double process %v", s)
|
||||
}
|
||||
var vars []*dwarf.Var
|
||||
var scopes []dwarf.Scope
|
||||
if ctxt.DebugInfo != nil {
|
||||
vars = ctxt.DebugInfo(s, curfn)
|
||||
scopes = ctxt.DebugInfo(s, curfn)
|
||||
}
|
||||
err := dwarf.PutFunc(dwCtxt{ctxt}, dsym, drsym, s.Name, !s.Static(), s, s.Size, scopes)
|
||||
if err != nil {
|
||||
ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
|
||||
}
|
||||
dwarf.PutFunc(dwCtxt{ctxt}, dsym, s.Name, !s.Static(), s, s.Size, vars)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,11 +135,14 @@ func (ctxt *Link) InitTextSym(s *LSym, flag int) {
|
|||
s.Type = objabi.STEXT
|
||||
ctxt.Text = append(ctxt.Text, s)
|
||||
|
||||
// Set up DWARF entry for s.
|
||||
dsym := ctxt.dwarfSym(s)
|
||||
// Set up DWARF entries for s.
|
||||
dsym, drsym := ctxt.dwarfSym(s)
|
||||
dsym.Type = objabi.SDWARFINFO
|
||||
dsym.Set(AttrDuplicateOK, s.DuplicateOK())
|
||||
drsym.Type = objabi.SDWARFRANGE
|
||||
drsym.Set(AttrDuplicateOK, s.DuplicateOK())
|
||||
ctxt.Data = append(ctxt.Data, dsym)
|
||||
ctxt.Data = append(ctxt.Data, drsym)
|
||||
|
||||
// Set up the function's gcargs and gclocals.
|
||||
// They will be filled in later if needed.
|
||||
|
|
|
|||
|
|
@ -56,4 +56,5 @@ const (
|
|||
STLSBSS
|
||||
// Debugging data
|
||||
SDWARFINFO
|
||||
SDWARFRANGE
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ package objabi
|
|||
|
||||
import "fmt"
|
||||
|
||||
const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFO"
|
||||
const _SymKind_name = "SxxxSTEXTSRODATASNOPTRDATASDATASBSSSNOPTRBSSSTLSBSSSDWARFINFOSDWARFRANGE"
|
||||
|
||||
var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61}
|
||||
var _SymKind_index = [...]uint8{0, 4, 9, 16, 26, 31, 35, 44, 51, 61, 72}
|
||||
|
||||
func (i SymKind) String() string {
|
||||
if i >= SymKind(len(_SymKind_index)-1) {
|
||||
|
|
|
|||
|
|
@ -574,9 +574,18 @@ func relocsym(ctxt *Link, s *Symbol) {
|
|||
}
|
||||
|
||||
case objabi.R_DWARFREF:
|
||||
if r.Sym.Sect == nil {
|
||||
var sectName string
|
||||
var vaddr int64
|
||||
switch {
|
||||
case r.Sym.Sect != nil:
|
||||
sectName = r.Sym.Sect.Name
|
||||
vaddr = int64(r.Sym.Sect.Vaddr)
|
||||
case r.Sym.Type == SDWARFRANGE:
|
||||
sectName = ".debug_ranges"
|
||||
default:
|
||||
Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name)
|
||||
}
|
||||
|
||||
if Linkmode == LinkExternal {
|
||||
r.Done = 0
|
||||
// PE code emits IMAGE_REL_I386_SECREL and IMAGE_REL_AMD64_SECREL
|
||||
|
|
@ -588,8 +597,9 @@ func relocsym(ctxt *Link, s *Symbol) {
|
|||
r.Type = objabi.R_ADDR
|
||||
}
|
||||
|
||||
r.Xsym = ctxt.Syms.ROLookup(r.Sym.Sect.Name, 0)
|
||||
r.Xadd = r.Add + Symaddr(r.Sym) - int64(r.Sym.Sect.Vaddr)
|
||||
r.Xsym = ctxt.Syms.ROLookup(sectName, 0)
|
||||
r.Xadd = r.Add + Symaddr(r.Sym) - vaddr
|
||||
|
||||
o = r.Xadd
|
||||
rs = r.Xsym
|
||||
if Iself && SysArch.Family == sys.AMD64 {
|
||||
|
|
@ -597,7 +607,7 @@ func relocsym(ctxt *Link, s *Symbol) {
|
|||
}
|
||||
break
|
||||
}
|
||||
o = Symaddr(r.Sym) + r.Add - int64(r.Sym.Sect.Vaddr)
|
||||
o = Symaddr(r.Sym) + r.Add - vaddr
|
||||
|
||||
case objabi.R_WEAKADDROFF:
|
||||
if !r.Sym.Attr.Reachable() {
|
||||
|
|
@ -1821,6 +1831,7 @@ func (ctxt *Link) dodata() {
|
|||
if s.Type != SDWARFSECT {
|
||||
break
|
||||
}
|
||||
|
||||
sect = addsection(&Segdwarf, s.Name, 04)
|
||||
sect.Align = 1
|
||||
datsize = Rnd(datsize, int64(sect.Align))
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@
|
|||
// - 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
|
||||
|
||||
|
|
@ -76,6 +75,7 @@ var arangessec *Symbol
|
|||
var framesec *Symbol
|
||||
var infosec *Symbol
|
||||
var linesec *Symbol
|
||||
var rangesec *Symbol
|
||||
|
||||
var gdbscript string
|
||||
|
||||
|
|
@ -1291,6 +1291,33 @@ func writeframes(ctxt *Link, syms []*Symbol) []*Symbol {
|
|||
return syms
|
||||
}
|
||||
|
||||
func writeranges(ctxt *Link, syms []*Symbol) []*Symbol {
|
||||
if rangesec == nil {
|
||||
rangesec = ctxt.Syms.Lookup(".debug_ranges", 0)
|
||||
}
|
||||
rangesec.Type = SDWARFSECT
|
||||
rangesec.Attr |= AttrReachable
|
||||
rangesec.R = rangesec.R[:0]
|
||||
|
||||
for _, s := range ctxt.Textp {
|
||||
rangeSym := ctxt.Syms.Lookup(dwarf.RangePrefix+s.Name, int(s.Version))
|
||||
rangeSym.Attr |= AttrReachable
|
||||
rangeSym.Type = SDWARFRANGE
|
||||
rangeSym.Value = rangesec.Size
|
||||
rangesec.P = append(rangesec.P, rangeSym.P...)
|
||||
for _, r := range rangeSym.R {
|
||||
r.Off += int32(rangesec.Size)
|
||||
rangesec.R = append(rangesec.R, r)
|
||||
}
|
||||
rangesec.Size += rangeSym.Size
|
||||
}
|
||||
if rangesec.Size > 0 {
|
||||
// PE does not like empty sections
|
||||
syms = append(syms, rangesec)
|
||||
}
|
||||
return syms
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk DWarfDebugInfoEntries, and emit .debug_info
|
||||
*/
|
||||
|
|
@ -1321,7 +1348,7 @@ func writeinfo(ctxt *Link, syms []*Symbol, funcs []*Symbol) []*Symbol {
|
|||
// 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)
|
||||
Adduint16(ctxt, s, 3) // dwarf version (appendix F)
|
||||
|
||||
// debug_abbrev_offset (*)
|
||||
adddwarfref(ctxt, s, abbrevsym, 4)
|
||||
|
|
@ -1553,6 +1580,7 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
|
|||
syms := writeabbrev(ctxt, nil)
|
||||
syms, funcs := writelines(ctxt, syms)
|
||||
syms = writeframes(ctxt, syms)
|
||||
syms = writeranges(ctxt, syms)
|
||||
|
||||
synthesizestringtypes(ctxt, dwtypes.Child)
|
||||
synthesizeslicetypes(ctxt, dwtypes.Child)
|
||||
|
|
@ -1594,6 +1622,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
|
|||
Addstring(shstrtab, ".debug_pubnames")
|
||||
Addstring(shstrtab, ".debug_pubtypes")
|
||||
Addstring(shstrtab, ".debug_gdb_scripts")
|
||||
Addstring(shstrtab, ".debug_ranges")
|
||||
if Linkmode == LinkExternal {
|
||||
Addstring(shstrtab, elfRelType+".debug_info")
|
||||
Addstring(shstrtab, elfRelType+".debug_aranges")
|
||||
|
|
@ -1601,6 +1630,7 @@ func dwarfaddshstrings(ctxt *Link, shstrtab *Symbol) {
|
|||
Addstring(shstrtab, elfRelType+".debug_frame")
|
||||
Addstring(shstrtab, elfRelType+".debug_pubnames")
|
||||
Addstring(shstrtab, elfRelType+".debug_pubtypes")
|
||||
Addstring(shstrtab, elfRelType+".debug_ranges")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1621,6 +1651,10 @@ func dwarfaddelfsectionsyms(ctxt *Link) {
|
|||
putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
|
||||
sym = ctxt.Syms.Lookup(".debug_frame", 0)
|
||||
putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
|
||||
sym = ctxt.Syms.Lookup(".debug_ranges", 0)
|
||||
if sym.Sect != nil {
|
||||
putelfsectionsym(sym, sym.Sect.Elfsect.shnum)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@ func (s *Symbol) ElfsymForReloc() int32 {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Symbol) Len() int64 {
|
||||
return s.Size
|
||||
}
|
||||
|
||||
// Attribute is a set of common symbol attributes.
|
||||
type Attribute int16
|
||||
|
||||
|
|
|
|||
32
src/cmd/link/internal/ld/nooptcgolink_test.go
Normal file
32
src/cmd/link/internal/ld/nooptcgolink_test.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
package ld
|
||||
|
||||
import (
|
||||
"internal/testenv"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNooptCgoBuild(t *testing.T) {
|
||||
testenv.MustHaveGoBuild(t)
|
||||
testenv.MustHaveCGO(t)
|
||||
dir, err := ioutil.TempDir("", "go-build")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
cmd := exec.Command("go", "build", "-gcflags=-N -l", "-o", filepath.Join(dir, "a.out"))
|
||||
cmd.Dir = filepath.Join(runtime.GOROOT(), "src", "runtime", "testdata", "testprogcgo")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Logf("go build output: %s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -104,6 +104,7 @@ const (
|
|||
SHOSTOBJ
|
||||
SDWARFSECT
|
||||
SDWARFINFO
|
||||
SDWARFRANGE
|
||||
SSUB = SymKind(1 << 8)
|
||||
SMASK = SymKind(SSUB - 1)
|
||||
SHIDDEN = SymKind(1 << 9)
|
||||
|
|
@ -122,6 +123,7 @@ var abiSymKindToSymKind = [...]SymKind{
|
|||
SNOPTRBSS,
|
||||
STLSBSS,
|
||||
SDWARFINFO,
|
||||
SDWARFRANGE,
|
||||
}
|
||||
|
||||
// readOnly are the symbol kinds that form read-only sections. In some
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ package ld
|
|||
|
||||
import "fmt"
|
||||
|
||||
const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFO"
|
||||
const _SymKind_name = "SxxxSTEXTSELFRXSECTSTYPESSTRINGSGOSTRINGSGOFUNCSGCBITSSRODATASFUNCTABSELFROSECTSMACHOPLTSTYPERELROSSTRINGRELROSGOSTRINGRELROSGOFUNCRELROSGCBITSRELROSRODATARELROSFUNCTABRELROSTYPELINKSITABLINKSSYMTABSPCLNTABSELFSECTSMACHOSMACHOGOTSWINDOWSSELFGOTSNOPTRDATASINITARRSDATASBSSSNOPTRBSSSTLSBSSSXREFSMACHOSYMSTRSMACHOSYMTABSMACHOINDIRECTPLTSMACHOINDIRECTGOTSFILESFILEPATHSCONSTSDYNIMPORTSHOSTOBJSDWARFSECTSDWARFINFOSDWARFRANGE"
|
||||
|
||||
var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 214, 220, 229, 237, 244, 254, 262, 267, 271, 280, 287, 292, 304, 316, 333, 350, 355, 364, 370, 380, 388, 398, 408}
|
||||
var _SymKind_index = [...]uint16{0, 4, 9, 19, 24, 31, 40, 47, 54, 61, 69, 79, 88, 98, 110, 124, 136, 148, 160, 173, 182, 191, 198, 206, 214, 220, 229, 237, 244, 254, 262, 267, 271, 280, 287, 292, 304, 316, 333, 350, 355, 364, 370, 380, 388, 398, 408, 419}
|
||||
|
||||
func (i SymKind) String() string {
|
||||
if i < 0 || i >= SymKind(len(_SymKind_index)-1) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue