github.com/v2fly/tools@v0.100.0/go/internal/gccgoimporter/importer.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 // Except for this comment and the import path, this file is a verbatim copy of the file 6 // with the same name in $GOROOT/src/go/internal/gccgoimporter. 7 8 // Package gccgoimporter implements Import for gccgo-generated object files. 9 package gccgoimporter // import "github.com/v2fly/tools/go/internal/gccgoimporter" 10 11 import ( 12 "debug/elf" 13 "fmt" 14 "go/types" 15 "io" 16 "os" 17 "path/filepath" 18 "strings" 19 ) 20 21 // A PackageInit describes an imported package that needs initialization. 22 type PackageInit struct { 23 Name string // short package name 24 InitFunc string // name of init function 25 Priority int // priority of init function, see InitData.Priority 26 } 27 28 // The gccgo-specific init data for a package. 29 type InitData struct { 30 // Initialization priority of this package relative to other packages. 31 // This is based on the maximum depth of the package's dependency graph; 32 // it is guaranteed to be greater than that of its dependencies. 33 Priority int 34 35 // The list of packages which this package depends on to be initialized, 36 // including itself if needed. This is the subset of the transitive closure of 37 // the package's dependencies that need initialization. 38 Inits []PackageInit 39 } 40 41 // Locate the file from which to read export data. 42 // This is intended to replicate the logic in gofrontend. 43 func findExportFile(searchpaths []string, pkgpath string) (string, error) { 44 for _, spath := range searchpaths { 45 pkgfullpath := filepath.Join(spath, pkgpath) 46 pkgdir, name := filepath.Split(pkgfullpath) 47 48 for _, filepath := range [...]string{ 49 pkgfullpath, 50 pkgfullpath + ".gox", 51 pkgdir + "lib" + name + ".so", 52 pkgdir + "lib" + name + ".a", 53 pkgfullpath + ".o", 54 } { 55 fi, err := os.Stat(filepath) 56 if err == nil && !fi.IsDir() { 57 return filepath, nil 58 } 59 } 60 } 61 62 return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":")) 63 } 64 65 const ( 66 gccgov1Magic = "v1;\n" 67 gccgov2Magic = "v2;\n" 68 gccgov3Magic = "v3;\n" 69 goimporterMagic = "\n$$ " 70 archiveMagic = "!<ar" 71 ) 72 73 // Opens the export data file at the given path. If this is an ELF file, 74 // searches for and opens the .go_export section. If this is an archive, 75 // reads the export data from the first member, which is assumed to be an ELF file. 76 // This is intended to replicate the logic in gofrontend. 77 func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) { 78 f, err := os.Open(fpath) 79 if err != nil { 80 return 81 } 82 closer = f 83 defer func() { 84 if err != nil && closer != nil { 85 f.Close() 86 } 87 }() 88 89 var magic [4]byte 90 _, err = f.ReadAt(magic[:], 0) 91 if err != nil { 92 return 93 } 94 95 var elfreader io.ReaderAt 96 switch string(magic[:]) { 97 case gccgov1Magic, gccgov2Magic, gccgov3Magic, goimporterMagic: 98 // Raw export data. 99 reader = f 100 return 101 102 case archiveMagic: 103 reader, err = arExportData(f) 104 return 105 106 default: 107 elfreader = f 108 } 109 110 ef, err := elf.NewFile(elfreader) 111 if err != nil { 112 return 113 } 114 115 sec := ef.Section(".go_export") 116 if sec == nil { 117 err = fmt.Errorf("%s: .go_export section not found", fpath) 118 return 119 } 120 121 reader = sec.Open() 122 return 123 } 124 125 // An Importer resolves import paths to Packages. The imports map records 126 // packages already known, indexed by package path. 127 // An importer must determine the canonical package path and check imports 128 // to see if it is already present in the map. If so, the Importer can return 129 // the map entry. Otherwise, the importer must load the package data for the 130 // given path into a new *Package, record it in imports map, and return the 131 // package. 132 type Importer func(imports map[string]*types.Package, path, srcDir string, lookup func(string) (io.ReadCloser, error)) (*types.Package, error) 133 134 func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer { 135 return func(imports map[string]*types.Package, pkgpath, srcDir string, lookup func(string) (io.ReadCloser, error)) (pkg *types.Package, err error) { 136 // TODO(gri): Use srcDir. 137 // Or not. It's possible that srcDir will fade in importance as 138 // the go command and other tools provide a translation table 139 // for relative imports (like ./foo or vendored imports). 140 if pkgpath == "unsafe" { 141 return types.Unsafe, nil 142 } 143 144 var reader io.ReadSeeker 145 var fpath string 146 var rc io.ReadCloser 147 if lookup != nil { 148 if p := imports[pkgpath]; p != nil && p.Complete() { 149 return p, nil 150 } 151 rc, err = lookup(pkgpath) 152 if err != nil { 153 return nil, err 154 } 155 } 156 if rc != nil { 157 defer rc.Close() 158 rs, ok := rc.(io.ReadSeeker) 159 if !ok { 160 return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc) 161 } 162 reader = rs 163 fpath = "<lookup " + pkgpath + ">" 164 // Take name from Name method (like on os.File) if present. 165 if n, ok := rc.(interface{ Name() string }); ok { 166 fpath = n.Name() 167 } 168 } else { 169 fpath, err = findExportFile(searchpaths, pkgpath) 170 if err != nil { 171 return nil, err 172 } 173 174 r, closer, err := openExportFile(fpath) 175 if err != nil { 176 return nil, err 177 } 178 if closer != nil { 179 defer closer.Close() 180 } 181 reader = r 182 } 183 184 var magics string 185 magics, err = readMagic(reader) 186 if err != nil { 187 return 188 } 189 190 if magics == archiveMagic { 191 reader, err = arExportData(reader) 192 if err != nil { 193 return 194 } 195 magics, err = readMagic(reader) 196 if err != nil { 197 return 198 } 199 } 200 201 switch magics { 202 case gccgov1Magic, gccgov2Magic, gccgov3Magic: 203 var p parser 204 p.init(fpath, reader, imports) 205 pkg = p.parsePackage() 206 if initmap != nil { 207 initmap[pkg] = p.initdata 208 } 209 210 // Excluded for now: Standard gccgo doesn't support this import format currently. 211 // case goimporterMagic: 212 // var data []byte 213 // data, err = ioutil.ReadAll(reader) 214 // if err != nil { 215 // return 216 // } 217 // var n int 218 // n, pkg, err = importer.ImportData(imports, data) 219 // if err != nil { 220 // return 221 // } 222 223 // if initmap != nil { 224 // suffixreader := bytes.NewReader(data[n:]) 225 // var p parser 226 // p.init(fpath, suffixreader, nil) 227 // p.parseInitData() 228 // initmap[pkg] = p.initdata 229 // } 230 231 default: 232 err = fmt.Errorf("unrecognized magic string: %q", magics) 233 } 234 235 return 236 } 237 } 238 239 // readMagic reads the four bytes at the start of a ReadSeeker and 240 // returns them as a string. 241 func readMagic(reader io.ReadSeeker) (string, error) { 242 var magic [4]byte 243 if _, err := reader.Read(magic[:]); err != nil { 244 return "", err 245 } 246 if _, err := reader.Seek(0, io.SeekStart); err != nil { 247 return "", err 248 } 249 return string(magic[:]), nil 250 }