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