mirror of
https://github.com/autc04/Retro68.git
synced 2024-11-28 21:49:33 +00:00
162 lines
3.7 KiB
Go
162 lines
3.7 KiB
Go
|
// 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
|
||
|
}
|