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