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