mirror of
https://github.com/burrowers/garble.git
synced 2025-10-05 07:46:59 +08:00

This teaches the program how to collect information from multiple Go versions and join it together. For this to work, it needs to select the Go versions itself, which is now possible via GOTOOLCHAIN. The merging of data is fairly simple; we join the results from all versions, and we remove duplicates from older Go versions. Start producing output with the Go version noted on every data point, so that we can easily scan what each Go version is contributing.
242 lines
6.2 KiB
Go
242 lines
6.2 KiB
Go
// Copyright (c) 2024, The Garble Authors.
|
|
// See LICENSE for licensing information.
|
|
|
|
//go:build ignore
|
|
|
|
// This is a program used with `go generate`, so it handles errors via panic.
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"cmp"
|
|
"fmt"
|
|
"go/format"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"regexp"
|
|
"slices"
|
|
"strings"
|
|
"text/template"
|
|
)
|
|
|
|
var goVersions = []string{"go1.22.6"}
|
|
|
|
var tmplTables = template.Must(template.New("").Parse(`
|
|
// Code generated by scripts/gen_go_std_tables.go; DO NOT EDIT.
|
|
|
|
// Generated from Go versions {{ .GoVersions }}.
|
|
|
|
package main
|
|
|
|
var runtimeAndDeps = map[string]bool{
|
|
{{- range $path := .RuntimeAndDeps }}
|
|
"{{ $path.String }}": true, // {{ $path.GoVersion }}
|
|
{{- end }}
|
|
}
|
|
|
|
var runtimeLinknamed = []string{
|
|
{{- range $path := .RuntimeLinknamed }}
|
|
"{{ $path.String }}", // {{ $path.GoVersion }}
|
|
{{- end }}
|
|
// The net package linknames to the runtime, not the other way around.
|
|
// TODO: support this automatically via our script.
|
|
"net",
|
|
}
|
|
|
|
var compilerIntrinsics = map[string]map[string]bool{
|
|
{{- range $intr := .CompilerIntrinsics }}
|
|
"{{ $intr.Path }}": {
|
|
{{- range $name := $intr.Names }}
|
|
"{{ $name.String }}": true, // {{ $name.GoVersion }}
|
|
{{- end }}
|
|
},
|
|
{{- end }}
|
|
}
|
|
|
|
var reflectSkipPkg = map[string]bool{
|
|
"fmt": true,
|
|
}
|
|
`[1:]))
|
|
|
|
type tmplData struct {
|
|
GoVersions []string
|
|
RuntimeAndDeps []versionedString
|
|
RuntimeLinknamed []versionedString
|
|
CompilerIntrinsics []tmplIntrinsic
|
|
}
|
|
|
|
type tmplIntrinsic struct {
|
|
Path string
|
|
Names []versionedString
|
|
}
|
|
|
|
func (t tmplIntrinsic) Compare(t2 tmplIntrinsic) int {
|
|
return cmp.Compare(t.Path, t2.Path)
|
|
}
|
|
|
|
func (t tmplIntrinsic) Equal(t2 tmplIntrinsic) bool {
|
|
return t.Compare(t2) == 0
|
|
}
|
|
|
|
type versionedString struct {
|
|
String string
|
|
GoVersion string
|
|
}
|
|
|
|
func (v versionedString) Compare(v2 versionedString) int {
|
|
if c := cmp.Compare(v.String, v2.String); c != 0 {
|
|
return c
|
|
}
|
|
// Negated so that newer Go versions go first.
|
|
return -cmp.Compare(v.GoVersion, v2.GoVersion)
|
|
}
|
|
|
|
func (v versionedString) Equal(v2 versionedString) bool {
|
|
// Note that we do equality based on String alone,
|
|
// because we only need one String entry with the latest version.
|
|
return v.String == v2.String
|
|
}
|
|
|
|
func cmdGo(goVersion string, args ...string) versionedString {
|
|
cmd := exec.Command("go", args...)
|
|
cmd.Env = append(cmd.Environ(), "GOTOOLCHAIN="+goVersion)
|
|
out, err := cmd.Output()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return versionedString{
|
|
String: string(bytes.TrimSpace(out)), // no trailing newline
|
|
GoVersion: goVersion,
|
|
}
|
|
}
|
|
|
|
func readFile(path string) string {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return string(data)
|
|
}
|
|
|
|
func lines(vs versionedString) []versionedString {
|
|
split := strings.Split(vs.String, "\n")
|
|
var versioned []versionedString
|
|
for _, s := range split {
|
|
versioned = append(versioned, versionedString{
|
|
String: s,
|
|
GoVersion: vs.GoVersion,
|
|
})
|
|
}
|
|
return versioned
|
|
}
|
|
|
|
var rxLinkname = regexp.MustCompile(`^//go:linkname .* ([^.]*)\.[^.]*$`)
|
|
var rxIntrinsic = regexp.MustCompile(`\b(addF|alias)\("([^"]*)", "([^"]*)",`)
|
|
|
|
func main() {
|
|
var runtimeAndDeps []versionedString
|
|
for _, goVersion := range goVersions {
|
|
runtimeAndDeps = append(runtimeAndDeps, lines(cmdGo(goVersion, "list", "-deps", "runtime"))...)
|
|
}
|
|
slices.SortFunc(runtimeAndDeps, versionedString.Compare)
|
|
runtimeAndDeps = slices.CompactFunc(runtimeAndDeps, versionedString.Equal)
|
|
|
|
var goroots []versionedString
|
|
for _, goVersion := range goVersions {
|
|
goroots = append(goroots, cmdGo(goVersion, "env", "GOROOT"))
|
|
}
|
|
|
|
// All packages that the runtime linknames to, except runtime and its dependencies.
|
|
// This resulting list is what we need to "go list" when obfuscating the runtime,
|
|
// as they are the packages that we may be missing.
|
|
var runtimeLinknamed []versionedString
|
|
for _, goroot := range goroots {
|
|
runtimeGoFiles, err := filepath.Glob(filepath.Join(goroot.String, "src", "runtime", "*.go"))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
for _, goFile := range runtimeGoFiles {
|
|
for _, line := range strings.Split(readFile(goFile), "\n") {
|
|
m := rxLinkname.FindStringSubmatch(line)
|
|
if m == nil {
|
|
continue
|
|
}
|
|
path := m[1]
|
|
switch path {
|
|
case "main", "runtime/metrics_test":
|
|
continue
|
|
}
|
|
runtimeLinknamed = append(runtimeLinknamed, versionedString{
|
|
String: path,
|
|
GoVersion: goroot.GoVersion,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
slices.SortFunc(runtimeLinknamed, versionedString.Compare)
|
|
runtimeLinknamed = slices.CompactFunc(runtimeLinknamed, versionedString.Equal)
|
|
runtimeLinknamed = slices.DeleteFunc(runtimeLinknamed, func(path versionedString) bool {
|
|
for _, prev := range runtimeAndDeps {
|
|
if prev.String == path.String {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
|
|
compilerIntrinsicsIndexByPath := make(map[string]int)
|
|
var compilerIntrinsics []tmplIntrinsic
|
|
for _, goroot := range goroots {
|
|
for _, line := range strings.Split(readFile(filepath.Join(
|
|
goroot.String, "src", "cmd", "compile", "internal", "ssagen", "ssa.go",
|
|
)), "\n") {
|
|
m := rxIntrinsic.FindStringSubmatch(line)
|
|
if m == nil {
|
|
continue
|
|
}
|
|
path, name := m[2], m[3]
|
|
vs := versionedString{
|
|
String: name,
|
|
GoVersion: goroot.GoVersion,
|
|
}
|
|
if i := compilerIntrinsicsIndexByPath[path]; i == 0 {
|
|
compilerIntrinsicsIndexByPath[path] = len(compilerIntrinsics)
|
|
compilerIntrinsics = append(compilerIntrinsics, tmplIntrinsic{
|
|
Path: path,
|
|
Names: []versionedString{vs},
|
|
})
|
|
} else {
|
|
compilerIntrinsics[i].Names = append(compilerIntrinsics[i].Names, vs)
|
|
}
|
|
}
|
|
}
|
|
slices.SortFunc(compilerIntrinsics, tmplIntrinsic.Compare)
|
|
compilerIntrinsics = slices.CompactFunc(compilerIntrinsics, tmplIntrinsic.Equal)
|
|
for path := range compilerIntrinsics {
|
|
intr := &compilerIntrinsics[path]
|
|
slices.SortFunc(intr.Names, versionedString.Compare)
|
|
intr.Names = slices.CompactFunc(intr.Names, versionedString.Equal)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
if err := tmplTables.Execute(&buf, tmplData{
|
|
GoVersions: goVersions,
|
|
RuntimeAndDeps: runtimeAndDeps,
|
|
RuntimeLinknamed: runtimeLinknamed,
|
|
CompilerIntrinsics: compilerIntrinsics,
|
|
}); err != nil {
|
|
panic(err)
|
|
}
|
|
out := buf.Bytes()
|
|
formatted, err := format.Source(out)
|
|
if err != nil {
|
|
fmt.Println(string(out))
|
|
panic(err)
|
|
}
|
|
|
|
if err := os.WriteFile("go_std_tables.go", formatted, 0o666); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|