github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/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 string) (*types.Package, error) 141 142 func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer { 143 return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) { 144 if pkgpath == "unsafe" { 145 return types.Unsafe, nil 146 } 147 148 fpath, err := findExportFile(searchpaths, pkgpath) 149 if err != nil { 150 return 151 } 152 153 reader, closer, err := openExportFile(fpath) 154 if err != nil { 155 return 156 } 157 if closer != nil { 158 defer closer.Close() 159 } 160 161 var magic [4]byte 162 _, err = reader.Read(magic[:]) 163 if err != nil { 164 return 165 } 166 _, err = reader.Seek(0, io.SeekStart) 167 if err != nil { 168 return 169 } 170 171 switch string(magic[:]) { 172 case gccgov1Magic, gccgov2Magic: 173 var p parser 174 p.init(fpath, reader, imports) 175 pkg = p.parsePackage() 176 if initmap != nil { 177 initmap[pkg] = p.initdata 178 } 179 180 // Excluded for now: Standard gccgo doesn't support this import format currently. 181 // case goimporterMagic: 182 // var data []byte 183 // data, err = ioutil.ReadAll(reader) 184 // if err != nil { 185 // return 186 // } 187 // var n int 188 // n, pkg, err = importer.ImportData(imports, data) 189 // if err != nil { 190 // return 191 // } 192 193 // if initmap != nil { 194 // suffixreader := bytes.NewReader(data[n:]) 195 // var p parser 196 // p.init(fpath, suffixreader, nil) 197 // p.parseInitData() 198 // initmap[pkg] = p.initdata 199 // } 200 201 default: 202 err = fmt.Errorf("unrecognized magic string: %q", string(magic[:])) 203 } 204 205 return 206 } 207 }