github.com/llvm-mirror/llgo@v0.0.0-20190322182713-bf6f0a60fce1/third_party/gotools/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 "llvm.org/llgo/third_party/gotools/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 "llvm.org/llgo/third_party/gotools/go/importer" 20 "llvm.org/llgo/third_party/gotools/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 95 var elfreader io.ReaderAt 96 switch string(magic[:]) { 97 case gccgov1Magic, goimporterMagic: 98 // Raw export data. 99 reader = f 100 return 101 102 case archiveMagic: 103 // TODO(pcc): Read the archive directly instead of using "ar". 104 f.Close() 105 closer = nil 106 107 cmd := exec.Command("ar", "p", fpath) 108 var out []byte 109 out, err = cmd.Output() 110 if err != nil { 111 return 112 } 113 114 elfreader = bytes.NewReader(out) 115 116 default: 117 elfreader = f 118 } 119 120 ef, err := elf.NewFile(elfreader) 121 if err != nil { 122 return 123 } 124 125 sec := ef.Section(".go_export") 126 if sec == nil { 127 err = fmt.Errorf("%s: .go_export section not found", fpath) 128 return 129 } 130 131 reader = sec.Open() 132 return 133 } 134 135 func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) types.Importer { 136 return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) { 137 if pkgpath == "unsafe" { 138 return types.Unsafe, nil 139 } 140 141 fpath, err := findExportFile(searchpaths, pkgpath) 142 if err != nil { 143 return 144 } 145 146 reader, closer, err := openExportFile(fpath) 147 if err != nil { 148 return 149 } 150 if closer != nil { 151 defer closer.Close() 152 } 153 154 var magic [4]byte 155 _, err = reader.Read(magic[:]) 156 if err != nil { 157 return 158 } 159 _, err = reader.Seek(0, 0) 160 if err != nil { 161 return 162 } 163 164 switch string(magic[:]) { 165 case gccgov1Magic: 166 var p parser 167 p.init(fpath, reader, imports) 168 pkg = p.parsePackage() 169 if initmap != nil { 170 initmap[pkg] = p.initdata 171 } 172 173 case goimporterMagic: 174 var data []byte 175 data, err = ioutil.ReadAll(reader) 176 if err != nil { 177 return 178 } 179 var n int 180 n, pkg, err = importer.ImportData(imports, data) 181 if err != nil { 182 return 183 } 184 185 if initmap != nil { 186 suffixreader := bytes.NewReader(data[n:]) 187 var p parser 188 p.init(fpath, suffixreader, nil) 189 p.parseInitData() 190 initmap[pkg] = p.initdata 191 } 192 193 default: 194 err = fmt.Errorf("unrecognized magic string: %q", string(magic[:])) 195 } 196 197 return 198 } 199 }