mirror of
https://github.com/golang/go
synced 2024-11-13 19:30:22 -07:00
cmd/addr2line, cmd/nm: factor object reading into cmd/internal/objfile
To do in another CL: make cmd/objdump use cmd/internal/objfile too. There is a package placement decision in this CL: cmd/internal/objfile instead of internal/objfile. I chose to put internal under cmd to make clear (and enforce) that no standard library packages should use this (it's a bit dependency-heavy). LGTM=r R=r CC=golang-codereviews https://golang.org/cl/123910043
This commit is contained in:
parent
fad69a7b77
commit
08033f9816
@ -19,17 +19,14 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"debug/elf"
|
|
||||||
"debug/gosym"
|
|
||||||
"debug/macho"
|
|
||||||
"debug/pe"
|
|
||||||
"debug/plan9obj"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"cmd/internal/objfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
func printUsage(w *os.File) {
|
func printUsage(w *os.File) {
|
||||||
@ -60,18 +57,12 @@ func main() {
|
|||||||
usage()
|
usage()
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.Open(flag.Arg(0))
|
f, err := objfile.Open(flag.Arg(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
textStart, symtab, pclntab, err := loadTables(f)
|
tab, err := f.PCLineTable()
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("reading %s: %v", flag.Arg(0), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
pcln := gosym.NewLineTable(pclntab, textStart)
|
|
||||||
tab, err := gosym.NewTable(symtab, pcln)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("reading %s: %v", flag.Arg(0), err)
|
log.Fatalf("reading %s: %v", flag.Arg(0), err)
|
||||||
}
|
}
|
||||||
@ -102,145 +93,3 @@ func main() {
|
|||||||
}
|
}
|
||||||
stdout.Flush()
|
stdout.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadTables(f *os.File) (textStart uint64, symtab, pclntab []byte, err error) {
|
|
||||||
if obj, err := elf.NewFile(f); err == nil {
|
|
||||||
if sect := obj.Section(".text"); sect != nil {
|
|
||||||
textStart = sect.Addr
|
|
||||||
}
|
|
||||||
if sect := obj.Section(".gosymtab"); sect != nil {
|
|
||||||
if symtab, err = sect.Data(); err != nil {
|
|
||||||
return 0, nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sect := obj.Section(".gopclntab"); sect != nil {
|
|
||||||
if pclntab, err = sect.Data(); err != nil {
|
|
||||||
return 0, nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return textStart, symtab, pclntab, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj, err := macho.NewFile(f); err == nil {
|
|
||||||
if sect := obj.Section("__text"); sect != nil {
|
|
||||||
textStart = sect.Addr
|
|
||||||
}
|
|
||||||
if sect := obj.Section("__gosymtab"); sect != nil {
|
|
||||||
if symtab, err = sect.Data(); err != nil {
|
|
||||||
return 0, nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if sect := obj.Section("__gopclntab"); sect != nil {
|
|
||||||
if pclntab, err = sect.Data(); err != nil {
|
|
||||||
return 0, nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return textStart, symtab, pclntab, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj, err := pe.NewFile(f); err == nil {
|
|
||||||
var imageBase uint64
|
|
||||||
switch oh := obj.OptionalHeader.(type) {
|
|
||||||
case *pe.OptionalHeader32:
|
|
||||||
imageBase = uint64(oh.ImageBase)
|
|
||||||
case *pe.OptionalHeader64:
|
|
||||||
imageBase = oh.ImageBase
|
|
||||||
default:
|
|
||||||
return 0, nil, nil, fmt.Errorf("pe file format not recognized")
|
|
||||||
}
|
|
||||||
if sect := obj.Section(".text"); sect != nil {
|
|
||||||
textStart = imageBase + uint64(sect.VirtualAddress)
|
|
||||||
}
|
|
||||||
if pclntab, err = loadPETable(obj, "pclntab", "epclntab"); err != nil {
|
|
||||||
return 0, nil, nil, err
|
|
||||||
}
|
|
||||||
if symtab, err = loadPETable(obj, "symtab", "esymtab"); err != nil {
|
|
||||||
return 0, nil, nil, err
|
|
||||||
}
|
|
||||||
return textStart, symtab, pclntab, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if obj, err := plan9obj.NewFile(f); err == nil {
|
|
||||||
textStart = obj.LoadAddress + obj.HdrSize
|
|
||||||
if pclntab, err = loadPlan9Table(obj, "pclntab", "epclntab"); err != nil {
|
|
||||||
return 0, nil, nil, err
|
|
||||||
}
|
|
||||||
if symtab, err = loadPlan9Table(obj, "symtab", "esymtab"); err != nil {
|
|
||||||
return 0, nil, nil, err
|
|
||||||
}
|
|
||||||
return textStart, symtab, pclntab, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, nil, nil, fmt.Errorf("unrecognized binary format")
|
|
||||||
}
|
|
||||||
|
|
||||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
|
||||||
for _, s := range f.Symbols {
|
|
||||||
if s.Name != name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if s.SectionNumber <= 0 {
|
|
||||||
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
|
|
||||||
}
|
|
||||||
if len(f.Sections) < int(s.SectionNumber) {
|
|
||||||
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
|
|
||||||
}
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("no %s symbol found", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
|
|
||||||
ssym, err := findPESymbol(f, sname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
esym, err := findPESymbol(f, ename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if ssym.SectionNumber != esym.SectionNumber {
|
|
||||||
return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
|
|
||||||
}
|
|
||||||
sect := f.Sections[ssym.SectionNumber-1]
|
|
||||||
data, err := sect.Data()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data[ssym.Value:esym.Value], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) {
|
|
||||||
syms, err := f.Symbols()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, s := range syms {
|
|
||||||
if s.Name != name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("no %s symbol found", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) {
|
|
||||||
ssym, err := findPlan9Symbol(f, sname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
esym, err := findPlan9Symbol(f, ename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
sect := f.Section("text")
|
|
||||||
if sect == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data, err := sect.Data()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
textStart := f.LoadAddress + f.HdrSize
|
|
||||||
return data[ssym.Value-textStart : esym.Value-textStart], nil
|
|
||||||
}
|
|
||||||
|
@ -4,24 +4,29 @@
|
|||||||
|
|
||||||
// Parsing of ELF executables (Linux, FreeBSD, and so on).
|
// Parsing of ELF executables (Linux, FreeBSD, and so on).
|
||||||
|
|
||||||
package main
|
package objfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func elfSymbols(f *os.File) []Sym {
|
type elfFile struct {
|
||||||
p, err := elf.NewFile(f)
|
elf *elf.File
|
||||||
if err != nil {
|
}
|
||||||
errorf("parsing %s: %v", f.Name(), err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
elfSyms, err := p.Symbols()
|
func openElf(r *os.File) (rawFile, error) {
|
||||||
|
f, err := elf.NewFile(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorf("parsing %s: %v", f.Name(), err)
|
return nil, err
|
||||||
return nil
|
}
|
||||||
|
return &elfFile{f}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *elfFile) symbols() ([]Sym, error) {
|
||||||
|
elfSyms, err := f.elf.Symbols()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var syms []Sym
|
var syms []Sym
|
||||||
@ -34,10 +39,10 @@ func elfSymbols(f *os.File) []Sym {
|
|||||||
sym.Code = 'B'
|
sym.Code = 'B'
|
||||||
default:
|
default:
|
||||||
i := int(s.Section)
|
i := int(s.Section)
|
||||||
if i < 0 || i >= len(p.Sections) {
|
if i < 0 || i >= len(f.elf.Sections) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
sect := p.Sections[i]
|
sect := f.elf.Sections[i]
|
||||||
switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
|
switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) {
|
||||||
case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
|
case elf.SHF_ALLOC | elf.SHF_EXECINSTR:
|
||||||
sym.Code = 'T'
|
sym.Code = 'T'
|
||||||
@ -53,5 +58,22 @@ func elfSymbols(f *os.File) []Sym {
|
|||||||
syms = append(syms, sym)
|
syms = append(syms, sym)
|
||||||
}
|
}
|
||||||
|
|
||||||
return syms
|
return syms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
|
||||||
|
if sect := f.elf.Section(".text"); sect != nil {
|
||||||
|
textStart = sect.Addr
|
||||||
|
}
|
||||||
|
if sect := f.elf.Section(".gosymtab"); sect != nil {
|
||||||
|
if symtab, err = sect.Data(); err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sect := f.elf.Section(".gopclntab"); sect != nil {
|
||||||
|
if pclntab, err = sect.Data(); err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return textStart, symtab, pclntab, nil
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
// Parsing of Go intermediate object files and archives.
|
// Parsing of Go intermediate object files and archives.
|
||||||
|
|
||||||
package main
|
package objfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/goobj"
|
"debug/goobj"
|
||||||
@ -12,6 +12,18 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type goobjFile struct {
|
||||||
|
goobj *goobj.Package
|
||||||
|
}
|
||||||
|
|
||||||
|
func openGoobj(r *os.File) (rawFile, error) {
|
||||||
|
f, err := goobj.Parse(r, `""`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &goobjFile{f}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func goobjName(id goobj.SymID) string {
|
func goobjName(id goobj.SymID) string {
|
||||||
if id.Version == 0 {
|
if id.Version == 0 {
|
||||||
return id.Name
|
return id.Name
|
||||||
@ -19,17 +31,11 @@ func goobjName(id goobj.SymID) string {
|
|||||||
return fmt.Sprintf("%s<%d>", id.Name, id.Version)
|
return fmt.Sprintf("%s<%d>", id.Name, id.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func goobjSymbols(f *os.File) []Sym {
|
func (f *goobjFile) symbols() ([]Sym, error) {
|
||||||
pkg, err := goobj.Parse(f, `""`)
|
|
||||||
if err != nil {
|
|
||||||
errorf("parsing %s: %v", f.Name(), err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
seen := make(map[goobj.SymID]bool)
|
seen := make(map[goobj.SymID]bool)
|
||||||
|
|
||||||
var syms []Sym
|
var syms []Sym
|
||||||
for _, s := range pkg.Syms {
|
for _, s := range f.goobj.Syms {
|
||||||
seen[s.SymID] = true
|
seen[s.SymID] = true
|
||||||
sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'}
|
sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'}
|
||||||
switch s.Kind {
|
switch s.Kind {
|
||||||
@ -50,7 +56,7 @@ func goobjSymbols(f *os.File) []Sym {
|
|||||||
syms = append(syms, sym)
|
syms = append(syms, sym)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range pkg.Syms {
|
for _, s := range f.goobj.Syms {
|
||||||
for _, r := range s.Reloc {
|
for _, r := range s.Reloc {
|
||||||
if !seen[r.Sym] {
|
if !seen[r.Sym] {
|
||||||
seen[r.Sym] = true
|
seen[r.Sym] = true
|
||||||
@ -64,5 +70,12 @@ func goobjSymbols(f *os.File) []Sym {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return syms
|
return syms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pcln does not make sense for Go object files, because each
|
||||||
|
// symbol has its own individual pcln table, so there is no global
|
||||||
|
// space of addresses to map.
|
||||||
|
func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
|
||||||
|
return 0, nil, nil, fmt.Errorf("pcln not available in go object file")
|
||||||
}
|
}
|
@ -4,36 +4,42 @@
|
|||||||
|
|
||||||
// Parsing of Mach-O executables (OS X).
|
// Parsing of Mach-O executables (OS X).
|
||||||
|
|
||||||
package main
|
package objfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/macho"
|
"debug/macho"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
func machoSymbols(f *os.File) []Sym {
|
type machoFile struct {
|
||||||
p, err := macho.NewFile(f)
|
macho *macho.File
|
||||||
if err != nil {
|
}
|
||||||
errorf("parsing %s: %v", f.Name(), err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.Symtab == nil {
|
func openMacho(r *os.File) (rawFile, error) {
|
||||||
errorf("%s: no symbol table", f.Name())
|
f, err := macho.NewFile(r)
|
||||||
return nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &machoFile{f}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *machoFile) symbols() ([]Sym, error) {
|
||||||
|
if f.macho.Symtab == nil {
|
||||||
|
return nil, fmt.Errorf("missing symbol table")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build sorted list of addresses of all symbols.
|
// Build sorted list of addresses of all symbols.
|
||||||
// We infer the size of a symbol by looking at where the next symbol begins.
|
// We infer the size of a symbol by looking at where the next symbol begins.
|
||||||
var addrs []uint64
|
var addrs []uint64
|
||||||
for _, s := range p.Symtab.Syms {
|
for _, s := range f.macho.Symtab.Syms {
|
||||||
addrs = append(addrs, s.Value)
|
addrs = append(addrs, s.Value)
|
||||||
}
|
}
|
||||||
sort.Sort(uint64s(addrs))
|
sort.Sort(uint64s(addrs))
|
||||||
|
|
||||||
var syms []Sym
|
var syms []Sym
|
||||||
for _, s := range p.Symtab.Syms {
|
for _, s := range f.macho.Symtab.Syms {
|
||||||
sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
|
sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'}
|
||||||
i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
|
i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
|
||||||
if i < len(addrs) {
|
if i < len(addrs) {
|
||||||
@ -41,8 +47,8 @@ func machoSymbols(f *os.File) []Sym {
|
|||||||
}
|
}
|
||||||
if s.Sect == 0 {
|
if s.Sect == 0 {
|
||||||
sym.Code = 'U'
|
sym.Code = 'U'
|
||||||
} else if int(s.Sect) <= len(p.Sections) {
|
} else if int(s.Sect) <= len(f.macho.Sections) {
|
||||||
sect := p.Sections[s.Sect-1]
|
sect := f.macho.Sections[s.Sect-1]
|
||||||
switch sect.Seg {
|
switch sect.Seg {
|
||||||
case "__TEXT":
|
case "__TEXT":
|
||||||
sym.Code = 'R'
|
sym.Code = 'R'
|
||||||
@ -59,7 +65,24 @@ func machoSymbols(f *os.File) []Sym {
|
|||||||
syms = append(syms, sym)
|
syms = append(syms, sym)
|
||||||
}
|
}
|
||||||
|
|
||||||
return syms
|
return syms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
|
||||||
|
if sect := f.macho.Section("__text"); sect != nil {
|
||||||
|
textStart = sect.Addr
|
||||||
|
}
|
||||||
|
if sect := f.macho.Section("__gosymtab"); sect != nil {
|
||||||
|
if symtab, err = sect.Data(); err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sect := f.macho.Section("__gopclntab"); sect != nil {
|
||||||
|
if pclntab, err = sect.Data(); err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return textStart, symtab, pclntab, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type uint64s []uint64
|
type uint64s []uint64
|
72
src/cmd/internal/objfile/objfile.go
Normal file
72
src/cmd/internal/objfile/objfile.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Package objfile implements portable access to OS-specific executable files.
|
||||||
|
package objfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"debug/gosym"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rawFile interface {
|
||||||
|
symbols() (syms []Sym, err error)
|
||||||
|
pcln() (textStart uint64, symtab, pclntab []byte, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A File is an opened executable file.
|
||||||
|
type File struct {
|
||||||
|
r *os.File
|
||||||
|
raw rawFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Sym is a symbol defined in an executable file.
|
||||||
|
type Sym struct {
|
||||||
|
Name string // symbol name
|
||||||
|
Addr uint64 // virtual address of symbol
|
||||||
|
Size int64 // size in bytes
|
||||||
|
Code rune // nm code (T for text, D for data, and so on)
|
||||||
|
Type string // XXX?
|
||||||
|
}
|
||||||
|
|
||||||
|
var openers = []func(*os.File) (rawFile, error){
|
||||||
|
openElf,
|
||||||
|
openGoobj,
|
||||||
|
openMacho,
|
||||||
|
openPE,
|
||||||
|
openPlan9,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open opens the named file.
|
||||||
|
// The caller must call f.Close when the file is no longer needed.
|
||||||
|
func Open(name string) (*File, error) {
|
||||||
|
r, err := os.Open(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, try := range openers {
|
||||||
|
if raw, err := try(r); err == nil {
|
||||||
|
return &File{r, raw}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.Close()
|
||||||
|
return nil, fmt.Errorf("open %s: unrecognized object file", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) Close() error {
|
||||||
|
return f.r.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) Symbols() ([]Sym, error) {
|
||||||
|
return f.raw.symbols()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) PCLineTable() (*gosym.Table, error) {
|
||||||
|
textStart, symtab, pclntab, err := f.raw.pcln()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart))
|
||||||
|
}
|
161
src/cmd/internal/objfile/pe.go
Normal file
161
src/cmd/internal/objfile/pe.go
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// Copyright 2013 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.
|
||||||
|
|
||||||
|
// Parsing of PE executables (Microsoft Windows).
|
||||||
|
|
||||||
|
package objfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"debug/pe"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type peFile struct {
|
||||||
|
pe *pe.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func openPE(r *os.File) (rawFile, error) {
|
||||||
|
f, err := pe.NewFile(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch f.OptionalHeader.(type) {
|
||||||
|
case *pe.OptionalHeader32, *pe.OptionalHeader64:
|
||||||
|
// ok
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unrecognized PE format")
|
||||||
|
}
|
||||||
|
return &peFile{f}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *peFile) symbols() ([]Sym, error) {
|
||||||
|
// Build sorted list of addresses of all symbols.
|
||||||
|
// We infer the size of a symbol by looking at where the next symbol begins.
|
||||||
|
var addrs []uint64
|
||||||
|
|
||||||
|
var imageBase uint64
|
||||||
|
switch oh := f.pe.OptionalHeader.(type) {
|
||||||
|
case *pe.OptionalHeader32:
|
||||||
|
imageBase = uint64(oh.ImageBase)
|
||||||
|
case *pe.OptionalHeader64:
|
||||||
|
imageBase = oh.ImageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
var syms []Sym
|
||||||
|
for _, s := range f.pe.Symbols {
|
||||||
|
const (
|
||||||
|
N_UNDEF = 0 // An undefined (extern) symbol
|
||||||
|
N_ABS = -1 // An absolute symbol (e_value is a constant, not an address)
|
||||||
|
N_DEBUG = -2 // A debugging symbol
|
||||||
|
)
|
||||||
|
sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'}
|
||||||
|
switch s.SectionNumber {
|
||||||
|
case N_UNDEF:
|
||||||
|
sym.Code = 'U'
|
||||||
|
case N_ABS:
|
||||||
|
sym.Code = 'C'
|
||||||
|
case N_DEBUG:
|
||||||
|
sym.Code = '?'
|
||||||
|
default:
|
||||||
|
if s.SectionNumber < 0 || len(f.pe.Sections) < int(s.SectionNumber) {
|
||||||
|
return nil, fmt.Errorf("invalid section number in symbol table")
|
||||||
|
}
|
||||||
|
sect := f.pe.Sections[s.SectionNumber-1]
|
||||||
|
const (
|
||||||
|
text = 0x20
|
||||||
|
data = 0x40
|
||||||
|
bss = 0x80
|
||||||
|
permX = 0x20000000
|
||||||
|
permR = 0x40000000
|
||||||
|
permW = 0x80000000
|
||||||
|
)
|
||||||
|
ch := sect.Characteristics
|
||||||
|
switch {
|
||||||
|
case ch&text != 0:
|
||||||
|
sym.Code = 'T'
|
||||||
|
case ch&data != 0:
|
||||||
|
if ch&permW == 0 {
|
||||||
|
sym.Code = 'R'
|
||||||
|
} else {
|
||||||
|
sym.Code = 'D'
|
||||||
|
}
|
||||||
|
case ch&bss != 0:
|
||||||
|
sym.Code = 'B'
|
||||||
|
}
|
||||||
|
sym.Addr += imageBase + uint64(sect.VirtualAddress)
|
||||||
|
}
|
||||||
|
syms = append(syms, sym)
|
||||||
|
addrs = append(addrs, sym.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(uint64s(addrs))
|
||||||
|
for i := range syms {
|
||||||
|
j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr })
|
||||||
|
if j < len(addrs) {
|
||||||
|
syms[i].Size = int64(addrs[j] - syms[i].Addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return syms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *peFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
|
||||||
|
var imageBase uint64
|
||||||
|
switch oh := f.pe.OptionalHeader.(type) {
|
||||||
|
case *pe.OptionalHeader32:
|
||||||
|
imageBase = uint64(oh.ImageBase)
|
||||||
|
case *pe.OptionalHeader64:
|
||||||
|
imageBase = oh.ImageBase
|
||||||
|
default:
|
||||||
|
return 0, nil, nil, fmt.Errorf("pe file format not recognized")
|
||||||
|
}
|
||||||
|
if sect := f.pe.Section(".text"); sect != nil {
|
||||||
|
textStart = imageBase + uint64(sect.VirtualAddress)
|
||||||
|
}
|
||||||
|
if pclntab, err = loadPETable(f.pe, "pclntab", "epclntab"); err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
if symtab, err = loadPETable(f.pe, "symtab", "esymtab"); err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
return textStart, symtab, pclntab, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
||||||
|
for _, s := range f.Symbols {
|
||||||
|
if s.Name != name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if s.SectionNumber <= 0 {
|
||||||
|
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
|
||||||
|
}
|
||||||
|
if len(f.Sections) < int(s.SectionNumber) {
|
||||||
|
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no %s symbol found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
|
||||||
|
ssym, err := findPESymbol(f, sname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
esym, err := findPESymbol(f, ename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if ssym.SectionNumber != esym.SectionNumber {
|
||||||
|
return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
|
||||||
|
}
|
||||||
|
sect := f.Sections[ssym.SectionNumber-1]
|
||||||
|
data, err := sect.Data()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data[ssym.Value:esym.Value], nil
|
||||||
|
}
|
100
src/cmd/internal/objfile/plan9obj.go
Normal file
100
src/cmd/internal/objfile/plan9obj.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
// Parsing of Plan 9 a.out executables.
|
||||||
|
|
||||||
|
package objfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"debug/plan9obj"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type plan9File struct {
|
||||||
|
plan9 *plan9obj.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func openPlan9(r *os.File) (rawFile, error) {
|
||||||
|
f, err := plan9obj.NewFile(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &plan9File{f}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *plan9File) symbols() ([]Sym, error) {
|
||||||
|
plan9Syms, err := f.plan9.Symbols()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build sorted list of addresses of all symbols.
|
||||||
|
// We infer the size of a symbol by looking at where the next symbol begins.
|
||||||
|
var addrs []uint64
|
||||||
|
for _, s := range plan9Syms {
|
||||||
|
addrs = append(addrs, s.Value)
|
||||||
|
}
|
||||||
|
sort.Sort(uint64s(addrs))
|
||||||
|
|
||||||
|
var syms []Sym
|
||||||
|
|
||||||
|
for _, s := range plan9Syms {
|
||||||
|
sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(s.Type)}
|
||||||
|
i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
|
||||||
|
if i < len(addrs) {
|
||||||
|
sym.Size = int64(addrs[i] - s.Value)
|
||||||
|
}
|
||||||
|
syms = append(syms, sym)
|
||||||
|
}
|
||||||
|
|
||||||
|
return syms, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *plan9File) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
|
||||||
|
textStart = f.plan9.LoadAddress + f.plan9.HdrSize
|
||||||
|
if pclntab, err = loadPlan9Table(f.plan9, "pclntab", "epclntab"); err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
if symtab, err = loadPlan9Table(f.plan9, "symtab", "esymtab"); err != nil {
|
||||||
|
return 0, nil, nil, err
|
||||||
|
}
|
||||||
|
return textStart, symtab, pclntab, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) {
|
||||||
|
syms, err := f.Symbols()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, s := range syms {
|
||||||
|
if s.Name != name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no %s symbol found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) {
|
||||||
|
ssym, err := findPlan9Symbol(f, sname)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
esym, err := findPlan9Symbol(f, ename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sect := f.Section("text")
|
||||||
|
if sect == nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, err := sect.Data()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
textStart := f.LoadAddress + f.HdrSize
|
||||||
|
return data[ssym.Value-textStart : esym.Value-textStart], nil
|
||||||
|
}
|
@ -6,13 +6,13 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"cmd/internal/objfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
@ -85,55 +85,22 @@ func errorf(format string, args ...interface{}) {
|
|||||||
exitCode = 1
|
exitCode = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
type Sym struct {
|
|
||||||
Addr uint64
|
|
||||||
Size int64
|
|
||||||
Code rune
|
|
||||||
Name string
|
|
||||||
Type string
|
|
||||||
}
|
|
||||||
|
|
||||||
var parsers = []struct {
|
|
||||||
prefix []byte
|
|
||||||
parse func(*os.File) []Sym
|
|
||||||
}{
|
|
||||||
{[]byte("!<arch>\n"), goobjSymbols},
|
|
||||||
{[]byte("go object "), goobjSymbols},
|
|
||||||
{[]byte("\x7FELF"), elfSymbols},
|
|
||||||
{[]byte("\xFE\xED\xFA\xCE"), machoSymbols},
|
|
||||||
{[]byte("\xFE\xED\xFA\xCF"), machoSymbols},
|
|
||||||
{[]byte("\xCE\xFA\xED\xFE"), machoSymbols},
|
|
||||||
{[]byte("\xCF\xFA\xED\xFE"), machoSymbols},
|
|
||||||
{[]byte("MZ"), peSymbols},
|
|
||||||
{[]byte("\x00\x00\x01\xEB"), plan9Symbols}, // 386
|
|
||||||
{[]byte("\x00\x00\x04\x07"), plan9Symbols}, // mips
|
|
||||||
{[]byte("\x00\x00\x06\x47"), plan9Symbols}, // arm
|
|
||||||
{[]byte("\x00\x00\x8A\x97"), plan9Symbols}, // amd64
|
|
||||||
}
|
|
||||||
|
|
||||||
func nm(file string) {
|
func nm(file string) {
|
||||||
f, err := os.Open(file)
|
f, err := objfile.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorf("%v", err)
|
errorf("%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
buf := make([]byte, 16)
|
syms, err := f.Symbols()
|
||||||
io.ReadFull(f, buf)
|
if err != nil {
|
||||||
f.Seek(0, 0)
|
errorf("reading %s: %v", file, err)
|
||||||
|
}
|
||||||
var syms []Sym
|
if len(syms) == 0 {
|
||||||
for _, p := range parsers {
|
errorf("reading %s: no symbols", file)
|
||||||
if bytes.HasPrefix(buf, p.prefix) {
|
|
||||||
syms = p.parse(f)
|
|
||||||
goto HaveSyms
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
errorf("%v: unknown file format", file)
|
|
||||||
return
|
|
||||||
|
|
||||||
HaveSyms:
|
|
||||||
switch *sortOrder {
|
switch *sortOrder {
|
||||||
case "address":
|
case "address":
|
||||||
sort.Sort(byAddr(syms))
|
sort.Sort(byAddr(syms))
|
||||||
@ -165,19 +132,19 @@ HaveSyms:
|
|||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
type byAddr []Sym
|
type byAddr []objfile.Sym
|
||||||
|
|
||||||
func (x byAddr) Len() int { return len(x) }
|
func (x byAddr) Len() int { return len(x) }
|
||||||
func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||||
func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
|
func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
|
||||||
|
|
||||||
type byName []Sym
|
type byName []objfile.Sym
|
||||||
|
|
||||||
func (x byName) Len() int { return len(x) }
|
func (x byName) Len() int { return len(x) }
|
||||||
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||||
func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name }
|
func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name }
|
||||||
|
|
||||||
type bySize []Sym
|
type bySize []objfile.Sym
|
||||||
|
|
||||||
func (x bySize) Len() int { return len(x) }
|
func (x bySize) Len() int { return len(x) }
|
||||||
func (x bySize) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
func (x bySize) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
|
||||||
|
@ -77,7 +77,7 @@ func TestNM(t *testing.T) {
|
|||||||
"elf/testdata/gcc-amd64-linux-exec",
|
"elf/testdata/gcc-amd64-linux-exec",
|
||||||
"macho/testdata/gcc-386-darwin-exec",
|
"macho/testdata/gcc-386-darwin-exec",
|
||||||
"macho/testdata/gcc-amd64-darwin-exec",
|
"macho/testdata/gcc-amd64-darwin-exec",
|
||||||
"pe/testdata/gcc-amd64-mingw-exec",
|
// "pe/testdata/gcc-amd64-mingw-exec", // no symbols!
|
||||||
"pe/testdata/gcc-386-mingw-exec",
|
"pe/testdata/gcc-386-mingw-exec",
|
||||||
"plan9obj/testdata/amd64-plan9-exec",
|
"plan9obj/testdata/amd64-plan9-exec",
|
||||||
"plan9obj/testdata/386-plan9-exec",
|
"plan9obj/testdata/386-plan9-exec",
|
||||||
@ -87,7 +87,7 @@ func TestNM(t *testing.T) {
|
|||||||
cmd := exec.Command(testnmpath, exepath)
|
cmd := exec.Command(testnmpath, exepath)
|
||||||
out, err := cmd.CombinedOutput()
|
out, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("go tool nm %v: %v\n%s", exepath, err, string(out))
|
t.Errorf("go tool nm %v: %v\n%s", exepath, err, string(out))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
// Copyright 2013 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.
|
|
||||||
|
|
||||||
// Parsing of PE executables (Microsoft Windows).
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"debug/pe"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
func peSymbols(f *os.File) []Sym {
|
|
||||||
p, err := pe.NewFile(f)
|
|
||||||
if err != nil {
|
|
||||||
errorf("parsing %s: %v", f.Name(), err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build sorted list of addresses of all symbols.
|
|
||||||
// We infer the size of a symbol by looking at where the next symbol begins.
|
|
||||||
var addrs []uint64
|
|
||||||
|
|
||||||
var imageBase uint64
|
|
||||||
switch oh := p.OptionalHeader.(type) {
|
|
||||||
case *pe.OptionalHeader32:
|
|
||||||
imageBase = uint64(oh.ImageBase)
|
|
||||||
case *pe.OptionalHeader64:
|
|
||||||
imageBase = oh.ImageBase
|
|
||||||
default:
|
|
||||||
errorf("parsing %s: file format not recognized", f.Name())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var syms []Sym
|
|
||||||
for _, s := range p.Symbols {
|
|
||||||
const (
|
|
||||||
N_UNDEF = 0 // An undefined (extern) symbol
|
|
||||||
N_ABS = -1 // An absolute symbol (e_value is a constant, not an address)
|
|
||||||
N_DEBUG = -2 // A debugging symbol
|
|
||||||
)
|
|
||||||
sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'}
|
|
||||||
switch s.SectionNumber {
|
|
||||||
case N_UNDEF:
|
|
||||||
sym.Code = 'U'
|
|
||||||
case N_ABS:
|
|
||||||
sym.Code = 'C'
|
|
||||||
case N_DEBUG:
|
|
||||||
sym.Code = '?'
|
|
||||||
default:
|
|
||||||
if s.SectionNumber < 0 {
|
|
||||||
errorf("parsing %s: invalid section number %d", f.Name(), s.SectionNumber)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if len(p.Sections) < int(s.SectionNumber) {
|
|
||||||
errorf("parsing %s: section number %d is large then max %d", f.Name(), s.SectionNumber, len(p.Sections))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
sect := p.Sections[s.SectionNumber-1]
|
|
||||||
const (
|
|
||||||
text = 0x20
|
|
||||||
data = 0x40
|
|
||||||
bss = 0x80
|
|
||||||
permX = 0x20000000
|
|
||||||
permR = 0x40000000
|
|
||||||
permW = 0x80000000
|
|
||||||
)
|
|
||||||
ch := sect.Characteristics
|
|
||||||
switch {
|
|
||||||
case ch&text != 0:
|
|
||||||
sym.Code = 'T'
|
|
||||||
case ch&data != 0:
|
|
||||||
if ch&permW == 0 {
|
|
||||||
sym.Code = 'R'
|
|
||||||
} else {
|
|
||||||
sym.Code = 'D'
|
|
||||||
}
|
|
||||||
case ch&bss != 0:
|
|
||||||
sym.Code = 'B'
|
|
||||||
}
|
|
||||||
sym.Addr += imageBase + uint64(sect.VirtualAddress)
|
|
||||||
}
|
|
||||||
syms = append(syms, sym)
|
|
||||||
addrs = append(addrs, sym.Addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(uint64s(addrs))
|
|
||||||
for i := range syms {
|
|
||||||
j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr })
|
|
||||||
if j < len(addrs) {
|
|
||||||
syms[i].Size = int64(addrs[j] - syms[i].Addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return syms
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
// Copyright 2014 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.
|
|
||||||
|
|
||||||
// Parsing of Plan 9 a.out executables.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"debug/plan9obj"
|
|
||||||
"os"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
func plan9Symbols(f *os.File) []Sym {
|
|
||||||
p, err := plan9obj.NewFile(f)
|
|
||||||
if err != nil {
|
|
||||||
errorf("parsing %s: %v", f.Name(), err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
plan9Syms, err := p.Symbols()
|
|
||||||
if err != nil {
|
|
||||||
errorf("parsing %s: %v", f.Name(), err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build sorted list of addresses of all symbols.
|
|
||||||
// We infer the size of a symbol by looking at where the next symbol begins.
|
|
||||||
var addrs []uint64
|
|
||||||
for _, s := range plan9Syms {
|
|
||||||
addrs = append(addrs, s.Value)
|
|
||||||
}
|
|
||||||
sort.Sort(uint64s(addrs))
|
|
||||||
|
|
||||||
var syms []Sym
|
|
||||||
|
|
||||||
for _, s := range plan9Syms {
|
|
||||||
sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(s.Type)}
|
|
||||||
i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value })
|
|
||||||
if i < len(addrs) {
|
|
||||||
sym.Size = int64(addrs[i] - s.Value)
|
|
||||||
}
|
|
||||||
syms = append(syms, sym)
|
|
||||||
}
|
|
||||||
|
|
||||||
return syms
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user