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