| | |
| | |
| | |
| |
|
| | package main |
| |
|
| | import ( |
| | "internal/obscuretestdata" |
| | "internal/platform" |
| | "internal/testenv" |
| | "os" |
| | "path/filepath" |
| | "runtime" |
| | "strings" |
| | "testing" |
| | "text/template" |
| | ) |
| |
|
| | |
| | |
| | func TestMain(m *testing.M) { |
| | if os.Getenv("GO_NMTEST_IS_NM") != "" { |
| | main() |
| | os.Exit(0) |
| | } |
| |
|
| | os.Setenv("GO_NMTEST_IS_NM", "1") |
| | os.Exit(m.Run()) |
| | } |
| |
|
| | func TestNonGoExecs(t *testing.T) { |
| | t.Parallel() |
| | testfiles := []string{ |
| | "debug/elf/testdata/gcc-386-freebsd-exec", |
| | "debug/elf/testdata/gcc-amd64-linux-exec", |
| | "debug/macho/testdata/gcc-386-darwin-exec.base64", |
| | "debug/macho/testdata/gcc-amd64-darwin-exec.base64", |
| | |
| | "debug/pe/testdata/gcc-386-mingw-exec", |
| | "debug/plan9obj/testdata/amd64-plan9-exec", |
| | "debug/plan9obj/testdata/386-plan9-exec", |
| | "internal/xcoff/testdata/gcc-ppc64-aix-dwarf2-exec", |
| | } |
| | for _, f := range testfiles { |
| | exepath := filepath.Join(testenv.GOROOT(t), "src", f) |
| | if strings.HasSuffix(f, ".base64") { |
| | tf, err := obscuretestdata.DecodeToTempFile(exepath) |
| | if err != nil { |
| | t.Errorf("obscuretestdata.DecodeToTempFile(%s): %v", exepath, err) |
| | continue |
| | } |
| | defer os.Remove(tf) |
| | exepath = tf |
| | } |
| |
|
| | cmd := testenv.Command(t, testenv.Executable(t), exepath) |
| | out, err := cmd.CombinedOutput() |
| | if err != nil { |
| | t.Errorf("go tool nm %v: %v\n%s", exepath, err, string(out)) |
| | } |
| | } |
| | } |
| |
|
| | func testGoExec(t *testing.T, iscgo, isexternallinker bool) { |
| | t.Parallel() |
| | tmpdir := t.TempDir() |
| |
|
| | src := filepath.Join(tmpdir, "a.go") |
| | file, err := os.Create(src) |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | err = template.Must(template.New("main").Parse(testexec)).Execute(file, iscgo) |
| | if e := file.Close(); err == nil { |
| | err = e |
| | } |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| |
|
| | exe := filepath.Join(tmpdir, "a.exe") |
| | args := []string{"build", "-o", exe} |
| | if iscgo { |
| | linkmode := "internal" |
| | if isexternallinker { |
| | linkmode = "external" |
| | } |
| | args = append(args, "-ldflags", "-linkmode="+linkmode) |
| | } |
| | args = append(args, src) |
| | out, err := testenv.Command(t, testenv.GoToolPath(t), args...).CombinedOutput() |
| | if err != nil { |
| | t.Fatalf("building test executable failed: %s %s", err, out) |
| | } |
| |
|
| | out, err = testenv.Command(t, exe).CombinedOutput() |
| | if err != nil { |
| | t.Fatalf("running test executable failed: %s %s", err, out) |
| | } |
| | names := make(map[string]string) |
| | for _, line := range strings.Split(string(out), "\n") { |
| | if line == "" { |
| | continue |
| | } |
| | f := strings.Split(line, "=") |
| | if len(f) != 2 { |
| | t.Fatalf("unexpected output line: %q", line) |
| | } |
| | names["main."+f[0]] = f[1] |
| | } |
| |
|
| | runtimeSyms := map[string]string{ |
| | "runtime.text": "T", |
| | "runtime.etext": "T", |
| | "runtime.rodata": "R", |
| | "runtime.erodata": "R", |
| | "runtime.epclntab": "R", |
| | "runtime.noptrdata": "D", |
| | } |
| |
|
| | out, err = testenv.Command(t, testenv.Executable(t), exe).CombinedOutput() |
| | if err != nil { |
| | t.Fatalf("go tool nm: %v\n%s", err, string(out)) |
| | } |
| |
|
| | relocated := func(code string) bool { |
| | if runtime.GOOS == "aix" { |
| | |
| | |
| | |
| | |
| | switch code { |
| | case "T", "t", "R", "r": |
| | return iscgo |
| | case "D", "d", "B", "b": |
| | return true |
| | } |
| | } |
| | if platform.DefaultPIE(runtime.GOOS, runtime.GOARCH, false) { |
| | |
| | return true |
| | } |
| | return false |
| | } |
| |
|
| | dups := make(map[string]bool) |
| | for _, line := range strings.Split(string(out), "\n") { |
| | f := strings.Fields(line) |
| | if len(f) < 3 { |
| | continue |
| | } |
| | name := f[2] |
| | if addr, found := names[name]; found { |
| | if want, have := addr, "0x"+f[0]; have != want { |
| | if !relocated(f[1]) { |
| | t.Errorf("want %s address for %s symbol, but have %s", want, name, have) |
| | } |
| | } |
| | delete(names, name) |
| | } |
| | if _, found := dups[name]; found { |
| | t.Errorf("duplicate name of %q is found", name) |
| | } |
| | if stype, found := runtimeSyms[name]; found { |
| | if runtime.GOOS == "plan9" && stype == "R" { |
| | |
| | stype = "D" |
| | } |
| | if want, have := stype, strings.ToUpper(f[1]); have != want { |
| | if runtime.GOOS == "android" && name == "runtime.epclntab" && have == "D" { |
| | |
| | t.Logf("(ignoring on %s) want %s type for %s symbol, but have %s", runtime.GOOS, want, name, have) |
| | } else { |
| | t.Errorf("want %s type for %s symbol, but have %s", want, name, have) |
| | } |
| | } |
| | delete(runtimeSyms, name) |
| | } |
| | } |
| | if len(names) > 0 { |
| | t.Errorf("executable is missing %v symbols", names) |
| | } |
| | if len(runtimeSyms) > 0 { |
| | t.Errorf("executable is missing %v symbols", runtimeSyms) |
| | } |
| | } |
| |
|
| | func TestGoExec(t *testing.T) { |
| | testGoExec(t, false, false) |
| | } |
| |
|
| | func testGoLib(t *testing.T, iscgo bool) { |
| | t.Parallel() |
| | tmpdir := t.TempDir() |
| |
|
| | gopath := filepath.Join(tmpdir, "gopath") |
| | libpath := filepath.Join(gopath, "src", "mylib") |
| |
|
| | err := os.MkdirAll(libpath, 0777) |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | src := filepath.Join(libpath, "a.go") |
| | file, err := os.Create(src) |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| | err = template.Must(template.New("mylib").Parse(testlib)).Execute(file, iscgo) |
| | if e := file.Close(); err == nil { |
| | err = e |
| | } |
| | if err == nil { |
| | err = os.WriteFile(filepath.Join(libpath, "go.mod"), []byte("module mylib\n"), 0666) |
| | } |
| | if err != nil { |
| | t.Fatal(err) |
| | } |
| |
|
| | cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-buildmode=archive", "-o", "mylib.a", ".") |
| | cmd.Dir = libpath |
| | cmd.Env = append(os.Environ(), "GOPATH="+gopath) |
| | out, err := cmd.CombinedOutput() |
| | if err != nil { |
| | t.Fatalf("building test lib failed: %s %s", err, out) |
| | } |
| | mylib := filepath.Join(libpath, "mylib.a") |
| |
|
| | out, err = testenv.Command(t, testenv.Executable(t), mylib).CombinedOutput() |
| | if err != nil { |
| | t.Fatalf("go tool nm: %v\n%s", err, string(out)) |
| | } |
| | type symType struct { |
| | Type string |
| | Name string |
| | CSym bool |
| | Found bool |
| | } |
| | var syms = []symType{ |
| | {"B", "mylib.Testdata", false, false}, |
| | {"T", "mylib.Testfunc", false, false}, |
| | } |
| | if iscgo { |
| | syms = append(syms, symType{"B", "mylib.TestCgodata", false, false}) |
| | syms = append(syms, symType{"T", "mylib.TestCgofunc", false, false}) |
| | if runtime.GOOS == "darwin" || runtime.GOOS == "ios" || (runtime.GOOS == "windows" && runtime.GOARCH == "386") { |
| | syms = append(syms, symType{"D", "_cgodata", true, false}) |
| | syms = append(syms, symType{"T", "_cgofunc", true, false}) |
| | } else if runtime.GOOS == "aix" { |
| | syms = append(syms, symType{"D", "cgodata", true, false}) |
| | syms = append(syms, symType{"T", ".cgofunc", true, false}) |
| | } else { |
| | syms = append(syms, symType{"D", "cgodata", true, false}) |
| | syms = append(syms, symType{"T", "cgofunc", true, false}) |
| | } |
| | } |
| |
|
| | for _, line := range strings.Split(string(out), "\n") { |
| | f := strings.Fields(line) |
| | var typ, name string |
| | var csym bool |
| | if iscgo { |
| | if len(f) < 4 { |
| | continue |
| | } |
| | csym = !strings.Contains(f[0], "_go_.o") |
| | typ = f[2] |
| | name = f[3] |
| | } else { |
| | if len(f) < 3 { |
| | continue |
| | } |
| | typ = f[1] |
| | name = f[2] |
| | } |
| | for i := range syms { |
| | sym := &syms[i] |
| | if sym.Type == typ && sym.Name == name && sym.CSym == csym { |
| | if sym.Found { |
| | t.Fatalf("duplicate symbol %s %s", sym.Type, sym.Name) |
| | } |
| | sym.Found = true |
| | } |
| | } |
| | } |
| | for _, sym := range syms { |
| | if !sym.Found { |
| | t.Errorf("cannot found symbol %s %s", sym.Type, sym.Name) |
| | } |
| | } |
| | } |
| |
|
| | func TestGoLib(t *testing.T) { |
| | testGoLib(t, false) |
| | } |
| |
|
| | const testexec = ` |
| | package main |
| | |
| | import "fmt" |
| | {{if .}}import "C" |
| | {{end}} |
| | |
| | func main() { |
| | testfunc() |
| | } |
| | |
| | var testdata uint32 |
| | |
| | func testfunc() { |
| | fmt.Printf("main=%p\n", main) |
| | fmt.Printf("testfunc=%p\n", testfunc) |
| | fmt.Printf("testdata=%p\n", &testdata) |
| | } |
| | ` |
| |
|
| | const testlib = ` |
| | package mylib |
| | |
| | {{if .}} |
| | // int cgodata = 5; |
| | // void cgofunc(void) {} |
| | import "C" |
| | |
| | var TestCgodata = C.cgodata |
| | |
| | func TestCgofunc() { |
| | C.cgofunc() |
| | } |
| | {{end}} |
| | |
| | var Testdata uint32 |
| | |
| | func Testfunc() {} |
| | ` |
| |
|