github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/cmd/internal/objfile/goobj.go (about) 1 // Copyright 2013 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 // Parsing of Go intermediate object files and archives. 6 7 package objfile 8 9 import ( 10 "cmd/internal/goobj" 11 "cmd/internal/objabi" 12 "cmd/internal/sys" 13 "debug/dwarf" 14 "debug/gosym" 15 "errors" 16 "fmt" 17 "os" 18 ) 19 20 type goobjFile struct { 21 goobj *goobj.Package 22 f *os.File // the underlying .o or .a file 23 } 24 25 func openGoobj(r *os.File) (rawFile, error) { 26 f, err := goobj.Parse(r, `""`) 27 if err != nil { 28 return nil, err 29 } 30 return &goobjFile{goobj: f, f: r}, nil 31 } 32 33 func goobjName(id goobj.SymID) string { 34 if id.Version == 0 { 35 return id.Name 36 } 37 return fmt.Sprintf("%s<%d>", id.Name, id.Version) 38 } 39 40 func (f *goobjFile) symbols() ([]Sym, error) { 41 seen := make(map[goobj.SymID]bool) 42 43 var syms []Sym 44 for _, s := range f.goobj.Syms { 45 seen[s.SymID] = true 46 sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'} 47 switch s.Kind { 48 case objabi.STEXT: 49 sym.Code = 'T' 50 case objabi.SRODATA: 51 sym.Code = 'R' 52 case objabi.SDATA: 53 sym.Code = 'D' 54 case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS: 55 sym.Code = 'B' 56 case objabi.SXREF, objabi.SCONST: 57 sym.Code = 'X' // should not see 58 } 59 if s.Version != 0 { 60 sym.Code += 'a' - 'A' 61 } 62 for i, r := range s.Reloc { 63 sym.Relocs = append(sym.Relocs, Reloc{Addr: uint64(s.Data.Offset) + uint64(r.Offset), Size: uint64(r.Size), Stringer: &s.Reloc[i]}) 64 } 65 syms = append(syms, sym) 66 } 67 68 for _, s := range f.goobj.Syms { 69 for _, r := range s.Reloc { 70 if !seen[r.Sym] { 71 seen[r.Sym] = true 72 sym := Sym{Name: goobjName(r.Sym), Code: 'U'} 73 if s.Version != 0 { 74 // should not happen but handle anyway 75 sym.Code = 'u' 76 } 77 syms = append(syms, sym) 78 } 79 } 80 } 81 82 return syms, nil 83 } 84 85 func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { 86 // Should never be called. We implement Liner below, callers 87 // should use that instead. 88 return 0, nil, nil, fmt.Errorf("pcln not available in go object file") 89 } 90 91 // Find returns the file name, line, and function data for the given pc. 92 // Returns "",0,nil if unknown. 93 // This function implements the Liner interface in preference to pcln() above. 94 func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { 95 // TODO: this is really inefficient. Binary search? Memoize last result? 96 var arch *sys.Arch 97 for _, a := range sys.Archs { 98 if a.Name == f.goobj.Arch { 99 arch = a 100 break 101 } 102 } 103 if arch == nil { 104 return "", 0, nil 105 } 106 for _, s := range f.goobj.Syms { 107 if pc < uint64(s.Data.Offset) || pc >= uint64(s.Data.Offset+s.Data.Size) { 108 continue 109 } 110 if s.Func == nil { 111 return "", 0, nil 112 } 113 pcfile := make([]byte, s.Func.PCFile.Size) 114 _, err := f.f.ReadAt(pcfile, s.Func.PCFile.Offset) 115 if err != nil { 116 return "", 0, nil 117 } 118 fileID := int(pcValue(pcfile, pc-uint64(s.Data.Offset), arch)) 119 fileName := s.Func.File[fileID] 120 pcline := make([]byte, s.Func.PCLine.Size) 121 _, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset) 122 if err != nil { 123 return "", 0, nil 124 } 125 line := int(pcValue(pcline, pc-uint64(s.Data.Offset), arch)) 126 // Note: we provide only the name in the Func structure. 127 // We could provide more if needed. 128 return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: s.Name}} 129 } 130 return "", 0, nil 131 } 132 133 // pcValue looks up the given PC in a pc value table. target is the 134 // offset of the pc from the entry point. 135 func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 { 136 val := int32(-1) 137 var pc uint64 138 for step(&tab, &pc, &val, pc == 0, arch) { 139 if target < pc { 140 return val 141 } 142 } 143 return -1 144 } 145 146 // step advances to the next pc, value pair in the encoded table. 147 func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool { 148 uvdelta := readvarint(p) 149 if uvdelta == 0 && !first { 150 return false 151 } 152 if uvdelta&1 != 0 { 153 uvdelta = ^(uvdelta >> 1) 154 } else { 155 uvdelta >>= 1 156 } 157 vdelta := int32(uvdelta) 158 pcdelta := readvarint(p) * uint32(arch.MinLC) 159 *pc += uint64(pcdelta) 160 *val += vdelta 161 return true 162 } 163 164 // readvarint reads, removes, and returns a varint from *p. 165 func readvarint(p *[]byte) uint32 { 166 var v, shift uint32 167 s := *p 168 for shift = 0; ; shift += 7 { 169 b := s[0] 170 s = s[1:] 171 v |= (uint32(b) & 0x7F) << shift 172 if b&0x80 == 0 { 173 break 174 } 175 } 176 *p = s 177 return v 178 } 179 180 // We treat the whole object file as the text section. 181 func (f *goobjFile) text() (textStart uint64, text []byte, err error) { 182 var info os.FileInfo 183 info, err = f.f.Stat() 184 if err != nil { 185 return 186 } 187 text = make([]byte, info.Size()) 188 _, err = f.f.ReadAt(text, 0) 189 return 190 } 191 192 func (f *goobjFile) goarch() string { 193 return f.goobj.Arch 194 } 195 196 func (f *goobjFile) loadAddress() (uint64, error) { 197 return 0, fmt.Errorf("unknown load address") 198 } 199 200 func (f *goobjFile) dwarf() (*dwarf.Data, error) { 201 return nil, errors.New("no DWARF data in go object file") 202 }