// 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 = "!" // 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 }