...

Source file src/go/internal/gccgoimporter/importer.go

     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		"go/types"
    13		"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 {
   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 = ioutil.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	}
   262	

View as plain text