blob: 17cb6fbc99efd28ab8141f09cd974d843c8fd2cb [file] [log] [blame]
// Copyright 2012 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.
// This is gccgo-specific code that uses DWARF information to fetch
// file/line information for PC values. This package registers itself
// with the runtime package.
package elf
import (
"debug/dwarf"
"debug/macho"
"os"
"runtime"
"sort"
"sync"
)
func init() {
// Register our lookup functions with the runtime package.
runtime.RegisterDebugLookup(funcFileLine, symbolValue)
}
// The file struct holds information for a specific file that is part
// of the execution.
type file struct {
elf *File // If ELF
macho *macho.File // If Mach-O
dwarf *dwarf.Data // DWARF information
symsByName []sym // Sorted by name
symsByAddr []sym // Sorted by address
}
// Sort symbols by name.
type symsByName []sym
func (s symsByName) Len() int { return len(s) }
func (s symsByName) Less(i, j int) bool { return s[i].name < s[j].name }
func (s symsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// Sort symbols by address.
type symsByAddr []sym
func (s symsByAddr) Len() int { return len(s) }
func (s symsByAddr) Less(i, j int) bool { return s[i].addr < s[j].addr }
func (s symsByAddr) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// The sym structure holds the information we care about for a symbol,
// namely name and address.
type sym struct {
name string
addr uintptr
}
// Open an input file.
func open(name string) (*file, error) {
efile, err := Open(name)
var mfile *macho.File
if err != nil {
var merr error
mfile, merr = macho.Open(name)
if merr != nil {
return nil, err
}
}
r := &file{elf: efile, macho: mfile}
if efile != nil {
r.dwarf, err = efile.DWARF()
} else {
r.dwarf, err = mfile.DWARF()
}
if err != nil {
return nil, err
}
var syms []sym
if efile != nil {
esyms, err := efile.Symbols()
if err != nil {
return nil, err
}
syms = make([]sym, 0, len(esyms))
for _, s := range esyms {
if ST_TYPE(s.Info) == STT_FUNC {
syms = append(syms, sym{s.Name, uintptr(s.Value)})
}
}
} else {
syms = make([]sym, 0, len(mfile.Symtab.Syms))
for _, s := range mfile.Symtab.Syms {
syms = append(syms, sym{s.Name, uintptr(s.Value)})
}
}
r.symsByName = make([]sym, len(syms))
copy(r.symsByName, syms)
sort.Sort(symsByName(r.symsByName))
r.symsByAddr = syms
sort.Sort(symsByAddr(r.symsByAddr))
return r, nil
}
// The main executable
var executable *file
// Only open the executable once.
var executableOnce sync.Once
func openExecutable() {
executableOnce.Do(func() {
f, err := open("/proc/self/exe")
if err != nil {
f, err = open(os.Args[0])
if err != nil {
return
}
}
executable = f
})
}
// The funcFileLine function looks up the function name, file name,
// and line number for a PC value.
func funcFileLine(pc uintptr, function *string, file *string, line *int) bool {
openExecutable()
if executable == nil || executable.dwarf == nil {
return false
}
f, ln, err := executable.dwarf.FileLine(uint64(pc))
if err != nil {
return false
}
*file = f
*line = ln
*function = ""
if len(executable.symsByAddr) > 0 && pc >= executable.symsByAddr[0].addr {
i := sort.Search(len(executable.symsByAddr),
func(i int) bool { return executable.symsByAddr[i].addr > pc })
*function = executable.symsByAddr[i-1].name
}
return true
}
// The symbolValue function fetches the value of a symbol.
func symbolValue(name string, val *uintptr) bool {
i := sort.Search(len(executable.symsByName),
func(i int) bool { return executable.symsByName[i].name >= name })
if i >= len(executable.symsByName) || executable.symsByName[i].name != name {
return false
}
*val = executable.symsByName[i].addr
return true
}