// Copyright 2011 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.// This file should be kept in sync with $GOROOT/src/internal/exportdata/exportdata.go.// This file also additionally implements FindExportData for gcexportdata.NewReader.package gcimporterimport ()// FindExportData positions the reader r at the beginning of the// export data section of an underlying cmd/compile created archive// file by reading from it. The reader must be positioned at the// start of the file before calling this function.// This returns the length of the export data in bytes.//// This function is needed by [gcexportdata.Read], which must// accept inputs produced by the last two releases of cmd/compile,// plus tip.func ( *bufio.Reader) ( int64, error) { , := FindPackageDefinition()if != nil {return } = int64() , , := ReadObjectHeaders()if != nil {return } -= int64(len())for , := range { -= int64(len()) }// Check for the binary export data section header "$$B\n". // TODO(taking): Unify with ReadExportDataHeader so that it stops at the 'u' instead of reading , := .ReadSlice('\n')if != nil {return } := string()if != "$$B\n" { = fmt.Errorf("unknown export data header: %q", )return } -= int64(len())// For files with a binary export data header "$$B\n", // these are always terminated by an end-of-section marker "\n$$\n". // So the last bytes must always be this constant. // // The end-of-section marker is not a part of the export data itself. // Do not include these in size. // // It would be nice to have sanity check that the final bytes after // the export data are indeed the end-of-section marker. The split // of gcexportdata.NewReader and gcexportdata.Read make checking this // ugly so gcimporter gives up enforcing this. The compiler and go/types // importer do enforce this, which seems good enough.const = "\n$$\n" -= int64(len())if < 0 { = fmt.Errorf("invalid size (%d) in the archive file: %d bytes remain without section headers (recompile package)", , )return }return}// ReadUnified reads the contents of the unified export data from a reader r// that contains the contents of a GC-created archive file.//// On success, the reader will be positioned after the end-of-section marker "\n$$\n".//// Supported GC-created archive files have 4 layers of nesting:// - An archive file containing a package definition file.// - The package definition file contains headers followed by a data section.// Headers are lines (≤ 4kb) that do not start with "$$".// - The data section starts with "$$B\n" followed by export data followed// by an end of section marker "\n$$\n". (The section start "$$\n" is no// longer supported.)// - The export data starts with a format byte ('u') followed by the <data> in// the given format. (See ReadExportDataHeader for older formats.)//// Putting this together, the bytes in a GC-created archive files are expected// to look like the following.// See cmd/internal/archive for more details on ar file headers.//// | <!arch>\n | ar file signature// | __.PKGDEF...size...\n | ar header for __.PKGDEF including size.// | go object <...>\n | objabi header// | <optional headers>\n | other headers such as build id// | $$B\n | binary format marker// | u<data>\n | unified export <data>// | $$\n | end-of-section marker// | [optional padding] | padding byte (0x0A) if size is odd// | [ar file header] | other ar files// | [ar file data] |func ( *bufio.Reader) ( []byte, error) {// We historically guaranteed headers at the default buffer size (4096) work. // This ensures we can use ReadSlice throughout.const = 4096 = bufio.NewReaderSize(, ) , := FindPackageDefinition()if != nil {return } := , , := ReadObjectHeaders()if != nil {return } -= len()for , := range { -= len() } , := ReadExportDataHeader()if != nil {return } -= // size also includes the end of section marker. Remove that many bytes from the end.const = "\n$$\n" -= len()if < 0 { = fmt.Errorf("invalid size (%d) in the archive file: %d bytes remain without section headers (recompile package)", , )return }// Read n bytes from buf. = make([]byte, ) _, = io.ReadFull(, )if != nil {return }// Check for marker at the end.var [len()]byte _, = io.ReadFull(, [:])if != nil {return }if := string([:]); != { = fmt.Errorf("read %q instead of end-of-section marker (%q)", , )return }return}// FindPackageDefinition positions the reader r at the beginning of a package// definition file ("__.PKGDEF") within a GC-created archive by reading// from it, and returns the size of the package definition file in the archive.//// The reader must be positioned at the start of the archive file before calling// this function, and "__.PKGDEF" is assumed to be the first file in the archive.//// See cmd/internal/archive for details on the archive format.func ( *bufio.Reader) ( int, error) {// Uses ReadSlice to limit risk of malformed inputs.// Read first line to make sure this is an object file. , := .ReadSlice('\n')if != nil { = fmt.Errorf("can't find export data (%v)", )return }// Is the first line an archive file signature?ifstring() != "!<arch>\n" { = fmt.Errorf("not the start of an archive file (%q)", )return }// package export block should be first = readArchiveHeader(, "__.PKGDEF")if <= 0 { = fmt.Errorf("not a package file")return }return}// ReadObjectHeaders reads object headers from the reader. Object headers are// lines that do not start with an end-of-section marker "$$". The first header// is the objabi header. On success, the reader will be positioned at the beginning// of the end-of-section marker.//// It returns an error if any header does not fit in r.Size() bytes.func ( *bufio.Reader) ( string, []string, error) {// line is a temporary buffer for headers. // Use bounded reads (ReadSlice, Peek) to limit risk of malformed inputs.var []byte// objapi header should be the first lineif , = .ReadSlice('\n'); != nil { = fmt.Errorf("can't find export data (%v)", )return } = string()// objapi header begins with "go object ".if !strings.HasPrefix(, "go object ") { = fmt.Errorf("not a go object file: %s", )return }// process remaining object header linesfor {// check for an end of section marker "$$" , = .Peek(2)if != nil {return }ifstring() == "$$" {return// stop }// read next header , = .ReadSlice('\n')if != nil {return } = append(, string()) }}// ReadExportDataHeader reads the export data header and format from r.// It returns the number of bytes read, or an error if the format is no longer// supported or it failed to read.//// The only currently supported format is binary export data in the// unified export format.func ( *bufio.Reader) ( int, error) {// Read export data header. , := .ReadSlice('\n')if != nil {return } := string()switch {case"$$\n": = fmt.Errorf("old textual export format no longer supported (recompile package)")returncase"$$B\n":varbyte , = .ReadByte()if != nil {return }// The unified export format starts with a 'u'.switch {case'u':default:// Older no longer supported export formats include: // indexed export format which started with an 'i'; and // the older binary export format which started with a 'c', // 'd', or 'v' (from "version"). = fmt.Errorf("binary export format %q is no longer supported (recompile package)", )return }default: = fmt.Errorf("unknown export data header: %q", )return } = len() + 1// + 1 is for 'u'return}// FindPkg returns the filename and unique package id for an import// path based on package information provided by build.Import (using// the build.Default build.Context). A relative srcDir is interpreted// relative to the current working directory.//// FindPkg is only used in tests within x/tools.func (, string) (, string, error) {// TODO(taking): Move internal/exportdata.FindPkg into its own file, // and then this copy into a _test package.if == "" {return"", "", errors.New("path is empty") }varstringswitch {default:// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" // Don't require the source files to be present.if , := filepath.Abs(); == nil { // see issue 14282 = }var *build.Package , = build.Import(, , build.FindOnly|build.AllowBinary)if .PkgObj == "" {if .Goroot && .Dir != "" { , = lookupGorootExport(.Dir)if == nil { _, = os.Stat() }if == nil {return , .ImportPath, nil } }goto } else { = strings.TrimSuffix(.PkgObj, ".a") } = .ImportPathcasebuild.IsLocalImport():// "./x" -> "/this/directory/x.ext", "/this/directory/x" = filepath.Join(, ) = casefilepath.IsAbs():// for completeness only - go/build.Import // does not support absolute imports // "/x" -> "/x.ext", "/x" = = }iffalse { // for debuggingif != {fmt.Printf("%s -> %s\n", , ) } }// try extensionsfor , := rangepkgExts { = + , := os.Stat()if == nil && !.IsDir() {return , , nil }if == nil { = } }:if == nil {return"", , fmt.Errorf("can't find import: %q", ) }return"", , fmt.Errorf("can't find import: %q: %w", , )}varpkgExts = [...]string{".a", ".o"} // a file from the build cache will have no extensionvarexportMapsync.Map// package dir → func() (string, error)// lookupGorootExport returns the location of the export data// (normally found in the build cache, but located in GOROOT/pkg// in prior Go releases) for the package located in pkgDir.//// (We use the package's directory instead of its import path// mainly to simplify handling of the packages in src/vendor// and cmd/vendor.)//// lookupGorootExport is only used in tests within x/tools.func ( string) (string, error) { , := exportMap.Load()if ! {var (sync.Oncestringerror ) , _ = exportMap.LoadOrStore(, func() (string, error) { .Do(func() { := exec.Command(filepath.Join(build.Default.GOROOT, "bin", "go"), "list", "-export", "-f", "{{.Export}}", ) .Dir = build.Default.GOROOT .Env = append(os.Environ(), "PWD="+.Dir, "GOROOT="+build.Default.GOROOT)var []byte , = .Output()if != nil {if , := .(*exec.ExitError); && len(.Stderr) > 0 { = errors.New(string(.Stderr)) }return } := strings.Split(string(bytes.TrimSpace()), "\n")iflen() != 1 { = fmt.Errorf("go list reported %d exports; expected 1", len())return } = [0] })return , }) }return .(func() (string, error))()}
The pages are generated with Goldsv0.7.6. (GOOS=linux GOARCH=amd64)