github.com/panjjo/go@v0.0.0-20161104043856-d62b31386338/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 92 var elfreader io.ReaderAt 93 switch string(magic[:]) { 94 case gccgov1Magic, goimporterMagic: 95 // Raw export data. 96 reader = f 97 return 98 99 case archiveMagic: 100 // TODO(pcc): Read the archive directly instead of using "ar". 101 f.Close() 102 closer = nil 103 104 cmd := exec.Command("ar", "p", fpath) 105 var out []byte 106 out, err = cmd.Output() 107 if err != nil { 108 return 109 } 110 111 elfreader = bytes.NewReader(out) 112 113 default: 114 elfreader = f 115 } 116 117 ef, err := elf.NewFile(elfreader) 118 if err != nil { 119 return 120 } 121 122 sec := ef.Section(".go_export") 123 if sec == nil { 124 err = fmt.Errorf("%s: .go_export section not found", fpath) 125 return 126 } 127 128 reader = sec.Open() 129 return 130 } 131 132 // An Importer resolves import paths to Packages. The imports map records 133 // packages already known, indexed by package path. 134 // An importer must determine the canonical package path and check imports 135 // to see if it is already present in the map. If so, the Importer can return 136 // the map entry. Otherwise, the importer must load the package data for the 137 // given path into a new *Package, record it in imports map, and return the 138 // package. 139 type Importer func(imports map[string]*types.Package, path string) (*types.Package, error) 140 141 func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) 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, io.SeekStart) 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 // Excluded for now: Standard gccgo doesn't support this import format currently. 180 // case goimporterMagic: 181 // var data []byte 182 // data, err = ioutil.ReadAll(reader) 183 // if err != nil { 184 // return 185 // } 186 // var n int 187 // n, pkg, err = importer.ImportData(imports, data) 188 // if err != nil { 189 // return 190 // } 191 192 // if initmap != nil { 193 // suffixreader := bytes.NewReader(data[n:]) 194 // var p parser 195 // p.init(fpath, suffixreader, nil) 196 // p.parseInitData() 197 // initmap[pkg] = p.initdata 198 // } 199 200 default: 201 err = fmt.Errorf("unrecognized magic string: %q", string(magic[:])) 202 } 203 204 return 205 } 206 }