]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/cgo: don't hardcode section name in TestNumberOfExportedFunctions
authorqmuntal <quimmuntal@gmail.com>
Mon, 22 Sep 2025 10:02:13 +0000 (12:02 +0200)
committerQuim Muntal <quimmuntal@gmail.com>
Tue, 23 Sep 2025 18:49:41 +0000 (11:49 -0700)
TestNumberOfExportedFunctions checks the number of exported functions
announced in the PE export table, getting it from the .edata section.
If the section is not found, the test is skipped. However, the PE spec
doesn't mandate that the export table be in a section named .edata,
making this test prone to being skipped unnecessarily.

While here, remove a check in cmd/go.testBuildmodePIE that was testing
the same thing in order to verify that the binary had a relocation table
. Not only the test is duplicated, but also it in unnecessary because
it already testing that the PE characteristics doesn't contain the
IMAGE_FILE_RELOCS_STRIPPED flag.

Closes #46719

Cq-Include-Trybots: luci.golang.try:gotip-windows-arm64
Change-Id: I28d1e261b38388868dd3c19ef6ddddad7bf105ef
Reviewed-on: https://go-review.googlesource.com/c/go/+/705755
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/cgo/internal/testcshared/cshared_test.go
src/cmd/go/go_test.go

index 2c4d33f599f8d7fa1681a484c7b7afe9699932d1..f1c30f8f9a2b2ae3ba50ad71556af8e4776b0660 100644 (file)
@@ -375,26 +375,7 @@ func TestExportedSymbols(t *testing.T) {
        }
 }
 
-func checkNumberOfExportedFunctionsWindows(t *testing.T, exportAllSymbols bool) {
-       const prog = `
-package main
-
-import "C"
-
-//export GoFunc
-func GoFunc() {
-       println(42)
-}
-
-//export GoFunc2
-func GoFunc2() {
-       println(24)
-}
-
-func main() {
-}
-`
-
+func checkNumberOfExportedFunctionsWindows(t *testing.T, prog string, exportedFunctions int, wantAll bool) {
        tmpdir := t.TempDir()
 
        srcfile := filepath.Join(tmpdir, "test.go")
@@ -403,7 +384,7 @@ func main() {
                t.Fatal(err)
        }
        argv := []string{"build", "-buildmode=c-shared"}
-       if exportAllSymbols {
+       if wantAll {
                argv = append(argv, "-ldflags", "-extldflags=-Wl,--export-all-symbols")
        }
        argv = append(argv, "-o", objfile, srcfile)
@@ -417,10 +398,36 @@ func main() {
                t.Fatalf("pe.Open failed: %v", err)
        }
        defer f.Close()
-       section := f.Section(".edata")
+
+       _, pe64 := f.OptionalHeader.(*pe.OptionalHeader64)
+       // grab the export data directory entry
+       var idd pe.DataDirectory
+       if pe64 {
+               idd = f.OptionalHeader.(*pe.OptionalHeader64).DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT]
+       } else {
+               idd = f.OptionalHeader.(*pe.OptionalHeader32).DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_EXPORT]
+       }
+
+       // figure out which section contains the import directory table
+       var section *pe.Section
+       for _, s := range f.Sections {
+               if s.Offset == 0 {
+                       continue
+               }
+               if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress-s.VirtualAddress < s.VirtualSize {
+                       section = s
+                       break
+               }
+       }
        if section == nil {
-               t.Skip(".edata section is not present")
+               t.Fatal("no section contains export directory")
        }
+       d, err := section.Data()
+       if err != nil {
+               t.Fatal(err)
+       }
+       // seek to the virtual address specified in the export data directory
+       d = d[idd.VirtualAddress-section.VirtualAddress:]
 
        // TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
        type IMAGE_EXPORT_DIRECTORY struct {
@@ -432,26 +439,22 @@ func main() {
                _                 [3]uint32
        }
        var e IMAGE_EXPORT_DIRECTORY
-       if err := binary.Read(section.Open(), binary.LittleEndian, &e); err != nil {
+       if err := binary.Read(bytes.NewReader(d), binary.LittleEndian, &e); err != nil {
                t.Fatalf("binary.Read failed: %v", err)
        }
 
-       // Only the two exported functions and _cgo_dummy_export should be exported
-       expectedNumber := uint32(3)
-
-       if exportAllSymbols {
-               if e.NumberOfFunctions <= expectedNumber {
-                       t.Fatalf("missing exported functions: %v", e.NumberOfFunctions)
-               }
-               if e.NumberOfNames <= expectedNumber {
-                       t.Fatalf("missing exported names: %v", e.NumberOfNames)
+       // Only the two exported functions and _cgo_dummy_export should be exported.
+       // NumberOfNames is the number of functions exported with a unique name.
+       // NumberOfFunctions can be higher than that because it also counts
+       // functions exported only by ordinal, a unique number asigned by the linker,
+       // and linkers might add an unknown number of their own ordinal-only functions.
+       if wantAll {
+               if e.NumberOfNames <= uint32(exportedFunctions) {
+                       t.Errorf("got %d exported names, want > %d", e.NumberOfNames, exportedFunctions)
                }
        } else {
-               if e.NumberOfFunctions != expectedNumber {
-                       t.Fatalf("got %d exported functions; want %d", e.NumberOfFunctions, expectedNumber)
-               }
-               if e.NumberOfNames != expectedNumber {
-                       t.Fatalf("got %d exported names; want %d", e.NumberOfNames, expectedNumber)
+               if e.NumberOfNames > uint32(exportedFunctions) {
+                       t.Errorf("got %d exported names, want <= %d", e.NumberOfNames, exportedFunctions)
                }
        }
 }
@@ -467,11 +470,42 @@ func TestNumberOfExportedFunctions(t *testing.T) {
 
        t.Parallel()
 
-       t.Run("OnlyExported", func(t *testing.T) {
-               checkNumberOfExportedFunctionsWindows(t, false)
+       const prog0 = `
+package main
+
+import "C"
+
+func main() {
+}
+`
+
+       const prog2 = `
+package main
+
+import "C"
+
+//export GoFunc
+func GoFunc() {
+       println(42)
+}
+
+//export GoFunc2
+func GoFunc2() {
+       println(24)
+}
+
+func main() {
+}
+`
+       // All programs export _cgo_dummy_export, so add 1 to the expected counts.
+       t.Run("OnlyExported/0", func(t *testing.T) {
+               checkNumberOfExportedFunctionsWindows(t, prog0, 0+1, false)
+       })
+       t.Run("OnlyExported/2", func(t *testing.T) {
+               checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, false)
        })
        t.Run("All", func(t *testing.T) {
-               checkNumberOfExportedFunctionsWindows(t, true)
+               checkNumberOfExportedFunctionsWindows(t, prog2, 2+1, true)
        })
 }
 
index 3e691abe41f702795e806b75eafc61906e5bc233..e4ee9bd1e8d2ec8b6b393ca166be177013dbdf2a 100644 (file)
@@ -9,7 +9,6 @@ import (
        "debug/elf"
        "debug/macho"
        "debug/pe"
-       "encoding/binary"
        "flag"
        "fmt"
        "go/format"
@@ -2131,38 +2130,6 @@ func testBuildmodePIE(t *testing.T, useCgo, setBuildmodeToPIE bool) {
                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.Skip(".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:
                // testBuildmodePIE opens object files, so it needs to understand the object
                // file format.