github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/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 } 57 if s.Version != 0 { 58 sym.Code += 'a' - 'A' 59 } 60 for i, r := range s.Reloc { 61 sym.Relocs = append(sym.Relocs, Reloc{Addr: uint64(s.Data.Offset) + uint64(r.Offset), Size: uint64(r.Size), Stringer: &s.Reloc[i]}) 62 } 63 syms = append(syms, sym) 64 } 65 66 for _, s := range f.goobj.Syms { 67 for _, r := range s.Reloc { 68 if !seen[r.Sym] { 69 seen[r.Sym] = true 70 sym := Sym{Name: goobjName(r.Sym), Code: 'U'} 71 if s.Version != 0 { 72 // should not happen but handle anyway 73 sym.Code = 'u' 74 } 75 syms = append(syms, sym) 76 } 77 } 78 } 79 80 return syms, nil 81 } 82 83 func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { 84 // Should never be called. We implement Liner below, callers 85 // should use that instead. 86 return 0, nil, nil, fmt.Errorf("pcln not available in go object file") 87 } 88 89 // Find returns the file name, line, and function data for the given pc. 90 // Returns "",0,nil if unknown. 91 // This function implements the Liner interface in preference to pcln() above. 92 func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { 93 // TODO: this is really inefficient. Binary search? Memoize last result? 94 var arch *sys.Arch 95 for _, a := range sys.Archs { 96 if a.Name == f.goobj.Arch { 97 arch = a 98 break 99 } 100 } 101 if arch == nil { 102 return "", 0, nil 103 } 104 for _, s := range f.goobj.Syms { 105 if pc < uint64(s.Data.Offset) || pc >= uint64(s.Data.Offset+s.Data.Size) { 106 continue 107 } 108 if s.Func == nil { 109 return "", 0, nil 110 } 111 pcfile := make([]byte, s.Func.PCFile.Size) 112 _, err := f.f.ReadAt(pcfile, s.Func.PCFile.Offset) 113 if err != nil { 114 return "", 0, nil 115 } 116 fileID := int(pcValue(pcfile, pc-uint64(s.Data.Offset), arch)) 117 fileName := s.Func.File[fileID] 118 pcline := make([]byte, s.Func.PCLine.Size) 119 _, err = f.f.ReadAt(pcline, s.Func.PCLine.Offset) 120 if err != nil { 121 return "", 0, nil 122 } 123 line := int(pcValue(pcline, pc-uint64(s.Data.Offset), arch)) 124 // Note: we provide only the name in the Func structure. 125 // We could provide more if needed. 126 return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: s.Name}} 127 } 128 return "", 0, nil 129 } 130 131 // pcValue looks up the given PC in a pc value table. target is the 132 // offset of the pc from the entry point. 133 func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 { 134 val := int32(-1) 135 var pc uint64 136 for step(&tab, &pc, &val, pc == 0, arch) { 137 if target < pc { 138 return val 139 } 140 } 141 return -1 142 } 143 144 // step advances to the next pc, value pair in the encoded table. 145 func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool { 146 uvdelta := readvarint(p) 147 if uvdelta == 0 && !first { 148 return false 149 } 150 if uvdelta&1 != 0 { 151 uvdelta = ^(uvdelta >> 1) 152 } else { 153 uvdelta >>= 1 154 } 155 vdelta := int32(uvdelta) 156 pcdelta := readvarint(p) * uint32(arch.MinLC) 157 *pc += uint64(pcdelta) 158 *val += vdelta 159 return true 160 } 161 162 // readvarint reads, removes, and returns a varint from *p. 163 func readvarint(p *[]byte) uint32 { 164 var v, shift uint32 165 s := *p 166 for shift = 0; ; shift += 7 { 167 b := s[0] 168 s = s[1:] 169 v |= (uint32(b) & 0x7F) << shift 170 if b&0x80 == 0 { 171 break 172 } 173 } 174 *p = s 175 return v 176 } 177 178 // We treat the whole object file as the text section. 179 func (f *goobjFile) text() (textStart uint64, text []byte, err error) { 180 var info os.FileInfo 181 info, err = f.f.Stat() 182 if err != nil { 183 return 184 } 185 text = make([]byte, info.Size()) 186 _, err = f.f.ReadAt(text, 0) 187 return 188 } 189 190 func (f *goobjFile) goarch() string { 191 return f.goobj.Arch 192 } 193 194 func (f *goobjFile) loadAddress() (uint64, error) { 195 return 0, fmt.Errorf("unknown load address") 196 } 197 198 func (f *goobjFile) dwarf() (*dwarf.Data, error) { 199 return nil, errors.New("no DWARF data in go object file") 200 }