%PDF- %PDF-
Direktori : /usr/local/go/src/go/internal/gccgoimporter/ |
Current File : //usr/local/go/src/go/internal/gccgoimporter/importer.go |
// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package gccgoimporter implements Import for gccgo-generated object files. package gccgoimporter // import "go/internal/gccgoimporter" import ( "bytes" "debug/elf" "fmt" "go/types" "internal/xcoff" "io" "os" "path/filepath" "strings" ) // A PackageInit describes an imported package that needs initialization. type PackageInit struct { Name string // short package name InitFunc string // name of init function Priority int // priority of init function, see InitData.Priority } // The gccgo-specific init data for a package. type InitData struct { // Initialization priority of this package relative to other packages. // This is based on the maximum depth of the package's dependency graph; // it is guaranteed to be greater than that of its dependencies. Priority int // The list of packages which this package depends on to be initialized, // including itself if needed. This is the subset of the transitive closure of // the package's dependencies that need initialization. Inits []PackageInit } // Locate the file from which to read export data. // This is intended to replicate the logic in gofrontend. func findExportFile(searchpaths []string, pkgpath string) (string, error) { for _, spath := range searchpaths { pkgfullpath := filepath.Join(spath, pkgpath) pkgdir, name := filepath.Split(pkgfullpath) for _, filepath := range [...]string{ pkgfullpath, pkgfullpath + ".gox", pkgdir + "lib" + name + ".so", pkgdir + "lib" + name + ".a", pkgfullpath + ".o", } { fi, err := os.Stat(filepath) if err == nil && !fi.IsDir() { return filepath, nil } } } return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":")) } const ( gccgov1Magic = "v1;\n" gccgov2Magic = "v2;\n" gccgov3Magic = "v3;\n" goimporterMagic = "\n$$ " archiveMagic = "!<ar" aixbigafMagic = "<big" ) // Opens the export data file at the given path. If this is an ELF file, // searches for and opens the .go_export section. If this is an archive, // reads the export data from the first member, which is assumed to be an ELF file. // This is intended to replicate the logic in gofrontend. func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) { f, err := os.Open(fpath) if err != nil { return } closer = f defer func() { if err != nil && closer != nil { f.Close() } }() var magic [4]byte _, err = f.ReadAt(magic[:], 0) if err != nil { return } var objreader io.ReaderAt switch string(magic[:]) { case gccgov1Magic, gccgov2Magic, gccgov3Magic, goimporterMagic: // Raw export data. reader = f return case archiveMagic, aixbigafMagic: reader, err = arExportData(f) return default: objreader = f } ef, err := elf.NewFile(objreader) if err == nil { sec := ef.Section(".go_export") if sec == nil { err = fmt.Errorf("%s: .go_export section not found", fpath) return } reader = sec.Open() return } xf, err := xcoff.NewFile(objreader) if err == nil { sdat := xf.CSect(".go_export") if sdat == nil { err = fmt.Errorf("%s: .go_export section not found", fpath) return } reader = bytes.NewReader(sdat) return } err = fmt.Errorf("%s: unrecognized file format", fpath) return } // An Importer resolves import paths to Packages. The imports map records // packages already known, indexed by package path. // An importer must determine the canonical package path and check imports // to see if it is already present in the map. If so, the Importer can return // the map entry. Otherwise, the importer must load the package data for the // given path into a new *Package, record it in imports map, and return the // package. type Importer func(imports map[string]*types.Package, path, srcDir string, lookup func(string) (io.ReadCloser, error)) (*types.Package, error) func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer { return func(imports map[string]*types.Package, pkgpath, srcDir string, lookup func(string) (io.ReadCloser, error)) (pkg *types.Package, err error) { // TODO(gri): Use srcDir. // Or not. It's possible that srcDir will fade in importance as // the go command and other tools provide a translation table // for relative imports (like ./foo or vendored imports). if pkgpath == "unsafe" { return types.Unsafe, nil } var reader io.ReadSeeker var fpath string var rc io.ReadCloser if lookup != nil { if p := imports[pkgpath]; p != nil && p.Complete() { return p, nil } rc, err = lookup(pkgpath) if err != nil { return nil, err } } if rc != nil { defer rc.Close() rs, ok := rc.(io.ReadSeeker) if !ok { return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc) } reader = rs fpath = "<lookup " + pkgpath + ">" // Take name from Name method (like on os.File) if present. if n, ok := rc.(interface{ Name() string }); ok { fpath = n.Name() } } else { fpath, err = findExportFile(searchpaths, pkgpath) if err != nil { return nil, err } r, closer, err := openExportFile(fpath) if err != nil { return nil, err } if closer != nil { defer closer.Close() } reader = r } var magics string magics, err = readMagic(reader) if err != nil { return } if magics == archiveMagic || magics == aixbigafMagic { reader, err = arExportData(reader) if err != nil { return } magics, err = readMagic(reader) if err != nil { return } } switch magics { case gccgov1Magic, gccgov2Magic, gccgov3Magic: var p parser p.init(fpath, reader, imports) pkg = p.parsePackage() if initmap != nil { initmap[pkg] = p.initdata } // Excluded for now: Standard gccgo doesn't support this import format currently. // case goimporterMagic: // var data []byte // data, err = io.ReadAll(reader) // if err != nil { // return // } // var n int // n, pkg, err = importer.ImportData(imports, data) // if err != nil { // return // } // if initmap != nil { // suffixreader := bytes.NewReader(data[n:]) // var p parser // p.init(fpath, suffixreader, nil) // p.parseInitData() // initmap[pkg] = p.initdata // } default: err = fmt.Errorf("unrecognized magic string: %q", magics) } return } } // readMagic reads the four bytes at the start of a ReadSeeker and // returns them as a string. func readMagic(reader io.ReadSeeker) (string, error) { var magic [4]byte if _, err := reader.Read(magic[:]); err != nil { return "", err } if _, err := reader.Seek(0, io.SeekStart); err != nil { return "", err } return string(magic[:]), nil }