github.com/aloncn/graphics-go@v0.0.1/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 goimporterMagic = "\n$$ " 67 archiveMagic = "!<ar" 68 ) 69 70 // Opens the export data file at the given path. If this is an ELF file, 71 // searches for and opens the .go_export section. If this is an archive, 72 // reads the export data from the first member, which is assumed to be an ELF file. 73 // This is intended to replicate the logic in gofrontend. 74 func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) { 75 f, err := os.Open(fpath) 76 if err != nil { 77 return 78 } 79 closer = f 80 defer func() { 81 if err != nil && closer != nil { 82 f.Close() 83 } 84 }() 85 86 var magic [4]byte 87 _, err = f.ReadAt(magic[:], 0) 88 if err != nil { 89 return 90 } 91 // reset to offset 0 - needed on Plan 9 (see issue #11265) 92 // TODO: remove once issue #11265 has been resolved. 93 _, err = f.Seek(0, 0) 94 if err != nil { 95 return 96 } 97 98 var elfreader io.ReaderAt 99 switch string(magic[:]) { 100 case gccgov1Magic, goimporterMagic: 101 // Raw export data. 102 reader = f 103 return 104 105 case archiveMagic: 106 // TODO(pcc): Read the archive directly instead of using "ar". 107 f.Close() 108 closer = nil 109 110 cmd := exec.Command("ar", "p", fpath) 111 var out []byte 112 out, err = cmd.Output() 113 if err != nil { 114 return 115 } 116 117 elfreader = bytes.NewReader(out) 118 119 default: 120 elfreader = f 121 } 122 123 ef, err := elf.NewFile(elfreader) 124 if err != nil { 125 return 126 } 127 128 sec := ef.Section(".go_export") 129 if sec == nil { 130 err = fmt.Errorf("%s: .go_export section not found", fpath) 131 return 132 } 133 134 reader = sec.Open() 135 return 136 } 137 138 // An Importer resolves import paths to Packages. The imports map records 139 // packages already known, indexed by package path. 140 // An importer must determine the canonical package path and check imports 141 // to see if it is already present in the map. If so, the Importer can return 142 // the map entry. Otherwise, the importer must load the package data for the 143 // given path into a new *Package, record it in imports map, and return the 144 // package. 145 type Importer func(imports map[string]*types.Package, path string) (*types.Package, error) 146 147 func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer { 148 return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) { 149 if pkgpath == "unsafe" { 150 return types.Unsafe, nil 151 } 152 153 fpath, err := findExportFile(searchpaths, pkgpath) 154 if err != nil { 155 return 156 } 157 158 reader, closer, err := openExportFile(fpath) 159 if err != nil { 160 return 161 } 162 if closer != nil { 163 defer closer.Close() 164 } 165 166 var magic [4]byte 167 _, err = reader.Read(magic[:]) 168 if err != nil { 169 return 170 } 171 _, err = reader.Seek(0, 0) 172 if err != nil { 173 return 174 } 175 176 switch string(magic[:]) { 177 case gccgov1Magic: 178 var p parser 179 p.init(fpath, reader, imports) 180 pkg = p.parsePackage() 181 if initmap != nil { 182 initmap[pkg] = p.initdata 183 } 184 185 // Excluded for now: Standard gccgo doesn't support this import format currently. 186 // case goimporterMagic: 187 // var data []byte 188 // data, err = ioutil.ReadAll(reader) 189 // if err != nil { 190 // return 191 // } 192 // var n int 193 // n, pkg, err = importer.ImportData(imports, data) 194 // if err != nil { 195 // return 196 // } 197 198 // if initmap != nil { 199 // suffixreader := bytes.NewReader(data[n:]) 200 // var p parser 201 // p.init(fpath, suffixreader, nil) 202 // p.parseInitData() 203 // initmap[pkg] = p.initdata 204 // } 205 206 default: 207 err = fmt.Errorf("unrecognized magic string: %q", string(magic[:])) 208 } 209 210 return 211 } 212 }