}
}
-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")
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)
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 {
_ [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)
}
}
}
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)
})
}
"debug/elf"
"debug/macho"
"debug/pe"
- "encoding/binary"
"flag"
"fmt"
"go/format"
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.