// 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 ( ) // 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( []string, string) (string, error) { for , := range { := filepath.Join(, ) , := filepath.Split() for , := range [...]string{ , + ".gox", + "lib" + + ".so", + "lib" + + ".a", + ".o", } { , := os.Stat() if == nil && !.IsDir() { return , nil } } } return "", fmt.Errorf("%s: could not find export data (tried %s)", , strings.Join(, ":")) } 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( string) ( io.ReadSeeker, io.Closer, error) { , := os.Open() if != nil { return } = defer func() { if != nil && != nil { .Close() } }() var [4]byte _, = .ReadAt([:], 0) if != nil { return } var io.ReaderAt switch string([:]) { case gccgov1Magic, gccgov2Magic, gccgov3Magic, goimporterMagic: // Raw export data. = return case archiveMagic, aixbigafMagic: , = arExportData() return default: = } , := elf.NewFile() if == nil { := .Section(".go_export") if == nil { = fmt.Errorf("%s: .go_export section not found", ) return } = .Open() return } , := xcoff.NewFile() if == nil { := .CSect(".go_export") if == nil { = fmt.Errorf("%s: .go_export section not found", ) return } = bytes.NewReader() return } = fmt.Errorf("%s: unrecognized file format", ) 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 ( []string, map[*types.Package]InitData) Importer { return func( map[string]*types.Package, , string, func(string) (io.ReadCloser, error)) ( *types.Package, 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 == "unsafe" { return types.Unsafe, nil } var io.ReadSeeker var string var io.ReadCloser if != nil { if := []; != nil && .Complete() { return , nil } , = () if != nil { return nil, } } if != nil { defer .Close() , := .(io.ReadSeeker) if ! { return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", ) } = = "<lookup " + + ">" // Take name from Name method (like on os.File) if present. if , := .(interface{ () string }); { = .() } } else { , = findExportFile(, ) if != nil { return nil, } , , := openExportFile() if != nil { return nil, } if != nil { defer .Close() } = } var string , = readMagic() if != nil { return } if == archiveMagic || == aixbigafMagic { , = arExportData() if != nil { return } , = readMagic() if != nil { return } } switch { case gccgov1Magic, gccgov2Magic, gccgov3Magic: var parser .init(, , ) = .parsePackage() if != nil { [] = .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: = fmt.Errorf("unrecognized magic string: %q", ) } return } } // readMagic reads the four bytes at the start of a ReadSeeker and // returns them as a string. func readMagic( io.ReadSeeker) (string, error) { var [4]byte if , := .Read([:]); != nil { return "", } if , := .Seek(0, io.SeekStart); != nil { return "", } return string([:]), nil }