}
ot = dgopkgpath(lsym, ot, tpkg)
- xcount := sort.Search(n, func(i int) bool { return !types.IsExported(m[i].name.Name) })
ot = dsymptr(lsym, ot, lsym, ot+3*Widthptr+uncommonSize(t))
- ot = duintptr(lsym, ot, uint64(xcount))
+ ot = duintptr(lsym, ot, uint64(n))
ot = duintptr(lsym, ot, uint64(n))
dataAdd := imethodSize() * n
ot = dextratype(lsym, ot, t, dataAdd)
// for security, only the exported fields.
case TSTRUCT:
fields := t.Fields().Slice()
+
+ // omitFieldForAwfulBoringCryptoKludge reports whether
+ // the field t should be omitted from the reflect data.
+ // In the crypto/... packages we omit an unexported field
+ // named "boring", to keep from breaking client code that
+ // expects rsa.PublicKey etc to have only public fields.
+ // As the name suggests, this is an awful kludge, but it is
+ // limited to the dev.boringcrypto branch and avoids
+ // much more invasive effects elsewhere.
+ omitFieldForAwfulBoringCryptoKludge := func(t *types.Field) bool {
+ if t.Sym == nil || t.Sym.Name != "boring" || t.Sym.Pkg == nil {
+ return false
+ }
+ path := t.Sym.Pkg.Path
+ if t.Sym.Pkg == localpkg {
+ path = myimportpath
+ }
+ return strings.HasPrefix(path, "crypto/")
+ }
+ newFields := fields[:0:0]
+ for _, t1 := range fields {
+ if !omitFieldForAwfulBoringCryptoKludge(t1) {
+ newFields = append(newFields, t1)
+ }
+ }
+ fields = newFields
+
for _, t1 := range fields {
dtypesym(t1.Type)
}
"debug/elf"
"debug/macho"
"debug/pe"
+ "encoding/binary"
"flag"
"fmt"
"go/format"
"internal/race"
"internal/testenv"
"io"
+ "io/fs"
"io/ioutil"
"log"
"os"
"cmd/internal/sys"
)
+ func init() {
+ // GOVCS defaults to public:git|hg,private:all,
+ // which breaks many tests here - they can't use non-git, non-hg VCS at all!
+ // Change to fully permissive.
+ // The tests of the GOVCS setting itself are in ../../testdata/script/govcs.txt.
+ os.Setenv("GOVCS", "*:all")
+ }
+
var (
- canRun = true // whether we can run go or ./testgo
canRace = false // whether we can run the race detector
canCgo = false // whether we can use cgo
canMSan = false // whether we can run the memory sanitizer
-
- exeSuffix string // ".exe" on Windows
-
- skipExternal = false // skip external tests
)
+ var exeSuffix string = func() string {
+ if runtime.GOOS == "windows" {
+ return ".exe"
+ }
+ return ""
+ }()
+
func tooSlow(t *testing.T) {
if testing.Short() {
// In -short mode; skip test, except run it on the {darwin,linux,windows}/amd64 builders.
if testenv.Builder() != "" && runtime.GOARCH == "amd64" && (runtime.GOOS == "linux" || runtime.GOOS == "darwin" || runtime.GOOS == "windows") {
return
}
+ t.Helper()
t.Skip("skipping test in -short mode")
}
}
- func init() {
- switch runtime.GOOS {
- case "android", "js":
- canRun = false
- case "darwin":
- // nothing to do
- case "ios":
- canRun = false
- case "linux":
- switch runtime.GOARCH {
- case "arm":
- // many linux/arm machines are too slow to run
- // the full set of external tests.
- skipExternal = true
- case "mips", "mipsle", "mips64", "mips64le":
- // Also slow.
- skipExternal = true
- if testenv.Builder() != "" {
- // On the builders, skip the cmd/go
- // tests. They're too slow and already
- // covered by other ports. There's
- // nothing os/arch specific in the
- // tests.
- canRun = false
- }
- }
- case "freebsd":
- switch runtime.GOARCH {
- case "arm":
- // many freebsd/arm machines are too slow to run
- // the full set of external tests.
- skipExternal = true
- canRun = false
- }
- case "plan9":
- switch runtime.GOARCH {
- case "arm":
- // many plan9/arm machines are too slow to run
- // the full set of external tests.
- skipExternal = true
- }
- case "windows":
- exeSuffix = ".exe"
- }
- }
-
// testGOROOT is the GOROOT to use when running testgo, a cmd/go binary
// build from this process's current GOROOT, but run from a different
// (temp) directory.
}
testGOCACHE = cache.DefaultDir()
- if canRun {
+ if testenv.HasGoBuild() {
testBin = filepath.Join(testTmpDir, "testbin")
if err := os.Mkdir(testBin, 0777); err != nil {
log.Fatal(err)
cmd.Stderr = new(strings.Builder)
if out, err := cmd.Output(); err != nil {
fmt.Fprintf(os.Stderr, "running testgo failed: %v\n%s", err, cmd.Stderr)
- canRun = false
+ os.Exit(2)
} else {
canCgo, err = strconv.ParseBool(strings.TrimSpace(string(out)))
if err != nil {
func testgo(t *testing.T) *testgoData {
t.Helper()
testenv.MustHaveGoBuild(t)
-
- if skipExternal {
- t.Skipf("skipping external tests on %s/%s", runtime.GOOS, runtime.GOARCH)
- }
+ testenv.SkipIfShortAndSlow(t)
return &testgoData{t: t}
}
// returning exit status.
func (tg *testgoData) doRun(args []string) error {
tg.t.Helper()
- if !canRun {
- panic("testgoData.doRun called but canRun false")
- }
if tg.inParallel {
for _, arg := range args {
if strings.HasPrefix(arg, "testdata") || strings.HasPrefix(arg, "./testdata") {
func removeAll(dir string) error {
// module cache has 0444 directories;
// make them writable in order to remove content.
- filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
// chmod not only directories, but also things that we couldn't even stat
// due to permission errors: they may also be unreadable directories.
if err != nil || info.IsDir() {
srcdir := filepath.Join(testGOROOT, copydir)
tg.tempDir(filepath.Join("goroot", copydir))
err := filepath.Walk(srcdir,
- func(path string, info os.FileInfo, err error) error {
+ func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if _, err := os.Stat(file); err != nil {
t.Fatalf("cannot find .Export result %s: %v", file, err)
}
+
+ tg.run("list", "-export", "-f", "{{.BuildID}}", "strings")
+ buildID := strings.TrimSpace(tg.stdout.String())
+ if buildID == "" {
+ t.Fatalf(".BuildID with -export was empty")
+ }
+
+ tg.run("tool", "buildid", file)
+ toolBuildID := strings.TrimSpace(tg.stdout.String())
+ if buildID != toolBuildID {
+ t.Fatalf(".BuildID with -export %q disagrees with 'go tool buildid' %q", buildID, toolBuildID)
+ }
}
// Issue 4096. Validate the output of unsuccessful go install foo/quxx.
tg.grepStdout("p2: false", "p2 listed as BinaryOnly")
}
-// Issue 16050.
-func TestAlwaysLinkSysoFiles(t *testing.T) {
+// Issue 16050 and 21884.
+func TestLinkSysoFiles(t *testing.T) {
+ if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
+ t.Skip("not linux/amd64")
+ }
+
tg := testgo(t)
defer tg.cleanup()
tg.parallel()
tg.setenv("CGO_ENABLED", "0")
tg.run("list", "-f", "{{.SysoFiles}}", "syso")
tg.grepStdout("a.syso", "missing syso file with CGO_ENABLED=0")
+
+ tg.setenv("CGO_ENABLED", "1")
+ tg.run("list", "-msan", "-f", "{{.SysoFiles}}", "syso")
+ tg.grepStdoutNot("a.syso", "unexpected syso file with -msan")
}
// Issue 16120.
tg.run("build", "-o", exe, "p")
}
- func copyFile(src, dst string, perm os.FileMode) error {
+ func copyFile(src, dst string, perm fs.FileMode) error {
sf, err := os.Open(src)
if err != nil {
return err
platform := fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)
switch platform {
- case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x",
+ case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/riscv64", "linux/s390x",
"android/amd64", "android/arm", "android/arm64", "android/386",
"freebsd/amd64",
"windows/386", "windows/amd64", "windows/arm":
if (dc & pe.IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) == 0 {
t.Error("IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE flag is not set")
}
+ if useCgo {
+ // Test that only one symbol is exported (#40795).
+ // PIE binaries don´t require .edata section but unfortunately
+ // binutils doesn´t generate a .reloc section unless there is
+ // at least one symbol exported.
+ // See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
+ section := f.Section(".edata")
+ if section == nil {
+ t.Fatalf(".edata section is not present")
+ }
+ // TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
+ type IMAGE_EXPORT_DIRECTORY struct {
+ _ [2]uint32
+ _ [2]uint16
+ _ [2]uint32
+ NumberOfFunctions uint32
+ NumberOfNames uint32
+ _ [3]uint32
+ }
+ var e IMAGE_EXPORT_DIRECTORY
+ if err := binary.Read(section.Open(), binary.LittleEndian, &e); err != nil {
+ t.Fatalf("binary.Read failed: %v", err)
+ }
+
+ // Only _cgo_dummy_export should be exported
+ if e.NumberOfFunctions != 1 {
+ t.Fatalf("got %d exported functions; want 1", e.NumberOfFunctions)
+ }
+ if e.NumberOfNames != 1 {
+ t.Fatalf("got %d exported names; want 1", e.NumberOfNames)
+ }
+ }
default:
panic("unreachable")
}
"go/build"
"go/scanner"
"go/token"
+ "io/fs"
"io/ioutil"
"os"
+ "path"
pathpkg "path"
"path/filepath"
"runtime"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
+ "cmd/go/internal/fsys"
"cmd/go/internal/modinfo"
"cmd/go/internal/modload"
"cmd/go/internal/par"
ConflictDir string `json:",omitempty"` // Dir is hidden by this other directory
ForTest string `json:",omitempty"` // package is only for use in named test
Export string `json:",omitempty"` // file containing export data (set by go list -export)
+ BuildID string `json:",omitempty"` // build ID of the compiled package (set by go list -export)
Module *modinfo.ModulePublic `json:",omitempty"` // info about package's module, if any
Match []string `json:",omitempty"` // command-line patterns matching this package
Goroot bool `json:",omitempty"` // is this package found in the Go root?
SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
SysoFiles []string `json:",omitempty"` // .syso system object files added to package
+ // Embedded files
+ EmbedPatterns []string `json:",omitempty"` // //go:embed patterns
+ EmbedFiles []string `json:",omitempty"` // files and directories matched by EmbedPatterns
+
// Cgo directives
CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler
CgoCPPFLAGS []string `json:",omitempty"` // cgo: flags for C preprocessor
// Test information
// If you add to this list you MUST add to p.AllFiles (below) too.
// Otherwise file name security lists will not apply to any new additions.
- TestGoFiles []string `json:",omitempty"` // _test.go files in package
- TestImports []string `json:",omitempty"` // imports from TestGoFiles
- XTestGoFiles []string `json:",omitempty"` // _test.go files outside package
- XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
+ TestGoFiles []string `json:",omitempty"` // _test.go files in package
+ TestImports []string `json:",omitempty"` // imports from TestGoFiles
+ TestEmbedPatterns []string `json:",omitempty"` // //go:embed patterns
+ TestEmbedFiles []string `json:",omitempty"` // //files matched by EmbedPatterns
+ XTestGoFiles []string `json:",omitempty"` // _test.go files outside package
+ XTestImports []string `json:",omitempty"` // imports from XTestGoFiles
+ XTestEmbedPatterns []string `json:",omitempty"` // //go:embed patterns
+ XTestEmbedFiles []string `json:",omitempty"` // //files matched by EmbedPatterns
}
// AllFiles returns the names of all the files considered for the package.
// The go/build package filtered others out (like foo_wrongGOARCH.s)
// and that's OK.
func (p *Package) AllFiles() []string {
- return str.StringList(
+ files := str.StringList(
p.GoFiles,
p.CgoFiles,
// no p.CompiledGoFiles, because they are from GoFiles or generated by us
p.TestGoFiles,
p.XTestGoFiles,
)
+
+ // EmbedFiles may overlap with the other files.
+ // Dedup, but delay building the map as long as possible.
+ // Only files in the current directory (no slash in name)
+ // need to be checked against the files variable above.
+ var have map[string]bool
+ for _, file := range p.EmbedFiles {
+ if !strings.Contains(file, "/") {
+ if have == nil {
+ have = make(map[string]bool)
+ for _, file := range files {
+ have[file] = true
+ }
+ }
+ if have[file] {
+ continue
+ }
+ }
+ files = append(files, file)
+ }
+ return files
}
// Desc returns the package "description", for use in b.showOutput.
GobinSubdir bool // install target would be subdir of GOBIN
BuildInfo string // add this info to package main
TestmainGo *[]byte // content for _testmain.go
+ Embed map[string][]string // //go:embed comment mapping
Asmflags []string // -asmflags for this package
Gcflags []string // -gcflags for this package
// package's source files themselves (scanner errors).
//
// TODO(matloob): Perhaps make each of those the errors in the first group
- // (including modload.ImportMissingError, and the corresponding
- // "cannot find package %q in any of" GOPATH-mode error
+ // (including modload.ImportMissingError, ImportMissingSumError, and the
+ // corresponding "cannot find package %q in any of" GOPATH-mode error
// produced in build.(*Context).Import; modload.AmbiguousImportError,
// and modload.PackageNotInModuleError; and the malformed module path errors
// produced in golang.org/x/mod/module.CheckMod) implement an interface
p.SwigFiles = pp.SwigFiles
p.SwigCXXFiles = pp.SwigCXXFiles
p.SysoFiles = pp.SysoFiles
+ if cfg.BuildMSan {
+ // There's no way for .syso files to be built both with and without
+ // support for memory sanitizer. Assume they are built without,
+ // and drop them.
+ p.SysoFiles = nil
+ }
p.CgoCFLAGS = pp.CgoCFLAGS
p.CgoCPPFLAGS = pp.CgoCPPFLAGS
p.CgoCXXFLAGS = pp.CgoCXXFLAGS
p.TestImports = nil
p.XTestImports = nil
}
+ p.EmbedPatterns = pp.EmbedPatterns
+ p.TestEmbedPatterns = pp.TestEmbedPatterns
+ p.XTestEmbedPatterns = pp.XTestEmbedPatterns
}
// A PackageError describes an error loading information about a package.
var (
_ ImportPathError = (*importError)(nil)
_ ImportPathError = (*modload.ImportMissingError)(nil)
+ _ ImportPathError = (*modload.ImportMissingSumError)(nil)
)
type importError struct {
// loadPackageData have completed. The preloader will not make any new calls
// to loadPackageData.
func (pre *preload) flush() {
+ // flush is usually deferred.
+ // Don't hang program waiting for workers on panic.
+ if v := recover(); v != nil {
+ panic(v)
+ }
+
close(pre.cancel)
for i := 0; i < preloadWorkerCount; i++ {
pre.sema <- struct{}{}
func isDir(path string) bool {
return isDirCache.Do(path, func() interface{} {
- fi, err := os.Stat(path)
+ fi, err := fsys.Stat(path)
return err == nil && fi.IsDir()
}).(bool)
}
p.setLoadPackageDataError(err, path, stk, importPos)
}
+ p.EmbedFiles, p.Internal.Embed, err = p.resolveEmbed(p.EmbedPatterns)
+ if err != nil {
+ setError(err)
+ }
+
useBindir := p.Name == "main"
if !p.Standard {
switch cfg.BuildBuildmode {
}
}
+ // ResolveEmbed resolves //go:embed patterns and returns only the file list.
+ // For use by go list to compute p.TestEmbedFiles and p.XTestEmbedFiles.
+ func (p *Package) ResolveEmbed(patterns []string) []string {
+ files, _, _ := p.resolveEmbed(patterns)
+ return files
+ }
+
+ // resolveEmbed resolves //go:embed patterns to precise file lists.
+ // It sets files to the list of unique files matched (for go list),
+ // and it sets pmap to the more precise mapping from
+ // patterns to files.
+ // TODO(rsc): All these messages need position information for better error reports.
+ func (p *Package) resolveEmbed(patterns []string) (files []string, pmap map[string][]string, err error) {
+ pmap = make(map[string][]string)
+ have := make(map[string]int)
+ dirOK := make(map[string]bool)
+ pid := 0 // pattern ID, to allow reuse of have map
+ for _, pattern := range patterns {
+ pid++
+
+ // Check pattern is valid for //go:embed.
+ if _, err := path.Match(pattern, ""); err != nil || !validEmbedPattern(pattern) {
+ return nil, nil, fmt.Errorf("pattern %s: invalid pattern syntax", pattern)
+ }
+
+ // Glob to find matches.
+ match, err := fsys.Glob(p.Dir + string(filepath.Separator) + filepath.FromSlash(pattern))
+ if err != nil {
+ return nil, nil, fmt.Errorf("pattern %s: %v", pattern, err)
+ }
+
+ // Filter list of matches down to the ones that will still exist when
+ // the directory is packaged up as a module. (If p.Dir is in the module cache,
+ // only those files exist already, but if p.Dir is in the current module,
+ // then there may be other things lying around, like symbolic links or .git directories.)
+ var list []string
+ for _, file := range match {
+ rel := filepath.ToSlash(file[len(p.Dir)+1:]) // file, relative to p.Dir
+
+ what := "file"
+ info, err := fsys.Lstat(file)
+ if err != nil {
+ return nil, nil, err
+ }
+ if info.IsDir() {
+ what = "directory"
+ }
+
+ // Check that directories along path do not begin a new module
+ // (do not contain a go.mod).
+ for dir := file; len(dir) > len(p.Dir)+1 && !dirOK[dir]; dir = filepath.Dir(dir) {
+ if _, err := fsys.Stat(filepath.Join(dir, "go.mod")); err == nil {
+ return nil, nil, fmt.Errorf("pattern %s: cannot embed %s %s: in different module", pattern, what, rel)
+ }
+ if dir != file {
+ if info, err := fsys.Lstat(dir); err == nil && !info.IsDir() {
+ return nil, nil, fmt.Errorf("pattern %s: cannot embed %s %s: in non-directory %s", pattern, what, rel, dir[len(p.Dir)+1:])
+ }
+ }
+ dirOK[dir] = true
+ if elem := filepath.Base(dir); isBadEmbedName(elem) {
+ if dir == file {
+ return nil, nil, fmt.Errorf("pattern %s: cannot embed %s %s: invalid name %s", pattern, what, rel, elem)
+ } else {
+ return nil, nil, fmt.Errorf("pattern %s: cannot embed %s %s: in invalid directory %s", pattern, what, rel, elem)
+ }
+ }
+ }
+
+ switch {
+ default:
+ return nil, nil, fmt.Errorf("pattern %s: cannot embed irregular file %s", pattern, rel)
+
+ case info.Mode().IsRegular():
+ if have[rel] != pid {
+ have[rel] = pid
+ list = append(list, rel)
+ }
+
+ case info.IsDir():
+ // Gather all files in the named directory, stopping at module boundaries
+ // and ignoring files that wouldn't be packaged into a module.
+ count := 0
+ err := fsys.Walk(file, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ rel := filepath.ToSlash(path[len(p.Dir)+1:])
+ if info.IsDir() {
+ if _, err := fsys.Stat(filepath.Join(path, "go.mod")); err == nil {
+ return filepath.SkipDir
+ }
+ return nil
+ }
+ if !info.Mode().IsRegular() {
+ return nil
+ }
+ if isBadEmbedName(info.Name()) {
+ // Ignore bad names, assuming they won't go into modules.
+ return nil
+ }
+ count++
+ if have[rel] != pid {
+ have[rel] = pid
+ list = append(list, rel)
+ }
+ return nil
+ })
+ if err != nil {
+ return nil, nil, err
+ }
+ if count == 0 {
+ return nil, nil, fmt.Errorf("pattern %s: cannot embed directory %s: contains no embeddable files", pattern, rel)
+ }
+ }
+ }
+
+ if len(list) == 0 {
+ return nil, nil, fmt.Errorf("pattern %s: no matching files found", pattern)
+ }
+ sort.Strings(list)
+ pmap[pattern] = list
+ }
+
+ for file := range have {
+ files = append(files, file)
+ }
+ sort.Strings(files)
+ return files, pmap, nil
+ }
+
+ func validEmbedPattern(pattern string) bool {
+ return pattern != "." && fs.ValidPath(pattern)
+ }
+
+ // isBadEmbedName reports whether name is the base name of a file that
+ // can't or won't be included in modules and therefore shouldn't be treated
+ // as existing for embedding.
+ func isBadEmbedName(name string) bool {
+ switch name {
+ // Version control directories won't be present in module.
+ case ".bzr", ".hg", ".git", ".svn":
+ return true
+ }
+ return false
+ }
+
// collectDeps populates p.Deps and p.DepsErrors by iterating over
// p.Internal.Imports.
//
if cfg.BuildContext.GOARCH != "arm64" {
return true
}
- case "darwin", "ios":
- if cfg.BuildContext.GOARCH == "arm64" {
- return true
- }
+ case "ios":
+ return true
}
// Currently build modes c-shared, pie (on systems that do not
// external linking mode, as of course does
// -ldflags=-linkmode=external. External linking mode forces
// an import of runtime/cgo.
+ // If there are multiple -linkmode options, the last one wins.
pieCgo := cfg.BuildBuildmode == "pie" && !sys.InternalLinkPIESupported(cfg.BuildContext.GOOS, cfg.BuildContext.GOARCH)
linkmodeExternal := false
if p != nil {
ldflags := BuildLdflags.For(p)
- for i, a := range ldflags {
- if a == "-linkmode=external" {
- linkmodeExternal = true
- }
- if a == "-linkmode" && i+1 < len(ldflags) && ldflags[i+1] == "external" {
+ for i := len(ldflags) - 1; i >= 0; i-- {
+ a := ldflags[i]
+ if a == "-linkmode=external" ||
+ a == "-linkmode" && i+1 < len(ldflags) && ldflags[i+1] == "external" {
linkmodeExternal = true
+ break
+ } else if a == "-linkmode=internal" ||
+ a == "-linkmode" && i+1 < len(ldflags) && ldflags[i+1] == "internal" {
+ break
}
}
}
if strings.HasSuffix(p, ".go") {
// We need to test whether the path is an actual Go file and not a
// package path or pattern ending in '.go' (see golang.org/issue/34653).
- if fi, err := os.Stat(p); err == nil && !fi.IsDir() {
+ if fi, err := fsys.Stat(p); err == nil && !fi.IsDir() {
return []*Package{GoFilesPackage(ctx, patterns)}
}
}
// to make it look like this is a standard package or
// command directory. So that local imports resolve
// consistently, the files must all be in the same directory.
- var dirent []os.FileInfo
+ var dirent []fs.FileInfo
var dir string
for _, file := range gofiles {
- fi, err := os.Stat(file)
+ fi, err := fsys.Stat(file)
if err != nil {
base.Fatalf("%s", err)
}
}
dirent = append(dirent, fi)
}
- ctxt.ReadDir = func(string) ([]os.FileInfo, error) { return dirent, nil }
+ ctxt.ReadDir = func(string) ([]fs.FileInfo, error) { return dirent, nil }
if cfg.ModulesEnabled {
modload.ImportFromFiles(ctx, gofiles)
// These packages can use internal linking mode.
// Others trigger external mode.
var internalpkg = []string{
+ "crypto/internal/boring",
"crypto/x509",
"net",
"os/user",
case objabi.Hdarwin, objabi.Haix:
case objabi.Hwindows:
argv = addASLRargs(argv)
- // Work around binutils limitation that strips relocation table for dynamicbase.
- // See https://sourceware.org/bugzilla/show_bug.cgi?id=19011
- argv = append(argv, "-Wl,--export-all-symbols")
default:
// ELF.
if ctxt.UseRelro() {
if combineDwarf {
dsym := filepath.Join(*flagTmpdir, "go.dwarf")
- if out, err := exec.Command("dsymutil", "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil {
+ if out, err := exec.Command("xcrun", "dsymutil", "-f", *flagOutfile, "-o", dsym).CombinedOutput(); err != nil {
Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out)
}
// Remove STAB (symbolic debugging) symbols after we are done with them (by dsymutil).
// They contain temporary file paths and make the build not reproducible.
- if out, err := exec.Command("strip", "-S", *flagOutfile).CombinedOutput(); err != nil {
+ if out, err := exec.Command("xcrun", "strip", "-S", *flagOutfile).CombinedOutput(); err != nil {
Exitf("%s: running strip failed: %v\n%s", os.Args[0], err, out)
}
// Skip combining if `dsymutil` didn't generate a file. See #11994.
magic := uint32(c1)<<24 | uint32(c2)<<16 | uint32(c3)<<8 | uint32(c4)
if magic == 0x7f454c46 { // \x7F E L F
ldelf := func(ctxt *Link, f *bio.Reader, pkg string, length int64, pn string) {
- textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn, ehdr.flags)
+ textp, flags, err := loadelf.Load(ctxt.loader, ctxt.Arch, ctxt.IncVersion(), f, pkg, length, pn, ehdr.Flags)
if err != nil {
Errorf(nil, "%v", err)
return
}
- ehdr.flags = flags
+ ehdr.Flags = flags
ctxt.Textp = append(ctxt.Textp, textp...)
}
return ldhostobj(ldelf, ctxt.HeadType, f, pkg, length, pn, file)
if target.Arch.PtrSize == 8 {
rela := ldr.MakeSymbolUpdater(syms.Rela)
rela.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
- rela.AddUint64(target.Arch, ELF64_R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp))
+ rela.AddUint64(target.Arch, elf.R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp))
rela.AddUint64(target.Arch, 0)
} else {
rel := ldr.MakeSymbolUpdater(syms.Rel)
rel.AddAddrPlus(target.Arch, got.Sym(), int64(ldr.SymGot(s)))
- rel.AddUint32(target.Arch, ELF32_R_INFO(uint32(ldr.SymDynid(s)), elfRelocTyp))
+ rel.AddUint32(target.Arch, elf.R_INFO32(uint32(ldr.SymDynid(s)), elfRelocTyp))
}
} else if target.IsDarwin() {
leg := ldr.MakeSymbolUpdater(syms.LinkEditGOT)
"time"
)
+import "crypto/internal/boring"
+
const urandomDevice = "/dev/urandom"
// Easy implementation: read from /dev/urandom.
// This is sufficient on Linux, OS X, and FreeBSD.
func init() {
+ if boring.Enabled {
+ Reader = boring.RandReader
+ return
+ }
if runtime.GOOS == "plan9" {
Reader = newReader(nil)
} else {
// urandom-style randomness.
var altGetRandom func([]byte) (ok bool)
+ func warnBlocked() {
+ println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
+ }
+
func (r *devReader) Read(b []byte) (n int, err error) {
+ boring.Unreachable()
if atomic.CompareAndSwapInt32(&r.used, 0, 1) {
// First use of randomness. Start timer to warn about
// being blocked on entropy not being available.
}
func (r *reader) Read(b []byte) (n int, err error) {
+ boring.Unreachable()
r.mu.Lock()
defer r.mu.Unlock()
n = len(b)
package tls
+import "crypto/internal/boring"
+
import (
"crypto"
"crypto/aes"
// flags is a bitmask of the suite* values, above.
flags int
cipher func(key, iv []byte, isRead bool) interface{}
- mac func(version uint16, macKey []byte) macFunction
+ mac func(key []byte) hash.Hash
aead func(key, fixedNonce []byte) aead
}
return cipher.NewCBCEncrypter(block, iv)
}
- // macSHA1 returns a macFunction for the given protocol version.
- func macSHA1(version uint16, key []byte) macFunction {
+ // macSHA1 returns a SHA-1 based constant time MAC.
+ func macSHA1(key []byte) hash.Hash {
- return hmac.New(newConstantTimeHash(sha1.New), key)
+ h := sha1.New
+ // The BoringCrypto SHA1 does not have a constant-time
+ // checksum function, so don't try to use it.
+ if !boring.Enabled {
+ h = newConstantTimeHash(h)
+ }
- return tls10MAC{h: hmac.New(h, key)}
++ return hmac.New(h, key)
}
- // macSHA256 returns a SHA-256 based MAC. These are only supported in TLS 1.2
- // so the given version is ignored.
- func macSHA256(version uint16, key []byte) macFunction {
- return tls10MAC{h: hmac.New(sha256.New, key)}
- }
-
- type macFunction interface {
- // Size returns the length of the MAC.
- Size() int
- // MAC appends the MAC of (seq, header, data) to out. The extra data is fed
- // into the MAC after obtaining the result to normalize timing. The result
- // is only valid until the next invocation of MAC as the buffer is reused.
- MAC(seq, header, data, extra []byte) []byte
+ // macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
+ // is currently only used in disabled-by-default cipher suites.
+ func macSHA256(key []byte) hash.Hash {
+ return hmac.New(sha256.New, key)
}
type aead interface {
return result, err
}
+type gcmtls interface {
+ NewGCMTLS() (cipher.AEAD, error)
+}
+
func aeadAESGCM(key, noncePrefix []byte) aead {
if len(noncePrefix) != noncePrefixLength {
panic("tls: internal error: wrong nonce length")
if err != nil {
panic(err)
}
- aead, err := cipher.NewGCM(aes)
+ var aead cipher.AEAD
+ if aesTLS, ok := aes.(gcmtls); ok {
+ aead, err = aesTLS.NewGCMTLS()
+ } else {
+ boring.Unreachable()
+ aead, err = cipher.NewGCM(aes)
+ }
if err != nil {
panic(err)
}
func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
+ boring.Unreachable()
return func() hash.Hash {
return &cthWrapper{h().(constantTimeHash)}
}
}
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
- type tls10MAC struct {
- h hash.Hash
- buf []byte
- }
-
- func (s tls10MAC) Size() int {
- return s.h.Size()
- }
-
- // MAC is guaranteed to take constant time, as long as
- // len(seq)+len(header)+len(data)+len(extra) is constant. extra is not fed into
- // the MAC, but is only provided to make the timing profile constant.
- func (s tls10MAC) MAC(seq, header, data, extra []byte) []byte {
- s.h.Reset()
- s.h.Write(seq)
- s.h.Write(header)
- s.h.Write(data)
- res := s.h.Sum(s.buf[:0])
+ func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte {
+ h.Reset()
+ h.Write(seq)
+ h.Write(header)
+ h.Write(data)
+ res := h.Sum(out)
if extra != nil {
- s.h.Write(extra)
+ h.Write(extra)
}
return res
}
import (
"bytes"
"container/list"
+ "context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
// hash function associated with the Ed25519 signature scheme.
var directSigning crypto.Hash = 0
-// supportedSignatureAlgorithms contains the signature and hash algorithms that
+// defaultSupportedSignatureAlgorithms contains the signature and hash algorithms that
// the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+
// CertificateRequest. The two fields are merged to match with TLS 1.3.
// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc.
-var supportedSignatureAlgorithms = []SignatureScheme{
+var defaultSupportedSignatureAlgorithms = []SignatureScheme{
PSSWithSHA256,
ECDSAWithP256AndSHA256,
Ed25519,
type ClientAuthType int
const (
+ // NoClientCert indicates that no client certificate should be requested
+ // during the handshake, and if any certificates are sent they will not
+ // be verified.
NoClientCert ClientAuthType = iota
+ // RequestClientCert indicates that a client certificate should be requested
+ // during the handshake, but does not require that the client send any
+ // certificates.
RequestClientCert
+ // RequireAnyClientCert indicates that a client certificate should be requested
+ // during the handshake, and that at least one certificate is required to be
+ // sent by the client, but that certificate is not required to be valid.
RequireAnyClientCert
+ // VerifyClientCertIfGiven indicates that a client certificate should be requested
+ // during the handshake, but does not require that the client sends a
+ // certificate. If the client does send a certificate it is required to be
+ // valid.
VerifyClientCertIfGiven
+ // RequireAndVerifyClientCert indicates that a client certificate should be requested
+ // during the handshake, and that at least one valid certificate is required
+ // to be sent by the client.
RequireAndVerifyClientCert
)
// config is embedded by the GetCertificate or GetConfigForClient caller,
// for use with SupportsCertificate.
config *Config
+
+ // ctx is the context of the handshake that is in progress.
+ ctx context.Context
+ }
+
+ // Context returns the context of the handshake that is in progress.
+ // This context is a child of the context passed to HandshakeContext,
+ // if any, and is canceled when the handshake concludes.
+ func (c *ClientHelloInfo) Context() context.Context {
+ return c.ctx
}
// CertificateRequestInfo contains information from a server's
// Version is the TLS version that was negotiated for this connection.
Version uint16
+
+ // ctx is the context of the handshake that is in progress.
+ ctx context.Context
+ }
+
+ // Context returns the context of the handshake that is in progress.
+ // This context is a child of the context passed to HandshakeContext,
+ // if any, and is canceled when the handshake concludes.
+ func (c *CertificateRequestInfo) Context() context.Context {
+ return c.ctx
}
// RenegotiationSupport enumerates the different levels of support for TLS
}
func (c *Config) cipherSuites() []uint16 {
+ if needFIPS() {
+ return fipsCipherSuites(c)
+ }
s := c.CipherSuites
if s == nil {
s = defaultCipherSuites()
func (c *Config) supportedVersions() []uint16 {
versions := make([]uint16, 0, len(supportedVersions))
for _, v := range supportedVersions {
+ if needFIPS() && (v < fipsMinVersion(c) || v > fipsMaxVersion(c)) {
+ continue
+ }
if c != nil && c.MinVersion != 0 && v < c.MinVersion {
continue
}
var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521}
func (c *Config) curvePreferences() []CurveID {
+ if needFIPS() {
+ return fipsCurvePreferences(c)
+ }
if c == nil || len(c.CurvePreferences) == 0 {
return defaultCurvePreferences
}
if err != nil {
continue
}
- if len(x509Cert.Subject.CommonName) > 0 {
+ // If SANs are *not* present, some clients will consider the certificate
+ // valid for the name in the Common Name.
+ if x509Cert.Subject.CommonName != "" && len(x509Cert.DNSNames) == 0 {
c.NameToCertificate[x509Cert.Subject.CommonName] = cert
}
for _, san := range x509Cert.DNSNames {
hasGCMAsm = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X
)
- if hasGCMAsm {
+ if hasGCMAsm || boringEnabled {
+ // If BoringCrypto is enabled, always prioritize AES-GCM.
// If AES-GCM hardware is provided then prioritise AES-GCM
// cipher suites.
topCipherSuites = []uint16{
import (
"bytes"
+ "context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/x509"
"errors"
"fmt"
+ "hash"
"io"
"net"
"strings"
type clientHandshakeState struct {
c *Conn
+ ctx context.Context
serverHello *serverHelloMsg
hello *clientHelloMsg
suite *cipherSuite
}
if hello.vers >= VersionTLS12 {
- hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms
+ hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
+ }
+ if testingOnlyForceClientHelloSignatureAlgorithms != nil {
+ hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
}
var params ecdheParameters
return hello, params, nil
}
- func (c *Conn) clientHandshake() (err error) {
+ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
if c.config == nil {
c.config = defaultConfig()
}
if c.vers == VersionTLS13 {
hs := &clientHandshakeStateTLS13{
c: c,
+ ctx: ctx,
serverHello: serverHello,
hello: hello,
ecdheParams: ecdheParams,
hs := &clientHandshakeState{
c: c,
+ ctx: ctx,
serverHello: serverHello,
hello: hello,
session: session,
certRequested = true
hs.finishedHash.Write(certReq.marshal())
- cri := certificateRequestInfoFromMsg(c.vers, certReq)
+ cri := certificateRequestInfoFromMsg(hs.ctx, c.vers, certReq)
if chainToSend, err = c.getClientCertificate(cri); err != nil {
c.sendAlert(alertInternalError)
return err
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
var clientCipher, serverCipher interface{}
- var clientHash, serverHash macFunction
+ var clientHash, serverHash hash.Hash
if hs.suite.cipher != nil {
clientCipher = hs.suite.cipher(clientKey, clientIV, false /* not for reading */)
- clientHash = hs.suite.mac(c.vers, clientMAC)
+ clientHash = hs.suite.mac(clientMAC)
serverCipher = hs.suite.cipher(serverKey, serverIV, true /* for reading */)
- serverHash = hs.suite.mac(c.vers, serverMAC)
+ serverHash = hs.suite.mac(serverMAC)
} else {
clientCipher = hs.suite.aead(clientKey, clientIV)
serverCipher = hs.suite.aead(serverKey, serverIV)
if !c.config.InsecureSkipVerify {
opts := x509.VerifyOptions{
+ IsBoring: isBoringCertificate,
+
Roots: c.config.RootCAs,
CurrentTime: c.config.time(),
DNSName: c.config.ServerName,
// certificateRequestInfoFromMsg generates a CertificateRequestInfo from a TLS
// <= 1.2 CertificateRequest, making an effort to fill in missing information.
- func certificateRequestInfoFromMsg(vers uint16, certReq *certificateRequestMsg) *CertificateRequestInfo {
+ func certificateRequestInfoFromMsg(ctx context.Context, vers uint16, certReq *certificateRequestMsg) *CertificateRequestInfo {
cri := &CertificateRequestInfo{
AcceptableCAs: certReq.certificateAuthorities,
Version: vers,
+ ctx: ctx,
}
var rsaAvail, ecAvail bool
import (
"bytes"
+ "context"
"crypto"
"crypto/hmac"
"crypto/rsa"
type clientHandshakeStateTLS13 struct {
c *Conn
+ ctx context.Context
serverHello *serverHelloMsg
hello *clientHelloMsg
ecdheParams ecdheParameters
func (hs *clientHandshakeStateTLS13) handshake() error {
c := hs.c
+ if needFIPS() {
+ return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
+ }
+
// The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
// sections 4.1.2 and 4.1.3.
if c.handshakes > 0 {
}
// See RFC 8446, Section 4.4.3.
- if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) {
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: certificate used with invalid signature algorithm")
}
AcceptableCAs: hs.certReq.certificateAuthorities,
SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
Version: c.vers,
+ ctx: hs.ctx,
})
if err != nil {
return err
package tls
import (
+ "context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/x509"
"errors"
"fmt"
+ "hash"
"io"
"sync/atomic"
"time"
// It's discarded once the handshake has completed.
type serverHandshakeState struct {
c *Conn
+ ctx context.Context
clientHello *clientHelloMsg
hello *serverHelloMsg
suite *cipherSuite
}
// serverHandshake performs a TLS handshake as a server.
- func (c *Conn) serverHandshake() error {
- clientHello, err := c.readClientHello()
+ func (c *Conn) serverHandshake(ctx context.Context) error {
+ clientHello, err := c.readClientHello(ctx)
if err != nil {
return err
}
if c.vers == VersionTLS13 {
hs := serverHandshakeStateTLS13{
c: c,
+ ctx: ctx,
clientHello: clientHello,
}
return hs.handshake()
hs := serverHandshakeState{
c: c,
+ ctx: ctx,
clientHello: clientHello,
}
return hs.handshake()
}
// readClientHello reads a ClientHello message and selects the protocol version.
- func (c *Conn) readClientHello() (*clientHelloMsg, error) {
+ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
msg, err := c.readHandshake()
if err != nil {
return nil, err
var configForClient *Config
originalConfig := c.config
if c.config.GetConfigForClient != nil {
- chi := clientHelloInfo(c, clientHello)
+ chi := clientHelloInfo(ctx, c, clientHello)
if configForClient, err = c.config.GetConfigForClient(chi); err != nil {
c.sendAlert(alertInternalError)
return nil, err
}
}
- hs.cert, err = c.config.getCertificate(clientHelloInfo(c, hs.clientHello))
+ hs.cert, err = c.config.getCertificate(clientHelloInfo(hs.ctx, c, hs.clientHello))
if err != nil {
if err == errNoCertificates {
c.sendAlert(alertUnrecognizedName)
}
if c.vers >= VersionTLS12 {
certReq.hasSignatureAlgorithm = true
- certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
+ certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
}
// An empty list of certificateAuthorities signals to
keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
var clientCipher, serverCipher interface{}
- var clientHash, serverHash macFunction
+ var clientHash, serverHash hash.Hash
if hs.suite.aead == nil {
clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
- clientHash = hs.suite.mac(c.vers, clientMAC)
+ clientHash = hs.suite.mac(clientMAC)
serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
- serverHash = hs.suite.mac(c.vers, serverMAC)
+ serverHash = hs.suite.mac(serverMAC)
} else {
clientCipher = hs.suite.aead(clientKey, clientIV)
serverCipher = hs.suite.aead(serverKey, serverIV)
if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
opts := x509.VerifyOptions{
+ IsBoring: isBoringCertificate,
+
Roots: c.config.ClientCAs,
CurrentTime: c.config.time(),
Intermediates: x509.NewCertPool(),
return nil
}
- func clientHelloInfo(c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
+ func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
supportedVersions := clientHello.supportedVersions
if len(clientHello.supportedVersions) == 0 {
supportedVersions = supportedVersionsFromMax(clientHello.vers)
SupportedVersions: supportedVersions,
Conn: c.conn,
config: c.config,
+ ctx: ctx,
}
}
import (
"bytes"
+ "context"
"crypto"
"crypto/hmac"
"crypto/rsa"
type serverHandshakeStateTLS13 struct {
c *Conn
+ ctx context.Context
clientHello *clientHelloMsg
hello *serverHelloMsg
sentDummyCCS bool
func (hs *serverHandshakeStateTLS13) handshake() error {
c := hs.c
+ if needFIPS() {
+ return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
+ }
+
// For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2.
if err := hs.processClientHello(); err != nil {
return err
return c.sendAlert(alertMissingExtension)
}
- certificate, err := c.config.getCertificate(clientHelloInfo(c, hs.clientHello))
+ certificate, err := c.config.getCertificate(clientHelloInfo(hs.ctx, c, hs.clientHello))
if err != nil {
if err == errNoCertificates {
c.sendAlert(alertUnrecognizedName)
certReq := new(certificateRequestMsgTLS13)
certReq.ocspStapling = true
certReq.scts = true
- certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
+ certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
if c.config.ClientCAs != nil {
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
}
}
// See RFC 8446, Section 4.4.3.
- if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) {
+ if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: client certificate used with invalid signature algorithm")
}
return msg
}
+ func (se SystemRootsError) Unwrap() error { return se.Err }
+
// errNotParsed is returned when a certificate without ASN.1 contents is
// verified. Platform-specific verification needs the ASN.1 contents.
var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificate")
// VerifyOptions contains parameters for Certificate.Verify.
type VerifyOptions struct {
+ // IsBoring is a validity check for BoringCrypto.
+ // If not nil, it will be called to check whether a given certificate
+ // can be used for constructing verification chains.
+ IsBoring func(*Certificate) bool
+
// DNSName, if set, is checked against the leaf certificate with
// Certificate.VerifyHostname or the platform verifier.
DNSName string
}
}
+ if opts.IsBoring != nil && !opts.IsBoring(c) {
+ // IncompatibleUsage is not quite right here,
+ // but it's also the "no chains found" error
+ // and is close enough.
+ return CertificateInvalidError{c, IncompatibleUsage, ""}
+ }
+
return nil
}
if len(c.Raw) == 0 {
return nil, errNotParsed
}
- if opts.Intermediates != nil {
- for _, intermediate := range opts.Intermediates.certs {
- if len(intermediate.Raw) == 0 {
- return nil, errNotParsed
- }
+ for i := 0; i < opts.Intermediates.len(); i++ {
+ c, err := opts.Intermediates.cert(i)
+ if err != nil {
+ return nil, fmt.Errorf("crypto/x509: error fetching intermediate: %w", err)
+ }
+ if len(c.Raw) == 0 {
+ return nil, errNotParsed
}
}
}
}
- for _, rootNum := range opts.Roots.findPotentialParents(c) {
- considerCandidate(rootCertificate, opts.Roots.certs[rootNum])
+ for _, root := range opts.Roots.findPotentialParents(c) {
+ considerCandidate(rootCertificate, root)
}
- for _, intermediateNum := range opts.Intermediates.findPotentialParents(c) {
- considerCandidate(intermediateCertificate, opts.Intermediates.certs[intermediateNum])
+ for _, intermediate := range opts.Intermediates.findPotentialParents(c) {
+ considerCandidate(intermediateCertificate, intermediate)
}
if len(chains) > 0 {
"fmt"
"go/ast"
"go/doc"
- "go/parser"
"go/token"
"internal/goroot"
"internal/goversion"
"io"
+ "io/fs"
"io/ioutil"
"os"
"os/exec"
// filepath.EvalSymlinks.
HasSubdir func(root, dir string) (rel string, ok bool)
- // ReadDir returns a slice of os.FileInfo, sorted by Name,
+ // ReadDir returns a slice of fs.FileInfo, sorted by Name,
// describing the content of the named directory.
// If ReadDir is nil, Import uses ioutil.ReadDir.
- ReadDir func(dir string) ([]os.FileInfo, error)
+ ReadDir func(dir string) ([]fs.FileInfo, error)
// OpenFile opens a file (not a directory) for reading.
// If OpenFile is nil, Import uses os.Open.
}
// readDir calls ctxt.ReadDir (if not nil) or else ioutil.ReadDir.
- func (ctxt *Context) readDir(path string) ([]os.FileInfo, error) {
+ func (ctxt *Context) readDir(path string) ([]fs.FileInfo, error) {
if f := ctxt.ReadDir; f != nil {
return f(path)
}
CgoLDFLAGS []string // Cgo LDFLAGS directives
CgoPkgConfig []string // Cgo pkg-config directives
- // Dependency information
- Imports []string // import paths from GoFiles, CgoFiles
- ImportPos map[string][]token.Position // line information for Imports
-
// Test information
- TestGoFiles []string // _test.go files in package
+ TestGoFiles []string // _test.go files in package
+ XTestGoFiles []string // _test.go files outside package
+
+ // Dependency information
+ Imports []string // import paths from GoFiles, CgoFiles
+ ImportPos map[string][]token.Position // line information for Imports
TestImports []string // import paths from TestGoFiles
TestImportPos map[string][]token.Position // line information for TestImports
- XTestGoFiles []string // _test.go files outside package
XTestImports []string // import paths from XTestGoFiles
XTestImportPos map[string][]token.Position // line information for XTestImports
+
+ // //go:embed patterns found in Go source files
+ // For example, if a source file says
+ // //go:embed a* b.c
+ // then the list will contain those two strings as separate entries.
+ // (See package embed for more details about //go:embed.)
+ EmbedPatterns []string // patterns from GoFiles, CgoFiles
+ TestEmbedPatterns []string // patterns from TestGoFiles
+ XTestEmbedPatterns []string // patterns from XTestGoFiles
}
// IsCommand reports whether the package is considered a
var badGoError error
var Sfiles []string // files with ".S"(capital S)/.sx(capital s equivalent for case insensitive filesystems)
var firstFile, firstCommentFile string
+ var embeds, testEmbeds, xTestEmbeds []string
imported := make(map[string][]token.Position)
testImported := make(map[string][]token.Position)
xTestImported := make(map[string][]token.Position)
if d.IsDir() {
continue
}
- if (d.Mode() & os.ModeSymlink) != 0 {
- if fi, err := os.Stat(filepath.Join(p.Dir, d.Name())); err == nil && fi.IsDir() {
+ if d.Mode()&fs.ModeSymlink != 0 {
+ if ctxt.isDir(ctxt.joinPath(p.Dir, d.Name())) {
// Symlinks to directories are not source files.
continue
}
p.InvalidGoFiles = append(p.InvalidGoFiles, name)
}
- match, data, filename, err := ctxt.matchFile(p.Dir, name, allTags, &p.BinaryOnly)
+ info, err := ctxt.matchFile(p.Dir, name, allTags, &p.BinaryOnly, fset)
if err != nil {
badFile(err)
continue
}
- if !match {
+ if info == nil {
if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") {
// not due to build constraints - don't report
} else if ext == ".go" {
}
continue
}
+ data, filename := info.header, info.name
// Going to save the file. For non-Go files, can stop here.
switch ext {
continue
}
- pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
- if err != nil {
- badFile(err)
+ if info.parseErr != nil {
+ badFile(info.parseErr)
continue
}
+ pf := info.parsed
pkg := pf.Name.Name
if pkg == "documentation" {
}
// Record imports and information about cgo.
- type importPos struct {
- path string
- pos token.Pos
- }
- var fileImports []importPos
isCgo := false
- for _, decl := range pf.Decls {
- d, ok := decl.(*ast.GenDecl)
- if !ok {
- continue
- }
- for _, dspec := range d.Specs {
- spec, ok := dspec.(*ast.ImportSpec)
- if !ok {
+ for _, imp := range info.imports {
+ if imp.path == "C" {
+ if isTest {
+ badFile(fmt.Errorf("use of cgo in test %s not supported", filename))
continue
}
- quoted := spec.Path.Value
- path, err := strconv.Unquote(quoted)
- if err != nil {
- panic(fmt.Sprintf("%s: parser returned invalid quoted string: <%s>", filename, quoted))
- }
- fileImports = append(fileImports, importPos{path, spec.Pos()})
- if path == "C" {
- if isTest {
- badFile(fmt.Errorf("use of cgo in test %s not supported", filename))
- } else {
- cg := spec.Doc
- if cg == nil && len(d.Specs) == 1 {
- cg = d.Doc
- }
- if cg != nil {
- if err := ctxt.saveCgo(filename, p, cg); err != nil {
- badFile(err)
- }
- }
- isCgo = true
+ isCgo = true
+ if imp.doc != nil {
+ if err := ctxt.saveCgo(filename, p, imp.doc); err != nil {
+ badFile(err)
}
}
}
}
- var fileList *[]string
+ var fileList, embedList *[]string
var importMap map[string][]token.Position
switch {
case isCgo:
if ctxt.CgoEnabled {
fileList = &p.CgoFiles
importMap = imported
+ embedList = &embeds
} else {
// Ignore imports from cgo files if cgo is disabled.
fileList = &p.IgnoredGoFiles
case isXTest:
fileList = &p.XTestGoFiles
importMap = xTestImported
+ embedList = &xTestEmbeds
case isTest:
fileList = &p.TestGoFiles
importMap = testImported
+ embedList = &testEmbeds
default:
fileList = &p.GoFiles
importMap = imported
+ embedList = &embeds
}
*fileList = append(*fileList, name)
if importMap != nil {
- for _, imp := range fileImports {
+ for _, imp := range info.imports {
importMap[imp.path] = append(importMap[imp.path], fset.Position(imp.pos))
}
}
+ if embedList != nil {
+ *embedList = append(*embedList, info.embeds...)
+ }
}
for tag := range allTags {
}
sort.Strings(p.AllTags)
+ p.EmbedPatterns = uniq(embeds)
+ p.TestEmbedPatterns = uniq(testEmbeds)
+ p.XTestEmbedPatterns = uniq(xTestEmbeds)
+
p.Imports, p.ImportPos = cleanImports(imported)
p.TestImports, p.TestImportPos = cleanImports(testImported)
p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
return nil
}
+ func uniq(list []string) []string {
+ if list == nil {
+ return nil
+ }
+ out := make([]string, len(list))
+ copy(out, list)
+ sort.Strings(out)
+ uniq := out[:0]
+ for _, x := range out {
+ if len(uniq) == 0 || uniq[len(uniq)-1] != x {
+ uniq = append(uniq, x)
+ }
+ }
+ return uniq
+ }
+
var errNoModules = errors.New("not using modules")
// importGo checks whether it can use the go command to find the directory for path.
}
}
for {
- info, err := os.Stat(filepath.Join(parent, "go.mod"))
- if err == nil && !info.IsDir() {
- break
+ if f, err := ctxt.openFile(ctxt.joinPath(parent, "go.mod")); err == nil {
+ buf := make([]byte, 100)
+ _, err := f.Read(buf)
+ f.Close()
+ if err == nil || err == io.EOF {
+ // go.mod exists and is readable (is a file, not a directory).
+ break
+ }
}
d := filepath.Dir(parent)
if len(d) >= len(parent) {
// MatchFile considers the name of the file and may use ctxt.OpenFile to
// read some or all of the file's content.
func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
- match, _, _, err = ctxt.matchFile(dir, name, nil, nil)
- return
+ info, err := ctxt.matchFile(dir, name, nil, nil, nil)
+ return info != nil, err
}
var dummyPkg Package
+ // fileInfo records information learned about a file included in a build.
+ type fileInfo struct {
+ name string // full name including dir
+ header []byte
+ fset *token.FileSet
+ parsed *ast.File
+ parseErr error
+ imports []fileImport
+ embeds []string
+ embedErr error
+ }
+
+ type fileImport struct {
+ path string
+ pos token.Pos
+ doc *ast.CommentGroup
+ }
+
// matchFile determines whether the file with the given name in the given directory
// should be included in the package being constructed.
- // It returns the data read from the file.
+ // If the file should be included, matchFile returns a non-nil *fileInfo (and a nil error).
+ // Non-nil errors are reserved for unexpected problems.
+ //
// If name denotes a Go program, matchFile reads until the end of the
- // imports (and returns that data) even though it only considers text
- // until the first non-comment.
+ // imports and returns that section of the file in the fileInfo's header field,
+ // even though it only considers text until the first non-comment
+ // for +build lines.
+ //
// If allTags is non-nil, matchFile records any encountered build tag
// by setting allTags[tag] = true.
- func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binaryOnly *bool) (match bool, data []byte, filename string, err error) {
+ func (ctxt *Context) matchFile(dir, name string, allTags map[string]bool, binaryOnly *bool, fset *token.FileSet) (*fileInfo, error) {
if strings.HasPrefix(name, "_") ||
strings.HasPrefix(name, ".") {
- return
+ return nil, nil
}
i := strings.LastIndex(name, ".")
ext := name[i:]
if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
- return
+ return nil, nil
}
if ext != ".go" && fileListForExt(&dummyPkg, ext) == nil {
// skip
- return
+ return nil, nil
}
+ info := &fileInfo{name: ctxt.joinPath(dir, name), fset: fset}
if ext == ".syso" {
// binary, no reading
- match = true
- return
+ return info, nil
}
- filename = ctxt.joinPath(dir, name)
- f, err := ctxt.openFile(filename)
+ f, err := ctxt.openFile(info.name)
if err != nil {
- return
+ return nil, err
}
- if strings.HasSuffix(filename, ".go") {
- data, err = readImports(f, false, nil)
- if strings.HasSuffix(filename, "_test.go") {
+ if strings.HasSuffix(name, ".go") {
+ err = readGoInfo(f, info)
+ if strings.HasSuffix(name, "_test.go") {
binaryOnly = nil // ignore //go:binary-only-package comments in _test.go files
}
} else {
binaryOnly = nil // ignore //go:binary-only-package comments in non-Go sources
- data, err = readComments(f)
+ info.header, err = readComments(f)
}
f.Close()
if err != nil {
- err = fmt.Errorf("read %s: %v", filename, err)
- return
+ return nil, fmt.Errorf("read %s: %v", info.name, err)
}
// Look for +build comments to accept or reject the file.
- ok, sawBinaryOnly, err := ctxt.shouldBuild(data, allTags)
+ ok, sawBinaryOnly, err := ctxt.shouldBuild(info.header, allTags)
if err != nil {
- return // non-nil err
+ return nil, err
}
if !ok && !ctxt.UseAllFiles {
- return // nil err
+ return nil, nil
}
if binaryOnly != nil && sawBinaryOnly {
*binaryOnly = true
}
- match = true
- return
+
+ return info, nil
}
func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
// $GOARCH
// cgo (if cgo is enabled)
// !cgo (if cgo is disabled)
+// boringcrypto
// ctxt.Compiler
// !ctxt.Compiler
// tag (if tag is listed in ctxt.BuildTags or ctxt.ReleaseTags)
if ctxt.GOOS == "ios" && name == "darwin" {
return true
}
+ // Let applications know that the Go+BoringCrypto toolchain is in use.
+ if name == "boringcrypto" {
+ return true
+ }
// other tags
for _, tag := range ctxt.BuildTags {
"bytes"
"fmt"
"internal/testenv"
+ "io/fs"
"io/ioutil"
"os"
"path/filepath"
"runtime"
"sort"
- "strconv"
"strings"
"testing"
)
< context
< TIME;
- TIME, io, sort
+ TIME, io, path, sort
< io/fs;
# MATH is RUNTIME plus the basic math packages.
MATH
< math/rand;
+ MATH
+ < runtime/metrics;
+
MATH, unicode/utf8
< strconv;
os/signal, STR
< path/filepath
- < io/ioutil, os/exec
+ < io/ioutil, os/exec;
+
+ io/ioutil, os/exec, os/signal
< OS;
reflect !< OS;
# Bulk of the standard library must not use cgo.
# The prohibition stops at net and os/user.
- C !< fmt, go/types, CRYPTO-MATH;
+ C !< fmt, go/types;
CGO, OS
< plugin;
NET, log
< net/mail;
- # CRYPTO is core crypto algorithms - no cgo, fmt, net.
- # Unfortunately, stuck with reflect via encoding/binary.
- encoding/binary, golang.org/x/sys/cpu, hash
+ NONE < crypto/internal/boring/sig;
+ sync/atomic < crypto/internal/boring/fipstls;
+
+ encoding/binary, golang.org/x/sys/cpu, hash,
+ FMT, math/big,
+ CGO, crypto/internal/boring/sig, crypto/internal/boring/fipstls
< crypto
< crypto/subtle
< crypto/internal/subtle
< crypto/cipher
+ < encoding/asn1
+ < crypto/internal/boring
< crypto/aes, crypto/des, crypto/hmac, crypto/md5, crypto/rc4,
crypto/sha1, crypto/sha256, crypto/sha512
- < CRYPTO;
-
- CGO, fmt, net !< CRYPTO;
-
- # CRYPTO-MATH is core bignum-based crypto - no cgo, net; fmt now ok.
- CRYPTO, FMT, math/big
< crypto/rand
< crypto/internal/randutil
< crypto/ed25519/internal/edwards25519
< crypto/ed25519
- < encoding/asn1
< golang.org/x/crypto/cryptobyte/asn1
< golang.org/x/crypto/cryptobyte
< golang.org/x/crypto/curve25519
< crypto/dsa, crypto/elliptic, crypto/rsa
< crypto/ecdsa
- < CRYPTO-MATH;
+ < CRYPTO-BORING;
- CGO, net !< CRYPTO-MATH;
+ net !< CRYPTO-BORING;
# TLS, Prince of Dependencies.
- CGO, CRYPTO-MATH, NET, container/list, encoding/hex, encoding/pem
+ CGO, CRYPTO-BORING, NET, container/list, encoding/hex, encoding/pem
< golang.org/x/crypto/internal/subtle
< golang.org/x/crypto/chacha20
< golang.org/x/crypto/poly1305
< crypto/x509
< crypto/tls;
+ crypto/internal/boring/sig, crypto/internal/boring/fipstls
+ < crypto/tls/fipsonly;
+
+ crypto/internal/boring
+ < crypto/boring;
+
# crypto-aware packages
NET, crypto/rand, mime/quotedprintable
< net/rpc
< net/rpc/jsonrpc;
+ # System Information
+ internal/cpu, sync
+ < internal/sysinfo;
+
# Test-only
log
- < testing/iotest;
+ < testing/iotest
+ < testing/fstest;
FMT, flag, math/rand
< testing/quick;
- FMT, flag, runtime/debug, runtime/trace
+ FMT, flag, runtime/debug, runtime/trace, internal/sysinfo
< testing;
internal/testlog, runtime/pprof, regexp
var pkgs []string
src := filepath.Join(goroot, "src") + string(filepath.Separator)
- walkFn := func(path string, fi os.FileInfo, err error) error {
+ walkFn := func(path string, fi fs.FileInfo, err error) error {
if err != nil || !fi.IsDir() || path == src {
return nil
}
if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
continue
}
- f, err := os.Open(filepath.Join(dir, name))
+ var info fileInfo
+ info.name = filepath.Join(dir, name)
+ f, err := os.Open(info.name)
if err != nil {
return nil, err
}
- var imp []string
- data, err := readImports(f, false, &imp)
+ err = readGoInfo(f, &info)
f.Close()
if err != nil {
return nil, fmt.Errorf("reading %v: %v", name, err)
}
- if bytes.Contains(data, buildIgnore) {
+ if bytes.Contains(info.header, buildIgnore) {
continue
}
- for _, quoted := range imp {
- path, err := strconv.Unquote(quoted)
- if err != nil {
- continue
- }
+ for _, imp := range info.imports {
+ path := imp.path
if !haveImport[path] {
haveImport[path] = true
imports = append(imports, path)