github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/gccgoexportdata/gccgoexportdata.go (about)

     1  // Copyright 2016 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 gccgoexportdata provides functions for reading export data
     6  // files containing type information produced by the gccgo compiler.
     7  //
     8  // This package is a stop-gap until such time as gccgo uses the same
     9  // export data format as gc; see Go issue 17573. Once that occurs, this
    10  // package will be deprecated and eventually deleted.
    11  package gccgoexportdata
    12  
    13  // TODO(adonovan): add Find, Write, Importer to the API,
    14  // for symmetry with gcexportdata.
    15  
    16  import (
    17  	"bytes"
    18  	"debug/elf"
    19  	"fmt"
    20  	"go/token"
    21  	"go/types"
    22  	"io"
    23  	"io/ioutil"
    24  	"strconv"
    25  	"strings"
    26  
    27  	"github.com/powerman/golang-tools/go/internal/gccgoimporter"
    28  )
    29  
    30  // CompilerInfo executes the specified gccgo compiler and returns
    31  // information about it: its version (e.g. "4.8.0"), its target triple
    32  // (e.g. "x86_64-unknown-linux-gnu"), and the list of directories it
    33  // searches to find standard packages. The given arguments are passed
    34  // directly to calls to the specified gccgo compiler.
    35  func CompilerInfo(gccgo string, args ...string) (version, triple string, dirs []string, err error) {
    36  	var inst gccgoimporter.GccgoInstallation
    37  	err = inst.InitFromDriver(gccgo, args...)
    38  	if err == nil {
    39  		version = inst.GccVersion
    40  		triple = inst.TargetTriple
    41  		dirs = inst.SearchPaths()
    42  	}
    43  	return
    44  }
    45  
    46  // NewReader returns a reader for the export data section of an object
    47  // (.o) or archive (.a) file read from r.
    48  func NewReader(r io.Reader) (io.Reader, error) {
    49  	data, err := ioutil.ReadAll(r)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	// If the file is an archive, extract the first section.
    55  	const archiveMagic = "!<arch>\n"
    56  	if bytes.HasPrefix(data, []byte(archiveMagic)) {
    57  		section, err := firstSection(data[len(archiveMagic):])
    58  		if err != nil {
    59  			return nil, err
    60  		}
    61  		data = section
    62  	}
    63  
    64  	// Data contains an ELF file with a .go_export section.
    65  	// ELF magic number is "\x7fELF".
    66  	ef, err := elf.NewFile(bytes.NewReader(data))
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  	sec := ef.Section(".go_export")
    71  	if sec == nil {
    72  		return nil, fmt.Errorf("no .go_export section")
    73  	}
    74  	return sec.Open(), nil
    75  }
    76  
    77  // firstSection returns the contents of the first regular file in an ELF
    78  // archive (http://www.sco.com/developers/devspecs/gabi41.pdf, ยง7.2).
    79  func firstSection(a []byte) ([]byte, error) {
    80  	for len(a) >= 60 {
    81  		var hdr []byte
    82  		hdr, a = a[:60], a[60:]
    83  
    84  		name := strings.TrimSpace(string(hdr[:16]))
    85  
    86  		sizeStr := string(hdr[48:58])
    87  		size, err := strconv.Atoi(strings.TrimSpace(sizeStr))
    88  		if err != nil {
    89  			return nil, fmt.Errorf("invalid size: %q", sizeStr)
    90  		}
    91  
    92  		if len(a) < size {
    93  			return nil, fmt.Errorf("invalid section size: %d", size)
    94  		}
    95  
    96  		// The payload is padded to an even number of bytes.
    97  		var payload []byte
    98  		payload, a = a[:size], a[size+size&1:]
    99  
   100  		// Skip special files:
   101  		//   "/"           archive symbol table
   102  		//   "/SYM64/"     archive symbol table on e.g. s390x
   103  		//   "//"          archive string table (if any filename is >15 bytes)
   104  		if name == "/" || name == "/SYM64/" || name == "//" {
   105  			continue
   106  		}
   107  
   108  		return payload, nil
   109  	}
   110  	return nil, fmt.Errorf("archive has no regular sections")
   111  }
   112  
   113  // Read reads export data from in, decodes it, and returns type
   114  // information for the package.
   115  // The package name is specified by path.
   116  //
   117  // The FileSet parameter is currently unused but exists for symmetry
   118  // with gcexportdata.
   119  //
   120  // Read may inspect and add to the imports map to ensure that references
   121  // within the export data to other packages are consistent.  The caller
   122  // must ensure that imports[path] does not exist, or exists but is
   123  // incomplete (see types.Package.Complete), and Read inserts the
   124  // resulting package into this map entry.
   125  //
   126  // On return, the state of the reader is undefined.
   127  func Read(in io.Reader, _ *token.FileSet, imports map[string]*types.Package, path string) (*types.Package, error) {
   128  	return gccgoimporter.Parse(in, imports, path)
   129  }