Commit b52b77cb authored by Than McIntosh's avatar Than McIntosh

cmd/compile, cmd/link: support for DWARF file reference relocations

New relocation flavor R_DWARFFILEREF, to be applied to DWARF attribute
values that correspond to file references (ex: DW_AT_decl_file,
DW_AT_call_file). The LSym for this relocation is the file itself; the
linker replaces the relocation target with the index of the specified
file in the line table's file section.

Note: for testing purposes this patch changes the DWARF function
subprogram DIE abbrev to include DW_AT_decl_file (allowed by DWARF
but not especially useful) so as to have a way to test this
functionality. This attribute will be removed once there are other
file reference attributes (coming as part of inlining support).

Change-Id: Icf676beb60fcc33f06d78e747ef717532daaa3ba
Reviewed-on: https://go-review.googlesource.com/73330
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarAustin Clements <austin@google.com>
parent a93bc1d2
...@@ -123,6 +123,7 @@ type Context interface { ...@@ -123,6 +123,7 @@ type Context interface {
AddAddress(s Sym, t interface{}, ofs int64) AddAddress(s Sym, t interface{}, ofs int64)
AddSectionOffset(s Sym, size int, t interface{}, ofs int64) AddSectionOffset(s Sym, size int, t interface{}, ofs int64)
AddString(s Sym, v string) AddString(s Sym, v string)
AddFileRef(s Sym, f interface{})
} }
// AppendUleb128 appends v to b using DWARF's unsigned LEB128 encoding. // AppendUleb128 appends v to b using DWARF's unsigned LEB128 encoding.
...@@ -303,6 +304,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{ ...@@ -303,6 +304,7 @@ var abbrevs = [DW_NABRV]dwAbbrev{
{DW_AT_low_pc, DW_FORM_addr}, {DW_AT_low_pc, DW_FORM_addr},
{DW_AT_high_pc, DW_FORM_addr}, {DW_AT_high_pc, DW_FORM_addr},
{DW_AT_frame_base, DW_FORM_block1}, {DW_AT_frame_base, DW_FORM_block1},
{DW_AT_decl_file, DW_FORM_data4},
{DW_AT_external, DW_FORM_flag}, {DW_AT_external, DW_FORM_flag},
}, },
}, },
...@@ -788,12 +790,14 @@ func PutRanges(ctxt Context, sym Sym, base Sym, ranges []Range) { ...@@ -788,12 +790,14 @@ func PutRanges(ctxt Context, sym Sym, base Sym, ranges []Range) {
// PutFunc writes a DIE for a function to s. // PutFunc writes a DIE for a function to s.
// It also writes child DIEs for each variable in vars. // It also writes child DIEs for each variable in vars.
func PutFunc(ctxt Context, info, loc, ranges Sym, name string, external bool, startPC Sym, size int64, scopes []Scope) error { func PutFunc(ctxt Context, info, loc, ranges, filesym Sym, name string, external bool, startPC Sym, size int64, scopes []Scope) error {
Uleb128put(ctxt, info, DW_ABRV_FUNCTION) Uleb128put(ctxt, info, DW_ABRV_FUNCTION)
putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name) putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_string, DW_CLS_STRING, int64(len(name)), name)
putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, 0, startPC) putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, 0, startPC)
putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, size, startPC) putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_addr, DW_CLS_ADDRESS, size, startPC)
putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa}) putattr(ctxt, info, DW_ABRV_FUNCTION, DW_FORM_block1, DW_CLS_BLOCK, 1, []byte{DW_OP_call_frame_cfa})
// DW_AT_decl_file attribute
ctxt.AddFileRef(info, filesym)
var ev int64 var ev int64
if external { if external {
ev = 1 ev = 1
......
...@@ -458,7 +458,14 @@ func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64 ...@@ -458,7 +458,14 @@ func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
rsym := t.(*LSym) rsym := t.(*LSym)
ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs) ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
r := &ls.R[len(ls.R)-1] r := &ls.R[len(ls.R)-1]
r.Type = objabi.R_DWARFREF r.Type = objabi.R_DWARFSECREF
}
func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) {
ls := s.(*LSym)
rsym := f.(*LSym)
ls.WriteAddr(c.Link, ls.Size, 4, rsym, 0)
r := &ls.R[len(ls.R)-1]
r.Type = objabi.R_DWARFFILEREF
} }
// dwarfSym returns the DWARF symbols for TEXT symbol. // dwarfSym returns the DWARF symbols for TEXT symbol.
...@@ -480,6 +487,19 @@ func (s *LSym) Len() int64 { ...@@ -480,6 +487,19 @@ func (s *LSym) Len() int64 {
return s.Size return s.Size
} }
// fileSymbol returns a symbol corresponding to the source file of the
// first instruction (prog) of the specified function. This will
// presumably be the file in which the function is defined.
func (ctxt *Link) fileSymbol(fn *LSym) *LSym {
p := fn.Func.Text
if p != nil {
f, _ := linkgetlineFromPos(ctxt, p.Pos)
fsym := ctxt.Lookup(f)
return fsym
}
return nil
}
// populateDWARF fills in the DWARF Debugging Information Entries for TEXT symbol s. // populateDWARF fills in the DWARF Debugging Information Entries for TEXT symbol s.
// The DWARFs symbol must already have been initialized in InitTextSym. // The DWARFs symbol must already have been initialized in InitTextSym.
func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym) { func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym) {
...@@ -491,7 +511,9 @@ func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym) { ...@@ -491,7 +511,9 @@ func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym) {
if ctxt.DebugInfo != nil { if ctxt.DebugInfo != nil {
scopes = ctxt.DebugInfo(s, curfn) scopes = ctxt.DebugInfo(s, curfn)
} }
err := dwarf.PutFunc(dwCtxt{ctxt}, info, loc, ranges, s.Name, !s.Static(), s, s.Size, scopes)
fs := ctxt.fileSymbol(s)
err := dwarf.PutFunc(dwCtxt{ctxt}, info, loc, ranges, fs, s.Name, !s.Static(), s, s.Size, scopes)
if err != nil { if err != nil {
ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err) ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
} }
......
...@@ -99,8 +99,15 @@ const ( ...@@ -99,8 +99,15 @@ const (
// of a JMP instruction, by encoding the address into the instruction. // of a JMP instruction, by encoding the address into the instruction.
// The stack nosplit check ignores this since it is not a function call. // The stack nosplit check ignores this since it is not a function call.
R_JMPMIPS R_JMPMIPS
// R_DWARFREF resolves to the offset of the symbol from its section.
R_DWARFREF // R_DWARFSECREF resolves to the offset of the symbol from its section.
// Target of relocation must be size 4 (in current implementation).
R_DWARFSECREF
// R_DWARFFILEREF resolves to an index into the DWARF .debug_line
// file table for the specified file symbol. Must be applied to an
// attribute of form DW_FORM_data4.
R_DWARFFILEREF
// Platform dependent relocations. Architectures with fixed width instructions // Platform dependent relocations. Architectures with fixed width instructions
// have the inherent issue that a 32-bit (or 64-bit!) displacement cannot be // have the inherent issue that a 32-bit (or 64-bit!) displacement cannot be
......
...@@ -4,9 +4,9 @@ package objabi ...@@ -4,9 +4,9 @@ package objabi
import "fmt" import "fmt"
const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLS" const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_WEAKADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_METHODOFFR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLS"
var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 129, 136, 144, 152, 160, 166, 172, 178, 188, 197, 208, 219, 229, 238, 248, 262, 276, 292, 306, 320, 331, 345, 360, 377, 395, 416, 426, 437, 450} var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 60, 66, 72, 81, 92, 101, 112, 122, 129, 136, 144, 152, 160, 166, 172, 178, 188, 197, 208, 219, 229, 238, 251, 265, 279, 293, 309, 323, 337, 348, 362, 377, 394, 412, 433, 443, 454, 467}
func (i RelocType) String() string { func (i RelocType) String() string {
i -= 1 i -= 1
......
...@@ -507,7 +507,7 @@ func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, secto ...@@ -507,7 +507,7 @@ func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, secto
default: default:
return false return false
case objabi.R_DWARFREF: case objabi.R_DWARFSECREF:
v = ld.IMAGE_REL_AMD64_SECREL v = ld.IMAGE_REL_AMD64_SECREL
case objabi.R_ADDR: case objabi.R_ADDR:
......
...@@ -110,6 +110,10 @@ func trampoline(ctxt *Link, s *sym.Symbol) { ...@@ -110,6 +110,10 @@ func trampoline(ctxt *Link, s *sym.Symbol) {
func relocsym(ctxt *Link, s *sym.Symbol) { func relocsym(ctxt *Link, s *sym.Symbol) {
for ri := int32(0); ri < int32(len(s.R)); ri++ { for ri := int32(0); ri < int32(len(s.R)); ri++ {
r := &s.R[ri] r := &s.R[ri]
if r.Done {
// Relocation already processed by an earlier phase.
continue
}
r.Done = true r.Done = true
off := r.Off off := r.Off
siz := int32(r.Siz) siz := int32(r.Siz)
...@@ -145,6 +149,12 @@ func relocsym(ctxt *Link, s *sym.Symbol) { ...@@ -145,6 +149,12 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
if r.Siz == 0 { // informational relocation - no work to do if r.Siz == 0 { // informational relocation - no work to do
continue continue
} }
if r.Type == objabi.R_DWARFFILEREF {
// These should have been processed previously during
// line table writing.
Errorf(s, "orphan R_DWARFFILEREF reloc to %v", r.Sym.Name)
continue
}
// We need to be able to reference dynimport symbols when linking against // We need to be able to reference dynimport symbols when linking against
// shared libraries, and Solaris needs it always // shared libraries, and Solaris needs it always
...@@ -306,7 +316,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) { ...@@ -306,7 +316,7 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", r.Sym.Name, uint64(o), Symaddr(r.Sym), r.Add) Errorf(s, "non-pc-relative relocation address for %s is too big: %#x (%#x + %#x)", r.Sym.Name, uint64(o), Symaddr(r.Sym), r.Add)
errorexit() errorexit()
} }
case objabi.R_DWARFREF: case objabi.R_DWARFSECREF:
if r.Sym.Sect == nil { if r.Sym.Sect == nil {
Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name) Errorf(s, "missing DWARF section for relocation target %s", r.Sym.Name)
} }
...@@ -324,9 +334,9 @@ func relocsym(ctxt *Link, s *sym.Symbol) { ...@@ -324,9 +334,9 @@ func relocsym(ctxt *Link, s *sym.Symbol) {
} }
// PE code emits IMAGE_REL_I386_SECREL and IMAGE_REL_AMD64_SECREL // PE code emits IMAGE_REL_I386_SECREL and IMAGE_REL_AMD64_SECREL
// for R_DWARFREF relocations, while R_ADDR is replaced with // for R_DWARFSECREF relocations, while R_ADDR is replaced with
// IMAGE_REL_I386_DIR32, IMAGE_REL_AMD64_ADDR64 and IMAGE_REL_AMD64_ADDR32. // IMAGE_REL_I386_DIR32, IMAGE_REL_AMD64_ADDR64 and IMAGE_REL_AMD64_ADDR32.
// Do not replace R_DWARFREF with R_ADDR for windows - // Do not replace R_DWARFSECREF with R_ADDR for windows -
// let PE code emit correct relocations. // let PE code emit correct relocations.
if ctxt.HeadType != objabi.Hwindows { if ctxt.HeadType != objabi.Hwindows {
r.Type = objabi.R_ADDR r.Type = objabi.R_ADDR
......
...@@ -62,10 +62,14 @@ func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64 ...@@ -62,10 +62,14 @@ func (c dwctxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64
ls.AddAddrPlus4(t.(*sym.Symbol), 0) ls.AddAddrPlus4(t.(*sym.Symbol), 0)
} }
r := &ls.R[len(ls.R)-1] r := &ls.R[len(ls.R)-1]
r.Type = objabi.R_DWARFREF r.Type = objabi.R_DWARFSECREF
r.Add = ofs r.Add = ofs
} }
func (c dwctxt) AddFileRef(s dwarf.Sym, f interface{}) {
panic("should be used only in the compiler")
}
var gdbscript string var gdbscript string
var dwarfp []*sym.Symbol var dwarfp []*sym.Symbol
...@@ -220,7 +224,7 @@ func adddwarfref(ctxt *Link, s *sym.Symbol, t *sym.Symbol, size int) int64 { ...@@ -220,7 +224,7 @@ func adddwarfref(ctxt *Link, s *sym.Symbol, t *sym.Symbol, size int) int64 {
result = s.AddAddrPlus4(t, 0) result = s.AddAddrPlus4(t, 0)
} }
r := &s.R[len(s.R)-1] r := &s.R[len(s.R)-1]
r.Type = objabi.R_DWARFREF r.Type = objabi.R_DWARFSECREF
return result return result
} }
...@@ -1030,7 +1034,7 @@ func importInfoSymbol(ctxt *Link, dsym *sym.Symbol) { ...@@ -1030,7 +1034,7 @@ func importInfoSymbol(ctxt *Link, dsym *sym.Symbol) {
dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable dsym.Attr |= sym.AttrNotInSymbolTable | sym.AttrReachable
dsym.Type = sym.SDWARFINFO dsym.Type = sym.SDWARFINFO
for _, r := range dsym.R { for _, r := range dsym.R {
if r.Type == objabi.R_DWARFREF && r.Sym.Size == 0 { if r.Type == objabi.R_DWARFSECREF && r.Sym.Size == 0 {
if ctxt.BuildMode == BuildModeShared { if ctxt.BuildMode == BuildModeShared {
// These type symbols may not be present in BuildModeShared. Skip. // These type symbols may not be present in BuildModeShared. Skip.
continue continue
...@@ -1184,6 +1188,40 @@ func writelines(ctxt *Link, lib *sym.Library, textp []*sym.Symbol, ls *sym.Symbo ...@@ -1184,6 +1188,40 @@ func writelines(ctxt *Link, lib *sym.Library, textp []*sym.Symbol, ls *sym.Symbo
ls.SetUint32(ctxt.Arch, unitLengthOffset, uint32(ls.Size-unitstart)) ls.SetUint32(ctxt.Arch, unitLengthOffset, uint32(ls.Size-unitstart))
ls.SetUint32(ctxt.Arch, headerLengthOffset, uint32(headerend-headerstart)) ls.SetUint32(ctxt.Arch, headerLengthOffset, uint32(headerend-headerstart))
// 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.
for fidx := 0; fidx < len(funcs); fidx++ {
f := funcs[fidx]
for ri := 0; ri < len(f.R); ri++ {
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 {
Errorf(f, "R_DWARFFILEREF relocation file missing: %v", r.Sym)
}
}
}
return dwinfo, funcs return dwinfo, funcs
} }
...@@ -1617,7 +1655,7 @@ func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*compilationUnit) []*sy ...@@ -1617,7 +1655,7 @@ func collectlocs(ctxt *Link, syms []*sym.Symbol, units []*compilationUnit) []*sy
for _, u := range units { for _, u := range units {
for _, fn := range u.funcDIEs { for _, fn := range u.funcDIEs {
for _, reloc := range fn.R { for _, reloc := range fn.R {
if reloc.Type == objabi.R_DWARFREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) { if reloc.Type == objabi.R_DWARFSECREF && strings.HasPrefix(reloc.Sym.Name, dwarf.LocPrefix) {
reloc.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable reloc.Sym.Attr |= sym.AttrReachable | sym.AttrNotInSymbolTable
syms = append(syms, reloc.Sym) syms = append(syms, reloc.Sym)
empty = false empty = false
......
...@@ -299,7 +299,7 @@ func main() { ...@@ -299,7 +299,7 @@ func main() {
} }
} }
func TestVarDeclCoords(t *testing.T) { func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) {
testenv.MustHaveGoBuild(t) testenv.MustHaveGoBuild(t)
if runtime.GOOS == "plan9" { if runtime.GOOS == "plan9" {
...@@ -329,6 +329,7 @@ func main() { ...@@ -329,6 +329,7 @@ func main() {
rdr := d.Reader() rdr := d.Reader()
var iEntry *dwarf.Entry var iEntry *dwarf.Entry
var pEntry *dwarf.Entry
foundMain := false foundMain := false
for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
if err != nil { if err != nil {
...@@ -336,6 +337,7 @@ func main() { ...@@ -336,6 +337,7 @@ func main() {
} }
if entry.Tag == dwarf.TagSubprogram && entry.Val(dwarf.AttrName).(string) == "main.main" { if entry.Tag == dwarf.TagSubprogram && entry.Val(dwarf.AttrName).(string) == "main.main" {
foundMain = true foundMain = true
pEntry = entry
continue continue
} }
if !foundMain { if !foundMain {
...@@ -354,4 +356,9 @@ func main() { ...@@ -354,4 +356,9 @@ func main() {
if line == nil || line.(int64) != 5 { if line == nil || line.(int64) != 5 {
t.Errorf("DW_AT_decl_line for i is %v, want 5", line) t.Errorf("DW_AT_decl_line for i is %v, want 5", line)
} }
file := pEntry.Val(dwarf.AttrDeclFile)
if file == nil || file.(int64) != 1 {
t.Errorf("DW_AT_decl_file for main is %v, want 1", file)
}
} }
...@@ -471,7 +471,7 @@ func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, secto ...@@ -471,7 +471,7 @@ func pereloc1(arch *sys.Arch, out *ld.OutBuf, s *sym.Symbol, r *sym.Reloc, secto
default: default:
return false return false
case objabi.R_DWARFREF: case objabi.R_DWARFSECREF:
v = ld.IMAGE_REL_I386_SECREL v = ld.IMAGE_REL_I386_SECREL
case objabi.R_ADDR: case objabi.R_ADDR:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment