Home | History | Annotate | Download | only in objfile
      1 // Copyright 2014 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // Package objfile implements portable access to OS-specific executable files.
      6 package objfile
      7 
      8 import (
      9 	"debug/dwarf"
     10 	"debug/gosym"
     11 	"fmt"
     12 	"os"
     13 	"sort"
     14 )
     15 
     16 type rawFile interface {
     17 	symbols() (syms []Sym, err error)
     18 	pcln() (textStart uint64, symtab, pclntab []byte, err error)
     19 	text() (textStart uint64, text []byte, err error)
     20 	goarch() string
     21 	loadAddress() (uint64, error)
     22 	dwarf() (*dwarf.Data, error)
     23 }
     24 
     25 // A File is an opened executable file.
     26 type File struct {
     27 	r   *os.File
     28 	raw rawFile
     29 }
     30 
     31 // A Sym is a symbol defined in an executable file.
     32 type Sym struct {
     33 	Name   string  // symbol name
     34 	Addr   uint64  // virtual address of symbol
     35 	Size   int64   // size in bytes
     36 	Code   rune    // nm code (T for text, D for data, and so on)
     37 	Type   string  // XXX?
     38 	Relocs []Reloc // in increasing Addr order
     39 }
     40 
     41 type Reloc struct {
     42 	Addr     uint64 // Address of first byte that reloc applies to.
     43 	Size     uint64 // Number of bytes
     44 	Stringer RelocStringer
     45 }
     46 
     47 type RelocStringer interface {
     48 	// insnOffset is the offset of the instruction containing the relocation
     49 	// from the start of the symbol containing the relocation.
     50 	String(insnOffset uint64) string
     51 }
     52 
     53 var openers = []func(*os.File) (rawFile, error){
     54 	openElf,
     55 	openGoobj,
     56 	openMacho,
     57 	openPE,
     58 	openPlan9,
     59 }
     60 
     61 // Open opens the named file.
     62 // The caller must call f.Close when the file is no longer needed.
     63 func Open(name string) (*File, error) {
     64 	r, err := os.Open(name)
     65 	if err != nil {
     66 		return nil, err
     67 	}
     68 	for _, try := range openers {
     69 		if raw, err := try(r); err == nil {
     70 			return &File{r, raw}, nil
     71 		}
     72 	}
     73 	r.Close()
     74 	return nil, fmt.Errorf("open %s: unrecognized object file", name)
     75 }
     76 
     77 func (f *File) Close() error {
     78 	return f.r.Close()
     79 }
     80 
     81 func (f *File) Symbols() ([]Sym, error) {
     82 	syms, err := f.raw.symbols()
     83 	if err != nil {
     84 		return nil, err
     85 	}
     86 	sort.Sort(byAddr(syms))
     87 	return syms, nil
     88 }
     89 
     90 type byAddr []Sym
     91 
     92 func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
     93 func (x byAddr) Len() int           { return len(x) }
     94 func (x byAddr) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
     95 
     96 func (f *File) PCLineTable() (Liner, error) {
     97 	// If the raw file implements Liner directly, use that.
     98 	// Currently, only Go intermediate objects and archives (goobj) use this path.
     99 	if pcln, ok := f.raw.(Liner); ok {
    100 		return pcln, nil
    101 	}
    102 	// Otherwise, read the pcln tables and build a Liner out of that.
    103 	textStart, symtab, pclntab, err := f.raw.pcln()
    104 	if err != nil {
    105 		return nil, err
    106 	}
    107 	return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart))
    108 }
    109 
    110 func (f *File) Text() (uint64, []byte, error) {
    111 	return f.raw.text()
    112 }
    113 
    114 func (f *File) GOARCH() string {
    115 	return f.raw.goarch()
    116 }
    117 
    118 // LoadAddress returns the expected load address of the file.
    119 // This differs from the actual load address for a position-independent
    120 // executable.
    121 func (f *File) LoadAddress() (uint64, error) {
    122 	return f.raw.loadAddress()
    123 }
    124 
    125 // DWARF returns DWARF debug data for the file, if any.
    126 // This is for cmd/pprof to locate cgo functions.
    127 func (f *File) DWARF() (*dwarf.Data, error) {
    128 	return f.raw.dwarf()
    129 }
    130