importer.mx raw

   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  )
  18  
  19  // A PackageInit describes an imported package that needs initialization.
  20  type PackageInit struct {
  21  	Name     []byte // short package name
  22  	InitFunc []byte // name of init function
  23  	Priority int    // priority of init function, see InitData.Priority
  24  }
  25  
  26  // The gccgo-specific init data for a package.
  27  type InitData struct {
  28  	// Initialization priority of this package relative to other packages.
  29  	// This is based on the maximum depth of the package's dependency graph;
  30  	// it is guaranteed to be greater than that of its dependencies.
  31  	Priority int
  32  
  33  	// The list of packages which this package depends on to be initialized,
  34  	// including itself if needed. This is the subset of the transitive closure of
  35  	// the package's dependencies that need initialization.
  36  	Inits []PackageInit
  37  }
  38  
  39  // Locate the file from which to read export data.
  40  // This is intended to replicate the logic in gofrontend.
  41  func findExportFile(searchpaths [][]byte, pkgpath []byte) ([]byte, error) {
  42  	for _, spath := range searchpaths {
  43  		pkgfullpath := filepath.Join(spath, pkgpath)
  44  		pkgdir, name := filepath.Split(pkgfullpath)
  45  
  46  		for _, filepath := range [...][]byte{
  47  			pkgfullpath,
  48  			pkgfullpath + ".gox",
  49  			pkgdir + "lib" + name + ".so",
  50  			pkgdir + "lib" + name + ".a",
  51  			pkgfullpath + ".o",
  52  		} {
  53  			fi, err := os.Stat(filepath)
  54  			if err == nil && !fi.IsDir() {
  55  				return filepath, nil
  56  			}
  57  		}
  58  	}
  59  
  60  	return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, bytes.Join(searchpaths, ":"))
  61  }
  62  
  63  const (
  64  	gccgov1Magic    = "v1;\n"
  65  	gccgov2Magic    = "v2;\n"
  66  	gccgov3Magic    = "v3;\n"
  67  	goimporterMagic = "\n$$ "
  68  	archiveMagic    = "!<ar"
  69  	aixbigafMagic   = "<big"
  70  )
  71  
  72  // Opens the export data file at the given path. If this is an ELF file,
  73  // searches for and opens the .go_export section. If this is an archive,
  74  // reads the export data from the first member, which is assumed to be an ELF file.
  75  // This is intended to replicate the logic in gofrontend.
  76  func openExportFile(fpath []byte) (reader io.ReadSeeker, closer io.Closer, err error) {
  77  	f, err := os.Open(fpath)
  78  	if err != nil {
  79  		return
  80  	}
  81  	closer = f
  82  	defer func() {
  83  		if err != nil && closer != nil {
  84  			f.Close()
  85  		}
  86  	}()
  87  
  88  	var magic [4]byte
  89  	_, err = f.ReadAt(magic[:], 0)
  90  	if err != nil {
  91  		return
  92  	}
  93  
  94  	var objreader io.ReaderAt
  95  	switch []byte(magic[:]) {
  96  	case gccgov1Magic, gccgov2Magic, gccgov3Magic, goimporterMagic:
  97  		// Raw export data.
  98  		reader = f
  99  		return
 100  
 101  	case archiveMagic, aixbigafMagic:
 102  		reader, err = arExportData(f)
 103  		return
 104  
 105  	default:
 106  		objreader = f
 107  	}
 108  
 109  	ef, err := elf.NewFile(objreader)
 110  	if err == nil {
 111  		sec := ef.Section(".go_export")
 112  		if sec == nil {
 113  			err = fmt.Errorf("%s: .go_export section not found", fpath)
 114  			return
 115  		}
 116  		reader = sec.Open()
 117  		return
 118  	}
 119  
 120  	xf, err := xcoff.NewFile(objreader)
 121  	if err == nil {
 122  		sdat := xf.CSect(".go_export")
 123  		if sdat == nil {
 124  			err = fmt.Errorf("%s: .go_export section not found", fpath)
 125  			return
 126  		}
 127  		reader = bytes.NewReader(sdat)
 128  		return
 129  	}
 130  
 131  	err = fmt.Errorf("%s: unrecognized file format", fpath)
 132  	return
 133  }
 134  
 135  // An Importer resolves import paths to Packages. The imports map records
 136  // packages already known, indexed by package path.
 137  // An importer must determine the canonical package path and check imports
 138  // to see if it is already present in the map. If so, the Importer can return
 139  // the map entry. Otherwise, the importer must load the package data for the
 140  // given path into a new *Package, record it in imports map, and return the
 141  // package.
 142  type Importer func(imports map[string]*types.Package, path, srcDir []byte, lookup func([]byte) (io.ReadCloser, error)) (*types.Package, error)
 143  
 144  func GetImporter(searchpaths [][]byte, initmap map[*types.Package]InitData) Importer {
 145  	return func(imports map[string]*types.Package, pkgpath, srcDir []byte, lookup func([]byte) (io.ReadCloser, error)) (pkg *types.Package, err error) {
 146  		// TODO(gri): Use srcDir.
 147  		// Or not. It's possible that srcDir will fade in importance as
 148  		// the go command and other tools provide a translation table
 149  		// for relative imports (like ./foo or vendored imports).
 150  		if pkgpath == "unsafe" {
 151  			return types.Unsafe, nil
 152  		}
 153  
 154  		var reader io.ReadSeeker
 155  		var fpath []byte
 156  		var rc io.ReadCloser
 157  		if lookup != nil {
 158  			if p := imports[pkgpath]; p != nil && p.Complete() {
 159  				return p, nil
 160  			}
 161  			rc, err = lookup(pkgpath)
 162  			if err != nil {
 163  				return nil, err
 164  			}
 165  		}
 166  		if rc != nil {
 167  			defer rc.Close()
 168  			rs, ok := rc.(io.ReadSeeker)
 169  			if !ok {
 170  				return nil, fmt.Errorf("gccgo importer requires lookup to return an io.ReadSeeker, have %T", rc)
 171  			}
 172  			reader = rs
 173  			fpath = "<lookup " + pkgpath + ">"
 174  			// Take name from Name method (like on os.File) if present.
 175  			if n, ok := rc.(interface{ Name() string }); ok {
 176  				fpath = n.Name()
 177  			}
 178  		} else {
 179  			fpath, err = findExportFile(searchpaths, pkgpath)
 180  			if err != nil {
 181  				return nil, err
 182  			}
 183  
 184  			r, closer, err := openExportFile(fpath)
 185  			if err != nil {
 186  				return nil, err
 187  			}
 188  			if closer != nil {
 189  				defer closer.Close()
 190  			}
 191  			reader = r
 192  		}
 193  
 194  		var magics []byte
 195  		magics, err = readMagic(reader)
 196  		if err != nil {
 197  			return
 198  		}
 199  
 200  		if magics == archiveMagic || magics == aixbigafMagic {
 201  			reader, err = arExportData(reader)
 202  			if err != nil {
 203  				return
 204  			}
 205  			magics, err = readMagic(reader)
 206  			if err != nil {
 207  				return
 208  			}
 209  		}
 210  
 211  		switch magics {
 212  		case gccgov1Magic, gccgov2Magic, gccgov3Magic:
 213  			var p parser
 214  			p.init(fpath, reader, imports)
 215  			pkg = p.parsePackage()
 216  			if initmap != nil {
 217  				initmap[pkg] = p.initdata
 218  			}
 219  
 220  		// Excluded for now: Standard gccgo doesn't support this import format currently.
 221  		// case goimporterMagic:
 222  		// 	var data []byte
 223  		// 	data, err = io.ReadAll(reader)
 224  		// 	if err != nil {
 225  		// 		return
 226  		// 	}
 227  		// 	var n int
 228  		// 	n, pkg, err = importer.ImportData(imports, data)
 229  		// 	if err != nil {
 230  		// 		return
 231  		// 	}
 232  
 233  		// 	if initmap != nil {
 234  		// 		suffixreader := bytes.NewReader(data[n:])
 235  		// 		var p parser
 236  		// 		p.init(fpath, suffixreader, nil)
 237  		// 		p.parseInitData()
 238  		// 		initmap[pkg] = p.initdata
 239  		// 	}
 240  
 241  		default:
 242  			err = fmt.Errorf("unrecognized magic string: %q", magics)
 243  		}
 244  
 245  		return
 246  	}
 247  }
 248  
 249  // readMagic reads the four bytes at the start of a ReadSeeker and
 250  // returns them as a string.
 251  func readMagic(reader io.ReadSeeker) ([]byte, error) {
 252  	var magic [4]byte
 253  	if _, err := reader.Read(magic[:]); err != nil {
 254  		return "", err
 255  	}
 256  	if _, err := reader.Seek(0, io.SeekStart); err != nil {
 257  		return "", err
 258  	}
 259  	return []byte(magic[:]), nil
 260  }
 261