github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/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 // Package gccgoimporter implements Import for gccgo-generated object files. 6 package gccgoimporter // import "golang.org/x/tools/go/gccgoimporter" 7 8 import ( 9 "bytes" 10 "debug/elf" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "strings" 18 19 "golang.org/x/tools/go/importer" 20 "golang.org/x/tools/go/types" 21 ) 22 23 // A PackageInit describes an imported package that needs initialization. 24 type PackageInit struct { 25 Name string // short package name 26 InitFunc string // name of init function 27 Priority int // priority of init function, see InitData.Priority 28 } 29 30 // The gccgo-specific init data for a package. 31 type InitData struct { 32 // Initialization priority of this package relative to other packages. 33 // This is based on the maximum depth of the package's dependency graph; 34 // it is guaranteed to be greater than that of its dependencies. 35 Priority int 36 37 // The list of packages which this package depends on to be initialized, 38 // including itself if needed. This is the subset of the transitive closure of 39 // the package's dependencies that need initialization. 40 Inits []PackageInit 41 } 42 43 // Locate the file from which to read export data. 44 // This is intended to replicate the logic in gofrontend. 45 func findExportFile(searchpaths []string, pkgpath string) (string, error) { 46 for _, spath := range searchpaths { 47 pkgfullpath := filepath.Join(spath, pkgpath) 48 pkgdir, name := filepath.Split(pkgfullpath) 49 50 for _, filepath := range [...]string{ 51 pkgfullpath, 52 pkgfullpath + ".gox", 53 pkgdir + "lib" + name + ".so", 54 pkgdir + "lib" + name + ".a", 55 pkgfullpath + ".o", 56 } { 57 fi, err := os.Stat(filepath) 58 if err == nil && !fi.IsDir() { 59 return filepath, nil 60 } 61 } 62 } 63 64 return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":")) 65 } 66 67 const ( 68 gccgov1Magic = "v1;\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 // reset to offset 0 - needed on Plan 9 (see issue #11265) 95 // TODO: remove once issue #11265 has been resolved. 96 _, err = f.Seek(0, 0) 97 if err != nil { 98 return 99 } 100 101 var elfreader io.ReaderAt 102 switch string(magic[:]) { 103 case gccgov1Magic, goimporterMagic: 104 // Raw export data. 105 reader = f 106 return 107 108 case archiveMagic: 109 // TODO(pcc): Read the archive directly instead of using "ar". 110 f.Close() 111 closer = nil 112 113 cmd := exec.Command("ar", "p", fpath) 114 var out []byte 115 out, err = cmd.Output() 116 if err != nil { 117 return 118 } 119 120 elfreader = bytes.NewReader(out) 121 122 default: 123 elfreader = f 124 } 125 126 ef, err := elf.NewFile(elfreader) 127 if err != nil { 128 return 129 } 130 131 sec := ef.Section(".go_export") 132 if sec == nil { 133 err = fmt.Errorf("%s: .go_export section not found", fpath) 134 return 135 } 136 137 reader = sec.Open() 138 return 139 } 140 141 func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) types.Importer { 142 return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) { 143 if pkgpath == "unsafe" { 144 return types.Unsafe, nil 145 } 146 147 fpath, err := findExportFile(searchpaths, pkgpath) 148 if err != nil { 149 return 150 } 151 152 reader, closer, err := openExportFile(fpath) 153 if err != nil { 154 return 155 } 156 if closer != nil { 157 defer closer.Close() 158 } 159 160 var magic [4]byte 161 _, err = reader.Read(magic[:]) 162 if err != nil { 163 return 164 } 165 _, err = reader.Seek(0, 0) 166 if err != nil { 167 return 168 } 169 170 switch string(magic[:]) { 171 case gccgov1Magic: 172 var p parser 173 p.init(fpath, reader, imports) 174 pkg = p.parsePackage() 175 if initmap != nil { 176 initmap[pkg] = p.initdata 177 } 178 179 case goimporterMagic: 180 var data []byte 181 data, err = ioutil.ReadAll(reader) 182 if err != nil { 183 return 184 } 185 var n int 186 n, pkg, err = importer.ImportData(imports, data) 187 if err != nil { 188 return 189 } 190 191 if initmap != nil { 192 suffixreader := bytes.NewReader(data[n:]) 193 var p parser 194 p.init(fpath, suffixreader, nil) 195 p.parseInitData() 196 initmap[pkg] = p.initdata 197 } 198 199 default: 200 err = fmt.Errorf("unrecognized magic string: %q", string(magic[:])) 201 } 202 203 return 204 } 205 }