2015-02-27 22:57:28 -05:00
// 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.
2015-03-05 13:57:36 -05:00
// TODO/NICETOHAVE:
// - eliminate DW_CLS_ if not used
// - package info in compilation units
2018-09-25 11:52:24 +02:00
// - assign types to their packages
2015-03-05 13:57:36 -05:00
// - gdb uses c syntax, meaning clumsy quoting is needed for go identifiers. eg
// ptype struct '[]uint8' and qualifiers need to be quoted away
// - file:line info for variables
// - make strings a typedef so prettyprinters can see the underlying string type
2015-02-27 22:57:28 -05:00
package ld
import (
2016-07-28 13:04:41 -04:00
"cmd/internal/dwarf"
2018-04-30 23:52:14 -04:00
"cmd/internal/obj"
2017-04-18 12:53:25 -07:00
"cmd/internal/objabi"
2017-10-01 02:37:20 +00:00
"cmd/internal/sys"
2017-10-04 17:54:04 -04:00
"cmd/link/internal/sym"
2015-02-27 22:57:28 -05:00
"fmt"
2016-03-14 09:23:04 -07:00
"log"
2018-04-24 13:05:10 +02:00
"sort"
2015-02-27 22:57:28 -05:00
"strings"
)
2016-08-19 22:40:38 -04:00
type dwctxt struct {
linkctxt * Link
}
2016-03-14 09:23:04 -07:00
2016-08-19 22:40:38 -04:00
func ( c dwctxt ) PtrSize ( ) int {
2017-09-30 21:10:49 +00:00
return c . linkctxt . Arch . PtrSize
2015-02-27 22:57:28 -05:00
}
2016-08-19 22:40:38 -04:00
func ( c dwctxt ) AddInt ( s dwarf . Sym , size int , i int64 ) {
2017-10-04 17:54:04 -04:00
ls := s . ( * sym . Symbol )
ls . AddUintXX ( c . linkctxt . Arch , uint64 ( i ) , size )
2015-02-27 22:57:28 -05:00
}
2016-08-19 22:40:38 -04:00
func ( c dwctxt ) AddBytes ( s dwarf . Sym , b [ ] byte ) {
2017-10-04 17:54:04 -04:00
ls := s . ( * sym . Symbol )
2017-09-30 15:06:44 +00:00
ls . AddBytes ( b )
2016-07-28 13:04:41 -04:00
}
2016-08-19 22:40:38 -04:00
func ( c dwctxt ) AddString ( s dwarf . Sym , v string ) {
2017-10-04 17:54:04 -04:00
Addstring ( s . ( * sym . Symbol ) , v )
2016-07-28 13:04:41 -04:00
}
2015-02-27 22:57:28 -05:00
2016-08-19 22:40:38 -04:00
func ( c dwctxt ) AddAddress ( s dwarf . Sym , data interface { } , value int64 ) {
2016-07-28 13:04:41 -04:00
if value != 0 {
2017-10-04 17:54:04 -04:00
value -= ( data . ( * sym . Symbol ) ) . Value
2016-07-28 13:04:41 -04:00
}
2017-10-04 17:54:04 -04:00
s . ( * sym . Symbol ) . AddAddrPlus ( c . linkctxt . Arch , data . ( * sym . Symbol ) , value )
2015-02-27 22:57:28 -05:00
}
2016-08-19 22:40:38 -04:00
func ( c dwctxt ) AddSectionOffset ( s dwarf . Sym , size int , t interface { } , ofs int64 ) {
2017-10-04 17:54:04 -04:00
ls := s . ( * sym . Symbol )
2016-07-28 13:04:41 -04:00
switch size {
default :
2016-09-17 09:39:33 -04:00
Errorf ( ls , "invalid size %d in adddwarfref\n" , size )
2016-07-28 13:04:41 -04:00
fallthrough
2017-09-30 21:10:49 +00:00
case c . linkctxt . Arch . PtrSize :
2017-10-04 17:54:04 -04:00
ls . AddAddr ( c . linkctxt . Arch , t . ( * sym . Symbol ) )
2016-07-28 13:04:41 -04:00
case 4 :
2017-10-04 17:54:04 -04:00
ls . AddAddrPlus4 ( t . ( * sym . Symbol ) , 0 )
2016-07-28 13:04:41 -04:00
}
r := & ls . R [ len ( ls . R ) - 1 ]
2018-04-12 17:07:14 -04:00
r . Type = objabi . R_ADDROFF
2016-07-28 13:04:41 -04:00
r . Add = ofs
2015-02-27 22:57:28 -05:00
}
2018-04-12 17:07:14 -04:00
func ( c dwctxt ) AddDWARFSectionOffset ( s dwarf . Sym , size int , t interface { } , ofs int64 ) {
c . AddSectionOffset ( s , size , t , ofs )
ls := s . ( * sym . Symbol )
ls . R [ len ( ls . R ) - 1 ] . Type = objabi . R_DWARFSECREF
}
2017-10-06 11:32:28 -04:00
func ( c dwctxt ) Logf ( format string , args ... interface { } ) {
c . linkctxt . Logf ( format , args ... )
}
// At the moment these interfaces are only used in the compiler.
2017-10-24 16:08:46 -04:00
func ( c dwctxt ) AddFileRef ( s dwarf . Sym , f interface { } ) {
panic ( "should be used only in the compiler" )
}
2017-10-06 11:32:28 -04:00
func ( c dwctxt ) CurrentOffset ( s dwarf . Sym ) int64 {
panic ( "should be used only in the compiler" )
}
func ( c dwctxt ) RecordDclReference ( s dwarf . Sym , t dwarf . Sym , dclIdx int , inlIndex int ) {
panic ( "should be used only in the compiler" )
}
func ( c dwctxt ) RecordChildDieOffsets ( s dwarf . Sym , vars [ ] * dwarf . Var , offsets [ ] int32 ) {
panic ( "should be used only in the compiler" )
}
2016-07-28 13:04:41 -04:00
var gdbscript string
2015-02-27 22:57:28 -05:00
2017-10-04 17:54:04 -04:00
var dwarfp [ ] * sym . Symbol
2016-03-14 09:23:04 -07:00
2017-10-04 17:54:04 -04:00
func writeabbrev ( ctxt * Link ) * sym . Symbol {
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
s := ctxt . Syms . Lookup ( ".debug_abbrev" , 0 )
2017-10-04 17:54:04 -04:00
s . Type = sym . SDWARFSECT
2017-09-30 15:06:44 +00:00
s . AddBytes ( dwarf . GetAbbrev ( ) )
2017-05-22 20:17:31 -04:00
return s
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
var dwtypes dwarf . DWDie
2015-02-27 22:57:28 -05:00
2016-07-28 13:04:41 -04:00
func newattr ( die * dwarf . DWDie , attr uint16 , cls int , value int64 , data interface { } ) * dwarf . DWAttr {
a := new ( dwarf . DWAttr )
a . Link = die . Attr
die . Attr = a
a . Atr = attr
a . Cls = uint8 ( cls )
a . Value = value
a . Data = data
2015-02-27 22:57:28 -05:00
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.
2016-07-28 13:04:41 -04:00
func getattr ( die * dwarf . DWDie , attr uint16 ) * dwarf . DWAttr {
if die . Attr . Atr == attr {
return die . Attr
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
a := die . Attr
b := a . Link
2015-02-27 22:57:28 -05:00
for b != nil {
2016-07-28 13:04:41 -04:00
if b . Atr == attr {
a . Link = b . Link
b . Link = die . Attr
die . Attr = b
2015-02-27 22:57:28 -05:00
return b
}
a = b
2016-07-28 13:04:41 -04:00
b = b . Link
2015-02-27 22:57:28 -05:00
}
return nil
}
2017-10-06 11:32:28 -04:00
// Every DIE manufactured by the linker has at least an AT_name
// attribute (but it will only be written out if it is listed in the abbrev).
// The compiler does create nameless DWARF DIEs (ex: concrete subprogram
// instance).
2016-08-19 22:40:38 -04:00
func newdie ( ctxt * Link , parent * dwarf . DWDie , abbrev int , name string , version int ) * dwarf . DWDie {
2016-07-28 13:04:41 -04:00
die := new ( dwarf . DWDie )
die . Abbrev = abbrev
die . Link = parent . Child
parent . Child = die
newattr ( die , dwarf . DW_AT_name , dwarf . DW_CLS_STRING , int64 ( len ( name ) ) , name )
if name != "" && ( abbrev <= dwarf . DW_ABRV_VARIABLE || abbrev >= dwarf . DW_ABRV_NULLTYPE ) {
if abbrev != dwarf . DW_ABRV_VARIABLE || version == 0 {
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
if abbrev == dwarf . DW_ABRV_COMPUNIT {
// Avoid collisions with "real" symbol names.
name = ".pkg." + name
}
2017-10-04 17:54:04 -04:00
s := ctxt . Syms . Lookup ( dwarf . InfoPrefix + name , version )
s . Attr |= sym . AttrNotInSymbolTable
s . Type = sym . SDWARFINFO
die . Sym = s
2016-03-14 09:23:04 -07:00
}
2015-02-27 22:57:28 -05:00
}
return die
}
2016-07-28 13:04:41 -04:00
func walktypedef ( die * dwarf . DWDie ) * dwarf . DWDie {
if die == nil {
return nil
}
2015-02-27 22:57:28 -05:00
// Resolve typedef if present.
2016-07-28 13:04:41 -04:00
if die . Abbrev == dwarf . DW_ABRV_TYPEDECL {
for attr := die . Attr ; attr != nil ; attr = attr . Link {
if attr . Atr == dwarf . DW_AT_type && attr . Cls == dwarf . DW_CLS_REFERENCE && attr . Data != nil {
return attr . Data . ( * dwarf . DWDie )
2015-02-27 22:57:28 -05:00
}
}
}
return die
}
2017-10-04 17:54:04 -04:00
func walksymtypedef ( ctxt * Link , s * sym . Symbol ) * sym . Symbol {
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
if t := ctxt . Syms . ROLookup ( s . Name + "..def" , int ( s . Version ) ) ; t != nil {
2016-03-14 09:23:04 -07:00
return t
}
return s
}
2015-02-27 22:57:28 -05:00
// Find child by AT_name using hashtable if available or linear scan
// if not.
2016-07-28 13:04:41 -04:00
func findchild ( die * dwarf . DWDie , name string ) * dwarf . DWDie {
var prev * dwarf . DWDie
2015-10-10 10:57:35 +00:00
for ; die != prev ; prev , die = die , walktypedef ( die ) {
2016-07-28 13:04:41 -04:00
for a := die . Child ; a != nil ; a = a . Link {
if name == getattr ( a , dwarf . DW_AT_name ) . Data {
2016-03-14 09:23:04 -07:00
return a
2015-02-27 22:57:28 -05:00
}
2015-10-10 10:57:35 +00:00
}
2016-03-14 09:23:04 -07:00
continue
2015-02-27 22:57:28 -05:00
}
return nil
}
2016-04-07 18:00:57 +03:00
// Used to avoid string allocation when looking up dwarf symbols
2016-07-28 13:04:41 -04:00
var prefixBuf = [ ] byte ( dwarf . InfoPrefix )
2016-04-07 18:00:57 +03:00
2017-10-04 17:54:04 -04:00
func find ( ctxt * Link , name string ) * sym . Symbol {
2016-04-07 18:00:57 +03:00
n := append ( prefixBuf , name ... )
// The string allocation below is optimized away because it is only used in a map lookup.
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
s := ctxt . Syms . ROLookup ( string ( n ) , 0 )
2016-07-28 13:04:41 -04:00
prefixBuf = n [ : len ( dwarf . InfoPrefix ) ]
2017-10-04 17:54:04 -04:00
if s != nil && s . Type == sym . SDWARFINFO {
2016-07-28 13:04:41 -04:00
return s
}
return nil
2016-03-14 09:23:04 -07:00
}
2017-10-04 17:54:04 -04:00
func mustFind ( ctxt * Link , name string ) * sym . Symbol {
2016-08-19 22:40:38 -04:00
r := find ( ctxt , name )
2015-02-27 22:57:28 -05:00
if r == nil {
2016-03-14 09:23:04 -07:00
Exitf ( "dwarf find: cannot find %s" , name )
2015-02-27 22:57:28 -05:00
}
return r
}
2017-10-04 17:54:04 -04:00
func adddwarfref ( ctxt * Link , s * sym . Symbol , t * sym . Symbol , size int ) int64 {
2016-03-14 09:23:04 -07:00
var result int64
switch size {
2015-02-27 22:57:28 -05:00
default :
2016-09-17 09:39:33 -04:00
Errorf ( s , "invalid size %d in adddwarfref\n" , size )
2016-03-14 09:23:04 -07:00
fallthrough
2017-09-30 21:10:49 +00:00
case ctxt . Arch . PtrSize :
2017-09-30 15:06:44 +00:00
result = s . AddAddr ( ctxt . Arch , t )
2016-03-14 09:23:04 -07:00
case 4 :
2017-09-30 15:06:44 +00:00
result = s . AddAddrPlus4 ( t , 0 )
2015-02-27 22:57:28 -05:00
}
2016-03-14 09:23:04 -07:00
r := & s . R [ len ( s . R ) - 1 ]
2017-10-24 16:08:46 -04:00
r . Type = objabi . R_DWARFSECREF
2016-03-14 09:23:04 -07:00
return result
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
func newrefattr ( die * dwarf . DWDie , attr uint16 , ref * sym . Symbol ) * dwarf . DWAttr {
2015-02-27 22:57:28 -05:00
if ref == nil {
return nil
}
2016-07-28 13:04:41 -04:00
return newattr ( die , attr , dwarf . DW_CLS_REFERENCE , 0 , ref )
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
func putdies ( linkctxt * Link , ctxt dwarf . Context , syms [ ] * sym . Symbol , die * dwarf . DWDie ) [ ] * sym . Symbol {
2016-07-28 13:04:41 -04:00
for ; die != nil ; die = die . Link {
2016-08-19 22:40:38 -04:00
syms = putdie ( linkctxt , ctxt , syms , die )
2015-02-27 22:57:28 -05:00
}
2017-09-30 15:06:44 +00:00
syms [ len ( syms ) - 1 ] . AddUint8 ( 0 )
2015-02-27 22:57:28 -05:00
2016-07-28 13:04:41 -04:00
return syms
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
func dtolsym ( s dwarf . Sym ) * sym . Symbol {
2016-07-28 13:04:41 -04:00
if s == nil {
return nil
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
return s . ( * sym . Symbol )
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
func putdie ( linkctxt * Link , ctxt dwarf . Context , syms [ ] * sym . Symbol , die * dwarf . DWDie ) [ ] * sym . Symbol {
2016-07-28 13:04:41 -04:00
s := dtolsym ( die . Sym )
2016-03-14 09:23:04 -07:00
if s == nil {
2016-07-28 13:04:41 -04:00
s = syms [ len ( syms ) - 1 ]
2016-03-14 09:23:04 -07:00
} else {
if s . Attr . OnList ( ) {
log . Fatalf ( "symbol %s listed multiple times" , s . Name )
}
2017-10-04 17:54:04 -04:00
s . Attr |= sym . AttrOnList
2016-07-28 13:04:41 -04:00
syms = append ( syms , s )
2016-03-14 09:23:04 -07:00
}
2016-07-28 13:04:41 -04:00
dwarf . Uleb128put ( ctxt , s , int64 ( die . Abbrev ) )
dwarf . PutAttrs ( ctxt , s , die . Abbrev , die . Attr )
if dwarf . HasChildren ( die ) {
2016-08-19 22:40:38 -04:00
return putdies ( linkctxt , ctxt , syms , die . Child )
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
return syms
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
func reverselist ( list * * dwarf . DWDie ) {
2015-03-02 12:35:15 -05:00
curr := * list
2016-07-28 13:04:41 -04:00
var prev * dwarf . DWDie
2015-02-27 22:57:28 -05:00
for curr != nil {
2017-10-09 10:11:00 +01:00
next := curr . Link
2016-07-28 13:04:41 -04:00
curr . Link = prev
2015-02-27 22:57:28 -05:00
prev = curr
curr = next
}
* list = prev
}
2016-07-28 13:04:41 -04:00
func reversetree ( list * * dwarf . DWDie ) {
2015-02-27 22:57:28 -05:00
reverselist ( list )
2016-07-28 13:04:41 -04:00
for die := * list ; die != nil ; die = die . Link {
if dwarf . HasChildren ( die ) {
reversetree ( & die . Child )
2015-02-27 22:57:28 -05:00
}
}
}
2016-07-28 13:04:41 -04:00
func newmemberoffsetattr ( die * dwarf . DWDie , offs int32 ) {
2017-07-26 15:58:25 -04:00
newattr ( die , dwarf . DW_AT_data_member_location , dwarf . DW_CLS_CONSTANT , int64 ( offs ) , nil )
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
// GDB doesn't like FORM_addr for AT_location, so emit a
2015-02-27 22:57:28 -05:00
// location expression that evals to a const.
2017-10-04 17:54:04 -04:00
func newabslocexprattr ( die * dwarf . DWDie , addr int64 , sym * sym . Symbol ) {
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_location , dwarf . DW_CLS_ADDRESS , addr , sym )
2015-02-27 22:57:28 -05:00
// below
}
// Lookup predefined types
2017-10-04 17:54:04 -04:00
func lookupOrDiag ( ctxt * Link , n string ) * sym . Symbol {
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
s := ctxt . Syms . ROLookup ( n , 0 )
2015-02-27 22:57:28 -05:00
if s == nil || s . Size == 0 {
2015-04-09 07:37:17 -04:00
Exitf ( "dwarf: missing type: %s" , n )
2015-02-27 22:57:28 -05:00
}
return s
}
2018-09-10 15:36:59 +02:00
func dotypedef ( ctxt * Link , parent * dwarf . DWDie , name string , def * dwarf . DWDie ) * dwarf . DWDie {
2015-02-27 22:57:28 -05:00
// Only emit typedefs for real names.
if strings . HasPrefix ( name , "map[" ) {
2018-09-10 15:36:59 +02:00
return nil
2015-02-27 22:57:28 -05:00
}
if strings . HasPrefix ( name , "struct {" ) {
2018-09-10 15:36:59 +02:00
return nil
2015-02-27 22:57:28 -05:00
}
if strings . HasPrefix ( name , "chan " ) {
2018-09-10 15:36:59 +02:00
return nil
2015-02-27 22:57:28 -05:00
}
if name [ 0 ] == '[' || name [ 0 ] == '*' {
2018-09-10 15:36:59 +02:00
return nil
2015-02-27 22:57:28 -05:00
}
if def == nil {
2016-09-17 09:39:33 -04:00
Errorf ( nil , "dwarf: bad def in dotypedef" )
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
s := ctxt . Syms . Lookup ( dtolsym ( def . Sym ) . Name + "..def" , 0 )
s . Attr |= sym . AttrNotInSymbolTable
s . Type = sym . SDWARFINFO
def . Sym = s
2016-03-14 09:23:04 -07:00
2015-02-27 22:57:28 -05:00
// 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.
2016-08-19 22:40:38 -04:00
die := newdie ( ctxt , parent , dwarf . DW_ABRV_TYPEDECL , name , 0 )
2015-02-27 22:57:28 -05:00
2017-10-04 17:54:04 -04:00
newrefattr ( die , dwarf . DW_AT_type , s )
2018-09-10 15:36:59 +02:00
return die
2015-02-27 22:57:28 -05:00
}
// Define gotype, for composite ones recurse into constituents.
2017-10-04 17:54:04 -04:00
func defgotype ( ctxt * Link , gotype * sym . Symbol ) * sym . Symbol {
2015-02-27 22:57:28 -05:00
if gotype == nil {
2016-08-19 22:40:38 -04:00
return mustFind ( ctxt , "<unspecified>" )
2015-02-27 22:57:28 -05:00
}
if ! strings . HasPrefix ( gotype . Name , "type." ) {
2016-09-17 09:39:33 -04:00
Errorf ( gotype , "dwarf: type name doesn't start with \"type.\"" )
2016-08-19 22:40:38 -04:00
return mustFind ( ctxt , "<unspecified>" )
2015-02-27 22:57:28 -05:00
}
2015-03-02 12:35:15 -05:00
name := gotype . Name [ 5 : ] // could also decode from Type.string
2015-02-27 22:57:28 -05:00
2016-08-19 22:40:38 -04:00
sdie := find ( ctxt , name )
2015-02-27 22:57:28 -05:00
2016-03-14 09:23:04 -07:00
if sdie != nil {
return sdie
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
return newtype ( ctxt , gotype ) . Sym . ( * sym . Symbol )
2016-03-14 09:23:04 -07:00
}
2015-02-27 22:57:28 -05:00
2017-10-04 17:54:04 -04:00
func newtype ( ctxt * Link , gotype * sym . Symbol ) * dwarf . DWDie {
2016-03-14 09:23:04 -07:00
name := gotype . Name [ 5 : ] // could also decode from Type.string
2017-09-30 21:10:49 +00:00
kind := decodetypeKind ( ctxt . Arch , gotype )
2016-08-22 10:33:13 -04:00
bytesize := decodetypeSize ( ctxt . Arch , gotype )
2015-02-27 22:57:28 -05:00
2018-09-10 15:36:59 +02:00
var die , typedefdie * dwarf . DWDie
2015-02-27 22:57:28 -05:00
switch kind {
2017-04-18 12:53:25 -07:00
case objabi . KindBool :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_BASETYPE , name , 0 )
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_encoding , dwarf . DW_CLS_CONSTANT , dwarf . DW_ATE_boolean , 0 )
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindInt ,
objabi . KindInt8 ,
objabi . KindInt16 ,
objabi . KindInt32 ,
objabi . KindInt64 :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_BASETYPE , name , 0 )
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_encoding , dwarf . DW_CLS_CONSTANT , dwarf . DW_ATE_signed , 0 )
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindUint ,
objabi . KindUint8 ,
objabi . KindUint16 ,
objabi . KindUint32 ,
objabi . KindUint64 ,
objabi . KindUintptr :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_BASETYPE , name , 0 )
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_encoding , dwarf . DW_CLS_CONSTANT , dwarf . DW_ATE_unsigned , 0 )
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindFloat32 ,
objabi . KindFloat64 :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_BASETYPE , name , 0 )
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_encoding , dwarf . DW_CLS_CONSTANT , dwarf . DW_ATE_float , 0 )
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindComplex64 ,
objabi . KindComplex128 :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_BASETYPE , name , 0 )
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_encoding , dwarf . DW_CLS_CONSTANT , dwarf . DW_ATE_complex_float , 0 )
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindArray :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_ARRAYTYPE , name , 0 )
2018-09-10 15:36:59 +02:00
typedefdie = dotypedef ( ctxt , & dwtypes , name , die )
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2017-09-30 21:10:49 +00:00
s := decodetypeArrayElem ( ctxt . Arch , gotype )
2016-08-19 22:40:38 -04:00
newrefattr ( die , dwarf . DW_AT_type , defgotype ( ctxt , s ) )
fld := newdie ( ctxt , die , dwarf . DW_ABRV_ARRAYRANGE , "range" , 0 )
2015-02-27 22:57:28 -05:00
// use actual length not upper bound; correct for 0-length arrays.
2016-08-22 10:33:13 -04:00
newattr ( fld , dwarf . DW_AT_count , dwarf . DW_CLS_CONSTANT , decodetypeArrayLen ( ctxt . Arch , gotype ) , 0 )
2015-02-27 22:57:28 -05:00
2016-08-19 22:40:38 -04:00
newrefattr ( fld , dwarf . DW_AT_type , mustFind ( ctxt , "uintptr" ) )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindChan :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_CHANTYPE , name , 0 )
2017-09-30 21:10:49 +00:00
s := decodetypeChanElem ( ctxt . Arch , gotype )
2016-08-19 22:40:38 -04:00
newrefattr ( die , dwarf . DW_AT_go_elem , defgotype ( ctxt , s ) )
2016-03-14 09:23:04 -07:00
// Save elem type for synthesizechantypes. We could synthesize here
// but that would change the order of DIEs we output.
2016-07-28 13:04:41 -04:00
newrefattr ( die , dwarf . DW_AT_type , s )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindFunc :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_FUNCTYPE , name , 0 )
2017-08-14 15:07:57 -07:00
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2018-09-10 15:36:59 +02:00
typedefdie = dotypedef ( ctxt , & dwtypes , name , die )
2016-08-22 10:33:13 -04:00
nfields := decodetypeFuncInCount ( ctxt . Arch , gotype )
2015-03-02 12:35:15 -05:00
for i := 0 ; i < nfields ; i ++ {
2018-04-06 21:41:06 +01:00
s := decodetypeFuncInType ( ctxt . Arch , gotype , i )
fld := newdie ( ctxt , die , dwarf . DW_ABRV_FUNCTYPEPARAM , s . Name [ 5 : ] , 0 )
2016-08-19 22:40:38 -04:00
newrefattr ( fld , dwarf . DW_AT_type , defgotype ( ctxt , s ) )
2015-02-27 22:57:28 -05:00
}
2016-08-22 10:33:13 -04:00
if decodetypeFuncDotdotdot ( ctxt . Arch , gotype ) {
2016-08-19 22:40:38 -04:00
newdie ( ctxt , die , dwarf . DW_ABRV_DOTDOTDOT , "..." , 0 )
2015-02-27 22:57:28 -05:00
}
2016-08-22 10:33:13 -04:00
nfields = decodetypeFuncOutCount ( ctxt . Arch , gotype )
2015-03-02 12:35:15 -05:00
for i := 0 ; i < nfields ; i ++ {
2018-04-06 21:41:06 +01:00
s := decodetypeFuncOutType ( ctxt . Arch , gotype , i )
fld := newdie ( ctxt , die , dwarf . DW_ABRV_FUNCTYPEPARAM , s . Name [ 5 : ] , 0 )
2016-08-19 22:40:38 -04:00
newrefattr ( fld , dwarf . DW_AT_type , defptrto ( ctxt , defgotype ( ctxt , s ) ) )
2015-02-27 22:57:28 -05:00
}
2017-04-18 12:53:25 -07:00
case objabi . KindInterface :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_IFACETYPE , name , 0 )
2018-09-10 15:36:59 +02:00
typedefdie = dotypedef ( ctxt , & dwtypes , name , die )
2016-08-22 10:33:13 -04:00
nfields := int ( decodetypeIfaceMethodCount ( ctxt . Arch , gotype ) )
2017-10-04 17:54:04 -04:00
var s * sym . Symbol
2015-02-27 22:57:28 -05:00
if nfields == 0 {
2016-08-22 10:33:13 -04:00
s = lookupOrDiag ( ctxt , "type.runtime.eface" )
2015-02-27 22:57:28 -05:00
} else {
2016-08-22 10:33:13 -04:00
s = lookupOrDiag ( ctxt , "type.runtime.iface" )
2015-02-27 22:57:28 -05:00
}
2016-08-19 22:40:38 -04:00
newrefattr ( die , dwarf . DW_AT_type , defgotype ( ctxt , s ) )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindMap :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_MAPTYPE , name , 0 )
2017-09-30 21:10:49 +00:00
s := decodetypeMapKey ( ctxt . Arch , gotype )
2016-08-19 22:40:38 -04:00
newrefattr ( die , dwarf . DW_AT_go_key , defgotype ( ctxt , s ) )
2017-09-30 21:10:49 +00:00
s = decodetypeMapValue ( ctxt . Arch , gotype )
2016-08-19 22:40:38 -04:00
newrefattr ( die , dwarf . DW_AT_go_elem , defgotype ( ctxt , s ) )
2016-03-14 09:23:04 -07:00
// Save gotype for use in synthesizemaptypes. We could synthesize here,
// but that would change the order of the DIEs.
2016-07-28 13:04:41 -04:00
newrefattr ( die , dwarf . DW_AT_type , gotype )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindPtr :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_PTRTYPE , name , 0 )
2018-09-10 15:36:59 +02:00
typedefdie = dotypedef ( ctxt , & dwtypes , name , die )
2017-09-30 21:10:49 +00:00
s := decodetypePtrElem ( ctxt . Arch , gotype )
2016-08-19 22:40:38 -04:00
newrefattr ( die , dwarf . DW_AT_type , defgotype ( ctxt , s ) )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindSlice :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_SLICETYPE , name , 0 )
2018-09-10 15:36:59 +02:00
typedefdie = dotypedef ( ctxt , & dwtypes , name , die )
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2017-09-30 21:10:49 +00:00
s := decodetypeArrayElem ( ctxt . Arch , gotype )
2016-08-19 22:40:38 -04:00
elem := defgotype ( ctxt , s )
2016-07-28 13:04:41 -04:00
newrefattr ( die , dwarf . DW_AT_go_elem , elem )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindString :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_STRINGTYPE , name , 0 )
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2015-02-27 22:57:28 -05:00
2017-04-18 12:53:25 -07:00
case objabi . KindStruct :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_STRUCTTYPE , name , 0 )
2018-09-10 15:36:59 +02:00
typedefdie = dotypedef ( ctxt , & dwtypes , name , die )
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , bytesize , 0 )
2016-08-22 10:33:13 -04:00
nfields := decodetypeStructFieldCount ( ctxt . Arch , gotype )
2015-03-02 12:35:15 -05:00
for i := 0 ; i < nfields ; i ++ {
2017-09-30 21:10:49 +00:00
f := decodetypeStructFieldName ( ctxt . Arch , gotype , i )
s := decodetypeStructFieldType ( ctxt . Arch , gotype , i )
2015-02-27 22:57:28 -05:00
if f == "" {
f = s . Name [ 5 : ] // skip "type."
}
2017-04-26 17:58:31 -04:00
fld := newdie ( ctxt , die , dwarf . DW_ABRV_STRUCTFIELD , f , 0 )
2016-08-19 22:40:38 -04:00
newrefattr ( fld , dwarf . DW_AT_type , defgotype ( ctxt , s ) )
2017-04-26 17:58:31 -04:00
offsetAnon := decodetypeStructFieldOffsAnon ( ctxt . Arch , gotype , i )
newmemberoffsetattr ( fld , int32 ( offsetAnon >> 1 ) )
if offsetAnon & 1 != 0 { // is embedded field
newattr ( fld , dwarf . DW_AT_go_embedded_field , dwarf . DW_CLS_FLAG , 1 , 0 )
}
2015-02-27 22:57:28 -05:00
}
2017-04-18 12:53:25 -07:00
case objabi . KindUnsafePointer :
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_BARE_PTRTYPE , name , 0 )
2015-02-27 22:57:28 -05:00
default :
2016-09-17 09:39:33 -04:00
Errorf ( gotype , "dwarf: definition of unknown kind %d" , kind )
2016-08-19 22:40:38 -04:00
die = newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_TYPEDECL , name , 0 )
newrefattr ( die , dwarf . DW_AT_type , mustFind ( ctxt , "<unspecified>" ) )
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_go_kind , dwarf . DW_CLS_CONSTANT , int64 ( kind ) , 0 )
2018-04-12 17:07:14 -04:00
if gotype . Attr . Reachable ( ) {
newattr ( die , dwarf . DW_AT_go_runtime_type , dwarf . DW_CLS_GO_TYPEREF , 0 , gotype )
}
2015-02-27 22:57:28 -05:00
2016-03-14 09:23:04 -07:00
if _ , ok := prototypedies [ gotype . Name ] ; ok {
prototypedies [ gotype . Name ] = die
}
2018-09-10 15:36:59 +02:00
if typedefdie != nil {
return typedefdie
}
2015-02-27 22:57:28 -05:00
return die
}
2017-10-04 17:54:04 -04:00
func nameFromDIESym ( dwtype * sym . Symbol ) string {
2016-07-28 13:04:41 -04:00
return strings . TrimSuffix ( dwtype . Name [ len ( dwarf . InfoPrefix ) : ] , "..def" )
2016-03-14 09:23:04 -07:00
}
2015-02-27 22:57:28 -05:00
// Find or construct *T given T.
2017-10-04 17:54:04 -04:00
func defptrto ( ctxt * Link , dwtype * sym . Symbol ) * sym . Symbol {
2016-03-14 09:23:04 -07:00
ptrname := "*" + nameFromDIESym ( dwtype )
2018-04-12 17:07:14 -04:00
if die := find ( ctxt , ptrname ) ; die != nil {
return die
2015-02-27 22:57:28 -05:00
}
2018-04-12 17:07:14 -04:00
pdie := newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_PTRTYPE , ptrname , 0 )
newrefattr ( pdie , dwarf . DW_AT_type , dwtype )
// The DWARF info synthesizes pointer types that don't exist at the
// language level, like *hash<...> and *bucket<...>, and the data
// pointers of slices. Link to the ones we can find.
gotype := ctxt . Syms . ROLookup ( "type." + ptrname , 0 )
if gotype != nil && gotype . Attr . Reachable ( ) {
newattr ( pdie , dwarf . DW_AT_go_runtime_type , dwarf . DW_CLS_GO_TYPEREF , 0 , gotype )
}
return dtolsym ( pdie . Sym )
2015-02-27 22:57:28 -05:00
}
// Copies src's children into dst. Copies attributes by value.
2016-03-01 23:21:55 +00:00
// DWAttr.data is copied as pointer only. If except is one of
2015-02-27 22:57:28 -05:00
// the top-level children, it will not be copied.
2016-08-19 22:40:38 -04:00
func copychildrenexcept ( ctxt * Link , dst * dwarf . DWDie , src * dwarf . DWDie , except * dwarf . DWDie ) {
2016-07-28 13:04:41 -04:00
for src = src . Child ; src != nil ; src = src . Link {
2015-02-27 22:57:28 -05:00
if src == except {
continue
}
2016-08-19 22:40:38 -04:00
c := newdie ( ctxt , dst , src . Abbrev , getattr ( src , dwarf . DW_AT_name ) . Data . ( string ) , 0 )
2016-07-28 13:04:41 -04:00
for a := src . Attr ; a != nil ; a = a . Link {
newattr ( c , a . Atr , int ( a . Cls ) , a . Value , a . Data )
2015-02-27 22:57:28 -05:00
}
2016-08-19 22:40:38 -04:00
copychildrenexcept ( ctxt , c , src , nil )
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
reverselist ( & dst . Child )
2015-02-27 22:57:28 -05:00
}
2016-08-19 22:40:38 -04:00
func copychildren ( ctxt * Link , dst * dwarf . DWDie , src * dwarf . DWDie ) {
copychildrenexcept ( ctxt , dst , src , nil )
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
// Search children (assumed to have TAG_member) for the one named
// field and set its AT_type to dwtype
2017-10-04 17:54:04 -04:00
func substitutetype ( structdie * dwarf . DWDie , field string , dwtype * sym . Symbol ) {
2016-03-14 09:23:04 -07:00
child := findchild ( structdie , field )
2015-02-27 22:57:28 -05:00
if child == nil {
2016-03-14 09:23:04 -07:00
Exitf ( "dwarf substitutetype: %s does not have member %s" ,
2016-07-28 13:04:41 -04:00
getattr ( structdie , dwarf . DW_AT_name ) . Data , field )
2015-02-27 22:57:28 -05:00
return
}
2016-07-28 13:04:41 -04:00
a := getattr ( child , dwarf . DW_AT_type )
2015-02-27 22:57:28 -05:00
if a != nil {
2016-07-28 13:04:41 -04:00
a . Data = dwtype
2015-02-27 22:57:28 -05:00
} else {
2016-07-28 13:04:41 -04:00
newrefattr ( child , dwarf . DW_AT_type , dwtype )
2015-02-27 22:57:28 -05:00
}
}
2016-08-19 22:40:38 -04:00
func findprotodie ( ctxt * Link , name string ) * dwarf . DWDie {
2016-03-14 09:23:04 -07:00
die , ok := prototypedies [ name ]
if ok && die == nil {
2016-08-22 10:33:13 -04:00
defgotype ( ctxt , lookupOrDiag ( ctxt , name ) )
2016-03-14 09:23:04 -07:00
die = prototypedies [ name ]
}
return die
}
2016-08-19 22:40:38 -04:00
func synthesizestringtypes ( ctxt * Link , die * dwarf . DWDie ) {
prototype := walktypedef ( findprotodie ( ctxt , "type.runtime.stringStructDWARF" ) )
2015-02-27 22:57:28 -05:00
if prototype == nil {
return
}
2016-07-28 13:04:41 -04:00
for ; die != nil ; die = die . Link {
if die . Abbrev != dwarf . DW_ABRV_STRINGTYPE {
2015-02-27 22:57:28 -05:00
continue
}
2016-08-19 22:40:38 -04:00
copychildren ( ctxt , die , prototype )
2015-02-27 22:57:28 -05:00
}
}
2016-08-19 22:40:38 -04:00
func synthesizeslicetypes ( ctxt * Link , die * dwarf . DWDie ) {
prototype := walktypedef ( findprotodie ( ctxt , "type.runtime.slice" ) )
2015-02-27 22:57:28 -05:00
if prototype == nil {
return
}
2016-07-28 13:04:41 -04:00
for ; die != nil ; die = die . Link {
if die . Abbrev != dwarf . DW_ABRV_SLICETYPE {
2015-02-27 22:57:28 -05:00
continue
}
2016-08-19 22:40:38 -04:00
copychildren ( ctxt , die , prototype )
2017-10-04 17:54:04 -04:00
elem := getattr ( die , dwarf . DW_AT_go_elem ) . Data . ( * sym . Symbol )
2016-08-19 22:40:38 -04:00
substitutetype ( die , "array" , defptrto ( ctxt , elem ) )
2015-02-27 22:57:28 -05:00
}
}
func mkinternaltypename ( base string , arg1 string , arg2 string ) string {
if arg2 == "" {
2018-04-06 21:41:06 +01:00
return fmt . Sprintf ( "%s<%s>" , base , arg1 )
2015-02-27 22:57:28 -05:00
}
2018-04-06 21:41:06 +01:00
return fmt . Sprintf ( "%s<%s,%s>" , base , arg1 , arg2 )
2015-02-27 22:57:28 -05:00
}
// synthesizemaptypes is way too closely married to runtime/hashmap.c
const (
MaxKeySize = 128
MaxValSize = 128
BucketSize = 8
)
2017-10-04 17:54:04 -04:00
func mkinternaltype ( ctxt * Link , abbrev int , typename , keyname , valname string , f func ( * dwarf . DWDie ) ) * sym . Symbol {
2016-03-14 09:23:04 -07:00
name := mkinternaltypename ( typename , keyname , valname )
2016-07-28 13:04:41 -04:00
symname := dwarf . InfoPrefix + name
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
s := ctxt . Syms . ROLookup ( symname , 0 )
2017-10-04 17:54:04 -04:00
if s != nil && s . Type == sym . SDWARFINFO {
2016-03-14 09:23:04 -07:00
return s
}
2016-08-19 22:40:38 -04:00
die := newdie ( ctxt , & dwtypes , abbrev , name , 0 )
2016-03-14 09:23:04 -07:00
f ( die )
2016-07-28 13:04:41 -04:00
return dtolsym ( die . Sym )
2016-03-14 09:23:04 -07:00
}
2016-08-19 22:40:38 -04:00
func synthesizemaptypes ( ctxt * Link , die * dwarf . DWDie ) {
hash := walktypedef ( findprotodie ( ctxt , "type.runtime.hmap" ) )
bucket := walktypedef ( findprotodie ( ctxt , "type.runtime.bmap" ) )
2015-03-02 12:35:15 -05:00
if hash == nil {
return
}
2016-07-28 13:04:41 -04:00
for ; die != nil ; die = die . Link {
if die . Abbrev != dwarf . DW_ABRV_MAPTYPE {
2015-02-27 22:57:28 -05:00
continue
}
2017-10-04 17:54:04 -04:00
gotype := getattr ( die , dwarf . DW_AT_type ) . Data . ( * sym . Symbol )
2017-09-30 21:10:49 +00:00
keytype := decodetypeMapKey ( ctxt . Arch , gotype )
valtype := decodetypeMapValue ( ctxt . Arch , gotype )
2016-08-22 10:33:13 -04:00
keysize , valsize := decodetypeSize ( ctxt . Arch , keytype ) , decodetypeSize ( ctxt . Arch , valtype )
2016-08-19 22:40:38 -04:00
keytype , valtype = walksymtypedef ( ctxt , defgotype ( ctxt , keytype ) ) , walksymtypedef ( ctxt , defgotype ( ctxt , valtype ) )
2015-02-27 22:57:28 -05:00
// compute size info like hashmap.c does.
2016-08-22 10:33:13 -04:00
indirectKey , indirectVal := false , false
2015-02-27 22:57:28 -05:00
if keysize > MaxKeySize {
2017-09-30 21:10:49 +00:00
keysize = int64 ( ctxt . Arch . PtrSize )
2016-08-22 10:33:13 -04:00
indirectKey = true
2015-02-27 22:57:28 -05:00
}
if valsize > MaxValSize {
2017-09-30 21:10:49 +00:00
valsize = int64 ( ctxt . Arch . PtrSize )
2016-08-22 10:33:13 -04:00
indirectVal = true
2015-02-27 22:57:28 -05:00
}
// Construct type to represent an array of BucketSize keys
2016-03-14 09:23:04 -07:00
keyname := nameFromDIESym ( keytype )
2016-08-19 22:40:38 -04:00
dwhks := mkinternaltype ( ctxt , dwarf . DW_ABRV_ARRAYTYPE , "[]key" , keyname , "" , func ( dwhk * dwarf . DWDie ) {
2016-07-28 13:04:41 -04:00
newattr ( dwhk , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , BucketSize * keysize , 0 )
2016-03-14 09:23:04 -07:00
t := keytype
2016-08-22 10:33:13 -04:00
if indirectKey {
2016-08-19 22:40:38 -04:00
t = defptrto ( ctxt , keytype )
2016-03-14 09:23:04 -07:00
}
2016-07-28 13:04:41 -04:00
newrefattr ( dwhk , dwarf . DW_AT_type , t )
2016-08-19 22:40:38 -04:00
fld := newdie ( ctxt , dwhk , dwarf . DW_ABRV_ARRAYRANGE , "size" , 0 )
2016-07-28 13:04:41 -04:00
newattr ( fld , dwarf . DW_AT_count , dwarf . DW_CLS_CONSTANT , BucketSize , 0 )
2016-08-19 22:40:38 -04:00
newrefattr ( fld , dwarf . DW_AT_type , mustFind ( ctxt , "uintptr" ) )
2016-03-14 09:23:04 -07:00
} )
2015-02-27 22:57:28 -05:00
// Construct type to represent an array of BucketSize values
2016-03-14 09:23:04 -07:00
valname := nameFromDIESym ( valtype )
2016-08-19 22:40:38 -04:00
dwhvs := mkinternaltype ( ctxt , dwarf . DW_ABRV_ARRAYTYPE , "[]val" , valname , "" , func ( dwhv * dwarf . DWDie ) {
2016-07-28 13:04:41 -04:00
newattr ( dwhv , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , BucketSize * valsize , 0 )
2016-03-14 09:23:04 -07:00
t := valtype
2016-08-22 10:33:13 -04:00
if indirectVal {
2016-08-19 22:40:38 -04:00
t = defptrto ( ctxt , valtype )
2016-03-14 09:23:04 -07:00
}
2016-07-28 13:04:41 -04:00
newrefattr ( dwhv , dwarf . DW_AT_type , t )
2016-08-19 22:40:38 -04:00
fld := newdie ( ctxt , dwhv , dwarf . DW_ABRV_ARRAYRANGE , "size" , 0 )
2016-07-28 13:04:41 -04:00
newattr ( fld , dwarf . DW_AT_count , dwarf . DW_CLS_CONSTANT , BucketSize , 0 )
2016-08-19 22:40:38 -04:00
newrefattr ( fld , dwarf . DW_AT_type , mustFind ( ctxt , "uintptr" ) )
2016-03-14 09:23:04 -07:00
} )
2015-02-27 22:57:28 -05:00
// Construct bucket<K,V>
2016-08-19 22:40:38 -04:00
dwhbs := mkinternaltype ( ctxt , dwarf . DW_ABRV_STRUCTTYPE , "bucket" , keyname , valname , func ( dwhb * dwarf . DWDie ) {
2016-03-14 09:23:04 -07:00
// Copy over all fields except the field "data" from the generic
// bucket. "data" will be replaced with keys/values below.
2016-08-19 22:40:38 -04:00
copychildrenexcept ( ctxt , dwhb , bucket , findchild ( bucket , "data" ) )
2016-03-14 09:23:04 -07:00
2016-08-19 22:40:38 -04:00
fld := newdie ( ctxt , dwhb , dwarf . DW_ABRV_STRUCTFIELD , "keys" , 0 )
2016-07-28 13:04:41 -04:00
newrefattr ( fld , dwarf . DW_AT_type , dwhks )
2016-03-14 09:23:04 -07:00
newmemberoffsetattr ( fld , BucketSize )
2016-08-19 22:40:38 -04:00
fld = newdie ( ctxt , dwhb , dwarf . DW_ABRV_STRUCTFIELD , "values" , 0 )
2016-07-28 13:04:41 -04:00
newrefattr ( fld , dwarf . DW_AT_type , dwhvs )
2016-03-14 09:23:04 -07:00
newmemberoffsetattr ( fld , BucketSize + BucketSize * int32 ( keysize ) )
2016-08-19 22:40:38 -04:00
fld = newdie ( ctxt , dwhb , dwarf . DW_ABRV_STRUCTFIELD , "overflow" , 0 )
newrefattr ( fld , dwarf . DW_AT_type , defptrto ( ctxt , dtolsym ( dwhb . Sym ) ) )
2016-03-14 09:23:04 -07:00
newmemberoffsetattr ( fld , BucketSize + BucketSize * ( int32 ( keysize ) + int32 ( valsize ) ) )
2017-09-30 21:10:49 +00:00
if ctxt . Arch . RegSize > ctxt . Arch . PtrSize {
2016-08-19 22:40:38 -04:00
fld = newdie ( ctxt , dwhb , dwarf . DW_ABRV_STRUCTFIELD , "pad" , 0 )
newrefattr ( fld , dwarf . DW_AT_type , mustFind ( ctxt , "uintptr" ) )
2017-09-30 21:10:49 +00:00
newmemberoffsetattr ( fld , BucketSize + BucketSize * ( int32 ( keysize ) + int32 ( valsize ) ) + int32 ( ctxt . Arch . PtrSize ) )
2016-03-14 09:23:04 -07:00
}
2015-02-27 22:57:28 -05:00
2017-09-30 21:10:49 +00:00
newattr ( dwhb , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , BucketSize + BucketSize * keysize + BucketSize * valsize + int64 ( ctxt . Arch . RegSize ) , 0 )
2016-03-14 09:23:04 -07:00
} )
2015-02-27 22:57:28 -05:00
// Construct hash<K,V>
2016-08-19 22:40:38 -04:00
dwhs := mkinternaltype ( ctxt , dwarf . DW_ABRV_STRUCTTYPE , "hash" , keyname , valname , func ( dwh * dwarf . DWDie ) {
copychildren ( ctxt , dwh , hash )
substitutetype ( dwh , "buckets" , defptrto ( ctxt , dwhbs ) )
substitutetype ( dwh , "oldbuckets" , defptrto ( ctxt , dwhbs ) )
2016-07-28 13:04:41 -04:00
newattr ( dwh , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , getattr ( hash , dwarf . DW_AT_byte_size ) . Value , nil )
2016-03-14 09:23:04 -07:00
} )
2015-02-27 22:57:28 -05:00
// make map type a pointer to hash<K,V>
2016-08-19 22:40:38 -04:00
newrefattr ( die , dwarf . DW_AT_type , defptrto ( ctxt , dwhs ) )
2015-02-27 22:57:28 -05:00
}
}
2016-08-19 22:40:38 -04:00
func synthesizechantypes ( ctxt * Link , die * dwarf . DWDie ) {
sudog := walktypedef ( findprotodie ( ctxt , "type.runtime.sudog" ) )
waitq := walktypedef ( findprotodie ( ctxt , "type.runtime.waitq" ) )
hchan := walktypedef ( findprotodie ( ctxt , "type.runtime.hchan" ) )
2015-02-27 22:57:28 -05:00
if sudog == nil || waitq == nil || hchan == nil {
return
}
2016-07-28 13:04:41 -04:00
sudogsize := int ( getattr ( sudog , dwarf . DW_AT_byte_size ) . Value )
2015-02-27 22:57:28 -05:00
2016-07-28 13:04:41 -04:00
for ; die != nil ; die = die . Link {
if die . Abbrev != dwarf . DW_ABRV_CHANTYPE {
2015-02-27 22:57:28 -05:00
continue
}
2017-10-04 17:54:04 -04:00
elemgotype := getattr ( die , dwarf . DW_AT_type ) . Data . ( * sym . Symbol )
2016-03-14 09:23:04 -07:00
elemname := elemgotype . Name [ 5 : ]
2016-08-19 22:40:38 -04:00
elemtype := walksymtypedef ( ctxt , defgotype ( ctxt , elemgotype ) )
2015-02-27 22:57:28 -05:00
// sudog<T>
2016-08-19 22:40:38 -04:00
dwss := mkinternaltype ( ctxt , dwarf . DW_ABRV_STRUCTTYPE , "sudog" , elemname , "" , func ( dws * dwarf . DWDie ) {
copychildren ( ctxt , dws , sudog )
2017-07-19 19:52:29 -07:00
substitutetype ( dws , "elem" , defptrto ( ctxt , elemtype ) )
newattr ( dws , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , int64 ( sudogsize ) , nil )
2016-03-14 09:23:04 -07:00
} )
2015-02-27 22:57:28 -05:00
// waitq<T>
2016-08-19 22:40:38 -04:00
dwws := mkinternaltype ( ctxt , dwarf . DW_ABRV_STRUCTTYPE , "waitq" , elemname , "" , func ( dww * dwarf . DWDie ) {
2015-02-27 22:57:28 -05:00
2016-08-19 22:40:38 -04:00
copychildren ( ctxt , dww , waitq )
substitutetype ( dww , "first" , defptrto ( ctxt , dwss ) )
substitutetype ( dww , "last" , defptrto ( ctxt , dwss ) )
2016-07-28 13:04:41 -04:00
newattr ( dww , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , getattr ( waitq , dwarf . DW_AT_byte_size ) . Value , nil )
2016-03-14 09:23:04 -07:00
} )
2015-02-27 22:57:28 -05:00
// hchan<T>
2016-08-19 22:40:38 -04:00
dwhs := mkinternaltype ( ctxt , dwarf . DW_ABRV_STRUCTTYPE , "hchan" , elemname , "" , func ( dwh * dwarf . DWDie ) {
copychildren ( ctxt , dwh , hchan )
2016-03-14 09:23:04 -07:00
substitutetype ( dwh , "recvq" , dwws )
substitutetype ( dwh , "sendq" , dwws )
2016-07-28 13:04:41 -04:00
newattr ( dwh , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , getattr ( hchan , dwarf . DW_AT_byte_size ) . Value , nil )
2016-03-14 09:23:04 -07:00
} )
2015-02-27 22:57:28 -05:00
2016-08-19 22:40:38 -04:00
newrefattr ( die , dwarf . DW_AT_type , defptrto ( ctxt , dwhs ) )
2015-02-27 22:57:28 -05:00
}
}
2018-04-24 13:05:10 +02:00
func dwarfDefineGlobal ( ctxt * Link , s * sym . Symbol , str string , v int64 , gotype * sym . Symbol ) {
2018-09-25 11:52:24 +02:00
lib := s . Lib
if lib == nil {
lib = ctxt . LibraryByPkg [ "runtime" ]
}
dv := newdie ( ctxt , ctxt . compUnitByPackage [ lib ] . dwinfo , dwarf . DW_ABRV_VARIABLE , str , int ( s . Version ) )
2018-04-24 13:05:10 +02:00
newabslocexprattr ( dv , v , s )
if s . Version == 0 {
newattr ( dv , dwarf . DW_AT_external , dwarf . DW_CLS_FLAG , 1 , 0 )
}
dt := defgotype ( ctxt , gotype )
newrefattr ( dv , dwarf . DW_AT_type , dt )
}
2015-02-27 22:57:28 -05:00
// For use with pass.c::genasmsym
2017-10-04 17:54:04 -04:00
func defdwsymb ( ctxt * Link , s * sym . Symbol , str string , t SymbolType , v int64 , gotype * sym . Symbol ) {
if strings . HasPrefix ( str , "go.string." ) {
2015-02-27 22:57:28 -05:00
return
}
2017-10-04 17:54:04 -04:00
if strings . HasPrefix ( str , "runtime.gcbits." ) {
2015-07-22 14:59:56 -04:00
return
}
2015-02-27 22:57:28 -05:00
switch t {
2016-09-16 16:22:08 +12:00
case DataSym , BSSSym :
2018-04-24 13:05:10 +02:00
switch s . Type {
case sym . SDATA , sym . SNOPTRDATA , sym . STYPE , sym . SBSS , sym . SNOPTRBSS , sym . STLSBSS :
// ok
case sym . SRODATA :
if gotype != nil {
defgotype ( ctxt , gotype )
}
return
default :
return
2015-02-27 22:57:28 -05:00
}
2018-10-17 09:36:06 +02:00
if ctxt . LinkMode != LinkExternal && isStaticTemp ( s . Name ) {
return
}
2018-04-24 13:05:10 +02:00
dwarfDefineGlobal ( ctxt , s , str , v , gotype )
2015-02-27 22:57:28 -05:00
2018-04-24 13:05:10 +02:00
case AutoSym , ParamSym , DeletedAutoSym :
defgotype ( ctxt , gotype )
2015-02-27 22:57:28 -05:00
}
}
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
// compilationUnit is per-compilation unit (equivalently, per-package)
// debug-related data.
type compilationUnit struct {
2017-10-06 11:32:28 -04:00
lib * sym . Library
consts * sym . Symbol // Package constants DIEs
pcs [ ] dwarf . Range // PC ranges, relative to textp[0]
dwinfo * dwarf . DWDie // CU root DIE
funcDIEs [ ] * sym . Symbol // Function DIE subtrees
absFnDIEs [ ] * sym . Symbol // Abstract function DIE subtrees
2018-04-24 13:05:10 +02:00
rangeSyms [ ] * sym . Symbol // symbols for debug_range
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
}
2018-04-24 13:05:10 +02:00
// calcCompUnitRanges calculates the PC ranges of the compilation units.
func calcCompUnitRanges ( ctxt * Link ) {
2017-10-10 16:19:49 -04:00
var prevUnit * compilationUnit
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
for _ , s := range ctxt . Textp {
if s . FuncInfo == nil {
continue
}
2018-04-24 13:05:10 +02:00
unit := ctxt . compUnitByPackage [ s . Lib ]
2017-10-10 16:19:49 -04:00
// Update PC ranges.
//
// We don't simply compare the end of the previous
// symbol with the start of the next because there's
// often a little padding between them. Instead, we
// only create boundaries between symbols from
// different units.
if prevUnit != unit {
2017-10-21 12:45:23 +02:00
unit . pcs = append ( unit . pcs , dwarf . Range { Start : s . Value - unit . lib . Textp [ 0 ] . Value } )
2017-10-10 16:19:49 -04:00
prevUnit = unit
}
2017-10-21 12:45:23 +02:00
unit . pcs [ len ( unit . pcs ) - 1 ] . End = s . Value - unit . lib . Textp [ 0 ] . Value + s . Size
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
}
}
2018-09-25 11:52:24 +02:00
func movetomodule ( ctxt * Link , parent * dwarf . DWDie ) {
runtimelib := ctxt . LibraryByPkg [ "runtime" ]
die := ctxt . compUnitByPackage [ runtimelib ] . dwinfo . Child
2016-07-28 13:04:41 -04:00
if die == nil {
2018-09-25 11:52:24 +02:00
ctxt . compUnitByPackage [ runtimelib ] . dwinfo . Child = parent . Child
2016-07-28 13:04:41 -04:00
return
}
for die . Link != nil {
die = die . Link
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
die . Link = parent . Child
2015-02-27 22:57:28 -05:00
}
2017-10-03 15:21:55 -04:00
// If the pcln table contains runtime/proc.go, use that to set gdbscript path.
2017-10-04 17:54:04 -04:00
func finddebugruntimepath ( s * sym . Symbol ) {
2015-02-27 22:57:28 -05:00
if gdbscript != "" {
return
}
2016-04-12 23:18:47 +03:00
for i := range s . FuncInfo . File {
f := s . FuncInfo . File [ i ]
2017-10-03 15:21:55 -04:00
// We can't use something that may be dead-code
// eliminated from a binary here. proc.go contains
// main and the scheduler, so it's not going anywhere.
if i := strings . Index ( f . Name , "runtime/proc.go" ) ; i >= 0 {
2015-02-27 22:57:28 -05:00
gdbscript = f . Name [ : i ] + "runtime/runtime-gdb.py"
break
}
}
}
/ *
2016-10-07 15:06:09 +11:00
* Generate a sequence of opcodes that is as short as possible .
2015-02-27 22:57:28 -05:00
* See section 6.2 .5
* /
const (
2016-10-07 15:06:09 +11:00
LINE_BASE = - 4
LINE_RANGE = 10
PC_RANGE = ( 255 - OPCODE_BASE ) / LINE_RANGE
2018-04-30 23:52:14 -04:00
OPCODE_BASE = 11
2015-02-27 22:57:28 -05:00
)
2017-10-04 17:54:04 -04:00
func putpclcdelta ( linkctxt * Link , ctxt dwarf . Context , s * sym . Symbol , deltaPC uint64 , deltaLC int64 ) {
2016-10-07 15:06:09 +11:00
// 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
}
2015-02-27 22:57:28 -05:00
}
}
2016-10-07 15:06:09 +11:00
if opcode < OPCODE_BASE || opcode > 255 {
panic ( fmt . Sprintf ( "produced invalid special opcode %d" , opcode ) )
}
2015-02-27 22:57:28 -05:00
2016-10-07 15:06:09 +11:00
// Subtract from deltaPC and deltaLC the amounts that the opcode will add.
deltaPC -= uint64 ( ( opcode - OPCODE_BASE ) / LINE_RANGE )
2018-02-25 20:08:22 +09:00
deltaLC -= ( opcode - OPCODE_BASE ) % LINE_RANGE + LINE_BASE
2016-10-07 15:06:09 +11:00
// Encode deltaPC.
2016-08-22 10:33:13 -04:00
if deltaPC != 0 {
2016-10-07 15:06:09 +11:00
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 ) )
}
2017-09-30 15:06:44 +00:00
s . AddUint8 ( dwarf . DW_LNS_const_add_pc )
2016-10-07 15:06:09 +11:00
} else if ( 1 << 14 ) <= deltaPC && deltaPC < ( 1 << 16 ) {
2017-09-30 15:06:44 +00:00
s . AddUint8 ( dwarf . DW_LNS_fixed_advance_pc )
s . AddUint16 ( linkctxt . Arch , uint16 ( deltaPC ) )
2016-10-07 15:06:09 +11:00
} else {
2017-09-30 15:06:44 +00:00
s . AddUint8 ( dwarf . DW_LNS_advance_pc )
2016-10-07 15:06:09 +11:00
dwarf . Uleb128put ( ctxt , s , int64 ( deltaPC ) )
}
}
// Encode deltaLC.
if deltaLC != 0 {
2017-09-30 15:06:44 +00:00
s . AddUint8 ( dwarf . DW_LNS_advance_line )
2016-10-07 15:06:09 +11:00
dwarf . Sleb128put ( ctxt , s , deltaLC )
2015-02-27 22:57:28 -05:00
}
2016-10-07 15:06:09 +11:00
// Output the special opcode.
2017-09-30 15:06:44 +00:00
s . AddUint8 ( uint8 ( opcode ) )
2015-02-27 22:57:28 -05:00
}
/ *
* Walk prog table , emit line program and build DIE tree .
* /
2015-04-08 12:55:34 -07:00
func getCompilationDir ( ) string {
2017-11-16 16:19:19 -05:00
// OSX requires this be set to something, but it's not easy to choose
// a value. Linking takes place in a temporary directory, so there's
// no point including it here. Paths in the file table are usually
// absolute, in which case debuggers will ignore this value. -trimpath
// produces relative paths, but we don't know where they start, so
// all we can do here is try not to make things worse.
return "."
2015-04-08 12:55:34 -07:00
}
2017-10-04 17:54:04 -04:00
func importInfoSymbol ( ctxt * Link , dsym * sym . Symbol ) {
dsym . Attr |= sym . AttrNotInSymbolTable | sym . AttrReachable
dsym . Type = sym . SDWARFINFO
2018-05-17 19:47:52 +03:00
for i := range dsym . R {
2018-08-27 11:49:38 -04:00
r := & dsym . R [ i ] // Copying sym.Reloc has measurable impact on performance
2017-10-24 16:08:46 -04:00
if r . Type == objabi . R_DWARFSECREF && r . Sym . Size == 0 {
2017-09-03 11:59:18 +02:00
n := nameFromDIESym ( r . Sym )
defgotype ( ctxt , ctxt . Syms . Lookup ( "type." + n , 0 ) )
}
}
}
2018-09-25 11:52:24 +02:00
func writelines ( ctxt * Link , unit * compilationUnit , ls * sym . Symbol ) {
2017-10-06 11:32:28 -04:00
2016-08-19 22:40:38 -04:00
var dwarfctxt dwarf . Context = dwctxt { ctxt }
2018-02-13 17:39:38 -05:00
is_stmt := uint8 ( 1 ) // initially = recommended default_is_stmt = 1, tracks is_stmt toggles.
2016-03-14 09:23:04 -07:00
2015-03-02 12:35:15 -05:00
unitstart := int64 ( - 1 )
2016-03-14 09:23:04 -07:00
headerstart := int64 ( - 1 )
2015-03-02 12:35:15 -05:00
headerend := int64 ( - 1 )
2015-02-27 22:57:28 -05:00
2018-09-25 11:52:24 +02:00
newattr ( unit . dwinfo , dwarf . DW_AT_stmt_list , dwarf . DW_CLS_PTR , ls . Size , ls )
2015-02-27 22:57:28 -05:00
// Write .debug_line Line Number Program Header (sec 6.2.4)
// Fields marked with (*) must be changed for 64-bit dwarf
2016-08-22 10:33:13 -04:00
unitLengthOffset := ls . Size
2017-09-30 15:06:44 +00:00
ls . AddUint32 ( ctxt . Arch , 0 ) // unit_length (*), filled in at end.
2016-03-14 09:23:04 -07:00
unitstart = ls . Size
2018-05-21 13:11:50 -04:00
ls . AddUint16 ( ctxt . Arch , 2 ) // dwarf version (appendix F) -- version 3 is incompatible w/ XCode 9.0's dsymutil, latest supported on OSX 10.12 as of 2018-05
2016-08-22 10:33:13 -04:00
headerLengthOffset := ls . Size
2017-09-30 15:06:44 +00:00
ls . AddUint32 ( ctxt . Arch , 0 ) // header_length (*), filled in at end.
2016-03-14 09:23:04 -07:00
headerstart = ls . Size
2015-02-27 22:57:28 -05:00
// cpos == unitstart + 4 + 2 + 4
2017-09-30 15:06:44 +00:00
ls . AddUint8 ( 1 ) // minimum_instruction_length
2018-02-13 17:39:38 -05:00
ls . AddUint8 ( is_stmt ) // default_is_stmt
2017-09-30 15:06:44 +00:00
ls . AddUint8 ( LINE_BASE & 0xFF ) // line_base
ls . AddUint8 ( LINE_RANGE ) // line_range
ls . AddUint8 ( OPCODE_BASE ) // opcode_base
ls . AddUint8 ( 0 ) // standard_opcode_lengths[1]
ls . AddUint8 ( 1 ) // standard_opcode_lengths[2]
ls . AddUint8 ( 1 ) // standard_opcode_lengths[3]
ls . AddUint8 ( 1 ) // standard_opcode_lengths[4]
ls . AddUint8 ( 1 ) // standard_opcode_lengths[5]
ls . AddUint8 ( 0 ) // standard_opcode_lengths[6]
ls . AddUint8 ( 0 ) // standard_opcode_lengths[7]
ls . AddUint8 ( 0 ) // standard_opcode_lengths[8]
ls . AddUint8 ( 1 ) // standard_opcode_lengths[9]
2018-04-30 23:52:14 -04:00
ls . AddUint8 ( 0 ) // standard_opcode_lengths[10]
2017-09-30 15:06:44 +00:00
ls . AddUint8 ( 0 ) // include_directories (empty)
2016-08-19 22:40:38 -04:00
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
// Create the file table. fileNums maps from global file
// indexes (created by numberfile) to CU-local indexes.
fileNums := make ( map [ int ] int )
2018-04-24 13:05:10 +02:00
for _ , s := range unit . lib . Textp { // textp has been dead-code-eliminated already.
dsym := ctxt . Syms . Lookup ( dwarf . InfoPrefix + s . Name , int ( s . Version ) )
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
for _ , f := range s . FuncInfo . File {
if _ , ok := fileNums [ int ( f . Value ) ] ; ok {
continue
}
// 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 )
}
2018-04-24 13:05:10 +02:00
for ri := 0 ; ri < len ( dsym . R ) ; ri ++ {
2017-10-06 11:32:28 -04:00
r := & dsym . R [ ri ]
if r . Type != objabi . R_DWARFFILEREF {
continue
}
2018-04-24 13:05:10 +02:00
// 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 {
continue
2017-10-06 11:32:28 -04:00
}
2018-04-24 13:05:10 +02:00
fileNums [ int ( r . Sym . Value ) ] = len ( fileNums ) + 1
Addstring ( ls , r . Sym . Name )
ls . AddUint8 ( 0 )
ls . AddUint8 ( 0 )
ls . AddUint8 ( 0 )
2017-10-06 11:32:28 -04:00
}
2015-02-27 22:57:28 -05:00
}
// 4 zeros: the string termination + 3 fields.
2017-09-30 15:06:44 +00:00
ls . AddUint8 ( 0 )
2015-02-27 22:57:28 -05:00
// terminate file_names.
2016-03-14 09:23:04 -07:00
headerend = ls . Size
2015-02-27 22:57:28 -05:00
2017-09-30 15:06:44 +00:00
ls . AddUint8 ( 0 ) // start extended opcode
2017-09-30 21:10:49 +00:00
dwarf . Uleb128put ( dwarfctxt , ls , 1 + int64 ( ctxt . Arch . PtrSize ) )
2017-09-30 15:06:44 +00:00
ls . AddUint8 ( dwarf . DW_LNE_set_address )
2015-02-27 22:57:28 -05:00
2018-04-24 13:05:10 +02:00
s := unit . lib . Textp [ 0 ]
2015-03-02 12:35:15 -05:00
pc := s . Value
line := 1
file := 1
2017-09-30 15:06:44 +00:00
ls . AddAddr ( ctxt . Arch , s )
2015-02-27 22:57:28 -05:00
2015-03-02 12:35:15 -05:00
var pcfile Pciter
var pcline Pciter
2018-02-13 17:39:38 -05:00
var pcstmt Pciter
2018-04-24 13:05:10 +02:00
for i , s := range unit . lib . Textp {
2015-02-27 22:57:28 -05:00
finddebugruntimepath ( s )
2016-08-19 22:40:38 -04:00
pciterinit ( ctxt , & pcfile , & s . FuncInfo . Pcfile )
pciterinit ( ctxt , & pcline , & s . FuncInfo . Pcline )
2018-04-24 13:05:10 +02:00
pciterinit ( ctxt , & pcstmt , & sym . Pcdata { P : s . FuncInfo . IsStmtSym . P } )
2015-02-27 22:57:28 -05:00
2018-05-02 12:45:47 +02:00
if pcstmt . done != 0 {
// Assembly files lack a pcstmt section, we assume that every instruction
// is a valid statement.
pcstmt . value = 1
}
2018-02-13 17:39:38 -05:00
var thispc uint32
// TODO this loop looks like it could exit with work remaining.
2018-05-02 12:45:47 +02:00
for pcfile . done == 0 && pcline . done == 0 {
2018-02-13 17:39:38 -05:00
// Only changed if it advanced
2015-02-27 22:57:28 -05:00
if int32 ( file ) != pcfile . value {
2017-09-30 15:06:44 +00:00
ls . AddUint8 ( dwarf . DW_LNS_set_file )
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
idx , ok := fileNums [ int ( pcfile . value ) ]
if ! ok {
Exitf ( "pcln table file missing from DWARF line table" )
}
dwarf . Uleb128put ( dwarfctxt , ls , int64 ( idx ) )
2015-02-27 22:57:28 -05:00
file = int ( pcfile . value )
}
2018-02-13 17:39:38 -05:00
// Only changed if it advanced
if is_stmt != uint8 ( pcstmt . value ) {
2018-04-30 23:52:14 -04:00
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 ) )
}
2018-02-13 17:39:38 -05:00
}
// 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 ) )
2015-02-27 22:57:28 -05:00
2018-02-13 17:39:38 -05:00
pc = s . Value + int64 ( thispc )
2015-02-27 22:57:28 -05:00
line = int ( pcline . value )
2018-02-13 17:39:38 -05:00
// Take the minimum step forward for the three iterators
thispc = pcfile . nextpc
if pcline . nextpc < thispc {
thispc = pcline . nextpc
}
2018-05-02 12:45:47 +02:00
if pcstmt . done == 0 && pcstmt . nextpc < thispc {
2018-02-13 17:39:38 -05:00
thispc = pcstmt . nextpc
}
if pcfile . nextpc == thispc {
pciternext ( & pcfile )
2015-02-27 22:57:28 -05:00
}
2018-05-02 12:45:47 +02:00
if pcstmt . done == 0 && pcstmt . nextpc == thispc {
2018-02-13 17:39:38 -05:00
pciternext ( & pcstmt )
}
if pcline . nextpc == thispc {
pciternext ( & pcline )
}
}
2018-04-24 13:05:10 +02:00
if is_stmt == 0 && i < len ( unit . lib . Textp ) - 1 {
2018-02-13 17:39:38 -05:00
// If there is more than one function, ensure default value is established.
2018-05-13 15:17:40 +02:00
is_stmt = 1
2018-02-13 17:39:38 -05:00
ls . AddUint8 ( uint8 ( dwarf . DW_LNS_negate_stmt ) )
2015-02-27 22:57:28 -05:00
}
}
2017-09-30 15:06:44 +00:00
ls . AddUint8 ( 0 ) // start extended opcode
2016-07-28 13:04:41 -04:00
dwarf . Uleb128put ( dwarfctxt , ls , 1 )
2017-09-30 15:06:44 +00:00
ls . AddUint8 ( dwarf . DW_LNE_end_sequence )
2016-03-14 09:23:04 -07:00
2017-09-30 15:06:44 +00:00
ls . SetUint32 ( ctxt . Arch , unitLengthOffset , uint32 ( ls . Size - unitstart ) )
ls . SetUint32 ( ctxt . Arch , headerLengthOffset , uint32 ( headerend - headerstart ) )
2016-03-14 09:23:04 -07:00
2017-10-24 16:08:46 -04:00
// Apply any R_DWARFFILEREF relocations, since we now know the
// line table file indices for this compilation unit. Note that
// this loop visits only subprogram DIEs: if the compiler is
// changed to generate DW_AT_decl_file attributes for other
// DIE flavors (ex: variables) then those DIEs would need to
// be included below.
2017-10-06 11:32:28 -04:00
missing := make ( map [ int ] interface { } )
2018-04-24 13:05:10 +02:00
for _ , f := range unit . funcDIEs {
2018-04-06 21:41:06 +01:00
for ri := range f . R {
2017-10-24 16:08:46 -04:00
r := & f . R [ ri ]
if r . Type != objabi . R_DWARFFILEREF {
continue
}
// Mark relocation as applied (signal to relocsym)
r . Done = true
idx , ok := fileNums [ int ( r . Sym . Value ) ]
if ok {
if int ( int32 ( idx ) ) != idx {
Errorf ( f , "bad R_DWARFFILEREF relocation: file index overflow" )
}
if r . Siz != 4 {
Errorf ( f , "bad R_DWARFFILEREF relocation: has size %d, expected 4" , r . Siz )
}
if r . Off < 0 || r . Off + 4 > int32 ( len ( f . P ) ) {
Errorf ( f , "bad R_DWARFFILEREF relocation offset %d + 4 would write past length %d" , r . Off , len ( s . P ) )
continue
}
ctxt . Arch . ByteOrder . PutUint32 ( f . P [ r . Off : r . Off + 4 ] , uint32 ( idx ) )
} else {
2017-10-06 11:32:28 -04:00
_ , found := missing [ int ( r . Sym . Value ) ]
if ! found {
Errorf ( f , "R_DWARFFILEREF relocation file missing: %v idx %d" , r . Sym , r . Sym . Value )
missing [ int ( r . Sym . Value ) ] = nil
}
2017-10-24 16:08:46 -04:00
}
}
}
2015-02-27 22:57:28 -05:00
}
2017-10-10 16:19:49 -04:00
// writepcranges generates the DW_AT_ranges table for compilation unit cu.
func writepcranges ( ctxt * Link , cu * dwarf . DWDie , base * sym . Symbol , pcs [ ] dwarf . Range , ranges * sym . Symbol ) {
var dwarfctxt dwarf . Context = dwctxt { ctxt }
// Create PC ranges for this CU.
newattr ( cu , dwarf . DW_AT_ranges , dwarf . DW_CLS_PTR , ranges . Size , ranges )
newattr ( cu , dwarf . DW_AT_low_pc , dwarf . DW_CLS_ADDRESS , base . Value , base )
dwarf . PutRanges ( dwarfctxt , ranges , nil , pcs )
}
2015-02-27 22:57:28 -05:00
/ *
* Emit . debug_frame
* /
const (
2016-04-13 13:34:41 -04:00
dataAlignmentFactor = - 4
2015-02-27 22:57:28 -05:00
)
2016-02-29 13:07:50 -09:00
// appendPCDeltaCFA appends per-PC CFA deltas to b and returns the final slice.
2017-10-01 02:37:20 +00:00
func appendPCDeltaCFA ( arch * sys . Arch , b [ ] byte , deltapc , cfa int64 ) [ ] byte {
2016-07-28 13:04:41 -04:00
b = append ( b , dwarf . DW_CFA_def_cfa_offset_sf )
b = dwarf . AppendSleb128 ( b , cfa / dataAlignmentFactor )
2016-02-29 13:07:50 -09:00
switch {
case deltapc < 0x40 :
2016-07-28 13:04:41 -04:00
b = append ( b , uint8 ( dwarf . DW_CFA_advance_loc + deltapc ) )
2016-02-29 13:07:50 -09:00
case deltapc < 0x100 :
2016-07-28 13:04:41 -04:00
b = append ( b , dwarf . DW_CFA_advance_loc1 )
2016-02-29 13:07:50 -09:00
b = append ( b , uint8 ( deltapc ) )
case deltapc < 0x10000 :
2017-10-01 02:37:20 +00:00
b = append ( b , dwarf . DW_CFA_advance_loc2 , 0 , 0 )
arch . ByteOrder . PutUint16 ( b [ len ( b ) - 2 : ] , uint16 ( deltapc ) )
2016-02-29 13:07:50 -09:00
default :
2017-10-01 02:37:20 +00:00
b = append ( b , dwarf . DW_CFA_advance_loc4 , 0 , 0 , 0 , 0 )
arch . ByteOrder . PutUint32 ( b [ len ( b ) - 4 : ] , uint32 ( deltapc ) )
2015-02-27 22:57:28 -05:00
}
2016-02-29 13:07:50 -09:00
return b
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
func writeframes ( ctxt * Link , syms [ ] * sym . Symbol ) [ ] * sym . Symbol {
2016-08-19 22:40:38 -04:00
var dwarfctxt dwarf . Context = dwctxt { ctxt }
2017-05-22 20:17:31 -04:00
fs := ctxt . Syms . Lookup ( ".debug_frame" , 0 )
2017-10-04 17:54:04 -04:00
fs . Type = sym . SDWARFSECT
2016-07-28 13:04:41 -04:00
syms = append ( syms , fs )
2015-02-27 22:57:28 -05:00
// Emit the CIE, Section 6.4.1
2016-04-13 13:34:41 -04:00
cieReserve := uint32 ( 16 )
2016-08-19 22:40:38 -04:00
if haslinkregister ( ctxt ) {
2016-04-13 13:34:41 -04:00
cieReserve = 32
}
2017-09-30 15:06:44 +00:00
fs . AddUint32 ( ctxt . Arch , cieReserve ) // initial length, must be multiple of thearch.ptrsize
fs . AddUint32 ( ctxt . Arch , 0xffffffff ) // cid.
fs . AddUint8 ( 3 ) // dwarf version (appendix F)
fs . AddUint8 ( 0 ) // augmentation ""
2016-07-28 13:04:41 -04:00
dwarf . Uleb128put ( dwarfctxt , fs , 1 ) // code_alignment_factor
dwarf . Sleb128put ( dwarfctxt , fs , dataAlignmentFactor ) // all CFI offset calculations include multiplication with this factor
2018-04-01 00:58:48 +03:00
dwarf . Uleb128put ( dwarfctxt , fs , int64 ( thearch . Dwarfreglr ) ) // return_address_register
2015-02-27 22:57:28 -05:00
2017-09-30 15:06:44 +00:00
fs . AddUint8 ( dwarf . DW_CFA_def_cfa ) // Set the current frame address..
2018-04-01 00:58:48 +03:00
dwarf . Uleb128put ( dwarfctxt , fs , int64 ( thearch . Dwarfregsp ) ) // ...to use the value in the platform's SP register (defined in l.go)...
2016-08-19 22:40:38 -04:00
if haslinkregister ( ctxt ) {
2016-07-28 13:04:41 -04:00
dwarf . Uleb128put ( dwarfctxt , fs , int64 ( 0 ) ) // ...plus a 0 offset.
2015-02-27 22:57:28 -05:00
2017-09-30 15:06:44 +00:00
fs . AddUint8 ( dwarf . DW_CFA_same_value ) // The platform's link register is unchanged during the prologue.
2018-04-01 00:58:48 +03:00
dwarf . Uleb128put ( dwarfctxt , fs , int64 ( thearch . Dwarfreglr ) )
2016-04-13 13:34:41 -04:00
2017-09-30 15:06:44 +00:00
fs . AddUint8 ( dwarf . DW_CFA_val_offset ) // The previous value...
2018-04-01 00:58:48 +03:00
dwarf . Uleb128put ( dwarfctxt , fs , int64 ( thearch . Dwarfregsp ) ) // ...of the platform's SP register...
2016-07-28 13:04:41 -04:00
dwarf . Uleb128put ( dwarfctxt , fs , int64 ( 0 ) ) // ...is CFA+0.
2015-05-07 00:48:09 -04:00
} else {
2017-09-30 21:10:49 +00:00
dwarf . Uleb128put ( dwarfctxt , fs , int64 ( ctxt . Arch . PtrSize ) ) // ...plus the word size (because the call instruction implicitly adds one word to the frame).
2016-04-13 13:34:41 -04:00
2017-09-30 15:06:44 +00:00
fs . AddUint8 ( dwarf . DW_CFA_offset_extended ) // The previous value...
2018-04-01 00:58:48 +03:00
dwarf . Uleb128put ( dwarfctxt , fs , int64 ( thearch . Dwarfreglr ) ) // ...of the return address...
2017-09-30 21:10:49 +00:00
dwarf . Uleb128put ( dwarfctxt , fs , int64 ( - ctxt . Arch . PtrSize ) / dataAlignmentFactor ) // ...is saved at [CFA - (PtrSize/4)].
2015-05-07 00:48:09 -04:00
}
2015-02-27 22:57:28 -05:00
// 4 is to exclude the length field.
2016-04-13 13:34:41 -04:00
pad := int64 ( cieReserve ) + 4 - fs . Size
2015-02-27 22:57:28 -05:00
if pad < 0 {
2016-04-13 13:34:41 -04:00
Exitf ( "dwarf: cieReserve too small by %d bytes." , - pad )
2015-02-27 22:57:28 -05:00
}
2017-09-30 15:06:44 +00:00
fs . AddBytes ( zeros [ : pad ] )
2015-02-27 22:57:28 -05:00
2016-02-29 13:07:50 -09:00
var deltaBuf [ ] byte
2015-03-02 12:35:15 -05:00
var pcsp Pciter
2016-09-17 10:01:17 -04:00
for _ , s := range ctxt . Textp {
2016-04-12 23:18:47 +03:00
if s . FuncInfo == nil {
2015-02-27 22:57:28 -05:00
continue
}
2016-02-29 13:07:50 -09:00
// Emit a FDE, Section 6.4.1.
// First build the section contents into a byte buffer.
deltaBuf = deltaBuf [ : 0 ]
2016-08-19 22:40:38 -04:00
for pciterinit ( ctxt , & pcsp , & s . FuncInfo . Pcsp ) ; pcsp . done == 0 ; pciternext ( & pcsp ) {
2015-10-10 10:57:35 +00:00
nextpc := pcsp . nextpc
2015-02-27 22:57:28 -05:00
// 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
}
}
2016-08-19 22:40:38 -04:00
if haslinkregister ( ctxt ) {
2016-04-13 13:34:41 -04:00
// 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.
2016-07-28 13:04:41 -04:00
deltaBuf = append ( deltaBuf , dwarf . DW_CFA_offset_extended_sf )
2018-04-01 00:58:48 +03:00
deltaBuf = dwarf . AppendUleb128 ( deltaBuf , uint64 ( thearch . Dwarfreglr ) )
2016-07-28 13:04:41 -04:00
deltaBuf = dwarf . AppendSleb128 ( deltaBuf , - int64 ( pcsp . value ) / dataAlignmentFactor )
2016-04-13 13:34:41 -04:00
} else {
// The return address is restored into the link register
// when a stack frame has been de-allocated.
2016-07-28 13:04:41 -04:00
deltaBuf = append ( deltaBuf , dwarf . DW_CFA_same_value )
2018-04-01 00:58:48 +03:00
deltaBuf = dwarf . AppendUleb128 ( deltaBuf , uint64 ( thearch . Dwarfreglr ) )
2016-04-13 13:34:41 -04:00
}
2017-10-01 02:37:20 +00:00
deltaBuf = appendPCDeltaCFA ( ctxt . Arch , deltaBuf , int64 ( nextpc ) - int64 ( pcsp . pc ) , int64 ( pcsp . value ) )
2015-05-07 00:48:09 -04:00
} else {
2017-10-01 02:37:20 +00:00
deltaBuf = appendPCDeltaCFA ( ctxt . Arch , deltaBuf , int64 ( nextpc ) - int64 ( pcsp . pc ) , int64 ( ctxt . Arch . PtrSize ) + int64 ( pcsp . value ) )
2015-05-07 00:48:09 -04:00
}
2015-02-27 22:57:28 -05:00
}
2017-09-30 21:10:49 +00:00
pad := int ( Rnd ( int64 ( len ( deltaBuf ) ) , int64 ( ctxt . Arch . PtrSize ) ) ) - len ( deltaBuf )
2016-02-29 13:07:50 -09:00
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
2017-09-30 15:06:44 +00:00
fs . AddUint32 ( ctxt . Arch , uint32 ( 4 + 2 * ctxt . Arch . PtrSize + len ( deltaBuf ) ) ) // length (excludes itself)
2017-10-05 10:20:17 -04:00
if ctxt . LinkMode == LinkExternal {
2017-05-22 20:17:31 -04:00
adddwarfref ( ctxt , fs , fs , 4 )
2015-02-27 22:57:28 -05:00
} else {
2017-09-30 15:06:44 +00:00
fs . AddUint32 ( ctxt . Arch , 0 ) // CIE offset
2015-02-27 22:57:28 -05:00
}
2017-09-30 15:06:44 +00:00
fs . AddAddr ( ctxt . Arch , s )
2017-10-04 17:54:04 -04:00
fs . AddUintXX ( ctxt . Arch , uint64 ( s . Size ) , ctxt . Arch . PtrSize ) // address range
2017-09-30 15:06:44 +00:00
fs . AddBytes ( deltaBuf )
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
return syms
2015-02-27 22:57:28 -05:00
}
/ *
* Walk DWarfDebugInfoEntries , and emit . debug_info
* /
const (
COMPUNITHEADERSIZE = 4 + 2 + 4 + 1
)
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
func writeinfo ( ctxt * Link , syms [ ] * sym . Symbol , units [ ] * compilationUnit , abbrevsym * sym . Symbol ) [ ] * sym . Symbol {
2017-05-22 20:17:31 -04:00
infosec := ctxt . Syms . Lookup ( ".debug_info" , 0 )
2017-10-04 17:54:04 -04:00
infosec . Type = sym . SDWARFINFO
infosec . Attr |= sym . AttrReachable
2016-07-28 13:04:41 -04:00
syms = append ( syms , infosec )
2015-02-27 22:57:28 -05:00
2016-08-19 22:40:38 -04:00
var dwarfctxt dwarf . Context = dwctxt { ctxt }
2016-07-28 13:04:41 -04:00
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
for _ , u := range units {
2018-09-25 11:52:24 +02:00
compunit := u . dwinfo
2016-07-28 13:04:41 -04:00
s := dtolsym ( compunit . Sym )
2018-09-25 11:52:24 +02:00
if len ( u . lib . Textp ) == 0 && u . dwinfo . Child == nil {
continue
}
2015-02-27 22:57:28 -05:00
// 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.
2017-09-30 15:06:44 +00:00
s . AddUint32 ( ctxt . Arch , 0 ) // unit_length (*), will be filled in later.
s . AddUint16 ( ctxt . Arch , 4 ) // dwarf version (appendix F)
2015-02-27 22:57:28 -05:00
// debug_abbrev_offset (*)
2016-08-19 22:40:38 -04:00
adddwarfref ( ctxt , s , abbrevsym , 4 )
2015-02-27 22:57:28 -05:00
2017-09-30 15:06:44 +00:00
s . AddUint8 ( uint8 ( ctxt . Arch . PtrSize ) ) // address_size
2015-02-27 22:57:28 -05:00
2016-07-28 13:04:41 -04:00
dwarf . Uleb128put ( dwarfctxt , s , int64 ( compunit . Abbrev ) )
dwarf . PutAttrs ( dwarfctxt , s , compunit . Abbrev , compunit . Attr )
2017-10-04 17:54:04 -04:00
cu := [ ] * sym . Symbol { s }
2017-10-06 11:32:28 -04:00
cu = append ( cu , u . absFnDIEs ... )
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
cu = append ( cu , u . funcDIEs ... )
if u . consts != nil {
cu = append ( cu , u . consts )
2017-09-03 11:59:18 +02:00
}
2016-08-19 22:40:38 -04:00
cu = putdies ( ctxt , dwarfctxt , cu , compunit . Child )
2016-07-28 13:04:41 -04:00
var cusize int64
for _ , child := range cu {
2016-03-14 09:23:04 -07:00
cusize += child . Size
}
2016-07-28 13:04:41 -04:00
cusize -= 4 // exclude the length field.
2017-09-30 15:06:44 +00:00
s . SetUint32 ( ctxt . Arch , 0 , uint32 ( cusize ) )
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
// Leave a breadcrumb for writepub. This does not
// appear in the DWARF output.
2016-07-28 13:04:41 -04:00
newattr ( compunit , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , cusize , 0 )
syms = append ( syms , cu ... )
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
return syms
2015-02-27 22:57:28 -05:00
}
/ *
* Emit . debug_pubnames / _types . _info must have been written before ,
* because we need die - > offs and infoo / infosize ;
* /
2016-07-28 13:04:41 -04:00
func ispubname ( die * dwarf . DWDie ) bool {
switch die . Abbrev {
case dwarf . DW_ABRV_FUNCTION , dwarf . DW_ABRV_VARIABLE :
a := getattr ( die , dwarf . DW_AT_external )
return a != nil && a . Value != 0
2015-02-27 22:57:28 -05:00
}
return false
}
2016-07-28 13:04:41 -04:00
func ispubtype ( die * dwarf . DWDie ) bool {
return die . Abbrev >= dwarf . DW_ABRV_NULLTYPE
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
func writepub ( ctxt * Link , sname string , ispub func ( * dwarf . DWDie ) bool , syms [ ] * sym . Symbol ) [ ] * sym . Symbol {
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
s := ctxt . Syms . Lookup ( sname , 0 )
2017-10-04 17:54:04 -04:00
s . Type = sym . SDWARFSECT
2016-07-28 13:04:41 -04:00
syms = append ( syms , s )
2015-02-27 22:57:28 -05:00
2018-09-25 11:52:24 +02:00
for _ , u := range ctxt . compUnits {
if len ( u . lib . Textp ) == 0 && u . dwinfo . Child == nil {
continue
}
compunit := u . dwinfo
2016-03-14 09:23:04 -07:00
sectionstart := s . Size
2016-07-28 13:04:41 -04:00
culength := uint32 ( getattr ( compunit , dwarf . DW_AT_byte_size ) . Value ) + 4
2015-02-27 22:57:28 -05:00
// Write .debug_pubnames/types Header (sec 6.1.1)
2017-09-30 15:06:44 +00:00
s . AddUint32 ( ctxt . Arch , 0 ) // unit_length (*), will be filled in later.
s . AddUint16 ( ctxt . Arch , 2 ) // dwarf version (appendix F)
2016-08-19 22:40:38 -04:00
adddwarfref ( ctxt , s , dtolsym ( compunit . Sym ) , 4 ) // debug_info_offset (of the Comp unit Header)
2017-09-30 15:06:44 +00:00
s . AddUint32 ( ctxt . Arch , culength ) // debug_info_length
2015-02-27 22:57:28 -05:00
2016-07-28 13:04:41 -04:00
for die := compunit . Child ; die != nil ; die = die . Link {
2015-02-27 22:57:28 -05:00
if ! ispub ( die ) {
continue
}
2016-07-28 13:04:41 -04:00
dwa := getattr ( die , dwarf . DW_AT_name )
name := dwa . Data . ( string )
if die . Sym == nil {
2016-03-14 09:23:04 -07:00
fmt . Println ( "Missing sym for " , name )
}
2016-08-19 22:40:38 -04:00
adddwarfref ( ctxt , s , dtolsym ( die . Sym ) , 4 )
2016-09-20 15:31:26 +12:00
Addstring ( s , name )
2015-02-27 22:57:28 -05:00
}
2017-09-30 15:06:44 +00:00
s . AddUint32 ( ctxt . Arch , 0 )
2015-02-27 22:57:28 -05:00
2017-09-30 15:06:44 +00:00
s . SetUint32 ( ctxt . Arch , sectionstart , uint32 ( s . Size - sectionstart ) - 4 ) // exclude the length field.
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
return syms
2015-02-27 22:57:28 -05:00
}
2017-10-04 17:54:04 -04:00
func writegdbscript ( ctxt * Link , syms [ ] * sym . Symbol ) [ ] * sym . Symbol {
2017-10-07 13:49:44 -04:00
if ctxt . LinkMode == LinkExternal && ctxt . HeadType == objabi . Hwindows && ctxt . BuildMode == BuildModeCArchive {
2017-05-07 11:04:24 -07:00
// gcc on Windows places .debug_gdb_scripts in the wrong location, which
// causes the program not to run. See https://golang.org/issue/20183
2017-05-11 11:55:59 +10:00
// Non c-archives can avoid this issue via a linker script
// (see fix near writeGDBLinkerScript).
// c-archive users would need to specify the linker script manually.
// For UX it's better not to deal with this.
2017-05-04 16:14:59 +03:00
return syms
}
2015-02-27 22:57:28 -05:00
2016-03-14 09:23:04 -07:00
if gdbscript != "" {
cmd/link: use ctxt.{Lookup,ROLookup} in favour of function versions of same
Done with two eg templates:
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linklookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.Lookup(name, v)
}
package p
import (
"cmd/link/internal/ld"
)
func before(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ld.Linkrlookup(ctxt, name, v)
}
func after(ctxt *ld.Link, name string, v int) *ld.Symbol {
return ctxt.Syms.ROLookup(name, v)
}
Change-Id: I00647dbf62294557bd24c29ad1f108fc786335f1
Reviewed-on: https://go-review.googlesource.com/29343
Run-TryBot: Michael Hudson-Doyle <michael.hudson@canonical.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2016-09-20 15:06:08 +12:00
s := ctxt . Syms . Lookup ( ".debug_gdb_scripts" , 0 )
2017-10-04 17:54:04 -04:00
s . Type = sym . SDWARFSECT
2016-07-28 13:04:41 -04:00
syms = append ( syms , s )
2017-09-30 15:06:44 +00:00
s . AddUint8 ( 1 ) // magic 1 byte?
2016-09-20 15:31:26 +12:00
Addstring ( s , gdbscript )
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
return syms
2015-02-27 22:57:28 -05:00
}
2016-07-28 13:04:41 -04:00
var prototypedies map [ string ] * dwarf . DWDie
2015-04-08 12:55:34 -07:00
2018-04-24 13:05:10 +02:00
func dwarfEnabled ( ctxt * Link ) bool {
2016-08-21 18:34:24 -04:00
if * FlagW { // disable dwarf
2018-04-24 13:05:10 +02:00
return false
2015-02-27 22:57:28 -05:00
}
2017-10-07 13:49:44 -04:00
if * FlagS && ctxt . HeadType != objabi . Hdarwin {
2018-04-24 13:05:10 +02:00
return false
2016-04-07 14:00:00 -04:00
}
2018-03-04 12:59:15 +01:00
if ctxt . HeadType == objabi . Hplan9 || ctxt . HeadType == objabi . Hjs {
2018-04-24 13:05:10 +02:00
return false
2016-03-14 09:23:04 -07:00
}
2015-02-27 22:57:28 -05:00
2017-10-05 10:20:17 -04:00
if ctxt . LinkMode == LinkExternal {
2017-02-08 12:47:43 +11:00
switch {
2017-10-07 13:43:38 -04:00
case ctxt . IsELF :
2017-10-07 13:49:44 -04:00
case ctxt . HeadType == objabi . Hdarwin :
case ctxt . HeadType == objabi . Hwindows :
2017-02-08 12:47:43 +11:00
default :
2018-04-24 13:05:10 +02:00
return false
2015-04-08 12:55:34 -07:00
}
2016-03-14 09:23:04 -07:00
}
2015-04-08 12:55:34 -07:00
2018-04-24 13:05:10 +02:00
return true
}
// dwarfGenerateDebugInfo generated debug info entries for all types,
// variables and functions in the program.
// Along with dwarfGenerateDebugSyms they are the two main entry points into
// dwarf generation: dwarfGenerateDebugInfo does all the work that should be
// done before symbol names are mangled while dwarfgeneratedebugsyms does
// all the work that can only be done after addresses have been assigned to
// text symbols.
func dwarfGenerateDebugInfo ( ctxt * Link ) {
if ! dwarfEnabled ( ctxt ) {
return
2015-02-27 22:57:28 -05:00
}
2018-04-24 13:05:10 +02:00
ctxt . compUnitByPackage = make ( map [ * sym . Library ] * compilationUnit )
2016-08-19 22:40:38 -04:00
// Forctxt.Diagnostic messages.
2016-07-28 13:04:41 -04:00
newattr ( & dwtypes , dwarf . DW_AT_name , dwarf . DW_CLS_STRING , int64 ( len ( "dwtypes" ) ) , "dwtypes" )
2015-02-27 22:57:28 -05:00
// Some types that must exist to define other ones.
2016-08-19 22:40:38 -04:00
newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_NULLTYPE , "<unspecified>" , 0 )
2015-02-27 22:57:28 -05:00
2016-08-19 22:40:38 -04:00
newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_NULLTYPE , "void" , 0 )
newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_BARE_PTRTYPE , "unsafe.Pointer" , 0 )
2015-02-27 22:57:28 -05:00
2016-08-19 22:40:38 -04:00
die := newdie ( ctxt , & dwtypes , dwarf . DW_ABRV_BASETYPE , "uintptr" , 0 ) // needed for array size
2016-07-28 13:04:41 -04:00
newattr ( die , dwarf . DW_AT_encoding , dwarf . DW_CLS_CONSTANT , dwarf . DW_ATE_unsigned , 0 )
2017-09-30 21:10:49 +00:00
newattr ( die , dwarf . DW_AT_byte_size , dwarf . DW_CLS_CONSTANT , int64 ( ctxt . Arch . PtrSize ) , 0 )
2017-04-18 12:53:25 -07:00
newattr ( die , dwarf . DW_AT_go_kind , dwarf . DW_CLS_CONSTANT , objabi . KindUintptr , 0 )
2018-04-12 17:07:14 -04:00
newattr ( die , dwarf . DW_AT_go_runtime_type , dwarf . DW_CLS_ADDRESS , 0 , lookupOrDiag ( ctxt , "type.uintptr" ) )
2015-02-27 22:57:28 -05:00
2016-03-14 09:23:04 -07:00
// Prototypes needed for type synthesis.
2016-07-28 13:04:41 -04:00
prototypedies = map [ string ] * dwarf . DWDie {
2016-03-14 09:23:04 -07:00
"type.runtime.stringStructDWARF" : nil ,
"type.runtime.slice" : nil ,
"type.runtime.hmap" : nil ,
"type.runtime.bmap" : nil ,
"type.runtime.sudog" : nil ,
"type.runtime.waitq" : nil ,
"type.runtime.hchan" : nil ,
}
2015-02-27 22:57:28 -05:00
// Needed by the prettyprinter code for interface inspection.
2017-03-18 15:09:40 +01:00
for _ , typ := range [ ] string {
"type.runtime._type" ,
"type.runtime.arraytype" ,
"type.runtime.chantype" ,
"type.runtime.functype" ,
"type.runtime.maptype" ,
"type.runtime.ptrtype" ,
"type.runtime.slicetype" ,
"type.runtime.structtype" ,
"type.runtime.interfacetype" ,
"type.runtime.itab" ,
"type.runtime.imethod" } {
defgotype ( ctxt , lookupOrDiag ( ctxt , typ ) )
}
2015-02-27 22:57:28 -05:00
2018-09-25 11:52:24 +02:00
// fake root DIE for compile unit DIEs
var dwroot dwarf . DWDie
2015-02-27 22:57:28 -05:00
2018-04-24 13:05:10 +02:00
for _ , lib := range ctxt . Library {
unit := & compilationUnit { lib : lib }
if s := ctxt . Syms . ROLookup ( dwarf . ConstInfoPrefix + lib . Pkg , 0 ) ; s != nil {
importInfoSymbol ( ctxt , s )
unit . consts = s
}
ctxt . compUnits = append ( ctxt . compUnits , unit )
ctxt . compUnitByPackage [ lib ] = unit
2018-09-25 11:52:24 +02:00
unit . dwinfo = newdie ( ctxt , & dwroot , dwarf . DW_ABRV_COMPUNIT , unit . lib . Pkg , 0 )
newattr ( unit . dwinfo , dwarf . DW_AT_language , dwarf . DW_CLS_CONSTANT , int64 ( dwarf . DW_LANG_Go ) , 0 )
// OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
compDir := getCompilationDir ( )
// TODO: Make this be the actual compilation directory, not
// the linker directory. If we move CU construction into the
// compiler, this should happen naturally.
newattr ( unit . dwinfo , dwarf . DW_AT_comp_dir , dwarf . DW_CLS_STRING , int64 ( len ( compDir ) ) , compDir )
producerExtra := ctxt . Syms . Lookup ( dwarf . CUInfoPrefix + "producer." + unit . lib . Pkg , 0 )
producer := "Go cmd/compile " + objabi . Version
if len ( producerExtra . P ) > 0 {
// We put a semicolon before the flags to clearly
// separate them from the version, which can be long
// and have lots of weird things in it in development
// versions. We promise not to put a semicolon in the
// version, so it should be safe for readers to scan
// forward to the semicolon.
producer += "; " + string ( producerExtra . P )
}
newattr ( unit . dwinfo , dwarf . DW_AT_producer , dwarf . DW_CLS_STRING , int64 ( len ( producer ) ) , producer )
if len ( lib . Textp ) == 0 {
unit . dwinfo . Abbrev = dwarf . DW_ABRV_COMPUNIT_TEXTLESS
}
2018-04-24 13:05:10 +02:00
// Scan all functions in this compilation unit, create DIEs for all
// referenced types, create the file table for debug_line, find all
// referenced abstract functions.
// Collect all debug_range symbols in unit.rangeSyms
for _ , s := range lib . Textp { // textp has been dead-code-eliminated already.
dsym := ctxt . Syms . ROLookup ( dwarf . InfoPrefix + s . Name , int ( s . Version ) )
dsym . Attr |= sym . AttrNotInSymbolTable | sym . AttrReachable
dsym . Type = sym . SDWARFINFO
unit . funcDIEs = append ( unit . funcDIEs , dsym )
rangeSym := ctxt . Syms . ROLookup ( dwarf . RangePrefix + s . Name , int ( s . Version ) )
if rangeSym != nil && rangeSym . Size > 0 {
rangeSym . Attr |= sym . AttrReachable | sym . AttrNotInSymbolTable
rangeSym . Type = sym . SDWARFRANGE
// LLVM doesn't support base address entries. Strip them out so LLDB and dsymutil don't get confused.
if ctxt . HeadType == objabi . Hdarwin {
removeDwarfAddrListBaseAddress ( ctxt , dsym , rangeSym , false )
}
unit . rangeSyms = append ( unit . rangeSyms , rangeSym )
}
for ri := 0 ; ri < len ( dsym . R ) ; ri ++ {
r := & dsym . R [ ri ]
if r . Type == objabi . R_DWARFSECREF {
rsym := r . Sym
if strings . HasPrefix ( rsym . Name , dwarf . InfoPrefix ) && strings . HasSuffix ( rsym . Name , dwarf . AbstractFuncSuffix ) && ! rsym . Attr . OnList ( ) {
// abstract function
rsym . Attr |= sym . AttrOnList
unit . absFnDIEs = append ( unit . absFnDIEs , rsym )
importInfoSymbol ( ctxt , rsym )
} else if rsym . Size == 0 {
// a type we do not have a DIE for
n := nameFromDIESym ( rsym )
defgotype ( ctxt , ctxt . Syms . Lookup ( "type." + n , 0 ) )
}
}
}
}
}
2018-09-25 11:52:24 +02:00
// Create DIEs for global variables and the types they use.
genasmsym ( ctxt , defdwsymb )
2018-04-24 13:05:10 +02:00
synthesizestringtypes ( ctxt , dwtypes . Child )
synthesizeslicetypes ( ctxt , dwtypes . Child )
synthesizemaptypes ( ctxt , dwtypes . Child )
synthesizechantypes ( ctxt , dwtypes . Child )
}
// dwarfGenerateDebugSyms constructs debug_line, debug_frame, debug_loc,
// debug_pubnames and debug_pubtypes. It also writes out the debug_info
// section using symbols generated in dwarfGenerateDebugInfo.
func dwarfGenerateDebugSyms ( ctxt * Link ) {
if ! dwarfEnabled ( ctxt ) {
return
}
if ctxt . Debugvlog != 0 {
ctxt . Logf ( "%5.2f dwarf\n" , Cputime ( ) )
}
2017-05-22 20:17:31 -04:00
abbrev := writeabbrev ( ctxt )
2017-10-04 17:54:04 -04:00
syms := [ ] * sym . Symbol { abbrev }
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
2018-04-24 13:05:10 +02:00
calcCompUnitRanges ( ctxt )
sort . Sort ( compilationUnitByStartPC ( ctxt . compUnits ) )
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
2017-10-10 16:19:49 -04:00
// Write per-package line and range tables and start their CU DIEs.
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
debugLine := ctxt . Syms . Lookup ( ".debug_line" , 0 )
debugLine . Type = sym . SDWARFSECT
2017-10-10 16:19:49 -04:00
debugRanges := ctxt . Syms . Lookup ( ".debug_ranges" , 0 )
debugRanges . Type = sym . SDWARFRANGE
debugRanges . Attr |= sym . AttrReachable
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
syms = append ( syms , debugLine )
2018-04-24 13:05:10 +02:00
for _ , u := range ctxt . compUnits {
2018-09-25 11:52:24 +02:00
reversetree ( & u . dwinfo . Child )
if u . dwinfo . Abbrev == dwarf . DW_ABRV_COMPUNIT_TEXTLESS {
continue
}
writelines ( ctxt , u , debugLine )
2017-10-21 12:45:23 +02:00
writepcranges ( ctxt , u . dwinfo , u . lib . Textp [ 0 ] , u . pcs , debugRanges )
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
}
2015-02-27 22:57:28 -05:00
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
// newdie adds DIEs to the *beginning* of the parent's DIE list.
// Now that we're done creating DIEs, reverse the trees so DIEs
// appear in the order they were created.
2016-07-28 13:04:41 -04:00
reversetree ( & dwtypes . Child )
2018-09-25 11:52:24 +02:00
movetomodule ( ctxt , & dwtypes )
2015-02-27 22:57:28 -05:00
2017-10-04 17:54:04 -04:00
// Need to reorder symbols so sym.SDWARFINFO is after all sym.SDWARFSECT
2016-03-14 09:23:04 -07:00
// (but we need to generate dies before writepub)
2018-04-24 13:05:10 +02:00
infosyms := writeinfo ( ctxt , nil , ctxt . compUnits , abbrev )
2016-07-28 13:04:41 -04:00
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
syms = writeframes ( ctxt , syms )
2016-08-19 22:40:38 -04:00
syms = writepub ( ctxt , ".debug_pubnames" , ispubname , syms )
syms = writepub ( ctxt , ".debug_pubtypes" , ispubtype , syms )
syms = writegdbscript ( ctxt , syms )
2017-10-10 16:19:49 -04:00
// Now we're done writing SDWARFSECT symbols, so we can write
// other SDWARF* symbols.
2016-07-28 13:04:41 -04:00
syms = append ( syms , infosyms ... )
2018-04-24 13:05:10 +02:00
syms = collectlocs ( ctxt , syms , ctxt . compUnits )
2017-10-10 16:19:49 -04:00
syms = append ( syms , debugRanges )
2018-04-24 13:05:10 +02:00
for _ , unit := range ctxt . compUnits {
syms = append ( syms , unit . rangeSyms ... )
}
2016-04-22 10:31:14 +12:00
dwarfp = syms
2015-02-27 22:57:28 -05:00
}
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
func collectlocs ( ctxt * Link , syms [ ] * sym . Symbol , units [ ] * compilationUnit ) [ ] * sym . Symbol {
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
empty := true
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
for _ , u := range units {
for _ , fn := range u . funcDIEs {
2018-05-17 19:47:52 +03:00
for i := range fn . R {
2018-08-27 11:49:38 -04:00
reloc := & fn . R [ i ] // Copying sym.Reloc has measurable impact on performance
2017-10-24 16:08:46 -04:00
if reloc . Type == objabi . R_DWARFSECREF && strings . HasPrefix ( reloc . Sym . Name , dwarf . LocPrefix ) {
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
reloc . Sym . Attr |= sym . AttrReachable | sym . AttrNotInSymbolTable
syms = append ( syms , reloc . Sym )
empty = false
cmd/link: fix up location lists for dsymutil
LLVM tools, particularly lldb and dsymutil, don't support base address
selection entries in location lists. When targeting GOOS=darwin,
mode, have the linker translate location lists to CU-relative form
instead.
Technically, this isn't necessary when linking internally, as long as
nobody plans to use anything other than Delve to look at the DWARF. But
someone might want to use lldb, and it's really confusing when dwarfdump
shows gibberish for the location entries. The performance cost isn't
noticeable, so enable it even for internal linking.
Doing this in the linker is a little weird, but it was more expensive in
the compiler, probably because the compiler is much more stressful to
the GC. Also, if we decide to only do it for external linking, the
compiler can't see the link mode.
Benchmark before and after this commit on Mac with -dwarflocationlists=1:
name old time/op new time/op delta
StdCmd 21.3s ± 1% 21.3s ± 1% ~ (p=0.310 n=27+27)
Only StdCmd is relevant, because only StdCmd runs the linker. Whatever
the cost is here, it's not very large.
Change-Id: Ic8ef780d0e263230ce6aa3ca3a32fc9abd750b1e
Reviewed-on: https://go-review.googlesource.com/97956
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2018-01-24 13:26:15 -05:00
// LLVM doesn't support base address entries. Strip them out so LLDB and dsymutil don't get confused.
if ctxt . HeadType == objabi . Hdarwin {
2018-02-19 15:26:49 +01:00
removeDwarfAddrListBaseAddress ( ctxt , fn , reloc . Sym , true )
cmd/link: fix up location lists for dsymutil
LLVM tools, particularly lldb and dsymutil, don't support base address
selection entries in location lists. When targeting GOOS=darwin,
mode, have the linker translate location lists to CU-relative form
instead.
Technically, this isn't necessary when linking internally, as long as
nobody plans to use anything other than Delve to look at the DWARF. But
someone might want to use lldb, and it's really confusing when dwarfdump
shows gibberish for the location entries. The performance cost isn't
noticeable, so enable it even for internal linking.
Doing this in the linker is a little weird, but it was more expensive in
the compiler, probably because the compiler is much more stressful to
the GC. Also, if we decide to only do it for external linking, the
compiler can't see the link mode.
Benchmark before and after this commit on Mac with -dwarflocationlists=1:
name old time/op new time/op delta
StdCmd 21.3s ± 1% 21.3s ± 1% ~ (p=0.310 n=27+27)
Only StdCmd is relevant, because only StdCmd runs the linker. Whatever
the cost is here, it's not very large.
Change-Id: Ic8ef780d0e263230ce6aa3ca3a32fc9abd750b1e
Reviewed-on: https://go-review.googlesource.com/97956
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2018-01-24 13:26:15 -05:00
}
cmd/link: one DWARF compilation unit per package
Currently, the linker generates one huge DWARF compilation unit for
the entire Go binary. This commit creates a separate compilation unit
and line table per Go package.
We temporarily lose compilation unit PC range information, since it's
now discontiguous, so harder to emit. We'll bring it back in the next
commit.
Beyond being "more traditional", this has various technical
advantages:
* It should speed up line table lookup, since that requires a
sequential scan of the line table. With this change, a debugger can
first locate the per-package line table and then scan only that line
table.
* Once we emit compilation unit PC ranges again, this should also
speed up various other debugger reverse PC lookups.
* It puts us in a good position to move more DWARF generation into the
compiler, which could produce at least the CU header, per-function
line table fragments, and per-function frame unwinding info that the
linker could simply paste together.
* It will let us record a per-package compiler command-line flags
(#22168).
Change-Id: Ibac642890984636b3ef1d4b37fe97f4453c2cc84
Reviewed-on: https://go-review.googlesource.com/69973
Run-TryBot: Austin Clements <austin@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
2017-10-09 16:19:56 -04:00
// One location list entry per function, but many relocations to it. Don't duplicate.
break
}
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
}
}
}
// Don't emit .debug_loc if it's empty -- it makes the ARM linker mad.
if ! empty {
locsym := ctxt . Syms . Lookup ( ".debug_loc" , 0 )
2017-10-04 17:54:04 -04:00
locsym . Type = sym . SDWARFLOC
locsym . Attr |= sym . AttrReachable
[dev.debug] cmd/compile: better DWARF with optimizations on
Debuggers use DWARF information to find local variables on the
stack and in registers. Prior to this CL, the DWARF information for
functions claimed that all variables were on the stack at all times.
That's incorrect when optimizations are enabled, and results in
debuggers showing data that is out of date or complete gibberish.
After this CL, the compiler is capable of representing variable
locations more accurately, and attempts to do so. Due to limitations of
the SSA backend, it's not possible to be completely correct.
There are a number of problems in the current design. One of the easier
to understand is that variable names currently must be attached to an
SSA value, but not all assignments in the source code actually result
in machine code. For example:
type myint int
var a int
b := myint(int)
and
b := (*uint64)(unsafe.Pointer(a))
don't generate machine code because the underlying representation is the
same, so the correct value of b will not be set when the user would
expect.
Generating the more precise debug information is behind a flag,
dwarflocationlists. Because of the issues described above, setting the
flag may not make the debugging experience much better, and may actually
make it worse in cases where the variable actually is on the stack and
the more complicated analysis doesn't realize it.
A number of changes are included:
- Add a new pseudo-instruction, RegKill, which indicates that the value
in the register has been clobbered.
- Adjust regalloc to emit RegKills in the right places. Significantly,
this means that phis are mixed with StoreReg and RegKills after
regalloc.
- Track variable decomposition in ssa.LocalSlots.
- After the SSA backend is done, analyze the result and build location
lists for each LocalSlot.
- After assembly is done, update the location lists with the assembled
PC offsets, recompose variables, and build DWARF location lists. Emit the
list as a new linker symbol, one per function.
- In the linker, aggregate the location lists into a .debug_loc section.
TODO:
- currently disabled for non-X86/AMD64 because there are no data tables.
go build -toolexec 'toolstash -cmp' -a std succeeds.
With -dwarflocationlists false:
before: f02812195637909ff675782c0b46836a8ff01976
after: 06f61e8112a42ac34fb80e0c818b3cdb84a5e7ec
benchstat -geomean /tmp/220352263 /tmp/621364410
completed 15 of 15, estimated time remaining 0s (eta 3:52PM)
name old time/op new time/op delta
Template 199ms ± 3% 198ms ± 2% ~ (p=0.400 n=15+14)
Unicode 96.6ms ± 5% 96.4ms ± 5% ~ (p=0.838 n=15+15)
GoTypes 653ms ± 2% 647ms ± 2% ~ (p=0.102 n=15+14)
Flate 133ms ± 6% 129ms ± 3% -2.62% (p=0.041 n=15+15)
GoParser 164ms ± 5% 159ms ± 3% -3.05% (p=0.000 n=15+15)
Reflect 428ms ± 4% 422ms ± 3% ~ (p=0.156 n=15+13)
Tar 123ms ±10% 124ms ± 8% ~ (p=0.461 n=15+15)
XML 228ms ± 3% 224ms ± 3% -1.57% (p=0.045 n=15+15)
[Geo mean] 206ms 377ms +82.86%
name old user-time/op new user-time/op delta
Template 292ms ±10% 301ms ±12% ~ (p=0.189 n=15+15)
Unicode 166ms ±37% 158ms ±14% ~ (p=0.418 n=15+14)
GoTypes 962ms ± 6% 963ms ± 7% ~ (p=0.976 n=15+15)
Flate 207ms ±19% 200ms ±14% ~ (p=0.345 n=14+15)
GoParser 246ms ±22% 240ms ±15% ~ (p=0.587 n=15+15)
Reflect 611ms ±13% 587ms ±14% ~ (p=0.085 n=15+13)
Tar 211ms ±12% 217ms ±14% ~ (p=0.355 n=14+15)
XML 335ms ±15% 320ms ±18% ~ (p=0.169 n=15+15)
[Geo mean] 317ms 583ms +83.72%
name old alloc/op new alloc/op delta
Template 40.2MB ± 0% 40.2MB ± 0% -0.15% (p=0.000 n=14+15)
Unicode 29.2MB ± 0% 29.3MB ± 0% ~ (p=0.624 n=15+15)
GoTypes 114MB ± 0% 114MB ± 0% -0.15% (p=0.000 n=15+14)
Flate 25.7MB ± 0% 25.6MB ± 0% -0.18% (p=0.000 n=13+15)
GoParser 32.2MB ± 0% 32.2MB ± 0% -0.14% (p=0.003 n=15+15)
Reflect 77.8MB ± 0% 77.9MB ± 0% ~ (p=0.061 n=15+15)
Tar 27.1MB ± 0% 27.0MB ± 0% -0.11% (p=0.029 n=15+15)
XML 42.7MB ± 0% 42.5MB ± 0% -0.29% (p=0.000 n=15+15)
[Geo mean] 42.1MB 75.0MB +78.05%
name old allocs/op new allocs/op delta
Template 402k ± 1% 398k ± 0% -0.91% (p=0.000 n=15+15)
Unicode 344k ± 1% 344k ± 0% ~ (p=0.715 n=15+14)
GoTypes 1.18M ± 0% 1.17M ± 0% -0.91% (p=0.000 n=15+14)
Flate 243k ± 0% 240k ± 1% -1.05% (p=0.000 n=13+15)
GoParser 327k ± 1% 324k ± 1% -0.96% (p=0.000 n=15+15)
Reflect 984k ± 1% 982k ± 0% ~ (p=0.050 n=15+15)
Tar 261k ± 1% 259k ± 1% -0.77% (p=0.000 n=15+15)
XML 411k ± 0% 404k ± 1% -1.55% (p=0.000 n=15+15)
[Geo mean] 439k 755k +72.01%
name old text-bytes new text-bytes delta
HelloSize 694kB ± 0% 694kB ± 0% -0.00% (p=0.000 n=15+15)
name old data-bytes new data-bytes delta
HelloSize 5.55kB ± 0% 5.55kB ± 0% ~ (all equal)
name old bss-bytes new bss-bytes delta
HelloSize 133kB ± 0% 133kB ± 0% ~ (all equal)
name old exe-bytes new exe-bytes delta
HelloSize 1.04MB ± 0% 1.04MB ± 0% ~ (all equal)
Change-Id: I991fc553ef175db46bb23b2128317bbd48de70d8
Reviewed-on: https://go-review.googlesource.com/41770
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
2017-07-21 18:30:19 -04:00
syms = append ( syms , locsym )
}
return syms
}
2018-02-19 15:26:49 +01:00
// removeDwarfAddrListBaseAddress removes base address selector entries from
// DWARF location lists and range lists.
func removeDwarfAddrListBaseAddress ( ctxt * Link , info , list * sym . Symbol , isloclist bool ) {
cmd/link: fix up location lists for dsymutil
LLVM tools, particularly lldb and dsymutil, don't support base address
selection entries in location lists. When targeting GOOS=darwin,
mode, have the linker translate location lists to CU-relative form
instead.
Technically, this isn't necessary when linking internally, as long as
nobody plans to use anything other than Delve to look at the DWARF. But
someone might want to use lldb, and it's really confusing when dwarfdump
shows gibberish for the location entries. The performance cost isn't
noticeable, so enable it even for internal linking.
Doing this in the linker is a little weird, but it was more expensive in
the compiler, probably because the compiler is much more stressful to
the GC. Also, if we decide to only do it for external linking, the
compiler can't see the link mode.
Benchmark before and after this commit on Mac with -dwarflocationlists=1:
name old time/op new time/op delta
StdCmd 21.3s ± 1% 21.3s ± 1% ~ (p=0.310 n=27+27)
Only StdCmd is relevant, because only StdCmd runs the linker. Whatever
the cost is here, it's not very large.
Change-Id: Ic8ef780d0e263230ce6aa3ca3a32fc9abd750b1e
Reviewed-on: https://go-review.googlesource.com/97956
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2018-01-24 13:26:15 -05:00
// The list symbol contains multiple lists, but they're all for the
// same function, and it's not empty.
fn := list . R [ 0 ] . Sym
// Discard the relocations for the base address entries.
list . R = list . R [ : 0 ]
// Add relocations for each location entry's start and end addresses,
// so that the base address entries aren't necessary.
// We could remove them entirely, but that's more work for a relatively
// small size win. If dsymutil runs it'll throw them away anyway.
// relocate adds a CU-relative relocation to fn+addr at offset.
relocate := func ( addr uint64 , offset int ) {
list . R = append ( list . R , sym . Reloc {
Off : int32 ( offset ) ,
Siz : uint8 ( ctxt . Arch . PtrSize ) ,
Type : objabi . R_ADDRCUOFF ,
Add : int64 ( addr ) ,
Sym : fn ,
} )
}
for i := 0 ; i < len ( list . P ) ; {
first := readPtr ( ctxt , list . P [ i : ] )
second := readPtr ( ctxt , list . P [ i + ctxt . Arch . PtrSize : ] )
if ( first == 0 && second == 0 ) ||
first == ^ uint64 ( 0 ) ||
( ctxt . Arch . PtrSize == 4 && first == uint64 ( ^ uint32 ( 0 ) ) ) {
// Base address selection entry or end of list. Ignore.
i += ctxt . Arch . PtrSize * 2
continue
}
relocate ( first , i )
relocate ( second , i + ctxt . Arch . PtrSize )
// Skip past the actual location.
i += ctxt . Arch . PtrSize * 2
2018-02-19 15:26:49 +01:00
if isloclist {
i += 2 + int ( ctxt . Arch . ByteOrder . Uint16 ( list . P [ i : ] ) )
}
cmd/link: fix up location lists for dsymutil
LLVM tools, particularly lldb and dsymutil, don't support base address
selection entries in location lists. When targeting GOOS=darwin,
mode, have the linker translate location lists to CU-relative form
instead.
Technically, this isn't necessary when linking internally, as long as
nobody plans to use anything other than Delve to look at the DWARF. But
someone might want to use lldb, and it's really confusing when dwarfdump
shows gibberish for the location entries. The performance cost isn't
noticeable, so enable it even for internal linking.
Doing this in the linker is a little weird, but it was more expensive in
the compiler, probably because the compiler is much more stressful to
the GC. Also, if we decide to only do it for external linking, the
compiler can't see the link mode.
Benchmark before and after this commit on Mac with -dwarflocationlists=1:
name old time/op new time/op delta
StdCmd 21.3s ± 1% 21.3s ± 1% ~ (p=0.310 n=27+27)
Only StdCmd is relevant, because only StdCmd runs the linker. Whatever
the cost is here, it's not very large.
Change-Id: Ic8ef780d0e263230ce6aa3ca3a32fc9abd750b1e
Reviewed-on: https://go-review.googlesource.com/97956
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
2018-01-24 13:26:15 -05:00
}
// Rewrite the DIE's relocations to point to the first location entry,
// not the now-useless base address selection entry.
for i := range info . R {
r := & info . R [ i ]
if r . Sym != list {
continue
}
r . Add += int64 ( 2 * ctxt . Arch . PtrSize )
}
}
// Read a pointer-sized uint from the beginning of buf.
func readPtr ( ctxt * Link , buf [ ] byte ) uint64 {
switch ctxt . Arch . PtrSize {
case 4 :
return uint64 ( ctxt . Arch . ByteOrder . Uint32 ( buf ) )
case 8 :
return ctxt . Arch . ByteOrder . Uint64 ( buf )
default :
panic ( "unexpected pointer size" )
}
}
2015-02-27 22:57:28 -05:00
/ *
* Elf .
* /
2017-10-04 17:54:04 -04:00
func dwarfaddshstrings ( ctxt * Link , shstrtab * sym . Symbol ) {
2016-08-21 18:34:24 -04:00
if * FlagW { // disable dwarf
2015-02-27 22:57:28 -05:00
return
}
cmd/link: compress DWARF sections in ELF binaries
Forked from CL 111895.
The trickiest part of this is that the binary layout code (blk,
elfshbits, and various other things) assumes a constant offset between
symbols' and sections' file locations and their virtual addresses.
Compression, of course, breaks this constant offset. But we need to
assign virtual addresses to everything before compression in order to
resolve relocations before compression. As a result, compression needs
to re-compute the "address" of the DWARF sections and symbols based on
their compressed size. Luckily, these are at the end of the file, so
this doesn't perturb any other sections or symbols. (And there is, of
course, a surprising amount of code that assumes the DWARF segment
comes last, so what's one more place?)
Relevant benchmarks:
name old time/op new time/op delta
StdCmd 10.3s ± 2% 10.8s ± 1% +5.43% (p=0.000 n=30+30)
name old text-bytes new text-bytes delta
HelloSize 746kB ± 0% 746kB ± 0% ~ (all equal)
CmdGoSize 8.41MB ± 0% 8.41MB ± 0% ~ (all equal)
[Geo mean] 2.50MB 2.50MB +0.00%
name old data-bytes new data-bytes delta
HelloSize 10.6kB ± 0% 10.6kB ± 0% ~ (all equal)
CmdGoSize 252kB ± 0% 252kB ± 0% ~ (all equal)
[Geo mean] 51.5kB 51.5kB +0.00%
name old bss-bytes new bss-bytes delta
HelloSize 125kB ± 0% 125kB ± 0% ~ (all equal)
CmdGoSize 145kB ± 0% 145kB ± 0% ~ (all equal)
[Geo mean] 135kB 135kB +0.00%
name old exe-bytes new exe-bytes delta
HelloSize 1.60MB ± 0% 1.05MB ± 0% -34.39% (p=0.000 n=30+30)
CmdGoSize 16.5MB ± 0% 11.3MB ± 0% -31.76% (p=0.000 n=30+30)
[Geo mean] 5.14MB 3.44MB -33.08%
Fixes #11799.
Updates #6853.
Change-Id: I64197afe4c01a237523a943088051ee056331c6f
Reviewed-on: https://go-review.googlesource.com/118276
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-05-05 21:49:40 -04:00
secs := [ ] string { "abbrev" , "frame" , "info" , "loc" , "line" , "pubnames" , "pubtypes" , "gdb_scripts" , "ranges" }
for _ , sec := range secs {
Addstring ( shstrtab , ".debug_" + sec )
if ctxt . LinkMode == LinkExternal {
Addstring ( shstrtab , elfRelType + ".debug_" + sec )
} else {
Addstring ( shstrtab , ".zdebug_" + sec )
}
2015-02-27 22:57:28 -05:00
}
}
2016-03-14 09:23:04 -07:00
// Add section symbols for DWARF debug info. This is called before
2015-02-27 22:57:28 -05:00
// dwarfaddelfheaders.
2016-08-19 22:40:38 -04:00
func dwarfaddelfsectionsyms ( ctxt * Link ) {
2016-08-21 18:34:24 -04:00
if * FlagW { // disable dwarf
2015-02-27 22:57:28 -05:00
return
}
2017-10-05 10:20:17 -04:00
if ctxt . LinkMode != LinkExternal {
2016-03-14 09:23:04 -07:00
return
2015-02-27 22:57:28 -05:00
}
cmd/link: compress DWARF sections in ELF binaries
Forked from CL 111895.
The trickiest part of this is that the binary layout code (blk,
elfshbits, and various other things) assumes a constant offset between
symbols' and sections' file locations and their virtual addresses.
Compression, of course, breaks this constant offset. But we need to
assign virtual addresses to everything before compression in order to
resolve relocations before compression. As a result, compression needs
to re-compute the "address" of the DWARF sections and symbols based on
their compressed size. Luckily, these are at the end of the file, so
this doesn't perturb any other sections or symbols. (And there is, of
course, a surprising amount of code that assumes the DWARF segment
comes last, so what's one more place?)
Relevant benchmarks:
name old time/op new time/op delta
StdCmd 10.3s ± 2% 10.8s ± 1% +5.43% (p=0.000 n=30+30)
name old text-bytes new text-bytes delta
HelloSize 746kB ± 0% 746kB ± 0% ~ (all equal)
CmdGoSize 8.41MB ± 0% 8.41MB ± 0% ~ (all equal)
[Geo mean] 2.50MB 2.50MB +0.00%
name old data-bytes new data-bytes delta
HelloSize 10.6kB ± 0% 10.6kB ± 0% ~ (all equal)
CmdGoSize 252kB ± 0% 252kB ± 0% ~ (all equal)
[Geo mean] 51.5kB 51.5kB +0.00%
name old bss-bytes new bss-bytes delta
HelloSize 125kB ± 0% 125kB ± 0% ~ (all equal)
CmdGoSize 145kB ± 0% 145kB ± 0% ~ (all equal)
[Geo mean] 135kB 135kB +0.00%
name old exe-bytes new exe-bytes delta
HelloSize 1.60MB ± 0% 1.05MB ± 0% -34.39% (p=0.000 n=30+30)
CmdGoSize 16.5MB ± 0% 11.3MB ± 0% -31.76% (p=0.000 n=30+30)
[Geo mean] 5.14MB 3.44MB -33.08%
Fixes #11799.
Updates #6853.
Change-Id: I64197afe4c01a237523a943088051ee056331c6f
Reviewed-on: https://go-review.googlesource.com/118276
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-05-05 21:49:40 -04:00
2017-10-04 17:54:04 -04:00
s := ctxt . Syms . Lookup ( ".debug_info" , 0 )
putelfsectionsym ( ctxt . Out , s , s . Sect . Elfsect . ( * ElfShdr ) . shnum )
s = ctxt . Syms . Lookup ( ".debug_abbrev" , 0 )
putelfsectionsym ( ctxt . Out , s , s . Sect . Elfsect . ( * ElfShdr ) . shnum )
s = ctxt . Syms . Lookup ( ".debug_line" , 0 )
putelfsectionsym ( ctxt . Out , s , s . Sect . Elfsect . ( * ElfShdr ) . shnum )
s = ctxt . Syms . Lookup ( ".debug_frame" , 0 )
putelfsectionsym ( ctxt . Out , s , s . Sect . Elfsect . ( * ElfShdr ) . shnum )
s = ctxt . Syms . Lookup ( ".debug_loc" , 0 )
if s . Sect != nil {
putelfsectionsym ( ctxt . Out , s , s . Sect . Elfsect . ( * ElfShdr ) . shnum )
}
s = ctxt . Syms . Lookup ( ".debug_ranges" , 0 )
if s . Sect != nil {
putelfsectionsym ( ctxt . Out , s , s . Sect . Elfsect . ( * ElfShdr ) . shnum )
2017-05-02 16:46:01 +02:00
}
2015-02-27 22:57:28 -05:00
}
cmd/link: compress DWARF sections in ELF binaries
Forked from CL 111895.
The trickiest part of this is that the binary layout code (blk,
elfshbits, and various other things) assumes a constant offset between
symbols' and sections' file locations and their virtual addresses.
Compression, of course, breaks this constant offset. But we need to
assign virtual addresses to everything before compression in order to
resolve relocations before compression. As a result, compression needs
to re-compute the "address" of the DWARF sections and symbols based on
their compressed size. Luckily, these are at the end of the file, so
this doesn't perturb any other sections or symbols. (And there is, of
course, a surprising amount of code that assumes the DWARF segment
comes last, so what's one more place?)
Relevant benchmarks:
name old time/op new time/op delta
StdCmd 10.3s ± 2% 10.8s ± 1% +5.43% (p=0.000 n=30+30)
name old text-bytes new text-bytes delta
HelloSize 746kB ± 0% 746kB ± 0% ~ (all equal)
CmdGoSize 8.41MB ± 0% 8.41MB ± 0% ~ (all equal)
[Geo mean] 2.50MB 2.50MB +0.00%
name old data-bytes new data-bytes delta
HelloSize 10.6kB ± 0% 10.6kB ± 0% ~ (all equal)
CmdGoSize 252kB ± 0% 252kB ± 0% ~ (all equal)
[Geo mean] 51.5kB 51.5kB +0.00%
name old bss-bytes new bss-bytes delta
HelloSize 125kB ± 0% 125kB ± 0% ~ (all equal)
CmdGoSize 145kB ± 0% 145kB ± 0% ~ (all equal)
[Geo mean] 135kB 135kB +0.00%
name old exe-bytes new exe-bytes delta
HelloSize 1.60MB ± 0% 1.05MB ± 0% -34.39% (p=0.000 n=30+30)
CmdGoSize 16.5MB ± 0% 11.3MB ± 0% -31.76% (p=0.000 n=30+30)
[Geo mean] 5.14MB 3.44MB -33.08%
Fixes #11799.
Updates #6853.
Change-Id: I64197afe4c01a237523a943088051ee056331c6f
Reviewed-on: https://go-review.googlesource.com/118276
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-05-05 21:49:40 -04:00
// dwarfcompress compresses the DWARF sections. This must happen after
// relocations are applied. After this, dwarfp will contain a
// different (new) set of symbols, and sections may have been replaced.
func dwarfcompress ( ctxt * Link ) {
2018-06-20 16:45:25 -04:00
supported := ctxt . IsELF || ctxt . HeadType == objabi . Hwindows || ctxt . HeadType == objabi . Hdarwin
if ! ctxt . compressDWARF || ! supported || ctxt . LinkMode != LinkInternal {
cmd/link: compress DWARF sections in ELF binaries
Forked from CL 111895.
The trickiest part of this is that the binary layout code (blk,
elfshbits, and various other things) assumes a constant offset between
symbols' and sections' file locations and their virtual addresses.
Compression, of course, breaks this constant offset. But we need to
assign virtual addresses to everything before compression in order to
resolve relocations before compression. As a result, compression needs
to re-compute the "address" of the DWARF sections and symbols based on
their compressed size. Luckily, these are at the end of the file, so
this doesn't perturb any other sections or symbols. (And there is, of
course, a surprising amount of code that assumes the DWARF segment
comes last, so what's one more place?)
Relevant benchmarks:
name old time/op new time/op delta
StdCmd 10.3s ± 2% 10.8s ± 1% +5.43% (p=0.000 n=30+30)
name old text-bytes new text-bytes delta
HelloSize 746kB ± 0% 746kB ± 0% ~ (all equal)
CmdGoSize 8.41MB ± 0% 8.41MB ± 0% ~ (all equal)
[Geo mean] 2.50MB 2.50MB +0.00%
name old data-bytes new data-bytes delta
HelloSize 10.6kB ± 0% 10.6kB ± 0% ~ (all equal)
CmdGoSize 252kB ± 0% 252kB ± 0% ~ (all equal)
[Geo mean] 51.5kB 51.5kB +0.00%
name old bss-bytes new bss-bytes delta
HelloSize 125kB ± 0% 125kB ± 0% ~ (all equal)
CmdGoSize 145kB ± 0% 145kB ± 0% ~ (all equal)
[Geo mean] 135kB 135kB +0.00%
name old exe-bytes new exe-bytes delta
HelloSize 1.60MB ± 0% 1.05MB ± 0% -34.39% (p=0.000 n=30+30)
CmdGoSize 16.5MB ± 0% 11.3MB ± 0% -31.76% (p=0.000 n=30+30)
[Geo mean] 5.14MB 3.44MB -33.08%
Fixes #11799.
Updates #6853.
Change-Id: I64197afe4c01a237523a943088051ee056331c6f
Reviewed-on: https://go-review.googlesource.com/118276
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-05-05 21:49:40 -04:00
return
}
var start int
var newDwarfp [ ] * sym . Symbol
Segdwarf . Sections = Segdwarf . Sections [ : 0 ]
for i , s := range dwarfp {
// Find the boundaries between sections and compress
// the whole section once we've found the last of its
// symbols.
if i + 1 >= len ( dwarfp ) || s . Sect != dwarfp [ i + 1 ] . Sect {
s1 := compressSyms ( ctxt , dwarfp [ start : i + 1 ] )
if s1 == nil {
// Compression didn't help.
newDwarfp = append ( newDwarfp , dwarfp [ start : i + 1 ] ... )
Segdwarf . Sections = append ( Segdwarf . Sections , s . Sect )
} else {
compressedSegName := ".zdebug_" + s . Sect . Name [ len ( ".debug_" ) : ]
sect := addsection ( ctxt . Arch , & Segdwarf , compressedSegName , 04 )
sect . Length = uint64 ( len ( s1 ) )
newSym := ctxt . Syms . Lookup ( compressedSegName , 0 )
newSym . P = s1
newSym . Size = int64 ( len ( s1 ) )
newSym . Sect = sect
newDwarfp = append ( newDwarfp , newSym )
}
start = i + 1
}
}
dwarfp = newDwarfp
// Re-compute the locations of the compressed DWARF symbols
// and sections, since the layout of these within the file is
// based on Section.Vaddr and Symbol.Value.
pos := Segdwarf . Vaddr
var prevSect * sym . Section
for _ , s := range dwarfp {
s . Value = int64 ( pos )
if s . Sect != prevSect {
s . Sect . Vaddr = uint64 ( s . Value )
prevSect = s . Sect
}
if s . Sub != nil {
log . Fatalf ( "%s: unexpected sub-symbols" , s )
}
pos += uint64 ( s . Size )
2018-06-19 15:39:17 -04:00
if ctxt . HeadType == objabi . Hwindows {
pos = uint64 ( Rnd ( int64 ( pos ) , PEFILEALIGN ) )
}
cmd/link: compress DWARF sections in ELF binaries
Forked from CL 111895.
The trickiest part of this is that the binary layout code (blk,
elfshbits, and various other things) assumes a constant offset between
symbols' and sections' file locations and their virtual addresses.
Compression, of course, breaks this constant offset. But we need to
assign virtual addresses to everything before compression in order to
resolve relocations before compression. As a result, compression needs
to re-compute the "address" of the DWARF sections and symbols based on
their compressed size. Luckily, these are at the end of the file, so
this doesn't perturb any other sections or symbols. (And there is, of
course, a surprising amount of code that assumes the DWARF segment
comes last, so what's one more place?)
Relevant benchmarks:
name old time/op new time/op delta
StdCmd 10.3s ± 2% 10.8s ± 1% +5.43% (p=0.000 n=30+30)
name old text-bytes new text-bytes delta
HelloSize 746kB ± 0% 746kB ± 0% ~ (all equal)
CmdGoSize 8.41MB ± 0% 8.41MB ± 0% ~ (all equal)
[Geo mean] 2.50MB 2.50MB +0.00%
name old data-bytes new data-bytes delta
HelloSize 10.6kB ± 0% 10.6kB ± 0% ~ (all equal)
CmdGoSize 252kB ± 0% 252kB ± 0% ~ (all equal)
[Geo mean] 51.5kB 51.5kB +0.00%
name old bss-bytes new bss-bytes delta
HelloSize 125kB ± 0% 125kB ± 0% ~ (all equal)
CmdGoSize 145kB ± 0% 145kB ± 0% ~ (all equal)
[Geo mean] 135kB 135kB +0.00%
name old exe-bytes new exe-bytes delta
HelloSize 1.60MB ± 0% 1.05MB ± 0% -34.39% (p=0.000 n=30+30)
CmdGoSize 16.5MB ± 0% 11.3MB ± 0% -31.76% (p=0.000 n=30+30)
[Geo mean] 5.14MB 3.44MB -33.08%
Fixes #11799.
Updates #6853.
Change-Id: I64197afe4c01a237523a943088051ee056331c6f
Reviewed-on: https://go-review.googlesource.com/118276
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
2018-05-05 21:49:40 -04:00
}
Segdwarf . Length = pos - Segdwarf . Vaddr
}
2018-04-24 13:05:10 +02:00
type compilationUnitByStartPC [ ] * compilationUnit
func ( v compilationUnitByStartPC ) Len ( ) int { return len ( v ) }
func ( v compilationUnitByStartPC ) Swap ( i , j int ) { v [ i ] , v [ j ] = v [ j ] , v [ i ] }
func ( v compilationUnitByStartPC ) Less ( i , j int ) bool {
2018-09-25 11:52:24 +02:00
switch {
case len ( v [ i ] . lib . Textp ) == 0 && len ( v [ j ] . lib . Textp ) == 0 :
return v [ i ] . lib . Pkg < v [ j ] . lib . Pkg
case len ( v [ i ] . lib . Textp ) != 0 && len ( v [ j ] . lib . Textp ) == 0 :
return true
case len ( v [ i ] . lib . Textp ) == 0 && len ( v [ j ] . lib . Textp ) != 0 :
return false
default :
return v [ i ] . lib . Textp [ 0 ] . Value < v [ j ] . lib . Textp [ 0 ] . Value
}
2018-04-24 13:05:10 +02:00
}