func (c *Context) loadPkg(pkg string) {
	rescfg := basePackagesConfig()
	rescfg.Mode = packages.NeedName
	rescfg.BuildFlags = []string{"-tags", makeTags()}
	respkgs, err := packages.Load(rescfg, pkg)
	if err != nil {
		c.failf("could not resolve package %q: %v", pkg, err)
	}
	if len(respkgs) != 1 {
		paths := make([]string, len(respkgs))
		for i, p := range respkgs {
			paths[i] = p.PkgPath
		}
		c.failf("cannot build multiple packages, but %q resolved to: %v", pkg, strings.Join(paths, ", "))
	}
	if respkgs[0].Name == "main" {
		c.failf("cannot fuzz package main")
	}
	pkgpath := respkgs[0].PkgPath
	cfg := basePackagesConfig()
	cfg.Mode = packages.LoadAllSyntax
	cfg.BuildFlags = []string{"-tags", makeTags()}
	cfg.ParseFile = func(fset *token.FileSet, filename string, src []byte) (*ast.File, error) {
		return parser.ParseFile(fset, filename, src, parser.ParseComments)
	}
	loadpkgs := []string{pkg, "github.com/dvyukov/go-fuzz/go-fuzz-dep"}
	if *flagLibFuzzer {
		loadpkgs = append(loadpkgs, "reflect")
	}
	initial, err := packages.Load(cfg, loadpkgs...)
	if err != nil {
		c.failf("could not load packages: %v", err)
	}
	if packages.PrintErrors(initial) > 0 {
		c.failf("typechecking of %v failed", pkg)
	}
	c.pkgs = initial
	for _, p := range initial {
		if p.PkgPath == pkgpath {
			c.fuzzpkg = p
			break
		}
	}
	if c.fuzzpkg == nil {
		c.failf("internal error: failed to find fuzz package; please file an issue")
	}
	foundFlagFunc := false
	s := c.fuzzpkg.Types.Scope()
	for _, n := range s.Names() {
		if !isFuzzFuncName(n) {
			continue
		}
		typ := s.Lookup(n).Type()
		sig, ok := typ.(*types.Signature)
		if !ok || sig.Variadic() || !isFuzzSig(sig) {
			if n == *flagFunc {
				c.failf("provided -func=%v, but %v is not a fuzz function", *flagFunc, *flagFunc)
			}
			continue
		}
		c.allFuncs = append(c.allFuncs, n)
		foundFlagFunc = foundFlagFunc || n == *flagFunc
	}
	if len(c.allFuncs) == 0 {
		c.failf("could not find any fuzz functions in %v", c.fuzzpkg.PkgPath)
	}
	if len(c.allFuncs) > 255 {
		c.failf("go-fuzz-build supports a maximum of 255 fuzz functions, found %v; please file an issue", len(c.allFuncs))
	}
	if *flagFunc != "" {
		if !foundFlagFunc {
			c.failf("could not find fuzz function %v in %v", *flagFunc, c.fuzzpkg.PkgPath)
		}
	} else {
		if len(c.allFuncs) == 1 {
			*flagFunc = c.allFuncs[0]
		} else if *flagLibFuzzer {
			c.failf("must specify a fuzz function with -libfuzzer, found: %v", strings.Join(c.allFuncs, ", "))
		}
	}
}
loadPkg loads, parses, and typechecks pkg (the package containing the Fuzz function),
go-fuzz-dep, and their dependencies.
package main
import (
	"archive/zip"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"go/types"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"
	"text/template"
	"unicode"
	"unicode/utf8"
	"golang.org/x/tools/go/packages"
	. "github.com/dvyukov/go-fuzz/internal/go-fuzz-types"
)
var (
	flagTag       = flag.String("tags", "", "a space-separated list of build tags to consider satisfied during the build")
	flagOut       = flag.String("o", "", "output file")
	flagFunc      = flag.String("func", "", "preferred entry function")
	flagWork      = flag.Bool("work", false, "don't remove working directory")
	flagRace      = flag.Bool("race", false, "enable race detector")
	flagCPU       = flag.Bool("cpuprofile", false, "generate cpu profile in cpu.pprof")
	flagLibFuzzer = flag.Bool("libfuzzer", false, "output static archive for use with libFuzzer")
	flagBuildX    = flag.Bool("x", false, "print the commands if build fails")
	flagPreserve  = flag.String("preserve", "", "a comma-separated list of import paths not to instrument")
	flagGoCmd     = flag.String("go", "go", `path to "go" command`)
)
func makeTags() string {
	tags := "gofuzz"
	if *flagLibFuzzer {
		tags += " gofuzz_libfuzzer"
	}
	if *flagRace {
		tags += " race"
	}
	if len(*flagTag) > 0 {
		tags += " " + *flagTag
	}
	return tags
}
func basePackagesConfig() *packages.Config {
	cfg := new(packages.Config)
	cfg.Env = os.Environ()
	return cfg
}
func checkModVendor() bool {
	val := os.Getenv("GOFLAGS")
	for _, s := range strings.Split(val, " ") {
		if s == "-mod=vendor" {
			return true
		}
	}
	return false
}
func main() {
	flag.Parse()
	c := new(Context)
	if flag.NArg() > 1 {
		c.failf("usage: go-fuzz-build [pkg]")
	}
	pkg := "."
	if flag.NArg() == 1 {
		pkg = flag.Arg(0)
	}
	if *flagFunc != "" && !isFuzzFuncName(*flagFunc) {
		c.failf("provided -func=%v, but %v is not a fuzz function name", *flagFunc, *flagFunc)
	}
	if *flagLibFuzzer && *flagRace {
		c.failf("-race and -libfuzzer are incompatible")
	}
	if checkModVendor() {
		c.failf("GOFLAGS with -mod=vendor is not supported")
	}
	c.startProfiling()   
	c.loadPkg(pkg)       
	c.getEnv()           
	c.loadStd()          
	c.calcIgnore()       
	c.makeWorkdir()      
	defer c.cleanup()    
	c.populateWorkdir()  
	if *flagOut == "" {
		ext := ".zip"
		if *flagLibFuzzer {
			ext = ".a"
		}
		*flagOut = c.pkgs[0].Name + "-fuzz" + ext
	}
	lits := c.gatherLiterals()
	var blocks, sonar []CoverBlock
	if *flagLibFuzzer {
		archive := c.buildInstrumentedBinary(&blocks, nil)
		c.moveFile(archive, *flagOut)
		return
	}
	coverBin := c.buildInstrumentedBinary(&blocks, nil)
	sonarBin := c.buildInstrumentedBinary(nil, &sonar)
	metaData := c.createMeta(lits, blocks, sonar)
	defer func() {
		os.Remove(coverBin)
		os.Remove(sonarBin)
		os.Remove(metaData)
	}()
	outf, err := os.Create(*flagOut)
	if err != nil {
		c.failf("failed to create output file: %v", err)
	}
	zipw := zip.NewWriter(outf)
	zipFile := func(name, datafile string) {
		w, err := zipw.Create(name)
		if err != nil {
			c.failf("failed to create zip file: %v", err)
		}
		f, err := os.Open(datafile)
		if err != nil {
			c.failf("failed to open data file %v", datafile)
		}
		if _, err := io.Copy(w, f); err != nil {
			c.failf("failed to write %v to zip file: %v", datafile, err)
		}
		f.Close()
		os.Remove(datafile)
	}
	zipFile("cover.exe", coverBin)
	zipFile("sonar.exe", sonarBin)
	zipFile("metadata", metaData)
	if err := zipw.Close(); err != nil {
		c.failf("failed to close zip file: %v", err)
	}
	if err := outf.Close(); err != nil {
		c.failf("failed to close out file: %v", err)
	}
}
type Context struct {
	fuzzpkg *packages.Package    
	pkgs    []*packages.Package  
	std    map[string]bool  
	ignore map[string]bool  
	allFuncs []string  
	workdir string
	GOROOT  string
	GOPATH  string
	cpuprofile *os.File
	cmdGoHasTrimPath bool  
}
func (c *Context) getEnv() {
	env := map[string]string{
		"GOROOT": "",
		"GOPATH": "",
	}
	for k := range env {
		v := os.Getenv(k)
		if v != "" {
			env[k] = v
			continue
		}
		out, err := exec.Command(*flagGoCmd, "env", k).CombinedOutput()
		if err != nil || len(out) == 0 {
			c.failf("%s is not set and failed to locate it: 'go env %s' returned '%s' (%v)", k, k, out, err)
		}
		env[k] = strings.TrimSpace(string(out))
	}
	c.GOROOT = env["GOROOT"]
	c.GOPATH = env["GOPATH"]
	out, err := exec.Command(*flagGoCmd, "list", "-f", "'{{context.ReleaseTags}}'", "runtime").CombinedOutput()
	if err != nil || len(out) == 0 {
		c.failf("go list -f '{{context.ReleaseTags}}' runtime returned '%s' (%v)", out, err)
	}
	c.cmdGoHasTrimPath = bytes.Contains(out, []byte("go1.13"))
}
func (c *Context) startProfiling() {
	if !*flagCPU {
		return
	}
	var err error
	c.cpuprofile, err = os.Create("cpu.pprof")
	if err != nil {
		c.failf("could not create cpu profile: %v", err)
	}
	pprof.StartCPUProfile(c.cpuprofile)
}