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