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