| // 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 |
| } |