// 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/dwarf" "debug/gosym" "fmt" "os" "sort" ) type rawFile interface { symbols() (syms []Sym, err error) pcln() (textStart uint64, symtab, pclntab []byte, err error) text() (textStart uint64, text []byte, err error) goarch() string loadAddress() (uint64, error) dwarf() (*dwarf.Data, 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? Relocs []Reloc // in increasing Addr order } type Reloc struct { Addr uint64 // Address of first byte that reloc applies to. Size uint64 // Number of bytes Stringer RelocStringer } type RelocStringer interface { // insnOffset is the offset of the instruction containing the relocation // from the start of the symbol containing the relocation. String(insnOffset uint64) string } var openers = []func(*os.File) (rawFile, error){ openElf, // openGoobj, // TODO(jbd): Bring it back when 1.8 is popular. 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) { syms, err := f.raw.symbols() if err != nil { return nil, err } sort.Sort(byAddr(syms)) return syms, nil } type byAddr []Sym func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr } func (x byAddr) Len() int { return len(x) } func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (f *File) PCLineTable() (Liner, error) { // If the raw file implements Liner directly, use that. // Currently, only Go intermediate objects and archives (goobj) use this path. if pcln, ok := f.raw.(Liner); ok { return pcln, nil } // Otherwise, read the pcln tables and build a Liner out of that. textStart, symtab, pclntab, err := f.raw.pcln() if err != nil { return nil, err } return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart)) } func (f *File) Text() (uint64, []byte, error) { return f.raw.text() } func (f *File) GOARCH() string { return f.raw.goarch() } // LoadAddress returns the expected load address of the file. // This differs from the actual load address for a position-independent // executable. func (f *File) LoadAddress() (uint64, error) { return f.raw.loadAddress() } // DWARF returns DWARF debug data for the file, if any. // This is for cmd/pprof to locate cgo functions. func (f *File) DWARF() (*dwarf.Data, error) { return f.raw.dwarf() }