github.com/bir3/gocompiler@v0.9.2202/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 "github.com/bir3/gocompiler/src/cmd/internal/archive" 11 "github.com/bir3/gocompiler/src/cmd/internal/goobj" 12 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 13 "github.com/bir3/gocompiler/src/cmd/internal/sys" 14 "debug/dwarf" 15 "debug/gosym" 16 "errors" 17 "fmt" 18 "io" 19 "os" 20 ) 21 22 type goobjFile struct { 23 goobj *archive.GoObj 24 r *goobj.Reader 25 f *os.File 26 arch *sys.Arch 27 } 28 29 func openGoFile(f *os.File) (*File, error) { 30 a, err := archive.Parse(f, false) 31 if err != nil { 32 return nil, err 33 } 34 entries := make([]*Entry, 0, len(a.Entries)) 35 L: 36 for _, e := range a.Entries { 37 switch e.Type { 38 case archive.EntryPkgDef, archive.EntrySentinelNonObj: 39 continue 40 case archive.EntryGoObj: 41 o := e.Obj 42 b := make([]byte, o.Size) 43 _, err := f.ReadAt(b, o.Offset) 44 if err != nil { 45 return nil, err 46 } 47 r := goobj.NewReaderFromBytes(b, false) 48 var arch *sys.Arch 49 for _, a := range sys.Archs { 50 if a.Name == e.Obj.Arch { 51 arch = a 52 break 53 } 54 } 55 entries = append(entries, &Entry{ 56 name: e.Name, 57 raw: &goobjFile{e.Obj, r, f, arch}, 58 }) 59 continue 60 case archive.EntryNativeObj: 61 nr := io.NewSectionReader(f, e.Offset, e.Size) 62 for _, try := range openers { 63 if raw, err := try(nr); err == nil { 64 entries = append(entries, &Entry{ 65 name: e.Name, 66 raw: raw, 67 }) 68 continue L 69 } 70 } 71 } 72 return nil, fmt.Errorf("open %s: unrecognized archive member %s", f.Name(), e.Name) 73 } 74 return &File{f, entries}, nil 75 } 76 77 func goobjName(name string, ver int) string { 78 if ver == 0 { 79 return name 80 } 81 return fmt.Sprintf("%s<%d>", name, ver) 82 } 83 84 type goobjReloc struct { 85 Off int32 86 Size uint8 87 Type objabi.RelocType 88 Add int64 89 Sym string 90 } 91 92 func (r goobjReloc) String(insnOffset uint64) string { 93 delta := int64(r.Off) - int64(insnOffset) 94 s := fmt.Sprintf("[%d:%d]%s", delta, delta+int64(r.Size), r.Type) 95 if r.Sym != "" { 96 if r.Add != 0 { 97 return fmt.Sprintf("%s:%s+%d", s, r.Sym, r.Add) 98 } 99 return fmt.Sprintf("%s:%s", s, r.Sym) 100 } 101 if r.Add != 0 { 102 return fmt.Sprintf("%s:%d", s, r.Add) 103 } 104 return s 105 } 106 107 func (f *goobjFile) symbols() ([]Sym, error) { 108 r := f.r 109 var syms []Sym 110 111 // Name of referenced indexed symbols. 112 nrefName := r.NRefName() 113 refNames := make(map[goobj.SymRef]string, nrefName) 114 for i := 0; i < nrefName; i++ { 115 rn := r.RefName(i) 116 refNames[rn.Sym()] = rn.Name(r) 117 } 118 119 abiToVer := func(abi uint16) int { 120 var ver int 121 if abi == goobj.SymABIstatic { 122 // Static symbol 123 ver = 1 124 } 125 return ver 126 } 127 128 resolveSymRef := func(s goobj.SymRef) string { 129 var i uint32 130 switch p := s.PkgIdx; p { 131 case goobj.PkgIdxInvalid: 132 if s.SymIdx != 0 { 133 panic("bad sym ref") 134 } 135 return "" 136 case goobj.PkgIdxHashed64: 137 i = s.SymIdx + uint32(r.NSym()) 138 case goobj.PkgIdxHashed: 139 i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()) 140 case goobj.PkgIdxNone: 141 i = s.SymIdx + uint32(r.NSym()+r.NHashed64def()+r.NHasheddef()) 142 case goobj.PkgIdxBuiltin: 143 name, abi := goobj.BuiltinName(int(s.SymIdx)) 144 return goobjName(name, abi) 145 case goobj.PkgIdxSelf: 146 i = s.SymIdx 147 default: 148 return refNames[s] 149 } 150 sym := r.Sym(i) 151 return goobjName(sym.Name(r), abiToVer(sym.ABI())) 152 } 153 154 // Defined symbols 155 ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) 156 for i := uint32(0); i < ndef; i++ { 157 osym := r.Sym(i) 158 if osym.Name(r) == "" { 159 continue // not a real symbol 160 } 161 name := osym.Name(r) 162 ver := osym.ABI() 163 name = goobjName(name, abiToVer(ver)) 164 typ := objabi.SymKind(osym.Type()) 165 var code rune = '?' 166 switch typ { 167 case objabi.STEXT: 168 code = 'T' 169 case objabi.SRODATA: 170 code = 'R' 171 case objabi.SNOPTRDATA, objabi.SDATA: 172 code = 'D' 173 case objabi.SBSS, objabi.SNOPTRBSS, objabi.STLSBSS: 174 code = 'B' 175 } 176 if ver >= goobj.SymABIstatic { 177 code += 'a' - 'A' 178 } 179 180 sym := Sym{ 181 Name: name, 182 Addr: uint64(r.DataOff(i)), 183 Size: int64(osym.Siz()), 184 Code: code, 185 } 186 187 relocs := r.Relocs(i) 188 sym.Relocs = make([]Reloc, len(relocs)) 189 for j := range relocs { 190 rel := &relocs[j] 191 sym.Relocs[j] = Reloc{ 192 Addr: uint64(r.DataOff(i)) + uint64(rel.Off()), 193 Size: uint64(rel.Siz()), 194 Stringer: goobjReloc{ 195 Off: rel.Off(), 196 Size: rel.Siz(), 197 Type: objabi.RelocType(rel.Type()), 198 Add: rel.Add(), 199 Sym: resolveSymRef(rel.Sym()), 200 }, 201 } 202 } 203 204 syms = append(syms, sym) 205 } 206 207 // Referenced symbols 208 n := ndef + uint32(r.NNonpkgref()) 209 for i := ndef; i < n; i++ { 210 osym := r.Sym(i) 211 sym := Sym{Name: osym.Name(r), Code: 'U'} 212 syms = append(syms, sym) 213 } 214 for i := 0; i < nrefName; i++ { 215 rn := r.RefName(i) 216 sym := Sym{Name: rn.Name(r), Code: 'U'} 217 syms = append(syms, sym) 218 } 219 220 return syms, nil 221 } 222 223 func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { 224 // Should never be called. We implement Liner below, callers 225 // should use that instead. 226 return 0, nil, nil, fmt.Errorf("pcln not available in go object file") 227 } 228 229 // Find returns the file name, line, and function data for the given pc. 230 // Returns "",0,nil if unknown. 231 // This function implements the Liner interface in preference to pcln() above. 232 func (f *goobjFile) PCToLine(pc uint64) (string, int, *gosym.Func) { 233 r := f.r 234 if f.arch == nil { 235 return "", 0, nil 236 } 237 getSymData := func(s goobj.SymRef) []byte { 238 if s.PkgIdx != goobj.PkgIdxHashed { 239 // We don't need the data for non-hashed symbols, yet. 240 panic("not supported") 241 } 242 i := uint32(s.SymIdx + uint32(r.NSym()+r.NHashed64def())) 243 return r.BytesAt(r.DataOff(i), r.DataSize(i)) 244 } 245 246 ndef := uint32(r.NSym() + r.NHashed64def() + r.NHasheddef() + r.NNonpkgdef()) 247 for i := uint32(0); i < ndef; i++ { 248 osym := r.Sym(i) 249 addr := uint64(r.DataOff(i)) 250 if pc < addr || pc >= addr+uint64(osym.Siz()) { 251 continue 252 } 253 var pcfileSym, pclineSym goobj.SymRef 254 for _, a := range r.Auxs(i) { 255 switch a.Type() { 256 case goobj.AuxPcfile: 257 pcfileSym = a.Sym() 258 case goobj.AuxPcline: 259 pclineSym = a.Sym() 260 } 261 } 262 if pcfileSym.IsZero() || pclineSym.IsZero() { 263 continue 264 } 265 pcline := getSymData(pclineSym) 266 line := int(pcValue(pcline, pc-addr, f.arch)) 267 pcfile := getSymData(pcfileSym) 268 fileID := pcValue(pcfile, pc-addr, f.arch) 269 fileName := r.File(int(fileID)) 270 // Note: we provide only the name in the Func structure. 271 // We could provide more if needed. 272 return fileName, line, &gosym.Func{Sym: &gosym.Sym{Name: osym.Name(r)}} 273 } 274 return "", 0, nil 275 } 276 277 // pcValue looks up the given PC in a pc value table. target is the 278 // offset of the pc from the entry point. 279 func pcValue(tab []byte, target uint64, arch *sys.Arch) int32 { 280 val := int32(-1) 281 var pc uint64 282 for step(&tab, &pc, &val, pc == 0, arch) { 283 if target < pc { 284 return val 285 } 286 } 287 return -1 288 } 289 290 // step advances to the next pc, value pair in the encoded table. 291 func step(p *[]byte, pc *uint64, val *int32, first bool, arch *sys.Arch) bool { 292 uvdelta := readvarint(p) 293 if uvdelta == 0 && !first { 294 return false 295 } 296 if uvdelta&1 != 0 { 297 uvdelta = ^(uvdelta >> 1) 298 } else { 299 uvdelta >>= 1 300 } 301 vdelta := int32(uvdelta) 302 pcdelta := readvarint(p) * uint32(arch.MinLC) 303 *pc += uint64(pcdelta) 304 *val += vdelta 305 return true 306 } 307 308 // readvarint reads, removes, and returns a varint from *p. 309 func readvarint(p *[]byte) uint32 { 310 var v, shift uint32 311 s := *p 312 for shift = 0; ; shift += 7 { 313 b := s[0] 314 s = s[1:] 315 v |= (uint32(b) & 0x7F) << shift 316 if b&0x80 == 0 { 317 break 318 } 319 } 320 *p = s 321 return v 322 } 323 324 // We treat the whole object file as the text section. 325 func (f *goobjFile) text() (textStart uint64, text []byte, err error) { 326 text = make([]byte, f.goobj.Size) 327 _, err = f.f.ReadAt(text, int64(f.goobj.Offset)) 328 return 329 } 330 331 func (f *goobjFile) goarch() string { 332 return f.goobj.Arch 333 } 334 335 func (f *goobjFile) loadAddress() (uint64, error) { 336 return 0, fmt.Errorf("unknown load address") 337 } 338 339 func (f *goobjFile) dwarf() (*dwarf.Data, error) { 340 return nil, errors.New("no DWARF data in go object file") 341 }