github.com/bir3/gocompiler@v0.9.2202/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 "github.com/bir3/gocompiler/src/go/types" 13 "github.com/bir3/gocompiler/src/internal/xcoff" 14 "io" 15 "os" 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 gccgov3Magic = "v3;\n" 68 goimporterMagic = "\n$$ " 69 archiveMagic = "!<ar" 70 aixbigafMagic = "<big" 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 objreader io.ReaderAt 96 switch string(magic[:]) { 97 case gccgov1Magic, gccgov2Magic, gccgov3Magic, goimporterMagic: 98 // Raw export data. 99 reader = f 100 return 101 102 case archiveMagic, aixbigafMagic: 103 reader, err = arExportData(f) 104 return 105 106 default: 107 objreader = f 108 } 109 110 ef, err := elf.NewFile(objreader) 111 if err == nil { 112 sec := ef.Section(".go_export") 113 if sec == nil { 114 err = fmt.Errorf("%s: .go_export section not found", fpath) 115 return 116 } 117 reader = sec.Open() 118 return 119 } 120 121 xf, err := xcoff.NewFile(objreader) 122 if err == nil { 123 sdat := xf.CSect(".go_export") 124 if sdat == nil { 125 err = fmt.Errorf("%s: .go_export section not found", fpath) 126 return 127 } 128 reader = bytes.NewReader(sdat) 129 return 130 } 131 132 err = fmt.Errorf("%s: unrecognized file format", fpath) 133 return 134 } 135 136 // An Importer resolves import paths to Packages. The imports map records 137 // packages already known, indexed by package path. 138 // An importer must determine the canonical package path and check imports 139 // to see if it is already present in the map. If so, the Importer can return 140 // the map entry. Otherwise, the importer must load the package data for the 141 // given path into a new *Package, record it in imports map, and return the 142 // package. 143 type Importer func(imports map[string]*types.Package, path, srcDir string, lookup func(string) (io.ReadCloser, error)) (*types.Package, error) 144 145 func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer { 146 return func(imports map[string]*types.Package, pkgpath, srcDir string, lookup func(string) (io.ReadCloser, error)) (pkg *types.Package, err error) { 147 // TODO(gri): Use srcDir. 148 // Or not. It's possible that srcDir will fade in importance as 149 // the go command and other tools provide a translation table 150 // for relative imports (like ./foo or vendored imports). 151 if pkgpath == "unsafe" { 152 return types.Unsafe, nil 153 } 154 155 var reader io.ReadSeeker 156 var fpath string 157 var rc io.ReadCloser 158 if lookup != nil { 159 if p := imports[pkgpath]; p != nil && p.Complete() { 160 return p, nil 161 } 162 rc, err = lookup(pkgpath) 163 if err != nil { 164 return nil, err 165 } 166 } 167 if rc != nil { 168 defer rc.Close() 169 rs, ok := rc.(io.ReadSeeker) 170 if !ok { 171 return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc) 172 } 173 reader = rs 174 fpath = "<lookup " + pkgpath + ">" 175 // Take name from Name method (like on os.File) if present. 176 if n, ok := rc.(interface{ Name() string }); ok { 177 fpath = n.Name() 178 } 179 } else { 180 fpath, err = findExportFile(searchpaths, pkgpath) 181 if err != nil { 182 return nil, err 183 } 184 185 r, closer, err := openExportFile(fpath) 186 if err != nil { 187 return nil, err 188 } 189 if closer != nil { 190 defer closer.Close() 191 } 192 reader = r 193 } 194 195 var magics string 196 magics, err = readMagic(reader) 197 if err != nil { 198 return 199 } 200 201 if magics == archiveMagic || magics == aixbigafMagic { 202 reader, err = arExportData(reader) 203 if err != nil { 204 return 205 } 206 magics, err = readMagic(reader) 207 if err != nil { 208 return 209 } 210 } 211 212 switch magics { 213 case gccgov1Magic, gccgov2Magic, gccgov3Magic: 214 var p parser 215 p.init(fpath, reader, imports) 216 pkg = p.parsePackage() 217 if initmap != nil { 218 initmap[pkg] = p.initdata 219 } 220 221 // Excluded for now: Standard gccgo doesn't support this import format currently. 222 // case goimporterMagic: 223 // var data []byte 224 // data, err = io.ReadAll(reader) 225 // if err != nil { 226 // return 227 // } 228 // var n int 229 // n, pkg, err = importer.ImportData(imports, data) 230 // if err != nil { 231 // return 232 // } 233 234 // if initmap != nil { 235 // suffixreader := bytes.NewReader(data[n:]) 236 // var p parser 237 // p.init(fpath, suffixreader, nil) 238 // p.parseInitData() 239 // initmap[pkg] = p.initdata 240 // } 241 242 default: 243 err = fmt.Errorf("unrecognized magic string: %q", magics) 244 } 245 246 return 247 } 248 } 249 250 // readMagic reads the four bytes at the start of a ReadSeeker and 251 // returns them as a string. 252 func readMagic(reader io.ReadSeeker) (string, error) { 253 var magic [4]byte 254 if _, err := reader.Read(magic[:]); err != nil { 255 return "", err 256 } 257 if _, err := reader.Seek(0, io.SeekStart); err != nil { 258 return "", err 259 } 260 return string(magic[:]), nil 261 }