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 	"io"
     13 	"os"
     14 	"sort"
     15 )
     16 
     17 type rawFile interface {
     18 	symbols() (syms []Sym, err error)
     19 	pcln() (textStart uint64, symtab, pclntab []byte, err error)
     20 	text() (textStart uint64, text []byte, err error)
     21 	goarch() string
     22 	loadAddress() (uint64, error)
     23 	dwarf() (*dwarf.Data, error)
     24 }
     25 
     26 // A File is an opened executable file.
     27 type File struct {
     28 	r       *os.File
     29 	entries []*Entry
     30 }
     31 
     32 type Entry struct {
     33 	name string
     34 	raw  rawFile
     35 }
     36 
     37 // A Sym is a symbol defined in an executable file.
     38 type Sym struct {
     39 	Name   string  // symbol name
     40 	Addr   uint64  // virtual address of symbol
     41 	Size   int64   // size in bytes
     42 	Code   rune    // nm code (T for text, D for data, and so on)
     43 	Type   string  // XXX?
     44 	Relocs []Reloc // in increasing Addr order
     45 }
     46 
     47 type Reloc struct {
     48 	Addr     uint64 // Address of first byte that reloc applies to.
     49 	Size     uint64 // Number of bytes
     50 	Stringer RelocStringer
     51 }
     52 
     53 type RelocStringer interface {
     54 	// insnOffset is the offset of the instruction containing the relocation
     55 	// from the start of the symbol containing the relocation.
     56 	String(insnOffset uint64) string
     57 }
     58 
     59 var openers = []func(io.ReaderAt) (rawFile, error){
     60 	openElf,
     61 	openMacho,
     62 	openPE,
     63 	openPlan9,
     64 }
     65 
     66 // Open opens the named file.
     67 // The caller must call f.Close when the file is no longer needed.
     68 func Open(name string) (*File, error) {
     69 	r, err := os.Open(name)
     70 	if err != nil {
     71 		return nil, err
     72 	}
     73 	if f, err := openGoFile(r); err == nil {
     74 		return f, nil
     75 	}
     76 	for _, try := range openers {
     77 		if raw, err := try(r); err == nil {
     78 			return &File{r, []*Entry{&Entry{raw: raw}}}, nil
     79 		}
     80 	}
     81 	r.Close()
     82 	return nil, fmt.Errorf("open %s: unrecognized object file", name)
     83 }
     84 
     85 func (f *File) Close() error {
     86 	return f.r.Close()
     87 }
     88 
     89 func (f *File) Entries() []*Entry {
     90 	return f.entries
     91 }
     92 
     93 func (f *File) Symbols() ([]Sym, error) {
     94 	return f.entries[0].Symbols()
     95 }
     96 
     97 func (f *File) PCLineTable() (Liner, error) {
     98 	return f.entries[0].PCLineTable()
     99 }
    100 
    101 func (f *File) Text() (uint64, []byte, error) {
    102 	return f.entries[0].Text()
    103 }
    104 
    105 func (f *File) GOARCH() string {
    106 	return f.entries[0].GOARCH()
    107 }
    108 
    109 func (f *File) LoadAddress() (uint64, error) {
    110 	return f.entries[0].LoadAddress()
    111 }
    112 
    113 func (f *File) DWARF() (*dwarf.Data, error) {
    114 	return f.entries[0].DWARF()
    115 }
    116 
    117 func (f *File) Disasm() (*Disasm, error) {
    118 	return f.entries[0].Disasm()
    119 }
    120 
    121 func (e *Entry) Name() string {
    122 	return e.name
    123 }
    124 
    125 func (e *Entry) Symbols() ([]Sym, error) {
    126 	syms, err := e.raw.symbols()
    127 	if err != nil {
    128 		return nil, err
    129 	}
    130 	sort.Sort(byAddr(syms))
    131 	return syms, nil
    132 }
    133 
    134 type byAddr []Sym
    135 
    136 func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr }
    137 func (x byAddr) Len() int           { return len(x) }
    138 func (x byAddr) Swap(i, j int)      { x[i], x[j] = x[j], x[i] }
    139 
    140 func (e *Entry) PCLineTable() (Liner, error) {
    141 	// If the raw file implements Liner directly, use that.
    142 	// Currently, only Go intermediate objects and archives (goobj) use this path.
    143 	if pcln, ok := e.raw.(Liner); ok {
    144 		return pcln, nil
    145 	}
    146 	// Otherwise, read the pcln tables and build a Liner out of that.
    147 	textStart, symtab, pclntab, err := e.raw.pcln()
    148 	if err != nil {
    149 		return nil, err
    150 	}
    151 	return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart))
    152 }
    153 
    154 func (e *Entry) Text() (uint64, []byte, error) {
    155 	return e.raw.text()
    156 }
    157 
    158 func (e *Entry) GOARCH() string {
    159 	return e.raw.goarch()
    160 }
    161 
    162 // LoadAddress returns the expected load address of the file.
    163 // This differs from the actual load address for a position-independent
    164 // executable.
    165 func (e *Entry) LoadAddress() (uint64, error) {
    166 	return e.raw.loadAddress()
    167 }
    168 
    169 // DWARF returns DWARF debug data for the file, if any.
    170 // This is for cmd/pprof to locate cgo functions.
    171 func (e *Entry) DWARF() (*dwarf.Data, error) {
    172 	return e.raw.dwarf()
    173 }
    174