mirror of
https://github.com/golang/go.git
synced 2025-10-19 19:13:18 +00:00
cmd/cgo: don't hardcode section name in TestNumberOfExportedFunctions
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>
This commit is contained in:
parent
61bf26a9ee
commit
a13d085a5b
2 changed files with 112 additions and 111 deletions
|
@ -375,8 +375,111 @@ func TestExportedSymbols(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func checkNumberOfExportedFunctionsWindows(t *testing.T, exportAllSymbols bool) {
|
||||
const prog = `
|
||||
func checkNumberOfExportedFunctionsWindows(t *testing.T, prog string, exportedFunctions int, wantAll bool) {
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
srcfile := filepath.Join(tmpdir, "test.go")
|
||||
objfile := filepath.Join(tmpdir, "test.dll")
|
||||
if err := os.WriteFile(srcfile, []byte(prog), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
argv := []string{"build", "-buildmode=c-shared"}
|
||||
if wantAll {
|
||||
argv = append(argv, "-ldflags", "-extldflags=-Wl,--export-all-symbols")
|
||||
}
|
||||
argv = append(argv, "-o", objfile, srcfile)
|
||||
out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("build failure: %s\n%s\n", err, string(out))
|
||||
}
|
||||
|
||||
f, err := pe.Open(objfile)
|
||||
if err != nil {
|
||||
t.Fatalf("pe.Open failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, 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.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 {
|
||||
_ [2]uint32
|
||||
_ [2]uint16
|
||||
_ [2]uint32
|
||||
NumberOfFunctions uint32
|
||||
NumberOfNames uint32
|
||||
_ [3]uint32
|
||||
}
|
||||
var e IMAGE_EXPORT_DIRECTORY
|
||||
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.
|
||||
// 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.NumberOfNames > uint32(exportedFunctions) {
|
||||
t.Errorf("got %d exported names, want <= %d", e.NumberOfNames, exportedFunctions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumberOfExportedFunctions(t *testing.T) {
|
||||
if GOOS != "windows" {
|
||||
t.Skip("skipping windows only test")
|
||||
}
|
||||
globalSkip(t)
|
||||
testenv.MustHaveGoBuild(t)
|
||||
testenv.MustHaveCGO(t)
|
||||
testenv.MustHaveBuildMode(t, "c-shared")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
const prog0 = `
|
||||
package main
|
||||
|
||||
import "C"
|
||||
|
||||
func main() {
|
||||
}
|
||||
`
|
||||
|
||||
const prog2 = `
|
||||
package main
|
||||
|
||||
import "C"
|
||||
|
@ -394,84 +497,15 @@ func GoFunc2() {
|
|||
func main() {
|
||||
}
|
||||
`
|
||||
|
||||
tmpdir := t.TempDir()
|
||||
|
||||
srcfile := filepath.Join(tmpdir, "test.go")
|
||||
objfile := filepath.Join(tmpdir, "test.dll")
|
||||
if err := os.WriteFile(srcfile, []byte(prog), 0666); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
argv := []string{"build", "-buildmode=c-shared"}
|
||||
if exportAllSymbols {
|
||||
argv = append(argv, "-ldflags", "-extldflags=-Wl,--export-all-symbols")
|
||||
}
|
||||
argv = append(argv, "-o", objfile, srcfile)
|
||||
out, err := exec.Command(testenv.GoToolPath(t), argv...).CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("build failure: %s\n%s\n", err, string(out))
|
||||
}
|
||||
|
||||
f, err := pe.Open(objfile)
|
||||
if err != nil {
|
||||
t.Fatalf("pe.Open failed: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
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 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)
|
||||
}
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNumberOfExportedFunctions(t *testing.T) {
|
||||
if GOOS != "windows" {
|
||||
t.Skip("skipping windows only test")
|
||||
}
|
||||
globalSkip(t)
|
||||
testenv.MustHaveGoBuild(t)
|
||||
testenv.MustHaveCGO(t)
|
||||
testenv.MustHaveBuildMode(t, "c-shared")
|
||||
|
||||
t.Parallel()
|
||||
|
||||
t.Run("OnlyExported", func(t *testing.T) {
|
||||
checkNumberOfExportedFunctionsWindows(t, false)
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue