cmd/link: -buildmode=c-shared exports many functions, not just //export functions
Opened this issue · 10 comments
What version of Go are you using (go version
)?
$ go version go version go1.12 windows/amd64
Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go envset GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\gamexg\AppData\Local\go-build
set GOEXE=.exe
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=D:\golang
set GOPROXY=
set GORACE=
set GOROOT=C:\Go
set GOTMPDIR=
set GOTOOLDIR=C:\Go\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 -fdebug-prefix-map=C:\Users\gamexg\AppData\Local\Temp\go-build864381234=/tmp/go-build -gno-record-gcc-switches
What did you do?
>go build -ldflags "-s -w" -buildmode=c-shared -o libhello.dll hello.go
// file hello.go
package main
// //Needed for build
import "C"
import (
"fmt"
"go.uber.org/zap"
)
//export SayHello
func SayHello(name string) {
fmt.Printf("func in Golang SayHello says: Hello, %s!\n", name)
}
//export Test
func Test() {
logger, _ := zap.NewProduction()
logger.Error("err")
}
func main() {
// We need the main function to make possible
// CGO compiler to compile the package as C shared library
}
What did you expect to see?
>dumpbin -exports libhello.dll
Microsoft (R) COFF/PE Dumper Version 14.00.24215.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file libhello.dll
File Type: DLL
Section contains the following exports for libhello.dll
00000000 characteristics
5C7FB64A time date stamp Wed Mar 6 20:00:10 2019
0.00 version
1 ordinal base
3320 number of functions
3320 number of names
ordinal hint RVA name
1 0 0024B8C0 SayHello
2 1 0024B910 Test
What did you see instead?
> dumpbin -exports libhello.dll
Microsoft (R) COFF/PE Dumper Version 14.00.24215.1
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file libhello.dll
File Type: DLL
Section contains the following exports for libhello.dll
00000000 characteristics
5C822141 time date stamp Fri Mar 8 16:01:05 2019
0.00 version
1 ordinal base
8462 number of functions
8462 number of names
ordinal hint RVA name
1 0 0024B8C0 SayHello
2 1 0024B910 Test
3 2 0024BC00 _cgo_get_context_function
4 3 00278768 _cgo_init
5 4 0024BAA0 _cgo_is_runtime_initialized
6 5 0024B9E0 _cgo_maybe_run_preinit
7 6 00278770 _cgo_notify_runtime_init_done
8 7 0005E040 _cgo_panic
9 8 0024B980 _cgo_preinit_init
10 9 0024B950 _cgo_release_context
11 A 00278778 _cgo_sys_thread_create
12 B 0024BCF0 _cgo_sys_thread_start
13 C 00278780 _cgo_thread_start
14 D 00057640 _cgo_topofstack
15 E 0024BAD0 _cgo_wait_runtime_init_done
16 F 00518760 _cgo_yield
17 10 0024B5F0 _cgoexp_ad639554f05a_SayHello
18 11 0024B720 _cgoexp_ad639554f05a_Test
19 12 00058E50 _rt0_amd64_windows_lib
20 13 00278748 _rt0_amd64_windows_lib.ptr
21 14 00058EB0 _rt0_amd64_windows_lib_go
22 15 001DFFC0 bufio.(*Reader).Discard
23 16 001DFDC0 bufio.(*Reader).Peek
24 17 001E0120 bufio.(*Reader).Read
25 18 001E0520 bufio.(*Reader).ReadByte
26 19 001E08E0 bufio.(*Reader).ReadLine
27 1A 001E06A0 bufio.(*Reader).ReadSlice
28 1B 001DFB80 bufio.(*Reader).Size
29 1C 001E0600 bufio.(*Reader).UnreadByte
30 1D 001E0B30 bufio.(*Reader).WriteTo
31 1E 001DFB90 bufio.(*Reader).fill
32 1F 001E0E60 bufio.(*Reader).writeBuf
33 20 001E0F70 bufio.(*Writer).Flush
34 21 001E1640 bufio.(*Writer).ReadFrom
35 22 001E0F60 bufio.(*Writer).Size
36 23 001E1150 bufio.(*Writer).Write
37 24 001E13A0 bufio.(*Writer).WriteByte
38 25 001E1470 bufio.(*Writer).WriteString
39 26 0051BA80 bufio.ErrAdvanceTooFar
40 27 0051BA90 bufio.ErrBufferFull
41 28 0051BAA0 bufio.ErrFinalToken
42 29 0051BAB0 bufio.ErrInvalidUnreadByte
43 2A 0051BAC0 bufio.ErrInvalidUnreadRune
44 2B 0051BAD0 bufio.ErrNegativeAdvance
45 2C 0051BAE0 bufio.ErrNegativeCount
46 2D 0051BAF0 bufio.ErrTooLong
47 2E 0051BB00 bufio.errNegativeRead
48 2F 0051BB10 bufio.errNegativeWrite
49 30 001E1D40 bufio.init
50 31 001E19E0 bufio.init.ializers
51 32 00537820 bufio.initdone
52 33 0036C7D0 bufio.statictmp_0
53 34 0036C7E0 bufio.statictmp_1
54 35 000C6600 bytes.(*Buffer).Len
55 36 000C7090 bytes.(*Buffer).Read
56 37 000C71D0 bytes.(*Buffer).ReadByte
57 38 000C6BB0 bytes.(*Buffer).ReadFrom
58 39 000C6620 bytes.(*Buffer).Reset
59 3A 000C6530 bytes.(*Buffer).String
60 3B 000C7260 bytes.(*Buffer).UnreadByte
61 3C 000C6950 bytes.(*Buffer).Write
62 3D 000C6FD0 bytes.(*Buffer).WriteByte
63 3E 000C6A80 bytes.(*Buffer).WriteString
64 3F 000C6E60 bytes.(*Buffer).WriteTo
65 40 000C6640 bytes.(*Buffer).grow
66 41 000C88B0 bytes.(*Reader).Len
67 42 000C88E0 bytes.(*Reader).Read
68 43 000C89E0 bytes.(*Reader).ReadByte
69 44 000C8A60 bytes.(*Reader).UnreadByte
70 45 000C8B20 bytes.(*Reader).WriteTo
71 46 000C7B60 bytes.EqualFold
72 47 0051BB20 bytes.ErrTooLarge
73 48 000C7E10 bytes.Index
74 49 000C7320 bytes.Map
75 4A 000C8C50 bytes.NewReader
76 4B 000C7AC0 bytes.TrimRight
77 4C 000C7670 bytes.TrimRightFunc
78 4D 0051BB30 bytes.errNegativeRead
79 4E 000C8850 bytes.hashStr
80 4F 000C85E0 bytes.indexRabinKarp
81 50 000C8F70 bytes.init
82 51 000C8E90 bytes.init.ializers
83 52 00537821 bytes.initdone
84 53 000C7790 bytes.lastIndexFunc
85 54 000C78A0 bytes.makeASCIISet
86 55 000C78F0 bytes.makeCutsetFunc
87 56 000C8D70 bytes.makeCutsetFunc.func1
88 57 000C8DB0 bytes.makeCutsetFunc.func2
89 58 000C8DE0 bytes.makeCutsetFunc.func3
90 59 000C6DA0 bytes.makeSlice
91 5A 000C8CF0 bytes.makeSlice.func1
92 5B 0036C7F0 bytes.statictmp_2
93 5C 0036C800 bytes.statictmp_5
94 5D 001E8DC0 compress/flate.(*CorruptInputError).Error
95 5E 001E8E90 compress/flate.(*InternalError).Error
96 5F 001E8B50 compress/flate.(*byFreq).Len
97 60 001E8BB0 compress/flate.(*byFreq).Less
98 61 001E8C50 compress/flate.(*byFreq).Swap
99 62 001E89B0 compress/flate.(*byLiteral).Len
100 63 001E8A10 compress/flate.(*byLiteral).Less
101 64 001E8AA0 compress/flate.(*byLiteral).Swap
102 65 001E6090 compress/flate.(*decompressor).Close
103 66 001E5E30 compress/flate.(*decompressor).Read
104 67 001E7DD0 compress/flate.(*decompressor).Reset
105 68 001E7630 compress/flate.(*decompressor).copyData
106 69 001E7270 compress/flate.(*decompressor).dataBlock
107 6A 001E7B10 compress/flate.(*decompressor).huffSym
108 6B 001E6820 compress/flate.(*decompressor).huffmanBlock
...
1678 68D 00279090 encoding/json.hex
1679 68E 00251B80 encoding/json.htmlSafeSet
1680 68F 000D8C00 encoding/json.init
1681 690 000D89B0 encoding/json.init.ializers
1682 691 00537843 encoding/json.initdone
1683 692 000CDE10 encoding/json.intEncoder
1684 693 000CEA80 encoding/json.interfaceEncoder
1685 694 000CD040 encoding/json.invalidValueEncoder
1686 695 000CC2E0 encoding/json.isEmptyValue
1687 696 000CB7B0 encoding/json.isValidNumber
1688 697 000D0820 encoding/json.isValidTag
1689 698 000D9010 encoding/json.jsonError.Error
1690 699 000CF250 encoding/json.mapEncoder.encode
1691 69A 000D92D0 encoding/json.mapEncoder.encode-fm
1692 69B 000D8760 encoding/json.mapEncoder.encode.func1
1693 69C 000CD0A0 encoding/json.marshalerEncoder
...
2084 823 00372C60 go.itab.go.uber.org/zap.errArray,go.uber.org/zap/zapcore.ArrayMarshaler
2085 824 00372C80 go.itab.go.uber.org/zap.float32s,go.uber.org/zap/zapcore.ArrayMarshaler
2086 825 00372CA0 go.itab.go.uber.org/zap.float64s,go.uber.org/zap/zapcore.ArrayMarshaler
2087 826 00372CC0 go.itab.go.uber.org/zap.int16s,go.uber.org/zap/zapcore.ArrayMarshaler
2088 827 00372CE0 go.itab.go.uber.org/zap.int32s,go.uber.org/zap/zapcore.ArrayMarshaler
2089 828 00372D00 go.itab.go.uber.org/zap.int64s,go.uber.org/zap/zapcore.ArrayMarshaler
2090 829 00372D20 go.itab.go.uber.org/zap.int8s,go.uber.org/zap/zapcore.ArrayMarshaler
2091 82A 00372D40 go.itab.go.uber.org/zap.ints,go.uber.org/zap/zapcore.ArrayMarshaler
2092 82B 00377280 go.itab.go.uber.org/zap.nopCloserSink,go.uber.org/zap.Sink
2093 82C 00372D60 go.itab.go.uber.org/zap.optionFunc,go.uber.org/zap.Option
...
2855 B26 00181CD0 internal/x/crypto/chacha20poly1305.(*chacha20poly1305).Seal
2856 B27 00182470 internal/x/crypto/chacha20poly1305.(*chacha20poly1305).open
2857 B28 00182F50 internal/x/crypto/chacha20poly1305.(*chacha20poly1305).openGeneric
2858 B29 00182100 internal/x/crypto/chacha20poly1305.(*chacha20poly1305).seal
2859 B2A 001828B0 internal/x/crypto/chacha20poly1305.(*chacha20poly1305).sealGeneric
...
Third-party library functions have also been exported.
Since most of the code for the project is not stored in the main package, it is actually equivalent to all function names being leaked.
It is hoped that only functions that explicitly mark //export will be exported, not all functions will be exported.
This problem also occurs in Linux.
This seems like a significant issue and quite easy to hit on Windows.
Using go 1.15.2 (but this also reproduces on 1.14 and 1.13) I can hit the error "Error: export ordinal too large" during compilation after importing a few 3rd party packages. My code only intends to export one function, but as this issue describes...dumpbin shows thousands of symbols being exported for each of the 3rd party packages that I imported. This quickly causes me to hit the 'export ordinal too large error'.
Is there any workaround that you are aware of for this? (switching to another buildmode such as -buildmode=exe is not an option for projects which require a shared library to be produced).
Also reported in #40795
cc @aclements @thanm @jeremyfaller @cherrymui @alexbrainman @mattn @zx2c4
Change https://golang.org/cl/262797 mentions this issue: cmd/cgo: avoid exporting all symbols on windows buildmode=c-shared
I´ve submitted a commit which adds __declspec(dllexport)
attribute on the C wrappers of exported functions. As per https://sourceware.org/binutils/docs/ld/WIN32.html this will avoid exporting all the unnecessary symbols.
I guess it can also fix #40795 by adding a dummy __declspec(dllexport)
function but I´ll try it in another CL.
That is fantastic news - thanks for taking a look at this @qmuntal ! Is there a workaround that we use today with golang 1.13 or 1.14 to avoid the unnecessary symbols being exported? I was trying to see if there is a way to call go build with params that would achieve something like -Wl,--exclude-all-symbols but still export the symbols for the functions we marked in the go code with //export ?
Edit: The following approach worked for me, but i'm still curious if there is a simpler/better way:
go build -buildmode=c-shared -ldflags="-extldflags=-Wl,--version-script=myproject.version" -o bin\myproject.dll myproject/sdk
Where the content of myproject.version file is:
CODEABI_1.0 {
global: MyExportedFunction;MyOtherExportedFunction;
local: *;
};
Resultant DLL only exports the functions I requested in the global section of the myproject.version file (and the syntax of that file also supports wildcard * to make it easy to export a bunch of functions without having to explicitly name each one).
@gopherbot please backport to 1.15
buildmode=pie became the default in 1.15, so folks have to manually set buildmode=exe to work around this bug.
Backport issue(s) opened: #43591 (for 1.15).
Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://golang.org/wiki/MinorReleases.
Change https://golang.org/cl/300693 mentions this issue: [release-branch.go1.15] cmd/cgo: avoid exporting all symbols on windows buildmode=c-shared