Skip to content

Commit 5055314

Browse files
qmuntaldmitshur
authored andcommitted
[release-branch.go1.15] cmd/cgo: avoid exporting all symbols on windows buildmode=c-shared
Disable default symbol auto-export behaviour by marking exported function with the __declspec(dllexport) attribute. Old behaviour can still be used by setting -extldflags=-Wl,--export-all-symbols. See https://sourceware.org/binutils/docs/ld/WIN32.html for more info. This change cuts 50kb of a "hello world" dll. Updates #6853. Updates #30674. Fixes #43591. Change-Id: I9c7fb09c677cc760f24d0f7d199740ae73981413 Reviewed-on: https://go-review.googlesource.com/c/go/+/262797 Run-TryBot: Ian Lance Taylor <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Alex Brainman <[email protected]> Trust: Alex Brainman <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/go/+/300693 Trust: Dmitri Shuralyov <[email protected]> Run-TryBot: Dmitri Shuralyov <[email protected]> Reviewed-by: David Chase <[email protected]>
1 parent a07f9d2 commit 5055314

File tree

2 files changed

+101
-1
lines changed

2 files changed

+101
-1
lines changed

misc/cgo/testcshared/cshared_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ package cshared_test
77
import (
88
"bytes"
99
"debug/elf"
10+
"debug/pe"
11+
"encoding/binary"
1012
"flag"
1113
"fmt"
1214
"io/ioutil"
@@ -355,6 +357,100 @@ func TestExportedSymbols(t *testing.T) {
355357
}
356358
}
357359

360+
func checkNumberOfExportedFunctionsWindows(t *testing.T, exportAllSymbols bool) {
361+
const prog = `
362+
package main
363+
364+
import "C"
365+
366+
//export GoFunc
367+
func GoFunc() {
368+
println(42)
369+
}
370+
371+
//export GoFunc2
372+
func GoFunc2() {
373+
println(24)
374+
}
375+
376+
func main() {
377+
}
378+
`
379+
380+
tmpdir := t.TempDir()
381+
382+
srcfile := filepath.Join(tmpdir, "test.go")
383+
objfile := filepath.Join(tmpdir, "test.dll")
384+
if err := ioutil.WriteFile(srcfile, []byte(prog), 0666); err != nil {
385+
t.Fatal(err)
386+
}
387+
argv := []string{"build", "-buildmode=c-shared"}
388+
if exportAllSymbols {
389+
argv = append(argv, "-ldflags", "-extldflags=-Wl,--export-all-symbols")
390+
}
391+
argv = append(argv, "-o", objfile, srcfile)
392+
out, err := exec.Command("go", argv...).CombinedOutput()
393+
if err != nil {
394+
t.Fatalf("build failure: %s\n%s\n", err, string(out))
395+
}
396+
397+
f, err := pe.Open(objfile)
398+
if err != nil {
399+
t.Fatalf("pe.Open failed: %v", err)
400+
}
401+
defer f.Close()
402+
section := f.Section(".edata")
403+
if section == nil {
404+
t.Error(".edata section is not present")
405+
}
406+
407+
// TODO: deduplicate this struct from cmd/link/internal/ld/pe.go
408+
type IMAGE_EXPORT_DIRECTORY struct {
409+
_ [2]uint32
410+
_ [2]uint16
411+
_ [2]uint32
412+
NumberOfFunctions uint32
413+
NumberOfNames uint32
414+
_ [3]uint32
415+
}
416+
var e IMAGE_EXPORT_DIRECTORY
417+
if err := binary.Read(section.Open(), binary.LittleEndian, &e); err != nil {
418+
t.Fatalf("binary.Read failed: %v", err)
419+
}
420+
421+
expectedNumber := uint32(2)
422+
423+
if exportAllSymbols {
424+
if e.NumberOfFunctions <= expectedNumber {
425+
t.Fatalf("missing exported functions: %v", e.NumberOfFunctions)
426+
}
427+
if e.NumberOfNames <= expectedNumber {
428+
t.Fatalf("missing exported names: %v", e.NumberOfNames)
429+
}
430+
} else {
431+
if e.NumberOfFunctions != expectedNumber {
432+
t.Fatalf("too many exported functions: %v", e.NumberOfFunctions)
433+
}
434+
if e.NumberOfNames != expectedNumber {
435+
t.Fatalf("too many exported names: %v", e.NumberOfNames)
436+
}
437+
}
438+
}
439+
440+
func TestNumberOfExportedFunctions(t *testing.T) {
441+
if GOOS != "windows" {
442+
t.Skip("skipping windows only test")
443+
}
444+
t.Parallel()
445+
446+
t.Run("OnlyExported", func(t *testing.T) {
447+
checkNumberOfExportedFunctionsWindows(t, false)
448+
})
449+
t.Run("All", func(t *testing.T) {
450+
checkNumberOfExportedFunctionsWindows(t, true)
451+
})
452+
}
453+
358454
// test1: shared library can be dynamically loaded and exported symbols are accessible.
359455
func TestExportedSymbolsWithDynamicLoad(t *testing.T) {
360456
t.Parallel()

src/cmd/cgo/out.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,11 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
961961
}
962962

963963
// Build the wrapper function compiled by gcc.
964-
s := fmt.Sprintf("%s %s(", gccResult, exp.ExpName)
964+
gccExport := ""
965+
if goos == "windows" {
966+
gccExport = "__declspec(dllexport)"
967+
}
968+
s := fmt.Sprintf("%s %s %s(", gccExport, gccResult, exp.ExpName)
965969
if fn.Recv != nil {
966970
s += p.cgoType(fn.Recv.List[0].Type).C.String()
967971
s += " recv"

0 commit comments

Comments
 (0)