summaryrefslogtreecommitdiffstats
path: root/src/cmd/cgo
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:19:13 +0000
commitccd992355df7192993c666236047820244914598 (patch)
treef00fea65147227b7743083c6148396f74cd66935 /src/cmd/cgo
parentInitial commit. (diff)
downloadgolang-1.21-ccd992355df7192993c666236047820244914598.tar.xz
golang-1.21-ccd992355df7192993c666236047820244914598.zip
Adding upstream version 1.21.8.upstream/1.21.8
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/cgo')
-rw-r--r--src/cmd/cgo/ast.go577
-rw-r--r--src/cmd/cgo/ast_go1.go25
-rw-r--r--src/cmd/cgo/ast_go118.go32
-rw-r--r--src/cmd/cgo/doc.go1064
-rw-r--r--src/cmd/cgo/gcc.go3519
-rw-r--r--src/cmd/cgo/godefs.go170
-rw-r--r--src/cmd/cgo/internal/cgotest/overlaydir.go75
-rw-r--r--src/cmd/cgo/internal/swig/swig_test.go153
-rw-r--r--src/cmd/cgo/internal/swig/testdata/callback/main.cc15
-rw-r--r--src/cmd/cgo/internal/swig/testdata/callback/main.go60
-rw-r--r--src/cmd/cgo/internal/swig/testdata/callback/main.h20
-rw-r--r--src/cmd/cgo/internal/swig/testdata/callback/main.swigcxx18
-rw-r--r--src/cmd/cgo/internal/swig/testdata/stdio/main.go45
-rw-r--r--src/cmd/cgo/internal/swig/testdata/stdio/main.swig24
-rw-r--r--src/cmd/cgo/internal/test/backdoor.go11
-rw-r--r--src/cmd/cgo/internal/test/buildid_linux.go78
-rw-r--r--src/cmd/cgo/internal/test/callback.go1782
-rw-r--r--src/cmd/cgo/internal/test/callback_c.c67
-rw-r--r--src/cmd/cgo/internal/test/callback_c_gc.c25
-rw-r--r--src/cmd/cgo/internal/test/callback_c_gccgo.c21
-rw-r--r--src/cmd/cgo/internal/test/cgo_linux_test.go45
-rw-r--r--src/cmd/cgo/internal/test/cgo_stubs_android_test.go12
-rw-r--r--src/cmd/cgo/internal/test/cgo_test.go112
-rw-r--r--src/cmd/cgo/internal/test/cgo_thread_lock.go57
-rw-r--r--src/cmd/cgo/internal/test/cgo_unix_test.go13
-rw-r--r--src/cmd/cgo/internal/test/cthread_unix.c58
-rw-r--r--src/cmd/cgo/internal/test/cthread_windows.c59
-rw-r--r--src/cmd/cgo/internal/test/gcc68255.go19
-rw-r--r--src/cmd/cgo/internal/test/gcc68255/a.go17
-rw-r--r--src/cmd/cgo/internal/test/gcc68255/c.c8
-rw-r--r--src/cmd/cgo/internal/test/gcc68255/c.h5
-rw-r--r--src/cmd/cgo/internal/test/issue1435.go203
-rw-r--r--src/cmd/cgo/internal/test/issue18146.go128
-rw-r--r--src/cmd/cgo/internal/test/issue20266.go21
-rw-r--r--src/cmd/cgo/internal/test/issue20266/issue20266.h9
-rw-r--r--src/cmd/cgo/internal/test/issue20910.c19
-rw-r--r--src/cmd/cgo/internal/test/issue21897.go56
-rw-r--r--src/cmd/cgo/internal/test/issue21897b.go13
-rw-r--r--src/cmd/cgo/internal/test/issue23555.go15
-rw-r--r--src/cmd/cgo/internal/test/issue23555a/a.go12
-rw-r--r--src/cmd/cgo/internal/test/issue23555b/a.go12
-rw-r--r--src/cmd/cgo/internal/test/issue24161_darwin_test.go33
-rw-r--r--src/cmd/cgo/internal/test/issue24161arg/def.go17
-rw-r--r--src/cmd/cgo/internal/test/issue24161arg/use.go19
-rw-r--r--src/cmd/cgo/internal/test/issue24161e0/main.go29
-rw-r--r--src/cmd/cgo/internal/test/issue24161e1/main.go38
-rw-r--r--src/cmd/cgo/internal/test/issue24161e2/main.go40
-rw-r--r--src/cmd/cgo/internal/test/issue24161res/restype.go23
-rw-r--r--src/cmd/cgo/internal/test/issue26213/jni.h29
-rw-r--r--src/cmd/cgo/internal/test/issue26213/test26213.go46
-rw-r--r--src/cmd/cgo/internal/test/issue26430.go12
-rw-r--r--src/cmd/cgo/internal/test/issue26430/a.go13
-rw-r--r--src/cmd/cgo/internal/test/issue26430/b.go13
-rw-r--r--src/cmd/cgo/internal/test/issue26743.go12
-rw-r--r--src/cmd/cgo/internal/test/issue26743/a.go11
-rw-r--r--src/cmd/cgo/internal/test/issue26743/b.go9
-rw-r--r--src/cmd/cgo/internal/test/issue27054/egl.h8
-rw-r--r--src/cmd/cgo/internal/test/issue27054/test27054.go21
-rw-r--r--src/cmd/cgo/internal/test/issue27340.go14
-rw-r--r--src/cmd/cgo/internal/test/issue27340/a.go42
-rw-r--r--src/cmd/cgo/internal/test/issue29563.go12
-rw-r--r--src/cmd/cgo/internal/test/issue29563/weak.go13
-rw-r--r--src/cmd/cgo/internal/test/issue29563/weak1.c11
-rw-r--r--src/cmd/cgo/internal/test/issue29563/weak2.c11
-rw-r--r--src/cmd/cgo/internal/test/issue30527.go16
-rw-r--r--src/cmd/cgo/internal/test/issue30527/a.go19
-rw-r--r--src/cmd/cgo/internal/test/issue30527/b.go11
-rw-r--r--src/cmd/cgo/internal/test/issue31891.c13
-rw-r--r--src/cmd/cgo/internal/test/issue4029.c29
-rw-r--r--src/cmd/cgo/internal/test/issue4029.go77
-rw-r--r--src/cmd/cgo/internal/test/issue4029w.go12
-rw-r--r--src/cmd/cgo/internal/test/issue41761.go20
-rw-r--r--src/cmd/cgo/internal/test/issue41761a/a.go14
-rw-r--r--src/cmd/cgo/internal/test/issue42018.go13
-rw-r--r--src/cmd/cgo/internal/test/issue42018_windows.go46
-rw-r--r--src/cmd/cgo/internal/test/issue42495.go15
-rw-r--r--src/cmd/cgo/internal/test/issue4273.c10
-rw-r--r--src/cmd/cgo/internal/test/issue4273b.c11
-rw-r--r--src/cmd/cgo/internal/test/issue4339.c18
-rw-r--r--src/cmd/cgo/internal/test/issue4339.h9
-rw-r--r--src/cmd/cgo/internal/test/issue43639.go11
-rw-r--r--src/cmd/cgo/internal/test/issue43639/a.go8
-rw-r--r--src/cmd/cgo/internal/test/issue52611.go15
-rw-r--r--src/cmd/cgo/internal/test/issue52611a/a.go16
-rw-r--r--src/cmd/cgo/internal/test/issue52611a/b.go11
-rw-r--r--src/cmd/cgo/internal/test/issue52611b/a.go11
-rw-r--r--src/cmd/cgo/internal/test/issue52611b/b.go16
-rw-r--r--src/cmd/cgo/internal/test/issue5548_c.c24
-rw-r--r--src/cmd/cgo/internal/test/issue5740a.c9
-rw-r--r--src/cmd/cgo/internal/test/issue5740b.c9
-rw-r--r--src/cmd/cgo/internal/test/issue6833_c.c10
-rw-r--r--src/cmd/cgo/internal/test/issue6907export_c.c11
-rw-r--r--src/cmd/cgo/internal/test/issue6997_linux.c28
-rw-r--r--src/cmd/cgo/internal/test/issue6997_linux.go44
-rw-r--r--src/cmd/cgo/internal/test/issue7234_test.go21
-rw-r--r--src/cmd/cgo/internal/test/issue8148.c11
-rw-r--r--src/cmd/cgo/internal/test/issue8148.go24
-rw-r--r--src/cmd/cgo/internal/test/issue8331.h7
-rw-r--r--src/cmd/cgo/internal/test/issue8517.go13
-rw-r--r--src/cmd/cgo/internal/test/issue8517_windows.c24
-rw-r--r--src/cmd/cgo/internal/test/issue8517_windows.go45
-rw-r--r--src/cmd/cgo/internal/test/issue8694.go40
-rw-r--r--src/cmd/cgo/internal/test/issue8756.go17
-rw-r--r--src/cmd/cgo/internal/test/issue8756/issue8756.go11
-rw-r--r--src/cmd/cgo/internal/test/issue8811.c8
-rw-r--r--src/cmd/cgo/internal/test/issue8828.go16
-rw-r--r--src/cmd/cgo/internal/test/issue8828/issue8828.c7
-rw-r--r--src/cmd/cgo/internal/test/issue8828/trivial.go8
-rw-r--r--src/cmd/cgo/internal/test/issue9026.go15
-rw-r--r--src/cmd/cgo/internal/test/issue9026/issue9026.go36
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_386.s27
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_amd64x.s26
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_arm.s39
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_arm64.s39
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_loong64.s28
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_mips64x.s32
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_mipsx.s30
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_ppc64x.s31
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_riscv64.s30
-rw-r--r--src/cmd/cgo/internal/test/issue9400/asm_s390x.s26
-rw-r--r--src/cmd/cgo/internal/test/issue9400/gccgo.go26
-rw-r--r--src/cmd/cgo/internal/test/issue9400/stubs.go11
-rw-r--r--src/cmd/cgo/internal/test/issue9400_linux.go67
-rw-r--r--src/cmd/cgo/internal/test/issue9510.go26
-rw-r--r--src/cmd/cgo/internal/test/issue9510a/a.go15
-rw-r--r--src/cmd/cgo/internal/test/issue9510b/b.go15
-rw-r--r--src/cmd/cgo/internal/test/setgid2_linux.go35
-rw-r--r--src/cmd/cgo/internal/test/setgid_linux.go49
-rw-r--r--src/cmd/cgo/internal/test/sigaltstack.go78
-rw-r--r--src/cmd/cgo/internal/test/sigprocmask.c51
-rw-r--r--src/cmd/cgo/internal/test/sigprocmask.go40
-rw-r--r--src/cmd/cgo/internal/test/test.go2303
-rw-r--r--src/cmd/cgo/internal/test/test26213.go17
-rw-r--r--src/cmd/cgo/internal/test/test_unix.go11
-rw-r--r--src/cmd/cgo/internal/test/test_windows.go9
-rw-r--r--src/cmd/cgo/internal/test/testx.c24
-rw-r--r--src/cmd/cgo/internal/test/testx.go597
-rw-r--r--src/cmd/cgo/internal/test/typeparam.go17
-rw-r--r--src/cmd/cgo/internal/testcarchive/carchive_test.go1399
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/libgo/libgo.go53
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/libgo2/libgo2.go91
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/libgo3/libgo3.go60
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/libgo4/libgo4.go55
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/libgo6/sigprof.go25
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/libgo7/sink.go17
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/libgo8/a.go36
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/libgo9/a.go14
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main.c48
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main2.c239
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main3.c210
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main4.c204
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main5.c105
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main6.c34
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main7.c18
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main8.c16
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main9.c24
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main_unix.c59
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/main_windows.c17
-rw-r--r--src/cmd/cgo/internal/testcarchive/testdata/p/p.go10
-rw-r--r--src/cmd/cgo/internal/testcshared/cshared_test.go874
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/go2c2go/go/shlib.go12
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/go2c2go/m1/c.c9
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/go2c2go/m1/main.go22
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/go2c2go/m2/main.go22
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/issue36233/issue36233.go30
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/libgo/libgo.go46
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/libgo2/dup2.go13
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/libgo2/dup3.go13
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/libgo2/libgo2.go52
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/libgo4/libgo4.go47
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/libgo5/libgo5.go47
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/main0.c42
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/main1.c69
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/main2.c56
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/main3.c29
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/main4.c215
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/main5.c199
-rw-r--r--src/cmd/cgo/internal/testcshared/testdata/p/p.go13
-rw-r--r--src/cmd/cgo/internal/testerrors/argposition_test.go137
-rw-r--r--src/cmd/cgo/internal/testerrors/badsym_test.go231
-rw-r--r--src/cmd/cgo/internal/testerrors/errors_test.go168
-rw-r--r--src/cmd/cgo/internal/testerrors/ptr_test.go706
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/err1.go22
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/err2.go110
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/err4.go15
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/err5.go10
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue11097a.go15
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue11097b.go15
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue14669.go23
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue18452.go18
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue18889.go7
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue28069.go26
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue28721.go29
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue33061.go17
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue42580.go44
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/issue50710.go14
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/long_double_size.go16
-rw-r--r--src/cmd/cgo/internal/testerrors/testdata/malloc.go34
-rw-r--r--src/cmd/cgo/internal/testfortran/fortran_test.go85
-rw-r--r--src/cmd/cgo/internal/testfortran/testdata/helloworld/helloworld.f903
-rw-r--r--src/cmd/cgo/internal/testfortran/testdata/testprog/answer.f909
-rw-r--r--src/cmd/cgo/internal/testfortran/testdata/testprog/fortran.go21
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/anonunion.go26
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/bitfields.go31
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go18
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/issue37479.go33
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/issue37621.go23
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/issue38649.go15
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/issue39534.go12
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/issue48396.go18
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/issue8478.go20
-rw-r--r--src/cmd/cgo/internal/testgodefs/testdata/main.go57
-rw-r--r--src/cmd/cgo/internal/testgodefs/testgodefs_test.go116
-rw-r--r--src/cmd/cgo/internal/testlife/life_test.go65
-rw-r--r--src/cmd/cgo/internal/testlife/testdata/c-life.c56
-rw-r--r--src/cmd/cgo/internal/testlife/testdata/life.go40
-rw-r--r--src/cmd/cgo/internal/testlife/testdata/life.h7
-rw-r--r--src/cmd/cgo/internal/testlife/testdata/main.go47
-rw-r--r--src/cmd/cgo/internal/testlife/testdata/main.out16
-rw-r--r--src/cmd/cgo/internal/testnocgo/nocgo.go22
-rw-r--r--src/cmd/cgo/internal/testnocgo/nocgo_test.go14
-rw-r--r--src/cmd/cgo/internal/testplugin/altpath/testdata/common/common.go11
-rw-r--r--src/cmd/cgo/internal/testplugin/altpath/testdata/plugin-mismatch/main.go17
-rw-r--r--src/cmd/cgo/internal/testplugin/plugin_test.go406
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/checkdwarf/main.go106
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/common/common.go11
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/forkexec/main.go30
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/host/host.go176
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/iface/main.go47
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/iface_a/a.go17
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/iface_b/b.go17
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/iface_i/i.go17
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue18584/main.go23
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue18584/plugin.go19
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue18676/dynamodbstreamsevt/definition.go13
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue18676/main.go32
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue18676/plugin.go11
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue19418/main.go29
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue19418/plugin.go7
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue19529/plugin.go15
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue19534/main.go23
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue19534/plugin.go9
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue22175/main.go28
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue22175/plugin1.go21
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue22175/plugin2.go9
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue22295.pkg/main.go28
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue22295.pkg/plugin.go16
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue24351/main.go21
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue24351/plugin.go14
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue25756/main.go52
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/c-life.c56
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/life.go40
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/life.h7
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue44956/base/base.go7
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue44956/main.go47
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue44956/plugin1.go9
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue44956/plugin2.go11
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue52937/main.go9
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue53989/main.go32
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue53989/p/p.go52
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue53989/plugin.go13
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue62430/main.go35
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/issue62430/plugin.go11
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/mangle/plugin.go38
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/method/main.go26
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/method/plugin.go13
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/method2/main.go32
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/method2/p/p.go9
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/method2/plugin.go11
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/method3/main.go32
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/method3/p/p.go17
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/method3/plugin.go11
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/plugin1/plugin1.go57
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/plugin2/plugin2.go44
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/sub/plugin1/plugin1.go23
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/unnamed1/main.go25
-rw-r--r--src/cmd/cgo/internal/testplugin/testdata/unnamed2/main.go23
-rw-r--r--src/cmd/cgo/internal/testsanitizers/asan_test.go149
-rw-r--r--src/cmd/cgo/internal/testsanitizers/cc_test.go582
-rw-r--r--src/cmd/cgo/internal/testsanitizers/cshared_test.go98
-rw-r--r--src/cmd/cgo/internal/testsanitizers/empty_test.go8
-rw-r--r--src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go93
-rw-r--r--src/cmd/cgo/internal/testsanitizers/msan_test.go84
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/arena_fail.go27
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan1_fail.go28
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan2_fail.go34
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan3_fail.go23
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan4_fail.go22
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan5_fail.go21
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_global1_fail.go25
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_global2_fail.go31
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_global3_fail.go28
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_global4_fail.go25
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_global5.go22
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/main.go28
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/p/p.go12
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail1.go27
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail2.go28
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail3.go21
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/asan_useAfterReturn.go26
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer1.go16
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer2.c11
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer2.go16
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan.go35
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan2.go35
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan2_cmsan.go38
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan3.go33
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan4.go50
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan5.go57
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan6.go75
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan7.go38
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan8.go109
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan_fail.go36
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/msan_shared.go12
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan.go44
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan10.go31
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan11.go55
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan12.go35
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan13.go90
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan14.go53
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan2.go55
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan3.go40
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan4.go34
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan5.go51
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan6.go49
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan7.go40
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan8.go60
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan9.go67
-rw-r--r--src/cmd/cgo/internal/testsanitizers/testdata/tsan_shared.go63
-rw-r--r--src/cmd/cgo/internal/testsanitizers/tsan_test.go80
-rw-r--r--src/cmd/cgo/internal/testshared/shared_test.go1174
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/dep2/dep2.go15
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/dep3/dep3.go22
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/depBase/asm.s10
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/depBase/dep.go37
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/depBase/gccgo.go9
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/depBase/stubs.go9
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/division/division.go17
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/exe/exe.go45
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/exe2/exe2.go8
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/exe3/exe3.go7
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/execgo/exe.go8
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/explicit/explicit.go9
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/gcdata/main/main.go37
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/gcdata/p/p.go7
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/global/main.go71
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/globallib/global.go17
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/iface/main.go17
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/iface_a/a.go17
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/iface_b/b.go17
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/iface_i/i.go17
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/implicit/implicit.go5
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/implicitcmd/implicitcmd.go10
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue25065/a.go21
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue30768/issue30768lib/lib.go11
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue30768/x_test.go22
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue39777/a/a.go9
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue39777/b/b.go7
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue44031/a/a.go9
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue44031/b/b.go17
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue44031/main/main.go20
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue47837/a/a.go19
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue47837/main/main.go14
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/issue58966/main.go15
-rw-r--r--src/cmd/cgo/internal/testshared/testdata/trivial/trivial.go9
-rw-r--r--src/cmd/cgo/internal/testso/so_test.go137
-rw-r--r--src/cmd/cgo/internal/testso/testdata/so/cgoso.c14
-rw-r--r--src/cmd/cgo/internal/testso/testdata/so/cgoso.go32
-rw-r--r--src/cmd/cgo/internal/testso/testdata/so/cgoso_c.c39
-rw-r--r--src/cmd/cgo/internal/testso/testdata/so/cgoso_unix.go20
-rw-r--r--src/cmd/cgo/internal/testso/testdata/so/main.go13
-rw-r--r--src/cmd/cgo/internal/testso/testdata/sovar/cgoso.go44
-rw-r--r--src/cmd/cgo/internal/testso/testdata/sovar/cgoso_c.c7
-rw-r--r--src/cmd/cgo/internal/testso/testdata/sovar/cgoso_c.h17
-rw-r--r--src/cmd/cgo/internal/testso/testdata/sovar/main.go13
-rw-r--r--src/cmd/cgo/internal/teststdio/stdio_test.go77
-rw-r--r--src/cmd/cgo/internal/teststdio/testdata/chain.go46
-rw-r--r--src/cmd/cgo/internal/teststdio/testdata/chain.out55
-rw-r--r--src/cmd/cgo/internal/teststdio/testdata/fib.go50
-rw-r--r--src/cmd/cgo/internal/teststdio/testdata/fib.out91
-rw-r--r--src/cmd/cgo/internal/teststdio/testdata/hello.go13
-rw-r--r--src/cmd/cgo/internal/teststdio/testdata/hello.out1
-rw-r--r--src/cmd/cgo/internal/teststdio/testdata/stdio/file.go42
-rw-r--r--src/cmd/cgo/internal/teststdio/testdata/stdio/stdio.go20
-rw-r--r--src/cmd/cgo/internal/testtls/tls.c47
-rw-r--r--src/cmd/cgo/internal/testtls/tls.go34
-rw-r--r--src/cmd/cgo/internal/testtls/tls_none.go13
-rw-r--r--src/cmd/cgo/internal/testtls/tls_test.go11
-rw-r--r--src/cmd/cgo/main.go501
-rw-r--r--src/cmd/cgo/out.go1988
-rw-r--r--src/cmd/cgo/util.go114
391 files changed, 30625 insertions, 0 deletions
diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
new file mode 100644
index 0000000..3cbbeaf
--- /dev/null
+++ b/src/cmd/cgo/ast.go
@@ -0,0 +1,577 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Parse input AST and prepare Prog structure.
+
+package main
+
+import (
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "os"
+ "strings"
+)
+
+func parse(name string, src []byte, flags parser.Mode) *ast.File {
+ ast1, err := parser.ParseFile(fset, name, src, flags)
+ if err != nil {
+ if list, ok := err.(scanner.ErrorList); ok {
+ // If err is a scanner.ErrorList, its String will print just
+ // the first error and then (+n more errors).
+ // Instead, turn it into a new Error that will return
+ // details for all the errors.
+ for _, e := range list {
+ fmt.Fprintln(os.Stderr, e)
+ }
+ os.Exit(2)
+ }
+ fatalf("parsing %s: %s", name, err)
+ }
+ return ast1
+}
+
+func sourceLine(n ast.Node) int {
+ return fset.Position(n.Pos()).Line
+}
+
+// ParseGo populates f with information learned from the Go source code
+// which was read from the named file. It gathers the C preamble
+// attached to the import "C" comment, a list of references to C.xxx,
+// a list of exported functions, and the actual AST, to be rewritten and
+// printed.
+func (f *File) ParseGo(abspath string, src []byte) {
+ // Two different parses: once with comments, once without.
+ // The printer is not good enough at printing comments in the
+ // right place when we start editing the AST behind its back,
+ // so we use ast1 to look for the doc comments on import "C"
+ // and on exported functions, and we use ast2 for translating
+ // and reprinting.
+ // In cgo mode, we ignore ast2 and just apply edits directly
+ // the text behind ast1. In godefs mode we modify and print ast2.
+ ast1 := parse(abspath, src, parser.SkipObjectResolution|parser.ParseComments)
+ ast2 := parse(abspath, src, parser.SkipObjectResolution)
+
+ f.Package = ast1.Name.Name
+ f.Name = make(map[string]*Name)
+ f.NamePos = make(map[*Name]token.Pos)
+
+ // In ast1, find the import "C" line and get any extra C preamble.
+ sawC := false
+ for _, decl := range ast1.Decls {
+ switch decl := decl.(type) {
+ case *ast.GenDecl:
+ for _, spec := range decl.Specs {
+ s, ok := spec.(*ast.ImportSpec)
+ if !ok || s.Path.Value != `"C"` {
+ continue
+ }
+ sawC = true
+ if s.Name != nil {
+ error_(s.Path.Pos(), `cannot rename import "C"`)
+ }
+ cg := s.Doc
+ if cg == nil && len(decl.Specs) == 1 {
+ cg = decl.Doc
+ }
+ if cg != nil {
+ if strings.ContainsAny(abspath, "\r\n") {
+ // This should have been checked when the file path was first resolved,
+ // but we double check here just to be sure.
+ fatalf("internal error: ParseGo: abspath contains unexpected newline character: %q", abspath)
+ }
+ f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), abspath)
+ f.Preamble += commentText(cg) + "\n"
+ f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
+ }
+ }
+
+ case *ast.FuncDecl:
+ // Also, reject attempts to declare methods on C.T or *C.T.
+ // (The generated code would otherwise accept this
+ // invalid input; see issue #57926.)
+ if decl.Recv != nil && len(decl.Recv.List) > 0 {
+ recvType := decl.Recv.List[0].Type
+ if recvType != nil {
+ t := recvType
+ if star, ok := unparen(t).(*ast.StarExpr); ok {
+ t = star.X
+ }
+ if sel, ok := unparen(t).(*ast.SelectorExpr); ok {
+ var buf strings.Builder
+ format.Node(&buf, fset, recvType)
+ error_(sel.Pos(), `cannot define new methods on non-local type %s`, &buf)
+ }
+ }
+ }
+ }
+
+ }
+ if !sawC {
+ error_(ast1.Package, `cannot find import "C"`)
+ }
+
+ // In ast2, strip the import "C" line.
+ if *godefs {
+ w := 0
+ for _, decl := range ast2.Decls {
+ d, ok := decl.(*ast.GenDecl)
+ if !ok {
+ ast2.Decls[w] = decl
+ w++
+ continue
+ }
+ ws := 0
+ for _, spec := range d.Specs {
+ s, ok := spec.(*ast.ImportSpec)
+ if !ok || s.Path.Value != `"C"` {
+ d.Specs[ws] = spec
+ ws++
+ }
+ }
+ if ws == 0 {
+ continue
+ }
+ d.Specs = d.Specs[0:ws]
+ ast2.Decls[w] = d
+ w++
+ }
+ ast2.Decls = ast2.Decls[0:w]
+ } else {
+ for _, decl := range ast2.Decls {
+ d, ok := decl.(*ast.GenDecl)
+ if !ok {
+ continue
+ }
+ for _, spec := range d.Specs {
+ if s, ok := spec.(*ast.ImportSpec); ok && s.Path.Value == `"C"` {
+ // Replace "C" with _ "unsafe", to keep program valid.
+ // (Deleting import statement or clause is not safe if it is followed
+ // in the source by an explicit semicolon.)
+ f.Edit.Replace(f.offset(s.Path.Pos()), f.offset(s.Path.End()), `_ "unsafe"`)
+ }
+ }
+ }
+ }
+
+ // Accumulate pointers to uses of C.x.
+ if f.Ref == nil {
+ f.Ref = make([]*Ref, 0, 8)
+ }
+ f.walk(ast2, ctxProg, (*File).validateIdents)
+ f.walk(ast2, ctxProg, (*File).saveExprs)
+
+ // Accumulate exported functions.
+ // The comments are only on ast1 but we need to
+ // save the function bodies from ast2.
+ // The first walk fills in ExpFunc, and the
+ // second walk changes the entries to
+ // refer to ast2 instead.
+ f.walk(ast1, ctxProg, (*File).saveExport)
+ f.walk(ast2, ctxProg, (*File).saveExport2)
+
+ f.Comments = ast1.Comments
+ f.AST = ast2
+}
+
+// Like ast.CommentGroup's Text method but preserves
+// leading blank lines, so that line numbers line up.
+func commentText(g *ast.CommentGroup) string {
+ var pieces []string
+ for _, com := range g.List {
+ c := com.Text
+ // Remove comment markers.
+ // The parser has given us exactly the comment text.
+ switch c[1] {
+ case '/':
+ //-style comment (no newline at the end)
+ c = c[2:] + "\n"
+ case '*':
+ /*-style comment */
+ c = c[2 : len(c)-2]
+ }
+ pieces = append(pieces, c)
+ }
+ return strings.Join(pieces, "")
+}
+
+func (f *File) validateIdents(x interface{}, context astContext) {
+ if x, ok := x.(*ast.Ident); ok {
+ if f.isMangledName(x.Name) {
+ error_(x.Pos(), "identifier %q may conflict with identifiers generated by cgo", x.Name)
+ }
+ }
+}
+
+// Save various references we are going to need later.
+func (f *File) saveExprs(x interface{}, context astContext) {
+ switch x := x.(type) {
+ case *ast.Expr:
+ switch (*x).(type) {
+ case *ast.SelectorExpr:
+ f.saveRef(x, context)
+ }
+ case *ast.CallExpr:
+ f.saveCall(x, context)
+ }
+}
+
+// Save references to C.xxx for later processing.
+func (f *File) saveRef(n *ast.Expr, context astContext) {
+ sel := (*n).(*ast.SelectorExpr)
+ // For now, assume that the only instance of capital C is when
+ // used as the imported package identifier.
+ // The parser should take care of scoping in the future, so
+ // that we will be able to distinguish a "top-level C" from a
+ // local C.
+ if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
+ return
+ }
+ if context == ctxAssign2 {
+ context = ctxExpr
+ }
+ if context == ctxEmbedType {
+ error_(sel.Pos(), "cannot embed C type")
+ }
+ goname := sel.Sel.Name
+ if goname == "errno" {
+ error_(sel.Pos(), "cannot refer to errno directly; see documentation")
+ return
+ }
+ if goname == "_CMalloc" {
+ error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
+ return
+ }
+ if goname == "malloc" {
+ goname = "_CMalloc"
+ }
+ name := f.Name[goname]
+ if name == nil {
+ name = &Name{
+ Go: goname,
+ }
+ f.Name[goname] = name
+ f.NamePos[name] = sel.Pos()
+ }
+ f.Ref = append(f.Ref, &Ref{
+ Name: name,
+ Expr: n,
+ Context: context,
+ })
+}
+
+// Save calls to C.xxx for later processing.
+func (f *File) saveCall(call *ast.CallExpr, context astContext) {
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
+ return
+ }
+ c := &Call{Call: call, Deferred: context == ctxDefer}
+ f.Calls = append(f.Calls, c)
+}
+
+// If a function should be exported add it to ExpFunc.
+func (f *File) saveExport(x interface{}, context astContext) {
+ n, ok := x.(*ast.FuncDecl)
+ if !ok {
+ return
+ }
+
+ if n.Doc == nil {
+ return
+ }
+ for _, c := range n.Doc.List {
+ if !strings.HasPrefix(c.Text, "//export ") {
+ continue
+ }
+
+ name := strings.TrimSpace(c.Text[9:])
+ if name == "" {
+ error_(c.Pos(), "export missing name")
+ }
+
+ if name != n.Name.Name {
+ error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
+ }
+
+ doc := ""
+ for _, c1 := range n.Doc.List {
+ if c1 != c {
+ doc += c1.Text + "\n"
+ }
+ }
+
+ f.ExpFunc = append(f.ExpFunc, &ExpFunc{
+ Func: n,
+ ExpName: name,
+ Doc: doc,
+ })
+ break
+ }
+}
+
+// Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
+func (f *File) saveExport2(x interface{}, context astContext) {
+ n, ok := x.(*ast.FuncDecl)
+ if !ok {
+ return
+ }
+
+ for _, exp := range f.ExpFunc {
+ if exp.Func.Name.Name == n.Name.Name {
+ exp.Func = n
+ break
+ }
+ }
+}
+
+type astContext int
+
+const (
+ ctxProg astContext = iota
+ ctxEmbedType
+ ctxType
+ ctxStmt
+ ctxExpr
+ ctxField
+ ctxParam
+ ctxAssign2 // assignment of a single expression to two variables
+ ctxSwitch
+ ctxTypeSwitch
+ ctxFile
+ ctxDecl
+ ctxSpec
+ ctxDefer
+ ctxCall // any function call other than ctxCall2
+ ctxCall2 // function call whose result is assigned to two variables
+ ctxSelector
+)
+
+// walk walks the AST x, calling visit(f, x, context) for each node.
+func (f *File) walk(x interface{}, context astContext, visit func(*File, interface{}, astContext)) {
+ visit(f, x, context)
+ switch n := x.(type) {
+ case *ast.Expr:
+ f.walk(*n, context, visit)
+
+ // everything else just recurs
+ default:
+ f.walkUnexpected(x, context, visit)
+
+ case nil:
+
+ // These are ordered and grouped to match ../../go/ast/ast.go
+ case *ast.Field:
+ if len(n.Names) == 0 && context == ctxField {
+ f.walk(&n.Type, ctxEmbedType, visit)
+ } else {
+ f.walk(&n.Type, ctxType, visit)
+ }
+ case *ast.FieldList:
+ for _, field := range n.List {
+ f.walk(field, context, visit)
+ }
+ case *ast.BadExpr:
+ case *ast.Ident:
+ case *ast.Ellipsis:
+ f.walk(&n.Elt, ctxType, visit)
+ case *ast.BasicLit:
+ case *ast.FuncLit:
+ f.walk(n.Type, ctxType, visit)
+ f.walk(n.Body, ctxStmt, visit)
+ case *ast.CompositeLit:
+ f.walk(&n.Type, ctxType, visit)
+ f.walk(n.Elts, ctxExpr, visit)
+ case *ast.ParenExpr:
+ f.walk(&n.X, context, visit)
+ case *ast.SelectorExpr:
+ f.walk(&n.X, ctxSelector, visit)
+ case *ast.IndexExpr:
+ f.walk(&n.X, ctxExpr, visit)
+ f.walk(&n.Index, ctxExpr, visit)
+ case *ast.SliceExpr:
+ f.walk(&n.X, ctxExpr, visit)
+ if n.Low != nil {
+ f.walk(&n.Low, ctxExpr, visit)
+ }
+ if n.High != nil {
+ f.walk(&n.High, ctxExpr, visit)
+ }
+ if n.Max != nil {
+ f.walk(&n.Max, ctxExpr, visit)
+ }
+ case *ast.TypeAssertExpr:
+ f.walk(&n.X, ctxExpr, visit)
+ f.walk(&n.Type, ctxType, visit)
+ case *ast.CallExpr:
+ if context == ctxAssign2 {
+ f.walk(&n.Fun, ctxCall2, visit)
+ } else {
+ f.walk(&n.Fun, ctxCall, visit)
+ }
+ f.walk(n.Args, ctxExpr, visit)
+ case *ast.StarExpr:
+ f.walk(&n.X, context, visit)
+ case *ast.UnaryExpr:
+ f.walk(&n.X, ctxExpr, visit)
+ case *ast.BinaryExpr:
+ f.walk(&n.X, ctxExpr, visit)
+ f.walk(&n.Y, ctxExpr, visit)
+ case *ast.KeyValueExpr:
+ f.walk(&n.Key, ctxExpr, visit)
+ f.walk(&n.Value, ctxExpr, visit)
+
+ case *ast.ArrayType:
+ f.walk(&n.Len, ctxExpr, visit)
+ f.walk(&n.Elt, ctxType, visit)
+ case *ast.StructType:
+ f.walk(n.Fields, ctxField, visit)
+ case *ast.FuncType:
+ if tparams := funcTypeTypeParams(n); tparams != nil {
+ f.walk(tparams, ctxParam, visit)
+ }
+ f.walk(n.Params, ctxParam, visit)
+ if n.Results != nil {
+ f.walk(n.Results, ctxParam, visit)
+ }
+ case *ast.InterfaceType:
+ f.walk(n.Methods, ctxField, visit)
+ case *ast.MapType:
+ f.walk(&n.Key, ctxType, visit)
+ f.walk(&n.Value, ctxType, visit)
+ case *ast.ChanType:
+ f.walk(&n.Value, ctxType, visit)
+
+ case *ast.BadStmt:
+ case *ast.DeclStmt:
+ f.walk(n.Decl, ctxDecl, visit)
+ case *ast.EmptyStmt:
+ case *ast.LabeledStmt:
+ f.walk(n.Stmt, ctxStmt, visit)
+ case *ast.ExprStmt:
+ f.walk(&n.X, ctxExpr, visit)
+ case *ast.SendStmt:
+ f.walk(&n.Chan, ctxExpr, visit)
+ f.walk(&n.Value, ctxExpr, visit)
+ case *ast.IncDecStmt:
+ f.walk(&n.X, ctxExpr, visit)
+ case *ast.AssignStmt:
+ f.walk(n.Lhs, ctxExpr, visit)
+ if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
+ f.walk(n.Rhs, ctxAssign2, visit)
+ } else {
+ f.walk(n.Rhs, ctxExpr, visit)
+ }
+ case *ast.GoStmt:
+ f.walk(n.Call, ctxExpr, visit)
+ case *ast.DeferStmt:
+ f.walk(n.Call, ctxDefer, visit)
+ case *ast.ReturnStmt:
+ f.walk(n.Results, ctxExpr, visit)
+ case *ast.BranchStmt:
+ case *ast.BlockStmt:
+ f.walk(n.List, context, visit)
+ case *ast.IfStmt:
+ f.walk(n.Init, ctxStmt, visit)
+ f.walk(&n.Cond, ctxExpr, visit)
+ f.walk(n.Body, ctxStmt, visit)
+ f.walk(n.Else, ctxStmt, visit)
+ case *ast.CaseClause:
+ if context == ctxTypeSwitch {
+ context = ctxType
+ } else {
+ context = ctxExpr
+ }
+ f.walk(n.List, context, visit)
+ f.walk(n.Body, ctxStmt, visit)
+ case *ast.SwitchStmt:
+ f.walk(n.Init, ctxStmt, visit)
+ f.walk(&n.Tag, ctxExpr, visit)
+ f.walk(n.Body, ctxSwitch, visit)
+ case *ast.TypeSwitchStmt:
+ f.walk(n.Init, ctxStmt, visit)
+ f.walk(n.Assign, ctxStmt, visit)
+ f.walk(n.Body, ctxTypeSwitch, visit)
+ case *ast.CommClause:
+ f.walk(n.Comm, ctxStmt, visit)
+ f.walk(n.Body, ctxStmt, visit)
+ case *ast.SelectStmt:
+ f.walk(n.Body, ctxStmt, visit)
+ case *ast.ForStmt:
+ f.walk(n.Init, ctxStmt, visit)
+ f.walk(&n.Cond, ctxExpr, visit)
+ f.walk(n.Post, ctxStmt, visit)
+ f.walk(n.Body, ctxStmt, visit)
+ case *ast.RangeStmt:
+ f.walk(&n.Key, ctxExpr, visit)
+ f.walk(&n.Value, ctxExpr, visit)
+ f.walk(&n.X, ctxExpr, visit)
+ f.walk(n.Body, ctxStmt, visit)
+
+ case *ast.ImportSpec:
+ case *ast.ValueSpec:
+ f.walk(&n.Type, ctxType, visit)
+ if len(n.Names) == 2 && len(n.Values) == 1 {
+ f.walk(&n.Values[0], ctxAssign2, visit)
+ } else {
+ f.walk(n.Values, ctxExpr, visit)
+ }
+ case *ast.TypeSpec:
+ if tparams := typeSpecTypeParams(n); tparams != nil {
+ f.walk(tparams, ctxParam, visit)
+ }
+ f.walk(&n.Type, ctxType, visit)
+
+ case *ast.BadDecl:
+ case *ast.GenDecl:
+ f.walk(n.Specs, ctxSpec, visit)
+ case *ast.FuncDecl:
+ if n.Recv != nil {
+ f.walk(n.Recv, ctxParam, visit)
+ }
+ f.walk(n.Type, ctxType, visit)
+ if n.Body != nil {
+ f.walk(n.Body, ctxStmt, visit)
+ }
+
+ case *ast.File:
+ f.walk(n.Decls, ctxDecl, visit)
+
+ case *ast.Package:
+ for _, file := range n.Files {
+ f.walk(file, ctxFile, visit)
+ }
+
+ case []ast.Decl:
+ for _, d := range n {
+ f.walk(d, context, visit)
+ }
+ case []ast.Expr:
+ for i := range n {
+ f.walk(&n[i], context, visit)
+ }
+ case []ast.Stmt:
+ for _, s := range n {
+ f.walk(s, context, visit)
+ }
+ case []ast.Spec:
+ for _, s := range n {
+ f.walk(s, context, visit)
+ }
+ }
+}
+
+// If x is of the form (T), unparen returns unparen(T), otherwise it returns x.
+func unparen(x ast.Expr) ast.Expr {
+ if p, isParen := x.(*ast.ParenExpr); isParen {
+ x = unparen(p.X)
+ }
+ return x
+}
diff --git a/src/cmd/cgo/ast_go1.go b/src/cmd/cgo/ast_go1.go
new file mode 100644
index 0000000..2f65f0f
--- /dev/null
+++ b/src/cmd/cgo/ast_go1.go
@@ -0,0 +1,25 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build compiler_bootstrap
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) {
+ error_(token.NoPos, "unexpected type %T in walk", x)
+ panic("unexpected type")
+}
+
+func funcTypeTypeParams(n *ast.FuncType) *ast.FieldList {
+ return nil
+}
+
+func typeSpecTypeParams(n *ast.TypeSpec) *ast.FieldList {
+ return nil
+}
diff --git a/src/cmd/cgo/ast_go118.go b/src/cmd/cgo/ast_go118.go
new file mode 100644
index 0000000..ced3072
--- /dev/null
+++ b/src/cmd/cgo/ast_go118.go
@@ -0,0 +1,32 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !compiler_bootstrap
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+func (f *File) walkUnexpected(x interface{}, context astContext, visit func(*File, interface{}, astContext)) {
+ switch n := x.(type) {
+ default:
+ error_(token.NoPos, "unexpected type %T in walk", x)
+ panic("unexpected type")
+
+ case *ast.IndexListExpr:
+ f.walk(&n.X, ctxExpr, visit)
+ f.walk(n.Indices, ctxExpr, visit)
+ }
+}
+
+func funcTypeTypeParams(n *ast.FuncType) *ast.FieldList {
+ return n.TypeParams
+}
+
+func typeSpecTypeParams(n *ast.TypeSpec) *ast.FieldList {
+ return n.TypeParams
+}
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
new file mode 100644
index 0000000..b1a288f
--- /dev/null
+++ b/src/cmd/cgo/doc.go
@@ -0,0 +1,1064 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+Cgo enables the creation of Go packages that call C code.
+
+# Using cgo with the go command
+
+To use cgo write normal Go code that imports a pseudo-package "C".
+The Go code can then refer to types such as C.size_t, variables such
+as C.stdout, or functions such as C.putchar.
+
+If the import of "C" is immediately preceded by a comment, that
+comment, called the preamble, is used as a header when compiling
+the C parts of the package. For example:
+
+ // #include <stdio.h>
+ // #include <errno.h>
+ import "C"
+
+The preamble may contain any C code, including function and variable
+declarations and definitions. These may then be referred to from Go
+code as though they were defined in the package "C". All names
+declared in the preamble may be used, even if they start with a
+lower-case letter. Exception: static variables in the preamble may
+not be referenced from Go code; static functions are permitted.
+
+See $GOROOT/cmd/cgo/internal/teststdio and $GOROOT/misc/cgo/gmp for examples. See
+"C? Go? Cgo!" for an introduction to using cgo:
+https://golang.org/doc/articles/c_go_cgo.html.
+
+CFLAGS, CPPFLAGS, CXXFLAGS, FFLAGS and LDFLAGS may be defined with pseudo
+#cgo directives within these comments to tweak the behavior of the C, C++
+or Fortran compiler. Values defined in multiple directives are concatenated
+together. The directive can include a list of build constraints limiting its
+effect to systems satisfying one of the constraints
+(see https://golang.org/pkg/go/build/#hdr-Build_Constraints for details about the constraint syntax).
+For example:
+
+ // #cgo CFLAGS: -DPNG_DEBUG=1
+ // #cgo amd64 386 CFLAGS: -DX86=1
+ // #cgo LDFLAGS: -lpng
+ // #include <png.h>
+ import "C"
+
+Alternatively, CPPFLAGS and LDFLAGS may be obtained via the pkg-config tool
+using a '#cgo pkg-config:' directive followed by the package names.
+For example:
+
+ // #cgo pkg-config: png cairo
+ // #include <png.h>
+ import "C"
+
+The default pkg-config tool may be changed by setting the PKG_CONFIG environment variable.
+
+For security reasons, only a limited set of flags are allowed, notably -D, -U, -I, and -l.
+To allow additional flags, set CGO_CFLAGS_ALLOW to a regular expression
+matching the new flags. To disallow flags that would otherwise be allowed,
+set CGO_CFLAGS_DISALLOW to a regular expression matching arguments
+that must be disallowed. In both cases the regular expression must match
+a full argument: to allow -mfoo=bar, use CGO_CFLAGS_ALLOW='-mfoo.*',
+not just CGO_CFLAGS_ALLOW='-mfoo'. Similarly named variables control
+the allowed CPPFLAGS, CXXFLAGS, FFLAGS, and LDFLAGS.
+
+Also for security reasons, only a limited set of characters are
+permitted, notably alphanumeric characters and a few symbols, such as
+'.', that will not be interpreted in unexpected ways. Attempts to use
+forbidden characters will get a "malformed #cgo argument" error.
+
+When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and
+CGO_LDFLAGS environment variables are added to the flags derived from
+these directives. Package-specific flags should be set using the
+directives, not the environment variables, so that builds work in
+unmodified environments. Flags obtained from environment variables
+are not subject to the security limitations described above.
+
+All the cgo CPPFLAGS and CFLAGS directives in a package are concatenated and
+used to compile C files in that package. All the CPPFLAGS and CXXFLAGS
+directives in a package are concatenated and used to compile C++ files in that
+package. All the CPPFLAGS and FFLAGS directives in a package are concatenated
+and used to compile Fortran files in that package. All the LDFLAGS directives
+in any package in the program are concatenated and used at link time. All the
+pkg-config directives are concatenated and sent to pkg-config simultaneously
+to add to each appropriate set of command-line flags.
+
+When the cgo directives are parsed, any occurrence of the string ${SRCDIR}
+will be replaced by the absolute path to the directory containing the source
+file. This allows pre-compiled static libraries to be included in the package
+directory and linked properly.
+For example if package foo is in the directory /go/src/foo:
+
+ // #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
+
+Will be expanded to:
+
+ // #cgo LDFLAGS: -L/go/src/foo/libs -lfoo
+
+When the Go tool sees that one or more Go files use the special import
+"C", it will look for other non-Go files in the directory and compile
+them as part of the Go package. Any .c, .s, .S or .sx files will be
+compiled with the C compiler. Any .cc, .cpp, or .cxx files will be
+compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be
+compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will
+not be compiled separately, but, if these header files are changed,
+the package (including its non-Go source files) will be recompiled.
+Note that changes to files in other directories do not cause the package
+to be recompiled, so all non-Go source code for the package should be
+stored in the package directory, not in subdirectories.
+The default C and C++ compilers may be changed by the CC and CXX
+environment variables, respectively; those environment variables
+may include command line options.
+
+The cgo tool will always invoke the C compiler with the source file's
+directory in the include path; i.e. -I${SRCDIR} is always implied. This
+means that if a header file foo/bar.h exists both in the source
+directory and also in the system include directory (or some other place
+specified by a -I flag), then "#include <foo/bar.h>" will always find the
+local version in preference to any other version.
+
+The cgo tool is enabled by default for native builds on systems where
+it is expected to work. It is disabled by default when cross-compiling
+as well as when the CC environment variable is unset and the default
+C compiler (typically gcc or clang) cannot be found on the system PATH.
+You can override the default by setting the CGO_ENABLED
+environment variable when running the go tool: set it to 1 to enable
+the use of cgo, and to 0 to disable it. The go tool will set the
+build constraint "cgo" if cgo is enabled. The special import "C"
+implies the "cgo" build constraint, as though the file also said
+"//go:build cgo". Therefore, if cgo is disabled, files that import
+"C" will not be built by the go tool. (For more about build constraints
+see https://golang.org/pkg/go/build/#hdr-Build_Constraints).
+
+When cross-compiling, you must specify a C cross-compiler for cgo to
+use. You can do this by setting the generic CC_FOR_TARGET or the
+more specific CC_FOR_${GOOS}_${GOARCH} (for example, CC_FOR_linux_arm)
+environment variable when building the toolchain using make.bash,
+or you can set the CC environment variable any time you run the go tool.
+
+The CXX_FOR_TARGET, CXX_FOR_${GOOS}_${GOARCH}, and CXX
+environment variables work in a similar way for C++ code.
+
+# Go references to C
+
+Within the Go file, C's struct field names that are keywords in Go
+can be accessed by prefixing them with an underscore: if x points at a C
+struct with a field named "type", x._type accesses the field.
+C struct fields that cannot be expressed in Go, such as bit fields
+or misaligned data, are omitted in the Go struct, replaced by
+appropriate padding to reach the next field or the end of the struct.
+
+The standard C numeric types are available under the names
+C.char, C.schar (signed char), C.uchar (unsigned char),
+C.short, C.ushort (unsigned short), C.int, C.uint (unsigned int),
+C.long, C.ulong (unsigned long), C.longlong (long long),
+C.ulonglong (unsigned long long), C.float, C.double,
+C.complexfloat (complex float), and C.complexdouble (complex double).
+The C type void* is represented by Go's unsafe.Pointer.
+The C types __int128_t and __uint128_t are represented by [16]byte.
+
+A few special C types which would normally be represented by a pointer
+type in Go are instead represented by a uintptr. See the Special
+cases section below.
+
+To access a struct, union, or enum type directly, prefix it with
+struct_, union_, or enum_, as in C.struct_stat.
+
+The size of any C type T is available as C.sizeof_T, as in
+C.sizeof_struct_stat.
+
+A C function may be declared in the Go file with a parameter type of
+the special name _GoString_. This function may be called with an
+ordinary Go string value. The string length, and a pointer to the
+string contents, may be accessed by calling the C functions
+
+ size_t _GoStringLen(_GoString_ s);
+ const char *_GoStringPtr(_GoString_ s);
+
+These functions are only available in the preamble, not in other C
+files. The C code must not modify the contents of the pointer returned
+by _GoStringPtr. Note that the string contents may not have a trailing
+NUL byte.
+
+As Go doesn't have support for C's union type in the general case,
+C's union types are represented as a Go byte array with the same length.
+
+Go structs cannot embed fields with C types.
+
+Go code cannot refer to zero-sized fields that occur at the end of
+non-empty C structs. To get the address of such a field (which is the
+only operation you can do with a zero-sized field) you must take the
+address of the struct and add the size of the struct.
+
+Cgo translates C types into equivalent unexported Go types.
+Because the translations are unexported, a Go package should not
+expose C types in its exported API: a C type used in one Go package
+is different from the same C type used in another.
+
+Any C function (even void functions) may be called in a multiple
+assignment context to retrieve both the return value (if any) and the
+C errno variable as an error (use _ to skip the result value if the
+function returns void). For example:
+
+ n, err = C.sqrt(-1)
+ _, err := C.voidFunc()
+ var n, err = C.sqrt(1)
+
+Calling C function pointers is currently not supported, however you can
+declare Go variables which hold C function pointers and pass them
+back and forth between Go and C. C code may call function pointers
+received from Go. For example:
+
+ package main
+
+ // typedef int (*intFunc) ();
+ //
+ // int
+ // bridge_int_func(intFunc f)
+ // {
+ // return f();
+ // }
+ //
+ // int fortytwo()
+ // {
+ // return 42;
+ // }
+ import "C"
+ import "fmt"
+
+ func main() {
+ f := C.intFunc(C.fortytwo)
+ fmt.Println(int(C.bridge_int_func(f)))
+ // Output: 42
+ }
+
+In C, a function argument written as a fixed size array
+actually requires a pointer to the first element of the array.
+C compilers are aware of this calling convention and adjust
+the call accordingly, but Go cannot. In Go, you must pass
+the pointer to the first element explicitly: C.f(&C.x[0]).
+
+Calling variadic C functions is not supported. It is possible to
+circumvent this by using a C function wrapper. For example:
+
+ package main
+
+ // #include <stdio.h>
+ // #include <stdlib.h>
+ //
+ // static void myprint(char* s) {
+ // printf("%s\n", s);
+ // }
+ import "C"
+ import "unsafe"
+
+ func main() {
+ cs := C.CString("Hello from stdio")
+ C.myprint(cs)
+ C.free(unsafe.Pointer(cs))
+ }
+
+A few special functions convert between Go and C types
+by making copies of the data. In pseudo-Go definitions:
+
+ // Go string to C string
+ // The C string is allocated in the C heap using malloc.
+ // It is the caller's responsibility to arrange for it to be
+ // freed, such as by calling C.free (be sure to include stdlib.h
+ // if C.free is needed).
+ func C.CString(string) *C.char
+
+ // Go []byte slice to C array
+ // The C array is allocated in the C heap using malloc.
+ // It is the caller's responsibility to arrange for it to be
+ // freed, such as by calling C.free (be sure to include stdlib.h
+ // if C.free is needed).
+ func C.CBytes([]byte) unsafe.Pointer
+
+ // C string to Go string
+ func C.GoString(*C.char) string
+
+ // C data with explicit length to Go string
+ func C.GoStringN(*C.char, C.int) string
+
+ // C data with explicit length to Go []byte
+ func C.GoBytes(unsafe.Pointer, C.int) []byte
+
+As a special case, C.malloc does not call the C library malloc directly
+but instead calls a Go helper function that wraps the C library malloc
+but guarantees never to return nil. If C's malloc indicates out of memory,
+the helper function crashes the program, like when Go itself runs out
+of memory. Because C.malloc cannot fail, it has no two-result form
+that returns errno.
+
+# C references to Go
+
+Go functions can be exported for use by C code in the following way:
+
+ //export MyFunction
+ func MyFunction(arg1, arg2 int, arg3 string) int64 {...}
+
+ //export MyFunction2
+ func MyFunction2(arg1, arg2 int, arg3 string) (int64, *C.char) {...}
+
+They will be available in the C code as:
+
+ extern GoInt64 MyFunction(int arg1, int arg2, GoString arg3);
+ extern struct MyFunction2_return MyFunction2(int arg1, int arg2, GoString arg3);
+
+found in the _cgo_export.h generated header, after any preambles
+copied from the cgo input files. Functions with multiple
+return values are mapped to functions returning a struct.
+
+Not all Go types can be mapped to C types in a useful way.
+Go struct types are not supported; use a C struct type.
+Go array types are not supported; use a C pointer.
+
+Go functions that take arguments of type string may be called with the
+C type _GoString_, described above. The _GoString_ type will be
+automatically defined in the preamble. Note that there is no way for C
+code to create a value of this type; this is only useful for passing
+string values from Go to C and back to Go.
+
+Using //export in a file places a restriction on the preamble:
+since it is copied into two different C output files, it must not
+contain any definitions, only declarations. If a file contains both
+definitions and declarations, then the two output files will produce
+duplicate symbols and the linker will fail. To avoid this, definitions
+must be placed in preambles in other files, or in C source files.
+
+# Passing pointers
+
+Go is a garbage collected language, and the garbage collector needs to
+know the location of every pointer to Go memory. Because of this,
+there are restrictions on passing pointers between Go and C.
+
+In this section the term Go pointer means a pointer to memory
+allocated by Go (such as by using the & operator or calling the
+predefined new function) and the term C pointer means a pointer to
+memory allocated by C (such as by a call to C.malloc). Whether a
+pointer is a Go pointer or a C pointer is a dynamic property
+determined by how the memory was allocated; it has nothing to do with
+the type of the pointer.
+
+Note that values of some Go types, other than the type's zero value,
+always include Go pointers. This is true of string, slice, interface,
+channel, map, and function types. A pointer type may hold a Go pointer
+or a C pointer. Array and struct types may or may not include Go
+pointers, depending on the element types. All the discussion below
+about Go pointers applies not just to pointer types, but also to other
+types that include Go pointers.
+
+All Go pointers passed to C must point to pinned Go memory. Go pointers
+passed as function arguments to C functions have the memory they point to
+implicitly pinned for the duration of the call. Go memory reachable from
+these function arguments must be pinned as long as the C code has access
+to it. Whether Go memory is pinned is a dynamic property of that memory
+region; it has nothing to do with the type of the pointer.
+
+Go values created by calling new, by taking the address of a composite
+literal, or by taking the address of a local variable may also have their
+memory pinned using [runtime.Pinner]. This type may be used to manage
+the duration of the memory's pinned status, potentially beyond the
+duration of a C function call. Memory may be pinned more than once and
+must be unpinned exactly the same number of times it has been pinned.
+
+Go code may pass a Go pointer to C provided the memory to which it
+points does not contain any Go pointers to memory that is unpinned. When
+passing a pointer to a field in a struct, the Go memory in question is
+the memory occupied by the field, not the entire struct. When passing a
+pointer to an element in an array or slice, the Go memory in question is
+the entire array or the entire backing array of the slice.
+
+C code may keep a copy of a Go pointer only as long as the memory it
+points to is pinned.
+
+C code may not keep a copy of a Go pointer after the call returns,
+unless the memory it points to is pinned with [runtime.Pinner] and the
+Pinner is not unpinned while the Go pointer is stored in C memory.
+This implies that C code may not keep a copy of a string, slice,
+channel, and so forth, because they cannot be pinned with
+[runtime.Pinner].
+
+The _GoString_ type also may not be pinned with [runtime.Pinner].
+Because it includes a Go pointer, the memory it points to is only pinned
+for the duration of the call; _GoString_ values may not be retained by C
+code.
+
+A Go function called by C code may return a Go pointer to pinned memory
+(which implies that it may not return a string, slice, channel, and so
+forth). A Go function called by C code may take C pointers as arguments,
+and it may store non-pointer data, C pointers, or Go pointers to pinned
+memory through those pointers. It may not store a Go pointer to unpinned
+memory in memory pointed to by a C pointer (which again, implies that it
+may not store a string, slice, channel, and so forth). A Go function
+called by C code may take a Go pointer but it must preserve the property
+that the Go memory to which it points (and the Go memory to which that
+memory points, and so on) is pinned.
+
+These rules are checked dynamically at runtime. The checking is
+controlled by the cgocheck setting of the GODEBUG environment
+variable. The default setting is GODEBUG=cgocheck=1, which implements
+reasonably cheap dynamic checks. These checks may be disabled
+entirely using GODEBUG=cgocheck=0. Complete checking of pointer
+handling, at some cost in run time, is available via GODEBUG=cgocheck=2.
+
+It is possible to defeat this enforcement by using the unsafe package,
+and of course there is nothing stopping the C code from doing anything
+it likes. However, programs that break these rules are likely to fail
+in unexpected and unpredictable ways.
+
+The runtime/cgo.Handle type can be used to safely pass Go values
+between Go and C. See the runtime/cgo package documentation for details.
+
+Note: the current implementation has a bug. While Go code is permitted
+to write nil or a C pointer (but not a Go pointer) to C memory, the
+current implementation may sometimes cause a runtime error if the
+contents of the C memory appear to be a Go pointer. Therefore, avoid
+passing uninitialized C memory to Go code if the Go code is going to
+store pointer values in it. Zero out the memory in C before passing it
+to Go.
+
+# Special cases
+
+A few special C types which would normally be represented by a pointer
+type in Go are instead represented by a uintptr. Those include:
+
+1. The *Ref types on Darwin, rooted at CoreFoundation's CFTypeRef type.
+
+2. The object types from Java's JNI interface:
+
+ jobject
+ jclass
+ jthrowable
+ jstring
+ jarray
+ jbooleanArray
+ jbyteArray
+ jcharArray
+ jshortArray
+ jintArray
+ jlongArray
+ jfloatArray
+ jdoubleArray
+ jobjectArray
+ jweak
+
+3. The EGLDisplay and EGLConfig types from the EGL API.
+
+These types are uintptr on the Go side because they would otherwise
+confuse the Go garbage collector; they are sometimes not really
+pointers but data structures encoded in a pointer type. All operations
+on these types must happen in C. The proper constant to initialize an
+empty such reference is 0, not nil.
+
+These special cases were introduced in Go 1.10. For auto-updating code
+from Go 1.9 and earlier, use the cftype or jni rewrites in the Go fix tool:
+
+ go tool fix -r cftype <pkg>
+ go tool fix -r jni <pkg>
+
+It will replace nil with 0 in the appropriate places.
+
+The EGLDisplay case was introduced in Go 1.12. Use the egl rewrite
+to auto-update code from Go 1.11 and earlier:
+
+ go tool fix -r egl <pkg>
+
+The EGLConfig case was introduced in Go 1.15. Use the eglconf rewrite
+to auto-update code from Go 1.14 and earlier:
+
+ go tool fix -r eglconf <pkg>
+
+# Using cgo directly
+
+Usage:
+
+ go tool cgo [cgo options] [-- compiler options] gofiles...
+
+Cgo transforms the specified input Go source files into several output
+Go and C source files.
+
+The compiler options are passed through uninterpreted when
+invoking the C compiler to compile the C parts of the package.
+
+The following options are available when running cgo directly:
+
+ -V
+ Print cgo version and exit.
+ -debug-define
+ Debugging option. Print #defines.
+ -debug-gcc
+ Debugging option. Trace C compiler execution and output.
+ -dynimport file
+ Write list of symbols imported by file. Write to
+ -dynout argument or to standard output. Used by go
+ build when building a cgo package.
+ -dynlinker
+ Write dynamic linker as part of -dynimport output.
+ -dynout file
+ Write -dynimport output to file.
+ -dynpackage package
+ Set Go package for -dynimport output.
+ -exportheader file
+ If there are any exported functions, write the
+ generated export declarations to file.
+ C code can #include this to see the declarations.
+ -importpath string
+ The import path for the Go package. Optional; used for
+ nicer comments in the generated files.
+ -import_runtime_cgo
+ If set (which it is by default) import runtime/cgo in
+ generated output.
+ -import_syscall
+ If set (which it is by default) import syscall in
+ generated output.
+ -gccgo
+ Generate output for the gccgo compiler rather than the
+ gc compiler.
+ -gccgoprefix prefix
+ The -fgo-prefix option to be used with gccgo.
+ -gccgopkgpath path
+ The -fgo-pkgpath option to be used with gccgo.
+ -gccgo_define_cgoincomplete
+ Define cgo.Incomplete locally rather than importing it from
+ the "runtime/cgo" package. Used for old gccgo versions.
+ -godefs
+ Write out input file in Go syntax replacing C package
+ names with real values. Used to generate files in the
+ syscall package when bootstrapping a new target.
+ -objdir directory
+ Put all generated files in directory.
+ -srcdir directory
+*/
+package main
+
+/*
+Implementation details.
+
+Cgo provides a way for Go programs to call C code linked into the same
+address space. This comment explains the operation of cgo.
+
+Cgo reads a set of Go source files and looks for statements saying
+import "C". If the import has a doc comment, that comment is
+taken as literal C code to be used as a preamble to any C code
+generated by cgo. A typical preamble #includes necessary definitions:
+
+ // #include <stdio.h>
+ import "C"
+
+For more details about the usage of cgo, see the documentation
+comment at the top of this file.
+
+Understanding C
+
+Cgo scans the Go source files that import "C" for uses of that
+package, such as C.puts. It collects all such identifiers. The next
+step is to determine each kind of name. In C.xxx the xxx might refer
+to a type, a function, a constant, or a global variable. Cgo must
+decide which.
+
+The obvious thing for cgo to do is to process the preamble, expanding
+#includes and processing the corresponding C code. That would require
+a full C parser and type checker that was also aware of any extensions
+known to the system compiler (for example, all the GNU C extensions) as
+well as the system-specific header locations and system-specific
+pre-#defined macros. This is certainly possible to do, but it is an
+enormous amount of work.
+
+Cgo takes a different approach. It determines the meaning of C
+identifiers not by parsing C code but by feeding carefully constructed
+programs into the system C compiler and interpreting the generated
+error messages, debug information, and object files. In practice,
+parsing these is significantly less work and more robust than parsing
+C source.
+
+Cgo first invokes gcc -E -dM on the preamble, in order to find out
+about simple #defines for constants and the like. These are recorded
+for later use.
+
+Next, cgo needs to identify the kinds for each identifier. For the
+identifiers C.foo, cgo generates this C program:
+
+ <preamble>
+ #line 1 "not-declared"
+ void __cgo_f_1_1(void) { __typeof__(foo) *__cgo_undefined__1; }
+ #line 1 "not-type"
+ void __cgo_f_1_2(void) { foo *__cgo_undefined__2; }
+ #line 1 "not-int-const"
+ void __cgo_f_1_3(void) { enum { __cgo_undefined__3 = (foo)*1 }; }
+ #line 1 "not-num-const"
+ void __cgo_f_1_4(void) { static const double __cgo_undefined__4 = (foo); }
+ #line 1 "not-str-lit"
+ void __cgo_f_1_5(void) { static const char __cgo_undefined__5[] = (foo); }
+
+This program will not compile, but cgo can use the presence or absence
+of an error message on a given line to deduce the information it
+needs. The program is syntactically valid regardless of whether each
+name is a type or an ordinary identifier, so there will be no syntax
+errors that might stop parsing early.
+
+An error on not-declared:1 indicates that foo is undeclared.
+An error on not-type:1 indicates that foo is not a type (if declared at all, it is an identifier).
+An error on not-int-const:1 indicates that foo is not an integer constant.
+An error on not-num-const:1 indicates that foo is not a number constant.
+An error on not-str-lit:1 indicates that foo is not a string literal.
+An error on not-signed-int-const:1 indicates that foo is not a signed integer constant.
+
+The line number specifies the name involved. In the example, 1 is foo.
+
+Next, cgo must learn the details of each type, variable, function, or
+constant. It can do this by reading object files. If cgo has decided
+that t1 is a type, v2 and v3 are variables or functions, and i4, i5
+are integer constants, u6 is an unsigned integer constant, and f7 and f8
+are float constants, and s9 and s10 are string constants, it generates:
+
+ <preamble>
+ __typeof__(t1) *__cgo__1;
+ __typeof__(v2) *__cgo__2;
+ __typeof__(v3) *__cgo__3;
+ __typeof__(i4) *__cgo__4;
+ enum { __cgo_enum__4 = i4 };
+ __typeof__(i5) *__cgo__5;
+ enum { __cgo_enum__5 = i5 };
+ __typeof__(u6) *__cgo__6;
+ enum { __cgo_enum__6 = u6 };
+ __typeof__(f7) *__cgo__7;
+ __typeof__(f8) *__cgo__8;
+ __typeof__(s9) *__cgo__9;
+ __typeof__(s10) *__cgo__10;
+
+ long long __cgodebug_ints[] = {
+ 0, // t1
+ 0, // v2
+ 0, // v3
+ i4,
+ i5,
+ u6,
+ 0, // f7
+ 0, // f8
+ 0, // s9
+ 0, // s10
+ 1
+ };
+
+ double __cgodebug_floats[] = {
+ 0, // t1
+ 0, // v2
+ 0, // v3
+ 0, // i4
+ 0, // i5
+ 0, // u6
+ f7,
+ f8,
+ 0, // s9
+ 0, // s10
+ 1
+ };
+
+ const char __cgodebug_str__9[] = s9;
+ const unsigned long long __cgodebug_strlen__9 = sizeof(s9)-1;
+ const char __cgodebug_str__10[] = s10;
+ const unsigned long long __cgodebug_strlen__10 = sizeof(s10)-1;
+
+and again invokes the system C compiler, to produce an object file
+containing debug information. Cgo parses the DWARF debug information
+for __cgo__N to learn the type of each identifier. (The types also
+distinguish functions from global variables.) Cgo reads the constant
+values from the __cgodebug_* from the object file's data segment.
+
+At this point cgo knows the meaning of each C.xxx well enough to start
+the translation process.
+
+Translating Go
+
+Given the input Go files x.go and y.go, cgo generates these source
+files:
+
+ x.cgo1.go # for gc (cmd/compile)
+ y.cgo1.go # for gc
+ _cgo_gotypes.go # for gc
+ _cgo_import.go # for gc (if -dynout _cgo_import.go)
+ x.cgo2.c # for gcc
+ y.cgo2.c # for gcc
+ _cgo_defun.c # for gcc (if -gccgo)
+ _cgo_export.c # for gcc
+ _cgo_export.h # for gcc
+ _cgo_main.c # for gcc
+ _cgo_flags # for alternative build tools
+
+The file x.cgo1.go is a copy of x.go with the import "C" removed and
+references to C.xxx replaced with names like _Cfunc_xxx or _Ctype_xxx.
+The definitions of those identifiers, written as Go functions, types,
+or variables, are provided in _cgo_gotypes.go.
+
+Here is a _cgo_gotypes.go containing definitions for needed C types:
+
+ type _Ctype_char int8
+ type _Ctype_int int32
+ type _Ctype_void [0]byte
+
+The _cgo_gotypes.go file also contains the definitions of the
+functions. They all have similar bodies that invoke runtime·cgocall
+to make a switch from the Go runtime world to the system C (GCC-based)
+world.
+
+For example, here is the definition of _Cfunc_puts:
+
+ //go:cgo_import_static _cgo_be59f0f25121_Cfunc_puts
+ //go:linkname __cgofn__cgo_be59f0f25121_Cfunc_puts _cgo_be59f0f25121_Cfunc_puts
+ var __cgofn__cgo_be59f0f25121_Cfunc_puts byte
+ var _cgo_be59f0f25121_Cfunc_puts = unsafe.Pointer(&__cgofn__cgo_be59f0f25121_Cfunc_puts)
+
+ func _Cfunc_puts(p0 *_Ctype_char) (r1 _Ctype_int) {
+ _cgo_runtime_cgocall(_cgo_be59f0f25121_Cfunc_puts, uintptr(unsafe.Pointer(&p0)))
+ return
+ }
+
+The hexadecimal number is a hash of cgo's input, chosen to be
+deterministic yet unlikely to collide with other uses. The actual
+function _cgo_be59f0f25121_Cfunc_puts is implemented in a C source
+file compiled by gcc, the file x.cgo2.c:
+
+ void
+ _cgo_be59f0f25121_Cfunc_puts(void *v)
+ {
+ struct {
+ char* p0;
+ int r;
+ char __pad12[4];
+ } __attribute__((__packed__, __gcc_struct__)) *a = v;
+ a->r = puts((void*)a->p0);
+ }
+
+It extracts the arguments from the pointer to _Cfunc_puts's argument
+frame, invokes the system C function (in this case, puts), stores the
+result in the frame, and returns.
+
+Linking
+
+Once the _cgo_export.c and *.cgo2.c files have been compiled with gcc,
+they need to be linked into the final binary, along with the libraries
+they might depend on (in the case of puts, stdio). cmd/link has been
+extended to understand basic ELF files, but it does not understand ELF
+in the full complexity that modern C libraries embrace, so it cannot
+in general generate direct references to the system libraries.
+
+Instead, the build process generates an object file using dynamic
+linkage to the desired libraries. The main function is provided by
+_cgo_main.c:
+
+ int main() { return 0; }
+ void crosscall2(void(*fn)(void*), void *a, int c, uintptr_t ctxt) { }
+ uintptr_t _cgo_wait_runtime_init_done(void) { return 0; }
+ void _cgo_release_context(uintptr_t ctxt) { }
+ char* _cgo_topofstack(void) { return (char*)0; }
+ void _cgo_allocate(void *a, int c) { }
+ void _cgo_panic(void *a, int c) { }
+ void _cgo_reginit(void) { }
+
+The extra functions here are stubs to satisfy the references in the C
+code generated for gcc. The build process links this stub, along with
+_cgo_export.c and *.cgo2.c, into a dynamic executable and then lets
+cgo examine the executable. Cgo records the list of shared library
+references and resolved names and writes them into a new file
+_cgo_import.go, which looks like:
+
+ //go:cgo_dynamic_linker "/lib64/ld-linux-x86-64.so.2"
+ //go:cgo_import_dynamic puts puts#GLIBC_2.2.5 "libc.so.6"
+ //go:cgo_import_dynamic __libc_start_main __libc_start_main#GLIBC_2.2.5 "libc.so.6"
+ //go:cgo_import_dynamic stdout stdout#GLIBC_2.2.5 "libc.so.6"
+ //go:cgo_import_dynamic fflush fflush#GLIBC_2.2.5 "libc.so.6"
+ //go:cgo_import_dynamic _ _ "libpthread.so.0"
+ //go:cgo_import_dynamic _ _ "libc.so.6"
+
+In the end, the compiled Go package, which will eventually be
+presented to cmd/link as part of a larger program, contains:
+
+ _go_.o # gc-compiled object for _cgo_gotypes.go, _cgo_import.go, *.cgo1.go
+ _all.o # gcc-compiled object for _cgo_export.c, *.cgo2.c
+
+If there is an error generating the _cgo_import.go file, then, instead
+of adding _cgo_import.go to the package, the go tool adds an empty
+file named dynimportfail. The _cgo_import.go file is only needed when
+using internal linking mode, which is not the default when linking
+programs that use cgo (as described below). If the linker sees a file
+named dynimportfail it reports an error if it has been told to use
+internal linking mode. This approach is taken because generating
+_cgo_import.go requires doing a full C link of the package, which can
+fail for reasons that are irrelevant when using external linking mode.
+
+The final program will be a dynamic executable, so that cmd/link can avoid
+needing to process arbitrary .o files. It only needs to process the .o
+files generated from C files that cgo writes, and those are much more
+limited in the ELF or other features that they use.
+
+In essence, the _cgo_import.o file includes the extra linking
+directives that cmd/link is not sophisticated enough to derive from _all.o
+on its own. Similarly, the _all.o uses dynamic references to real
+system object code because cmd/link is not sophisticated enough to process
+the real code.
+
+The main benefits of this system are that cmd/link remains relatively simple
+(it does not need to implement a complete ELF and Mach-O linker) and
+that gcc is not needed after the package is compiled. For example,
+package net uses cgo for access to name resolution functions provided
+by libc. Although gcc is needed to compile package net, gcc is not
+needed to link programs that import package net.
+
+Runtime
+
+When using cgo, Go must not assume that it owns all details of the
+process. In particular it needs to coordinate with C in the use of
+threads and thread-local storage. The runtime package declares a few
+variables:
+
+ var (
+ iscgo bool
+ _cgo_init unsafe.Pointer
+ _cgo_thread_start unsafe.Pointer
+ )
+
+Any package using cgo imports "runtime/cgo", which provides
+initializations for these variables. It sets iscgo to true, _cgo_init
+to a gcc-compiled function that can be called early during program
+startup, and _cgo_thread_start to a gcc-compiled function that can be
+used to create a new thread, in place of the runtime's usual direct
+system calls.
+
+Internal and External Linking
+
+The text above describes "internal" linking, in which cmd/link parses and
+links host object files (ELF, Mach-O, PE, and so on) into the final
+executable itself. Keeping cmd/link simple means we cannot possibly
+implement the full semantics of the host linker, so the kinds of
+objects that can be linked directly into the binary is limited (other
+code can only be used as a dynamic library). On the other hand, when
+using internal linking, cmd/link can generate Go binaries by itself.
+
+In order to allow linking arbitrary object files without requiring
+dynamic libraries, cgo supports an "external" linking mode too. In
+external linking mode, cmd/link does not process any host object files.
+Instead, it collects all the Go code and writes a single go.o object
+file containing it. Then it invokes the host linker (usually gcc) to
+combine the go.o object file and any supporting non-Go code into a
+final executable. External linking avoids the dynamic library
+requirement but introduces a requirement that the host linker be
+present to create such a binary.
+
+Most builds both compile source code and invoke the linker to create a
+binary. When cgo is involved, the compile step already requires gcc, so
+it is not problematic for the link step to require gcc too.
+
+An important exception is builds using a pre-compiled copy of the
+standard library. In particular, package net uses cgo on most systems,
+and we want to preserve the ability to compile pure Go code that
+imports net without requiring gcc to be present at link time. (In this
+case, the dynamic library requirement is less significant, because the
+only library involved is libc.so, which can usually be assumed
+present.)
+
+This conflict between functionality and the gcc requirement means we
+must support both internal and external linking, depending on the
+circumstances: if net is the only cgo-using package, then internal
+linking is probably fine, but if other packages are involved, so that there
+are dependencies on libraries beyond libc, external linking is likely
+to work better. The compilation of a package records the relevant
+information to support both linking modes, leaving the decision
+to be made when linking the final binary.
+
+Linking Directives
+
+In either linking mode, package-specific directives must be passed
+through to cmd/link. These are communicated by writing //go: directives in a
+Go source file compiled by gc. The directives are copied into the .o
+object file and then processed by the linker.
+
+The directives are:
+
+//go:cgo_import_dynamic <local> [<remote> ["<library>"]]
+
+ In internal linking mode, allow an unresolved reference to
+ <local>, assuming it will be resolved by a dynamic library
+ symbol. The optional <remote> specifies the symbol's name and
+ possibly version in the dynamic library, and the optional "<library>"
+ names the specific library where the symbol should be found.
+
+ On AIX, the library pattern is slightly different. It must be
+ "lib.a/obj.o" with obj.o the member of this library exporting
+ this symbol.
+
+ In the <remote>, # or @ can be used to introduce a symbol version.
+
+ Examples:
+ //go:cgo_import_dynamic puts
+ //go:cgo_import_dynamic puts puts#GLIBC_2.2.5
+ //go:cgo_import_dynamic puts puts#GLIBC_2.2.5 "libc.so.6"
+
+ A side effect of the cgo_import_dynamic directive with a
+ library is to make the final binary depend on that dynamic
+ library. To get the dependency without importing any specific
+ symbols, use _ for local and remote.
+
+ Example:
+ //go:cgo_import_dynamic _ _ "libc.so.6"
+
+ For compatibility with current versions of SWIG,
+ #pragma dynimport is an alias for //go:cgo_import_dynamic.
+
+//go:cgo_dynamic_linker "<path>"
+
+ In internal linking mode, use "<path>" as the dynamic linker
+ in the final binary. This directive is only needed from one
+ package when constructing a binary; by convention it is
+ supplied by runtime/cgo.
+
+ Example:
+ //go:cgo_dynamic_linker "/lib/ld-linux.so.2"
+
+//go:cgo_export_dynamic <local> <remote>
+
+ In internal linking mode, put the Go symbol
+ named <local> into the program's exported symbol table as
+ <remote>, so that C code can refer to it by that name. This
+ mechanism makes it possible for C code to call back into Go or
+ to share Go's data.
+
+ For compatibility with current versions of SWIG,
+ #pragma dynexport is an alias for //go:cgo_export_dynamic.
+
+//go:cgo_import_static <local>
+
+ In external linking mode, allow unresolved references to
+ <local> in the go.o object file prepared for the host linker,
+ under the assumption that <local> will be supplied by the
+ other object files that will be linked with go.o.
+
+ Example:
+ //go:cgo_import_static puts_wrapper
+
+//go:cgo_export_static <local> <remote>
+
+ In external linking mode, put the Go symbol
+ named <local> into the program's exported symbol table as
+ <remote>, so that C code can refer to it by that name. This
+ mechanism makes it possible for C code to call back into Go or
+ to share Go's data.
+
+//go:cgo_ldflag "<arg>"
+
+ In external linking mode, invoke the host linker (usually gcc)
+ with "<arg>" as a command-line argument following the .o files.
+ Note that the arguments are for "gcc", not "ld".
+
+ Example:
+ //go:cgo_ldflag "-lpthread"
+ //go:cgo_ldflag "-L/usr/local/sqlite3/lib"
+
+A package compiled with cgo will include directives for both
+internal and external linking; the linker will select the appropriate
+subset for the chosen linking mode.
+
+Example
+
+As a simple example, consider a package that uses cgo to call C.sin.
+The following code will be generated by cgo:
+
+ // compiled by gc
+
+ //go:cgo_ldflag "-lm"
+
+ type _Ctype_double float64
+
+ //go:cgo_import_static _cgo_gcc_Cfunc_sin
+ //go:linkname __cgo_gcc_Cfunc_sin _cgo_gcc_Cfunc_sin
+ var __cgo_gcc_Cfunc_sin byte
+ var _cgo_gcc_Cfunc_sin = unsafe.Pointer(&__cgo_gcc_Cfunc_sin)
+
+ func _Cfunc_sin(p0 _Ctype_double) (r1 _Ctype_double) {
+ _cgo_runtime_cgocall(_cgo_gcc_Cfunc_sin, uintptr(unsafe.Pointer(&p0)))
+ return
+ }
+
+ // compiled by gcc, into foo.cgo2.o
+
+ void
+ _cgo_gcc_Cfunc_sin(void *v)
+ {
+ struct {
+ double p0;
+ double r;
+ } __attribute__((__packed__)) *a = v;
+ a->r = sin(a->p0);
+ }
+
+What happens at link time depends on whether the final binary is linked
+using the internal or external mode. If other packages are compiled in
+"external only" mode, then the final link will be an external one.
+Otherwise the link will be an internal one.
+
+The linking directives are used according to the kind of final link
+used.
+
+In internal mode, cmd/link itself processes all the host object files, in
+particular foo.cgo2.o. To do so, it uses the cgo_import_dynamic and
+cgo_dynamic_linker directives to learn that the otherwise undefined
+reference to sin in foo.cgo2.o should be rewritten to refer to the
+symbol sin with version GLIBC_2.2.5 from the dynamic library
+"libm.so.6", and the binary should request "/lib/ld-linux.so.2" as its
+runtime dynamic linker.
+
+In external mode, cmd/link does not process any host object files, in
+particular foo.cgo2.o. It links together the gc-generated object
+files, along with any other Go code, into a go.o file. While doing
+that, cmd/link will discover that there is no definition for
+_cgo_gcc_Cfunc_sin, referred to by the gc-compiled source file. This
+is okay, because cmd/link also processes the cgo_import_static directive and
+knows that _cgo_gcc_Cfunc_sin is expected to be supplied by a host
+object file, so cmd/link does not treat the missing symbol as an error when
+creating go.o. Indeed, the definition for _cgo_gcc_Cfunc_sin will be
+provided to the host linker by foo2.cgo.o, which in turn will need the
+symbol 'sin'. cmd/link also processes the cgo_ldflag directives, so that it
+knows that the eventual host link command must include the -lm
+argument, so that the host linker will be able to find 'sin' in the
+math library.
+
+cmd/link Command Line Interface
+
+The go command and any other Go-aware build systems invoke cmd/link
+to link a collection of packages into a single binary. By default, cmd/link will
+present the same interface it does today:
+
+ cmd/link main.a
+
+produces a file named a.out, even if cmd/link does so by invoking the host
+linker in external linking mode.
+
+By default, cmd/link will decide the linking mode as follows: if the only
+packages using cgo are those on a list of known standard library
+packages (net, os/user, runtime/cgo), cmd/link will use internal linking
+mode. Otherwise, there are non-standard cgo packages involved, and cmd/link
+will use external linking mode. The first rule means that a build of
+the godoc binary, which uses net but no other cgo, can run without
+needing gcc available. The second rule means that a build of a
+cgo-wrapped library like sqlite3 can generate a standalone executable
+instead of needing to refer to a dynamic library. The specific choice
+can be overridden using a command line flag: cmd/link -linkmode=internal or
+cmd/link -linkmode=external.
+
+In an external link, cmd/link will create a temporary directory, write any
+host object files found in package archives to that directory (renamed
+to avoid conflicts), write the go.o file to that directory, and invoke
+the host linker. The default value for the host linker is $CC, split
+into fields, or else "gcc". The specific host linker command line can
+be overridden using command line flags: cmd/link -extld=clang
+-extldflags='-ggdb -O3'. If any package in a build includes a .cc or
+other file compiled by the C++ compiler, the go tool will use the
+-extld option to set the host linker to the C++ compiler.
+
+These defaults mean that Go-aware build systems can ignore the linking
+changes and keep running plain 'cmd/link' and get reasonable results, but
+they can also control the linking details if desired.
+
+*/
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
new file mode 100644
index 0000000..7ae6fbc
--- /dev/null
+++ b/src/cmd/cgo/gcc.go
@@ -0,0 +1,3519 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Annotate Ref in Prog with C types by parsing gcc debug output.
+// Conversion of debug output to Go types.
+
+package main
+
+import (
+ "bytes"
+ "debug/dwarf"
+ "debug/elf"
+ "debug/macho"
+ "debug/pe"
+ "encoding/binary"
+ "errors"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "internal/xcoff"
+ "math"
+ "os"
+ "os/exec"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "cmd/internal/quoted"
+)
+
+var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
+var debugGcc = flag.Bool("debug-gcc", false, "print gcc invocations")
+
+var nameToC = map[string]string{
+ "schar": "signed char",
+ "uchar": "unsigned char",
+ "ushort": "unsigned short",
+ "uint": "unsigned int",
+ "ulong": "unsigned long",
+ "longlong": "long long",
+ "ulonglong": "unsigned long long",
+ "complexfloat": "float _Complex",
+ "complexdouble": "double _Complex",
+}
+
+var incomplete = "_cgopackage.Incomplete"
+
+// cname returns the C name to use for C.s.
+// The expansions are listed in nameToC and also
+// struct_foo becomes "struct foo", and similarly for
+// union and enum.
+func cname(s string) string {
+ if t, ok := nameToC[s]; ok {
+ return t
+ }
+
+ if strings.HasPrefix(s, "struct_") {
+ return "struct " + s[len("struct_"):]
+ }
+ if strings.HasPrefix(s, "union_") {
+ return "union " + s[len("union_"):]
+ }
+ if strings.HasPrefix(s, "enum_") {
+ return "enum " + s[len("enum_"):]
+ }
+ if strings.HasPrefix(s, "sizeof_") {
+ return "sizeof(" + cname(s[len("sizeof_"):]) + ")"
+ }
+ return s
+}
+
+// DiscardCgoDirectives processes the import C preamble, and discards
+// all #cgo CFLAGS and LDFLAGS directives, so they don't make their
+// way into _cgo_export.h.
+func (f *File) DiscardCgoDirectives() {
+ linesIn := strings.Split(f.Preamble, "\n")
+ linesOut := make([]string, 0, len(linesIn))
+ for _, line := range linesIn {
+ l := strings.TrimSpace(line)
+ if len(l) < 5 || l[:4] != "#cgo" || !unicode.IsSpace(rune(l[4])) {
+ linesOut = append(linesOut, line)
+ } else {
+ linesOut = append(linesOut, "")
+ }
+ }
+ f.Preamble = strings.Join(linesOut, "\n")
+}
+
+// addToFlag appends args to flag. All flags are later written out onto the
+// _cgo_flags file for the build system to use.
+func (p *Package) addToFlag(flag string, args []string) {
+ p.CgoFlags[flag] = append(p.CgoFlags[flag], args...)
+ if flag == "CFLAGS" {
+ // We'll also need these when preprocessing for dwarf information.
+ // However, discard any -g options: we need to be able
+ // to parse the debug info, so stick to what we expect.
+ for _, arg := range args {
+ if !strings.HasPrefix(arg, "-g") {
+ p.GccOptions = append(p.GccOptions, arg)
+ }
+ }
+ }
+}
+
+// splitQuoted splits the string s around each instance of one or more consecutive
+// white space characters while taking into account quotes and escaping, and
+// returns an array of substrings of s or an empty list if s contains only white space.
+// Single quotes and double quotes are recognized to prevent splitting within the
+// quoted region, and are removed from the resulting substrings. If a quote in s
+// isn't closed err will be set and r will have the unclosed argument as the
+// last element. The backslash is used for escaping.
+//
+// For example, the following string:
+//
+// `a b:"c d" 'e''f' "g\""`
+//
+// Would be parsed as:
+//
+// []string{"a", "b:c d", "ef", `g"`}
+func splitQuoted(s string) (r []string, err error) {
+ var args []string
+ arg := make([]rune, len(s))
+ escaped := false
+ quoted := false
+ quote := '\x00'
+ i := 0
+ for _, r := range s {
+ switch {
+ case escaped:
+ escaped = false
+ case r == '\\':
+ escaped = true
+ continue
+ case quote != 0:
+ if r == quote {
+ quote = 0
+ continue
+ }
+ case r == '"' || r == '\'':
+ quoted = true
+ quote = r
+ continue
+ case unicode.IsSpace(r):
+ if quoted || i > 0 {
+ quoted = false
+ args = append(args, string(arg[:i]))
+ i = 0
+ }
+ continue
+ }
+ arg[i] = r
+ i++
+ }
+ if quoted || i > 0 {
+ args = append(args, string(arg[:i]))
+ }
+ if quote != 0 {
+ err = errors.New("unclosed quote")
+ } else if escaped {
+ err = errors.New("unfinished escaping")
+ }
+ return args, err
+}
+
+// Translate rewrites f.AST, the original Go input, to remove
+// references to the imported package C, replacing them with
+// references to the equivalent Go types, functions, and variables.
+func (p *Package) Translate(f *File) {
+ for _, cref := range f.Ref {
+ // Convert C.ulong to C.unsigned long, etc.
+ cref.Name.C = cname(cref.Name.Go)
+ }
+
+ var conv typeConv
+ conv.Init(p.PtrSize, p.IntSize)
+
+ p.loadDefines(f)
+ p.typedefs = map[string]bool{}
+ p.typedefList = nil
+ numTypedefs := -1
+ for len(p.typedefs) > numTypedefs {
+ numTypedefs = len(p.typedefs)
+ // Also ask about any typedefs we've seen so far.
+ for _, info := range p.typedefList {
+ if f.Name[info.typedef] != nil {
+ continue
+ }
+ n := &Name{
+ Go: info.typedef,
+ C: info.typedef,
+ }
+ f.Name[info.typedef] = n
+ f.NamePos[n] = info.pos
+ }
+ needType := p.guessKinds(f)
+ if len(needType) > 0 {
+ p.loadDWARF(f, &conv, needType)
+ }
+
+ // In godefs mode we're OK with the typedefs, which
+ // will presumably also be defined in the file, we
+ // don't want to resolve them to their base types.
+ if *godefs {
+ break
+ }
+ }
+ p.prepareNames(f)
+ if p.rewriteCalls(f) {
+ // Add `import _cgo_unsafe "unsafe"` after the package statement.
+ f.Edit.Insert(f.offset(f.AST.Name.End()), "; import _cgo_unsafe \"unsafe\"")
+ }
+ p.rewriteRef(f)
+}
+
+// loadDefines coerces gcc into spitting out the #defines in use
+// in the file f and saves relevant renamings in f.Name[name].Define.
+func (p *Package) loadDefines(f *File) {
+ var b bytes.Buffer
+ b.WriteString(builtinProlog)
+ b.WriteString(f.Preamble)
+ stdout := p.gccDefines(b.Bytes())
+
+ for _, line := range strings.Split(stdout, "\n") {
+ if len(line) < 9 || line[0:7] != "#define" {
+ continue
+ }
+
+ line = strings.TrimSpace(line[8:])
+
+ var key, val string
+ spaceIndex := strings.Index(line, " ")
+ tabIndex := strings.Index(line, "\t")
+
+ if spaceIndex == -1 && tabIndex == -1 {
+ continue
+ } else if tabIndex == -1 || (spaceIndex != -1 && spaceIndex < tabIndex) {
+ key = line[0:spaceIndex]
+ val = strings.TrimSpace(line[spaceIndex:])
+ } else {
+ key = line[0:tabIndex]
+ val = strings.TrimSpace(line[tabIndex:])
+ }
+
+ if key == "__clang__" {
+ p.GccIsClang = true
+ }
+
+ if n := f.Name[key]; n != nil {
+ if *debugDefine {
+ fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val)
+ }
+ n.Define = val
+ }
+ }
+}
+
+// guessKinds tricks gcc into revealing the kind of each
+// name xxx for the references C.xxx in the Go input.
+// The kind is either a constant, type, or variable.
+func (p *Package) guessKinds(f *File) []*Name {
+ // Determine kinds for names we already know about,
+ // like #defines or 'struct foo', before bothering with gcc.
+ var names, needType []*Name
+ optional := map[*Name]bool{}
+ for _, key := range nameKeys(f.Name) {
+ n := f.Name[key]
+ // If we've already found this name as a #define
+ // and we can translate it as a constant value, do so.
+ if n.Define != "" {
+ if i, err := strconv.ParseInt(n.Define, 0, 64); err == nil {
+ n.Kind = "iconst"
+ // Turn decimal into hex, just for consistency
+ // with enum-derived constants. Otherwise
+ // in the cgo -godefs output half the constants
+ // are in hex and half are in whatever the #define used.
+ n.Const = fmt.Sprintf("%#x", i)
+ } else if n.Define[0] == '\'' {
+ if _, err := parser.ParseExpr(n.Define); err == nil {
+ n.Kind = "iconst"
+ n.Const = n.Define
+ }
+ } else if n.Define[0] == '"' {
+ if _, err := parser.ParseExpr(n.Define); err == nil {
+ n.Kind = "sconst"
+ n.Const = n.Define
+ }
+ }
+
+ if n.IsConst() {
+ continue
+ }
+ }
+
+ // If this is a struct, union, or enum type name, no need to guess the kind.
+ if strings.HasPrefix(n.C, "struct ") || strings.HasPrefix(n.C, "union ") || strings.HasPrefix(n.C, "enum ") {
+ n.Kind = "type"
+ needType = append(needType, n)
+ continue
+ }
+
+ if (goos == "darwin" || goos == "ios") && strings.HasSuffix(n.C, "Ref") {
+ // For FooRef, find out if FooGetTypeID exists.
+ s := n.C[:len(n.C)-3] + "GetTypeID"
+ n := &Name{Go: s, C: s}
+ names = append(names, n)
+ optional[n] = true
+ }
+
+ // Otherwise, we'll need to find out from gcc.
+ names = append(names, n)
+ }
+
+ // Bypass gcc if there's nothing left to find out.
+ if len(names) == 0 {
+ return needType
+ }
+
+ // Coerce gcc into telling us whether each name is a type, a value, or undeclared.
+ // For names, find out whether they are integer constants.
+ // We used to look at specific warning or error messages here, but that tied the
+ // behavior too closely to specific versions of the compilers.
+ // Instead, arrange that we can infer what we need from only the presence or absence
+ // of an error on a specific line.
+ //
+ // For each name, we generate these lines, where xxx is the index in toSniff plus one.
+ //
+ // #line xxx "not-declared"
+ // void __cgo_f_xxx_1(void) { __typeof__(name) *__cgo_undefined__1; }
+ // #line xxx "not-type"
+ // void __cgo_f_xxx_2(void) { name *__cgo_undefined__2; }
+ // #line xxx "not-int-const"
+ // void __cgo_f_xxx_3(void) { enum { __cgo_undefined__3 = (name)*1 }; }
+ // #line xxx "not-num-const"
+ // void __cgo_f_xxx_4(void) { static const double __cgo_undefined__4 = (name); }
+ // #line xxx "not-str-lit"
+ // void __cgo_f_xxx_5(void) { static const char __cgo_undefined__5[] = (name); }
+ //
+ // If we see an error at not-declared:xxx, the corresponding name is not declared.
+ // If we see an error at not-type:xxx, the corresponding name is not a type.
+ // If we see an error at not-int-const:xxx, the corresponding name is not an integer constant.
+ // If we see an error at not-num-const:xxx, the corresponding name is not a number constant.
+ // If we see an error at not-str-lit:xxx, the corresponding name is not a string literal.
+ //
+ // The specific input forms are chosen so that they are valid C syntax regardless of
+ // whether name denotes a type or an expression.
+
+ var b bytes.Buffer
+ b.WriteString(builtinProlog)
+ b.WriteString(f.Preamble)
+
+ for i, n := range names {
+ fmt.Fprintf(&b, "#line %d \"not-declared\"\n"+
+ "void __cgo_f_%d_1(void) { __typeof__(%s) *__cgo_undefined__1; }\n"+
+ "#line %d \"not-type\"\n"+
+ "void __cgo_f_%d_2(void) { %s *__cgo_undefined__2; }\n"+
+ "#line %d \"not-int-const\"\n"+
+ "void __cgo_f_%d_3(void) { enum { __cgo_undefined__3 = (%s)*1 }; }\n"+
+ "#line %d \"not-num-const\"\n"+
+ "void __cgo_f_%d_4(void) { static const double __cgo_undefined__4 = (%s); }\n"+
+ "#line %d \"not-str-lit\"\n"+
+ "void __cgo_f_%d_5(void) { static const char __cgo_undefined__5[] = (%s); }\n",
+ i+1, i+1, n.C,
+ i+1, i+1, n.C,
+ i+1, i+1, n.C,
+ i+1, i+1, n.C,
+ i+1, i+1, n.C,
+ )
+ }
+ fmt.Fprintf(&b, "#line 1 \"completed\"\n"+
+ "int __cgo__1 = __cgo__2;\n")
+
+ // We need to parse the output from this gcc command, so ensure that it
+ // doesn't have any ANSI escape sequences in it. (TERM=dumb is
+ // insufficient; if the user specifies CGO_CFLAGS=-fdiagnostics-color,
+ // GCC will ignore TERM, and GCC can also be configured at compile-time
+ // to ignore TERM.)
+ stderr := p.gccErrors(b.Bytes(), "-fdiagnostics-color=never")
+ if strings.Contains(stderr, "unrecognized command line option") {
+ // We're using an old version of GCC that doesn't understand
+ // -fdiagnostics-color. Those versions can't print color anyway,
+ // so just rerun without that option.
+ stderr = p.gccErrors(b.Bytes())
+ }
+ if stderr == "" {
+ fatalf("%s produced no output\non input:\n%s", gccBaseCmd[0], b.Bytes())
+ }
+
+ completed := false
+ sniff := make([]int, len(names))
+ const (
+ notType = 1 << iota
+ notIntConst
+ notNumConst
+ notStrLiteral
+ notDeclared
+ )
+ sawUnmatchedErrors := false
+ for _, line := range strings.Split(stderr, "\n") {
+ // Ignore warnings and random comments, with one
+ // exception: newer GCC versions will sometimes emit
+ // an error on a macro #define with a note referring
+ // to where the expansion occurs. We care about where
+ // the expansion occurs, so in that case treat the note
+ // as an error.
+ isError := strings.Contains(line, ": error:")
+ isErrorNote := strings.Contains(line, ": note:") && sawUnmatchedErrors
+ if !isError && !isErrorNote {
+ continue
+ }
+
+ c1 := strings.Index(line, ":")
+ if c1 < 0 {
+ continue
+ }
+ c2 := strings.Index(line[c1+1:], ":")
+ if c2 < 0 {
+ continue
+ }
+ c2 += c1 + 1
+
+ filename := line[:c1]
+ i, _ := strconv.Atoi(line[c1+1 : c2])
+ i--
+ if i < 0 || i >= len(names) {
+ if isError {
+ sawUnmatchedErrors = true
+ }
+ continue
+ }
+
+ switch filename {
+ case "completed":
+ // Strictly speaking, there is no guarantee that seeing the error at completed:1
+ // (at the end of the file) means we've seen all the errors from earlier in the file,
+ // but usually it does. Certainly if we don't see the completed:1 error, we did
+ // not get all the errors we expected.
+ completed = true
+
+ case "not-declared":
+ sniff[i] |= notDeclared
+ case "not-type":
+ sniff[i] |= notType
+ case "not-int-const":
+ sniff[i] |= notIntConst
+ case "not-num-const":
+ sniff[i] |= notNumConst
+ case "not-str-lit":
+ sniff[i] |= notStrLiteral
+ default:
+ if isError {
+ sawUnmatchedErrors = true
+ }
+ continue
+ }
+
+ sawUnmatchedErrors = false
+ }
+
+ if !completed {
+ fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", gccBaseCmd[0], b.Bytes(), stderr)
+ }
+
+ for i, n := range names {
+ switch sniff[i] {
+ default:
+ if sniff[i]&notDeclared != 0 && optional[n] {
+ // Ignore optional undeclared identifiers.
+ // Don't report an error, and skip adding n to the needType array.
+ continue
+ }
+ error_(f.NamePos[n], "could not determine kind of name for C.%s", fixGo(n.Go))
+ case notStrLiteral | notType:
+ n.Kind = "iconst"
+ case notIntConst | notStrLiteral | notType:
+ n.Kind = "fconst"
+ case notIntConst | notNumConst | notType:
+ n.Kind = "sconst"
+ case notIntConst | notNumConst | notStrLiteral:
+ n.Kind = "type"
+ case notIntConst | notNumConst | notStrLiteral | notType:
+ n.Kind = "not-type"
+ }
+ needType = append(needType, n)
+ }
+ if nerrors > 0 {
+ // Check if compiling the preamble by itself causes any errors,
+ // because the messages we've printed out so far aren't helpful
+ // to users debugging preamble mistakes. See issue 8442.
+ preambleErrors := p.gccErrors([]byte(builtinProlog + f.Preamble))
+ if len(preambleErrors) > 0 {
+ error_(token.NoPos, "\n%s errors for preamble:\n%s", gccBaseCmd[0], preambleErrors)
+ }
+
+ fatalf("unresolved names")
+ }
+
+ return needType
+}
+
+// loadDWARF parses the DWARF debug information generated
+// by gcc to learn the details of the constants, variables, and types
+// being referred to as C.xxx.
+func (p *Package) loadDWARF(f *File, conv *typeConv, names []*Name) {
+ // Extract the types from the DWARF section of an object
+ // from a well-formed C program. Gcc only generates DWARF info
+ // for symbols in the object file, so it is not enough to print the
+ // preamble and hope the symbols we care about will be there.
+ // Instead, emit
+ // __typeof__(names[i]) *__cgo__i;
+ // for each entry in names and then dereference the type we
+ // learn for __cgo__i.
+ var b bytes.Buffer
+ b.WriteString(builtinProlog)
+ b.WriteString(f.Preamble)
+ b.WriteString("#line 1 \"cgo-dwarf-inference\"\n")
+ for i, n := range names {
+ fmt.Fprintf(&b, "__typeof__(%s) *__cgo__%d;\n", n.C, i)
+ if n.Kind == "iconst" {
+ fmt.Fprintf(&b, "enum { __cgo_enum__%d = %s };\n", i, n.C)
+ }
+ }
+
+ // We create a data block initialized with the values,
+ // so we can read them out of the object file.
+ fmt.Fprintf(&b, "long long __cgodebug_ints[] = {\n")
+ for _, n := range names {
+ if n.Kind == "iconst" {
+ fmt.Fprintf(&b, "\t%s,\n", n.C)
+ } else {
+ fmt.Fprintf(&b, "\t0,\n")
+ }
+ }
+ // for the last entry, we cannot use 0, otherwise
+ // in case all __cgodebug_data is zero initialized,
+ // LLVM-based gcc will place the it in the __DATA.__common
+ // zero-filled section (our debug/macho doesn't support
+ // this)
+ fmt.Fprintf(&b, "\t1\n")
+ fmt.Fprintf(&b, "};\n")
+
+ // do the same work for floats.
+ fmt.Fprintf(&b, "double __cgodebug_floats[] = {\n")
+ for _, n := range names {
+ if n.Kind == "fconst" {
+ fmt.Fprintf(&b, "\t%s,\n", n.C)
+ } else {
+ fmt.Fprintf(&b, "\t0,\n")
+ }
+ }
+ fmt.Fprintf(&b, "\t1\n")
+ fmt.Fprintf(&b, "};\n")
+
+ // do the same work for strings.
+ for i, n := range names {
+ if n.Kind == "sconst" {
+ fmt.Fprintf(&b, "const char __cgodebug_str__%d[] = %s;\n", i, n.C)
+ fmt.Fprintf(&b, "const unsigned long long __cgodebug_strlen__%d = sizeof(%s)-1;\n", i, n.C)
+ }
+ }
+
+ d, ints, floats, strs := p.gccDebug(b.Bytes(), len(names))
+
+ // Scan DWARF info for top-level TagVariable entries with AttrName __cgo__i.
+ types := make([]dwarf.Type, len(names))
+ r := d.Reader()
+ for {
+ e, err := r.Next()
+ if err != nil {
+ fatalf("reading DWARF entry: %s", err)
+ }
+ if e == nil {
+ break
+ }
+ switch e.Tag {
+ case dwarf.TagVariable:
+ name, _ := e.Val(dwarf.AttrName).(string)
+ // As of https://reviews.llvm.org/D123534, clang
+ // now emits DW_TAG_variable DIEs that have
+ // no name (so as to be able to describe the
+ // type and source locations of constant strings
+ // like the second arg in the call below:
+ //
+ // myfunction(42, "foo")
+ //
+ // If a var has no name we won't see attempts to
+ // refer to it via "C.<name>", so skip these vars
+ //
+ // See issue 53000 for more context.
+ if name == "" {
+ break
+ }
+ typOff, _ := e.Val(dwarf.AttrType).(dwarf.Offset)
+ if typOff == 0 {
+ if e.Val(dwarf.AttrSpecification) != nil {
+ // Since we are reading all the DWARF,
+ // assume we will see the variable elsewhere.
+ break
+ }
+ fatalf("malformed DWARF TagVariable entry")
+ }
+ if !strings.HasPrefix(name, "__cgo__") {
+ break
+ }
+ typ, err := d.Type(typOff)
+ if err != nil {
+ fatalf("loading DWARF type: %s", err)
+ }
+ t, ok := typ.(*dwarf.PtrType)
+ if !ok || t == nil {
+ fatalf("internal error: %s has non-pointer type", name)
+ }
+ i, err := strconv.Atoi(name[7:])
+ if err != nil {
+ fatalf("malformed __cgo__ name: %s", name)
+ }
+ types[i] = t.Type
+ p.recordTypedefs(t.Type, f.NamePos[names[i]])
+ }
+ if e.Tag != dwarf.TagCompileUnit {
+ r.SkipChildren()
+ }
+ }
+
+ // Record types and typedef information.
+ for i, n := range names {
+ if strings.HasSuffix(n.Go, "GetTypeID") && types[i].String() == "func() CFTypeID" {
+ conv.getTypeIDs[n.Go[:len(n.Go)-9]] = true
+ }
+ }
+ for i, n := range names {
+ if types[i] == nil {
+ continue
+ }
+ pos := f.NamePos[n]
+ f, fok := types[i].(*dwarf.FuncType)
+ if n.Kind != "type" && fok {
+ n.Kind = "func"
+ n.FuncType = conv.FuncType(f, pos)
+ } else {
+ n.Type = conv.Type(types[i], pos)
+ switch n.Kind {
+ case "iconst":
+ if i < len(ints) {
+ if _, ok := types[i].(*dwarf.UintType); ok {
+ n.Const = fmt.Sprintf("%#x", uint64(ints[i]))
+ } else {
+ n.Const = fmt.Sprintf("%#x", ints[i])
+ }
+ }
+ case "fconst":
+ if i >= len(floats) {
+ break
+ }
+ switch base(types[i]).(type) {
+ case *dwarf.IntType, *dwarf.UintType:
+ // This has an integer type so it's
+ // not really a floating point
+ // constant. This can happen when the
+ // C compiler complains about using
+ // the value as an integer constant,
+ // but not as a general constant.
+ // Treat this as a variable of the
+ // appropriate type, not a constant,
+ // to get C-style type handling,
+ // avoiding the problem that C permits
+ // uint64(-1) but Go does not.
+ // See issue 26066.
+ n.Kind = "var"
+ default:
+ n.Const = fmt.Sprintf("%f", floats[i])
+ }
+ case "sconst":
+ if i < len(strs) {
+ n.Const = fmt.Sprintf("%q", strs[i])
+ }
+ }
+ }
+ conv.FinishType(pos)
+ }
+}
+
+// recordTypedefs remembers in p.typedefs all the typedefs used in dtypes and its children.
+func (p *Package) recordTypedefs(dtype dwarf.Type, pos token.Pos) {
+ p.recordTypedefs1(dtype, pos, map[dwarf.Type]bool{})
+}
+
+func (p *Package) recordTypedefs1(dtype dwarf.Type, pos token.Pos, visited map[dwarf.Type]bool) {
+ if dtype == nil {
+ return
+ }
+ if visited[dtype] {
+ return
+ }
+ visited[dtype] = true
+ switch dt := dtype.(type) {
+ case *dwarf.TypedefType:
+ if strings.HasPrefix(dt.Name, "__builtin") {
+ // Don't look inside builtin types. There be dragons.
+ return
+ }
+ if !p.typedefs[dt.Name] {
+ p.typedefs[dt.Name] = true
+ p.typedefList = append(p.typedefList, typedefInfo{dt.Name, pos})
+ p.recordTypedefs1(dt.Type, pos, visited)
+ }
+ case *dwarf.PtrType:
+ p.recordTypedefs1(dt.Type, pos, visited)
+ case *dwarf.ArrayType:
+ p.recordTypedefs1(dt.Type, pos, visited)
+ case *dwarf.QualType:
+ p.recordTypedefs1(dt.Type, pos, visited)
+ case *dwarf.FuncType:
+ p.recordTypedefs1(dt.ReturnType, pos, visited)
+ for _, a := range dt.ParamType {
+ p.recordTypedefs1(a, pos, visited)
+ }
+ case *dwarf.StructType:
+ for _, f := range dt.Field {
+ p.recordTypedefs1(f.Type, pos, visited)
+ }
+ }
+}
+
+// prepareNames finalizes the Kind field of not-type names and sets
+// the mangled name of all names.
+func (p *Package) prepareNames(f *File) {
+ for _, n := range f.Name {
+ if n.Kind == "not-type" {
+ if n.Define == "" {
+ n.Kind = "var"
+ } else {
+ n.Kind = "macro"
+ n.FuncType = &FuncType{
+ Result: n.Type,
+ Go: &ast.FuncType{
+ Results: &ast.FieldList{List: []*ast.Field{{Type: n.Type.Go}}},
+ },
+ }
+ }
+ }
+ p.mangleName(n)
+ if n.Kind == "type" && typedef[n.Mangle] == nil {
+ typedef[n.Mangle] = n.Type
+ }
+ }
+}
+
+// mangleName does name mangling to translate names
+// from the original Go source files to the names
+// used in the final Go files generated by cgo.
+func (p *Package) mangleName(n *Name) {
+ // When using gccgo variables have to be
+ // exported so that they become global symbols
+ // that the C code can refer to.
+ prefix := "_C"
+ if *gccgo && n.IsVar() {
+ prefix = "C"
+ }
+ n.Mangle = prefix + n.Kind + "_" + n.Go
+}
+
+func (f *File) isMangledName(s string) bool {
+ prefix := "_C"
+ if strings.HasPrefix(s, prefix) {
+ t := s[len(prefix):]
+ for _, k := range nameKinds {
+ if strings.HasPrefix(t, k+"_") {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// rewriteCalls rewrites all calls that pass pointers to check that
+// they follow the rules for passing pointers between Go and C.
+// This reports whether the package needs to import unsafe as _cgo_unsafe.
+func (p *Package) rewriteCalls(f *File) bool {
+ needsUnsafe := false
+ // Walk backward so that in C.f1(C.f2()) we rewrite C.f2 first.
+ for _, call := range f.Calls {
+ if call.Done {
+ continue
+ }
+ start := f.offset(call.Call.Pos())
+ end := f.offset(call.Call.End())
+ str, nu := p.rewriteCall(f, call)
+ if str != "" {
+ f.Edit.Replace(start, end, str)
+ if nu {
+ needsUnsafe = true
+ }
+ }
+ }
+ return needsUnsafe
+}
+
+// rewriteCall rewrites one call to add pointer checks.
+// If any pointer checks are required, we rewrite the call into a
+// function literal that calls _cgoCheckPointer for each pointer
+// argument and then calls the original function.
+// This returns the rewritten call and whether the package needs to
+// import unsafe as _cgo_unsafe.
+// If it returns the empty string, the call did not need to be rewritten.
+func (p *Package) rewriteCall(f *File, call *Call) (string, bool) {
+ // This is a call to C.xxx; set goname to "xxx".
+ // It may have already been mangled by rewriteName.
+ var goname string
+ switch fun := call.Call.Fun.(type) {
+ case *ast.SelectorExpr:
+ goname = fun.Sel.Name
+ case *ast.Ident:
+ goname = strings.TrimPrefix(fun.Name, "_C2func_")
+ goname = strings.TrimPrefix(goname, "_Cfunc_")
+ }
+ if goname == "" || goname == "malloc" {
+ return "", false
+ }
+ name := f.Name[goname]
+ if name == nil || name.Kind != "func" {
+ // Probably a type conversion.
+ return "", false
+ }
+
+ params := name.FuncType.Params
+ args := call.Call.Args
+ end := call.Call.End()
+
+ // Avoid a crash if the number of arguments doesn't match
+ // the number of parameters.
+ // This will be caught when the generated file is compiled.
+ if len(args) != len(params) {
+ return "", false
+ }
+
+ any := false
+ for i, param := range params {
+ if p.needsPointerCheck(f, param.Go, args[i]) {
+ any = true
+ break
+ }
+ }
+ if !any {
+ return "", false
+ }
+
+ // We need to rewrite this call.
+ //
+ // Rewrite C.f(p) to
+ // func() {
+ // _cgo0 := p
+ // _cgoCheckPointer(_cgo0, nil)
+ // C.f(_cgo0)
+ // }()
+ // Using a function literal like this lets us evaluate the
+ // function arguments only once while doing pointer checks.
+ // This is particularly useful when passing additional arguments
+ // to _cgoCheckPointer, as done in checkIndex and checkAddr.
+ //
+ // When the function argument is a conversion to unsafe.Pointer,
+ // we unwrap the conversion before checking the pointer,
+ // and then wrap again when calling C.f. This lets us check
+ // the real type of the pointer in some cases. See issue #25941.
+ //
+ // When the call to C.f is deferred, we use an additional function
+ // literal to evaluate the arguments at the right time.
+ // defer func() func() {
+ // _cgo0 := p
+ // return func() {
+ // _cgoCheckPointer(_cgo0, nil)
+ // C.f(_cgo0)
+ // }
+ // }()()
+ // This works because the defer statement evaluates the first
+ // function literal in order to get the function to call.
+
+ var sb bytes.Buffer
+ sb.WriteString("func() ")
+ if call.Deferred {
+ sb.WriteString("func() ")
+ }
+
+ needsUnsafe := false
+ result := false
+ twoResults := false
+ if !call.Deferred {
+ // Check whether this call expects two results.
+ for _, ref := range f.Ref {
+ if ref.Expr != &call.Call.Fun {
+ continue
+ }
+ if ref.Context == ctxCall2 {
+ sb.WriteString("(")
+ result = true
+ twoResults = true
+ }
+ break
+ }
+
+ // Add the result type, if any.
+ if name.FuncType.Result != nil {
+ rtype := p.rewriteUnsafe(name.FuncType.Result.Go)
+ if rtype != name.FuncType.Result.Go {
+ needsUnsafe = true
+ }
+ sb.WriteString(gofmtLine(rtype))
+ result = true
+ }
+
+ // Add the second result type, if any.
+ if twoResults {
+ if name.FuncType.Result == nil {
+ // An explicit void result looks odd but it
+ // seems to be how cgo has worked historically.
+ sb.WriteString("_Ctype_void")
+ }
+ sb.WriteString(", error)")
+ }
+ }
+
+ sb.WriteString("{ ")
+
+ // Define _cgoN for each argument value.
+ // Write _cgoCheckPointer calls to sbCheck.
+ var sbCheck bytes.Buffer
+ for i, param := range params {
+ origArg := args[i]
+ arg, nu := p.mangle(f, &args[i], true)
+ if nu {
+ needsUnsafe = true
+ }
+
+ // Use "var x T = ..." syntax to explicitly convert untyped
+ // constants to the parameter type, to avoid a type mismatch.
+ ptype := p.rewriteUnsafe(param.Go)
+
+ if !p.needsPointerCheck(f, param.Go, args[i]) || param.BadPointer || p.checkUnsafeStringData(args[i]) {
+ if ptype != param.Go {
+ needsUnsafe = true
+ }
+ fmt.Fprintf(&sb, "var _cgo%d %s = %s; ", i,
+ gofmtLine(ptype), gofmtPos(arg, origArg.Pos()))
+ continue
+ }
+
+ // Check for &a[i].
+ if p.checkIndex(&sb, &sbCheck, arg, i) {
+ continue
+ }
+
+ // Check for &x.
+ if p.checkAddr(&sb, &sbCheck, arg, i) {
+ continue
+ }
+
+ // Check for a[:].
+ if p.checkSlice(&sb, &sbCheck, arg, i) {
+ continue
+ }
+
+ fmt.Fprintf(&sb, "_cgo%d := %s; ", i, gofmtPos(arg, origArg.Pos()))
+ fmt.Fprintf(&sbCheck, "_cgoCheckPointer(_cgo%d, nil); ", i)
+ }
+
+ if call.Deferred {
+ sb.WriteString("return func() { ")
+ }
+
+ // Write out the calls to _cgoCheckPointer.
+ sb.WriteString(sbCheck.String())
+
+ if result {
+ sb.WriteString("return ")
+ }
+
+ m, nu := p.mangle(f, &call.Call.Fun, false)
+ if nu {
+ needsUnsafe = true
+ }
+ sb.WriteString(gofmtPos(m, end))
+
+ sb.WriteString("(")
+ for i := range params {
+ if i > 0 {
+ sb.WriteString(", ")
+ }
+ fmt.Fprintf(&sb, "_cgo%d", i)
+ }
+ sb.WriteString("); ")
+ if call.Deferred {
+ sb.WriteString("}")
+ }
+ sb.WriteString("}")
+ if call.Deferred {
+ sb.WriteString("()")
+ }
+ sb.WriteString("()")
+
+ return sb.String(), needsUnsafe
+}
+
+// needsPointerCheck reports whether the type t needs a pointer check.
+// This is true if t is a pointer and if the value to which it points
+// might contain a pointer.
+func (p *Package) needsPointerCheck(f *File, t ast.Expr, arg ast.Expr) bool {
+ // An untyped nil does not need a pointer check, and when
+ // _cgoCheckPointer returns the untyped nil the type assertion we
+ // are going to insert will fail. Easier to just skip nil arguments.
+ // TODO: Note that this fails if nil is shadowed.
+ if id, ok := arg.(*ast.Ident); ok && id.Name == "nil" {
+ return false
+ }
+
+ return p.hasPointer(f, t, true)
+}
+
+// hasPointer is used by needsPointerCheck. If top is true it returns
+// whether t is or contains a pointer that might point to a pointer.
+// If top is false it reports whether t is or contains a pointer.
+// f may be nil.
+func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {
+ switch t := t.(type) {
+ case *ast.ArrayType:
+ if t.Len == nil {
+ if !top {
+ return true
+ }
+ return p.hasPointer(f, t.Elt, false)
+ }
+ return p.hasPointer(f, t.Elt, top)
+ case *ast.StructType:
+ for _, field := range t.Fields.List {
+ if p.hasPointer(f, field.Type, top) {
+ return true
+ }
+ }
+ return false
+ case *ast.StarExpr: // Pointer type.
+ if !top {
+ return true
+ }
+ // Check whether this is a pointer to a C union (or class)
+ // type that contains a pointer.
+ if unionWithPointer[t.X] {
+ return true
+ }
+ return p.hasPointer(f, t.X, false)
+ case *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType:
+ return true
+ case *ast.Ident:
+ // TODO: Handle types defined within function.
+ for _, d := range p.Decl {
+ gd, ok := d.(*ast.GenDecl)
+ if !ok || gd.Tok != token.TYPE {
+ continue
+ }
+ for _, spec := range gd.Specs {
+ ts, ok := spec.(*ast.TypeSpec)
+ if !ok {
+ continue
+ }
+ if ts.Name.Name == t.Name {
+ return p.hasPointer(f, ts.Type, top)
+ }
+ }
+ }
+ if def := typedef[t.Name]; def != nil {
+ return p.hasPointer(f, def.Go, top)
+ }
+ if t.Name == "string" {
+ return !top
+ }
+ if t.Name == "error" {
+ return true
+ }
+ if goTypes[t.Name] != nil {
+ return false
+ }
+ // We can't figure out the type. Conservative
+ // approach is to assume it has a pointer.
+ return true
+ case *ast.SelectorExpr:
+ if l, ok := t.X.(*ast.Ident); !ok || l.Name != "C" {
+ // Type defined in a different package.
+ // Conservative approach is to assume it has a
+ // pointer.
+ return true
+ }
+ if f == nil {
+ // Conservative approach: assume pointer.
+ return true
+ }
+ name := f.Name[t.Sel.Name]
+ if name != nil && name.Kind == "type" && name.Type != nil && name.Type.Go != nil {
+ return p.hasPointer(f, name.Type.Go, top)
+ }
+ // We can't figure out the type. Conservative
+ // approach is to assume it has a pointer.
+ return true
+ default:
+ error_(t.Pos(), "could not understand type %s", gofmt(t))
+ return true
+ }
+}
+
+// mangle replaces references to C names in arg with the mangled names,
+// rewriting calls when it finds them.
+// It removes the corresponding references in f.Ref and f.Calls, so that we
+// don't try to do the replacement again in rewriteRef or rewriteCall.
+// If addPosition is true, add position info to the idents of C names in arg.
+func (p *Package) mangle(f *File, arg *ast.Expr, addPosition bool) (ast.Expr, bool) {
+ needsUnsafe := false
+ f.walk(arg, ctxExpr, func(f *File, arg interface{}, context astContext) {
+ px, ok := arg.(*ast.Expr)
+ if !ok {
+ return
+ }
+ sel, ok := (*px).(*ast.SelectorExpr)
+ if ok {
+ if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
+ return
+ }
+
+ for _, r := range f.Ref {
+ if r.Expr == px {
+ *px = p.rewriteName(f, r, addPosition)
+ r.Done = true
+ break
+ }
+ }
+
+ return
+ }
+
+ call, ok := (*px).(*ast.CallExpr)
+ if !ok {
+ return
+ }
+
+ for _, c := range f.Calls {
+ if !c.Done && c.Call.Lparen == call.Lparen {
+ cstr, nu := p.rewriteCall(f, c)
+ if cstr != "" {
+ // Smuggle the rewritten call through an ident.
+ *px = ast.NewIdent(cstr)
+ if nu {
+ needsUnsafe = true
+ }
+ c.Done = true
+ }
+ }
+ }
+ })
+ return *arg, needsUnsafe
+}
+
+// checkIndex checks whether arg has the form &a[i], possibly inside
+// type conversions. If so, then in the general case it writes
+//
+// _cgoIndexNN := a
+// _cgoNN := &cgoIndexNN[i] // with type conversions, if any
+//
+// to sb, and writes
+//
+// _cgoCheckPointer(_cgoNN, _cgoIndexNN)
+//
+// to sbCheck, and returns true. If a is a simple variable or field reference,
+// it writes
+//
+// _cgoIndexNN := &a
+//
+// and dereferences the uses of _cgoIndexNN. Taking the address avoids
+// making a copy of an array.
+//
+// This tells _cgoCheckPointer to check the complete contents of the
+// slice or array being indexed, but no other part of the memory allocation.
+func (p *Package) checkIndex(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool {
+ // Strip type conversions.
+ x := arg
+ for {
+ c, ok := x.(*ast.CallExpr)
+ if !ok || len(c.Args) != 1 {
+ break
+ }
+ if !p.isType(c.Fun) && !p.isUnsafeData(c.Fun, false) {
+ break
+ }
+ x = c.Args[0]
+ }
+ u, ok := x.(*ast.UnaryExpr)
+ if !ok || u.Op != token.AND {
+ return false
+ }
+ index, ok := u.X.(*ast.IndexExpr)
+ if !ok {
+ return false
+ }
+
+ addr := ""
+ deref := ""
+ if p.isVariable(index.X) {
+ addr = "&"
+ deref = "*"
+ }
+
+ fmt.Fprintf(sb, "_cgoIndex%d := %s%s; ", i, addr, gofmtPos(index.X, index.X.Pos()))
+ origX := index.X
+ index.X = ast.NewIdent(fmt.Sprintf("_cgoIndex%d", i))
+ if deref == "*" {
+ index.X = &ast.StarExpr{X: index.X}
+ }
+ fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos()))
+ index.X = origX
+
+ fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgo%d, %s_cgoIndex%d); ", i, deref, i)
+
+ return true
+}
+
+// checkAddr checks whether arg has the form &x, possibly inside type
+// conversions. If so, it writes
+//
+// _cgoBaseNN := &x
+// _cgoNN := _cgoBaseNN // with type conversions, if any
+//
+// to sb, and writes
+//
+// _cgoCheckPointer(_cgoBaseNN, true)
+//
+// to sbCheck, and returns true. This tells _cgoCheckPointer to check
+// just the contents of the pointer being passed, not any other part
+// of the memory allocation. This is run after checkIndex, which looks
+// for the special case of &a[i], which requires different checks.
+func (p *Package) checkAddr(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool {
+ // Strip type conversions.
+ px := &arg
+ for {
+ c, ok := (*px).(*ast.CallExpr)
+ if !ok || len(c.Args) != 1 {
+ break
+ }
+ if !p.isType(c.Fun) && !p.isUnsafeData(c.Fun, false) {
+ break
+ }
+ px = &c.Args[0]
+ }
+ if u, ok := (*px).(*ast.UnaryExpr); !ok || u.Op != token.AND {
+ return false
+ }
+
+ fmt.Fprintf(sb, "_cgoBase%d := %s; ", i, gofmtPos(*px, (*px).Pos()))
+
+ origX := *px
+ *px = ast.NewIdent(fmt.Sprintf("_cgoBase%d", i))
+ fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos()))
+ *px = origX
+
+ // Use "0 == 0" to do the right thing in the unlikely event
+ // that "true" is shadowed.
+ fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgoBase%d, 0 == 0); ", i)
+
+ return true
+}
+
+// checkSlice checks whether arg has the form x[i:j], possibly inside
+// type conversions. If so, it writes
+//
+// _cgoSliceNN := x[i:j]
+// _cgoNN := _cgoSliceNN // with type conversions, if any
+//
+// to sb, and writes
+//
+// _cgoCheckPointer(_cgoSliceNN, true)
+//
+// to sbCheck, and returns true. This tells _cgoCheckPointer to check
+// just the contents of the slice being passed, not any other part
+// of the memory allocation.
+func (p *Package) checkSlice(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) bool {
+ // Strip type conversions.
+ px := &arg
+ for {
+ c, ok := (*px).(*ast.CallExpr)
+ if !ok || len(c.Args) != 1 {
+ break
+ }
+ if !p.isType(c.Fun) && !p.isUnsafeData(c.Fun, false) {
+ break
+ }
+ px = &c.Args[0]
+ }
+ if _, ok := (*px).(*ast.SliceExpr); !ok {
+ return false
+ }
+
+ fmt.Fprintf(sb, "_cgoSlice%d := %s; ", i, gofmtPos(*px, (*px).Pos()))
+
+ origX := *px
+ *px = ast.NewIdent(fmt.Sprintf("_cgoSlice%d", i))
+ fmt.Fprintf(sb, "_cgo%d := %s; ", i, gofmtPos(arg, arg.Pos()))
+ *px = origX
+
+ // Use 0 == 0 to do the right thing in the unlikely event
+ // that "true" is shadowed.
+ fmt.Fprintf(sbCheck, "_cgoCheckPointer(_cgoSlice%d, 0 == 0); ", i)
+
+ return true
+}
+
+// checkUnsafeStringData checks for a call to unsafe.StringData.
+// The result of that call can't contain a pointer so there is
+// no need to call _cgoCheckPointer.
+func (p *Package) checkUnsafeStringData(arg ast.Expr) bool {
+ x := arg
+ for {
+ c, ok := x.(*ast.CallExpr)
+ if !ok || len(c.Args) != 1 {
+ break
+ }
+ if p.isUnsafeData(c.Fun, true) {
+ return true
+ }
+ if !p.isType(c.Fun) {
+ break
+ }
+ x = c.Args[0]
+ }
+ return false
+}
+
+// isType reports whether the expression is definitely a type.
+// This is conservative--it returns false for an unknown identifier.
+func (p *Package) isType(t ast.Expr) bool {
+ switch t := t.(type) {
+ case *ast.SelectorExpr:
+ id, ok := t.X.(*ast.Ident)
+ if !ok {
+ return false
+ }
+ if id.Name == "unsafe" && t.Sel.Name == "Pointer" {
+ return true
+ }
+ if id.Name == "C" && typedef["_Ctype_"+t.Sel.Name] != nil {
+ return true
+ }
+ return false
+ case *ast.Ident:
+ // TODO: This ignores shadowing.
+ switch t.Name {
+ case "unsafe.Pointer", "bool", "byte",
+ "complex64", "complex128",
+ "error",
+ "float32", "float64",
+ "int", "int8", "int16", "int32", "int64",
+ "rune", "string",
+ "uint", "uint8", "uint16", "uint32", "uint64", "uintptr":
+
+ return true
+ }
+ if strings.HasPrefix(t.Name, "_Ctype_") {
+ return true
+ }
+ case *ast.ParenExpr:
+ return p.isType(t.X)
+ case *ast.StarExpr:
+ return p.isType(t.X)
+ case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType,
+ *ast.MapType, *ast.ChanType:
+
+ return true
+ }
+ return false
+}
+
+// isUnsafeData reports whether the expression is unsafe.StringData
+// or unsafe.SliceData. We can ignore these when checking for pointers
+// because they don't change whether or not their argument contains
+// any Go pointers. If onlyStringData is true we only check for StringData.
+func (p *Package) isUnsafeData(x ast.Expr, onlyStringData bool) bool {
+ st, ok := x.(*ast.SelectorExpr)
+ if !ok {
+ return false
+ }
+ id, ok := st.X.(*ast.Ident)
+ if !ok {
+ return false
+ }
+ if id.Name != "unsafe" {
+ return false
+ }
+ if !onlyStringData && st.Sel.Name == "SliceData" {
+ return true
+ }
+ return st.Sel.Name == "StringData"
+}
+
+// isVariable reports whether x is a variable, possibly with field references.
+func (p *Package) isVariable(x ast.Expr) bool {
+ switch x := x.(type) {
+ case *ast.Ident:
+ return true
+ case *ast.SelectorExpr:
+ return p.isVariable(x.X)
+ case *ast.IndexExpr:
+ return true
+ }
+ return false
+}
+
+// rewriteUnsafe returns a version of t with references to unsafe.Pointer
+// rewritten to use _cgo_unsafe.Pointer instead.
+func (p *Package) rewriteUnsafe(t ast.Expr) ast.Expr {
+ switch t := t.(type) {
+ case *ast.Ident:
+ // We don't see a SelectorExpr for unsafe.Pointer;
+ // this is created by code in this file.
+ if t.Name == "unsafe.Pointer" {
+ return ast.NewIdent("_cgo_unsafe.Pointer")
+ }
+ case *ast.ArrayType:
+ t1 := p.rewriteUnsafe(t.Elt)
+ if t1 != t.Elt {
+ r := *t
+ r.Elt = t1
+ return &r
+ }
+ case *ast.StructType:
+ changed := false
+ fields := *t.Fields
+ fields.List = nil
+ for _, f := range t.Fields.List {
+ ft := p.rewriteUnsafe(f.Type)
+ if ft == f.Type {
+ fields.List = append(fields.List, f)
+ } else {
+ fn := *f
+ fn.Type = ft
+ fields.List = append(fields.List, &fn)
+ changed = true
+ }
+ }
+ if changed {
+ r := *t
+ r.Fields = &fields
+ return &r
+ }
+ case *ast.StarExpr: // Pointer type.
+ x1 := p.rewriteUnsafe(t.X)
+ if x1 != t.X {
+ r := *t
+ r.X = x1
+ return &r
+ }
+ }
+ return t
+}
+
+// rewriteRef rewrites all the C.xxx references in f.AST to refer to the
+// Go equivalents, now that we have figured out the meaning of all
+// the xxx. In *godefs mode, rewriteRef replaces the names
+// with full definitions instead of mangled names.
+func (p *Package) rewriteRef(f *File) {
+ // Keep a list of all the functions, to remove the ones
+ // only used as expressions and avoid generating bridge
+ // code for them.
+ functions := make(map[string]bool)
+
+ for _, n := range f.Name {
+ if n.Kind == "func" {
+ functions[n.Go] = false
+ }
+ }
+
+ // Now that we have all the name types filled in,
+ // scan through the Refs to identify the ones that
+ // are trying to do a ,err call. Also check that
+ // functions are only used in calls.
+ for _, r := range f.Ref {
+ if r.Name.IsConst() && r.Name.Const == "" {
+ error_(r.Pos(), "unable to find value of constant C.%s", fixGo(r.Name.Go))
+ }
+
+ if r.Name.Kind == "func" {
+ switch r.Context {
+ case ctxCall, ctxCall2:
+ functions[r.Name.Go] = true
+ }
+ }
+
+ expr := p.rewriteName(f, r, false)
+
+ if *godefs {
+ // Substitute definition for mangled type name.
+ if r.Name.Type != nil && r.Name.Kind == "type" {
+ expr = r.Name.Type.Go
+ }
+ if id, ok := expr.(*ast.Ident); ok {
+ if t := typedef[id.Name]; t != nil {
+ expr = t.Go
+ }
+ if id.Name == r.Name.Mangle && r.Name.Const != "" {
+ expr = ast.NewIdent(r.Name.Const)
+ }
+ }
+ }
+
+ // Copy position information from old expr into new expr,
+ // in case expression being replaced is first on line.
+ // See golang.org/issue/6563.
+ pos := (*r.Expr).Pos()
+ if x, ok := expr.(*ast.Ident); ok {
+ expr = &ast.Ident{NamePos: pos, Name: x.Name}
+ }
+
+ // Change AST, because some later processing depends on it,
+ // and also because -godefs mode still prints the AST.
+ old := *r.Expr
+ *r.Expr = expr
+
+ // Record source-level edit for cgo output.
+ if !r.Done {
+ // Prepend a space in case the earlier code ends
+ // with '/', which would give us a "//" comment.
+ repl := " " + gofmtPos(expr, old.Pos())
+ end := fset.Position(old.End())
+ // Subtract 1 from the column if we are going to
+ // append a close parenthesis. That will set the
+ // correct column for the following characters.
+ sub := 0
+ if r.Name.Kind != "type" {
+ sub = 1
+ }
+ if end.Column > sub {
+ repl = fmt.Sprintf("%s /*line :%d:%d*/", repl, end.Line, end.Column-sub)
+ }
+ if r.Name.Kind != "type" {
+ repl = "(" + repl + ")"
+ }
+ f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), repl)
+ }
+ }
+
+ // Remove functions only used as expressions, so their respective
+ // bridge functions are not generated.
+ for name, used := range functions {
+ if !used {
+ delete(f.Name, name)
+ }
+ }
+}
+
+// rewriteName returns the expression used to rewrite a reference.
+// If addPosition is true, add position info in the ident name.
+func (p *Package) rewriteName(f *File, r *Ref, addPosition bool) ast.Expr {
+ getNewIdent := ast.NewIdent
+ if addPosition {
+ getNewIdent = func(newName string) *ast.Ident {
+ mangledIdent := ast.NewIdent(newName)
+ if len(newName) == len(r.Name.Go) {
+ return mangledIdent
+ }
+ p := fset.Position((*r.Expr).End())
+ if p.Column == 0 {
+ return mangledIdent
+ }
+ return ast.NewIdent(fmt.Sprintf("%s /*line :%d:%d*/", newName, p.Line, p.Column))
+ }
+ }
+ var expr ast.Expr = getNewIdent(r.Name.Mangle) // default
+ switch r.Context {
+ case ctxCall, ctxCall2:
+ if r.Name.Kind != "func" {
+ if r.Name.Kind == "type" {
+ r.Context = ctxType
+ if r.Name.Type == nil {
+ error_(r.Pos(), "invalid conversion to C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
+ }
+ break
+ }
+ error_(r.Pos(), "call of non-function C.%s", fixGo(r.Name.Go))
+ break
+ }
+ if r.Context == ctxCall2 {
+ if r.Name.Go == "_CMalloc" {
+ error_(r.Pos(), "no two-result form for C.malloc")
+ break
+ }
+ // Invent new Name for the two-result function.
+ n := f.Name["2"+r.Name.Go]
+ if n == nil {
+ n = new(Name)
+ *n = *r.Name
+ n.AddError = true
+ n.Mangle = "_C2func_" + n.Go
+ f.Name["2"+r.Name.Go] = n
+ }
+ expr = getNewIdent(n.Mangle)
+ r.Name = n
+ break
+ }
+ case ctxExpr:
+ switch r.Name.Kind {
+ case "func":
+ if builtinDefs[r.Name.C] != "" {
+ error_(r.Pos(), "use of builtin '%s' not in function call", fixGo(r.Name.C))
+ }
+
+ // Function is being used in an expression, to e.g. pass around a C function pointer.
+ // Create a new Name for this Ref which causes the variable to be declared in Go land.
+ fpName := "fp_" + r.Name.Go
+ name := f.Name[fpName]
+ if name == nil {
+ name = &Name{
+ Go: fpName,
+ C: r.Name.C,
+ Kind: "fpvar",
+ Type: &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*"), Go: ast.NewIdent("unsafe.Pointer")},
+ }
+ p.mangleName(name)
+ f.Name[fpName] = name
+ }
+ r.Name = name
+ // Rewrite into call to _Cgo_ptr to prevent assignments. The _Cgo_ptr
+ // function is defined in out.go and simply returns its argument. See
+ // issue 7757.
+ expr = &ast.CallExpr{
+ Fun: &ast.Ident{NamePos: (*r.Expr).Pos(), Name: "_Cgo_ptr"},
+ Args: []ast.Expr{getNewIdent(name.Mangle)},
+ }
+ case "type":
+ // Okay - might be new(T), T(x), Generic[T], etc.
+ if r.Name.Type == nil {
+ error_(r.Pos(), "expression C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
+ }
+ case "var":
+ expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
+ case "macro":
+ expr = &ast.CallExpr{Fun: expr}
+ }
+ case ctxSelector:
+ if r.Name.Kind == "var" {
+ expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
+ } else {
+ error_(r.Pos(), "only C variables allowed in selector expression %s", fixGo(r.Name.Go))
+ }
+ case ctxType:
+ if r.Name.Kind != "type" {
+ error_(r.Pos(), "expression C.%s used as type", fixGo(r.Name.Go))
+ } else if r.Name.Type == nil {
+ // Use of C.enum_x, C.struct_x or C.union_x without C definition.
+ // GCC won't raise an error when using pointers to such unknown types.
+ error_(r.Pos(), "type C.%s: undefined C type '%s'", fixGo(r.Name.Go), r.Name.C)
+ }
+ default:
+ if r.Name.Kind == "func" {
+ error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go))
+ }
+ }
+ return expr
+}
+
+// gofmtPos returns the gofmt-formatted string for an AST node,
+// with a comment setting the position before the node.
+func gofmtPos(n ast.Expr, pos token.Pos) string {
+ s := gofmtLine(n)
+ p := fset.Position(pos)
+ if p.Column == 0 {
+ return s
+ }
+ return fmt.Sprintf("/*line :%d:%d*/%s", p.Line, p.Column, s)
+}
+
+// checkGCCBaseCmd returns the start of the compiler command line.
+// It uses $CC if set, or else $GCC, or else the compiler recorded
+// during the initial build as defaultCC.
+// defaultCC is defined in zdefaultcc.go, written by cmd/dist.
+//
+// The compiler command line is split into arguments on whitespace. Quotes
+// are understood, so arguments may contain whitespace.
+//
+// checkGCCBaseCmd confirms that the compiler exists in PATH, returning
+// an error if it does not.
+func checkGCCBaseCmd() ([]string, error) {
+ // Use $CC if set, since that's what the build uses.
+ value := os.Getenv("CC")
+ if value == "" {
+ // Try $GCC if set, since that's what we used to use.
+ value = os.Getenv("GCC")
+ }
+ if value == "" {
+ value = defaultCC(goos, goarch)
+ }
+ args, err := quoted.Split(value)
+ if err != nil {
+ return nil, err
+ }
+ if len(args) == 0 {
+ return nil, errors.New("CC not set and no default found")
+ }
+ if _, err := exec.LookPath(args[0]); err != nil {
+ return nil, fmt.Errorf("C compiler %q not found: %v", args[0], err)
+ }
+ return args[:len(args):len(args)], nil
+}
+
+// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm".
+func (p *Package) gccMachine() []string {
+ switch goarch {
+ case "amd64":
+ if goos == "darwin" {
+ return []string{"-arch", "x86_64", "-m64"}
+ }
+ return []string{"-m64"}
+ case "arm64":
+ if goos == "darwin" {
+ return []string{"-arch", "arm64"}
+ }
+ case "386":
+ return []string{"-m32"}
+ case "arm":
+ return []string{"-marm"} // not thumb
+ case "s390":
+ return []string{"-m31"}
+ case "s390x":
+ return []string{"-m64"}
+ case "mips64", "mips64le":
+ if gomips64 == "hardfloat" {
+ return []string{"-mabi=64", "-mhard-float"}
+ } else if gomips64 == "softfloat" {
+ return []string{"-mabi=64", "-msoft-float"}
+ }
+ case "mips", "mipsle":
+ if gomips == "hardfloat" {
+ return []string{"-mabi=32", "-mfp32", "-mhard-float", "-mno-odd-spreg"}
+ } else if gomips == "softfloat" {
+ return []string{"-mabi=32", "-msoft-float"}
+ }
+ case "loong64":
+ return []string{"-mabi=lp64d"}
+ }
+ return nil
+}
+
+func gccTmp() string {
+ return *objDir + "_cgo_.o"
+}
+
+// gccCmd returns the gcc command line to use for compiling
+// the input.
+func (p *Package) gccCmd() []string {
+ c := append(gccBaseCmd,
+ "-w", // no warnings
+ "-Wno-error", // warnings are not errors
+ "-o"+gccTmp(), // write object to tmp
+ "-gdwarf-2", // generate DWARF v2 debugging symbols
+ "-c", // do not link
+ "-xc", // input language is C
+ )
+ if p.GccIsClang {
+ c = append(c,
+ "-ferror-limit=0",
+ // Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn)
+ // doesn't have -Wno-unneeded-internal-declaration, so we need yet another
+ // flag to disable the warning. Yes, really good diagnostics, clang.
+ "-Wno-unknown-warning-option",
+ "-Wno-unneeded-internal-declaration",
+ "-Wno-unused-function",
+ "-Qunused-arguments",
+ // Clang embeds prototypes for some builtin functions,
+ // like malloc and calloc, but all size_t parameters are
+ // incorrectly typed unsigned long. We work around that
+ // by disabling the builtin functions (this is safe as
+ // it won't affect the actual compilation of the C code).
+ // See: https://golang.org/issue/6506.
+ "-fno-builtin",
+ )
+ }
+
+ c = append(c, p.GccOptions...)
+ c = append(c, p.gccMachine()...)
+ if goos == "aix" {
+ c = append(c, "-maix64")
+ c = append(c, "-mcmodel=large")
+ }
+ // disable LTO so we get an object whose symbols we can read
+ c = append(c, "-fno-lto")
+ c = append(c, "-") //read input from standard input
+ return c
+}
+
+// gccDebug runs gcc -gdwarf-2 over the C program stdin and
+// returns the corresponding DWARF data and, if present, debug data block.
+func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int64, floats []float64, strs []string) {
+ runGcc(stdin, p.gccCmd())
+
+ isDebugInts := func(s string) bool {
+ // Some systems use leading _ to denote non-assembly symbols.
+ return s == "__cgodebug_ints" || s == "___cgodebug_ints"
+ }
+ isDebugFloats := func(s string) bool {
+ // Some systems use leading _ to denote non-assembly symbols.
+ return s == "__cgodebug_floats" || s == "___cgodebug_floats"
+ }
+ indexOfDebugStr := func(s string) int {
+ // Some systems use leading _ to denote non-assembly symbols.
+ if strings.HasPrefix(s, "___") {
+ s = s[1:]
+ }
+ if strings.HasPrefix(s, "__cgodebug_str__") {
+ if n, err := strconv.Atoi(s[len("__cgodebug_str__"):]); err == nil {
+ return n
+ }
+ }
+ return -1
+ }
+ indexOfDebugStrlen := func(s string) int {
+ // Some systems use leading _ to denote non-assembly symbols.
+ if strings.HasPrefix(s, "___") {
+ s = s[1:]
+ }
+ if strings.HasPrefix(s, "__cgodebug_strlen__") {
+ if n, err := strconv.Atoi(s[len("__cgodebug_strlen__"):]); err == nil {
+ return n
+ }
+ }
+ return -1
+ }
+
+ strs = make([]string, nnames)
+
+ strdata := make(map[int]string, nnames)
+ strlens := make(map[int]int, nnames)
+
+ buildStrings := func() {
+ for n, strlen := range strlens {
+ data := strdata[n]
+ if len(data) <= strlen {
+ fatalf("invalid string literal")
+ }
+ strs[n] = data[:strlen]
+ }
+ }
+
+ if f, err := macho.Open(gccTmp()); err == nil {
+ defer f.Close()
+ d, err := f.DWARF()
+ if err != nil {
+ fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
+ }
+ bo := f.ByteOrder
+ if f.Symtab != nil {
+ for i := range f.Symtab.Syms {
+ s := &f.Symtab.Syms[i]
+ switch {
+ case isDebugInts(s.Name):
+ // Found it. Now find data section.
+ if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value-sect.Addr:]
+ ints = make([]int64, len(data)/8)
+ for i := range ints {
+ ints[i] = int64(bo.Uint64(data[i*8:]))
+ }
+ }
+ }
+ }
+ case isDebugFloats(s.Name):
+ // Found it. Now find data section.
+ if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value-sect.Addr:]
+ floats = make([]float64, len(data)/8)
+ for i := range floats {
+ floats[i] = math.Float64frombits(bo.Uint64(data[i*8:]))
+ }
+ }
+ }
+ }
+ default:
+ if n := indexOfDebugStr(s.Name); n != -1 {
+ // Found it. Now find data section.
+ if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value-sect.Addr:]
+ strdata[n] = string(data)
+ }
+ }
+ }
+ break
+ }
+ if n := indexOfDebugStrlen(s.Name); n != -1 {
+ // Found it. Now find data section.
+ if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value-sect.Addr:]
+ strlen := bo.Uint64(data[:8])
+ if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt?
+ fatalf("string literal too big")
+ }
+ strlens[n] = int(strlen)
+ }
+ }
+ }
+ break
+ }
+ }
+ }
+
+ buildStrings()
+ }
+ return d, ints, floats, strs
+ }
+
+ if f, err := elf.Open(gccTmp()); err == nil {
+ defer f.Close()
+ d, err := f.DWARF()
+ if err != nil {
+ fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
+ }
+ bo := f.ByteOrder
+ symtab, err := f.Symbols()
+ if err == nil {
+ // Check for use of -fsanitize=hwaddress (issue 53285).
+ removeTag := func(v uint64) uint64 { return v }
+ if goarch == "arm64" {
+ for i := range symtab {
+ if symtab[i].Name == "__hwasan_init" {
+ // -fsanitize=hwaddress on ARM
+ // uses the upper byte of a
+ // memory address as a hardware
+ // tag. Remove it so that
+ // we can find the associated
+ // data.
+ removeTag = func(v uint64) uint64 { return v &^ (0xff << (64 - 8)) }
+ break
+ }
+ }
+ }
+
+ for i := range symtab {
+ s := &symtab[i]
+ switch {
+ case isDebugInts(s.Name):
+ // Found it. Now find data section.
+ if i := int(s.Section); 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ val := removeTag(s.Value)
+ if sect.Addr <= val && val < sect.Addr+sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[val-sect.Addr:]
+ ints = make([]int64, len(data)/8)
+ for i := range ints {
+ ints[i] = int64(bo.Uint64(data[i*8:]))
+ }
+ }
+ }
+ }
+ case isDebugFloats(s.Name):
+ // Found it. Now find data section.
+ if i := int(s.Section); 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ val := removeTag(s.Value)
+ if sect.Addr <= val && val < sect.Addr+sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[val-sect.Addr:]
+ floats = make([]float64, len(data)/8)
+ for i := range floats {
+ floats[i] = math.Float64frombits(bo.Uint64(data[i*8:]))
+ }
+ }
+ }
+ }
+ default:
+ if n := indexOfDebugStr(s.Name); n != -1 {
+ // Found it. Now find data section.
+ if i := int(s.Section); 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ val := removeTag(s.Value)
+ if sect.Addr <= val && val < sect.Addr+sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[val-sect.Addr:]
+ strdata[n] = string(data)
+ }
+ }
+ }
+ break
+ }
+ if n := indexOfDebugStrlen(s.Name); n != -1 {
+ // Found it. Now find data section.
+ if i := int(s.Section); 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ val := removeTag(s.Value)
+ if sect.Addr <= val && val < sect.Addr+sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[val-sect.Addr:]
+ strlen := bo.Uint64(data[:8])
+ if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt?
+ fatalf("string literal too big")
+ }
+ strlens[n] = int(strlen)
+ }
+ }
+ }
+ break
+ }
+ }
+ }
+
+ buildStrings()
+ }
+ return d, ints, floats, strs
+ }
+
+ if f, err := pe.Open(gccTmp()); err == nil {
+ defer f.Close()
+ d, err := f.DWARF()
+ if err != nil {
+ fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
+ }
+ bo := binary.LittleEndian
+ for _, s := range f.Symbols {
+ switch {
+ case isDebugInts(s.Name):
+ if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if s.Value < sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value:]
+ ints = make([]int64, len(data)/8)
+ for i := range ints {
+ ints[i] = int64(bo.Uint64(data[i*8:]))
+ }
+ }
+ }
+ }
+ case isDebugFloats(s.Name):
+ if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if s.Value < sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value:]
+ floats = make([]float64, len(data)/8)
+ for i := range floats {
+ floats[i] = math.Float64frombits(bo.Uint64(data[i*8:]))
+ }
+ }
+ }
+ }
+ default:
+ if n := indexOfDebugStr(s.Name); n != -1 {
+ if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if s.Value < sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value:]
+ strdata[n] = string(data)
+ }
+ }
+ }
+ break
+ }
+ if n := indexOfDebugStrlen(s.Name); n != -1 {
+ if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if s.Value < sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value:]
+ strlen := bo.Uint64(data[:8])
+ if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt?
+ fatalf("string literal too big")
+ }
+ strlens[n] = int(strlen)
+ }
+ }
+ }
+ break
+ }
+ }
+ }
+
+ buildStrings()
+
+ return d, ints, floats, strs
+ }
+
+ if f, err := xcoff.Open(gccTmp()); err == nil {
+ defer f.Close()
+ d, err := f.DWARF()
+ if err != nil {
+ fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
+ }
+ bo := binary.BigEndian
+ for _, s := range f.Symbols {
+ switch {
+ case isDebugInts(s.Name):
+ if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if s.Value < sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value:]
+ ints = make([]int64, len(data)/8)
+ for i := range ints {
+ ints[i] = int64(bo.Uint64(data[i*8:]))
+ }
+ }
+ }
+ }
+ case isDebugFloats(s.Name):
+ if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if s.Value < sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value:]
+ floats = make([]float64, len(data)/8)
+ for i := range floats {
+ floats[i] = math.Float64frombits(bo.Uint64(data[i*8:]))
+ }
+ }
+ }
+ }
+ default:
+ if n := indexOfDebugStr(s.Name); n != -1 {
+ if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if s.Value < sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value:]
+ strdata[n] = string(data)
+ }
+ }
+ }
+ break
+ }
+ if n := indexOfDebugStrlen(s.Name); n != -1 {
+ if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
+ sect := f.Sections[i]
+ if s.Value < sect.Size {
+ if sdat, err := sect.Data(); err == nil {
+ data := sdat[s.Value:]
+ strlen := bo.Uint64(data[:8])
+ if strlen > (1<<(uint(p.IntSize*8)-1) - 1) { // greater than MaxInt?
+ fatalf("string literal too big")
+ }
+ strlens[n] = int(strlen)
+ }
+ }
+ }
+ break
+ }
+ }
+ }
+
+ buildStrings()
+ return d, ints, floats, strs
+ }
+ fatalf("cannot parse gcc output %s as ELF, Mach-O, PE, XCOFF object", gccTmp())
+ panic("not reached")
+}
+
+// gccDefines runs gcc -E -dM -xc - over the C program stdin
+// and returns the corresponding standard output, which is the
+// #defines that gcc encountered while processing the input
+// and its included files.
+func (p *Package) gccDefines(stdin []byte) string {
+ base := append(gccBaseCmd, "-E", "-dM", "-xc")
+ base = append(base, p.gccMachine()...)
+ stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-"))
+ return stdout
+}
+
+// gccErrors runs gcc over the C program stdin and returns
+// the errors that gcc prints. That is, this function expects
+// gcc to fail.
+func (p *Package) gccErrors(stdin []byte, extraArgs ...string) string {
+ // TODO(rsc): require failure
+ args := p.gccCmd()
+
+ // Optimization options can confuse the error messages; remove them.
+ nargs := make([]string, 0, len(args)+len(extraArgs))
+ for _, arg := range args {
+ if !strings.HasPrefix(arg, "-O") {
+ nargs = append(nargs, arg)
+ }
+ }
+
+ // Force -O0 optimization and append extra arguments, but keep the
+ // trailing "-" at the end.
+ li := len(nargs) - 1
+ last := nargs[li]
+ nargs[li] = "-O0"
+ nargs = append(nargs, extraArgs...)
+ nargs = append(nargs, last)
+
+ if *debugGcc {
+ fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(nargs, " "))
+ os.Stderr.Write(stdin)
+ fmt.Fprint(os.Stderr, "EOF\n")
+ }
+ stdout, stderr, _ := run(stdin, nargs)
+ if *debugGcc {
+ os.Stderr.Write(stdout)
+ os.Stderr.Write(stderr)
+ }
+ return string(stderr)
+}
+
+// runGcc runs the gcc command line args with stdin on standard input.
+// If the command exits with a non-zero exit status, runGcc prints
+// details about what was run and exits.
+// Otherwise runGcc returns the data written to standard output and standard error.
+// Note that for some of the uses we expect useful data back
+// on standard error, but for those uses gcc must still exit 0.
+func runGcc(stdin []byte, args []string) (string, string) {
+ if *debugGcc {
+ fmt.Fprintf(os.Stderr, "$ %s <<EOF\n", strings.Join(args, " "))
+ os.Stderr.Write(stdin)
+ fmt.Fprint(os.Stderr, "EOF\n")
+ }
+ stdout, stderr, ok := run(stdin, args)
+ if *debugGcc {
+ os.Stderr.Write(stdout)
+ os.Stderr.Write(stderr)
+ }
+ if !ok {
+ os.Stderr.Write(stderr)
+ os.Exit(2)
+ }
+ return string(stdout), string(stderr)
+}
+
+// A typeConv is a translator from dwarf types to Go types
+// with equivalent memory layout.
+type typeConv struct {
+ // Cache of already-translated or in-progress types.
+ m map[string]*Type
+
+ // Map from types to incomplete pointers to those types.
+ ptrs map[string][]*Type
+ // Keys of ptrs in insertion order (deterministic worklist)
+ // ptrKeys contains exactly the keys in ptrs.
+ ptrKeys []dwarf.Type
+
+ // Type names X for which there exists an XGetTypeID function with type func() CFTypeID.
+ getTypeIDs map[string]bool
+
+ // incompleteStructs contains C structs that should be marked Incomplete.
+ incompleteStructs map[string]bool
+
+ // Predeclared types.
+ bool ast.Expr
+ byte ast.Expr // denotes padding
+ int8, int16, int32, int64 ast.Expr
+ uint8, uint16, uint32, uint64, uintptr ast.Expr
+ float32, float64 ast.Expr
+ complex64, complex128 ast.Expr
+ void ast.Expr
+ string ast.Expr
+ goVoid ast.Expr // _Ctype_void, denotes C's void
+ goVoidPtr ast.Expr // unsafe.Pointer or *byte
+
+ ptrSize int64
+ intSize int64
+}
+
+var tagGen int
+var typedef = make(map[string]*Type)
+var goIdent = make(map[string]*ast.Ident)
+
+// unionWithPointer is true for a Go type that represents a C union (or class)
+// that may contain a pointer. This is used for cgo pointer checking.
+var unionWithPointer = make(map[ast.Expr]bool)
+
+// anonymousStructTag provides a consistent tag for an anonymous struct.
+// The same dwarf.StructType pointer will always get the same tag.
+var anonymousStructTag = make(map[*dwarf.StructType]string)
+
+func (c *typeConv) Init(ptrSize, intSize int64) {
+ c.ptrSize = ptrSize
+ c.intSize = intSize
+ c.m = make(map[string]*Type)
+ c.ptrs = make(map[string][]*Type)
+ c.getTypeIDs = make(map[string]bool)
+ c.incompleteStructs = make(map[string]bool)
+ c.bool = c.Ident("bool")
+ c.byte = c.Ident("byte")
+ c.int8 = c.Ident("int8")
+ c.int16 = c.Ident("int16")
+ c.int32 = c.Ident("int32")
+ c.int64 = c.Ident("int64")
+ c.uint8 = c.Ident("uint8")
+ c.uint16 = c.Ident("uint16")
+ c.uint32 = c.Ident("uint32")
+ c.uint64 = c.Ident("uint64")
+ c.uintptr = c.Ident("uintptr")
+ c.float32 = c.Ident("float32")
+ c.float64 = c.Ident("float64")
+ c.complex64 = c.Ident("complex64")
+ c.complex128 = c.Ident("complex128")
+ c.void = c.Ident("void")
+ c.string = c.Ident("string")
+ c.goVoid = c.Ident("_Ctype_void")
+
+ // Normally cgo translates void* to unsafe.Pointer,
+ // but for historical reasons -godefs uses *byte instead.
+ if *godefs {
+ c.goVoidPtr = &ast.StarExpr{X: c.byte}
+ } else {
+ c.goVoidPtr = c.Ident("unsafe.Pointer")
+ }
+}
+
+// base strips away qualifiers and typedefs to get the underlying type.
+func base(dt dwarf.Type) dwarf.Type {
+ for {
+ if d, ok := dt.(*dwarf.QualType); ok {
+ dt = d.Type
+ continue
+ }
+ if d, ok := dt.(*dwarf.TypedefType); ok {
+ dt = d.Type
+ continue
+ }
+ break
+ }
+ return dt
+}
+
+// unqual strips away qualifiers from a DWARF type.
+// In general we don't care about top-level qualifiers.
+func unqual(dt dwarf.Type) dwarf.Type {
+ for {
+ if d, ok := dt.(*dwarf.QualType); ok {
+ dt = d.Type
+ } else {
+ break
+ }
+ }
+ return dt
+}
+
+// Map from dwarf text names to aliases we use in package "C".
+var dwarfToName = map[string]string{
+ "long int": "long",
+ "long unsigned int": "ulong",
+ "unsigned int": "uint",
+ "short unsigned int": "ushort",
+ "unsigned short": "ushort", // Used by Clang; issue 13129.
+ "short int": "short",
+ "long long int": "longlong",
+ "long long unsigned int": "ulonglong",
+ "signed char": "schar",
+ "unsigned char": "uchar",
+ "unsigned long": "ulong", // Used by Clang 14; issue 53013.
+ "unsigned long long": "ulonglong", // Used by Clang 14; issue 53013.
+}
+
+const signedDelta = 64
+
+// String returns the current type representation. Format arguments
+// are assembled within this method so that any changes in mutable
+// values are taken into account.
+func (tr *TypeRepr) String() string {
+ if len(tr.Repr) == 0 {
+ return ""
+ }
+ if len(tr.FormatArgs) == 0 {
+ return tr.Repr
+ }
+ return fmt.Sprintf(tr.Repr, tr.FormatArgs...)
+}
+
+// Empty reports whether the result of String would be "".
+func (tr *TypeRepr) Empty() bool {
+ return len(tr.Repr) == 0
+}
+
+// Set modifies the type representation.
+// If fargs are provided, repr is used as a format for fmt.Sprintf.
+// Otherwise, repr is used unprocessed as the type representation.
+func (tr *TypeRepr) Set(repr string, fargs ...interface{}) {
+ tr.Repr = repr
+ tr.FormatArgs = fargs
+}
+
+// FinishType completes any outstanding type mapping work.
+// In particular, it resolves incomplete pointer types.
+func (c *typeConv) FinishType(pos token.Pos) {
+ // Completing one pointer type might produce more to complete.
+ // Keep looping until they're all done.
+ for len(c.ptrKeys) > 0 {
+ dtype := c.ptrKeys[0]
+ dtypeKey := dtype.String()
+ c.ptrKeys = c.ptrKeys[1:]
+ ptrs := c.ptrs[dtypeKey]
+ delete(c.ptrs, dtypeKey)
+
+ // Note Type might invalidate c.ptrs[dtypeKey].
+ t := c.Type(dtype, pos)
+ for _, ptr := range ptrs {
+ ptr.Go.(*ast.StarExpr).X = t.Go
+ ptr.C.Set("%s*", t.C)
+ }
+ }
+}
+
+// Type returns a *Type with the same memory layout as
+// dtype when used as the type of a variable or a struct field.
+func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
+ return c.loadType(dtype, pos, "")
+}
+
+// loadType recursively loads the requested dtype and its dependency graph.
+func (c *typeConv) loadType(dtype dwarf.Type, pos token.Pos, parent string) *Type {
+ // Always recompute bad pointer typedefs, as the set of such
+ // typedefs changes as we see more types.
+ checkCache := true
+ if dtt, ok := dtype.(*dwarf.TypedefType); ok && c.badPointerTypedef(dtt) {
+ checkCache = false
+ }
+
+ // The cache key should be relative to its parent.
+ // See issue https://golang.org/issue/31891
+ key := parent + " > " + dtype.String()
+
+ if checkCache {
+ if t, ok := c.m[key]; ok {
+ if t.Go == nil {
+ fatalf("%s: type conversion loop at %s", lineno(pos), dtype)
+ }
+ return t
+ }
+ }
+
+ t := new(Type)
+ t.Size = dtype.Size() // note: wrong for array of pointers, corrected below
+ t.Align = -1
+ t.C = &TypeRepr{Repr: dtype.Common().Name}
+ c.m[key] = t
+
+ switch dt := dtype.(type) {
+ default:
+ fatalf("%s: unexpected type: %s", lineno(pos), dtype)
+
+ case *dwarf.AddrType:
+ if t.Size != c.ptrSize {
+ fatalf("%s: unexpected: %d-byte address type - %s", lineno(pos), t.Size, dtype)
+ }
+ t.Go = c.uintptr
+ t.Align = t.Size
+
+ case *dwarf.ArrayType:
+ if dt.StrideBitSize > 0 {
+ // Cannot represent bit-sized elements in Go.
+ t.Go = c.Opaque(t.Size)
+ break
+ }
+ count := dt.Count
+ if count == -1 {
+ // Indicates flexible array member, which Go doesn't support.
+ // Translate to zero-length array instead.
+ count = 0
+ }
+ sub := c.Type(dt.Type, pos)
+ t.Align = sub.Align
+ t.Go = &ast.ArrayType{
+ Len: c.intExpr(count),
+ Elt: sub.Go,
+ }
+ // Recalculate t.Size now that we know sub.Size.
+ t.Size = count * sub.Size
+ t.C.Set("__typeof__(%s[%d])", sub.C, dt.Count)
+
+ case *dwarf.BoolType:
+ t.Go = c.bool
+ t.Align = 1
+
+ case *dwarf.CharType:
+ if t.Size != 1 {
+ fatalf("%s: unexpected: %d-byte char type - %s", lineno(pos), t.Size, dtype)
+ }
+ t.Go = c.int8
+ t.Align = 1
+
+ case *dwarf.EnumType:
+ if t.Align = t.Size; t.Align >= c.ptrSize {
+ t.Align = c.ptrSize
+ }
+ t.C.Set("enum " + dt.EnumName)
+ signed := 0
+ t.EnumValues = make(map[string]int64)
+ for _, ev := range dt.Val {
+ t.EnumValues[ev.Name] = ev.Val
+ if ev.Val < 0 {
+ signed = signedDelta
+ }
+ }
+ switch t.Size + int64(signed) {
+ default:
+ fatalf("%s: unexpected: %d-byte enum type - %s", lineno(pos), t.Size, dtype)
+ case 1:
+ t.Go = c.uint8
+ case 2:
+ t.Go = c.uint16
+ case 4:
+ t.Go = c.uint32
+ case 8:
+ t.Go = c.uint64
+ case 1 + signedDelta:
+ t.Go = c.int8
+ case 2 + signedDelta:
+ t.Go = c.int16
+ case 4 + signedDelta:
+ t.Go = c.int32
+ case 8 + signedDelta:
+ t.Go = c.int64
+ }
+
+ case *dwarf.FloatType:
+ switch t.Size {
+ default:
+ fatalf("%s: unexpected: %d-byte float type - %s", lineno(pos), t.Size, dtype)
+ case 4:
+ t.Go = c.float32
+ case 8:
+ t.Go = c.float64
+ }
+ if t.Align = t.Size; t.Align >= c.ptrSize {
+ t.Align = c.ptrSize
+ }
+
+ case *dwarf.ComplexType:
+ switch t.Size {
+ default:
+ fatalf("%s: unexpected: %d-byte complex type - %s", lineno(pos), t.Size, dtype)
+ case 8:
+ t.Go = c.complex64
+ case 16:
+ t.Go = c.complex128
+ }
+ if t.Align = t.Size / 2; t.Align >= c.ptrSize {
+ t.Align = c.ptrSize
+ }
+
+ case *dwarf.FuncType:
+ // No attempt at translation: would enable calls
+ // directly between worlds, but we need to moderate those.
+ t.Go = c.uintptr
+ t.Align = c.ptrSize
+
+ case *dwarf.IntType:
+ if dt.BitSize > 0 {
+ fatalf("%s: unexpected: %d-bit int type - %s", lineno(pos), dt.BitSize, dtype)
+ }
+ switch t.Size {
+ default:
+ fatalf("%s: unexpected: %d-byte int type - %s", lineno(pos), t.Size, dtype)
+ case 1:
+ t.Go = c.int8
+ case 2:
+ t.Go = c.int16
+ case 4:
+ t.Go = c.int32
+ case 8:
+ t.Go = c.int64
+ case 16:
+ t.Go = &ast.ArrayType{
+ Len: c.intExpr(t.Size),
+ Elt: c.uint8,
+ }
+ }
+ if t.Align = t.Size; t.Align >= c.ptrSize {
+ t.Align = c.ptrSize
+ }
+
+ case *dwarf.PtrType:
+ // Clang doesn't emit DW_AT_byte_size for pointer types.
+ if t.Size != c.ptrSize && t.Size != -1 {
+ fatalf("%s: unexpected: %d-byte pointer type - %s", lineno(pos), t.Size, dtype)
+ }
+ t.Size = c.ptrSize
+ t.Align = c.ptrSize
+
+ if _, ok := base(dt.Type).(*dwarf.VoidType); ok {
+ t.Go = c.goVoidPtr
+ t.C.Set("void*")
+ dq := dt.Type
+ for {
+ if d, ok := dq.(*dwarf.QualType); ok {
+ t.C.Set(d.Qual + " " + t.C.String())
+ dq = d.Type
+ } else {
+ break
+ }
+ }
+ break
+ }
+
+ // Placeholder initialization; completed in FinishType.
+ t.Go = &ast.StarExpr{}
+ t.C.Set("<incomplete>*")
+ key := dt.Type.String()
+ if _, ok := c.ptrs[key]; !ok {
+ c.ptrKeys = append(c.ptrKeys, dt.Type)
+ }
+ c.ptrs[key] = append(c.ptrs[key], t)
+
+ case *dwarf.QualType:
+ t1 := c.Type(dt.Type, pos)
+ t.Size = t1.Size
+ t.Align = t1.Align
+ t.Go = t1.Go
+ if unionWithPointer[t1.Go] {
+ unionWithPointer[t.Go] = true
+ }
+ t.EnumValues = nil
+ t.Typedef = ""
+ t.C.Set("%s "+dt.Qual, t1.C)
+ return t
+
+ case *dwarf.StructType:
+ // Convert to Go struct, being careful about alignment.
+ // Have to give it a name to simulate C "struct foo" references.
+ tag := dt.StructName
+ if dt.ByteSize < 0 && tag == "" { // opaque unnamed struct - should not be possible
+ break
+ }
+ if tag == "" {
+ tag = anonymousStructTag[dt]
+ if tag == "" {
+ tag = "__" + strconv.Itoa(tagGen)
+ tagGen++
+ anonymousStructTag[dt] = tag
+ }
+ } else if t.C.Empty() {
+ t.C.Set(dt.Kind + " " + tag)
+ }
+ name := c.Ident("_Ctype_" + dt.Kind + "_" + tag)
+ t.Go = name // publish before recursive calls
+ goIdent[name.Name] = name
+ if dt.ByteSize < 0 {
+ // Don't override old type
+ if _, ok := typedef[name.Name]; ok {
+ break
+ }
+
+ // Size calculation in c.Struct/c.Opaque will die with size=-1 (unknown),
+ // so execute the basic things that the struct case would do
+ // other than try to determine a Go representation.
+ tt := *t
+ tt.C = &TypeRepr{"%s %s", []interface{}{dt.Kind, tag}}
+ // We don't know what the representation of this struct is, so don't let
+ // anyone allocate one on the Go side. As a side effect of this annotation,
+ // pointers to this type will not be considered pointers in Go. They won't
+ // get writebarrier-ed or adjusted during a stack copy. This should handle
+ // all the cases badPointerTypedef used to handle, but hopefully will
+ // continue to work going forward without any more need for cgo changes.
+ tt.Go = c.Ident(incomplete)
+ typedef[name.Name] = &tt
+ break
+ }
+ switch dt.Kind {
+ case "class", "union":
+ t.Go = c.Opaque(t.Size)
+ if c.dwarfHasPointer(dt, pos) {
+ unionWithPointer[t.Go] = true
+ }
+ if t.C.Empty() {
+ t.C.Set("__typeof__(unsigned char[%d])", t.Size)
+ }
+ t.Align = 1 // TODO: should probably base this on field alignment.
+ typedef[name.Name] = t
+ case "struct":
+ g, csyntax, align := c.Struct(dt, pos)
+ if t.C.Empty() {
+ t.C.Set(csyntax)
+ }
+ t.Align = align
+ tt := *t
+ if tag != "" {
+ tt.C = &TypeRepr{"struct %s", []interface{}{tag}}
+ }
+ tt.Go = g
+ if c.incompleteStructs[tag] {
+ tt.Go = c.Ident(incomplete)
+ }
+ typedef[name.Name] = &tt
+ }
+
+ case *dwarf.TypedefType:
+ // Record typedef for printing.
+ if dt.Name == "_GoString_" {
+ // Special C name for Go string type.
+ // Knows string layout used by compilers: pointer plus length,
+ // which rounds up to 2 pointers after alignment.
+ t.Go = c.string
+ t.Size = c.ptrSize * 2
+ t.Align = c.ptrSize
+ break
+ }
+ if dt.Name == "_GoBytes_" {
+ // Special C name for Go []byte type.
+ // Knows slice layout used by compilers: pointer, length, cap.
+ t.Go = c.Ident("[]byte")
+ t.Size = c.ptrSize + 4 + 4
+ t.Align = c.ptrSize
+ break
+ }
+ name := c.Ident("_Ctype_" + dt.Name)
+ goIdent[name.Name] = name
+ akey := ""
+ if c.anonymousStructTypedef(dt) {
+ // only load type recursively for typedefs of anonymous
+ // structs, see issues 37479 and 37621.
+ akey = key
+ }
+ sub := c.loadType(dt.Type, pos, akey)
+ if c.badPointerTypedef(dt) {
+ // Treat this typedef as a uintptr.
+ s := *sub
+ s.Go = c.uintptr
+ s.BadPointer = true
+ sub = &s
+ // Make sure we update any previously computed type.
+ if oldType := typedef[name.Name]; oldType != nil {
+ oldType.Go = sub.Go
+ oldType.BadPointer = true
+ }
+ }
+ if c.badVoidPointerTypedef(dt) {
+ // Treat this typedef as a pointer to a _cgopackage.Incomplete.
+ s := *sub
+ s.Go = c.Ident("*" + incomplete)
+ sub = &s
+ // Make sure we update any previously computed type.
+ if oldType := typedef[name.Name]; oldType != nil {
+ oldType.Go = sub.Go
+ }
+ }
+ // Check for non-pointer "struct <tag>{...}; typedef struct <tag> *<name>"
+ // typedefs that should be marked Incomplete.
+ if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
+ if strct, ok := ptr.Type.(*dwarf.StructType); ok {
+ if c.badStructPointerTypedef(dt.Name, strct) {
+ c.incompleteStructs[strct.StructName] = true
+ // Make sure we update any previously computed type.
+ name := "_Ctype_struct_" + strct.StructName
+ if oldType := typedef[name]; oldType != nil {
+ oldType.Go = c.Ident(incomplete)
+ }
+ }
+ }
+ }
+ t.Go = name
+ t.BadPointer = sub.BadPointer
+ if unionWithPointer[sub.Go] {
+ unionWithPointer[t.Go] = true
+ }
+ t.Size = sub.Size
+ t.Align = sub.Align
+ oldType := typedef[name.Name]
+ if oldType == nil {
+ tt := *t
+ tt.Go = sub.Go
+ tt.BadPointer = sub.BadPointer
+ typedef[name.Name] = &tt
+ }
+
+ // If sub.Go.Name is "_Ctype_struct_foo" or "_Ctype_union_foo" or "_Ctype_class_foo",
+ // use that as the Go form for this typedef too, so that the typedef will be interchangeable
+ // with the base type.
+ // In -godefs mode, do this for all typedefs.
+ if isStructUnionClass(sub.Go) || *godefs {
+ t.Go = sub.Go
+
+ if isStructUnionClass(sub.Go) {
+ // Use the typedef name for C code.
+ typedef[sub.Go.(*ast.Ident).Name].C = t.C
+ }
+
+ // If we've seen this typedef before, and it
+ // was an anonymous struct/union/class before
+ // too, use the old definition.
+ // TODO: it would be safer to only do this if
+ // we verify that the types are the same.
+ if oldType != nil && isStructUnionClass(oldType.Go) {
+ t.Go = oldType.Go
+ }
+ }
+
+ case *dwarf.UcharType:
+ if t.Size != 1 {
+ fatalf("%s: unexpected: %d-byte uchar type - %s", lineno(pos), t.Size, dtype)
+ }
+ t.Go = c.uint8
+ t.Align = 1
+
+ case *dwarf.UintType:
+ if dt.BitSize > 0 {
+ fatalf("%s: unexpected: %d-bit uint type - %s", lineno(pos), dt.BitSize, dtype)
+ }
+ switch t.Size {
+ default:
+ fatalf("%s: unexpected: %d-byte uint type - %s", lineno(pos), t.Size, dtype)
+ case 1:
+ t.Go = c.uint8
+ case 2:
+ t.Go = c.uint16
+ case 4:
+ t.Go = c.uint32
+ case 8:
+ t.Go = c.uint64
+ case 16:
+ t.Go = &ast.ArrayType{
+ Len: c.intExpr(t.Size),
+ Elt: c.uint8,
+ }
+ }
+ if t.Align = t.Size; t.Align >= c.ptrSize {
+ t.Align = c.ptrSize
+ }
+
+ case *dwarf.VoidType:
+ t.Go = c.goVoid
+ t.C.Set("void")
+ t.Align = 1
+ }
+
+ switch dtype.(type) {
+ case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.ComplexType, *dwarf.IntType, *dwarf.FloatType, *dwarf.UcharType, *dwarf.UintType:
+ s := dtype.Common().Name
+ if s != "" {
+ if ss, ok := dwarfToName[s]; ok {
+ s = ss
+ }
+ s = strings.Replace(s, " ", "", -1)
+ name := c.Ident("_Ctype_" + s)
+ tt := *t
+ typedef[name.Name] = &tt
+ if !*godefs {
+ t.Go = name
+ }
+ }
+ }
+
+ if t.Size < 0 {
+ // Unsized types are [0]byte, unless they're typedefs of other types
+ // or structs with tags.
+ // if so, use the name we've already defined.
+ t.Size = 0
+ switch dt := dtype.(type) {
+ case *dwarf.TypedefType:
+ // ok
+ case *dwarf.StructType:
+ if dt.StructName != "" {
+ break
+ }
+ t.Go = c.Opaque(0)
+ default:
+ t.Go = c.Opaque(0)
+ }
+ if t.C.Empty() {
+ t.C.Set("void")
+ }
+ }
+
+ if t.C.Empty() {
+ fatalf("%s: internal error: did not create C name for %s", lineno(pos), dtype)
+ }
+
+ return t
+}
+
+// isStructUnionClass reports whether the type described by the Go syntax x
+// is a struct, union, or class with a tag.
+func isStructUnionClass(x ast.Expr) bool {
+ id, ok := x.(*ast.Ident)
+ if !ok {
+ return false
+ }
+ name := id.Name
+ return strings.HasPrefix(name, "_Ctype_struct_") ||
+ strings.HasPrefix(name, "_Ctype_union_") ||
+ strings.HasPrefix(name, "_Ctype_class_")
+}
+
+// FuncArg returns a Go type with the same memory layout as
+// dtype when used as the type of a C function argument.
+func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
+ t := c.Type(unqual(dtype), pos)
+ switch dt := dtype.(type) {
+ case *dwarf.ArrayType:
+ // Arrays are passed implicitly as pointers in C.
+ // In Go, we must be explicit.
+ tr := &TypeRepr{}
+ tr.Set("%s*", t.C)
+ return &Type{
+ Size: c.ptrSize,
+ Align: c.ptrSize,
+ Go: &ast.StarExpr{X: t.Go},
+ C: tr,
+ }
+ case *dwarf.TypedefType:
+ // C has much more relaxed rules than Go for
+ // implicit type conversions. When the parameter
+ // is type T defined as *X, simulate a little of the
+ // laxness of C by making the argument *X instead of T.
+ if ptr, ok := base(dt.Type).(*dwarf.PtrType); ok {
+ // Unless the typedef happens to point to void* since
+ // Go has special rules around using unsafe.Pointer.
+ if _, void := base(ptr.Type).(*dwarf.VoidType); void {
+ break
+ }
+ // ...or the typedef is one in which we expect bad pointers.
+ // It will be a uintptr instead of *X.
+ if c.baseBadPointerTypedef(dt) {
+ break
+ }
+
+ t = c.Type(ptr, pos)
+ if t == nil {
+ return nil
+ }
+
+ // For a struct/union/class, remember the C spelling,
+ // in case it has __attribute__((unavailable)).
+ // See issue 2888.
+ if isStructUnionClass(t.Go) {
+ t.Typedef = dt.Name
+ }
+ }
+ }
+ return t
+}
+
+// FuncType returns the Go type analogous to dtype.
+// There is no guarantee about matching memory layout.
+func (c *typeConv) FuncType(dtype *dwarf.FuncType, pos token.Pos) *FuncType {
+ p := make([]*Type, len(dtype.ParamType))
+ gp := make([]*ast.Field, len(dtype.ParamType))
+ for i, f := range dtype.ParamType {
+ // gcc's DWARF generator outputs a single DotDotDotType parameter for
+ // function pointers that specify no parameters (e.g. void
+ // (*__cgo_0)()). Treat this special case as void. This case is
+ // invalid according to ISO C anyway (i.e. void (*__cgo_1)(...) is not
+ // legal).
+ if _, ok := f.(*dwarf.DotDotDotType); ok && i == 0 {
+ p, gp = nil, nil
+ break
+ }
+ p[i] = c.FuncArg(f, pos)
+ gp[i] = &ast.Field{Type: p[i].Go}
+ }
+ var r *Type
+ var gr []*ast.Field
+ if _, ok := base(dtype.ReturnType).(*dwarf.VoidType); ok {
+ gr = []*ast.Field{{Type: c.goVoid}}
+ } else if dtype.ReturnType != nil {
+ r = c.Type(unqual(dtype.ReturnType), pos)
+ gr = []*ast.Field{{Type: r.Go}}
+ }
+ return &FuncType{
+ Params: p,
+ Result: r,
+ Go: &ast.FuncType{
+ Params: &ast.FieldList{List: gp},
+ Results: &ast.FieldList{List: gr},
+ },
+ }
+}
+
+// Identifier
+func (c *typeConv) Ident(s string) *ast.Ident {
+ return ast.NewIdent(s)
+}
+
+// Opaque type of n bytes.
+func (c *typeConv) Opaque(n int64) ast.Expr {
+ return &ast.ArrayType{
+ Len: c.intExpr(n),
+ Elt: c.byte,
+ }
+}
+
+// Expr for integer n.
+func (c *typeConv) intExpr(n int64) ast.Expr {
+ return &ast.BasicLit{
+ Kind: token.INT,
+ Value: strconv.FormatInt(n, 10),
+ }
+}
+
+// Add padding of given size to fld.
+func (c *typeConv) pad(fld []*ast.Field, sizes []int64, size int64) ([]*ast.Field, []int64) {
+ n := len(fld)
+ fld = fld[0 : n+1]
+ fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident("_")}, Type: c.Opaque(size)}
+ sizes = sizes[0 : n+1]
+ sizes[n] = size
+ return fld, sizes
+}
+
+// Struct conversion: return Go and (gc) C syntax for type.
+func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) {
+ // Minimum alignment for a struct is 1 byte.
+ align = 1
+
+ var buf strings.Builder
+ buf.WriteString("struct {")
+ fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field
+ sizes := make([]int64, 0, 2*len(dt.Field)+1)
+ off := int64(0)
+
+ // Rename struct fields that happen to be named Go keywords into
+ // _{keyword}. Create a map from C ident -> Go ident. The Go ident will
+ // be mangled. Any existing identifier that already has the same name on
+ // the C-side will cause the Go-mangled version to be prefixed with _.
+ // (e.g. in a struct with fields '_type' and 'type', the latter would be
+ // rendered as '__type' in Go).
+ ident := make(map[string]string)
+ used := make(map[string]bool)
+ for _, f := range dt.Field {
+ ident[f.Name] = f.Name
+ used[f.Name] = true
+ }
+
+ if !*godefs {
+ for cid, goid := range ident {
+ if token.Lookup(goid).IsKeyword() {
+ // Avoid keyword
+ goid = "_" + goid
+
+ // Also avoid existing fields
+ for _, exist := used[goid]; exist; _, exist = used[goid] {
+ goid = "_" + goid
+ }
+
+ used[goid] = true
+ ident[cid] = goid
+ }
+ }
+ }
+
+ anon := 0
+ for _, f := range dt.Field {
+ name := f.Name
+ ft := f.Type
+
+ // In godefs mode, if this field is a C11
+ // anonymous union then treat the first field in the
+ // union as the field in the struct. This handles
+ // cases like the glibc <sys/resource.h> file; see
+ // issue 6677.
+ if *godefs {
+ if st, ok := f.Type.(*dwarf.StructType); ok && name == "" && st.Kind == "union" && len(st.Field) > 0 && !used[st.Field[0].Name] {
+ name = st.Field[0].Name
+ ident[name] = name
+ ft = st.Field[0].Type
+ }
+ }
+
+ // TODO: Handle fields that are anonymous structs by
+ // promoting the fields of the inner struct.
+
+ t := c.Type(ft, pos)
+ tgo := t.Go
+ size := t.Size
+ talign := t.Align
+ if f.BitOffset > 0 || f.BitSize > 0 {
+ // The layout of bitfields is implementation defined,
+ // so we don't know how they correspond to Go fields
+ // even if they are aligned at byte boundaries.
+ continue
+ }
+
+ if talign > 0 && f.ByteOffset%talign != 0 {
+ // Drop misaligned fields, the same way we drop integer bit fields.
+ // The goal is to make available what can be made available.
+ // Otherwise one bad and unneeded field in an otherwise okay struct
+ // makes the whole program not compile. Much of the time these
+ // structs are in system headers that cannot be corrected.
+ continue
+ }
+
+ // Round off up to talign, assumed to be a power of 2.
+ off = (off + talign - 1) &^ (talign - 1)
+
+ if f.ByteOffset > off {
+ fld, sizes = c.pad(fld, sizes, f.ByteOffset-off)
+ off = f.ByteOffset
+ }
+ if f.ByteOffset < off {
+ // Drop a packed field that we can't represent.
+ continue
+ }
+
+ n := len(fld)
+ fld = fld[0 : n+1]
+ if name == "" {
+ name = fmt.Sprintf("anon%d", anon)
+ anon++
+ ident[name] = name
+ }
+ fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[name])}, Type: tgo}
+ sizes = sizes[0 : n+1]
+ sizes[n] = size
+ off += size
+ buf.WriteString(t.C.String())
+ buf.WriteString(" ")
+ buf.WriteString(name)
+ buf.WriteString("; ")
+ if talign > align {
+ align = talign
+ }
+ }
+ if off < dt.ByteSize {
+ fld, sizes = c.pad(fld, sizes, dt.ByteSize-off)
+ off = dt.ByteSize
+ }
+
+ // If the last field in a non-zero-sized struct is zero-sized
+ // the compiler is going to pad it by one (see issue 9401).
+ // We can't permit that, because then the size of the Go
+ // struct will not be the same as the size of the C struct.
+ // Our only option in such a case is to remove the field,
+ // which means that it cannot be referenced from Go.
+ for off > 0 && sizes[len(sizes)-1] == 0 {
+ n := len(sizes)
+ fld = fld[0 : n-1]
+ sizes = sizes[0 : n-1]
+ }
+
+ if off != dt.ByteSize {
+ fatalf("%s: struct size calculation error off=%d bytesize=%d", lineno(pos), off, dt.ByteSize)
+ }
+ buf.WriteString("}")
+ csyntax = buf.String()
+
+ if *godefs {
+ godefsFields(fld)
+ }
+ expr = &ast.StructType{Fields: &ast.FieldList{List: fld}}
+ return
+}
+
+// dwarfHasPointer reports whether the DWARF type dt contains a pointer.
+func (c *typeConv) dwarfHasPointer(dt dwarf.Type, pos token.Pos) bool {
+ switch dt := dt.(type) {
+ default:
+ fatalf("%s: unexpected type: %s", lineno(pos), dt)
+ return false
+
+ case *dwarf.AddrType, *dwarf.BoolType, *dwarf.CharType, *dwarf.EnumType,
+ *dwarf.FloatType, *dwarf.ComplexType, *dwarf.FuncType,
+ *dwarf.IntType, *dwarf.UcharType, *dwarf.UintType, *dwarf.VoidType:
+
+ return false
+
+ case *dwarf.ArrayType:
+ return c.dwarfHasPointer(dt.Type, pos)
+
+ case *dwarf.PtrType:
+ return true
+
+ case *dwarf.QualType:
+ return c.dwarfHasPointer(dt.Type, pos)
+
+ case *dwarf.StructType:
+ for _, f := range dt.Field {
+ if c.dwarfHasPointer(f.Type, pos) {
+ return true
+ }
+ }
+ return false
+
+ case *dwarf.TypedefType:
+ if dt.Name == "_GoString_" || dt.Name == "_GoBytes_" {
+ return true
+ }
+ return c.dwarfHasPointer(dt.Type, pos)
+ }
+}
+
+func upper(s string) string {
+ if s == "" {
+ return ""
+ }
+ r, size := utf8.DecodeRuneInString(s)
+ if r == '_' {
+ return "X" + s
+ }
+ return string(unicode.ToUpper(r)) + s[size:]
+}
+
+// godefsFields rewrites field names for use in Go or C definitions.
+// It strips leading common prefixes (like tv_ in tv_sec, tv_usec)
+// converts names to upper case, and rewrites _ into Pad_godefs_n,
+// so that all fields are exported.
+func godefsFields(fld []*ast.Field) {
+ prefix := fieldPrefix(fld)
+
+ // Issue 48396: check for duplicate field names.
+ if prefix != "" {
+ names := make(map[string]bool)
+ fldLoop:
+ for _, f := range fld {
+ for _, n := range f.Names {
+ name := n.Name
+ if name == "_" {
+ continue
+ }
+ if name != prefix {
+ name = strings.TrimPrefix(n.Name, prefix)
+ }
+ name = upper(name)
+ if names[name] {
+ // Field name conflict: don't remove prefix.
+ prefix = ""
+ break fldLoop
+ }
+ names[name] = true
+ }
+ }
+ }
+
+ npad := 0
+ for _, f := range fld {
+ for _, n := range f.Names {
+ if n.Name != prefix {
+ n.Name = strings.TrimPrefix(n.Name, prefix)
+ }
+ if n.Name == "_" {
+ // Use exported name instead.
+ n.Name = "Pad_cgo_" + strconv.Itoa(npad)
+ npad++
+ }
+ n.Name = upper(n.Name)
+ }
+ }
+}
+
+// fieldPrefix returns the prefix that should be removed from all the
+// field names when generating the C or Go code. For generated
+// C, we leave the names as is (tv_sec, tv_usec), since that's what
+// people are used to seeing in C. For generated Go code, such as
+// package syscall's data structures, we drop a common prefix
+// (so sec, usec, which will get turned into Sec, Usec for exporting).
+func fieldPrefix(fld []*ast.Field) string {
+ prefix := ""
+ for _, f := range fld {
+ for _, n := range f.Names {
+ // Ignore field names that don't have the prefix we're
+ // looking for. It is common in C headers to have fields
+ // named, say, _pad in an otherwise prefixed header.
+ // If the struct has 3 fields tv_sec, tv_usec, _pad1, then we
+ // still want to remove the tv_ prefix.
+ // The check for "orig_" here handles orig_eax in the
+ // x86 ptrace register sets, which otherwise have all fields
+ // with reg_ prefixes.
+ if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") {
+ continue
+ }
+ i := strings.Index(n.Name, "_")
+ if i < 0 {
+ continue
+ }
+ if prefix == "" {
+ prefix = n.Name[:i+1]
+ } else if prefix != n.Name[:i+1] {
+ return ""
+ }
+ }
+ }
+ return prefix
+}
+
+// anonymousStructTypedef reports whether dt is a C typedef for an anonymous
+// struct.
+func (c *typeConv) anonymousStructTypedef(dt *dwarf.TypedefType) bool {
+ st, ok := dt.Type.(*dwarf.StructType)
+ return ok && st.StructName == ""
+}
+
+// badPointerTypedef reports whether dt is a C typedef that should not be
+// considered a pointer in Go. A typedef is bad if C code sometimes stores
+// non-pointers in this type.
+// TODO: Currently our best solution is to find these manually and list them as
+// they come up. A better solution is desired.
+// Note: DEPRECATED. There is now a better solution. Search for incomplete in this file.
+func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
+ if c.badCFType(dt) {
+ return true
+ }
+ if c.badJNI(dt) {
+ return true
+ }
+ if c.badEGLType(dt) {
+ return true
+ }
+ return false
+}
+
+// badVoidPointerTypedef is like badPointerTypeDef, but for "void *" typedefs that should be _cgopackage.Incomplete.
+func (c *typeConv) badVoidPointerTypedef(dt *dwarf.TypedefType) bool {
+ // Match the Windows HANDLE type (#42018).
+ if goos != "windows" || dt.Name != "HANDLE" {
+ return false
+ }
+ // Check that the typedef is "typedef void *<name>".
+ if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
+ if _, ok := ptr.Type.(*dwarf.VoidType); ok {
+ return true
+ }
+ }
+ return false
+}
+
+// badStructPointerTypedef is like badVoidPointerTypedef but for structs.
+func (c *typeConv) badStructPointerTypedef(name string, dt *dwarf.StructType) bool {
+ // Windows handle types can all potentially contain non-pointers.
+ // badVoidPointerTypedef handles the "void *" HANDLE type, but other
+ // handles are defined as
+ //
+ // struct <name>__{int unused;}; typedef struct <name>__ *name;
+ //
+ // by the DECLARE_HANDLE macro in STRICT mode. The macro is declared in
+ // the Windows ntdef.h header,
+ //
+ // https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/shared/ntdef.h#L779
+ if goos != "windows" {
+ return false
+ }
+ if len(dt.Field) != 1 {
+ return false
+ }
+ if dt.StructName != name+"__" {
+ return false
+ }
+ if f := dt.Field[0]; f.Name != "unused" || f.Type.Common().Name != "int" {
+ return false
+ }
+ return true
+}
+
+// baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef
+// as badPointerTypedef reports.
+func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool {
+ for {
+ if t, ok := dt.Type.(*dwarf.TypedefType); ok {
+ dt = t
+ continue
+ }
+ break
+ }
+ return c.badPointerTypedef(dt)
+}
+
+func (c *typeConv) badCFType(dt *dwarf.TypedefType) bool {
+ // The real bad types are CFNumberRef and CFDateRef.
+ // Sometimes non-pointers are stored in these types.
+ // CFTypeRef is a supertype of those, so it can have bad pointers in it as well.
+ // We return true for the other *Ref types just so casting between them is easier.
+ // We identify the correct set of types as those ending in Ref and for which
+ // there exists a corresponding GetTypeID function.
+ // See comment below for details about the bad pointers.
+ if goos != "darwin" && goos != "ios" {
+ return false
+ }
+ s := dt.Name
+ if !strings.HasSuffix(s, "Ref") {
+ return false
+ }
+ s = s[:len(s)-3]
+ if s == "CFType" {
+ return true
+ }
+ if c.getTypeIDs[s] {
+ return true
+ }
+ if i := strings.Index(s, "Mutable"); i >= 0 && c.getTypeIDs[s[:i]+s[i+7:]] {
+ // Mutable and immutable variants share a type ID.
+ return true
+ }
+ return false
+}
+
+// Comment from Darwin's CFInternal.h
+/*
+// Tagged pointer support
+// Low-bit set means tagged object, next 3 bits (currently)
+// define the tagged object class, next 4 bits are for type
+// information for the specific tagged object class. Thus,
+// the low byte is for type info, and the rest of a pointer
+// (32 or 64-bit) is for payload, whatever the tagged class.
+//
+// Note that the specific integers used to identify the
+// specific tagged classes can and will change from release
+// to release (that's why this stuff is in CF*Internal*.h),
+// as can the definition of type info vs payload above.
+//
+#if __LP64__
+#define CF_IS_TAGGED_OBJ(PTR) ((uintptr_t)(PTR) & 0x1)
+#define CF_TAGGED_OBJ_TYPE(PTR) ((uintptr_t)(PTR) & 0xF)
+#else
+#define CF_IS_TAGGED_OBJ(PTR) 0
+#define CF_TAGGED_OBJ_TYPE(PTR) 0
+#endif
+
+enum {
+ kCFTaggedObjectID_Invalid = 0,
+ kCFTaggedObjectID_Atom = (0 << 1) + 1,
+ kCFTaggedObjectID_Undefined3 = (1 << 1) + 1,
+ kCFTaggedObjectID_Undefined2 = (2 << 1) + 1,
+ kCFTaggedObjectID_Integer = (3 << 1) + 1,
+ kCFTaggedObjectID_DateTS = (4 << 1) + 1,
+ kCFTaggedObjectID_ManagedObjectID = (5 << 1) + 1, // Core Data
+ kCFTaggedObjectID_Date = (6 << 1) + 1,
+ kCFTaggedObjectID_Undefined7 = (7 << 1) + 1,
+};
+*/
+
+func (c *typeConv) badJNI(dt *dwarf.TypedefType) bool {
+ // In Dalvik and ART, the jobject type in the JNI interface of the JVM has the
+ // property that it is sometimes (always?) a small integer instead of a real pointer.
+ // Note: although only the android JVMs are bad in this respect, we declare the JNI types
+ // bad regardless of platform, so the same Go code compiles on both android and non-android.
+ if parent, ok := jniTypes[dt.Name]; ok {
+ // Try to make sure we're talking about a JNI type, not just some random user's
+ // type that happens to use the same name.
+ // C doesn't have the notion of a package, so it's hard to be certain.
+
+ // Walk up to jobject, checking each typedef on the way.
+ w := dt
+ for parent != "" {
+ t, ok := w.Type.(*dwarf.TypedefType)
+ if !ok || t.Name != parent {
+ return false
+ }
+ w = t
+ parent, ok = jniTypes[w.Name]
+ if !ok {
+ return false
+ }
+ }
+
+ // Check that the typedef is either:
+ // 1:
+ // struct _jobject;
+ // typedef struct _jobject *jobject;
+ // 2: (in NDK16 in C++)
+ // class _jobject {};
+ // typedef _jobject* jobject;
+ // 3: (in NDK16 in C)
+ // typedef void* jobject;
+ if ptr, ok := w.Type.(*dwarf.PtrType); ok {
+ switch v := ptr.Type.(type) {
+ case *dwarf.VoidType:
+ return true
+ case *dwarf.StructType:
+ if v.StructName == "_jobject" && len(v.Field) == 0 {
+ switch v.Kind {
+ case "struct":
+ if v.Incomplete {
+ return true
+ }
+ case "class":
+ if !v.Incomplete {
+ return true
+ }
+ }
+ }
+ }
+ }
+ }
+ return false
+}
+
+func (c *typeConv) badEGLType(dt *dwarf.TypedefType) bool {
+ if dt.Name != "EGLDisplay" && dt.Name != "EGLConfig" {
+ return false
+ }
+ // Check that the typedef is "typedef void *<name>".
+ if ptr, ok := dt.Type.(*dwarf.PtrType); ok {
+ if _, ok := ptr.Type.(*dwarf.VoidType); ok {
+ return true
+ }
+ }
+ return false
+}
+
+// jniTypes maps from JNI types that we want to be uintptrs, to the underlying type to which
+// they are mapped. The base "jobject" maps to the empty string.
+var jniTypes = map[string]string{
+ "jobject": "",
+ "jclass": "jobject",
+ "jthrowable": "jobject",
+ "jstring": "jobject",
+ "jarray": "jobject",
+ "jbooleanArray": "jarray",
+ "jbyteArray": "jarray",
+ "jcharArray": "jarray",
+ "jshortArray": "jarray",
+ "jintArray": "jarray",
+ "jlongArray": "jarray",
+ "jfloatArray": "jarray",
+ "jdoubleArray": "jarray",
+ "jobjectArray": "jarray",
+ "jweak": "jobject",
+}
diff --git a/src/cmd/cgo/godefs.go b/src/cmd/cgo/godefs.go
new file mode 100644
index 0000000..f628670
--- /dev/null
+++ b/src/cmd/cgo/godefs.go
@@ -0,0 +1,170 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "go/ast"
+ "go/printer"
+ "go/token"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// godefs returns the output for -godefs mode.
+func (p *Package) godefs(f *File, args []string) string {
+ var buf strings.Builder
+
+ fmt.Fprintf(&buf, "// Code generated by cmd/cgo -godefs; DO NOT EDIT.\n")
+ fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(args[0]), strings.Join(args[1:], " "))
+ fmt.Fprintf(&buf, "\n")
+
+ override := make(map[string]string)
+
+ // Allow source file to specify override mappings.
+ // For example, the socket data structures refer
+ // to in_addr and in_addr6 structs but we want to be
+ // able to treat them as byte arrays, so the godefs
+ // inputs in package syscall say
+ //
+ // // +godefs map struct_in_addr [4]byte
+ // // +godefs map struct_in_addr6 [16]byte
+ //
+ for _, g := range f.Comments {
+ for _, c := range g.List {
+ i := strings.Index(c.Text, "+godefs map")
+ if i < 0 {
+ continue
+ }
+ s := strings.TrimSpace(c.Text[i+len("+godefs map"):])
+ i = strings.Index(s, " ")
+ if i < 0 {
+ fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text)
+ continue
+ }
+ override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:])
+ }
+ }
+ for _, n := range f.Name {
+ if s := override[n.Go]; s != "" {
+ override[n.Mangle] = s
+ }
+ }
+
+ // Otherwise, if the source file says type T C.whatever,
+ // use "T" as the mangling of C.whatever,
+ // except in the definition (handled at end of function).
+ refName := make(map[*ast.Expr]*Name)
+ for _, r := range f.Ref {
+ refName[r.Expr] = r.Name
+ }
+ for _, d := range f.AST.Decls {
+ d, ok := d.(*ast.GenDecl)
+ if !ok || d.Tok != token.TYPE {
+ continue
+ }
+ for _, s := range d.Specs {
+ s := s.(*ast.TypeSpec)
+ n := refName[&s.Type]
+ if n != nil && n.Mangle != "" {
+ override[n.Mangle] = s.Name.Name
+ }
+ }
+ }
+
+ // Extend overrides using typedefs:
+ // If we know that C.xxx should format as T
+ // and xxx is a typedef for yyy, make C.yyy format as T.
+ for typ, def := range typedef {
+ if new := override[typ]; new != "" {
+ if id, ok := def.Go.(*ast.Ident); ok {
+ override[id.Name] = new
+ }
+ }
+ }
+
+ // Apply overrides.
+ for old, new := range override {
+ if id := goIdent[old]; id != nil {
+ id.Name = new
+ }
+ }
+
+ // Any names still using the _C syntax are not going to compile,
+ // although in general we don't know whether they all made it
+ // into the file, so we can't warn here.
+ //
+ // The most common case is union types, which begin with
+ // _Ctype_union and for which typedef[name] is a Go byte
+ // array of the appropriate size (such as [4]byte).
+ // Substitute those union types with byte arrays.
+ for name, id := range goIdent {
+ if id.Name == name && strings.Contains(name, "_Ctype_union") {
+ if def := typedef[name]; def != nil {
+ id.Name = gofmt(def)
+ }
+ }
+ }
+
+ conf.Fprint(&buf, fset, f.AST)
+
+ return buf.String()
+}
+
+var gofmtBuf strings.Builder
+
+// gofmt returns the gofmt-formatted string for an AST node.
+func gofmt(n interface{}) string {
+ gofmtBuf.Reset()
+ err := printer.Fprint(&gofmtBuf, fset, n)
+ if err != nil {
+ return "<" + err.Error() + ">"
+ }
+ return gofmtBuf.String()
+}
+
+// gofmtLineReplacer is used to put a gofmt-formatted string for an
+// AST expression onto a single line. The lexer normally inserts a
+// semicolon at each newline, so we can replace newline with semicolon.
+// However, we can't do that in cases where the lexer would not insert
+// a semicolon. We only have to worry about cases that can occur in an
+// expression passed through gofmt, which means composite literals and
+// (due to the printer possibly inserting newlines because of position
+// information) operators.
+var gofmtLineReplacer = strings.NewReplacer(
+ // Want to replace \n without ; after everything from
+ // https://golang.org/ref/spec#Operators_and_punctuation
+ // EXCEPT ++ -- ) ] }
+ "++\n", "++;",
+ "--\n", "--;",
+
+ "+\n", "+ ",
+ "-\n", "- ",
+ "*\n", "* ",
+ "/\n", "/ ",
+ "%\n", "% ",
+ "&\n", "& ",
+ "|\n", "| ",
+ "^\n", "^ ",
+ "<\n", "< ",
+ ">\n", "> ",
+ "=\n", "= ",
+ "!\n", "! ", // not possible in gofmt today
+ "(\n", "(",
+ "[\n", "[", // not possible in gofmt today
+ "{\n", "{",
+ ",\n", ",",
+ ".\n", ". ",
+ ":\n", ": ", // not possible in gofmt today
+
+ "\n", ";",
+)
+
+// gofmtLine returns the gofmt-formatted string for an AST node,
+// ensuring that it is on a single line.
+func gofmtLine(n interface{}) string {
+ return gofmtLineReplacer.Replace(gofmt(n))
+}
diff --git a/src/cmd/cgo/internal/cgotest/overlaydir.go b/src/cmd/cgo/internal/cgotest/overlaydir.go
new file mode 100644
index 0000000..c6b1615
--- /dev/null
+++ b/src/cmd/cgo/internal/cgotest/overlaydir.go
@@ -0,0 +1,75 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+import (
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+// OverlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
+func OverlayDir(dstRoot, srcRoot string) error {
+ dstRoot = filepath.Clean(dstRoot)
+ if err := os.MkdirAll(dstRoot, 0777); err != nil {
+ return err
+ }
+
+ srcRoot, err := filepath.Abs(srcRoot)
+ if err != nil {
+ return err
+ }
+
+ return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error {
+ if err != nil || srcPath == srcRoot {
+ return err
+ }
+
+ suffix := strings.TrimPrefix(srcPath, srcRoot)
+ for len(suffix) > 0 && suffix[0] == filepath.Separator {
+ suffix = suffix[1:]
+ }
+ dstPath := filepath.Join(dstRoot, suffix)
+
+ perm := info.Mode() & os.ModePerm
+ if info.Mode()&os.ModeSymlink != 0 {
+ info, err = os.Stat(srcPath)
+ if err != nil {
+ return err
+ }
+ perm = info.Mode() & os.ModePerm
+ }
+
+ // Always copy directories (don't symlink them).
+ // If we add a file in the overlay, we don't want to add it in the original.
+ if info.IsDir() {
+ return os.MkdirAll(dstPath, perm|0200)
+ }
+
+ // If the OS supports symlinks, use them instead of copying bytes.
+ if err := os.Symlink(srcPath, dstPath); err == nil {
+ return nil
+ }
+
+ // Otherwise, copy the bytes.
+ src, err := os.Open(srcPath)
+ if err != nil {
+ return err
+ }
+ defer src.Close()
+
+ dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
+ if err != nil {
+ return err
+ }
+
+ _, err = io.Copy(dst, src)
+ if closeErr := dst.Close(); err == nil {
+ err = closeErr
+ }
+ return err
+ })
+}
diff --git a/src/cmd/cgo/internal/swig/swig_test.go b/src/cmd/cgo/internal/swig/swig_test.go
new file mode 100644
index 0000000..4156313
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/swig_test.go
@@ -0,0 +1,153 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package swig
+
+import (
+ "cmd/internal/quoted"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+)
+
+func TestStdio(t *testing.T) {
+ testenv.MustHaveCGO(t)
+ mustHaveSwig(t)
+ run(t, "testdata/stdio", false)
+}
+
+func TestCall(t *testing.T) {
+ testenv.MustHaveCGO(t)
+ mustHaveSwig(t)
+ mustHaveCxx(t)
+ run(t, "testdata/callback", false, "Call")
+ t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Call") })
+}
+
+func TestCallback(t *testing.T) {
+ testenv.MustHaveCGO(t)
+ mustHaveSwig(t)
+ mustHaveCxx(t)
+ run(t, "testdata/callback", false, "Callback")
+ t.Run("lto", func(t *testing.T) { run(t, "testdata/callback", true, "Callback") })
+}
+
+func run(t *testing.T, dir string, lto bool, args ...string) {
+ runArgs := append([]string{"run", "."}, args...)
+ cmd := exec.Command("go", runArgs...)
+ cmd.Dir = dir
+ if lto {
+ const cflags = "-flto -Wno-lto-type-mismatch -Wno-unknown-warning-option"
+ cmd.Env = append(cmd.Environ(),
+ "CGO_CFLAGS="+cflags,
+ "CGO_CXXFLAGS="+cflags,
+ "CGO_LDFLAGS="+cflags)
+ }
+ out, err := cmd.CombinedOutput()
+ if string(out) != "OK\n" {
+ t.Errorf("%s", string(out))
+ }
+ if err != nil {
+ t.Errorf("%s", err)
+ }
+}
+
+func mustHaveCxx(t *testing.T) {
+ // Ask the go tool for the CXX it's configured to use.
+ cxx, err := exec.Command("go", "env", "CXX").CombinedOutput()
+ if err != nil {
+ t.Fatalf("go env CXX failed: %s", err)
+ }
+ args, err := quoted.Split(string(cxx))
+ if err != nil {
+ t.Skipf("could not parse 'go env CXX' output %q: %s", string(cxx), err)
+ }
+ if len(args) == 0 {
+ t.Skip("no C++ compiler")
+ }
+ testenv.MustHaveExecPath(t, string(args[0]))
+}
+
+var (
+ swigOnce sync.Once
+ haveSwig bool
+)
+
+func mustHaveSwig(t *testing.T) {
+ swigOnce.Do(func() {
+ mustHaveSwigOnce(t)
+ haveSwig = true
+ })
+ // The first call will skip t with a nice message. On later calls, we just skip.
+ if !haveSwig {
+ t.Skip("swig not found")
+ }
+}
+
+func mustHaveSwigOnce(t *testing.T) {
+ swig, err := exec.LookPath("swig")
+ if err != nil {
+ t.Skipf("swig not in PATH: %s", err)
+ }
+
+ // Check that swig was installed with Go support by checking
+ // that a go directory exists inside the swiglib directory.
+ // See https://golang.org/issue/23469.
+ output, err := exec.Command(swig, "-go", "-swiglib").Output()
+ if err != nil {
+ t.Skip("swig is missing Go support")
+ }
+ swigDir := strings.TrimSpace(string(output))
+
+ _, err = os.Stat(filepath.Join(swigDir, "go"))
+ if err != nil {
+ t.Skip("swig is missing Go support")
+ }
+
+ // Check that swig has a new enough version.
+ // See https://golang.org/issue/22858.
+ out, err := exec.Command(swig, "-version").CombinedOutput()
+ if err != nil {
+ t.Skipf("failed to get swig version:%s\n%s", err, string(out))
+ }
+
+ re := regexp.MustCompile(`[vV]ersion +(\d+)([.]\d+)?([.]\d+)?`)
+ matches := re.FindSubmatch(out)
+ if matches == nil {
+ // Can't find version number; hope for the best.
+ t.Logf("failed to find swig version, continuing")
+ return
+ }
+
+ var parseError error
+ atoi := func(s string) int {
+ x, err := strconv.Atoi(s)
+ if err != nil && parseError == nil {
+ parseError = err
+ }
+ return x
+ }
+ var major, minor, patch int
+ major = atoi(string(matches[1]))
+ if len(matches[2]) > 0 {
+ minor = atoi(string(matches[2][1:]))
+ }
+ if len(matches[3]) > 0 {
+ patch = atoi(string(matches[3][1:]))
+ }
+ if parseError != nil {
+ t.Logf("error parsing swig version %q, continuing anyway: %s", string(matches[0]), parseError)
+ return
+ }
+ t.Logf("found swig version %d.%d.%d", major, minor, patch)
+ if major < 3 || (major == 3 && minor == 0 && patch < 6) {
+ t.Skip("test requires swig 3.0.6 or later")
+ }
+}
diff --git a/src/cmd/cgo/internal/swig/testdata/callback/main.cc b/src/cmd/cgo/internal/swig/testdata/callback/main.cc
new file mode 100644
index 0000000..7de917c
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/callback/main.cc
@@ -0,0 +1,15 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This .cc file will be automatically compiled by the go tool and
+// included in the package.
+
+#include <string>
+#include "main.h"
+
+std::string Caller::call() {
+ if (callback_ != 0)
+ return callback_->run();
+ return "";
+}
diff --git a/src/cmd/cgo/internal/swig/testdata/callback/main.go b/src/cmd/cgo/internal/swig/testdata/callback/main.go
new file mode 100644
index 0000000..73034a0
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/callback/main.go
@@ -0,0 +1,60 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+)
+
+func main() {
+ if len(os.Args) != 2 {
+ fatal("usage: callback testname")
+ }
+ switch os.Args[1] {
+ default:
+ fatal("unknown test %q", os.Args[1])
+ case "Call":
+ testCall()
+ case "Callback":
+ testCallback()
+ }
+ println("OK")
+}
+
+func fatal(f string, args ...any) {
+ fmt.Fprintln(os.Stderr, fmt.Sprintf(f, args...))
+ os.Exit(1)
+}
+
+type GoCallback struct{}
+
+func (p *GoCallback) Run() string {
+ return "GoCallback.Run"
+}
+
+func testCall() {
+ c := NewCaller()
+ cb := NewCallback()
+
+ c.SetCallback(cb)
+ s := c.Call()
+ if s != "Callback::run" {
+ fatal("unexpected string from Call: %q", s)
+ }
+ c.DelCallback()
+}
+
+func testCallback() {
+ c := NewCaller()
+ cb := NewDirectorCallback(&GoCallback{})
+ c.SetCallback(cb)
+ s := c.Call()
+ if s != "GoCallback.Run" {
+ fatal("unexpected string from Call with callback: %q", s)
+ }
+ c.DelCallback()
+ DeleteDirectorCallback(cb)
+}
diff --git a/src/cmd/cgo/internal/swig/testdata/callback/main.h b/src/cmd/cgo/internal/swig/testdata/callback/main.h
new file mode 100644
index 0000000..4b66106
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/callback/main.h
@@ -0,0 +1,20 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+class Callback {
+public:
+ virtual ~Callback() { }
+ virtual std::string run() { return "Callback::run"; }
+};
+
+class Caller {
+private:
+ Callback *callback_;
+public:
+ Caller(): callback_(0) { }
+ ~Caller() { delCallback(); }
+ void delCallback() { delete callback_; callback_ = 0; }
+ void setCallback(Callback *cb) { delCallback(); callback_ = cb; }
+ std::string call();
+};
diff --git a/src/cmd/cgo/internal/swig/testdata/callback/main.swigcxx b/src/cmd/cgo/internal/swig/testdata/callback/main.swigcxx
new file mode 100644
index 0000000..0fd73d6
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/callback/main.swigcxx
@@ -0,0 +1,18 @@
+/* Copyright 2011 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file. */
+
+/* An example of writing a C++ virtual function in Go. */
+
+%module(directors="1") callback
+
+%{
+#include <string>
+#include "main.h"
+%}
+
+%include "std_string.i"
+
+%feature("director");
+
+%include "main.h"
diff --git a/src/cmd/cgo/internal/swig/testdata/stdio/main.go b/src/cmd/cgo/internal/swig/testdata/stdio/main.go
new file mode 100644
index 0000000..0296dd3
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/stdio/main.go
@@ -0,0 +1,45 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file is here just to cause problems.
+// main.swig turns into a file also named main.go.
+// Make sure cmd/go keeps them separate
+// when both are passed to cgo.
+
+package main
+
+//int F(void) { return 1; }
+import "C"
+import (
+ "fmt"
+ "os"
+)
+
+func F() int { return int(C.F()) }
+
+func main() {
+ if x := int(C.F()); x != 1 {
+ fatal("x = %d, want 1", x)
+ }
+
+ // Open this file itself and verify that the first few characters are
+ // as expected.
+ f := Fopen("main.go", "r")
+ if f.Swigcptr() == 0 {
+ fatal("fopen failed")
+ }
+ if Fgetc(f) != '/' || Fgetc(f) != '/' || Fgetc(f) != ' ' || Fgetc(f) != 'C' {
+ fatal("read unexpected characters")
+ }
+ if Fclose(f) != 0 {
+ fatal("fclose failed")
+ }
+
+ println("OK")
+}
+
+func fatal(f string, args ...any) {
+ fmt.Fprintln(os.Stderr, fmt.Sprintf(f, args...))
+ os.Exit(1)
+}
diff --git a/src/cmd/cgo/internal/swig/testdata/stdio/main.swig b/src/cmd/cgo/internal/swig/testdata/stdio/main.swig
new file mode 100644
index 0000000..b28ae0a
--- /dev/null
+++ b/src/cmd/cgo/internal/swig/testdata/stdio/main.swig
@@ -0,0 +1,24 @@
+/* Copyright 2011 The Go Authors. All rights reserved.
+ Use of this source code is governed by a BSD-style
+ license that can be found in the LICENSE file. */
+
+/* A trivial example of wrapping a C library using SWIG. */
+
+%{
+#include <stdio.h>
+#include <stdlib.h>
+%}
+
+%typemap(gotype) const char * "string"
+%typemap(in) const char * %{
+ $1 = malloc($input.n + 1);
+ memcpy($1, $input.p, $input.n);
+ $1[$input.n] = '\0';
+%}
+%typemap(freearg) const char * %{
+ free($1);
+%}
+
+FILE *fopen(const char *name, const char *mode);
+int fclose(FILE *);
+int fgetc(FILE *);
diff --git a/src/cmd/cgo/internal/test/backdoor.go b/src/cmd/cgo/internal/test/backdoor.go
new file mode 100644
index 0000000..6fb33d6
--- /dev/null
+++ b/src/cmd/cgo/internal/test/backdoor.go
@@ -0,0 +1,11 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+import _ "unsafe"
+
+//go:linkname lockedOSThread runtime.lockedOSThread
+//extern runtime_lockedOSThread
+func lockedOSThread() bool
diff --git a/src/cmd/cgo/internal/test/buildid_linux.go b/src/cmd/cgo/internal/test/buildid_linux.go
new file mode 100644
index 0000000..84d3edb
--- /dev/null
+++ b/src/cmd/cgo/internal/test/buildid_linux.go
@@ -0,0 +1,78 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+// Test that we have no more than one build ID. In the past we used
+// to generate a separate build ID for each package using cgo, and the
+// linker concatenated them all. We don't want that--we only want
+// one.
+
+import (
+ "bytes"
+ "debug/elf"
+ "os"
+ "testing"
+)
+
+func testBuildID(t *testing.T) {
+ f, err := elf.Open("/proc/self/exe")
+ if err != nil {
+ if os.IsNotExist(err) {
+ t.Skip("no /proc/self/exe")
+ }
+ t.Fatal("opening /proc/self/exe: ", err)
+ }
+ defer f.Close()
+
+ c := 0
+sections:
+ for i, s := range f.Sections {
+ if s.Type != elf.SHT_NOTE {
+ continue
+ }
+
+ d, err := s.Data()
+ if err != nil {
+ t.Logf("reading data of note section %d: %v", i, err)
+ continue
+ }
+
+ for len(d) > 0 {
+
+ // ELF standards differ as to the sizes in
+ // note sections. Both the GNU linker and
+ // gold always generate 32-bit sizes, so that
+ // is what we assume here.
+
+ if len(d) < 12 {
+ t.Logf("note section %d too short (%d < 12)", i, len(d))
+ continue sections
+ }
+
+ namesz := f.ByteOrder.Uint32(d)
+ descsz := f.ByteOrder.Uint32(d[4:])
+ typ := f.ByteOrder.Uint32(d[8:])
+
+ an := (namesz + 3) &^ 3
+ ad := (descsz + 3) &^ 3
+
+ if int(12+an+ad) > len(d) {
+ t.Logf("note section %d too short for header (%d < 12 + align(%d,4) + align(%d,4))", i, len(d), namesz, descsz)
+ continue sections
+ }
+
+ // 3 == NT_GNU_BUILD_ID
+ if typ == 3 && namesz == 4 && bytes.Equal(d[12:16], []byte("GNU\000")) {
+ c++
+ }
+
+ d = d[12+an+ad:]
+ }
+ }
+
+ if c > 1 {
+ t.Errorf("found %d build ID notes", c)
+ }
+}
diff --git a/src/cmd/cgo/internal/test/callback.go b/src/cmd/cgo/internal/test/callback.go
new file mode 100644
index 0000000..478bf82
--- /dev/null
+++ b/src/cmd/cgo/internal/test/callback.go
@@ -0,0 +1,1782 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+void callback(void *f);
+void callGoFoo(void);
+void callGoStackCheck(void);
+void callPanic(void);
+int callGoReturnVal(void);
+int returnAfterGrow(void);
+int returnAfterGrowFromGo(void);
+void callGoWithString(void);
+*/
+import "C"
+
+import (
+ "path"
+ "runtime"
+ "strings"
+ "sync"
+ "testing"
+ "unsafe"
+)
+
+// Pass a func value from nestedCall to goCallback using an integer token.
+var callbackMutex sync.Mutex
+var callbackToken int
+var callbackFuncs = make(map[int]func())
+
+// nestedCall calls into C, back into Go, and finally to f.
+func nestedCall(f func()) {
+ // callback(x) calls goCallback(x)
+ callbackMutex.Lock()
+ callbackToken++
+ i := callbackToken
+ callbackFuncs[i] = f
+ callbackMutex.Unlock()
+
+ // Pass the address of i because the C function was written to
+ // take a pointer. We could pass an int if we felt like
+ // rewriting the C code.
+ C.callback(unsafe.Pointer(&i))
+
+ callbackMutex.Lock()
+ delete(callbackFuncs, i)
+ callbackMutex.Unlock()
+}
+
+//export goCallback
+func goCallback(p unsafe.Pointer) {
+ i := *(*int)(p)
+
+ callbackMutex.Lock()
+ f := callbackFuncs[i]
+ callbackMutex.Unlock()
+
+ if f == nil {
+ panic("missing callback function")
+ }
+ f()
+}
+
+func testCallback(t *testing.T) {
+ var x = false
+ nestedCall(func() { x = true })
+ if !x {
+ t.Fatal("nestedCall did not call func")
+ }
+}
+
+func testCallbackGC(t *testing.T) {
+ nestedCall(runtime.GC)
+}
+
+func testCallbackPanic(t *testing.T) {
+ // Make sure panic during callback unwinds properly.
+ if lockedOSThread() {
+ t.Fatal("locked OS thread on entry to TestCallbackPanic")
+ }
+ defer func() {
+ s := recover()
+ if s == nil {
+ t.Fatal("did not panic")
+ }
+ if s.(string) != "callback panic" {
+ t.Fatal("wrong panic:", s)
+ }
+ if lockedOSThread() {
+ t.Fatal("locked OS thread on exit from TestCallbackPanic")
+ }
+ }()
+ nestedCall(func() { panic("callback panic") })
+ panic("nestedCall returned")
+}
+
+func testCallbackPanicLoop(t *testing.T) {
+ // Make sure we don't blow out m->g0 stack.
+ for i := 0; i < 100000; i++ {
+ testCallbackPanic(t)
+ }
+}
+
+func testCallbackPanicLocked(t *testing.T) {
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if !lockedOSThread() {
+ t.Fatal("runtime.LockOSThread didn't")
+ }
+ defer func() {
+ s := recover()
+ if s == nil {
+ t.Fatal("did not panic")
+ }
+ if s.(string) != "callback panic" {
+ t.Fatal("wrong panic:", s)
+ }
+ if !lockedOSThread() {
+ t.Fatal("lost lock on OS thread after panic")
+ }
+ }()
+ nestedCall(func() { panic("callback panic") })
+ panic("nestedCall returned")
+}
+
+// Callback with zero arguments used to make the stack misaligned,
+// which broke the garbage collector and other things.
+func testZeroArgCallback(t *testing.T) {
+ defer func() {
+ s := recover()
+ if s != nil {
+ t.Fatal("panic during callback:", s)
+ }
+ }()
+ C.callGoFoo()
+}
+
+//export goFoo
+func goFoo() {
+ x := 1
+ for i := 0; i < 10000; i++ {
+ // variadic call mallocs + writes to
+ variadic(x, x, x)
+ if x != 1 {
+ panic("bad x")
+ }
+ }
+}
+
+func variadic(x ...interface{}) {}
+
+func testBlocking(t *testing.T) {
+ c := make(chan int)
+ go func() {
+ for i := 0; i < 10; i++ {
+ c <- <-c
+ }
+ }()
+ nestedCall(func() {
+ for i := 0; i < 10; i++ {
+ c <- i
+ if j := <-c; j != i {
+ t.Errorf("out of sync %d != %d", j, i)
+ }
+ }
+ })
+}
+
+// Test that the stack can be unwound through a call out and call back
+// into Go.
+func testCallbackCallers(t *testing.T) {
+ if runtime.Compiler != "gc" {
+ // The exact function names are not going to be the same.
+ t.Skip("skipping for non-gc toolchain")
+ }
+ pc := make([]uintptr, 100)
+ n := 0
+ name := []string{
+ "runtime.cgocallbackg1",
+ "runtime.cgocallbackg",
+ "runtime.cgocallback",
+ "runtime.systemstack_switch",
+ "runtime.cgocall",
+ "test._Cfunc_callback",
+ "test.nestedCall.func1",
+ "test.nestedCall",
+ "test.testCallbackCallers",
+ "test.TestCallbackCallers",
+ "testing.tRunner",
+ "runtime.goexit",
+ }
+ nestedCall(func() {
+ n = runtime.Callers(4, pc)
+ })
+ if n != len(name) {
+ t.Errorf("expected %d frames, got %d", len(name), n)
+ }
+ for i := 0; i < n; i++ {
+ f := runtime.FuncForPC(pc[i] - 1) // TODO: use runtime.CallersFrames
+ if f == nil {
+ t.Fatalf("expected non-nil Func for pc %d", pc[i])
+ }
+ fname := f.Name()
+ // Remove the prepended pathname from automatically
+ // generated cgo function names.
+ if strings.HasPrefix(fname, "_") {
+ fname = path.Base(f.Name()[1:])
+ }
+ // In module mode, this package has a fully-qualified import path.
+ // Remove it if present.
+ fname = strings.TrimPrefix(fname, "cmd/cgo/internal/")
+
+ namei := ""
+ if i < len(name) {
+ namei = name[i]
+ }
+ if fname != namei {
+ t.Errorf("stk[%d] = %q, want %q", i, fname, namei)
+ }
+ }
+}
+
+func testPanicFromC(t *testing.T) {
+ defer func() {
+ r := recover()
+ if r == nil {
+ t.Fatal("did not panic")
+ }
+ if r.(string) != "panic from C" {
+ t.Fatal("wrong panic:", r)
+ }
+ }()
+ C.callPanic()
+}
+
+// Test that C code can return a value if it calls a Go function that
+// causes a stack copy.
+func testReturnAfterGrow(t *testing.T) {
+ // Use a new goroutine so that we get a small stack.
+ c := make(chan int)
+ go func() {
+ c <- int(C.returnAfterGrow())
+ }()
+ if got, want := <-c, 123456; got != want {
+ t.Errorf("got %d want %d", got, want)
+ }
+}
+
+// Test that we can return a value from Go->C->Go if the Go code
+// causes a stack copy.
+func testReturnAfterGrowFromGo(t *testing.T) {
+ // Use a new goroutine so that we get a small stack.
+ c := make(chan int)
+ go func() {
+ c <- int(C.returnAfterGrowFromGo())
+ }()
+ if got, want := <-c, 129*128/2; got != want {
+ t.Errorf("got %d want %d", got, want)
+ }
+}
+
+//export goReturnVal
+func goReturnVal() (r C.int) {
+ // Force a stack copy.
+ var f func(int) int
+ f = func(i int) int {
+ var buf [256]byte
+ use(buf[:])
+ if i == 0 {
+ return 0
+ }
+ return i + f(i-1)
+ }
+ r = C.int(f(128))
+ return
+}
+
+// Test that C can pass in a Go string from a string constant.
+func testCallGoWithString(t *testing.T) {
+ C.callGoWithString()
+ want := "string passed from C to Go"
+ if stringFromGo != want {
+ t.Errorf("string passed through C is %s, want %s", stringFromGo, want)
+ }
+}
+
+var stringFromGo string
+
+//export goWithString
+func goWithString(s string) {
+ stringFromGo = s
+}
+
+func testCallbackStack(t *testing.T) {
+ // Make cgo call and callback with different amount of stack available.
+ // We do not do any explicit checks, just ensure that it does not crash.
+ for _, f := range splitTests {
+ f()
+ }
+}
+
+//export goStackCheck
+func goStackCheck() {
+ // use some stack memory to trigger split stack check
+ var buf [256]byte
+ use(buf[:])
+}
+
+var Used byte
+
+func use(buf []byte) {
+ for _, c := range buf {
+ Used += c
+ }
+}
+
+var splitTests = []func(){
+ // Edit .+1,/^}/-1|seq 4 4 5000 | sed 's/.*/ stack&,/' | fmt
+ stack4, stack8, stack12, stack16, stack20, stack24, stack28,
+ stack32, stack36, stack40, stack44, stack48, stack52, stack56,
+ stack60, stack64, stack68, stack72, stack76, stack80, stack84,
+ stack88, stack92, stack96, stack100, stack104, stack108, stack112,
+ stack116, stack120, stack124, stack128, stack132, stack136,
+ stack140, stack144, stack148, stack152, stack156, stack160,
+ stack164, stack168, stack172, stack176, stack180, stack184,
+ stack188, stack192, stack196, stack200, stack204, stack208,
+ stack212, stack216, stack220, stack224, stack228, stack232,
+ stack236, stack240, stack244, stack248, stack252, stack256,
+ stack260, stack264, stack268, stack272, stack276, stack280,
+ stack284, stack288, stack292, stack296, stack300, stack304,
+ stack308, stack312, stack316, stack320, stack324, stack328,
+ stack332, stack336, stack340, stack344, stack348, stack352,
+ stack356, stack360, stack364, stack368, stack372, stack376,
+ stack380, stack384, stack388, stack392, stack396, stack400,
+ stack404, stack408, stack412, stack416, stack420, stack424,
+ stack428, stack432, stack436, stack440, stack444, stack448,
+ stack452, stack456, stack460, stack464, stack468, stack472,
+ stack476, stack480, stack484, stack488, stack492, stack496,
+ stack500, stack504, stack508, stack512, stack516, stack520,
+ stack524, stack528, stack532, stack536, stack540, stack544,
+ stack548, stack552, stack556, stack560, stack564, stack568,
+ stack572, stack576, stack580, stack584, stack588, stack592,
+ stack596, stack600, stack604, stack608, stack612, stack616,
+ stack620, stack624, stack628, stack632, stack636, stack640,
+ stack644, stack648, stack652, stack656, stack660, stack664,
+ stack668, stack672, stack676, stack680, stack684, stack688,
+ stack692, stack696, stack700, stack704, stack708, stack712,
+ stack716, stack720, stack724, stack728, stack732, stack736,
+ stack740, stack744, stack748, stack752, stack756, stack760,
+ stack764, stack768, stack772, stack776, stack780, stack784,
+ stack788, stack792, stack796, stack800, stack804, stack808,
+ stack812, stack816, stack820, stack824, stack828, stack832,
+ stack836, stack840, stack844, stack848, stack852, stack856,
+ stack860, stack864, stack868, stack872, stack876, stack880,
+ stack884, stack888, stack892, stack896, stack900, stack904,
+ stack908, stack912, stack916, stack920, stack924, stack928,
+ stack932, stack936, stack940, stack944, stack948, stack952,
+ stack956, stack960, stack964, stack968, stack972, stack976,
+ stack980, stack984, stack988, stack992, stack996, stack1000,
+ stack1004, stack1008, stack1012, stack1016, stack1020, stack1024,
+ stack1028, stack1032, stack1036, stack1040, stack1044, stack1048,
+ stack1052, stack1056, stack1060, stack1064, stack1068, stack1072,
+ stack1076, stack1080, stack1084, stack1088, stack1092, stack1096,
+ stack1100, stack1104, stack1108, stack1112, stack1116, stack1120,
+ stack1124, stack1128, stack1132, stack1136, stack1140, stack1144,
+ stack1148, stack1152, stack1156, stack1160, stack1164, stack1168,
+ stack1172, stack1176, stack1180, stack1184, stack1188, stack1192,
+ stack1196, stack1200, stack1204, stack1208, stack1212, stack1216,
+ stack1220, stack1224, stack1228, stack1232, stack1236, stack1240,
+ stack1244, stack1248, stack1252, stack1256, stack1260, stack1264,
+ stack1268, stack1272, stack1276, stack1280, stack1284, stack1288,
+ stack1292, stack1296, stack1300, stack1304, stack1308, stack1312,
+ stack1316, stack1320, stack1324, stack1328, stack1332, stack1336,
+ stack1340, stack1344, stack1348, stack1352, stack1356, stack1360,
+ stack1364, stack1368, stack1372, stack1376, stack1380, stack1384,
+ stack1388, stack1392, stack1396, stack1400, stack1404, stack1408,
+ stack1412, stack1416, stack1420, stack1424, stack1428, stack1432,
+ stack1436, stack1440, stack1444, stack1448, stack1452, stack1456,
+ stack1460, stack1464, stack1468, stack1472, stack1476, stack1480,
+ stack1484, stack1488, stack1492, stack1496, stack1500, stack1504,
+ stack1508, stack1512, stack1516, stack1520, stack1524, stack1528,
+ stack1532, stack1536, stack1540, stack1544, stack1548, stack1552,
+ stack1556, stack1560, stack1564, stack1568, stack1572, stack1576,
+ stack1580, stack1584, stack1588, stack1592, stack1596, stack1600,
+ stack1604, stack1608, stack1612, stack1616, stack1620, stack1624,
+ stack1628, stack1632, stack1636, stack1640, stack1644, stack1648,
+ stack1652, stack1656, stack1660, stack1664, stack1668, stack1672,
+ stack1676, stack1680, stack1684, stack1688, stack1692, stack1696,
+ stack1700, stack1704, stack1708, stack1712, stack1716, stack1720,
+ stack1724, stack1728, stack1732, stack1736, stack1740, stack1744,
+ stack1748, stack1752, stack1756, stack1760, stack1764, stack1768,
+ stack1772, stack1776, stack1780, stack1784, stack1788, stack1792,
+ stack1796, stack1800, stack1804, stack1808, stack1812, stack1816,
+ stack1820, stack1824, stack1828, stack1832, stack1836, stack1840,
+ stack1844, stack1848, stack1852, stack1856, stack1860, stack1864,
+ stack1868, stack1872, stack1876, stack1880, stack1884, stack1888,
+ stack1892, stack1896, stack1900, stack1904, stack1908, stack1912,
+ stack1916, stack1920, stack1924, stack1928, stack1932, stack1936,
+ stack1940, stack1944, stack1948, stack1952, stack1956, stack1960,
+ stack1964, stack1968, stack1972, stack1976, stack1980, stack1984,
+ stack1988, stack1992, stack1996, stack2000, stack2004, stack2008,
+ stack2012, stack2016, stack2020, stack2024, stack2028, stack2032,
+ stack2036, stack2040, stack2044, stack2048, stack2052, stack2056,
+ stack2060, stack2064, stack2068, stack2072, stack2076, stack2080,
+ stack2084, stack2088, stack2092, stack2096, stack2100, stack2104,
+ stack2108, stack2112, stack2116, stack2120, stack2124, stack2128,
+ stack2132, stack2136, stack2140, stack2144, stack2148, stack2152,
+ stack2156, stack2160, stack2164, stack2168, stack2172, stack2176,
+ stack2180, stack2184, stack2188, stack2192, stack2196, stack2200,
+ stack2204, stack2208, stack2212, stack2216, stack2220, stack2224,
+ stack2228, stack2232, stack2236, stack2240, stack2244, stack2248,
+ stack2252, stack2256, stack2260, stack2264, stack2268, stack2272,
+ stack2276, stack2280, stack2284, stack2288, stack2292, stack2296,
+ stack2300, stack2304, stack2308, stack2312, stack2316, stack2320,
+ stack2324, stack2328, stack2332, stack2336, stack2340, stack2344,
+ stack2348, stack2352, stack2356, stack2360, stack2364, stack2368,
+ stack2372, stack2376, stack2380, stack2384, stack2388, stack2392,
+ stack2396, stack2400, stack2404, stack2408, stack2412, stack2416,
+ stack2420, stack2424, stack2428, stack2432, stack2436, stack2440,
+ stack2444, stack2448, stack2452, stack2456, stack2460, stack2464,
+ stack2468, stack2472, stack2476, stack2480, stack2484, stack2488,
+ stack2492, stack2496, stack2500, stack2504, stack2508, stack2512,
+ stack2516, stack2520, stack2524, stack2528, stack2532, stack2536,
+ stack2540, stack2544, stack2548, stack2552, stack2556, stack2560,
+ stack2564, stack2568, stack2572, stack2576, stack2580, stack2584,
+ stack2588, stack2592, stack2596, stack2600, stack2604, stack2608,
+ stack2612, stack2616, stack2620, stack2624, stack2628, stack2632,
+ stack2636, stack2640, stack2644, stack2648, stack2652, stack2656,
+ stack2660, stack2664, stack2668, stack2672, stack2676, stack2680,
+ stack2684, stack2688, stack2692, stack2696, stack2700, stack2704,
+ stack2708, stack2712, stack2716, stack2720, stack2724, stack2728,
+ stack2732, stack2736, stack2740, stack2744, stack2748, stack2752,
+ stack2756, stack2760, stack2764, stack2768, stack2772, stack2776,
+ stack2780, stack2784, stack2788, stack2792, stack2796, stack2800,
+ stack2804, stack2808, stack2812, stack2816, stack2820, stack2824,
+ stack2828, stack2832, stack2836, stack2840, stack2844, stack2848,
+ stack2852, stack2856, stack2860, stack2864, stack2868, stack2872,
+ stack2876, stack2880, stack2884, stack2888, stack2892, stack2896,
+ stack2900, stack2904, stack2908, stack2912, stack2916, stack2920,
+ stack2924, stack2928, stack2932, stack2936, stack2940, stack2944,
+ stack2948, stack2952, stack2956, stack2960, stack2964, stack2968,
+ stack2972, stack2976, stack2980, stack2984, stack2988, stack2992,
+ stack2996, stack3000, stack3004, stack3008, stack3012, stack3016,
+ stack3020, stack3024, stack3028, stack3032, stack3036, stack3040,
+ stack3044, stack3048, stack3052, stack3056, stack3060, stack3064,
+ stack3068, stack3072, stack3076, stack3080, stack3084, stack3088,
+ stack3092, stack3096, stack3100, stack3104, stack3108, stack3112,
+ stack3116, stack3120, stack3124, stack3128, stack3132, stack3136,
+ stack3140, stack3144, stack3148, stack3152, stack3156, stack3160,
+ stack3164, stack3168, stack3172, stack3176, stack3180, stack3184,
+ stack3188, stack3192, stack3196, stack3200, stack3204, stack3208,
+ stack3212, stack3216, stack3220, stack3224, stack3228, stack3232,
+ stack3236, stack3240, stack3244, stack3248, stack3252, stack3256,
+ stack3260, stack3264, stack3268, stack3272, stack3276, stack3280,
+ stack3284, stack3288, stack3292, stack3296, stack3300, stack3304,
+ stack3308, stack3312, stack3316, stack3320, stack3324, stack3328,
+ stack3332, stack3336, stack3340, stack3344, stack3348, stack3352,
+ stack3356, stack3360, stack3364, stack3368, stack3372, stack3376,
+ stack3380, stack3384, stack3388, stack3392, stack3396, stack3400,
+ stack3404, stack3408, stack3412, stack3416, stack3420, stack3424,
+ stack3428, stack3432, stack3436, stack3440, stack3444, stack3448,
+ stack3452, stack3456, stack3460, stack3464, stack3468, stack3472,
+ stack3476, stack3480, stack3484, stack3488, stack3492, stack3496,
+ stack3500, stack3504, stack3508, stack3512, stack3516, stack3520,
+ stack3524, stack3528, stack3532, stack3536, stack3540, stack3544,
+ stack3548, stack3552, stack3556, stack3560, stack3564, stack3568,
+ stack3572, stack3576, stack3580, stack3584, stack3588, stack3592,
+ stack3596, stack3600, stack3604, stack3608, stack3612, stack3616,
+ stack3620, stack3624, stack3628, stack3632, stack3636, stack3640,
+ stack3644, stack3648, stack3652, stack3656, stack3660, stack3664,
+ stack3668, stack3672, stack3676, stack3680, stack3684, stack3688,
+ stack3692, stack3696, stack3700, stack3704, stack3708, stack3712,
+ stack3716, stack3720, stack3724, stack3728, stack3732, stack3736,
+ stack3740, stack3744, stack3748, stack3752, stack3756, stack3760,
+ stack3764, stack3768, stack3772, stack3776, stack3780, stack3784,
+ stack3788, stack3792, stack3796, stack3800, stack3804, stack3808,
+ stack3812, stack3816, stack3820, stack3824, stack3828, stack3832,
+ stack3836, stack3840, stack3844, stack3848, stack3852, stack3856,
+ stack3860, stack3864, stack3868, stack3872, stack3876, stack3880,
+ stack3884, stack3888, stack3892, stack3896, stack3900, stack3904,
+ stack3908, stack3912, stack3916, stack3920, stack3924, stack3928,
+ stack3932, stack3936, stack3940, stack3944, stack3948, stack3952,
+ stack3956, stack3960, stack3964, stack3968, stack3972, stack3976,
+ stack3980, stack3984, stack3988, stack3992, stack3996, stack4000,
+ stack4004, stack4008, stack4012, stack4016, stack4020, stack4024,
+ stack4028, stack4032, stack4036, stack4040, stack4044, stack4048,
+ stack4052, stack4056, stack4060, stack4064, stack4068, stack4072,
+ stack4076, stack4080, stack4084, stack4088, stack4092, stack4096,
+ stack4100, stack4104, stack4108, stack4112, stack4116, stack4120,
+ stack4124, stack4128, stack4132, stack4136, stack4140, stack4144,
+ stack4148, stack4152, stack4156, stack4160, stack4164, stack4168,
+ stack4172, stack4176, stack4180, stack4184, stack4188, stack4192,
+ stack4196, stack4200, stack4204, stack4208, stack4212, stack4216,
+ stack4220, stack4224, stack4228, stack4232, stack4236, stack4240,
+ stack4244, stack4248, stack4252, stack4256, stack4260, stack4264,
+ stack4268, stack4272, stack4276, stack4280, stack4284, stack4288,
+ stack4292, stack4296, stack4300, stack4304, stack4308, stack4312,
+ stack4316, stack4320, stack4324, stack4328, stack4332, stack4336,
+ stack4340, stack4344, stack4348, stack4352, stack4356, stack4360,
+ stack4364, stack4368, stack4372, stack4376, stack4380, stack4384,
+ stack4388, stack4392, stack4396, stack4400, stack4404, stack4408,
+ stack4412, stack4416, stack4420, stack4424, stack4428, stack4432,
+ stack4436, stack4440, stack4444, stack4448, stack4452, stack4456,
+ stack4460, stack4464, stack4468, stack4472, stack4476, stack4480,
+ stack4484, stack4488, stack4492, stack4496, stack4500, stack4504,
+ stack4508, stack4512, stack4516, stack4520, stack4524, stack4528,
+ stack4532, stack4536, stack4540, stack4544, stack4548, stack4552,
+ stack4556, stack4560, stack4564, stack4568, stack4572, stack4576,
+ stack4580, stack4584, stack4588, stack4592, stack4596, stack4600,
+ stack4604, stack4608, stack4612, stack4616, stack4620, stack4624,
+ stack4628, stack4632, stack4636, stack4640, stack4644, stack4648,
+ stack4652, stack4656, stack4660, stack4664, stack4668, stack4672,
+ stack4676, stack4680, stack4684, stack4688, stack4692, stack4696,
+ stack4700, stack4704, stack4708, stack4712, stack4716, stack4720,
+ stack4724, stack4728, stack4732, stack4736, stack4740, stack4744,
+ stack4748, stack4752, stack4756, stack4760, stack4764, stack4768,
+ stack4772, stack4776, stack4780, stack4784, stack4788, stack4792,
+ stack4796, stack4800, stack4804, stack4808, stack4812, stack4816,
+ stack4820, stack4824, stack4828, stack4832, stack4836, stack4840,
+ stack4844, stack4848, stack4852, stack4856, stack4860, stack4864,
+ stack4868, stack4872, stack4876, stack4880, stack4884, stack4888,
+ stack4892, stack4896, stack4900, stack4904, stack4908, stack4912,
+ stack4916, stack4920, stack4924, stack4928, stack4932, stack4936,
+ stack4940, stack4944, stack4948, stack4952, stack4956, stack4960,
+ stack4964, stack4968, stack4972, stack4976, stack4980, stack4984,
+ stack4988, stack4992, stack4996, stack5000,
+}
+
+// Edit .+1,$ | seq 4 4 5000 | sed 's/.*/func stack&() { var buf [&]byte; use(buf[:]); C.callGoStackCheck() }/'
+func stack4() { var buf [4]byte; use(buf[:]); C.callGoStackCheck() }
+func stack8() { var buf [8]byte; use(buf[:]); C.callGoStackCheck() }
+func stack12() { var buf [12]byte; use(buf[:]); C.callGoStackCheck() }
+func stack16() { var buf [16]byte; use(buf[:]); C.callGoStackCheck() }
+func stack20() { var buf [20]byte; use(buf[:]); C.callGoStackCheck() }
+func stack24() { var buf [24]byte; use(buf[:]); C.callGoStackCheck() }
+func stack28() { var buf [28]byte; use(buf[:]); C.callGoStackCheck() }
+func stack32() { var buf [32]byte; use(buf[:]); C.callGoStackCheck() }
+func stack36() { var buf [36]byte; use(buf[:]); C.callGoStackCheck() }
+func stack40() { var buf [40]byte; use(buf[:]); C.callGoStackCheck() }
+func stack44() { var buf [44]byte; use(buf[:]); C.callGoStackCheck() }
+func stack48() { var buf [48]byte; use(buf[:]); C.callGoStackCheck() }
+func stack52() { var buf [52]byte; use(buf[:]); C.callGoStackCheck() }
+func stack56() { var buf [56]byte; use(buf[:]); C.callGoStackCheck() }
+func stack60() { var buf [60]byte; use(buf[:]); C.callGoStackCheck() }
+func stack64() { var buf [64]byte; use(buf[:]); C.callGoStackCheck() }
+func stack68() { var buf [68]byte; use(buf[:]); C.callGoStackCheck() }
+func stack72() { var buf [72]byte; use(buf[:]); C.callGoStackCheck() }
+func stack76() { var buf [76]byte; use(buf[:]); C.callGoStackCheck() }
+func stack80() { var buf [80]byte; use(buf[:]); C.callGoStackCheck() }
+func stack84() { var buf [84]byte; use(buf[:]); C.callGoStackCheck() }
+func stack88() { var buf [88]byte; use(buf[:]); C.callGoStackCheck() }
+func stack92() { var buf [92]byte; use(buf[:]); C.callGoStackCheck() }
+func stack96() { var buf [96]byte; use(buf[:]); C.callGoStackCheck() }
+func stack100() { var buf [100]byte; use(buf[:]); C.callGoStackCheck() }
+func stack104() { var buf [104]byte; use(buf[:]); C.callGoStackCheck() }
+func stack108() { var buf [108]byte; use(buf[:]); C.callGoStackCheck() }
+func stack112() { var buf [112]byte; use(buf[:]); C.callGoStackCheck() }
+func stack116() { var buf [116]byte; use(buf[:]); C.callGoStackCheck() }
+func stack120() { var buf [120]byte; use(buf[:]); C.callGoStackCheck() }
+func stack124() { var buf [124]byte; use(buf[:]); C.callGoStackCheck() }
+func stack128() { var buf [128]byte; use(buf[:]); C.callGoStackCheck() }
+func stack132() { var buf [132]byte; use(buf[:]); C.callGoStackCheck() }
+func stack136() { var buf [136]byte; use(buf[:]); C.callGoStackCheck() }
+func stack140() { var buf [140]byte; use(buf[:]); C.callGoStackCheck() }
+func stack144() { var buf [144]byte; use(buf[:]); C.callGoStackCheck() }
+func stack148() { var buf [148]byte; use(buf[:]); C.callGoStackCheck() }
+func stack152() { var buf [152]byte; use(buf[:]); C.callGoStackCheck() }
+func stack156() { var buf [156]byte; use(buf[:]); C.callGoStackCheck() }
+func stack160() { var buf [160]byte; use(buf[:]); C.callGoStackCheck() }
+func stack164() { var buf [164]byte; use(buf[:]); C.callGoStackCheck() }
+func stack168() { var buf [168]byte; use(buf[:]); C.callGoStackCheck() }
+func stack172() { var buf [172]byte; use(buf[:]); C.callGoStackCheck() }
+func stack176() { var buf [176]byte; use(buf[:]); C.callGoStackCheck() }
+func stack180() { var buf [180]byte; use(buf[:]); C.callGoStackCheck() }
+func stack184() { var buf [184]byte; use(buf[:]); C.callGoStackCheck() }
+func stack188() { var buf [188]byte; use(buf[:]); C.callGoStackCheck() }
+func stack192() { var buf [192]byte; use(buf[:]); C.callGoStackCheck() }
+func stack196() { var buf [196]byte; use(buf[:]); C.callGoStackCheck() }
+func stack200() { var buf [200]byte; use(buf[:]); C.callGoStackCheck() }
+func stack204() { var buf [204]byte; use(buf[:]); C.callGoStackCheck() }
+func stack208() { var buf [208]byte; use(buf[:]); C.callGoStackCheck() }
+func stack212() { var buf [212]byte; use(buf[:]); C.callGoStackCheck() }
+func stack216() { var buf [216]byte; use(buf[:]); C.callGoStackCheck() }
+func stack220() { var buf [220]byte; use(buf[:]); C.callGoStackCheck() }
+func stack224() { var buf [224]byte; use(buf[:]); C.callGoStackCheck() }
+func stack228() { var buf [228]byte; use(buf[:]); C.callGoStackCheck() }
+func stack232() { var buf [232]byte; use(buf[:]); C.callGoStackCheck() }
+func stack236() { var buf [236]byte; use(buf[:]); C.callGoStackCheck() }
+func stack240() { var buf [240]byte; use(buf[:]); C.callGoStackCheck() }
+func stack244() { var buf [244]byte; use(buf[:]); C.callGoStackCheck() }
+func stack248() { var buf [248]byte; use(buf[:]); C.callGoStackCheck() }
+func stack252() { var buf [252]byte; use(buf[:]); C.callGoStackCheck() }
+func stack256() { var buf [256]byte; use(buf[:]); C.callGoStackCheck() }
+func stack260() { var buf [260]byte; use(buf[:]); C.callGoStackCheck() }
+func stack264() { var buf [264]byte; use(buf[:]); C.callGoStackCheck() }
+func stack268() { var buf [268]byte; use(buf[:]); C.callGoStackCheck() }
+func stack272() { var buf [272]byte; use(buf[:]); C.callGoStackCheck() }
+func stack276() { var buf [276]byte; use(buf[:]); C.callGoStackCheck() }
+func stack280() { var buf [280]byte; use(buf[:]); C.callGoStackCheck() }
+func stack284() { var buf [284]byte; use(buf[:]); C.callGoStackCheck() }
+func stack288() { var buf [288]byte; use(buf[:]); C.callGoStackCheck() }
+func stack292() { var buf [292]byte; use(buf[:]); C.callGoStackCheck() }
+func stack296() { var buf [296]byte; use(buf[:]); C.callGoStackCheck() }
+func stack300() { var buf [300]byte; use(buf[:]); C.callGoStackCheck() }
+func stack304() { var buf [304]byte; use(buf[:]); C.callGoStackCheck() }
+func stack308() { var buf [308]byte; use(buf[:]); C.callGoStackCheck() }
+func stack312() { var buf [312]byte; use(buf[:]); C.callGoStackCheck() }
+func stack316() { var buf [316]byte; use(buf[:]); C.callGoStackCheck() }
+func stack320() { var buf [320]byte; use(buf[:]); C.callGoStackCheck() }
+func stack324() { var buf [324]byte; use(buf[:]); C.callGoStackCheck() }
+func stack328() { var buf [328]byte; use(buf[:]); C.callGoStackCheck() }
+func stack332() { var buf [332]byte; use(buf[:]); C.callGoStackCheck() }
+func stack336() { var buf [336]byte; use(buf[:]); C.callGoStackCheck() }
+func stack340() { var buf [340]byte; use(buf[:]); C.callGoStackCheck() }
+func stack344() { var buf [344]byte; use(buf[:]); C.callGoStackCheck() }
+func stack348() { var buf [348]byte; use(buf[:]); C.callGoStackCheck() }
+func stack352() { var buf [352]byte; use(buf[:]); C.callGoStackCheck() }
+func stack356() { var buf [356]byte; use(buf[:]); C.callGoStackCheck() }
+func stack360() { var buf [360]byte; use(buf[:]); C.callGoStackCheck() }
+func stack364() { var buf [364]byte; use(buf[:]); C.callGoStackCheck() }
+func stack368() { var buf [368]byte; use(buf[:]); C.callGoStackCheck() }
+func stack372() { var buf [372]byte; use(buf[:]); C.callGoStackCheck() }
+func stack376() { var buf [376]byte; use(buf[:]); C.callGoStackCheck() }
+func stack380() { var buf [380]byte; use(buf[:]); C.callGoStackCheck() }
+func stack384() { var buf [384]byte; use(buf[:]); C.callGoStackCheck() }
+func stack388() { var buf [388]byte; use(buf[:]); C.callGoStackCheck() }
+func stack392() { var buf [392]byte; use(buf[:]); C.callGoStackCheck() }
+func stack396() { var buf [396]byte; use(buf[:]); C.callGoStackCheck() }
+func stack400() { var buf [400]byte; use(buf[:]); C.callGoStackCheck() }
+func stack404() { var buf [404]byte; use(buf[:]); C.callGoStackCheck() }
+func stack408() { var buf [408]byte; use(buf[:]); C.callGoStackCheck() }
+func stack412() { var buf [412]byte; use(buf[:]); C.callGoStackCheck() }
+func stack416() { var buf [416]byte; use(buf[:]); C.callGoStackCheck() }
+func stack420() { var buf [420]byte; use(buf[:]); C.callGoStackCheck() }
+func stack424() { var buf [424]byte; use(buf[:]); C.callGoStackCheck() }
+func stack428() { var buf [428]byte; use(buf[:]); C.callGoStackCheck() }
+func stack432() { var buf [432]byte; use(buf[:]); C.callGoStackCheck() }
+func stack436() { var buf [436]byte; use(buf[:]); C.callGoStackCheck() }
+func stack440() { var buf [440]byte; use(buf[:]); C.callGoStackCheck() }
+func stack444() { var buf [444]byte; use(buf[:]); C.callGoStackCheck() }
+func stack448() { var buf [448]byte; use(buf[:]); C.callGoStackCheck() }
+func stack452() { var buf [452]byte; use(buf[:]); C.callGoStackCheck() }
+func stack456() { var buf [456]byte; use(buf[:]); C.callGoStackCheck() }
+func stack460() { var buf [460]byte; use(buf[:]); C.callGoStackCheck() }
+func stack464() { var buf [464]byte; use(buf[:]); C.callGoStackCheck() }
+func stack468() { var buf [468]byte; use(buf[:]); C.callGoStackCheck() }
+func stack472() { var buf [472]byte; use(buf[:]); C.callGoStackCheck() }
+func stack476() { var buf [476]byte; use(buf[:]); C.callGoStackCheck() }
+func stack480() { var buf [480]byte; use(buf[:]); C.callGoStackCheck() }
+func stack484() { var buf [484]byte; use(buf[:]); C.callGoStackCheck() }
+func stack488() { var buf [488]byte; use(buf[:]); C.callGoStackCheck() }
+func stack492() { var buf [492]byte; use(buf[:]); C.callGoStackCheck() }
+func stack496() { var buf [496]byte; use(buf[:]); C.callGoStackCheck() }
+func stack500() { var buf [500]byte; use(buf[:]); C.callGoStackCheck() }
+func stack504() { var buf [504]byte; use(buf[:]); C.callGoStackCheck() }
+func stack508() { var buf [508]byte; use(buf[:]); C.callGoStackCheck() }
+func stack512() { var buf [512]byte; use(buf[:]); C.callGoStackCheck() }
+func stack516() { var buf [516]byte; use(buf[:]); C.callGoStackCheck() }
+func stack520() { var buf [520]byte; use(buf[:]); C.callGoStackCheck() }
+func stack524() { var buf [524]byte; use(buf[:]); C.callGoStackCheck() }
+func stack528() { var buf [528]byte; use(buf[:]); C.callGoStackCheck() }
+func stack532() { var buf [532]byte; use(buf[:]); C.callGoStackCheck() }
+func stack536() { var buf [536]byte; use(buf[:]); C.callGoStackCheck() }
+func stack540() { var buf [540]byte; use(buf[:]); C.callGoStackCheck() }
+func stack544() { var buf [544]byte; use(buf[:]); C.callGoStackCheck() }
+func stack548() { var buf [548]byte; use(buf[:]); C.callGoStackCheck() }
+func stack552() { var buf [552]byte; use(buf[:]); C.callGoStackCheck() }
+func stack556() { var buf [556]byte; use(buf[:]); C.callGoStackCheck() }
+func stack560() { var buf [560]byte; use(buf[:]); C.callGoStackCheck() }
+func stack564() { var buf [564]byte; use(buf[:]); C.callGoStackCheck() }
+func stack568() { var buf [568]byte; use(buf[:]); C.callGoStackCheck() }
+func stack572() { var buf [572]byte; use(buf[:]); C.callGoStackCheck() }
+func stack576() { var buf [576]byte; use(buf[:]); C.callGoStackCheck() }
+func stack580() { var buf [580]byte; use(buf[:]); C.callGoStackCheck() }
+func stack584() { var buf [584]byte; use(buf[:]); C.callGoStackCheck() }
+func stack588() { var buf [588]byte; use(buf[:]); C.callGoStackCheck() }
+func stack592() { var buf [592]byte; use(buf[:]); C.callGoStackCheck() }
+func stack596() { var buf [596]byte; use(buf[:]); C.callGoStackCheck() }
+func stack600() { var buf [600]byte; use(buf[:]); C.callGoStackCheck() }
+func stack604() { var buf [604]byte; use(buf[:]); C.callGoStackCheck() }
+func stack608() { var buf [608]byte; use(buf[:]); C.callGoStackCheck() }
+func stack612() { var buf [612]byte; use(buf[:]); C.callGoStackCheck() }
+func stack616() { var buf [616]byte; use(buf[:]); C.callGoStackCheck() }
+func stack620() { var buf [620]byte; use(buf[:]); C.callGoStackCheck() }
+func stack624() { var buf [624]byte; use(buf[:]); C.callGoStackCheck() }
+func stack628() { var buf [628]byte; use(buf[:]); C.callGoStackCheck() }
+func stack632() { var buf [632]byte; use(buf[:]); C.callGoStackCheck() }
+func stack636() { var buf [636]byte; use(buf[:]); C.callGoStackCheck() }
+func stack640() { var buf [640]byte; use(buf[:]); C.callGoStackCheck() }
+func stack644() { var buf [644]byte; use(buf[:]); C.callGoStackCheck() }
+func stack648() { var buf [648]byte; use(buf[:]); C.callGoStackCheck() }
+func stack652() { var buf [652]byte; use(buf[:]); C.callGoStackCheck() }
+func stack656() { var buf [656]byte; use(buf[:]); C.callGoStackCheck() }
+func stack660() { var buf [660]byte; use(buf[:]); C.callGoStackCheck() }
+func stack664() { var buf [664]byte; use(buf[:]); C.callGoStackCheck() }
+func stack668() { var buf [668]byte; use(buf[:]); C.callGoStackCheck() }
+func stack672() { var buf [672]byte; use(buf[:]); C.callGoStackCheck() }
+func stack676() { var buf [676]byte; use(buf[:]); C.callGoStackCheck() }
+func stack680() { var buf [680]byte; use(buf[:]); C.callGoStackCheck() }
+func stack684() { var buf [684]byte; use(buf[:]); C.callGoStackCheck() }
+func stack688() { var buf [688]byte; use(buf[:]); C.callGoStackCheck() }
+func stack692() { var buf [692]byte; use(buf[:]); C.callGoStackCheck() }
+func stack696() { var buf [696]byte; use(buf[:]); C.callGoStackCheck() }
+func stack700() { var buf [700]byte; use(buf[:]); C.callGoStackCheck() }
+func stack704() { var buf [704]byte; use(buf[:]); C.callGoStackCheck() }
+func stack708() { var buf [708]byte; use(buf[:]); C.callGoStackCheck() }
+func stack712() { var buf [712]byte; use(buf[:]); C.callGoStackCheck() }
+func stack716() { var buf [716]byte; use(buf[:]); C.callGoStackCheck() }
+func stack720() { var buf [720]byte; use(buf[:]); C.callGoStackCheck() }
+func stack724() { var buf [724]byte; use(buf[:]); C.callGoStackCheck() }
+func stack728() { var buf [728]byte; use(buf[:]); C.callGoStackCheck() }
+func stack732() { var buf [732]byte; use(buf[:]); C.callGoStackCheck() }
+func stack736() { var buf [736]byte; use(buf[:]); C.callGoStackCheck() }
+func stack740() { var buf [740]byte; use(buf[:]); C.callGoStackCheck() }
+func stack744() { var buf [744]byte; use(buf[:]); C.callGoStackCheck() }
+func stack748() { var buf [748]byte; use(buf[:]); C.callGoStackCheck() }
+func stack752() { var buf [752]byte; use(buf[:]); C.callGoStackCheck() }
+func stack756() { var buf [756]byte; use(buf[:]); C.callGoStackCheck() }
+func stack760() { var buf [760]byte; use(buf[:]); C.callGoStackCheck() }
+func stack764() { var buf [764]byte; use(buf[:]); C.callGoStackCheck() }
+func stack768() { var buf [768]byte; use(buf[:]); C.callGoStackCheck() }
+func stack772() { var buf [772]byte; use(buf[:]); C.callGoStackCheck() }
+func stack776() { var buf [776]byte; use(buf[:]); C.callGoStackCheck() }
+func stack780() { var buf [780]byte; use(buf[:]); C.callGoStackCheck() }
+func stack784() { var buf [784]byte; use(buf[:]); C.callGoStackCheck() }
+func stack788() { var buf [788]byte; use(buf[:]); C.callGoStackCheck() }
+func stack792() { var buf [792]byte; use(buf[:]); C.callGoStackCheck() }
+func stack796() { var buf [796]byte; use(buf[:]); C.callGoStackCheck() }
+func stack800() { var buf [800]byte; use(buf[:]); C.callGoStackCheck() }
+func stack804() { var buf [804]byte; use(buf[:]); C.callGoStackCheck() }
+func stack808() { var buf [808]byte; use(buf[:]); C.callGoStackCheck() }
+func stack812() { var buf [812]byte; use(buf[:]); C.callGoStackCheck() }
+func stack816() { var buf [816]byte; use(buf[:]); C.callGoStackCheck() }
+func stack820() { var buf [820]byte; use(buf[:]); C.callGoStackCheck() }
+func stack824() { var buf [824]byte; use(buf[:]); C.callGoStackCheck() }
+func stack828() { var buf [828]byte; use(buf[:]); C.callGoStackCheck() }
+func stack832() { var buf [832]byte; use(buf[:]); C.callGoStackCheck() }
+func stack836() { var buf [836]byte; use(buf[:]); C.callGoStackCheck() }
+func stack840() { var buf [840]byte; use(buf[:]); C.callGoStackCheck() }
+func stack844() { var buf [844]byte; use(buf[:]); C.callGoStackCheck() }
+func stack848() { var buf [848]byte; use(buf[:]); C.callGoStackCheck() }
+func stack852() { var buf [852]byte; use(buf[:]); C.callGoStackCheck() }
+func stack856() { var buf [856]byte; use(buf[:]); C.callGoStackCheck() }
+func stack860() { var buf [860]byte; use(buf[:]); C.callGoStackCheck() }
+func stack864() { var buf [864]byte; use(buf[:]); C.callGoStackCheck() }
+func stack868() { var buf [868]byte; use(buf[:]); C.callGoStackCheck() }
+func stack872() { var buf [872]byte; use(buf[:]); C.callGoStackCheck() }
+func stack876() { var buf [876]byte; use(buf[:]); C.callGoStackCheck() }
+func stack880() { var buf [880]byte; use(buf[:]); C.callGoStackCheck() }
+func stack884() { var buf [884]byte; use(buf[:]); C.callGoStackCheck() }
+func stack888() { var buf [888]byte; use(buf[:]); C.callGoStackCheck() }
+func stack892() { var buf [892]byte; use(buf[:]); C.callGoStackCheck() }
+func stack896() { var buf [896]byte; use(buf[:]); C.callGoStackCheck() }
+func stack900() { var buf [900]byte; use(buf[:]); C.callGoStackCheck() }
+func stack904() { var buf [904]byte; use(buf[:]); C.callGoStackCheck() }
+func stack908() { var buf [908]byte; use(buf[:]); C.callGoStackCheck() }
+func stack912() { var buf [912]byte; use(buf[:]); C.callGoStackCheck() }
+func stack916() { var buf [916]byte; use(buf[:]); C.callGoStackCheck() }
+func stack920() { var buf [920]byte; use(buf[:]); C.callGoStackCheck() }
+func stack924() { var buf [924]byte; use(buf[:]); C.callGoStackCheck() }
+func stack928() { var buf [928]byte; use(buf[:]); C.callGoStackCheck() }
+func stack932() { var buf [932]byte; use(buf[:]); C.callGoStackCheck() }
+func stack936() { var buf [936]byte; use(buf[:]); C.callGoStackCheck() }
+func stack940() { var buf [940]byte; use(buf[:]); C.callGoStackCheck() }
+func stack944() { var buf [944]byte; use(buf[:]); C.callGoStackCheck() }
+func stack948() { var buf [948]byte; use(buf[:]); C.callGoStackCheck() }
+func stack952() { var buf [952]byte; use(buf[:]); C.callGoStackCheck() }
+func stack956() { var buf [956]byte; use(buf[:]); C.callGoStackCheck() }
+func stack960() { var buf [960]byte; use(buf[:]); C.callGoStackCheck() }
+func stack964() { var buf [964]byte; use(buf[:]); C.callGoStackCheck() }
+func stack968() { var buf [968]byte; use(buf[:]); C.callGoStackCheck() }
+func stack972() { var buf [972]byte; use(buf[:]); C.callGoStackCheck() }
+func stack976() { var buf [976]byte; use(buf[:]); C.callGoStackCheck() }
+func stack980() { var buf [980]byte; use(buf[:]); C.callGoStackCheck() }
+func stack984() { var buf [984]byte; use(buf[:]); C.callGoStackCheck() }
+func stack988() { var buf [988]byte; use(buf[:]); C.callGoStackCheck() }
+func stack992() { var buf [992]byte; use(buf[:]); C.callGoStackCheck() }
+func stack996() { var buf [996]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1000() { var buf [1000]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1004() { var buf [1004]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1008() { var buf [1008]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1012() { var buf [1012]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1016() { var buf [1016]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1020() { var buf [1020]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1024() { var buf [1024]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1028() { var buf [1028]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1032() { var buf [1032]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1036() { var buf [1036]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1040() { var buf [1040]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1044() { var buf [1044]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1048() { var buf [1048]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1052() { var buf [1052]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1056() { var buf [1056]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1060() { var buf [1060]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1064() { var buf [1064]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1068() { var buf [1068]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1072() { var buf [1072]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1076() { var buf [1076]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1080() { var buf [1080]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1084() { var buf [1084]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1088() { var buf [1088]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1092() { var buf [1092]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1096() { var buf [1096]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1100() { var buf [1100]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1104() { var buf [1104]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1108() { var buf [1108]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1112() { var buf [1112]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1116() { var buf [1116]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1120() { var buf [1120]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1124() { var buf [1124]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1128() { var buf [1128]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1132() { var buf [1132]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1136() { var buf [1136]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1140() { var buf [1140]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1144() { var buf [1144]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1148() { var buf [1148]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1152() { var buf [1152]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1156() { var buf [1156]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1160() { var buf [1160]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1164() { var buf [1164]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1168() { var buf [1168]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1172() { var buf [1172]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1176() { var buf [1176]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1180() { var buf [1180]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1184() { var buf [1184]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1188() { var buf [1188]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1192() { var buf [1192]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1196() { var buf [1196]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1200() { var buf [1200]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1204() { var buf [1204]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1208() { var buf [1208]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1212() { var buf [1212]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1216() { var buf [1216]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1220() { var buf [1220]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1224() { var buf [1224]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1228() { var buf [1228]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1232() { var buf [1232]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1236() { var buf [1236]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1240() { var buf [1240]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1244() { var buf [1244]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1248() { var buf [1248]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1252() { var buf [1252]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1256() { var buf [1256]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1260() { var buf [1260]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1264() { var buf [1264]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1268() { var buf [1268]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1272() { var buf [1272]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1276() { var buf [1276]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1280() { var buf [1280]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1284() { var buf [1284]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1288() { var buf [1288]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1292() { var buf [1292]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1296() { var buf [1296]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1300() { var buf [1300]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1304() { var buf [1304]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1308() { var buf [1308]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1312() { var buf [1312]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1316() { var buf [1316]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1320() { var buf [1320]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1324() { var buf [1324]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1328() { var buf [1328]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1332() { var buf [1332]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1336() { var buf [1336]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1340() { var buf [1340]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1344() { var buf [1344]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1348() { var buf [1348]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1352() { var buf [1352]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1356() { var buf [1356]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1360() { var buf [1360]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1364() { var buf [1364]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1368() { var buf [1368]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1372() { var buf [1372]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1376() { var buf [1376]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1380() { var buf [1380]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1384() { var buf [1384]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1388() { var buf [1388]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1392() { var buf [1392]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1396() { var buf [1396]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1400() { var buf [1400]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1404() { var buf [1404]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1408() { var buf [1408]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1412() { var buf [1412]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1416() { var buf [1416]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1420() { var buf [1420]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1424() { var buf [1424]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1428() { var buf [1428]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1432() { var buf [1432]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1436() { var buf [1436]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1440() { var buf [1440]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1444() { var buf [1444]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1448() { var buf [1448]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1452() { var buf [1452]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1456() { var buf [1456]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1460() { var buf [1460]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1464() { var buf [1464]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1468() { var buf [1468]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1472() { var buf [1472]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1476() { var buf [1476]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1480() { var buf [1480]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1484() { var buf [1484]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1488() { var buf [1488]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1492() { var buf [1492]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1496() { var buf [1496]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1500() { var buf [1500]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1504() { var buf [1504]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1508() { var buf [1508]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1512() { var buf [1512]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1516() { var buf [1516]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1520() { var buf [1520]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1524() { var buf [1524]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1528() { var buf [1528]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1532() { var buf [1532]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1536() { var buf [1536]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1540() { var buf [1540]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1544() { var buf [1544]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1548() { var buf [1548]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1552() { var buf [1552]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1556() { var buf [1556]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1560() { var buf [1560]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1564() { var buf [1564]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1568() { var buf [1568]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1572() { var buf [1572]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1576() { var buf [1576]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1580() { var buf [1580]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1584() { var buf [1584]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1588() { var buf [1588]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1592() { var buf [1592]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1596() { var buf [1596]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1600() { var buf [1600]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1604() { var buf [1604]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1608() { var buf [1608]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1612() { var buf [1612]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1616() { var buf [1616]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1620() { var buf [1620]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1624() { var buf [1624]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1628() { var buf [1628]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1632() { var buf [1632]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1636() { var buf [1636]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1640() { var buf [1640]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1644() { var buf [1644]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1648() { var buf [1648]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1652() { var buf [1652]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1656() { var buf [1656]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1660() { var buf [1660]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1664() { var buf [1664]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1668() { var buf [1668]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1672() { var buf [1672]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1676() { var buf [1676]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1680() { var buf [1680]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1684() { var buf [1684]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1688() { var buf [1688]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1692() { var buf [1692]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1696() { var buf [1696]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1700() { var buf [1700]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1704() { var buf [1704]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1708() { var buf [1708]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1712() { var buf [1712]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1716() { var buf [1716]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1720() { var buf [1720]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1724() { var buf [1724]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1728() { var buf [1728]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1732() { var buf [1732]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1736() { var buf [1736]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1740() { var buf [1740]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1744() { var buf [1744]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1748() { var buf [1748]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1752() { var buf [1752]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1756() { var buf [1756]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1760() { var buf [1760]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1764() { var buf [1764]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1768() { var buf [1768]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1772() { var buf [1772]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1776() { var buf [1776]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1780() { var buf [1780]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1784() { var buf [1784]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1788() { var buf [1788]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1792() { var buf [1792]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1796() { var buf [1796]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1800() { var buf [1800]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1804() { var buf [1804]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1808() { var buf [1808]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1812() { var buf [1812]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1816() { var buf [1816]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1820() { var buf [1820]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1824() { var buf [1824]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1828() { var buf [1828]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1832() { var buf [1832]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1836() { var buf [1836]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1840() { var buf [1840]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1844() { var buf [1844]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1848() { var buf [1848]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1852() { var buf [1852]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1856() { var buf [1856]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1860() { var buf [1860]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1864() { var buf [1864]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1868() { var buf [1868]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1872() { var buf [1872]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1876() { var buf [1876]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1880() { var buf [1880]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1884() { var buf [1884]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1888() { var buf [1888]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1892() { var buf [1892]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1896() { var buf [1896]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1900() { var buf [1900]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1904() { var buf [1904]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1908() { var buf [1908]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1912() { var buf [1912]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1916() { var buf [1916]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1920() { var buf [1920]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1924() { var buf [1924]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1928() { var buf [1928]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1932() { var buf [1932]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1936() { var buf [1936]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1940() { var buf [1940]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1944() { var buf [1944]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1948() { var buf [1948]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1952() { var buf [1952]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1956() { var buf [1956]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1960() { var buf [1960]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1964() { var buf [1964]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1968() { var buf [1968]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1972() { var buf [1972]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1976() { var buf [1976]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1980() { var buf [1980]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1984() { var buf [1984]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1988() { var buf [1988]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1992() { var buf [1992]byte; use(buf[:]); C.callGoStackCheck() }
+func stack1996() { var buf [1996]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2000() { var buf [2000]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2004() { var buf [2004]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2008() { var buf [2008]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2012() { var buf [2012]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2016() { var buf [2016]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2020() { var buf [2020]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2024() { var buf [2024]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2028() { var buf [2028]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2032() { var buf [2032]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2036() { var buf [2036]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2040() { var buf [2040]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2044() { var buf [2044]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2048() { var buf [2048]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2052() { var buf [2052]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2056() { var buf [2056]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2060() { var buf [2060]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2064() { var buf [2064]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2068() { var buf [2068]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2072() { var buf [2072]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2076() { var buf [2076]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2080() { var buf [2080]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2084() { var buf [2084]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2088() { var buf [2088]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2092() { var buf [2092]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2096() { var buf [2096]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2100() { var buf [2100]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2104() { var buf [2104]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2108() { var buf [2108]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2112() { var buf [2112]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2116() { var buf [2116]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2120() { var buf [2120]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2124() { var buf [2124]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2128() { var buf [2128]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2132() { var buf [2132]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2136() { var buf [2136]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2140() { var buf [2140]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2144() { var buf [2144]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2148() { var buf [2148]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2152() { var buf [2152]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2156() { var buf [2156]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2160() { var buf [2160]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2164() { var buf [2164]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2168() { var buf [2168]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2172() { var buf [2172]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2176() { var buf [2176]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2180() { var buf [2180]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2184() { var buf [2184]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2188() { var buf [2188]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2192() { var buf [2192]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2196() { var buf [2196]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2200() { var buf [2200]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2204() { var buf [2204]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2208() { var buf [2208]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2212() { var buf [2212]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2216() { var buf [2216]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2220() { var buf [2220]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2224() { var buf [2224]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2228() { var buf [2228]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2232() { var buf [2232]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2236() { var buf [2236]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2240() { var buf [2240]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2244() { var buf [2244]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2248() { var buf [2248]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2252() { var buf [2252]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2256() { var buf [2256]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2260() { var buf [2260]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2264() { var buf [2264]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2268() { var buf [2268]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2272() { var buf [2272]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2276() { var buf [2276]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2280() { var buf [2280]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2284() { var buf [2284]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2288() { var buf [2288]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2292() { var buf [2292]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2296() { var buf [2296]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2300() { var buf [2300]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2304() { var buf [2304]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2308() { var buf [2308]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2312() { var buf [2312]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2316() { var buf [2316]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2320() { var buf [2320]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2324() { var buf [2324]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2328() { var buf [2328]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2332() { var buf [2332]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2336() { var buf [2336]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2340() { var buf [2340]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2344() { var buf [2344]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2348() { var buf [2348]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2352() { var buf [2352]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2356() { var buf [2356]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2360() { var buf [2360]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2364() { var buf [2364]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2368() { var buf [2368]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2372() { var buf [2372]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2376() { var buf [2376]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2380() { var buf [2380]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2384() { var buf [2384]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2388() { var buf [2388]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2392() { var buf [2392]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2396() { var buf [2396]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2400() { var buf [2400]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2404() { var buf [2404]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2408() { var buf [2408]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2412() { var buf [2412]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2416() { var buf [2416]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2420() { var buf [2420]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2424() { var buf [2424]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2428() { var buf [2428]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2432() { var buf [2432]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2436() { var buf [2436]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2440() { var buf [2440]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2444() { var buf [2444]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2448() { var buf [2448]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2452() { var buf [2452]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2456() { var buf [2456]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2460() { var buf [2460]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2464() { var buf [2464]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2468() { var buf [2468]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2472() { var buf [2472]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2476() { var buf [2476]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2480() { var buf [2480]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2484() { var buf [2484]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2488() { var buf [2488]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2492() { var buf [2492]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2496() { var buf [2496]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2500() { var buf [2500]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2504() { var buf [2504]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2508() { var buf [2508]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2512() { var buf [2512]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2516() { var buf [2516]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2520() { var buf [2520]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2524() { var buf [2524]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2528() { var buf [2528]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2532() { var buf [2532]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2536() { var buf [2536]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2540() { var buf [2540]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2544() { var buf [2544]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2548() { var buf [2548]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2552() { var buf [2552]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2556() { var buf [2556]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2560() { var buf [2560]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2564() { var buf [2564]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2568() { var buf [2568]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2572() { var buf [2572]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2576() { var buf [2576]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2580() { var buf [2580]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2584() { var buf [2584]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2588() { var buf [2588]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2592() { var buf [2592]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2596() { var buf [2596]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2600() { var buf [2600]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2604() { var buf [2604]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2608() { var buf [2608]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2612() { var buf [2612]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2616() { var buf [2616]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2620() { var buf [2620]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2624() { var buf [2624]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2628() { var buf [2628]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2632() { var buf [2632]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2636() { var buf [2636]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2640() { var buf [2640]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2644() { var buf [2644]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2648() { var buf [2648]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2652() { var buf [2652]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2656() { var buf [2656]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2660() { var buf [2660]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2664() { var buf [2664]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2668() { var buf [2668]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2672() { var buf [2672]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2676() { var buf [2676]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2680() { var buf [2680]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2684() { var buf [2684]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2688() { var buf [2688]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2692() { var buf [2692]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2696() { var buf [2696]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2700() { var buf [2700]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2704() { var buf [2704]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2708() { var buf [2708]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2712() { var buf [2712]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2716() { var buf [2716]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2720() { var buf [2720]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2724() { var buf [2724]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2728() { var buf [2728]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2732() { var buf [2732]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2736() { var buf [2736]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2740() { var buf [2740]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2744() { var buf [2744]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2748() { var buf [2748]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2752() { var buf [2752]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2756() { var buf [2756]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2760() { var buf [2760]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2764() { var buf [2764]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2768() { var buf [2768]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2772() { var buf [2772]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2776() { var buf [2776]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2780() { var buf [2780]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2784() { var buf [2784]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2788() { var buf [2788]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2792() { var buf [2792]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2796() { var buf [2796]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2800() { var buf [2800]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2804() { var buf [2804]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2808() { var buf [2808]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2812() { var buf [2812]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2816() { var buf [2816]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2820() { var buf [2820]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2824() { var buf [2824]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2828() { var buf [2828]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2832() { var buf [2832]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2836() { var buf [2836]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2840() { var buf [2840]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2844() { var buf [2844]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2848() { var buf [2848]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2852() { var buf [2852]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2856() { var buf [2856]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2860() { var buf [2860]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2864() { var buf [2864]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2868() { var buf [2868]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2872() { var buf [2872]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2876() { var buf [2876]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2880() { var buf [2880]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2884() { var buf [2884]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2888() { var buf [2888]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2892() { var buf [2892]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2896() { var buf [2896]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2900() { var buf [2900]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2904() { var buf [2904]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2908() { var buf [2908]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2912() { var buf [2912]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2916() { var buf [2916]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2920() { var buf [2920]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2924() { var buf [2924]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2928() { var buf [2928]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2932() { var buf [2932]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2936() { var buf [2936]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2940() { var buf [2940]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2944() { var buf [2944]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2948() { var buf [2948]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2952() { var buf [2952]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2956() { var buf [2956]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2960() { var buf [2960]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2964() { var buf [2964]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2968() { var buf [2968]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2972() { var buf [2972]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2976() { var buf [2976]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2980() { var buf [2980]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2984() { var buf [2984]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2988() { var buf [2988]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2992() { var buf [2992]byte; use(buf[:]); C.callGoStackCheck() }
+func stack2996() { var buf [2996]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3000() { var buf [3000]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3004() { var buf [3004]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3008() { var buf [3008]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3012() { var buf [3012]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3016() { var buf [3016]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3020() { var buf [3020]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3024() { var buf [3024]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3028() { var buf [3028]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3032() { var buf [3032]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3036() { var buf [3036]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3040() { var buf [3040]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3044() { var buf [3044]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3048() { var buf [3048]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3052() { var buf [3052]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3056() { var buf [3056]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3060() { var buf [3060]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3064() { var buf [3064]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3068() { var buf [3068]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3072() { var buf [3072]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3076() { var buf [3076]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3080() { var buf [3080]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3084() { var buf [3084]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3088() { var buf [3088]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3092() { var buf [3092]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3096() { var buf [3096]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3100() { var buf [3100]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3104() { var buf [3104]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3108() { var buf [3108]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3112() { var buf [3112]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3116() { var buf [3116]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3120() { var buf [3120]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3124() { var buf [3124]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3128() { var buf [3128]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3132() { var buf [3132]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3136() { var buf [3136]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3140() { var buf [3140]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3144() { var buf [3144]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3148() { var buf [3148]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3152() { var buf [3152]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3156() { var buf [3156]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3160() { var buf [3160]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3164() { var buf [3164]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3168() { var buf [3168]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3172() { var buf [3172]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3176() { var buf [3176]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3180() { var buf [3180]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3184() { var buf [3184]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3188() { var buf [3188]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3192() { var buf [3192]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3196() { var buf [3196]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3200() { var buf [3200]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3204() { var buf [3204]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3208() { var buf [3208]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3212() { var buf [3212]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3216() { var buf [3216]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3220() { var buf [3220]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3224() { var buf [3224]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3228() { var buf [3228]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3232() { var buf [3232]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3236() { var buf [3236]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3240() { var buf [3240]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3244() { var buf [3244]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3248() { var buf [3248]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3252() { var buf [3252]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3256() { var buf [3256]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3260() { var buf [3260]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3264() { var buf [3264]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3268() { var buf [3268]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3272() { var buf [3272]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3276() { var buf [3276]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3280() { var buf [3280]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3284() { var buf [3284]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3288() { var buf [3288]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3292() { var buf [3292]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3296() { var buf [3296]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3300() { var buf [3300]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3304() { var buf [3304]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3308() { var buf [3308]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3312() { var buf [3312]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3316() { var buf [3316]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3320() { var buf [3320]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3324() { var buf [3324]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3328() { var buf [3328]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3332() { var buf [3332]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3336() { var buf [3336]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3340() { var buf [3340]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3344() { var buf [3344]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3348() { var buf [3348]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3352() { var buf [3352]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3356() { var buf [3356]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3360() { var buf [3360]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3364() { var buf [3364]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3368() { var buf [3368]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3372() { var buf [3372]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3376() { var buf [3376]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3380() { var buf [3380]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3384() { var buf [3384]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3388() { var buf [3388]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3392() { var buf [3392]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3396() { var buf [3396]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3400() { var buf [3400]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3404() { var buf [3404]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3408() { var buf [3408]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3412() { var buf [3412]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3416() { var buf [3416]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3420() { var buf [3420]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3424() { var buf [3424]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3428() { var buf [3428]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3432() { var buf [3432]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3436() { var buf [3436]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3440() { var buf [3440]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3444() { var buf [3444]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3448() { var buf [3448]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3452() { var buf [3452]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3456() { var buf [3456]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3460() { var buf [3460]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3464() { var buf [3464]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3468() { var buf [3468]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3472() { var buf [3472]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3476() { var buf [3476]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3480() { var buf [3480]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3484() { var buf [3484]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3488() { var buf [3488]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3492() { var buf [3492]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3496() { var buf [3496]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3500() { var buf [3500]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3504() { var buf [3504]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3508() { var buf [3508]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3512() { var buf [3512]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3516() { var buf [3516]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3520() { var buf [3520]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3524() { var buf [3524]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3528() { var buf [3528]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3532() { var buf [3532]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3536() { var buf [3536]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3540() { var buf [3540]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3544() { var buf [3544]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3548() { var buf [3548]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3552() { var buf [3552]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3556() { var buf [3556]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3560() { var buf [3560]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3564() { var buf [3564]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3568() { var buf [3568]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3572() { var buf [3572]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3576() { var buf [3576]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3580() { var buf [3580]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3584() { var buf [3584]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3588() { var buf [3588]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3592() { var buf [3592]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3596() { var buf [3596]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3600() { var buf [3600]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3604() { var buf [3604]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3608() { var buf [3608]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3612() { var buf [3612]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3616() { var buf [3616]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3620() { var buf [3620]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3624() { var buf [3624]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3628() { var buf [3628]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3632() { var buf [3632]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3636() { var buf [3636]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3640() { var buf [3640]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3644() { var buf [3644]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3648() { var buf [3648]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3652() { var buf [3652]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3656() { var buf [3656]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3660() { var buf [3660]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3664() { var buf [3664]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3668() { var buf [3668]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3672() { var buf [3672]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3676() { var buf [3676]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3680() { var buf [3680]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3684() { var buf [3684]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3688() { var buf [3688]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3692() { var buf [3692]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3696() { var buf [3696]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3700() { var buf [3700]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3704() { var buf [3704]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3708() { var buf [3708]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3712() { var buf [3712]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3716() { var buf [3716]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3720() { var buf [3720]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3724() { var buf [3724]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3728() { var buf [3728]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3732() { var buf [3732]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3736() { var buf [3736]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3740() { var buf [3740]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3744() { var buf [3744]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3748() { var buf [3748]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3752() { var buf [3752]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3756() { var buf [3756]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3760() { var buf [3760]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3764() { var buf [3764]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3768() { var buf [3768]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3772() { var buf [3772]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3776() { var buf [3776]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3780() { var buf [3780]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3784() { var buf [3784]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3788() { var buf [3788]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3792() { var buf [3792]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3796() { var buf [3796]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3800() { var buf [3800]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3804() { var buf [3804]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3808() { var buf [3808]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3812() { var buf [3812]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3816() { var buf [3816]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3820() { var buf [3820]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3824() { var buf [3824]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3828() { var buf [3828]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3832() { var buf [3832]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3836() { var buf [3836]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3840() { var buf [3840]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3844() { var buf [3844]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3848() { var buf [3848]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3852() { var buf [3852]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3856() { var buf [3856]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3860() { var buf [3860]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3864() { var buf [3864]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3868() { var buf [3868]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3872() { var buf [3872]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3876() { var buf [3876]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3880() { var buf [3880]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3884() { var buf [3884]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3888() { var buf [3888]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3892() { var buf [3892]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3896() { var buf [3896]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3900() { var buf [3900]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3904() { var buf [3904]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3908() { var buf [3908]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3912() { var buf [3912]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3916() { var buf [3916]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3920() { var buf [3920]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3924() { var buf [3924]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3928() { var buf [3928]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3932() { var buf [3932]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3936() { var buf [3936]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3940() { var buf [3940]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3944() { var buf [3944]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3948() { var buf [3948]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3952() { var buf [3952]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3956() { var buf [3956]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3960() { var buf [3960]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3964() { var buf [3964]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3968() { var buf [3968]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3972() { var buf [3972]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3976() { var buf [3976]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3980() { var buf [3980]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3984() { var buf [3984]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3988() { var buf [3988]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3992() { var buf [3992]byte; use(buf[:]); C.callGoStackCheck() }
+func stack3996() { var buf [3996]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4000() { var buf [4000]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4004() { var buf [4004]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4008() { var buf [4008]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4012() { var buf [4012]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4016() { var buf [4016]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4020() { var buf [4020]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4024() { var buf [4024]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4028() { var buf [4028]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4032() { var buf [4032]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4036() { var buf [4036]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4040() { var buf [4040]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4044() { var buf [4044]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4048() { var buf [4048]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4052() { var buf [4052]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4056() { var buf [4056]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4060() { var buf [4060]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4064() { var buf [4064]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4068() { var buf [4068]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4072() { var buf [4072]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4076() { var buf [4076]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4080() { var buf [4080]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4084() { var buf [4084]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4088() { var buf [4088]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4092() { var buf [4092]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4096() { var buf [4096]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4100() { var buf [4100]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4104() { var buf [4104]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4108() { var buf [4108]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4112() { var buf [4112]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4116() { var buf [4116]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4120() { var buf [4120]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4124() { var buf [4124]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4128() { var buf [4128]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4132() { var buf [4132]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4136() { var buf [4136]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4140() { var buf [4140]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4144() { var buf [4144]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4148() { var buf [4148]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4152() { var buf [4152]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4156() { var buf [4156]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4160() { var buf [4160]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4164() { var buf [4164]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4168() { var buf [4168]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4172() { var buf [4172]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4176() { var buf [4176]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4180() { var buf [4180]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4184() { var buf [4184]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4188() { var buf [4188]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4192() { var buf [4192]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4196() { var buf [4196]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4200() { var buf [4200]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4204() { var buf [4204]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4208() { var buf [4208]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4212() { var buf [4212]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4216() { var buf [4216]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4220() { var buf [4220]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4224() { var buf [4224]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4228() { var buf [4228]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4232() { var buf [4232]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4236() { var buf [4236]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4240() { var buf [4240]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4244() { var buf [4244]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4248() { var buf [4248]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4252() { var buf [4252]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4256() { var buf [4256]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4260() { var buf [4260]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4264() { var buf [4264]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4268() { var buf [4268]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4272() { var buf [4272]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4276() { var buf [4276]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4280() { var buf [4280]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4284() { var buf [4284]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4288() { var buf [4288]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4292() { var buf [4292]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4296() { var buf [4296]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4300() { var buf [4300]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4304() { var buf [4304]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4308() { var buf [4308]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4312() { var buf [4312]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4316() { var buf [4316]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4320() { var buf [4320]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4324() { var buf [4324]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4328() { var buf [4328]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4332() { var buf [4332]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4336() { var buf [4336]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4340() { var buf [4340]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4344() { var buf [4344]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4348() { var buf [4348]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4352() { var buf [4352]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4356() { var buf [4356]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4360() { var buf [4360]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4364() { var buf [4364]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4368() { var buf [4368]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4372() { var buf [4372]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4376() { var buf [4376]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4380() { var buf [4380]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4384() { var buf [4384]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4388() { var buf [4388]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4392() { var buf [4392]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4396() { var buf [4396]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4400() { var buf [4400]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4404() { var buf [4404]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4408() { var buf [4408]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4412() { var buf [4412]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4416() { var buf [4416]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4420() { var buf [4420]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4424() { var buf [4424]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4428() { var buf [4428]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4432() { var buf [4432]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4436() { var buf [4436]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4440() { var buf [4440]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4444() { var buf [4444]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4448() { var buf [4448]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4452() { var buf [4452]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4456() { var buf [4456]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4460() { var buf [4460]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4464() { var buf [4464]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4468() { var buf [4468]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4472() { var buf [4472]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4476() { var buf [4476]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4480() { var buf [4480]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4484() { var buf [4484]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4488() { var buf [4488]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4492() { var buf [4492]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4496() { var buf [4496]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4500() { var buf [4500]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4504() { var buf [4504]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4508() { var buf [4508]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4512() { var buf [4512]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4516() { var buf [4516]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4520() { var buf [4520]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4524() { var buf [4524]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4528() { var buf [4528]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4532() { var buf [4532]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4536() { var buf [4536]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4540() { var buf [4540]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4544() { var buf [4544]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4548() { var buf [4548]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4552() { var buf [4552]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4556() { var buf [4556]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4560() { var buf [4560]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4564() { var buf [4564]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4568() { var buf [4568]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4572() { var buf [4572]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4576() { var buf [4576]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4580() { var buf [4580]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4584() { var buf [4584]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4588() { var buf [4588]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4592() { var buf [4592]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4596() { var buf [4596]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4600() { var buf [4600]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4604() { var buf [4604]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4608() { var buf [4608]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4612() { var buf [4612]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4616() { var buf [4616]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4620() { var buf [4620]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4624() { var buf [4624]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4628() { var buf [4628]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4632() { var buf [4632]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4636() { var buf [4636]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4640() { var buf [4640]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4644() { var buf [4644]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4648() { var buf [4648]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4652() { var buf [4652]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4656() { var buf [4656]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4660() { var buf [4660]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4664() { var buf [4664]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4668() { var buf [4668]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4672() { var buf [4672]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4676() { var buf [4676]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4680() { var buf [4680]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4684() { var buf [4684]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4688() { var buf [4688]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4692() { var buf [4692]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4696() { var buf [4696]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4700() { var buf [4700]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4704() { var buf [4704]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4708() { var buf [4708]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4712() { var buf [4712]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4716() { var buf [4716]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4720() { var buf [4720]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4724() { var buf [4724]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4728() { var buf [4728]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4732() { var buf [4732]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4736() { var buf [4736]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4740() { var buf [4740]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4744() { var buf [4744]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4748() { var buf [4748]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4752() { var buf [4752]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4756() { var buf [4756]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4760() { var buf [4760]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4764() { var buf [4764]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4768() { var buf [4768]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4772() { var buf [4772]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4776() { var buf [4776]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4780() { var buf [4780]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4784() { var buf [4784]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4788() { var buf [4788]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4792() { var buf [4792]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4796() { var buf [4796]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4800() { var buf [4800]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4804() { var buf [4804]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4808() { var buf [4808]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4812() { var buf [4812]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4816() { var buf [4816]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4820() { var buf [4820]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4824() { var buf [4824]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4828() { var buf [4828]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4832() { var buf [4832]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4836() { var buf [4836]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4840() { var buf [4840]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4844() { var buf [4844]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4848() { var buf [4848]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4852() { var buf [4852]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4856() { var buf [4856]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4860() { var buf [4860]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4864() { var buf [4864]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4868() { var buf [4868]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4872() { var buf [4872]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4876() { var buf [4876]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4880() { var buf [4880]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4884() { var buf [4884]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4888() { var buf [4888]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4892() { var buf [4892]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4896() { var buf [4896]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4900() { var buf [4900]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4904() { var buf [4904]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4908() { var buf [4908]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4912() { var buf [4912]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4916() { var buf [4916]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4920() { var buf [4920]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4924() { var buf [4924]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4928() { var buf [4928]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4932() { var buf [4932]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4936() { var buf [4936]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4940() { var buf [4940]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4944() { var buf [4944]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4948() { var buf [4948]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4952() { var buf [4952]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4956() { var buf [4956]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4960() { var buf [4960]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4964() { var buf [4964]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4968() { var buf [4968]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4972() { var buf [4972]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4976() { var buf [4976]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4980() { var buf [4980]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4984() { var buf [4984]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4988() { var buf [4988]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4992() { var buf [4992]byte; use(buf[:]); C.callGoStackCheck() }
+func stack4996() { var buf [4996]byte; use(buf[:]); C.callGoStackCheck() }
+func stack5000() { var buf [5000]byte; use(buf[:]); C.callGoStackCheck() }
diff --git a/src/cmd/cgo/internal/test/callback_c.c b/src/cmd/cgo/internal/test/callback_c.c
new file mode 100644
index 0000000..8ecf70f
--- /dev/null
+++ b/src/cmd/cgo/internal/test/callback_c.c
@@ -0,0 +1,67 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <string.h>
+
+#include "_cgo_export.h"
+
+void
+callback(void *f)
+{
+ // use some stack space
+ volatile char data[64*1024];
+
+ data[0] = 0;
+ goCallback(f);
+ data[sizeof(data)-1] = 0;
+}
+
+void
+callGoFoo(void)
+{
+ extern void goFoo(void);
+ goFoo();
+}
+
+void
+IntoC(void)
+{
+ BackIntoGo();
+}
+
+void
+Issue1560InC(void)
+{
+ Issue1560FromC();
+}
+
+void
+callGoStackCheck(void)
+{
+ extern void goStackCheck(void);
+ goStackCheck();
+}
+
+int
+returnAfterGrow(void)
+{
+ extern int goReturnVal(void);
+ goReturnVal();
+ return 123456;
+}
+
+int
+returnAfterGrowFromGo(void)
+{
+ extern int goReturnVal(void);
+ return goReturnVal();
+}
+
+void
+callGoWithString(void)
+{
+ extern void goWithString(GoString);
+ const char *str = "string passed from C to Go";
+ goWithString((GoString){str, strlen(str)});
+}
diff --git a/src/cmd/cgo/internal/test/callback_c_gc.c b/src/cmd/cgo/internal/test/callback_c_gc.c
new file mode 100644
index 0000000..c6666c2
--- /dev/null
+++ b/src/cmd/cgo/internal/test/callback_c_gc.c
@@ -0,0 +1,25 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gc
+
+#include "_cgo_export.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Test calling panic from C. This is what SWIG does. */
+
+extern void crosscall2(void (*fn)(void *, int), void *, int);
+extern void _cgo_panic(void *, int);
+extern void _cgo_allocate(void *, int);
+
+void
+callPanic(void)
+{
+ struct { const char *p; } a;
+ a.p = "panic from C";
+ crosscall2(_cgo_panic, &a, sizeof a);
+ *(int*)1 = 1;
+}
diff --git a/src/cmd/cgo/internal/test/callback_c_gccgo.c b/src/cmd/cgo/internal/test/callback_c_gccgo.c
new file mode 100644
index 0000000..91d37f0
--- /dev/null
+++ b/src/cmd/cgo/internal/test/callback_c_gccgo.c
@@ -0,0 +1,21 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gccgo
+
+#include "_cgo_export.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Test calling panic from C. This is what SWIG does. */
+
+extern void _cgo_panic(const char *);
+extern void *_cgo_allocate(size_t);
+
+void
+callPanic(void)
+{
+ _cgo_panic("panic from C");
+}
diff --git a/src/cmd/cgo/internal/test/cgo_linux_test.go b/src/cmd/cgo/internal/test/cgo_linux_test.go
new file mode 100644
index 0000000..3defc32
--- /dev/null
+++ b/src/cmd/cgo/internal/test/cgo_linux_test.go
@@ -0,0 +1,45 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+package cgotest
+
+import (
+ "os"
+ "runtime"
+ "testing"
+)
+
+func TestSetgid(t *testing.T) {
+ if runtime.GOOS == "android" {
+ t.Skip("unsupported on Android")
+ }
+ if _, err := os.Stat("/etc/alpine-release"); err == nil {
+ t.Skip("setgid is broken with musl libc - go.dev/issue/39857")
+ }
+ testSetgid(t)
+}
+
+func TestSetgidStress(t *testing.T) {
+ if runtime.GOOS == "android" {
+ t.Skip("unsupported on Android")
+ }
+ if _, err := os.Stat("/etc/alpine-release"); err == nil {
+ t.Skip("setgid is broken with musl libc - go.dev/issue/39857")
+ }
+ testSetgidStress(t)
+}
+
+func Test1435(t *testing.T) { test1435(t) }
+func Test6997(t *testing.T) { test6997(t) }
+
+func Test9400(t *testing.T) {
+ if _, err := os.Stat("/etc/alpine-release"); err == nil {
+ t.Skip("setgid is broken with musl libc - go.dev/issue/39857")
+ }
+ test9400(t)
+}
+
+func TestBuildID(t *testing.T) { testBuildID(t) }
diff --git a/src/cmd/cgo/internal/test/cgo_stubs_android_test.go b/src/cmd/cgo/internal/test/cgo_stubs_android_test.go
new file mode 100644
index 0000000..a1c2482
--- /dev/null
+++ b/src/cmd/cgo/internal/test/cgo_stubs_android_test.go
@@ -0,0 +1,12 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+import "testing"
+
+// Stubs for tests that fails to build on Android
+func test6997(t *testing.T) {}
+func test8694(t *testing.T) {}
+func testSigaltstack(t *testing.T) {}
diff --git a/src/cmd/cgo/internal/test/cgo_test.go b/src/cmd/cgo/internal/test/cgo_test.go
new file mode 100644
index 0000000..5e02888
--- /dev/null
+++ b/src/cmd/cgo/internal/test/cgo_test.go
@@ -0,0 +1,112 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+package cgotest
+
+import "testing"
+
+// The actual test functions are in non-_test.go files
+// so that they can use cgo (import "C").
+// These wrappers are here for gotest to find.
+
+func Test1328(t *testing.T) { test1328(t) }
+func Test1560(t *testing.T) { test1560(t) }
+func Test1635(t *testing.T) { test1635(t) }
+func Test3250(t *testing.T) { test3250(t) }
+func Test3729(t *testing.T) { test3729(t) }
+func Test3775(t *testing.T) { test3775(t) }
+func Test4029(t *testing.T) { test4029(t) }
+func Test4339(t *testing.T) { test4339(t) }
+func Test5227(t *testing.T) { test5227(t) }
+func Test5242(t *testing.T) { test5242(t) }
+func Test5337(t *testing.T) { test5337(t) }
+func Test5548(t *testing.T) { test5548(t) }
+func Test5603(t *testing.T) { test5603(t) }
+func Test5986(t *testing.T) { test5986(t) }
+func Test6390(t *testing.T) { test6390(t) }
+func Test6833(t *testing.T) { test6833(t) }
+func Test6907(t *testing.T) { test6907(t) }
+func Test6907Go(t *testing.T) { test6907Go(t) }
+func Test7560(t *testing.T) { test7560(t) }
+func Test7665(t *testing.T) { test7665(t) }
+func Test7978(t *testing.T) { test7978(t) }
+func Test8092(t *testing.T) { test8092(t) }
+func Test8517(t *testing.T) { test8517(t) }
+func Test8694(t *testing.T) { test8694(t) }
+func Test8756(t *testing.T) { test8756(t) }
+func Test8811(t *testing.T) { test8811(t) }
+func Test9026(t *testing.T) { test9026(t) }
+func Test9510(t *testing.T) { test9510(t) }
+func Test9557(t *testing.T) { test9557(t) }
+func Test10303(t *testing.T) { test10303(t, 10) }
+func Test11925(t *testing.T) { test11925(t) }
+func Test12030(t *testing.T) { test12030(t) }
+func Test14838(t *testing.T) { test14838(t) }
+func Test17065(t *testing.T) { test17065(t) }
+func Test17537(t *testing.T) { test17537(t) }
+func Test18126(t *testing.T) { test18126(t) }
+func Test18720(t *testing.T) { test18720(t) }
+func Test20129(t *testing.T) { test20129(t) }
+func Test20266(t *testing.T) { test20266(t) }
+func Test20369(t *testing.T) { test20369(t) }
+func Test20910(t *testing.T) { test20910(t) }
+func Test21708(t *testing.T) { test21708(t) }
+func Test21809(t *testing.T) { test21809(t) }
+func Test21897(t *testing.T) { test21897(t) }
+func Test22906(t *testing.T) { test22906(t) }
+func Test23356(t *testing.T) { test23356(t) }
+func Test24206(t *testing.T) { test24206(t) }
+func Test25143(t *testing.T) { test25143(t) }
+func Test26066(t *testing.T) { test26066(t) }
+func Test26213(t *testing.T) { test26213(t) }
+func Test27660(t *testing.T) { test27660(t) }
+func Test28896(t *testing.T) { test28896(t) }
+func Test30065(t *testing.T) { test30065(t) }
+func Test32579(t *testing.T) { test32579(t) }
+func Test31891(t *testing.T) { test31891(t) }
+func Test42018(t *testing.T) { test42018(t) }
+func Test45451(t *testing.T) { test45451(t) }
+func Test49633(t *testing.T) { test49633(t) }
+func TestAlign(t *testing.T) { testAlign(t) }
+func TestAtol(t *testing.T) { testAtol(t) }
+func TestBlocking(t *testing.T) { testBlocking(t) }
+func TestBoolAlign(t *testing.T) { testBoolAlign(t) }
+func TestCallGoWithString(t *testing.T) { testCallGoWithString(t) }
+func TestCallback(t *testing.T) { testCallback(t) }
+func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) }
+func TestCallbackGC(t *testing.T) { testCallbackGC(t) }
+func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) }
+func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) }
+func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) }
+func TestCallbackStack(t *testing.T) { testCallbackStack(t) }
+func TestCflags(t *testing.T) { testCflags(t) }
+func TestCheckConst(t *testing.T) { testCheckConst(t) }
+func TestConst(t *testing.T) { testConst(t) }
+func TestCthread(t *testing.T) { testCthread(t) }
+func TestEnum(t *testing.T) { testEnum(t) }
+func TestNamedEnum(t *testing.T) { testNamedEnum(t) }
+func TestCastToEnum(t *testing.T) { testCastToEnum(t) }
+func TestErrno(t *testing.T) { testErrno(t) }
+func TestFpVar(t *testing.T) { testFpVar(t) }
+func TestGCC68255(t *testing.T) { testGCC68255(t) }
+func TestHandle(t *testing.T) { testHandle(t) }
+func TestHelpers(t *testing.T) { testHelpers(t) }
+func TestLibgcc(t *testing.T) { testLibgcc(t) }
+func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) }
+func TestNaming(t *testing.T) { testNaming(t) }
+func TestPanicFromC(t *testing.T) { testPanicFromC(t) }
+func TestPrintf(t *testing.T) { testPrintf(t) }
+func TestReturnAfterGrow(t *testing.T) { testReturnAfterGrow(t) }
+func TestReturnAfterGrowFromGo(t *testing.T) { testReturnAfterGrowFromGo(t) }
+func TestSetEnv(t *testing.T) { testSetEnv(t) }
+func TestThreadLock(t *testing.T) { testThreadLockFunc(t) }
+func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) }
+func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) }
+
+func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
+func BenchmarkGoString(b *testing.B) { benchGoString(b) }
+func BenchmarkCGoCallback(b *testing.B) { benchCallback(b) }
+func BenchmarkCGoInCThread(b *testing.B) { benchCGoInCthread(b) }
diff --git a/src/cmd/cgo/internal/test/cgo_thread_lock.go b/src/cmd/cgo/internal/test/cgo_thread_lock.go
new file mode 100644
index 0000000..e874938
--- /dev/null
+++ b/src/cmd/cgo/internal/test/cgo_thread_lock.go
@@ -0,0 +1,57 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux
+
+package cgotest
+
+/*
+#include <unistd.h>
+#include <stdbool.h>
+#include <sys/syscall.h>
+void Gosched(void);
+static bool Ctid(void) {
+ long tid1 = syscall(SYS_gettid);
+ Gosched();
+ return tid1 == syscall(SYS_gettid);
+}
+*/
+import "C"
+
+import (
+ "runtime"
+ "testing"
+ "time"
+)
+
+//export Gosched
+func Gosched() {
+ runtime.Gosched()
+}
+
+func init() {
+ testThreadLockFunc = testThreadLock
+}
+
+func testThreadLock(t *testing.T) {
+ stop := make(chan int)
+ go func() {
+ // We need the G continue running,
+ // so the M has a chance to run this G.
+ for {
+ select {
+ case <-stop:
+ return
+ case <-time.After(time.Millisecond * 100):
+ }
+ }
+ }()
+ defer close(stop)
+
+ for i := 0; i < 1000; i++ {
+ if !C.Ctid() {
+ t.Fatalf("cgo has not locked OS thread")
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/test/cgo_unix_test.go b/src/cmd/cgo/internal/test/cgo_unix_test.go
new file mode 100644
index 0000000..5c1f9b7
--- /dev/null
+++ b/src/cmd/cgo/internal/test/cgo_unix_test.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo && !windows
+
+package cgotest
+
+import "testing"
+
+func TestSigaltstack(t *testing.T) { testSigaltstack(t) }
+func TestSigprocmask(t *testing.T) { testSigprocmask(t) }
+func Test18146(t *testing.T) { test18146(t) }
diff --git a/src/cmd/cgo/internal/test/cthread_unix.c b/src/cmd/cgo/internal/test/cthread_unix.c
new file mode 100644
index 0000000..d0da643
--- /dev/null
+++ b/src/cmd/cgo/internal/test/cthread_unix.c
@@ -0,0 +1,58 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
+
+#include <pthread.h>
+#include "_cgo_export.h"
+
+static void*
+addThread(void *p)
+{
+ int i, max;
+
+ max = *(int*)p;
+ for(i=0; i<max; i++)
+ Add(i);
+ return 0;
+}
+
+void
+doAdd(int max, int nthread)
+{
+ enum { MaxThread = 20 };
+ int i;
+ pthread_t thread_id[MaxThread];
+
+ if(nthread > MaxThread)
+ nthread = MaxThread;
+ for(i=0; i<nthread; i++)
+ pthread_create(&thread_id[i], 0, addThread, &max);
+ for(i=0; i<nthread; i++)
+ pthread_join(thread_id[i], 0);
+}
+
+static void*
+goDummyCallbackThread(void* p)
+{
+ int i, max;
+
+ max = *(int*)p;
+ for(i=0; i<max; i++)
+ goDummy();
+ return NULL;
+}
+
+int
+callGoInCThread(int max)
+{
+ pthread_t thread;
+
+ if (pthread_create(&thread, NULL, goDummyCallbackThread, (void*)(&max)) != 0)
+ return -1;
+ if (pthread_join(thread, NULL) != 0)
+ return -1;
+
+ return max;
+}
diff --git a/src/cmd/cgo/internal/test/cthread_windows.c b/src/cmd/cgo/internal/test/cthread_windows.c
new file mode 100644
index 0000000..4e52209
--- /dev/null
+++ b/src/cmd/cgo/internal/test/cthread_windows.c
@@ -0,0 +1,59 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <process.h>
+#include "_cgo_export.h"
+
+__stdcall
+static unsigned int
+addThread(void *p)
+{
+ int i, max;
+
+ max = *(int*)p;
+ for(i=0; i<max; i++)
+ Add(i);
+ return 0;
+}
+
+void
+doAdd(int max, int nthread)
+{
+ enum { MaxThread = 20 };
+ int i;
+ uintptr_t thread_id[MaxThread];
+
+ if(nthread > MaxThread)
+ nthread = MaxThread;
+ for(i=0; i<nthread; i++)
+ thread_id[i] = _beginthreadex(0, 0, addThread, &max, 0, 0);
+ for(i=0; i<nthread; i++) {
+ WaitForSingleObject((HANDLE)thread_id[i], INFINITE);
+ CloseHandle((HANDLE)thread_id[i]);
+ }
+}
+
+__stdcall
+static unsigned int
+goDummyCallbackThread(void* p)
+{
+ int i, max;
+
+ max = *(int*)p;
+ for(i=0; i<max; i++)
+ goDummy();
+ return 0;
+}
+
+int
+callGoInCThread(int max)
+{
+ uintptr_t thread_id;
+ thread_id = _beginthreadex(0, 0, goDummyCallbackThread, &max, 0, 0);
+ WaitForSingleObject((HANDLE)thread_id, INFINITE);
+ CloseHandle((HANDLE)thread_id);
+ return max;
+}
diff --git a/src/cmd/cgo/internal/test/gcc68255.go b/src/cmd/cgo/internal/test/gcc68255.go
new file mode 100644
index 0000000..f5493a9
--- /dev/null
+++ b/src/cmd/cgo/internal/test/gcc68255.go
@@ -0,0 +1,19 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+package cgotest
+
+import (
+ "testing"
+
+ "cmd/cgo/internal/test/gcc68255"
+)
+
+func testGCC68255(t *testing.T) {
+ if !gcc68255.F() {
+ t.Error("C global variable was not initialized")
+ }
+}
diff --git a/src/cmd/cgo/internal/test/gcc68255/a.go b/src/cmd/cgo/internal/test/gcc68255/a.go
new file mode 100644
index 0000000..e106dee
--- /dev/null
+++ b/src/cmd/cgo/internal/test/gcc68255/a.go
@@ -0,0 +1,17 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that it's OK to have C code that does nothing other than
+// initialize a global variable. This used to fail with gccgo.
+
+package gcc68255
+
+/*
+#include "c.h"
+*/
+import "C"
+
+func F() bool {
+ return C.v != nil
+}
diff --git a/src/cmd/cgo/internal/test/gcc68255/c.c b/src/cmd/cgo/internal/test/gcc68255/c.c
new file mode 100644
index 0000000..a4fe193
--- /dev/null
+++ b/src/cmd/cgo/internal/test/gcc68255/c.c
@@ -0,0 +1,8 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+static void f(void) {
+}
+
+void (*v)(void) = f;
diff --git a/src/cmd/cgo/internal/test/gcc68255/c.h b/src/cmd/cgo/internal/test/gcc68255/c.h
new file mode 100644
index 0000000..05ecd81
--- /dev/null
+++ b/src/cmd/cgo/internal/test/gcc68255/c.h
@@ -0,0 +1,5 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+extern void (*v)(void);
diff --git a/src/cmd/cgo/internal/test/issue1435.go b/src/cmd/cgo/internal/test/issue1435.go
new file mode 100644
index 0000000..a672e26
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue1435.go
@@ -0,0 +1,203 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && cgo
+
+package cgotest
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+ "sort"
+ "strings"
+ "syscall"
+ "testing"
+)
+
+// #include <stdio.h>
+// #include <stdlib.h>
+// #include <pthread.h>
+// #include <unistd.h>
+// #include <sys/types.h>
+//
+// pthread_t *t = NULL;
+// pthread_mutex_t mu;
+// int nts = 0;
+// int all_done = 0;
+//
+// static void *aFn(void *vargp) {
+// int done = 0;
+// while (!done) {
+// usleep(100);
+// pthread_mutex_lock(&mu);
+// done = all_done;
+// pthread_mutex_unlock(&mu);
+// }
+// return NULL;
+// }
+//
+// void trial(int argc) {
+// int i;
+// nts = argc;
+// t = calloc(nts, sizeof(pthread_t));
+// pthread_mutex_init(&mu, NULL);
+// for (i = 0; i < nts; i++) {
+// pthread_create(&t[i], NULL, aFn, NULL);
+// }
+// }
+//
+// void cleanup(void) {
+// int i;
+// pthread_mutex_lock(&mu);
+// all_done = 1;
+// pthread_mutex_unlock(&mu);
+// for (i = 0; i < nts; i++) {
+// pthread_join(t[i], NULL);
+// }
+// pthread_mutex_destroy(&mu);
+// free(t);
+// }
+import "C"
+
+// compareStatus is used to confirm the contents of the thread
+// specific status files match expectations.
+func compareStatus(filter, expect string) error {
+ expected := filter + expect
+ pid := syscall.Getpid()
+ fs, err := os.ReadDir(fmt.Sprintf("/proc/%d/task", pid))
+ if err != nil {
+ return fmt.Errorf("unable to find %d tasks: %v", pid, err)
+ }
+ expectedProc := fmt.Sprintf("Pid:\t%d", pid)
+ foundAThread := false
+ for _, f := range fs {
+ tf := fmt.Sprintf("/proc/%s/status", f.Name())
+ d, err := os.ReadFile(tf)
+ if err != nil {
+ // There are a surprising number of ways this
+ // can error out on linux. We've seen all of
+ // the following, so treat any error here as
+ // equivalent to the "process is gone":
+ // os.IsNotExist(err),
+ // "... : no such process",
+ // "... : bad file descriptor.
+ continue
+ }
+ lines := strings.Split(string(d), "\n")
+ for _, line := range lines {
+ // Different kernel vintages pad differently.
+ line = strings.TrimSpace(line)
+ if strings.HasPrefix(line, "Pid:\t") {
+ // On loaded systems, it is possible
+ // for a TID to be reused really
+ // quickly. As such, we need to
+ // validate that the thread status
+ // info we just read is a task of the
+ // same process PID as we are
+ // currently running, and not a
+ // recently terminated thread
+ // resurfaced in a different process.
+ if line != expectedProc {
+ break
+ }
+ // Fall through in the unlikely case
+ // that filter at some point is
+ // "Pid:\t".
+ }
+ if strings.HasPrefix(line, filter) {
+ if line == expected {
+ foundAThread = true
+ break
+ }
+ if filter == "Groups:" && strings.HasPrefix(line, "Groups:\t") {
+ // https://github.com/golang/go/issues/46145
+ // Containers don't reliably output this line in sorted order so manually sort and compare that.
+ a := strings.Split(line[8:], " ")
+ sort.Strings(a)
+ got := strings.Join(a, " ")
+ if got == expected[8:] {
+ foundAThread = true
+ break
+ }
+
+ }
+ return fmt.Errorf("%q got:%q want:%q (bad) [pid=%d file:'%s' %v]\n", tf, line, expected, pid, string(d), expectedProc)
+ }
+ }
+ }
+ if !foundAThread {
+ return fmt.Errorf("found no thread /proc/<TID>/status files for process %q", expectedProc)
+ }
+ return nil
+}
+
+// test1435 test 9 glibc implemented setuid/gid syscall functions are
+// mapped. This test is a slightly more expansive test than that of
+// src/syscall/syscall_linux_test.go:TestSetuidEtc() insofar as it
+// launches concurrent threads from C code via CGo and validates that
+// they are subject to the system calls being tested. For the actual
+// Go functionality being tested here, the syscall_linux_test version
+// is considered authoritative, but non-trivial improvements to that
+// should be mirrored here.
+func test1435(t *testing.T) {
+ if syscall.Getuid() != 0 {
+ t.Skip("skipping root only test")
+ }
+ if runtime.GOOS == "linux" {
+ if _, err := os.Stat("/etc/alpine-release"); err == nil {
+ t.Skip("skipping failing test on alpine - go.dev/issue/19938")
+ }
+ }
+
+ // Launch some threads in C.
+ const cts = 5
+ C.trial(cts)
+ defer C.cleanup()
+
+ vs := []struct {
+ call string
+ fn func() error
+ filter, expect string
+ }{
+ {call: "Setegid(1)", fn: func() error { return syscall.Setegid(1) }, filter: "Gid:", expect: "\t0\t1\t0\t1"},
+ {call: "Setegid(0)", fn: func() error { return syscall.Setegid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
+
+ {call: "Seteuid(1)", fn: func() error { return syscall.Seteuid(1) }, filter: "Uid:", expect: "\t0\t1\t0\t1"},
+ {call: "Setuid(0)", fn: func() error { return syscall.Setuid(0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
+
+ {call: "Setgid(1)", fn: func() error { return syscall.Setgid(1) }, filter: "Gid:", expect: "\t1\t1\t1\t1"},
+ {call: "Setgid(0)", fn: func() error { return syscall.Setgid(0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
+
+ {call: "Setgroups([]int{0,1,2,3})", fn: func() error { return syscall.Setgroups([]int{0, 1, 2, 3}) }, filter: "Groups:", expect: "\t0 1 2 3"},
+ {call: "Setgroups(nil)", fn: func() error { return syscall.Setgroups(nil) }, filter: "Groups:", expect: ""},
+ {call: "Setgroups([]int{0})", fn: func() error { return syscall.Setgroups([]int{0}) }, filter: "Groups:", expect: "\t0"},
+
+ {call: "Setregid(101,0)", fn: func() error { return syscall.Setregid(101, 0) }, filter: "Gid:", expect: "\t101\t0\t0\t0"},
+ {call: "Setregid(0,102)", fn: func() error { return syscall.Setregid(0, 102) }, filter: "Gid:", expect: "\t0\t102\t102\t102"},
+ {call: "Setregid(0,0)", fn: func() error { return syscall.Setregid(0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
+
+ {call: "Setreuid(1,0)", fn: func() error { return syscall.Setreuid(1, 0) }, filter: "Uid:", expect: "\t1\t0\t0\t0"},
+ {call: "Setreuid(0,2)", fn: func() error { return syscall.Setreuid(0, 2) }, filter: "Uid:", expect: "\t0\t2\t2\t2"},
+ {call: "Setreuid(0,0)", fn: func() error { return syscall.Setreuid(0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
+
+ {call: "Setresgid(101,0,102)", fn: func() error { return syscall.Setresgid(101, 0, 102) }, filter: "Gid:", expect: "\t101\t0\t102\t0"},
+ {call: "Setresgid(0,102,101)", fn: func() error { return syscall.Setresgid(0, 102, 101) }, filter: "Gid:", expect: "\t0\t102\t101\t102"},
+ {call: "Setresgid(0,0,0)", fn: func() error { return syscall.Setresgid(0, 0, 0) }, filter: "Gid:", expect: "\t0\t0\t0\t0"},
+
+ {call: "Setresuid(1,0,2)", fn: func() error { return syscall.Setresuid(1, 0, 2) }, filter: "Uid:", expect: "\t1\t0\t2\t0"},
+ {call: "Setresuid(0,2,1)", fn: func() error { return syscall.Setresuid(0, 2, 1) }, filter: "Uid:", expect: "\t0\t2\t1\t2"},
+ {call: "Setresuid(0,0,0)", fn: func() error { return syscall.Setresuid(0, 0, 0) }, filter: "Uid:", expect: "\t0\t0\t0\t0"},
+ }
+
+ for i, v := range vs {
+ if err := v.fn(); err != nil {
+ t.Errorf("[%d] %q failed: %v", i, v.call, err)
+ continue
+ }
+ if err := compareStatus(v.filter, v.expect); err != nil {
+ t.Errorf("[%d] %q comparison: %v", i, v.call, err)
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue18146.go b/src/cmd/cgo/internal/test/issue18146.go
new file mode 100644
index 0000000..d302bd0
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue18146.go
@@ -0,0 +1,128 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo && !windows
+
+// Issue 18146: pthread_create failure during syscall.Exec.
+
+package cgotest
+
+import (
+ "bytes"
+ "crypto/md5"
+ "os"
+ "os/exec"
+ "runtime"
+ "syscall"
+ "testing"
+ "time"
+)
+
+func test18146(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping in short mode")
+ }
+
+ if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
+ t.Skipf("skipping flaky test on %s; see golang.org/issue/18202", runtime.GOOS)
+ }
+
+ if runtime.GOARCH == "mips" || runtime.GOARCH == "mips64" {
+ t.Skipf("skipping on %s", runtime.GOARCH)
+ }
+
+ attempts := 1000
+ threads := 4
+
+ // Restrict the number of attempts based on RLIMIT_NPROC.
+ // Tediously, RLIMIT_NPROC was left out of the syscall package,
+ // probably because it is not in POSIX.1, so we define it here.
+ // It is not defined on Solaris.
+ var nproc int
+ setNproc := true
+ switch runtime.GOOS {
+ default:
+ setNproc = false
+ case "aix":
+ nproc = 9
+ case "linux":
+ nproc = 6
+ case "darwin", "dragonfly", "freebsd", "netbsd", "openbsd":
+ nproc = 7
+ }
+ if setNproc {
+ var rlim syscall.Rlimit
+ if syscall.Getrlimit(nproc, &rlim) == nil {
+ max := int(rlim.Cur) / (threads + 5)
+ if attempts > max {
+ t.Logf("lowering attempts from %d to %d for RLIMIT_NPROC", attempts, max)
+ attempts = max
+ }
+ }
+ }
+
+ if os.Getenv("test18146") == "exec" {
+ runtime.GOMAXPROCS(1)
+ for n := threads; n > 0; n-- {
+ go func() {
+ for {
+ _ = md5.Sum([]byte("Hello, ï €!"))
+ }
+ }()
+ }
+ runtime.GOMAXPROCS(threads)
+ argv := append(os.Args, "-test.run=NoSuchTestExists")
+ if err := syscall.Exec(os.Args[0], argv, os.Environ()); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ var cmds []*exec.Cmd
+ defer func() {
+ for _, cmd := range cmds {
+ cmd.Process.Kill()
+ }
+ }()
+
+ args := append(append([]string(nil), os.Args[1:]...), "-test.run=Test18146")
+ for n := attempts; n > 0; n-- {
+ cmd := exec.Command(os.Args[0], args...)
+ cmd.Env = append(os.Environ(), "test18146=exec")
+ buf := bytes.NewBuffer(nil)
+ cmd.Stdout = buf
+ cmd.Stderr = buf
+ if err := cmd.Start(); err != nil {
+ // We are starting so many processes that on
+ // some systems (problem seen on Darwin,
+ // Dragonfly, OpenBSD) the fork call will fail
+ // with EAGAIN.
+ if pe, ok := err.(*os.PathError); ok {
+ err = pe.Err
+ }
+ if se, ok := err.(syscall.Errno); ok && (se == syscall.EAGAIN || se == syscall.EMFILE) {
+ time.Sleep(time.Millisecond)
+ continue
+ }
+
+ t.Error(err)
+ return
+ }
+ cmds = append(cmds, cmd)
+ }
+
+ failures := 0
+ for _, cmd := range cmds {
+ err := cmd.Wait()
+ if err == nil {
+ continue
+ }
+
+ t.Errorf("syscall.Exec failed: %v\n%s", err, cmd.Stdout)
+ failures++
+ }
+
+ if failures > 0 {
+ t.Logf("Failed %v of %v attempts.", failures, len(cmds))
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue20266.go b/src/cmd/cgo/internal/test/issue20266.go
new file mode 100644
index 0000000..9f95086
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue20266.go
@@ -0,0 +1,21 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 20266: use -I with a relative path.
+
+package cgotest
+
+/*
+#cgo CFLAGS: -I issue20266 -Iissue20266 -Ddef20266
+#include "issue20266.h"
+*/
+import "C"
+
+import "testing"
+
+func test20266(t *testing.T) {
+ if got, want := C.issue20266, 20266; got != want {
+ t.Errorf("got %d, want %d", got, want)
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue20266/issue20266.h b/src/cmd/cgo/internal/test/issue20266/issue20266.h
new file mode 100644
index 0000000..8d3258e
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue20266/issue20266.h
@@ -0,0 +1,9 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#define issue20266 20266
+
+#ifndef def20266
+#error "expected def20266 to be defined"
+#endif
diff --git a/src/cmd/cgo/internal/test/issue20910.c b/src/cmd/cgo/internal/test/issue20910.c
new file mode 100644
index 0000000..e8d623f
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue20910.c
@@ -0,0 +1,19 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "_cgo_export.h"
+
+/* Test calling a Go function with multiple return values. */
+
+void
+callMulti(void)
+{
+ struct multi_return result = multi();
+ assert(strcmp(result.r0, "multi") == 0);
+ assert(result.r1 == 0);
+ free(result.r0);
+}
diff --git a/src/cmd/cgo/internal/test/issue21897.go b/src/cmd/cgo/internal/test/issue21897.go
new file mode 100644
index 0000000..cd3600a
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue21897.go
@@ -0,0 +1,56 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin && cgo && !internal
+
+package cgotest
+
+/*
+#cgo LDFLAGS: -framework CoreFoundation
+#include <CoreFoundation/CoreFoundation.h>
+*/
+import "C"
+import (
+ "runtime/debug"
+ "testing"
+ "unsafe"
+)
+
+func test21897(t *testing.T) {
+ // Please write barrier, kick in soon.
+ defer debug.SetGCPercent(debug.SetGCPercent(1))
+
+ for i := 0; i < 10000; i++ {
+ testCFNumberRef()
+ testCFDateRef()
+ testCFBooleanRef()
+ // Allocate some memory, so eventually the write barrier is enabled
+ // and it will see writes of bad pointers in the test* functions below.
+ byteSliceSink = make([]byte, 1024)
+ }
+}
+
+var byteSliceSink []byte
+
+func testCFNumberRef() {
+ var v int64 = 0
+ xCFNumberRef = C.CFNumberCreate(C.kCFAllocatorSystemDefault, C.kCFNumberSInt64Type, unsafe.Pointer(&v))
+ //fmt.Printf("CFNumberRef: %x\n", uintptr(unsafe.Pointer(xCFNumberRef)))
+}
+
+var xCFNumberRef C.CFNumberRef
+
+func testCFDateRef() {
+ xCFDateRef = C.CFDateCreate(C.kCFAllocatorSystemDefault, 0) // 0 value is 1 Jan 2001 00:00:00 GMT
+ //fmt.Printf("CFDateRef: %x\n", uintptr(unsafe.Pointer(xCFDateRef)))
+}
+
+var xCFDateRef C.CFDateRef
+
+func testCFBooleanRef() {
+ xCFBooleanRef = C.kCFBooleanFalse
+ //fmt.Printf("CFBooleanRef: %x\n", uintptr(unsafe.Pointer(xCFBooleanRef)))
+}
+
+var xCFBooleanRef C.CFBooleanRef
diff --git a/src/cmd/cgo/internal/test/issue21897b.go b/src/cmd/cgo/internal/test/issue21897b.go
new file mode 100644
index 0000000..e12564c
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue21897b.go
@@ -0,0 +1,13 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !darwin || !cgo || internal
+
+package cgotest
+
+import "testing"
+
+func test21897(t *testing.T) {
+ t.Skip("test runs only on darwin+cgo")
+}
diff --git a/src/cmd/cgo/internal/test/issue23555.go b/src/cmd/cgo/internal/test/issue23555.go
new file mode 100644
index 0000000..1232148
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue23555.go
@@ -0,0 +1,15 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+// Test that we can have two identical cgo packages in a single binary.
+// No runtime test; just make sure it compiles.
+
+package cgotest
+
+import (
+ _ "cmd/cgo/internal/test/issue23555a"
+ _ "cmd/cgo/internal/test/issue23555b"
+)
diff --git a/src/cmd/cgo/internal/test/issue23555a/a.go b/src/cmd/cgo/internal/test/issue23555a/a.go
new file mode 100644
index 0000000..cb6626b
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue23555a/a.go
@@ -0,0 +1,12 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue23555
+
+// #include <stdlib.h>
+import "C"
+
+func X() {
+ C.free(C.malloc(10))
+}
diff --git a/src/cmd/cgo/internal/test/issue23555b/a.go b/src/cmd/cgo/internal/test/issue23555b/a.go
new file mode 100644
index 0000000..cb6626b
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue23555b/a.go
@@ -0,0 +1,12 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue23555
+
+// #include <stdlib.h>
+import "C"
+
+func X() {
+ C.free(C.malloc(10))
+}
diff --git a/src/cmd/cgo/internal/test/issue24161_darwin_test.go b/src/cmd/cgo/internal/test/issue24161_darwin_test.go
new file mode 100644
index 0000000..9d08751
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue24161_darwin_test.go
@@ -0,0 +1,33 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+package cgotest
+
+import (
+ "testing"
+
+ "cmd/cgo/internal/test/issue24161arg"
+ "cmd/cgo/internal/test/issue24161e0"
+ "cmd/cgo/internal/test/issue24161e1"
+ "cmd/cgo/internal/test/issue24161e2"
+ "cmd/cgo/internal/test/issue24161res"
+)
+
+func Test24161Arg(t *testing.T) {
+ issue24161arg.Test(t)
+}
+func Test24161Res(t *testing.T) {
+ issue24161res.Test(t)
+}
+func Test24161Example0(t *testing.T) {
+ issue24161e0.Test(t)
+}
+func Test24161Example1(t *testing.T) {
+ issue24161e1.Test(t)
+}
+func Test24161Example2(t *testing.T) {
+ issue24161e2.Test(t)
+}
diff --git a/src/cmd/cgo/internal/test/issue24161arg/def.go b/src/cmd/cgo/internal/test/issue24161arg/def.go
new file mode 100644
index 0000000..acea3ae
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue24161arg/def.go
@@ -0,0 +1,17 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin
+
+package issue24161arg
+
+/*
+#cgo LDFLAGS: -framework CoreFoundation
+#include <CoreFoundation/CoreFoundation.h>
+*/
+import "C"
+
+func test24161array() C.CFArrayRef {
+ return C.CFArrayCreate(0, nil, 0, nil)
+}
diff --git a/src/cmd/cgo/internal/test/issue24161arg/use.go b/src/cmd/cgo/internal/test/issue24161arg/use.go
new file mode 100644
index 0000000..7987105
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue24161arg/use.go
@@ -0,0 +1,19 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin
+
+package issue24161arg
+
+/*
+#cgo LDFLAGS: -framework CoreFoundation
+#include <CoreFoundation/CoreFoundation.h>
+*/
+import "C"
+import "testing"
+
+func Test(t *testing.T) {
+ a := test24161array()
+ C.CFArrayCreateCopy(0, a)
+}
diff --git a/src/cmd/cgo/internal/test/issue24161e0/main.go b/src/cmd/cgo/internal/test/issue24161e0/main.go
new file mode 100644
index 0000000..5912fe2
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue24161e0/main.go
@@ -0,0 +1,29 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin
+
+package issue24161e0
+
+/*
+#cgo CFLAGS: -x objective-c
+#cgo LDFLAGS: -framework CoreFoundation -framework Security
+#include <TargetConditionals.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#if TARGET_OS_IPHONE == 0 && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
+ typedef CFStringRef SecKeyAlgorithm;
+ static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;}
+ #define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo()
+ static SecKeyAlgorithm foo(void){return NULL;}
+#endif
+*/
+import "C"
+import "testing"
+
+func f1() {
+ C.SecKeyCreateSignature(0, C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 0, nil)
+}
+
+func Test(t *testing.T) {}
diff --git a/src/cmd/cgo/internal/test/issue24161e1/main.go b/src/cmd/cgo/internal/test/issue24161e1/main.go
new file mode 100644
index 0000000..8c2bc6e
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue24161e1/main.go
@@ -0,0 +1,38 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin
+
+package issue24161e1
+
+/*
+#cgo CFLAGS: -x objective-c
+#cgo LDFLAGS: -framework CoreFoundation -framework Security
+#include <TargetConditionals.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#if TARGET_OS_IPHONE == 0 && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
+ typedef CFStringRef SecKeyAlgorithm;
+ static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;}
+ #define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo()
+ static SecKeyAlgorithm foo(void){return NULL;}
+#endif
+*/
+import "C"
+import (
+ "fmt"
+ "testing"
+)
+
+func f1() {
+ C.SecKeyCreateSignature(0, C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 0, nil)
+}
+
+func f2(e C.CFErrorRef) {
+ if desc := C.CFErrorCopyDescription(e); desc != 0 {
+ fmt.Println(desc)
+ }
+}
+
+func Test(t *testing.T) {}
diff --git a/src/cmd/cgo/internal/test/issue24161e2/main.go b/src/cmd/cgo/internal/test/issue24161e2/main.go
new file mode 100644
index 0000000..159f479
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue24161e2/main.go
@@ -0,0 +1,40 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin
+
+package issue24161e2
+
+/*
+#cgo CFLAGS: -x objective-c
+#cgo LDFLAGS: -framework CoreFoundation -framework Security
+#include <TargetConditionals.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#if TARGET_OS_IPHONE == 0 && __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
+ typedef CFStringRef SecKeyAlgorithm;
+ static CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error){return NULL;}
+ #define kSecKeyAlgorithmECDSASignatureDigestX962SHA1 foo()
+ static SecKeyAlgorithm foo(void){return NULL;}
+#endif
+*/
+import "C"
+import (
+ "fmt"
+ "testing"
+)
+
+var _ C.CFStringRef
+
+func f1() {
+ C.SecKeyCreateSignature(0, C.kSecKeyAlgorithmECDSASignatureDigestX962SHA1, 0, nil)
+}
+
+func f2(e C.CFErrorRef) {
+ if desc := C.CFErrorCopyDescription(e); desc != 0 {
+ fmt.Println(desc)
+ }
+}
+
+func Test(t *testing.T) {}
diff --git a/src/cmd/cgo/internal/test/issue24161res/restype.go b/src/cmd/cgo/internal/test/issue24161res/restype.go
new file mode 100644
index 0000000..07cb98d
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue24161res/restype.go
@@ -0,0 +1,23 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin
+
+package issue24161res
+
+/*
+#cgo LDFLAGS: -framework CoreFoundation
+#include <CoreFoundation/CoreFoundation.h>
+*/
+import "C"
+import (
+ "reflect"
+ "testing"
+)
+
+func Test(t *testing.T) {
+ if k := reflect.TypeOf(C.CFArrayCreate(0, nil, 0, nil)).Kind(); k != reflect.Uintptr {
+ t.Fatalf("bad kind %s\n", k)
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue26213/jni.h b/src/cmd/cgo/internal/test/issue26213/jni.h
new file mode 100644
index 0000000..0c76979
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue26213/jni.h
@@ -0,0 +1,29 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// It's going to be hard to include a whole real JVM to test this.
+// So we'll simulate a really easy JVM using just the parts we need.
+
+// This is the relevant part of jni.h.
+
+// On Android NDK16, jobject is defined like this in C and C++
+typedef void* jobject;
+
+typedef jobject jclass;
+typedef jobject jthrowable;
+typedef jobject jstring;
+typedef jobject jarray;
+typedef jarray jbooleanArray;
+typedef jarray jbyteArray;
+typedef jarray jcharArray;
+typedef jarray jshortArray;
+typedef jarray jintArray;
+typedef jarray jlongArray;
+typedef jarray jfloatArray;
+typedef jarray jdoubleArray;
+typedef jarray jobjectArray;
+
+typedef jobject jweak;
+
+// Note: jvalue is already a non-pointer type due to it being a C union.
diff --git a/src/cmd/cgo/internal/test/issue26213/test26213.go b/src/cmd/cgo/internal/test/issue26213/test26213.go
new file mode 100644
index 0000000..5d1f637
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue26213/test26213.go
@@ -0,0 +1,46 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue26213
+
+/*
+#include "jni.h"
+*/
+import "C"
+import (
+ "testing"
+)
+
+func Test26213(t *testing.T) {
+ var x1 C.jobject = 0 // Note: 0, not nil. That makes sure we use uintptr for these types.
+ _ = x1
+ var x2 C.jclass = 0
+ _ = x2
+ var x3 C.jthrowable = 0
+ _ = x3
+ var x4 C.jstring = 0
+ _ = x4
+ var x5 C.jarray = 0
+ _ = x5
+ var x6 C.jbooleanArray = 0
+ _ = x6
+ var x7 C.jbyteArray = 0
+ _ = x7
+ var x8 C.jcharArray = 0
+ _ = x8
+ var x9 C.jshortArray = 0
+ _ = x9
+ var x10 C.jintArray = 0
+ _ = x10
+ var x11 C.jlongArray = 0
+ _ = x11
+ var x12 C.jfloatArray = 0
+ _ = x12
+ var x13 C.jdoubleArray = 0
+ _ = x13
+ var x14 C.jobjectArray = 0
+ _ = x14
+ var x15 C.jweak = 0
+ _ = x15
+}
diff --git a/src/cmd/cgo/internal/test/issue26430.go b/src/cmd/cgo/internal/test/issue26430.go
new file mode 100644
index 0000000..837a745
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue26430.go
@@ -0,0 +1,12 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+// Issue 26430: incomplete typedef leads to inconsistent typedefs error.
+// No runtime test; just make sure it compiles.
+
+package cgotest
+
+import _ "cmd/cgo/internal/test/issue26430"
diff --git a/src/cmd/cgo/internal/test/issue26430/a.go b/src/cmd/cgo/internal/test/issue26430/a.go
new file mode 100644
index 0000000..fbaa46b
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue26430/a.go
@@ -0,0 +1,13 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+// typedef struct S ST;
+// static ST* F() { return 0; }
+import "C"
+
+func F1() {
+ C.F()
+}
diff --git a/src/cmd/cgo/internal/test/issue26430/b.go b/src/cmd/cgo/internal/test/issue26430/b.go
new file mode 100644
index 0000000..a7c527c
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue26430/b.go
@@ -0,0 +1,13 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+// typedef struct S ST;
+// struct S { int f; };
+import "C"
+
+func F2(p *C.ST) {
+ p.f = 1
+}
diff --git a/src/cmd/cgo/internal/test/issue26743.go b/src/cmd/cgo/internal/test/issue26743.go
new file mode 100644
index 0000000..b6e1ac5
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue26743.go
@@ -0,0 +1,12 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+// Issue 26743: typedef of uint leads to inconsistent typedefs error.
+// No runtime test; just make sure it compiles.
+
+package cgotest
+
+import _ "cmd/cgo/internal/test/issue26743"
diff --git a/src/cmd/cgo/internal/test/issue26743/a.go b/src/cmd/cgo/internal/test/issue26743/a.go
new file mode 100644
index 0000000..a3df179
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue26743/a.go
@@ -0,0 +1,11 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue26743
+
+// typedef unsigned int uint;
+// int C1(uint x) { return x; }
+import "C"
+
+var V1 = C.C1(0)
diff --git a/src/cmd/cgo/internal/test/issue26743/b.go b/src/cmd/cgo/internal/test/issue26743/b.go
new file mode 100644
index 0000000..c5f1ae4
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue26743/b.go
@@ -0,0 +1,9 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue26743
+
+import "C"
+
+var V2 C.uint
diff --git a/src/cmd/cgo/internal/test/issue27054/egl.h b/src/cmd/cgo/internal/test/issue27054/egl.h
new file mode 100644
index 0000000..3079627
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue27054/egl.h
@@ -0,0 +1,8 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This is the relevant part of EGL/egl.h.
+
+typedef void *EGLDisplay;
+typedef void *EGLConfig;
diff --git a/src/cmd/cgo/internal/test/issue27054/test27054.go b/src/cmd/cgo/internal/test/issue27054/test27054.go
new file mode 100644
index 0000000..01bf43a
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue27054/test27054.go
@@ -0,0 +1,21 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue27054
+
+/*
+#include "egl.h"
+*/
+import "C"
+import (
+ "testing"
+)
+
+func Test27054(t *testing.T) {
+ var (
+ // Note: 0, not nil. That makes sure we use uintptr for these types.
+ _ C.EGLDisplay = 0
+ _ C.EGLConfig = 0
+ )
+}
diff --git a/src/cmd/cgo/internal/test/issue27340.go b/src/cmd/cgo/internal/test/issue27340.go
new file mode 100644
index 0000000..a6de328
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue27340.go
@@ -0,0 +1,14 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+// Failed to resolve typedefs consistently.
+// No runtime test; just make sure it compiles.
+
+package cgotest
+
+import "cmd/cgo/internal/test/issue27340"
+
+var issue27340Var = issue27340.Issue27340GoFunc
diff --git a/src/cmd/cgo/internal/test/issue27340/a.go b/src/cmd/cgo/internal/test/issue27340/a.go
new file mode 100644
index 0000000..f5b120c
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue27340/a.go
@@ -0,0 +1,42 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Failed to resolve typedefs consistently.
+// No runtime test; just make sure it compiles.
+// In separate directory to isolate #pragma GCC diagnostic.
+
+package issue27340
+
+// We use the #pragma to avoid a compiler warning about incompatible
+// pointer types, because we generate code passing a struct ptr rather
+// than using the typedef. This warning is expected and does not break
+// a normal build.
+// We can only disable -Wincompatible-pointer-types starting with GCC 5.
+
+// #if __GNU_MAJOR__ >= 5
+//
+// #pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
+//
+// typedef struct {
+// int a;
+// } issue27340Struct, *issue27340Ptr;
+//
+// static void issue27340CFunc(issue27340Ptr p) {}
+//
+// #else /* _GNU_MAJOR_ < 5 */
+//
+// typedef struct {
+// int a;
+// } issue27340Struct;
+//
+// static issue27340Struct* issue27340Ptr(issue27340Struct* p) { return p; }
+//
+// static void issue27340CFunc(issue27340Struct *p) {}
+// #endif /* _GNU_MAJOR_ < 5 */
+import "C"
+
+func Issue27340GoFunc() {
+ var s C.issue27340Struct
+ C.issue27340CFunc(C.issue27340Ptr(&s))
+}
diff --git a/src/cmd/cgo/internal/test/issue29563.go b/src/cmd/cgo/internal/test/issue29563.go
new file mode 100644
index 0000000..f507759
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue29563.go
@@ -0,0 +1,12 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo && !windows
+
+// Issue 29563: internal linker fails on duplicate weak symbols.
+// No runtime test; just make sure it compiles.
+
+package cgotest
+
+import _ "cmd/cgo/internal/test/issue29563"
diff --git a/src/cmd/cgo/internal/test/issue29563/weak.go b/src/cmd/cgo/internal/test/issue29563/weak.go
new file mode 100644
index 0000000..21cf635
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue29563/weak.go
@@ -0,0 +1,13 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue29563
+
+//int foo1();
+//int foo2();
+import "C"
+
+func Bar() int {
+ return int(C.foo1()) + int(C.foo2())
+}
diff --git a/src/cmd/cgo/internal/test/issue29563/weak1.c b/src/cmd/cgo/internal/test/issue29563/weak1.c
new file mode 100644
index 0000000..86a2273
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue29563/weak1.c
@@ -0,0 +1,11 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+extern int weaksym __attribute__((__weak__));
+int weaksym = 42;
+
+int foo1()
+{
+ return weaksym;
+}
diff --git a/src/cmd/cgo/internal/test/issue29563/weak2.c b/src/cmd/cgo/internal/test/issue29563/weak2.c
new file mode 100644
index 0000000..e01eae8
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue29563/weak2.c
@@ -0,0 +1,11 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+extern int weaksym __attribute__((__weak__));
+int weaksym = 42;
+
+int foo2()
+{
+ return weaksym;
+}
diff --git a/src/cmd/cgo/internal/test/issue30527.go b/src/cmd/cgo/internal/test/issue30527.go
new file mode 100644
index 0000000..d3e57b6
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue30527.go
@@ -0,0 +1,16 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+// Issue 30527: function call rewriting casts untyped
+// constants to int because of ":=" usage.
+
+package cgotest
+
+import "cmd/cgo/internal/test/issue30527"
+
+func issue30527G() {
+ issue30527.G(nil)
+}
diff --git a/src/cmd/cgo/internal/test/issue30527/a.go b/src/cmd/cgo/internal/test/issue30527/a.go
new file mode 100644
index 0000000..eb50147
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue30527/a.go
@@ -0,0 +1,19 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue30527
+
+import "math"
+
+/*
+#include <inttypes.h>
+
+static void issue30527F(char **p, uint64_t mod, uint32_t unused) {}
+*/
+import "C"
+
+func G(p **C.char) {
+ C.issue30527F(p, math.MaxUint64, 1)
+ C.issue30527F(p, 1<<64-1, Z)
+}
diff --git a/src/cmd/cgo/internal/test/issue30527/b.go b/src/cmd/cgo/internal/test/issue30527/b.go
new file mode 100644
index 0000000..87e8255
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue30527/b.go
@@ -0,0 +1,11 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue30527
+
+const (
+ X = 1 << iota
+ Y
+ Z
+)
diff --git a/src/cmd/cgo/internal/test/issue31891.c b/src/cmd/cgo/internal/test/issue31891.c
new file mode 100644
index 0000000..67a0dda
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue31891.c
@@ -0,0 +1,13 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "_cgo_export.h"
+
+void callIssue31891() {
+ Issue31891A a;
+ useIssue31891A(&a);
+
+ Issue31891B b;
+ useIssue31891B(&b);
+}
diff --git a/src/cmd/cgo/internal/test/issue4029.c b/src/cmd/cgo/internal/test/issue4029.c
new file mode 100644
index 0000000..212d692
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue4029.c
@@ -0,0 +1,29 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !windows && !static && (!darwin || (!internal_pie && !arm64))
+
+#include <stdint.h>
+#include <dlfcn.h>
+
+// Write our own versions of dlopen/dlsym/dlclose so that we represent
+// the opaque handle as a Go uintptr rather than a Go pointer to avoid
+// garbage collector confusion. See issue 23663.
+
+uintptr_t dlopen4029(char* name, int flags) {
+ return (uintptr_t)(dlopen(name, flags));
+}
+
+uintptr_t dlsym4029(uintptr_t handle, char* name) {
+ return (uintptr_t)(dlsym((void*)(handle), name));
+}
+
+int dlclose4029(uintptr_t handle) {
+ return dlclose((void*)(handle));
+}
+
+void call4029(void *arg) {
+ void (*fn)(void) = arg;
+ fn();
+}
diff --git a/src/cmd/cgo/internal/test/issue4029.go b/src/cmd/cgo/internal/test/issue4029.go
new file mode 100644
index 0000000..686b767
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue4029.go
@@ -0,0 +1,77 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !windows && !static && (!darwin || (!internal_pie && !arm64))
+
+// Excluded in darwin internal linking PIE mode, as dynamic export is not
+// supported.
+// Excluded in internal linking mode on darwin/arm64, as it is always PIE.
+
+package cgotest
+
+/*
+#include <stdint.h>
+#include <dlfcn.h>
+#cgo linux LDFLAGS: -ldl
+
+extern uintptr_t dlopen4029(char*, int);
+extern uintptr_t dlsym4029(uintptr_t, char*);
+extern int dlclose4029(uintptr_t);
+
+extern void call4029(uintptr_t arg);
+*/
+import "C"
+
+import (
+ "testing"
+)
+
+var callbacks int
+
+//export IMPIsOpaque
+func IMPIsOpaque() {
+ callbacks++
+}
+
+//export IMPInitWithFrame
+func IMPInitWithFrame() {
+ callbacks++
+}
+
+//export IMPDrawRect
+func IMPDrawRect() {
+ callbacks++
+}
+
+//export IMPWindowResize
+func IMPWindowResize() {
+ callbacks++
+}
+
+func test4029(t *testing.T) {
+ loadThySelf(t, "IMPWindowResize")
+ loadThySelf(t, "IMPDrawRect")
+ loadThySelf(t, "IMPInitWithFrame")
+ loadThySelf(t, "IMPIsOpaque")
+ if callbacks != 4 {
+ t.Errorf("got %d callbacks, expected 4", callbacks)
+ }
+}
+
+func loadThySelf(t *testing.T, symbol string) {
+ this_process := C.dlopen4029(nil, C.RTLD_NOW)
+ if this_process == 0 {
+ t.Error("dlopen:", C.GoString(C.dlerror()))
+ return
+ }
+ defer C.dlclose4029(this_process)
+
+ symbol_address := C.dlsym4029(this_process, C.CString(symbol))
+ if symbol_address == 0 {
+ t.Error("dlsym:", C.GoString(C.dlerror()))
+ return
+ }
+ t.Log(symbol, symbol_address)
+ C.call4029(symbol_address)
+}
diff --git a/src/cmd/cgo/internal/test/issue4029w.go b/src/cmd/cgo/internal/test/issue4029w.go
new file mode 100644
index 0000000..91dad6a
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue4029w.go
@@ -0,0 +1,12 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build windows || static || (darwin && internal_pie) || (darwin && arm64)
+
+package cgotest
+
+import "testing"
+
+func test4029(t *testing.T) {
+}
diff --git a/src/cmd/cgo/internal/test/issue41761.go b/src/cmd/cgo/internal/test/issue41761.go
new file mode 100644
index 0000000..27d9047
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue41761.go
@@ -0,0 +1,20 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+ typedef struct S41761 S41761;
+*/
+import "C"
+
+import (
+ "cmd/cgo/internal/test/issue41761a"
+ "testing"
+)
+
+func test41761(t *testing.T) {
+ var x issue41761a.T
+ _ = (*C.struct_S41761)(x.X)
+}
diff --git a/src/cmd/cgo/internal/test/issue41761a/a.go b/src/cmd/cgo/internal/test/issue41761a/a.go
new file mode 100644
index 0000000..1c52782
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue41761a/a.go
@@ -0,0 +1,14 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue41761a
+
+/*
+ typedef struct S41761 S41761;
+*/
+import "C"
+
+type T struct {
+ X *C.S41761
+}
diff --git a/src/cmd/cgo/internal/test/issue42018.go b/src/cmd/cgo/internal/test/issue42018.go
new file mode 100644
index 0000000..6b369bf
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue42018.go
@@ -0,0 +1,13 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !windows
+
+package cgotest
+
+import "testing"
+
+func test42018(t *testing.T) {
+ t.Skip("skipping Windows-only test")
+}
diff --git a/src/cmd/cgo/internal/test/issue42018_windows.go b/src/cmd/cgo/internal/test/issue42018_windows.go
new file mode 100644
index 0000000..8f4570a
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue42018_windows.go
@@ -0,0 +1,46 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+typedef void *HANDLE;
+
+struct HWND__{int unused;}; typedef struct HWND__ *HWND;
+*/
+import "C"
+
+import (
+ "testing"
+ "unsafe"
+)
+
+func test42018(t *testing.T) {
+ // Test that Windows handles are marked go:notinheap, by growing the
+ // stack and checking for pointer adjustments. Trick from
+ // test/fixedbugs/issue40954.go.
+ var i int
+ handle := C.HANDLE(unsafe.Pointer(uintptr(unsafe.Pointer(&i))))
+ recurseHANDLE(100, handle, uintptr(unsafe.Pointer(&i)))
+ hwnd := C.HWND(unsafe.Pointer(uintptr(unsafe.Pointer(&i))))
+ recurseHWND(400, hwnd, uintptr(unsafe.Pointer(&i)))
+}
+
+func recurseHANDLE(n int, p C.HANDLE, v uintptr) {
+ if n > 0 {
+ recurseHANDLE(n-1, p, v)
+ }
+ if uintptr(unsafe.Pointer(p)) != v {
+ panic("adjusted notinheap pointer")
+ }
+}
+
+func recurseHWND(n int, p C.HWND, v uintptr) {
+ if n > 0 {
+ recurseHWND(n-1, p, v)
+ }
+ if uintptr(unsafe.Pointer(p)) != v {
+ panic("adjusted notinheap pointer")
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue42495.go b/src/cmd/cgo/internal/test/issue42495.go
new file mode 100644
index 0000000..509a67d
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue42495.go
@@ -0,0 +1,15 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+// typedef struct { } T42495A;
+// typedef struct { int x[0]; } T42495B;
+import "C"
+
+//export Issue42495A
+func Issue42495A(C.T42495A) {}
+
+//export Issue42495B
+func Issue42495B(C.T42495B) {}
diff --git a/src/cmd/cgo/internal/test/issue4273.c b/src/cmd/cgo/internal/test/issue4273.c
new file mode 100644
index 0000000..cac9876
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue4273.c
@@ -0,0 +1,10 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifdef __ELF__
+__attribute__((weak))
+__attribute__((visibility("hidden")))
+void _compilerrt_abort_impl(const char *file, int line, const char *func) {
+}
+#endif
diff --git a/src/cmd/cgo/internal/test/issue4273b.c b/src/cmd/cgo/internal/test/issue4273b.c
new file mode 100644
index 0000000..71e3f0d
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue4273b.c
@@ -0,0 +1,11 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#ifdef __ELF__
+extern void _compilerrt_abort_impl(const char *file, int line, const char *func);
+
+void __my_abort(const char *file, int line, const char *func) {
+ _compilerrt_abort_impl(file, line, func);
+}
+#endif
diff --git a/src/cmd/cgo/internal/test/issue4339.c b/src/cmd/cgo/internal/test/issue4339.c
new file mode 100644
index 0000000..15d0004
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue4339.c
@@ -0,0 +1,18 @@
+#include <stdio.h>
+#include "issue4339.h"
+
+static void
+impl(void)
+{
+ //printf("impl\n");
+}
+
+Issue4339 exported4339 = {"bar", impl};
+
+void
+handle4339(Issue4339 *x)
+{
+ //printf("handle\n");
+ x->bar();
+ //printf("done\n");
+}
diff --git a/src/cmd/cgo/internal/test/issue4339.h b/src/cmd/cgo/internal/test/issue4339.h
new file mode 100644
index 0000000..20f6ceb
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue4339.h
@@ -0,0 +1,9 @@
+typedef struct Issue4339 Issue4339;
+
+struct Issue4339 {
+ char *name;
+ void (*bar)(void);
+};
+
+extern Issue4339 exported4339;
+void handle4339(Issue4339*);
diff --git a/src/cmd/cgo/internal/test/issue43639.go b/src/cmd/cgo/internal/test/issue43639.go
new file mode 100644
index 0000000..c297bfe
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue43639.go
@@ -0,0 +1,11 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+package cgotest
+
+// Issue 43639: No runtime test needed, make sure package cmd/cgo/internal/test/issue43639 compiles well.
+
+import _ "cmd/cgo/internal/test/issue43639"
diff --git a/src/cmd/cgo/internal/test/issue43639/a.go b/src/cmd/cgo/internal/test/issue43639/a.go
new file mode 100644
index 0000000..fe37d5e
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue43639/a.go
@@ -0,0 +1,8 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue43639
+
+// #cgo CFLAGS: -W -Wall -Werror
+import "C"
diff --git a/src/cmd/cgo/internal/test/issue52611.go b/src/cmd/cgo/internal/test/issue52611.go
new file mode 100644
index 0000000..9082a53
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue52611.go
@@ -0,0 +1,15 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+// Issue 52611: inconsistent compiler behaviour when compiling a C.struct.
+// No runtime test; just make sure it compiles.
+
+package cgotest
+
+import (
+ _ "cmd/cgo/internal/test/issue52611a"
+ _ "cmd/cgo/internal/test/issue52611b"
+)
diff --git a/src/cmd/cgo/internal/test/issue52611a/a.go b/src/cmd/cgo/internal/test/issue52611a/a.go
new file mode 100644
index 0000000..0764688
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue52611a/a.go
@@ -0,0 +1,16 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue52611a
+
+/*
+typedef struct Foo {
+ int X;
+} Foo;
+*/
+import "C"
+
+func GetX1(foo *C.struct_Foo) int32 {
+ return int32(foo.X)
+}
diff --git a/src/cmd/cgo/internal/test/issue52611a/b.go b/src/cmd/cgo/internal/test/issue52611a/b.go
new file mode 100644
index 0000000..74a50c5
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue52611a/b.go
@@ -0,0 +1,11 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue52611a
+
+import "C"
+
+func GetX2(foo *C.struct_Foo) int32 {
+ return int32(foo.X)
+}
diff --git a/src/cmd/cgo/internal/test/issue52611b/a.go b/src/cmd/cgo/internal/test/issue52611b/a.go
new file mode 100644
index 0000000..730b52f
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue52611b/a.go
@@ -0,0 +1,11 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue52611b
+
+import "C"
+
+func GetX1(bar *C.struct_Bar) int32 {
+ return int32(bar.X)
+}
diff --git a/src/cmd/cgo/internal/test/issue52611b/b.go b/src/cmd/cgo/internal/test/issue52611b/b.go
new file mode 100644
index 0000000..d304175
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue52611b/b.go
@@ -0,0 +1,16 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue52611b
+
+/*
+typedef struct Bar {
+ int X;
+} Bar;
+*/
+import "C"
+
+func GetX2(bar *C.struct_Bar) int32 {
+ return int32(bar.X)
+}
diff --git a/src/cmd/cgo/internal/test/issue5548_c.c b/src/cmd/cgo/internal/test/issue5548_c.c
new file mode 100644
index 0000000..8411526
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue5548_c.c
@@ -0,0 +1,24 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "_cgo_export.h"
+
+static void clobber_stack() {
+ volatile char a[1024];
+ int i;
+ for(i = 0; i < sizeof a; i++)
+ a[i] = 0xff;
+}
+
+static int call_go() {
+ GoString s;
+ s.p = "test";
+ s.n = 4;
+ return issue5548FromC(s, 42);
+}
+
+int issue5548_in_c() {
+ clobber_stack();
+ return call_go();
+}
diff --git a/src/cmd/cgo/internal/test/issue5740a.c b/src/cmd/cgo/internal/test/issue5740a.c
new file mode 100644
index 0000000..a6a7d0c
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue5740a.c
@@ -0,0 +1,9 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+static int volatile val = 2;
+
+int test5740a() {
+ return val;
+}
diff --git a/src/cmd/cgo/internal/test/issue5740b.c b/src/cmd/cgo/internal/test/issue5740b.c
new file mode 100644
index 0000000..c2ff5fb
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue5740b.c
@@ -0,0 +1,9 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+static int volatile val = 3;
+
+int test5740b() {
+ return val;
+}
diff --git a/src/cmd/cgo/internal/test/issue6833_c.c b/src/cmd/cgo/internal/test/issue6833_c.c
new file mode 100644
index 0000000..c94c2c6
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue6833_c.c
@@ -0,0 +1,10 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "_cgo_export.h"
+
+unsigned long long
+issue6833Func(unsigned int aui, unsigned long long aull) {
+ return GoIssue6833Func(aui, aull);
+}
diff --git a/src/cmd/cgo/internal/test/issue6907export_c.c b/src/cmd/cgo/internal/test/issue6907export_c.c
new file mode 100644
index 0000000..9b1a4fc
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue6907export_c.c
@@ -0,0 +1,11 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <string.h>
+
+#include "_cgo_export.h"
+
+int CheckIssue6907C(_GoString_ s) {
+ return CheckIssue6907Go(s);
+}
diff --git a/src/cmd/cgo/internal/test/issue6997_linux.c b/src/cmd/cgo/internal/test/issue6997_linux.c
new file mode 100644
index 0000000..c6d251b
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue6997_linux.c
@@ -0,0 +1,28 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !android
+
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+static pthread_t thread;
+
+static void* threadfunc(void* dummy) {
+ while(1) {
+ sleep(1);
+ }
+}
+
+int StartThread() {
+ return pthread_create(&thread, NULL, &threadfunc, NULL);
+}
+
+int CancelThread() {
+ void *r;
+ pthread_cancel(thread);
+ pthread_join(thread, &r);
+ return (r == PTHREAD_CANCELED);
+}
diff --git a/src/cmd/cgo/internal/test/issue6997_linux.go b/src/cmd/cgo/internal/test/issue6997_linux.go
new file mode 100644
index 0000000..1de5edd
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue6997_linux.go
@@ -0,0 +1,44 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !android
+
+// Test that pthread_cancel works as expected
+// (NPTL uses SIGRTMIN to implement thread cancellation)
+// See https://golang.org/issue/6997
+package cgotest
+
+/*
+#cgo CFLAGS: -pthread
+#cgo LDFLAGS: -pthread
+extern int StartThread();
+extern int CancelThread();
+*/
+import "C"
+
+import (
+ "testing"
+ "time"
+)
+
+func test6997(t *testing.T) {
+ r := C.StartThread()
+ if r != 0 {
+ t.Error("pthread_create failed")
+ }
+ c := make(chan C.int)
+ go func() {
+ time.Sleep(500 * time.Millisecond)
+ c <- C.CancelThread()
+ }()
+
+ select {
+ case r = <-c:
+ if r == 0 {
+ t.Error("pthread finished but wasn't canceled??")
+ }
+ case <-time.After(30 * time.Second):
+ t.Error("hung in pthread_cancel/pthread_join")
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue7234_test.go b/src/cmd/cgo/internal/test/issue7234_test.go
new file mode 100644
index 0000000..c191a1a
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue7234_test.go
@@ -0,0 +1,21 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+import "testing"
+
+// This test actually doesn't have anything to do with cgo. It is a
+// test of https://golang.org/issue/7234, a compiler/linker bug in
+// handling string constants when using -linkmode=external. The test
+// is in this directory because we routinely test -linkmode=external
+// here.
+
+var v7234 = [...]string{"runtime/cgo"}
+
+func Test7234(t *testing.T) {
+ if v7234[0] != "runtime/cgo" {
+ t.Errorf("bad string constant %q", v7234[0])
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue8148.c b/src/cmd/cgo/internal/test/issue8148.c
new file mode 100644
index 0000000..927b434
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8148.c
@@ -0,0 +1,11 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "_cgo_export.h"
+
+int get8148(void) {
+ T t;
+ t.i = 42;
+ return issue8148Callback(&t);
+}
diff --git a/src/cmd/cgo/internal/test/issue8148.go b/src/cmd/cgo/internal/test/issue8148.go
new file mode 100644
index 0000000..aee9003
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8148.go
@@ -0,0 +1,24 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 8148. A typedef of an unnamed struct didn't work when used
+// with an exported Go function. No runtime test; just make sure it
+// compiles.
+
+package cgotest
+
+/*
+typedef struct { int i; } T;
+int get8148(void);
+*/
+import "C"
+
+//export issue8148Callback
+func issue8148Callback(t *C.T) C.int {
+ return t.i
+}
+
+func Issue8148() int {
+ return int(C.get8148())
+}
diff --git a/src/cmd/cgo/internal/test/issue8331.h b/src/cmd/cgo/internal/test/issue8331.h
new file mode 100644
index 0000000..8065be0
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8331.h
@@ -0,0 +1,7 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+typedef struct {
+ int i;
+} issue8331;
diff --git a/src/cmd/cgo/internal/test/issue8517.go b/src/cmd/cgo/internal/test/issue8517.go
new file mode 100644
index 0000000..2261513
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8517.go
@@ -0,0 +1,13 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !windows
+
+package cgotest
+
+import "testing"
+
+func test8517(t *testing.T) {
+ t.Skip("skipping windows only test")
+}
diff --git a/src/cmd/cgo/internal/test/issue8517_windows.c b/src/cmd/cgo/internal/test/issue8517_windows.c
new file mode 100644
index 0000000..a0b94c1
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8517_windows.c
@@ -0,0 +1,24 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "windows.h"
+
+extern void testHandleLeaksCallback();
+
+DWORD WINAPI testHandleLeaksFunc(LPVOID lpThreadParameter)
+{
+ int i;
+ for(i = 0; i < 100; i++) {
+ testHandleLeaksCallback();
+ }
+ return 0;
+}
+
+void testHandleLeaks()
+{
+ HANDLE h;
+ h = CreateThread(NULL, 0, &testHandleLeaksFunc, 0, 0, NULL);
+ WaitForSingleObject(h, INFINITE);
+ CloseHandle(h);
+}
diff --git a/src/cmd/cgo/internal/test/issue8517_windows.go b/src/cmd/cgo/internal/test/issue8517_windows.go
new file mode 100644
index 0000000..3782631
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8517_windows.go
@@ -0,0 +1,45 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+//void testHandleLeaks();
+import "C"
+
+import (
+ "syscall"
+ "testing"
+ "unsafe"
+)
+
+var issue8517counter int
+
+var (
+ kernel32 = syscall.MustLoadDLL("kernel32.dll")
+ getProcessHandleCount = kernel32.MustFindProc("GetProcessHandleCount")
+)
+
+func processHandleCount(t *testing.T) int {
+ const current_process = ^uintptr(0)
+ var c uint32
+ r, _, err := getProcessHandleCount.Call(current_process, uintptr(unsafe.Pointer(&c)))
+ if r == 0 {
+ t.Fatal(err)
+ }
+ return int(c)
+}
+
+func test8517(t *testing.T) {
+ c1 := processHandleCount(t)
+ C.testHandleLeaks()
+ c2 := processHandleCount(t)
+ if c1+issue8517counter <= c2 {
+ t.Fatalf("too many handles leaked: issue8517counter=%v c1=%v c2=%v", issue8517counter, c1, c2)
+ }
+}
+
+//export testHandleLeaksCallback
+func testHandleLeaksCallback() {
+ issue8517counter++
+}
diff --git a/src/cmd/cgo/internal/test/issue8694.go b/src/cmd/cgo/internal/test/issue8694.go
new file mode 100644
index 0000000..3b8f065
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8694.go
@@ -0,0 +1,40 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !android
+
+package cgotest
+
+/*
+#include <complex.h>
+
+complex float complexFloatSquared(complex float a) { return a*a; }
+complex double complexDoubleSquared(complex double a) { return a*a; }
+*/
+import "C"
+
+import (
+ "runtime"
+ "testing"
+)
+
+func test8694(t *testing.T) {
+ if runtime.GOARCH == "arm" {
+ t.Skip("test8694 is disabled on ARM because 5l cannot handle thumb library.")
+ }
+ // Really just testing that this compiles, but check answer anyway.
+ x := C.complexfloat(2 + 3i)
+ x2 := x * x
+ cx2 := C.complexFloatSquared(x)
+ if cx2 != x2 {
+ t.Errorf("C.complexFloatSquared(%v) = %v, want %v", x, cx2, x2)
+ }
+
+ y := C.complexdouble(2 + 3i)
+ y2 := y * y
+ cy2 := C.complexDoubleSquared(y)
+ if cy2 != y2 {
+ t.Errorf("C.complexDoubleSquared(%v) = %v, want %v", y, cy2, y2)
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue8756.go b/src/cmd/cgo/internal/test/issue8756.go
new file mode 100644
index 0000000..817f449
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8756.go
@@ -0,0 +1,17 @@
+package cgotest
+
+/*
+#cgo !darwin LDFLAGS: -lm
+#include <math.h>
+*/
+import "C"
+import (
+ "testing"
+
+ "cmd/cgo/internal/test/issue8756"
+)
+
+func test8756(t *testing.T) {
+ issue8756.Pow()
+ C.pow(1, 2)
+}
diff --git a/src/cmd/cgo/internal/test/issue8756/issue8756.go b/src/cmd/cgo/internal/test/issue8756/issue8756.go
new file mode 100644
index 0000000..223397f
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8756/issue8756.go
@@ -0,0 +1,11 @@
+package issue8756
+
+/*
+#cgo !darwin LDFLAGS: -lm
+#include <math.h>
+*/
+import "C"
+
+func Pow() {
+ C.pow(1, 2)
+}
diff --git a/src/cmd/cgo/internal/test/issue8811.c b/src/cmd/cgo/internal/test/issue8811.c
new file mode 100644
index 0000000..41b3c7c
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8811.c
@@ -0,0 +1,8 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+int issue8811Initialized = 0;
+
+void issue8811Init() {
+}
diff --git a/src/cmd/cgo/internal/test/issue8828.go b/src/cmd/cgo/internal/test/issue8828.go
new file mode 100644
index 0000000..9904a66
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8828.go
@@ -0,0 +1,16 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+// Issue 8828: compiling a file with -compiler=gccgo fails if a .c file
+// has the same name as compiled directory.
+
+package cgotest
+
+import "cmd/cgo/internal/test/issue8828"
+
+func p() {
+ issue8828.Bar()
+}
diff --git a/src/cmd/cgo/internal/test/issue8828/issue8828.c b/src/cmd/cgo/internal/test/issue8828/issue8828.c
new file mode 100644
index 0000000..27ec23a
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8828/issue8828.c
@@ -0,0 +1,7 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+void foo()
+{
+}
diff --git a/src/cmd/cgo/internal/test/issue8828/trivial.go b/src/cmd/cgo/internal/test/issue8828/trivial.go
new file mode 100644
index 0000000..e7b9a4e
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue8828/trivial.go
@@ -0,0 +1,8 @@
+package issue8828
+
+//void foo();
+import "C"
+
+func Bar() {
+ C.foo()
+}
diff --git a/src/cmd/cgo/internal/test/issue9026.go b/src/cmd/cgo/internal/test/issue9026.go
new file mode 100644
index 0000000..bab06ba
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9026.go
@@ -0,0 +1,15 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+package cgotest
+
+import (
+ "testing"
+
+ "cmd/cgo/internal/test/issue9026"
+)
+
+func test9026(t *testing.T) { issue9026.Test(t) }
diff --git a/src/cmd/cgo/internal/test/issue9026/issue9026.go b/src/cmd/cgo/internal/test/issue9026/issue9026.go
new file mode 100644
index 0000000..ff269ca
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9026/issue9026.go
@@ -0,0 +1,36 @@
+package issue9026
+
+// This file appears in its own package since the assertion tests the
+// per-package counter used to create fresh identifiers.
+
+/*
+typedef struct { int i; } git_merge_file_input;
+
+typedef struct { int j; } git_merge_file_options;
+
+void git_merge_file(
+ git_merge_file_input *in,
+ git_merge_file_options *opts) {}
+*/
+import "C"
+import (
+ "fmt"
+ "testing"
+)
+
+func Test(t *testing.T) {
+ var in C.git_merge_file_input
+ var opts *C.git_merge_file_options
+ C.git_merge_file(&in, opts)
+
+ // Test that the generated type names are deterministic.
+ // (Previously this would fail about 10% of the time.)
+ //
+ // Brittle: the assertion may fail spuriously when the algorithm
+ // changes, but should remain stable otherwise.
+ got := fmt.Sprintf("%T %T", in, opts)
+ want := "issue9026._Ctype_struct___0 *issue9026._Ctype_struct___1"
+ if got != want {
+ t.Errorf("Non-deterministic type names: got %s, want %s", got, want)
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_386.s b/src/cmd/cgo/internal/test/issue9400/asm_386.s
new file mode 100644
index 0000000..8a38301
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_386.s
@@ -0,0 +1,27 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gc
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0
+ MOVL $·Baton(SB), BX
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADDL $(1024 * 8), SP
+
+ // Ask signaller to setgid
+ MOVL $1, (BX)
+
+ // Wait for setgid completion
+loop:
+ PAUSE
+ MOVL (BX), AX
+ CMPL AX, $0
+ JNE loop
+
+ // Restore stack
+ SUBL $(1024 * 8), SP
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_amd64x.s b/src/cmd/cgo/internal/test/issue9400/asm_amd64x.s
new file mode 100644
index 0000000..07adaf7
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_amd64x.s
@@ -0,0 +1,26 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build (amd64 || amd64p32) && gc
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADDQ $(1024 * 8), SP
+
+ // Ask signaller to setgid
+ MOVL $1, ·Baton(SB)
+
+ // Wait for setgid completion
+loop:
+ PAUSE
+ MOVL ·Baton(SB), AX
+ CMPL AX, $0
+ JNE loop
+
+ // Restore stack
+ SUBQ $(1024 * 8), SP
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_arm.s b/src/cmd/cgo/internal/test/issue9400/asm_arm.s
new file mode 100644
index 0000000..4126172
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_arm.s
@@ -0,0 +1,39 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gc
+
+#include "textflag.h"
+
+TEXT cas<>(SB),NOSPLIT,$0
+ MOVW $0xffff0fc0, R15 // R15 is PC
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0
+ // Save link register
+ MOVW R14, R4
+
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADD $(1024 * 8), R13
+
+ // Ask signaller to setgid
+ MOVW $·Baton(SB), R2
+storeloop:
+ MOVW 0(R2), R0
+ MOVW $1, R1
+ BL cas<>(SB)
+ BCC storeloop
+
+ // Wait for setgid completion
+loop:
+ MOVW $0, R0
+ MOVW $0, R1
+ BL cas<>(SB)
+ BCC loop
+
+ // Restore stack
+ SUB $(1024 * 8), R13
+
+ MOVW R4, R14
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_arm64.s b/src/cmd/cgo/internal/test/issue9400/asm_arm64.s
new file mode 100644
index 0000000..affbd71
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_arm64.s
@@ -0,0 +1,39 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gc
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0
+ // Save link register
+ MOVD R30, R9
+
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADD $(1024 * 8), RSP
+
+ // Ask signaller to setgid
+ MOVD $·Baton(SB), R0
+ MOVD $1, R1
+storeloop:
+ LDAXRW (R0), R2
+ STLXRW R1, (R0), R3
+ CBNZ R3, storeloop
+
+ // Wait for setgid completion
+ MOVW $0, R1
+ MOVW $0, R2
+loop:
+ LDAXRW (R0), R3
+ CMPW R1, R3
+ BNE loop
+ STLXRW R2, (R0), R3
+ CBNZ R3, loop
+
+ // Restore stack
+ SUB $(1024 * 8), RSP
+
+ MOVD R9, R30
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_loong64.s b/src/cmd/cgo/internal/test/issue9400/asm_loong64.s
new file mode 100644
index 0000000..c242fc6
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_loong64.s
@@ -0,0 +1,28 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADDV $(1024*8), R3
+
+ // Ask signaller to setgid
+ MOVW $1, R12
+ DBAR
+ MOVW R12, ·Baton(SB)
+ DBAR
+
+ // Wait for setgid completion
+loop:
+ DBAR
+ MOVW ·Baton(SB), R12
+ OR R13, R13, R13 // hint that we're in a spin loop
+ BNE R12, loop
+ DBAR
+
+ // Restore stack
+ ADDV $(-1024*8), R3
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s b/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s
new file mode 100644
index 0000000..1f492ea
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_mips64x.s
@@ -0,0 +1,32 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build (mips64 || mips64le) && gc
+
+#include "textflag.h"
+
+#define SYNC WORD $0xf
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADDV $(1024*8), R29
+
+ // Ask signaller to setgid
+ MOVW $1, R1
+ SYNC
+ MOVW R1, ·Baton(SB)
+ SYNC
+
+ // Wait for setgid completion
+loop:
+ SYNC
+ MOVW ·Baton(SB), R1
+ OR R2, R2, R2 // hint that we're in a spin loop
+ BNE R1, loop
+ SYNC
+
+ // Restore stack
+ ADDV $(-1024*8), R29
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_mipsx.s b/src/cmd/cgo/internal/test/issue9400/asm_mipsx.s
new file mode 100644
index 0000000..695273d
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_mipsx.s
@@ -0,0 +1,30 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build (mips || mipsle) && gc
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADDU $(1024*8), R29
+
+ // Ask signaller to setgid
+ MOVW $1, R1
+ SYNC
+ MOVW R1, ·Baton(SB)
+ SYNC
+
+ // Wait for setgid completion
+loop:
+ SYNC
+ MOVW ·Baton(SB), R1
+ OR R2, R2, R2 // hint that we're in a spin loop
+ BNE R1, loop
+ SYNC
+
+ // Restore stack
+ ADDU $(-1024*8), R29
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_ppc64x.s b/src/cmd/cgo/internal/test/issue9400/asm_ppc64x.s
new file mode 100644
index 0000000..5f13f16
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_ppc64x.s
@@ -0,0 +1,31 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build (ppc64 || ppc64le) && gc
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADD $(1024 * 8), R1
+
+ // Ask signaller to setgid
+ MOVW $1, R3
+ SYNC
+ MOVW R3, ·Baton(SB)
+
+ // Wait for setgid completion
+loop:
+ SYNC
+ MOVW ·Baton(SB), R3
+ CMP R3, $0
+ // Hint that we're in a spin loop
+ OR R1, R1, R1
+ BNE loop
+ ISYNC
+
+ // Restore stack
+ SUB $(1024 * 8), R1
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s b/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s
new file mode 100644
index 0000000..fa34f6b
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_riscv64.s
@@ -0,0 +1,30 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build riscv64 && gc
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT|NOFRAME,$0-0
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADD $(1024*8), X2
+
+ // Ask signaller to setgid
+ MOV $1, X5
+ FENCE
+ MOVW X5, ·Baton(SB)
+ FENCE
+
+ // Wait for setgid completion
+loop:
+ FENCE
+ MOVW ·Baton(SB), X5
+ OR X6, X6, X6 // hint that we're in a spin loop
+ BNE ZERO, X5, loop
+ FENCE
+
+ // Restore stack
+ ADD $(-1024*8), X2
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/asm_s390x.s b/src/cmd/cgo/internal/test/issue9400/asm_s390x.s
new file mode 100644
index 0000000..2552fa7
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/asm_s390x.s
@@ -0,0 +1,26 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gc
+
+#include "textflag.h"
+
+TEXT ·RewindAndSetgid(SB),NOSPLIT,$0-0
+ // Rewind stack pointer so anything that happens on the stack
+ // will clobber the test pattern created by the caller
+ ADD $(1024 * 8), R15
+
+ // Ask signaller to setgid
+ MOVD $·Baton(SB), R5
+ MOVW $1, 0(R5)
+
+ // Wait for setgid completion
+loop:
+ SYNC
+ MOVW ·Baton(SB), R3
+ CMPBNE R3, $0, loop
+
+ // Restore stack
+ SUB $(1024 * 8), R15
+ RET
diff --git a/src/cmd/cgo/internal/test/issue9400/gccgo.go b/src/cmd/cgo/internal/test/issue9400/gccgo.go
new file mode 100644
index 0000000..4dd987b
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/gccgo.go
@@ -0,0 +1,26 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gccgo
+
+package issue9400
+
+import (
+ "runtime"
+ "sync/atomic"
+)
+
+// The test for the gc compiler resets the stack pointer so that the
+// stack gets modified. We don't have a way to do that for gccgo
+// without writing more assembly code, which we haven't bothered to
+// do. So this is not much of a test.
+
+var Baton int32
+
+func RewindAndSetgid() {
+ atomic.StoreInt32(&Baton, 1)
+ for atomic.LoadInt32(&Baton) != 0 {
+ runtime.Gosched()
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue9400/stubs.go b/src/cmd/cgo/internal/test/issue9400/stubs.go
new file mode 100644
index 0000000..c2b235a
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400/stubs.go
@@ -0,0 +1,11 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux && gc
+
+package issue9400
+
+var Baton int32
+
+func RewindAndSetgid()
diff --git a/src/cmd/cgo/internal/test/issue9400_linux.go b/src/cmd/cgo/internal/test/issue9400_linux.go
new file mode 100644
index 0000000..1511e25
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9400_linux.go
@@ -0,0 +1,67 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that SIGSETXID runs on signal stack, since it's likely to
+// overflow if it runs on the Go stack.
+
+package cgotest
+
+/*
+#include <sys/types.h>
+#include <unistd.h>
+*/
+import "C"
+
+import (
+ "runtime"
+ "runtime/debug"
+ "sync/atomic"
+ "testing"
+
+ "cmd/cgo/internal/test/issue9400"
+)
+
+func test9400(t *testing.T) {
+ // We synchronize through a shared variable, so we need two procs
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
+
+ // Start signaller
+ atomic.StoreInt32(&issue9400.Baton, 0)
+ go func() {
+ // Wait for RewindAndSetgid
+ for atomic.LoadInt32(&issue9400.Baton) == 0 {
+ runtime.Gosched()
+ }
+ // Broadcast SIGSETXID
+ runtime.LockOSThread()
+ C.setgid(0)
+ // Indicate that signalling is done
+ atomic.StoreInt32(&issue9400.Baton, 0)
+ }()
+
+ // Grow the stack and put down a test pattern
+ const pattern = 0x123456789abcdef
+ var big [1024]uint64 // len must match assembly
+ for i := range big {
+ big[i] = pattern
+ }
+
+ // Disable GC for the duration of the test.
+ // This avoids a potential GC deadlock when spinning in uninterruptable ASM below #49695.
+ defer debug.SetGCPercent(debug.SetGCPercent(-1))
+ // SetGCPercent waits until the mark phase is over, but the runtime
+ // also preempts at the start of the sweep phase, so make sure that's
+ // done too. See #49695.
+ runtime.GC()
+
+ // Temporarily rewind the stack and trigger SIGSETXID
+ issue9400.RewindAndSetgid()
+
+ // Check test pattern
+ for i := range big {
+ if big[i] != pattern {
+ t.Fatalf("entry %d of test pattern is wrong; %#x != %#x", i, big[i], uint64(pattern))
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/test/issue9510.go b/src/cmd/cgo/internal/test/issue9510.go
new file mode 100644
index 0000000..7f0aff4
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9510.go
@@ -0,0 +1,26 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+// Test that we can link together two different cgo packages that both
+// use the same libgcc function.
+
+package cgotest
+
+import (
+ "runtime"
+ "testing"
+
+ "cmd/cgo/internal/test/issue9510a"
+ "cmd/cgo/internal/test/issue9510b"
+)
+
+func test9510(t *testing.T) {
+ if runtime.GOARCH == "arm" {
+ t.Skip("skipping because libgcc may be a Thumb library")
+ }
+ issue9510a.F(1, 1)
+ issue9510b.F(1, 1)
+}
diff --git a/src/cmd/cgo/internal/test/issue9510a/a.go b/src/cmd/cgo/internal/test/issue9510a/a.go
new file mode 100644
index 0000000..1a5224b
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9510a/a.go
@@ -0,0 +1,15 @@
+package issue9510a
+
+/*
+static double csquare(double a, double b) {
+ __complex__ double d;
+ __real__ d = a;
+ __imag__ d = b;
+ return __real__ (d * d);
+}
+*/
+import "C"
+
+func F(a, b float64) float64 {
+ return float64(C.csquare(C.double(a), C.double(b)))
+}
diff --git a/src/cmd/cgo/internal/test/issue9510b/b.go b/src/cmd/cgo/internal/test/issue9510b/b.go
new file mode 100644
index 0000000..5016b39
--- /dev/null
+++ b/src/cmd/cgo/internal/test/issue9510b/b.go
@@ -0,0 +1,15 @@
+package issue9510b
+
+/*
+static double csquare(double a, double b) {
+ __complex__ double d;
+ __real__ d = a;
+ __imag__ d = b;
+ return __real__ (d * d);
+}
+*/
+import "C"
+
+func F(a, b float64) float64 {
+ return float64(C.csquare(C.double(a), C.double(b)))
+}
diff --git a/src/cmd/cgo/internal/test/setgid2_linux.go b/src/cmd/cgo/internal/test/setgid2_linux.go
new file mode 100644
index 0000000..438f5ae
--- /dev/null
+++ b/src/cmd/cgo/internal/test/setgid2_linux.go
@@ -0,0 +1,35 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Stress test setgid and thread creation. A thread
+// can get a SIGSETXID signal early on at thread
+// initialization, causing crash. See issue 53374.
+
+package cgotest
+
+/*
+#include <sys/types.h>
+#include <unistd.h>
+*/
+import "C"
+
+import (
+ "runtime"
+ "testing"
+)
+
+func testSetgidStress(t *testing.T) {
+ const N = 50
+ ch := make(chan int, N)
+ for i := 0; i < N; i++ {
+ go func() {
+ C.setgid(0)
+ ch <- 1
+ runtime.LockOSThread() // so every goroutine uses a new thread
+ }()
+ }
+ for i := 0; i < N; i++ {
+ <-ch
+ }
+}
diff --git a/src/cmd/cgo/internal/test/setgid_linux.go b/src/cmd/cgo/internal/test/setgid_linux.go
new file mode 100644
index 0000000..7c64946
--- /dev/null
+++ b/src/cmd/cgo/internal/test/setgid_linux.go
@@ -0,0 +1,49 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that setgid does not hang on Linux.
+// See https://golang.org/issue/3871 for details.
+
+package cgotest
+
+/*
+#include <sys/types.h>
+#include <unistd.h>
+*/
+import "C"
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+ "testing"
+ "time"
+)
+
+func runTestSetgid() bool {
+ c := make(chan bool)
+ go func() {
+ C.setgid(0)
+ c <- true
+ }()
+ select {
+ case <-c:
+ return true
+ case <-time.After(5 * time.Second):
+ return false
+ }
+
+}
+
+func testSetgid(t *testing.T) {
+ if !runTestSetgid() {
+ t.Error("setgid hung")
+ }
+
+ // Now try it again after using signal.Notify.
+ signal.Notify(make(chan os.Signal, 1), syscall.SIGINT)
+ if !runTestSetgid() {
+ t.Error("setgid hung after signal.Notify")
+ }
+}
diff --git a/src/cmd/cgo/internal/test/sigaltstack.go b/src/cmd/cgo/internal/test/sigaltstack.go
new file mode 100644
index 0000000..d468cf8
--- /dev/null
+++ b/src/cmd/cgo/internal/test/sigaltstack.go
@@ -0,0 +1,78 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !windows && !android
+
+// Test that the Go runtime still works if C code changes the signal stack.
+
+package cgotest
+
+/*
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _AIX
+// On AIX, SIGSTKSZ is too small to handle Go sighandler.
+#define CSIGSTKSZ 0x4000
+#else
+#define CSIGSTKSZ SIGSTKSZ
+#endif
+
+static stack_t oss;
+static char signalStack[CSIGSTKSZ];
+
+static void changeSignalStack(void) {
+ stack_t ss;
+ memset(&ss, 0, sizeof ss);
+ ss.ss_sp = signalStack;
+ ss.ss_flags = 0;
+ ss.ss_size = CSIGSTKSZ;
+ if (sigaltstack(&ss, &oss) < 0) {
+ perror("sigaltstack");
+ abort();
+ }
+}
+
+static void restoreSignalStack(void) {
+#if (defined(__x86_64__) || defined(__i386__)) && defined(__APPLE__)
+ // The Darwin C library enforces a minimum that the kernel does not.
+ // This is OK since we allocated this much space in mpreinit,
+ // it was just removed from the buffer by stackalloc.
+ oss.ss_size = MINSIGSTKSZ;
+#endif
+ if (sigaltstack(&oss, NULL) < 0) {
+ perror("sigaltstack restore");
+ abort();
+ }
+}
+
+static int zero(void) {
+ return 0;
+}
+*/
+import "C"
+
+import (
+ "runtime"
+ "testing"
+)
+
+func testSigaltstack(t *testing.T) {
+ switch {
+ case runtime.GOOS == "solaris", runtime.GOOS == "illumos", runtime.GOOS == "ios" && runtime.GOARCH == "arm64":
+ t.Skipf("switching signal stack not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
+ }
+
+ C.changeSignalStack()
+ defer C.restoreSignalStack()
+ defer func() {
+ if recover() == nil {
+ t.Error("did not see expected panic")
+ }
+ }()
+ v := 1 / int(C.zero())
+ t.Errorf("unexpected success of division by zero == %d", v)
+}
diff --git a/src/cmd/cgo/internal/test/sigprocmask.c b/src/cmd/cgo/internal/test/sigprocmask.c
new file mode 100644
index 0000000..4315833
--- /dev/null
+++ b/src/cmd/cgo/internal/test/sigprocmask.c
@@ -0,0 +1,51 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !windows
+
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+extern void IntoGoAndBack();
+
+int CheckBlocked() {
+ sigset_t mask;
+ sigprocmask(SIG_BLOCK, NULL, &mask);
+ return sigismember(&mask, SIGIO);
+}
+
+static void* sigthreadfunc(void* unused) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGIO);
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+ IntoGoAndBack();
+ return NULL;
+}
+
+int RunSigThread() {
+ int tries;
+ pthread_t thread;
+ int r;
+ struct timespec ts;
+
+ for (tries = 0; tries < 20; tries++) {
+ r = pthread_create(&thread, NULL, &sigthreadfunc, NULL);
+ if (r == 0) {
+ return pthread_join(thread, NULL);
+ }
+ if (r != EAGAIN) {
+ return r;
+ }
+ ts.tv_sec = 0;
+ ts.tv_nsec = (tries + 1) * 1000 * 1000; // Milliseconds.
+ nanosleep(&ts, NULL);
+ }
+ return EAGAIN;
+}
diff --git a/src/cmd/cgo/internal/test/sigprocmask.go b/src/cmd/cgo/internal/test/sigprocmask.go
new file mode 100644
index 0000000..6cc04d6
--- /dev/null
+++ b/src/cmd/cgo/internal/test/sigprocmask.go
@@ -0,0 +1,40 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !windows
+
+package cgotest
+
+/*
+#cgo CFLAGS: -pthread
+#cgo LDFLAGS: -pthread
+extern int RunSigThread();
+extern int CheckBlocked();
+*/
+import "C"
+import (
+ "os"
+ "os/signal"
+ "syscall"
+ "testing"
+)
+
+var blocked bool
+
+//export IntoGoAndBack
+func IntoGoAndBack() {
+ // Verify that SIGIO stays blocked on the C thread
+ // even when unblocked for signal.Notify().
+ signal.Notify(make(chan os.Signal), syscall.SIGIO)
+ blocked = C.CheckBlocked() != 0
+}
+
+func testSigprocmask(t *testing.T) {
+ if r := C.RunSigThread(); r != 0 {
+ t.Errorf("pthread_create/pthread_join failed: %d", r)
+ }
+ if !blocked {
+ t.Error("Go runtime unblocked SIGIO")
+ }
+}
diff --git a/src/cmd/cgo/internal/test/test.go b/src/cmd/cgo/internal/test/test.go
new file mode 100644
index 0000000..7da5a85
--- /dev/null
+++ b/src/cmd/cgo/internal/test/test.go
@@ -0,0 +1,2303 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test cases for cgo.
+// Both the import "C" prologue and the main file are sorted by issue number.
+// This file contains C definitions (not just declarations)
+// and so it must NOT contain any //export directives on Go functions.
+// See testx.go for exports.
+
+package cgotest
+
+/*
+#include <complex.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#cgo !darwin LDFLAGS: -lm
+
+#ifndef WIN32
+#include <pthread.h>
+#include <signal.h>
+#endif
+
+// alignment tests
+
+typedef unsigned char Uint8;
+typedef unsigned short Uint16;
+
+typedef enum {
+ MOD1 = 0x0000,
+ MODX = 0x8000
+} SDLMod;
+
+typedef enum {
+ A1 = 1,
+ B1 = 322,
+ SDLK_LAST
+} SDLKey;
+
+typedef struct SDL_keysym {
+ Uint8 scancode;
+ SDLKey sym;
+ SDLMod mod;
+ Uint16 unicode;
+} SDL_keysym;
+
+typedef struct SDL_KeyboardEvent {
+ Uint8 typ;
+ Uint8 which;
+ Uint8 state;
+ SDL_keysym keysym;
+} SDL_KeyboardEvent;
+
+void makeEvent(SDL_KeyboardEvent *event) {
+ unsigned char *p;
+ int i;
+
+ p = (unsigned char*)event;
+ for (i=0; i<sizeof *event; i++) {
+ p[i] = i;
+ }
+}
+
+int same(SDL_KeyboardEvent* e, Uint8 typ, Uint8 which, Uint8 state, Uint8 scan, SDLKey sym, SDLMod mod, Uint16 uni) {
+ return e->typ == typ && e->which == which && e->state == state && e->keysym.scancode == scan && e->keysym.sym == sym && e->keysym.mod == mod && e->keysym.unicode == uni;
+}
+
+void cTest(SDL_KeyboardEvent *event) {
+ printf("C: %#x %#x %#x %#x %#x %#x %#x\n", event->typ, event->which, event->state,
+ event->keysym.scancode, event->keysym.sym, event->keysym.mod, event->keysym.unicode);
+ fflush(stdout);
+}
+
+// api
+
+const char *greeting = "hello, world";
+
+// basic test cases
+
+#define SHIFT(x, y) ((x)<<(y))
+#define KILO SHIFT(1, 10)
+#define UINT32VAL 0xc008427bU
+
+enum E {
+ Enum1 = 1,
+ Enum2 = 2,
+};
+
+typedef unsigned char cgo_uuid_t[20];
+
+void uuid_generate(cgo_uuid_t x) {
+ x[0] = 0;
+}
+
+struct S {
+ int x;
+};
+
+const char *cstr = "abcefghijklmnopqrstuvwxyzABCEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+
+extern enum E myConstFunc(struct S* const ctx, int const id, struct S **const filter);
+
+enum E myConstFunc(struct S *const ctx, int const id, struct S **const filter) { return 0; }
+
+int add(int x, int y) {
+ return x+y;
+};
+
+// Following mimics vulkan complex definitions for benchmarking cgocheck overhead.
+
+typedef uint32_t VkFlags;
+typedef VkFlags VkDeviceQueueCreateFlags;
+typedef uint32_t VkStructureType;
+
+typedef struct VkDeviceQueueCreateInfo {
+ VkStructureType sType;
+ const void* pNext;
+ VkDeviceQueueCreateFlags flags;
+ uint32_t queueFamilyIndex;
+ uint32_t queueCount;
+ const float* pQueuePriorities;
+} VkDeviceQueueCreateInfo;
+
+typedef struct VkPhysicalDeviceFeatures {
+ uint32_t bools[56];
+} VkPhysicalDeviceFeatures;
+
+typedef struct VkDeviceCreateInfo {
+ VkStructureType sType;
+ const void* pNext;
+ VkFlags flags;
+ uint32_t queueCreateInfoCount;
+ const VkDeviceQueueCreateInfo* pQueueCreateInfos;
+ uint32_t enabledLayerCount;
+ const char* const* ppEnabledLayerNames;
+ uint32_t enabledExtensionCount;
+ const char* const* ppEnabledExtensionNames;
+ const VkPhysicalDeviceFeatures* pEnabledFeatures;
+} VkDeviceCreateInfo;
+
+void handleComplexPointer(VkDeviceCreateInfo *a0) {}
+void handleComplexPointer8(
+ VkDeviceCreateInfo *a0, VkDeviceCreateInfo *a1, VkDeviceCreateInfo *a2, VkDeviceCreateInfo *a3,
+ VkDeviceCreateInfo *a4, VkDeviceCreateInfo *a5, VkDeviceCreateInfo *a6, VkDeviceCreateInfo *a7
+) {}
+
+// complex alignment
+
+struct {
+ float x;
+ _Complex float y;
+} cplxAlign = { 3.14, 2.17 };
+
+// constants and pointer checking
+
+#define CheckConstVal 0
+
+typedef struct {
+ int *p;
+} CheckConstStruct;
+
+static void CheckConstFunc(CheckConstStruct *p, int e) {}
+
+// duplicate symbol
+
+int base_symbol = 0;
+#define alias_one base_symbol
+#define alias_two base_symbol
+
+// function pointer variables
+
+typedef int (*intFunc) ();
+
+int
+bridge_int_func(intFunc f)
+{
+ return f();
+}
+
+int fortytwo()
+{
+ return 42;
+}
+
+// issue 1222
+typedef union {
+ long align;
+} xxpthread_mutex_t;
+struct ibv_async_event {
+ union {
+ int x;
+ } element;
+};
+struct ibv_context {
+ xxpthread_mutex_t mutex;
+};
+
+// issue 1635
+// Mac OS X's gcc will generate scattered relocation 2/1 for
+// this function on Darwin/386, and 8l couldn't handle it.
+// this example is in issue 1635
+void scatter() {
+ void *p = scatter;
+ printf("scatter = %p\n", p);
+}
+
+// Adding this explicit extern declaration makes this a test for
+// https://gcc.gnu.org/PR68072 aka https://golang.org/issue/13344 .
+// It used to cause a cgo error when building with GCC 6.
+extern int hola;
+
+// this example is in issue 3253
+int hola = 0;
+int testHola() { return hola; }
+
+// issue 3250
+#ifdef WIN32
+void testSendSIG() {}
+#else
+static void *thread(void *p) {
+ const int M = 100;
+ int i;
+ (void)p;
+ for (i = 0; i < M; i++) {
+ pthread_kill(pthread_self(), SIGCHLD);
+ usleep(rand() % 20 + 5);
+ }
+ return NULL;
+}
+void testSendSIG() {
+ const int N = 20;
+ int i;
+ pthread_t tid[N];
+ for (i = 0; i < N; i++) {
+ usleep(rand() % 200 + 100);
+ pthread_create(&tid[i], 0, thread, NULL);
+ }
+ for (i = 0; i < N; i++)
+ pthread_join(tid[i], 0);
+}
+#endif
+
+// issue 3261
+// libgcc on ARM might be compiled as thumb code, but our 5l
+// can't handle that, so we have to disable this test on arm.
+#ifdef __ARMEL__
+int vabs(int x) {
+ puts("testLibgcc is disabled on ARM because 5l cannot handle thumb library.");
+ return (x < 0) ? -x : x;
+}
+#elif defined(__arm64__) && defined(__clang__)
+int vabs(int x) {
+ puts("testLibgcc is disabled on ARM64 with clang due to lack of libgcc.");
+ return (x < 0) ? -x : x;
+}
+#else
+int __absvsi2(int); // dummy prototype for libgcc function
+// we shouldn't name the function abs, as gcc might use
+// the builtin one.
+int vabs(int x) { return __absvsi2(x); }
+#endif
+
+
+// issue 3729
+// access errno from void C function
+const char _expA = 0x42;
+const float _expB = 3.14159;
+const short _expC = 0x55aa;
+const int _expD = 0xdeadbeef;
+
+#ifdef WIN32
+void g(void) {}
+void g2(int x, char a, float b, short c, int d) {}
+#else
+
+void g(void) {
+ errno = E2BIG;
+}
+
+// try to pass some non-trivial arguments to function g2
+void g2(int x, char a, float b, short c, int d) {
+ if (a == _expA && b == _expB && c == _expC && d == _expD)
+ errno = x;
+ else
+ errno = -1;
+}
+#endif
+
+// issue 3945
+// Test that cgo reserves enough stack space during cgo call.
+// See https://golang.org/issue/3945 for details.
+void say() {
+ printf("%s from C\n", "hello");
+}
+
+// issue 4054 part 1 - other half in testx.go
+
+typedef enum {
+ A = 0,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ II,
+ J,
+} issue4054a;
+
+// issue 4339
+// We've historically permitted #include <>, so test it here. Issue 29333.
+// Also see issue 41059.
+#include <issue4339.h>
+
+// issue 4417
+// cmd/cgo: bool alignment/padding issue.
+// bool alignment is wrong and causing wrong arguments when calling functions.
+static int c_bool(bool a, bool b, int c, bool d, bool e) {
+ return c;
+}
+
+// issue 4857
+#cgo CFLAGS: -Werror
+const struct { int a; } *issue4857() { return (void *)0; }
+
+// issue 5224
+// Test that the #cgo CFLAGS directive works,
+// with and without platform filters.
+#cgo CFLAGS: -DCOMMON_VALUE=123
+#cgo windows CFLAGS: -DIS_WINDOWS=1
+#cgo !windows CFLAGS: -DIS_WINDOWS=0
+int common = COMMON_VALUE;
+int is_windows = IS_WINDOWS;
+
+// issue 5227
+// linker incorrectly treats common symbols and
+// leaves them undefined.
+
+typedef struct {
+ int Count;
+} Fontinfo;
+
+Fontinfo SansTypeface;
+
+extern void init();
+
+Fontinfo loadfont() {
+ Fontinfo f = {0};
+ return f;
+}
+
+void init() {
+ SansTypeface = loadfont();
+}
+
+// issue 5242
+// Cgo incorrectly computed the alignment of structs
+// with no Go accessible fields as 0, and then panicked on
+// modulo-by-zero computations.
+
+// issue 50987
+// disable arm64 GCC warnings
+#cgo CFLAGS: -Wno-psabi -Wno-unknown-warning-option
+
+typedef struct {
+} foo;
+
+typedef struct {
+ int x : 1;
+} bar;
+
+int issue5242(foo f, bar b) {
+ return 5242;
+}
+
+// issue 5337
+// Verify that we can withstand SIGPROF received on foreign threads
+
+#ifdef WIN32
+void test5337() {}
+#else
+static void *thread1(void *p) {
+ (void)p;
+ pthread_kill(pthread_self(), SIGPROF);
+ return NULL;
+}
+void test5337() {
+ pthread_t tid;
+ pthread_create(&tid, 0, thread1, NULL);
+ pthread_join(tid, 0);
+}
+#endif
+
+// issue 5603
+
+const long long issue5603exp = 0x12345678;
+long long issue5603foo0() { return issue5603exp; }
+long long issue5603foo1(void *p) { return issue5603exp; }
+long long issue5603foo2(void *p, void *q) { return issue5603exp; }
+long long issue5603foo3(void *p, void *q, void *r) { return issue5603exp; }
+long long issue5603foo4(void *p, void *q, void *r, void *s) { return issue5603exp; }
+
+// issue 5740
+
+int test5740a(void), test5740b(void);
+
+// issue 5986
+static void output5986()
+{
+ int current_row = 0, row_count = 0;
+ double sum_squares = 0;
+ double d;
+ do {
+ if (current_row == 10) {
+ current_row = 0;
+ }
+ ++row_count;
+ }
+ while (current_row++ != 1);
+ d = sqrt(sum_squares / row_count);
+ printf("sqrt is: %g\n", d);
+}
+
+// issue 6128
+// Test handling of #defined names in clang.
+// NOTE: Must use hex, or else a shortcut for decimals
+// in cgo avoids trying to pass this to clang.
+#define X 0x1
+
+// issue 6472
+typedef struct
+{
+ struct
+ {
+ int x;
+ } y[16];
+} z;
+
+// issue 6612
+// Test new scheme for deciding whether C.name is an expression, type, constant.
+// Clang silences some warnings when the name is a #defined macro, so test those too
+// (even though we now use errors exclusively, not warnings).
+
+void myfunc(void) {}
+int myvar = 5;
+const char *mytext = "abcdef";
+typedef int mytype;
+enum {
+ myenum = 1234,
+};
+
+#define myfunc_def myfunc
+#define myvar_def myvar
+#define mytext_def mytext
+#define mytype_def mytype
+#define myenum_def myenum
+#define myint_def 12345
+#define myfloat_def 1.5
+#define mystring_def "hello"
+
+// issue 6907
+char* Issue6907CopyString(_GoString_ s) {
+ size_t n;
+ const char *p;
+ char *r;
+
+ n = _GoStringLen(s);
+ p = _GoStringPtr(s);
+ r = malloc(n + 1);
+ memmove(r, p, n);
+ r[n] = '\0';
+ return r;
+}
+
+// issue 7560
+typedef struct {
+ char x;
+ long y;
+} __attribute__((__packed__)) misaligned;
+
+int
+offset7560(void)
+{
+ return (uintptr_t)&((misaligned*)0)->y;
+}
+
+// issue 7786
+// No runtime test, just make sure that typedef and struct/union/class are interchangeable at compile time.
+
+struct test7786;
+typedef struct test7786 typedef_test7786;
+void f7786(struct test7786 *ctx) {}
+void g7786(typedef_test7786 *ctx) {}
+
+typedef struct body7786 typedef_body7786;
+struct body7786 { int x; };
+void b7786(struct body7786 *ctx) {}
+void c7786(typedef_body7786 *ctx) {}
+
+typedef union union7786 typedef_union7786;
+void u7786(union union7786 *ctx) {}
+void v7786(typedef_union7786 *ctx) {}
+
+// issue 8092
+// Test that linker defined symbols (e.g., text, data) don't
+// conflict with C symbols.
+char text[] = "text";
+char data[] = "data";
+char *ctext(void) { return text; }
+char *cdata(void) { return data; }
+
+// issue 8428
+// Cgo inconsistently translated zero size arrays.
+
+struct issue8428one {
+ char b;
+ char rest[];
+};
+
+struct issue8428two {
+ void *p;
+ char b;
+ char rest[0];
+ char pad;
+};
+
+struct issue8428three {
+ char w[1][2][3][0];
+ char x[2][3][0][1];
+ char y[3][0][1][2];
+ char z[0][1][2][3];
+};
+
+// issue 8331 part 1 - part 2 in testx.go
+// A typedef of an unnamed struct is the same struct when
+// #include'd twice. No runtime test; just make sure it compiles.
+#include "issue8331.h"
+
+// issue 8368 and 8441
+// Recursive struct definitions didn't work.
+// No runtime test; just make sure it compiles.
+typedef struct one one;
+typedef struct two two;
+struct one {
+ two *x;
+};
+struct two {
+ one *x;
+};
+
+// issue 8811
+
+extern int issue8811Initialized;
+extern void issue8811Init();
+
+void issue8811Execute() {
+ if(!issue8811Initialized)
+ issue8811Init();
+}
+
+// issue 8945
+
+typedef void (*PFunc8945)();
+PFunc8945 func8945;
+
+// issue 9557
+
+struct issue9557_t {
+ int a;
+} test9557bar = { 42 };
+struct issue9557_t *issue9557foo = &test9557bar;
+
+// issue 10303
+// Pointers passed to C were not marked as escaping (bug in cgo).
+
+typedef int *intptr;
+
+void setintstar(int *x) {
+ *x = 1;
+}
+
+void setintptr(intptr x) {
+ *x = 1;
+}
+
+void setvoidptr(void *x) {
+ *(int*)x = 1;
+}
+
+typedef struct Struct Struct;
+struct Struct {
+ int *P;
+};
+
+void setstruct(Struct s) {
+ *s.P = 1;
+}
+
+// issue 11925
+// Structs with zero-length trailing fields are now padded by the Go compiler.
+
+struct a11925 {
+ int i;
+ char a[0];
+ char b[0];
+};
+
+struct b11925 {
+ int i;
+ char a[0];
+ char b[];
+};
+
+// issue 12030
+void issue12030conv(char *buf, double x) {
+ sprintf(buf, "d=%g", x);
+}
+
+// issue 14838
+
+int check_cbytes(char *b, size_t l) {
+ int i;
+ for (i = 0; i < l; i++) {
+ if (b[i] != i) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+// issue 17065
+// Test that C symbols larger than a page play nicely with the race detector.
+int ii[65537];
+
+// issue 17537
+// The void* cast introduced by cgo to avoid problems
+// with const/volatile qualifiers breaks C preprocessor macros that
+// emulate functions.
+
+typedef struct {
+ int i;
+} S17537;
+
+int I17537(S17537 *p);
+
+#define I17537(p) ((p)->i)
+
+// Calling this function used to fail without the cast.
+const int F17537(const char **p) {
+ return **p;
+}
+
+// issue 17723
+// API compatibility checks
+
+typedef char *cstring_pointer;
+static void cstring_pointer_fun(cstring_pointer dummy) { }
+const char *api_hello = "hello!";
+
+// Calling this function used to trigger an error from the C compiler
+// (issue 18298).
+void F18298(const void *const *p) {
+}
+
+// Test that conversions between typedefs work as they used to.
+typedef const void *T18298_1;
+struct S18298 { int i; };
+typedef const struct S18298 *T18298_2;
+void G18298(T18298_1 t) {
+}
+
+// issue 18126
+// cgo check of void function returning errno.
+void Issue18126C(void **p) {}
+
+// issue 18720
+
+#define HELLO "hello"
+#define WORLD "world"
+#define HELLO_WORLD HELLO "\000" WORLD
+
+struct foo { char c; };
+#define SIZE_OF(x) sizeof(x)
+#define SIZE_OF_FOO SIZE_OF(struct foo)
+#define VAR1 VAR
+#define VAR var
+int var = 5;
+
+#define ADDR &var
+
+#define CALL fn()
+int fn(void) {
+ return ++var;
+}
+
+// issue 20129
+
+int issue20129 = 0;
+typedef void issue20129Void;
+issue20129Void issue20129Foo() {
+ issue20129 = 1;
+}
+typedef issue20129Void issue20129Void2;
+issue20129Void2 issue20129Bar() {
+ issue20129 = 2;
+}
+
+// issue 20369
+#define XUINT64_MAX 18446744073709551615ULL
+
+// issue 21668
+// Fail to guess the kind of the constant "x".
+// No runtime test; just make sure it compiles.
+const int x21668 = 42;
+
+// issue 21708
+#define CAST_TO_INT64 (int64_t)(-1)
+
+// issue 21809
+// Compile C `typedef` to go type aliases.
+
+typedef long MySigned_t;
+// tests alias-to-alias
+typedef MySigned_t MySigned2_t;
+long takes_long(long x) { return x * x; }
+MySigned_t takes_typedef(MySigned_t x) { return x * x; }
+
+// issue 22906
+
+// It's going to be hard to include a whole real JVM to test this.
+// So we'll simulate a really easy JVM using just the parts we need.
+// This is the relevant part of jni.h.
+
+struct _jobject;
+
+typedef struct _jobject *jobject;
+typedef jobject jclass;
+typedef jobject jthrowable;
+typedef jobject jstring;
+typedef jobject jarray;
+typedef jarray jbooleanArray;
+typedef jarray jbyteArray;
+typedef jarray jcharArray;
+typedef jarray jshortArray;
+typedef jarray jintArray;
+typedef jarray jlongArray;
+typedef jarray jfloatArray;
+typedef jarray jdoubleArray;
+typedef jarray jobjectArray;
+
+typedef jobject jweak;
+
+// Note: jvalue is already a non-pointer type due to it being a C union.
+
+// issue 22958
+
+typedef struct {
+ unsigned long long f8 : 8;
+ unsigned long long f16 : 16;
+ unsigned long long f24 : 24;
+ unsigned long long f32 : 32;
+ unsigned long long f40 : 40;
+ unsigned long long f48 : 48;
+ unsigned long long f56 : 56;
+ unsigned long long f64 : 64;
+} issue22958Type;
+
+// issue 23356
+int a(void) { return 5; };
+int r(void) { return 3; };
+
+// issue 23720
+typedef int *issue23720A;
+typedef const int *issue23720B;
+void issue23720F(issue23720B a) {}
+
+// issue 24206
+#if defined(__linux__) && defined(__x86_64__)
+#include <sys/mman.h>
+// Returns string with null byte at the last valid address
+char* dangerousString1() {
+ int pageSize = 4096;
+ char *data = mmap(0, 2 * pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0);
+ mprotect(data + pageSize,pageSize,PROT_NONE);
+ int start = pageSize - 123 - 1; // last 123 bytes of first page + 1 null byte
+ int i = start;
+ for (; i < pageSize; i++) {
+ data[i] = 'x';
+ }
+ data[pageSize -1 ] = 0;
+ return data+start;
+}
+
+char* dangerousString2() {
+ int pageSize = 4096;
+ char *data = mmap(0, 3 * pageSize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0);
+ mprotect(data + 2 * pageSize,pageSize,PROT_NONE);
+ int start = pageSize - 123 - 1; // last 123 bytes of first page + 1 null byte
+ int i = start;
+ for (; i < 2 * pageSize; i++) {
+ data[i] = 'x';
+ }
+ data[2*pageSize -1 ] = 0;
+ return data+start;
+}
+#else
+char *dangerousString1() { return NULL; }
+char *dangerousString2() { return NULL; }
+#endif
+
+// issue 26066
+const unsigned long long int issue26066 = (const unsigned long long) -1;
+
+// issue 26517
+// Introduce two pointer types which are distinct, but have the same
+// base type. Make sure that both of those pointer types get resolved
+// correctly. Before the fix for 26517 if one of these pointer types
+// was resolved before the other one was processed, the second one
+// would never be resolved.
+// Before this issue was fixed this test failed on Windows,
+// where va_list expands to a named char* type.
+typedef va_list TypeOne;
+typedef char *TypeTwo;
+
+// issue 28540
+
+static void twoargs1(void *p, int n) {}
+static void *twoargs2() { return 0; }
+static int twoargs3(void * p) { return 0; }
+
+// issue 28545
+// Failed to add type conversion for negative constant.
+
+static void issue28545F(char **p, int n, complex double a) {}
+
+// issue 28772 part 1 - part 2 in testx.go
+// Failed to add type conversion for Go constant set to C constant.
+// No runtime test; just make sure it compiles.
+
+#define issue28772Constant 1
+
+// issue 28896
+// cgo was incorrectly adding padding after a packed struct.
+typedef struct {
+ void *f1;
+ uint32_t f2;
+} __attribute__((__packed__)) innerPacked;
+
+typedef struct {
+ innerPacked g1;
+ uint64_t g2;
+} outerPacked;
+
+typedef struct {
+ void *f1;
+ uint32_t f2;
+} innerUnpacked;
+
+typedef struct {
+ innerUnpacked g1;
+ uint64_t g2;
+} outerUnpacked;
+
+size_t offset(int x) {
+ switch (x) {
+ case 0:
+ return offsetof(innerPacked, f2);
+ case 1:
+ return offsetof(outerPacked, g2);
+ case 2:
+ return offsetof(innerUnpacked, f2);
+ case 3:
+ return offsetof(outerUnpacked, g2);
+ default:
+ abort();
+ }
+}
+
+// issue 29748
+
+typedef struct { char **p; } S29748;
+static int f29748(S29748 *p) { return 0; }
+
+// issue 29781
+// Error with newline inserted into constant expression.
+// Compilation test only, nothing to run.
+
+static void issue29781F(char **p, int n) {}
+#define ISSUE29781C 0
+
+// issue 31093
+static uint16_t issue31093F(uint16_t v) { return v; }
+
+// issue 32579
+typedef struct S32579 { unsigned char data[1]; } S32579;
+
+// issue 37033, cgo.Handle
+extern void GoFunc37033(uintptr_t handle);
+void cFunc37033(uintptr_t handle) { GoFunc37033(handle); }
+
+// issue 38649
+// Test that #define'd type aliases work.
+#define netbsd_gid unsigned int
+
+// issue 40494
+// Inconsistent handling of tagged enum and union types.
+enum Enum40494 { X_40494 };
+union Union40494 { int x; };
+void issue40494(enum Enum40494 e, union Union40494* up) {}
+
+// Issue 45451, bad handling of go:notinheap types.
+typedef struct issue45451Undefined issue45451;
+
+// Issue 49633, example of cgo.Handle with void*.
+extern void GoFunc49633(void*);
+void cfunc49633(void *context) { GoFunc49633(context); }
+
+*/
+import "C"
+
+import (
+ "context"
+ "fmt"
+ "math"
+ "math/rand"
+ "os"
+ "os/signal"
+ "reflect"
+ "runtime"
+ "runtime/cgo"
+ "sync"
+ "syscall"
+ "testing"
+ "time"
+ "unsafe"
+)
+
+// alignment
+
+func testAlign(t *testing.T) {
+ var evt C.SDL_KeyboardEvent
+ C.makeEvent(&evt)
+ if C.same(&evt, evt.typ, evt.which, evt.state, evt.keysym.scancode, evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode) == 0 {
+ t.Error("*** bad alignment")
+ C.cTest(&evt)
+ t.Errorf("Go: %#x %#x %#x %#x %#x %#x %#x\n",
+ evt.typ, evt.which, evt.state, evt.keysym.scancode,
+ evt.keysym.sym, evt.keysym.mod, evt.keysym.unicode)
+ t.Error(evt)
+ }
+}
+
+// api
+
+const greeting = "hello, world"
+
+type testPair struct {
+ Name string
+ Got, Want interface{}
+}
+
+var testPairs = []testPair{
+ {"GoString", C.GoString(C.greeting), greeting},
+ {"GoStringN", C.GoStringN(C.greeting, 5), greeting[:5]},
+ {"GoBytes", C.GoBytes(unsafe.Pointer(C.greeting), 5), []byte(greeting[:5])},
+}
+
+func testHelpers(t *testing.T) {
+ for _, pair := range testPairs {
+ if !reflect.DeepEqual(pair.Got, pair.Want) {
+ t.Errorf("%s: got %#v, want %#v", pair.Name, pair.Got, pair.Want)
+ }
+ }
+}
+
+// basic test cases
+
+const EINVAL = C.EINVAL /* test #define */
+
+var KILO = C.KILO
+
+func uuidgen() {
+ var uuid C.cgo_uuid_t
+ C.uuid_generate(&uuid[0])
+}
+
+func Strtol(s string, base int) (int, error) {
+ p := C.CString(s)
+ n, err := C.strtol(p, nil, C.int(base))
+ C.free(unsafe.Pointer(p))
+ return int(n), err
+}
+
+func Atol(s string) int {
+ p := C.CString(s)
+ n := C.atol(p)
+ C.free(unsafe.Pointer(p))
+ return int(n)
+}
+
+func testConst(t *testing.T) {
+ C.myConstFunc(nil, 0, nil)
+}
+
+func testEnum(t *testing.T) {
+ if C.Enum1 != 1 || C.Enum2 != 2 {
+ t.Error("bad enum", C.Enum1, C.Enum2)
+ }
+}
+
+func testNamedEnum(t *testing.T) {
+ e := new(C.enum_E)
+
+ *e = C.Enum1
+ if *e != 1 {
+ t.Error("bad enum", C.Enum1)
+ }
+
+ *e = C.Enum2
+ if *e != 2 {
+ t.Error("bad enum", C.Enum2)
+ }
+}
+
+func testCastToEnum(t *testing.T) {
+ e := C.enum_E(C.Enum1)
+ if e != 1 {
+ t.Error("bad enum", C.Enum1)
+ }
+
+ e = C.enum_E(C.Enum2)
+ if e != 2 {
+ t.Error("bad enum", C.Enum2)
+ }
+}
+
+func testAtol(t *testing.T) {
+ l := Atol("123")
+ if l != 123 {
+ t.Error("Atol 123: ", l)
+ }
+}
+
+func testErrno(t *testing.T) {
+ p := C.CString("no-such-file")
+ m := C.CString("r")
+ f, err := C.fopen(p, m)
+ C.free(unsafe.Pointer(p))
+ C.free(unsafe.Pointer(m))
+ if err == nil {
+ C.fclose(f)
+ t.Fatalf("C.fopen: should fail")
+ }
+ if err != syscall.ENOENT {
+ t.Fatalf("C.fopen: unexpected error: %v", err)
+ }
+}
+
+func testMultipleAssign(t *testing.T) {
+ p := C.CString("234")
+ n, m := C.strtol(p, nil, 345), C.strtol(p, nil, 10)
+ if runtime.GOOS == "openbsd" {
+ // Bug in OpenBSD strtol(3) - base > 36 succeeds.
+ if (n != 0 && n != 239089) || m != 234 {
+ t.Fatal("Strtol x2: ", n, m)
+ }
+ } else if n != 0 || m != 234 {
+ t.Fatal("Strtol x2: ", n, m)
+ }
+ C.free(unsafe.Pointer(p))
+}
+
+var (
+ cuint = (C.uint)(0)
+ culong C.ulong
+ cchar C.char
+)
+
+type Context struct {
+ ctx *C.struct_ibv_context
+}
+
+func benchCgoCall(b *testing.B) {
+ b.Run("add-int", func(b *testing.B) {
+ const x = C.int(2)
+ const y = C.int(3)
+
+ for i := 0; i < b.N; i++ {
+ C.add(x, y)
+ }
+ })
+
+ b.Run("one-pointer", func(b *testing.B) {
+ var a0 C.VkDeviceCreateInfo
+ for i := 0; i < b.N; i++ {
+ C.handleComplexPointer(&a0)
+ }
+ })
+ b.Run("eight-pointers", func(b *testing.B) {
+ var a0, a1, a2, a3, a4, a5, a6, a7 C.VkDeviceCreateInfo
+ for i := 0; i < b.N; i++ {
+ C.handleComplexPointer8(&a0, &a1, &a2, &a3, &a4, &a5, &a6, &a7)
+ }
+ })
+ b.Run("eight-pointers-nil", func(b *testing.B) {
+ var a0, a1, a2, a3, a4, a5, a6, a7 *C.VkDeviceCreateInfo
+ for i := 0; i < b.N; i++ {
+ C.handleComplexPointer8(a0, a1, a2, a3, a4, a5, a6, a7)
+ }
+ })
+ b.Run("eight-pointers-array", func(b *testing.B) {
+ var a [8]C.VkDeviceCreateInfo
+ for i := 0; i < b.N; i++ {
+ C.handleComplexPointer8(&a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7])
+ }
+ })
+ b.Run("eight-pointers-slice", func(b *testing.B) {
+ a := make([]C.VkDeviceCreateInfo, 8)
+ for i := 0; i < b.N; i++ {
+ C.handleComplexPointer8(&a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7])
+ }
+ })
+}
+
+// Benchmark measuring overhead from Go to C and back to Go (via a callback)
+func benchCallback(b *testing.B) {
+ var x = false
+ for i := 0; i < b.N; i++ {
+ nestedCall(func() { x = true })
+ }
+ if !x {
+ b.Fatal("nestedCall was not invoked")
+ }
+}
+
+var sinkString string
+
+func benchGoString(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ sinkString = C.GoString(C.cstr)
+ }
+ const want = "abcefghijklmnopqrstuvwxyzABCEFGHIJKLMNOPQRSTUVWXYZ1234567890"
+ if sinkString != want {
+ b.Fatalf("%q != %q", sinkString, want)
+ }
+}
+
+// Static (build-time) test that syntax traversal visits all operands of s[i:j:k].
+func sliceOperands(array [2000]int) {
+ _ = array[C.KILO:C.KILO:C.KILO] // no type error
+}
+
+// set in cgo_thread_lock.go init
+var testThreadLockFunc = func(*testing.T) {}
+
+// complex alignment
+
+func TestComplexAlign(t *testing.T) {
+ if C.cplxAlign.x != 3.14 {
+ t.Errorf("got %v, expected 3.14", C.cplxAlign.x)
+ }
+ if C.cplxAlign.y != 2.17 {
+ t.Errorf("got %v, expected 2.17", C.cplxAlign.y)
+ }
+}
+
+// constants and pointer checking
+
+func testCheckConst(t *testing.T) {
+ // The test is that this compiles successfully.
+ p := C.malloc(C.size_t(unsafe.Sizeof(C.int(0))))
+ defer C.free(p)
+ C.CheckConstFunc(&C.CheckConstStruct{(*C.int)(p)}, C.CheckConstVal)
+}
+
+// duplicate symbol
+
+func duplicateSymbols() {
+ fmt.Printf("%v %v %v\n", C.base_symbol, C.alias_one, C.alias_two)
+}
+
+// environment
+
+// This is really an os package test but here for convenience.
+func testSetEnv(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ // Go uses SetEnvironmentVariable on windows. However,
+ // C runtime takes a *copy* at process startup of the
+ // OS environment, and stores it in environ/envp.
+ // It is this copy that getenv/putenv manipulate.
+ t.Logf("skipping test")
+ return
+ }
+ const key = "CGO_OS_TEST_KEY"
+ const val = "CGO_OS_TEST_VALUE"
+ os.Setenv(key, val)
+ keyc := C.CString(key)
+ defer C.free(unsafe.Pointer(keyc))
+ v := C.getenv(keyc)
+ if uintptr(unsafe.Pointer(v)) == 0 {
+ t.Fatal("getenv returned NULL")
+ }
+ vs := C.GoString(v)
+ if vs != val {
+ t.Fatalf("getenv() = %q; want %q", vs, val)
+ }
+}
+
+// function pointer variables
+
+func callBridge(f C.intFunc) int {
+ return int(C.bridge_int_func(f))
+}
+
+func callCBridge(f C.intFunc) C.int {
+ return C.bridge_int_func(f)
+}
+
+func testFpVar(t *testing.T) {
+ const expected = 42
+ f := C.intFunc(C.fortytwo)
+ res1 := C.bridge_int_func(f)
+ if r1 := int(res1); r1 != expected {
+ t.Errorf("got %d, want %d", r1, expected)
+ }
+ res2 := callCBridge(f)
+ if r2 := int(res2); r2 != expected {
+ t.Errorf("got %d, want %d", r2, expected)
+ }
+ r3 := callBridge(f)
+ if r3 != expected {
+ t.Errorf("got %d, want %d", r3, expected)
+ }
+}
+
+// issue 1222
+type AsyncEvent struct {
+ event C.struct_ibv_async_event
+}
+
+// issue 1635
+
+func test1635(t *testing.T) {
+ C.scatter()
+ if v := C.hola; v != 0 {
+ t.Fatalf("C.hola is %d, should be 0", v)
+ }
+ if v := C.testHola(); v != 0 {
+ t.Fatalf("C.testHola() is %d, should be 0", v)
+ }
+}
+
+// issue 2470
+
+func testUnsignedInt(t *testing.T) {
+ a := (int64)(C.UINT32VAL)
+ b := (int64)(0xc008427b)
+ if a != b {
+ t.Errorf("Incorrect unsigned int - got %x, want %x", a, b)
+ }
+}
+
+// issue 3250
+
+func test3250(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("not applicable on windows")
+ }
+
+ t.Skip("skipped, see golang.org/issue/5885")
+ var (
+ thres = 1
+ sig = syscall_dot_SIGCHLD
+ )
+ type result struct {
+ n int
+ sig os.Signal
+ }
+ var (
+ sigCh = make(chan os.Signal, 10)
+ waitStart = make(chan struct{})
+ waitDone = make(chan result)
+ )
+
+ signal.Notify(sigCh, sig)
+
+ go func() {
+ n := 0
+ alarm := time.After(time.Second * 3)
+ for {
+ select {
+ case <-waitStart:
+ waitStart = nil
+ case v := <-sigCh:
+ n++
+ if v != sig || n > thres {
+ waitDone <- result{n, v}
+ return
+ }
+ case <-alarm:
+ waitDone <- result{n, sig}
+ return
+ }
+ }
+ }()
+
+ waitStart <- struct{}{}
+ C.testSendSIG()
+ r := <-waitDone
+ if r.sig != sig {
+ t.Fatalf("received signal %v, but want %v", r.sig, sig)
+ }
+ t.Logf("got %d signals\n", r.n)
+ if r.n <= thres {
+ t.Fatalf("expected more than %d", thres)
+ }
+}
+
+// issue 3261
+
+func testLibgcc(t *testing.T) {
+ var table = []struct {
+ in, out C.int
+ }{
+ {0, 0},
+ {1, 1},
+ {-42, 42},
+ {1000300, 1000300},
+ {1 - 1<<31, 1<<31 - 1},
+ }
+ for _, v := range table {
+ if o := C.vabs(v.in); o != v.out {
+ t.Fatalf("abs(%d) got %d, should be %d", v.in, o, v.out)
+ return
+ }
+ }
+}
+
+// issue 3729
+
+func test3729(t *testing.T) {
+ if runtime.GOOS == "windows" {
+ t.Skip("skipping on windows")
+ }
+
+ _, e := C.g()
+ if e != syscall.E2BIG {
+ t.Errorf("got %q, expect %q", e, syscall.E2BIG)
+ }
+ _, e = C.g2(C.EINVAL, C._expA, C._expB, C._expC, C._expD)
+ if e != syscall.EINVAL {
+ t.Errorf("got %q, expect %q", e, syscall.EINVAL)
+ }
+}
+
+// issue 3945
+
+func testPrintf(t *testing.T) {
+ C.say()
+}
+
+// issue 4054
+
+var issue4054a = []int{C.A, C.B, C.C, C.D, C.E, C.F, C.G, C.H, C.I, C.J}
+
+// issue 4339
+
+func test4339(t *testing.T) {
+ C.handle4339(&C.exported4339)
+}
+
+// issue 4417
+
+func testBoolAlign(t *testing.T) {
+ b := C.c_bool(true, true, 10, true, false)
+ if b != 10 {
+ t.Fatalf("found %d expected 10\n", b)
+ }
+ b = C.c_bool(true, true, 5, true, true)
+ if b != 5 {
+ t.Fatalf("found %d expected 5\n", b)
+ }
+ b = C.c_bool(true, true, 3, true, false)
+ if b != 3 {
+ t.Fatalf("found %d expected 3\n", b)
+ }
+ b = C.c_bool(false, false, 1, true, false)
+ if b != 1 {
+ t.Fatalf("found %d expected 1\n", b)
+ }
+ b = C.c_bool(false, true, 200, true, false)
+ if b != 200 {
+ t.Fatalf("found %d expected 200\n", b)
+ }
+}
+
+// issue 4857
+
+func test4857() {
+ _ = C.issue4857()
+}
+
+// issue 5224
+
+func testCflags(t *testing.T) {
+ is_windows := C.is_windows == 1
+ if is_windows != (runtime.GOOS == "windows") {
+ t.Errorf("is_windows: %v, runtime.GOOS: %s", is_windows, runtime.GOOS)
+ }
+ if C.common != 123 {
+ t.Errorf("common: %v (expected 123)", C.common)
+ }
+}
+
+// issue 5227
+
+func test5227(t *testing.T) {
+ C.init()
+}
+
+func selectfont() C.Fontinfo {
+ return C.SansTypeface
+}
+
+// issue 5242
+
+func test5242(t *testing.T) {
+ if got := C.issue5242(C.foo{}, C.bar{}); got != 5242 {
+ t.Errorf("got %v", got)
+ }
+}
+
+func test5603(t *testing.T) {
+ var x [5]int64
+ exp := int64(C.issue5603exp)
+ x[0] = int64(C.issue5603foo0())
+ x[1] = int64(C.issue5603foo1(nil))
+ x[2] = int64(C.issue5603foo2(nil, nil))
+ x[3] = int64(C.issue5603foo3(nil, nil, nil))
+ x[4] = int64(C.issue5603foo4(nil, nil, nil, nil))
+ for i, v := range x {
+ if v != exp {
+ t.Errorf("issue5603foo%d() returns %v, expected %v", i, v, exp)
+ }
+ }
+}
+
+// issue 5337
+
+func test5337(t *testing.T) {
+ C.test5337()
+}
+
+// issue 5740
+
+func test5740(t *testing.T) {
+ if v := C.test5740a() + C.test5740b(); v != 5 {
+ t.Errorf("expected 5, got %v", v)
+ }
+}
+
+// issue 5986
+
+func test5986(t *testing.T) {
+ C.output5986()
+}
+
+// issue 6128
+
+func test6128() {
+ // nothing to run, just make sure this compiles.
+ _ = C.X
+}
+
+// issue 6390
+
+func test6390(t *testing.T) {
+ p1 := C.malloc(1024)
+ if p1 == nil {
+ t.Fatalf("C.malloc(1024) returned nil")
+ }
+ p2 := C.malloc(0)
+ if p2 == nil {
+ t.Fatalf("C.malloc(0) returned nil")
+ }
+ C.free(p1)
+ C.free(p2)
+}
+
+func test6472() {
+ // nothing to run, just make sure this compiles
+ s := new(C.z)
+ println(s.y[0].x)
+}
+
+// issue 6506
+
+func test6506() {
+ // nothing to run, just make sure this compiles
+ var x C.size_t
+
+ C.calloc(x, x)
+ C.malloc(x)
+ C.realloc(nil, x)
+ C.memcpy(nil, nil, x)
+ C.memcmp(nil, nil, x)
+ C.memmove(nil, nil, x)
+ C.strncpy(nil, nil, x)
+ C.strncmp(nil, nil, x)
+ C.strncat(nil, nil, x)
+ x = C.strxfrm(nil, nil, x)
+ C.memchr(nil, 0, x)
+ x = C.strcspn(nil, nil)
+ x = C.strspn(nil, nil)
+ C.memset(nil, 0, x)
+ x = C.strlen(nil)
+ _ = x
+}
+
+// issue 6612
+
+func testNaming(t *testing.T) {
+ C.myfunc()
+ C.myfunc_def()
+ if v := C.myvar; v != 5 {
+ t.Errorf("C.myvar = %d, want 5", v)
+ }
+ if v := C.myvar_def; v != 5 {
+ t.Errorf("C.myvar_def = %d, want 5", v)
+ }
+ if s := C.GoString(C.mytext); s != "abcdef" {
+ t.Errorf("C.mytext = %q, want %q", s, "abcdef")
+ }
+ if s := C.GoString(C.mytext_def); s != "abcdef" {
+ t.Errorf("C.mytext_def = %q, want %q", s, "abcdef")
+ }
+ if c := C.myenum; c != 1234 {
+ t.Errorf("C.myenum = %v, want 1234", c)
+ }
+ if c := C.myenum_def; c != 1234 {
+ t.Errorf("C.myenum_def = %v, want 1234", c)
+ }
+ {
+ const c = C.myenum
+ if c != 1234 {
+ t.Errorf("C.myenum as const = %v, want 1234", c)
+ }
+ }
+ {
+ const c = C.myenum_def
+ if c != 1234 {
+ t.Errorf("C.myenum as const = %v, want 1234", c)
+ }
+ }
+ if c := C.myint_def; c != 12345 {
+ t.Errorf("C.myint_def = %v, want 12345", c)
+ }
+ {
+ const c = C.myint_def
+ if c != 12345 {
+ t.Errorf("C.myint as const = %v, want 12345", c)
+ }
+ }
+
+ if c := C.myfloat_def; c != 1.5 {
+ t.Errorf("C.myint_def = %v, want 1.5", c)
+ }
+ {
+ const c = C.myfloat_def
+ if c != 1.5 {
+ t.Errorf("C.myint as const = %v, want 1.5", c)
+ }
+ }
+
+ if s := C.mystring_def; s != "hello" {
+ t.Errorf("C.mystring_def = %q, want %q", s, "hello")
+ }
+}
+
+// issue 6907
+
+func test6907(t *testing.T) {
+ want := "yarn"
+ if got := C.GoString(C.Issue6907CopyString(want)); got != want {
+ t.Errorf("C.GoString(C.Issue6907CopyString(%q)) == %q, want %q", want, got, want)
+ }
+}
+
+// issue 7560
+
+func test7560(t *testing.T) {
+ // some mingw don't implement __packed__ correctly.
+ if C.offset7560() != 1 {
+ t.Skip("C compiler did not pack struct")
+ }
+
+ // C.misaligned should have x but then a padding field to get to the end of the struct.
+ // There should not be a field named 'y'.
+ var v C.misaligned
+ rt := reflect.TypeOf(&v).Elem()
+ if rt.NumField() != 2 || rt.Field(0).Name != "x" || rt.Field(1).Name != "_" {
+ t.Errorf("unexpected fields in C.misaligned:\n")
+ for i := 0; i < rt.NumField(); i++ {
+ t.Logf("%+v\n", rt.Field(i))
+ }
+ }
+}
+
+// issue 7786
+
+func f() {
+ var x1 *C.typedef_test7786
+ var x2 *C.struct_test7786
+ x1 = x2
+ x2 = x1
+ C.f7786(x1)
+ C.f7786(x2)
+ C.g7786(x1)
+ C.g7786(x2)
+
+ var b1 *C.typedef_body7786
+ var b2 *C.struct_body7786
+ b1 = b2
+ b2 = b1
+ C.b7786(b1)
+ C.b7786(b2)
+ C.c7786(b1)
+ C.c7786(b2)
+
+ var u1 *C.typedef_union7786
+ var u2 *C.union_union7786
+ u1 = u2
+ u2 = u1
+ C.u7786(u1)
+ C.u7786(u2)
+ C.v7786(u1)
+ C.v7786(u2)
+}
+
+// issue 8092
+
+func test8092(t *testing.T) {
+ tests := []struct {
+ s string
+ a, b *C.char
+ }{
+ {"text", &C.text[0], C.ctext()},
+ {"data", &C.data[0], C.cdata()},
+ }
+ for _, test := range tests {
+ if test.a != test.b {
+ t.Errorf("%s: pointer mismatch: %v != %v", test.s, test.a, test.b)
+ }
+ if got := C.GoString(test.a); got != test.s {
+ t.Errorf("%s: points at %#v, want %#v", test.s, got, test.s)
+ }
+ }
+}
+
+// issues 8368 and 8441
+
+func issue8368(one *C.struct_one, two *C.struct_two) {
+}
+
+func issue8441(one *C.one, two *C.two) {
+ issue8441(two.x, one.x)
+}
+
+// issue 8428
+
+var _ = C.struct_issue8428one{
+ b: C.char(0),
+ // The trailing rest field is not available in cgo.
+ // See issue 11925.
+ // rest: [0]C.char{},
+}
+
+var _ = C.struct_issue8428two{
+ p: unsafe.Pointer(nil),
+ b: C.char(0),
+ rest: [0]C.char{},
+}
+
+var _ = C.struct_issue8428three{
+ w: [1][2][3][0]C.char{},
+ x: [2][3][0][1]C.char{},
+ y: [3][0][1][2]C.char{},
+ z: [0][1][2][3]C.char{},
+}
+
+// issue 8811
+
+func test8811(t *testing.T) {
+ C.issue8811Execute()
+}
+
+// issue 9557
+
+func test9557(t *testing.T) {
+ // implicitly dereference a Go variable
+ foo := C.issue9557foo
+ if v := foo.a; v != 42 {
+ t.Fatalf("foo.a expected 42, but got %d", v)
+ }
+
+ // explicitly dereference a C variable
+ if v := (*C.issue9557foo).a; v != 42 {
+ t.Fatalf("(*C.issue9557foo).a expected 42, but is %d", v)
+ }
+
+ // implicitly dereference a C variable
+ if v := C.issue9557foo.a; v != 42 {
+ t.Fatalf("C.issue9557foo.a expected 42, but is %d", v)
+ }
+}
+
+// issue 8331 part 1
+
+func issue8331a() C.issue8331 {
+ return issue8331Var
+}
+
+// issue 10303
+
+func test10303(t *testing.T, n int) {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("gccgo permits C pointers on the stack")
+ }
+
+ // Run at a few different stack depths just to avoid an unlucky pass
+ // due to variables ending up on different pages.
+ if n > 0 {
+ test10303(t, n-1)
+ }
+ if t.Failed() {
+ return
+ }
+ var x, y, z, v, si C.int
+ var s C.Struct
+ C.setintstar(&x)
+ C.setintptr(&y)
+ C.setvoidptr(unsafe.Pointer(&v))
+ s.P = &si
+ C.setstruct(s)
+
+ if uintptr(unsafe.Pointer(&x))&^0xfff == uintptr(unsafe.Pointer(&z))&^0xfff {
+ t.Error("C int* argument on stack")
+ }
+ if uintptr(unsafe.Pointer(&y))&^0xfff == uintptr(unsafe.Pointer(&z))&^0xfff {
+ t.Error("C intptr argument on stack")
+ }
+ if uintptr(unsafe.Pointer(&v))&^0xfff == uintptr(unsafe.Pointer(&z))&^0xfff {
+ t.Error("C void* argument on stack")
+ }
+ if uintptr(unsafe.Pointer(&si))&^0xfff == uintptr(unsafe.Pointer(&z))&^0xfff {
+ t.Error("C struct field pointer on stack")
+ }
+}
+
+// issue 11925
+
+func test11925(t *testing.T) {
+ if C.sizeof_struct_a11925 != unsafe.Sizeof(C.struct_a11925{}) {
+ t.Errorf("size of a changed: C %d, Go %d", C.sizeof_struct_a11925, unsafe.Sizeof(C.struct_a11925{}))
+ }
+ if C.sizeof_struct_b11925 != unsafe.Sizeof(C.struct_b11925{}) {
+ t.Errorf("size of b changed: C %d, Go %d", C.sizeof_struct_b11925, unsafe.Sizeof(C.struct_b11925{}))
+ }
+}
+
+// issue 12030
+
+func test12030(t *testing.T) {
+ buf := (*C.char)(C.malloc(256))
+ defer C.free(unsafe.Pointer(buf))
+ for _, f := range []float64{1.0, 2.0, 3.14} {
+ C.issue12030conv(buf, C.double(f))
+ got := C.GoString(buf)
+ if want := fmt.Sprintf("d=%g", f); got != want {
+ t.Fatalf("C.sprintf failed for %g: %q != %q", f, got, want)
+ }
+ }
+}
+
+// issue 13402
+
+var _ C.complexfloat
+var _ C.complexdouble
+
+// issue 13930
+// Test that cgo's multiple-value special form for
+// C function calls works in variable declaration statements.
+
+var _, _ = C.abs(0)
+
+// issue 14838
+
+func test14838(t *testing.T) {
+ data := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+ cData := C.CBytes(data)
+ defer C.free(cData)
+
+ if C.check_cbytes((*C.char)(cData), C.size_t(len(data))) == 0 {
+ t.Fatalf("mismatched data: expected %v, got %v", data, (*(*[10]byte)(unsafe.Pointer(cData)))[:])
+ }
+}
+
+// issue 17065
+
+var sink C.int
+
+func test17065(t *testing.T) {
+ if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
+ t.Skip("broken on darwin; issue 17065")
+ }
+ for i := range C.ii {
+ sink = C.ii[i]
+ }
+}
+
+// issue 17537
+
+func test17537(t *testing.T) {
+ v := C.S17537{i: 17537}
+ if got, want := C.I17537(&v), C.int(17537); got != want {
+ t.Errorf("got %d, want %d", got, want)
+ }
+
+ p := (*C.char)(C.malloc(1))
+ *p = 17
+ if got, want := C.F17537(&p), C.int(17); got != want {
+ t.Errorf("got %d, want %d", got, want)
+ }
+
+ C.F18298(nil)
+ var v18298 C.T18298_2
+ C.G18298(C.T18298_1(v18298))
+}
+
+// issue 17723
+
+func testAPI() {
+ var cs *C.char
+ cs = C.CString("hello")
+ defer C.free(unsafe.Pointer(cs))
+ var s string
+ s = C.GoString((*C.char)(C.api_hello))
+ s = C.GoStringN((*C.char)(C.api_hello), C.int(6))
+ var b []byte
+ b = C.GoBytes(unsafe.Pointer(C.api_hello), C.int(6))
+ _, _ = s, b
+ C.cstring_pointer_fun(nil)
+}
+
+// issue 18126
+
+func test18126(t *testing.T) {
+ p := C.malloc(1)
+ _, err := C.Issue18126C(&p)
+ C.free(p)
+ _ = err
+}
+
+// issue 18720
+
+func test18720(t *testing.T) {
+ if got, want := C.HELLO_WORLD, "hello\000world"; got != want {
+ t.Errorf("C.HELLO_WORLD == %q, expected %q", got, want)
+ }
+
+ if got, want := C.VAR1, C.int(5); got != want {
+ t.Errorf("C.VAR1 == %v, expected %v", got, want)
+ }
+
+ if got, want := *C.ADDR, C.int(5); got != want {
+ t.Errorf("*C.ADDR == %v, expected %v", got, want)
+ }
+
+ if got, want := C.CALL, C.int(6); got != want {
+ t.Errorf("C.CALL == %v, expected %v", got, want)
+ }
+
+ if got, want := C.CALL, C.int(7); got != want {
+ t.Errorf("C.CALL == %v, expected %v", got, want)
+ }
+
+ // Issue 20125.
+ if got, want := C.SIZE_OF_FOO, 1; got != want {
+ t.Errorf("C.SIZE_OF_FOO == %v, expected %v", got, want)
+ }
+}
+
+// issue 20129
+
+func test20129(t *testing.T) {
+ if C.issue20129 != 0 {
+ t.Fatal("test is broken")
+ }
+ C.issue20129Foo()
+ if C.issue20129 != 1 {
+ t.Errorf("got %v but expected %v", C.issue20129, 1)
+ }
+ C.issue20129Bar()
+ if C.issue20129 != 2 {
+ t.Errorf("got %v but expected %v", C.issue20129, 2)
+ }
+}
+
+// issue 20369
+
+func test20369(t *testing.T) {
+ if C.XUINT64_MAX != math.MaxUint64 {
+ t.Fatalf("got %v, want %v", uint64(C.XUINT64_MAX), uint64(math.MaxUint64))
+ }
+}
+
+// issue 21668
+
+var issue21668_X = C.x21668
+
+// issue 21708
+
+func test21708(t *testing.T) {
+ if got, want := C.CAST_TO_INT64, -1; got != want {
+ t.Errorf("C.CAST_TO_INT64 == %v, expected %v", got, want)
+ }
+}
+
+// issue 21809
+
+func test21809(t *testing.T) {
+ longVar := C.long(3)
+ typedefVar := C.MySigned_t(4)
+ typedefTypedefVar := C.MySigned2_t(5)
+
+ // all three should be considered identical to `long`
+ if ret := C.takes_long(longVar); ret != 9 {
+ t.Errorf("got %v but expected %v", ret, 9)
+ }
+ if ret := C.takes_long(typedefVar); ret != 16 {
+ t.Errorf("got %v but expected %v", ret, 16)
+ }
+ if ret := C.takes_long(typedefTypedefVar); ret != 25 {
+ t.Errorf("got %v but expected %v", ret, 25)
+ }
+
+ // They should also be identical to the typedef'd type
+ if ret := C.takes_typedef(longVar); ret != 9 {
+ t.Errorf("got %v but expected %v", ret, 9)
+ }
+ if ret := C.takes_typedef(typedefVar); ret != 16 {
+ t.Errorf("got %v but expected %v", ret, 16)
+ }
+ if ret := C.takes_typedef(typedefTypedefVar); ret != 25 {
+ t.Errorf("got %v but expected %v", ret, 25)
+ }
+}
+
+// issue 22906
+
+func test22906(t *testing.T) {
+ var x1 C.jobject = 0 // Note: 0, not nil. That makes sure we use uintptr for these types.
+ _ = x1
+ var x2 C.jclass = 0
+ _ = x2
+ var x3 C.jthrowable = 0
+ _ = x3
+ var x4 C.jstring = 0
+ _ = x4
+ var x5 C.jarray = 0
+ _ = x5
+ var x6 C.jbooleanArray = 0
+ _ = x6
+ var x7 C.jbyteArray = 0
+ _ = x7
+ var x8 C.jcharArray = 0
+ _ = x8
+ var x9 C.jshortArray = 0
+ _ = x9
+ var x10 C.jintArray = 0
+ _ = x10
+ var x11 C.jlongArray = 0
+ _ = x11
+ var x12 C.jfloatArray = 0
+ _ = x12
+ var x13 C.jdoubleArray = 0
+ _ = x13
+ var x14 C.jobjectArray = 0
+ _ = x14
+ var x15 C.jweak = 0
+ _ = x15
+}
+
+// issue 22958
+// Nothing to run, just make sure this compiles.
+var Vissue22958 C.issue22958Type
+
+func test23356(t *testing.T) {
+ if got, want := C.a(), C.int(5); got != want {
+ t.Errorf("C.a() == %v, expected %v", got, want)
+ }
+ if got, want := C.r(), C.int(3); got != want {
+ t.Errorf("C.r() == %v, expected %v", got, want)
+ }
+}
+
+// issue 23720
+
+func Issue23720F() {
+ var x C.issue23720A
+ C.issue23720F(x)
+}
+
+// issue 24206
+
+func test24206(t *testing.T) {
+ if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
+ t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
+ }
+
+ if l := len(C.GoString(C.dangerousString1())); l != 123 {
+ t.Errorf("Incorrect string length - got %d, want 123", l)
+ }
+ if l := len(C.GoString(C.dangerousString2())); l != 4096+123 {
+ t.Errorf("Incorrect string length - got %d, want %d", l, 4096+123)
+ }
+}
+
+// issue 25143
+
+func issue25143sum(ns ...C.int) C.int {
+ total := C.int(0)
+ for _, n := range ns {
+ total += n
+ }
+ return total
+}
+
+func test25143(t *testing.T) {
+ if got, want := issue25143sum(1, 2, 3), C.int(6); got != want {
+ t.Errorf("issue25143sum(1, 2, 3) == %v, expected %v", got, want)
+ }
+}
+
+// issue 26066
+// Wrong type of constant with GCC 8 and newer.
+
+func test26066(t *testing.T) {
+ var i = int64(C.issue26066)
+ if i != -1 {
+ t.Errorf("got %d, want -1", i)
+ }
+}
+
+// issue 26517
+var a C.TypeOne
+var b C.TypeTwo
+
+// issue 27660
+// Stress the interaction between the race detector and cgo in an
+// attempt to reproduce the memory corruption described in #27660.
+// The bug was very timing sensitive; at the time of writing this
+// test would only trigger the bug about once out of every five runs.
+
+func test27660(t *testing.T) {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ ints := make([]int, 100)
+ locks := make([]sync.Mutex, 100)
+ // Slowly create threads so that ThreadSanitizer is forced to
+ // frequently resize its SyncClocks.
+ for i := 0; i < 100; i++ {
+ go func() {
+ for ctx.Err() == nil {
+ // Sleep in C for long enough that it is likely that the runtime
+ // will retake this goroutine's currently wired P.
+ C.usleep(1000 /* 1ms */)
+ runtime.Gosched() // avoid starvation (see #28701)
+ }
+ }()
+ go func() {
+ // Trigger lots of synchronization and memory reads/writes to
+ // increase the likelihood that the race described in #27660
+ // results in corruption of ThreadSanitizer's internal state
+ // and thus an assertion failure or segfault.
+ i := 0
+ for ctx.Err() == nil {
+ j := rand.Intn(100)
+ locks[j].Lock()
+ ints[j]++
+ locks[j].Unlock()
+ // needed for gccgo, to avoid creation of an
+ // unpreemptible "fast path" in this loop. Choice
+ // of (1<<24) is somewhat arbitrary.
+ if i%(1<<24) == 0 {
+ runtime.Gosched()
+ }
+ i++
+
+ }
+ }()
+ time.Sleep(time.Millisecond)
+ }
+}
+
+// issue 28540
+
+func twoargsF() {
+ var v struct{ p *byte }
+ C.twoargs1(C.twoargs2(), C.twoargs3(unsafe.Pointer(&v)))
+}
+
+// issue 28545
+
+func issue28545G(p **C.char) {
+ C.issue28545F(p, -1, (0))
+ C.issue28545F(p, 2+3, complex(1, 1))
+ C.issue28545F(p, issue28772Constant, issue28772Constant2)
+}
+
+// issue 28772 part 1 - part 2 in testx.go
+
+const issue28772Constant = C.issue28772Constant
+
+// issue 28896
+
+func offset(i int) uintptr {
+ var pi C.innerPacked
+ var po C.outerPacked
+ var ui C.innerUnpacked
+ var uo C.outerUnpacked
+ switch i {
+ case 0:
+ return unsafe.Offsetof(pi.f2)
+ case 1:
+ return unsafe.Offsetof(po.g2)
+ case 2:
+ return unsafe.Offsetof(ui.f2)
+ case 3:
+ return unsafe.Offsetof(uo.g2)
+ default:
+ panic("can't happen")
+ }
+}
+
+func test28896(t *testing.T) {
+ for i := 0; i < 4; i++ {
+ c := uintptr(C.offset(C.int(i)))
+ g := offset(i)
+ if c != g {
+ t.Errorf("%d: C: %d != Go %d", i, c, g)
+ }
+ }
+}
+
+// issue 29383
+// cgo's /*line*/ comments failed when inserted after '/',
+// because the result looked like a "//" comment.
+// No runtime test; just make sure it compiles.
+
+func Issue29383(n, size uint) int {
+ if ^C.size_t(0)/C.size_t(n) < C.size_t(size) {
+ return 0
+ }
+ return 0
+}
+
+// issue 29748
+// Error handling a struct initializer that requires pointer checking.
+// Compilation test only, nothing to run.
+
+var Vissue29748 = C.f29748(&C.S29748{
+ nil,
+})
+
+func Fissue299748() {
+ C.f29748(&C.S29748{
+ nil,
+ })
+}
+
+// issue 29781
+
+var issue29781X struct{ X int }
+
+func issue29781F(...int) int { return 0 }
+
+func issue29781G() {
+ var p *C.char
+ C.issue29781F(&p, C.ISSUE29781C+1)
+ C.issue29781F(nil, (C.int)(
+ 0))
+ C.issue29781F(&p, (C.int)(0))
+ C.issue29781F(&p, (C.int)(
+ 0))
+ C.issue29781F(&p, (C.int)(issue29781X.
+ X))
+}
+
+// issue 30065
+
+func test30065(t *testing.T) {
+ var a [256]byte
+ b := []byte("a")
+ C.memcpy(unsafe.Pointer(&a), unsafe.Pointer(&b[0]), 1)
+ if a[0] != 'a' {
+ t.Errorf("&a failed: got %c, want %c", a[0], 'a')
+ }
+
+ b = []byte("b")
+ C.memcpy(unsafe.Pointer(&a[0]), unsafe.Pointer(&b[0]), 1)
+ if a[0] != 'b' {
+ t.Errorf("&a[0] failed: got %c, want %c", a[0], 'b')
+ }
+
+ d := make([]byte, 256)
+ b = []byte("c")
+ C.memcpy(unsafe.Pointer(&d[0]), unsafe.Pointer(&b[0]), 1)
+ if d[0] != 'c' {
+ t.Errorf("&d[0] failed: got %c, want %c", d[0], 'c')
+ }
+}
+
+// issue 31093
+// No runtime test; just make sure it compiles.
+
+func Issue31093() {
+ C.issue31093F(C.ushort(0))
+}
+
+// issue 32579
+
+func test32579(t *testing.T) {
+ var s [1]C.struct_S32579
+ C.memset(unsafe.Pointer(&s[0].data[0]), 1, 1)
+ if s[0].data[0] != 1 {
+ t.Errorf("&s[0].data[0] failed: got %d, want %d", s[0].data[0], 1)
+ }
+}
+
+// issue 37033, check if cgo.Handle works properly
+
+func testHandle(t *testing.T) {
+ ch := make(chan int)
+
+ for i := 0; i < 42; i++ {
+ h := cgo.NewHandle(ch)
+ go func() {
+ C.cFunc37033(C.uintptr_t(h))
+ }()
+ if v := <-ch; issue37033 != v {
+ t.Fatalf("unexpected receiving value: got %d, want %d", v, issue37033)
+ }
+ h.Delete()
+ }
+}
+
+// issue 38649
+
+var issue38649 C.netbsd_gid = 42
+
+// issue 39877
+
+var issue39877 *C.void = nil
+
+// issue 40494
+// No runtime test; just make sure it compiles.
+
+func Issue40494() {
+ C.issue40494(C.enum_Enum40494(C.X_40494), (*C.union_Union40494)(nil))
+}
+
+// Issue 45451.
+func test45451(t *testing.T) {
+ var u *C.issue45451
+ typ := reflect.ValueOf(u).Type().Elem()
+
+ // The type is undefined in C so allocating it should panic.
+ defer func() {
+ if r := recover(); r == nil {
+ t.Error("expected panic")
+ }
+ }()
+
+ _ = reflect.New(typ)
+ t.Errorf("reflect.New(%v) should have panicked", typ)
+}
+
+// issue 52542
+
+func func52542[T ~[]C.int]() {}
+
+type type52542[T ~*C.float] struct{}
diff --git a/src/cmd/cgo/internal/test/test26213.go b/src/cmd/cgo/internal/test/test26213.go
new file mode 100644
index 0000000..04f8e84
--- /dev/null
+++ b/src/cmd/cgo/internal/test/test26213.go
@@ -0,0 +1,17 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build cgo
+
+package cgotest
+
+import (
+ "testing"
+
+ "cmd/cgo/internal/test/issue26213"
+)
+
+func test26213(t *testing.T) {
+ issue26213.Test26213(t)
+}
diff --git a/src/cmd/cgo/internal/test/test_unix.go b/src/cmd/cgo/internal/test/test_unix.go
new file mode 100644
index 0000000..664c485
--- /dev/null
+++ b/src/cmd/cgo/internal/test/test_unix.go
@@ -0,0 +1,11 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build unix
+
+package cgotest
+
+import "syscall"
+
+var syscall_dot_SIGCHLD = syscall.SIGCHLD
diff --git a/src/cmd/cgo/internal/test/test_windows.go b/src/cmd/cgo/internal/test/test_windows.go
new file mode 100644
index 0000000..7bfb33a
--- /dev/null
+++ b/src/cmd/cgo/internal/test/test_windows.go
@@ -0,0 +1,9 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+import "syscall"
+
+var syscall_dot_SIGCHLD syscall.Signal
diff --git a/src/cmd/cgo/internal/test/testx.c b/src/cmd/cgo/internal/test/testx.c
new file mode 100644
index 0000000..1258e32
--- /dev/null
+++ b/src/cmd/cgo/internal/test/testx.c
@@ -0,0 +1,24 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "_cgo_export.h"
+
+void lockOSThreadC(void) {
+ lockOSThreadCallback();
+}
+
+void issue7978c(uint32_t *sync) {
+ while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 0)
+ ;
+ __atomic_add_fetch(sync, 1, __ATOMIC_SEQ_CST);
+ while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 2)
+ ;
+ issue7978cb();
+ __atomic_add_fetch(sync, 1, __ATOMIC_SEQ_CST);
+ while(__atomic_load_n(sync, __ATOMIC_SEQ_CST) != 6)
+ ;
+}
+
+void f7665(void) {
+}
diff --git a/src/cmd/cgo/internal/test/testx.go b/src/cmd/cgo/internal/test/testx.go
new file mode 100644
index 0000000..0e2a51a
--- /dev/null
+++ b/src/cmd/cgo/internal/test/testx.go
@@ -0,0 +1,597 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test cases for cgo.
+// Both the import "C" prologue and the main file are sorted by issue number.
+// This file contains //export directives on Go functions
+// and so it must NOT contain C definitions (only declarations).
+// See test.go for C definitions.
+
+package cgotest
+
+import (
+ "runtime"
+ "runtime/cgo"
+ "runtime/debug"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "unsafe"
+)
+
+/*
+// threads
+extern void doAdd(int, int);
+extern int callGoInCThread(int);
+
+// issue 1328
+void IntoC(void);
+
+// issue 1560
+extern void Issue1560InC(void);
+
+// twoSleep returns the absolute start time of the first sleep
+// in ms.
+long long twoSleep(int);
+
+// issue 3775
+void lockOSThreadC(void);
+int usleep(unsigned usec);
+
+// issue 4054 part 2 - part 1 in test.go
+typedef enum {
+ A = 0,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ II,
+ J,
+} issue4054b;
+
+// issue 5548
+
+extern int issue5548_in_c(void);
+
+// issue 6833
+
+extern unsigned long long issue6833Func(unsigned int, unsigned long long);
+
+// issue 6907
+
+extern int CheckIssue6907C(_GoString_);
+
+// issue 7665
+
+extern void f7665(void);
+
+// issue 7978
+// Stack tracing didn't work during cgo code after calling a Go
+// callback. Make sure GC works and the stack trace is correct.
+
+#include <stdint.h>
+
+// use ugly atomic variable sync since that doesn't require calling back into
+// Go code or OS dependencies
+void issue7978c(uint32_t *sync);
+
+// issue 8331 part 2 - part 1 in test.go
+// A typedef of an unnamed struct is the same struct when
+// #include'd twice. No runtime test; just make sure it compiles.
+#include "issue8331.h"
+
+// issue 8945
+
+typedef void (*PFunc8945)();
+extern PFunc8945 func8945; // definition is in test.go
+
+// issue 20910
+void callMulti(void);
+
+// issue 28772 part 2 - part 1 in issuex.go
+#define issue28772Constant2 2
+
+
+// issue 31891
+typedef struct {
+ long obj;
+} Issue31891A;
+
+typedef struct {
+ long obj;
+} Issue31891B;
+
+void callIssue31891(void);
+
+typedef struct {
+ int i;
+} Issue38408, *PIssue38408;
+
+extern void cfunc49633(void*); // definition is in test.go
+*/
+import "C"
+
+// exports
+
+//export ReturnIntLong
+func ReturnIntLong() (int, C.long) {
+ return 1, 2
+}
+
+//export gc
+func gc() {
+ runtime.GC()
+}
+
+// threads
+
+var sum struct {
+ sync.Mutex
+ i int
+}
+
+//export Add
+func Add(x int) {
+ defer func() {
+ recover()
+ }()
+ sum.Lock()
+ sum.i += x
+ sum.Unlock()
+ var p *int
+ *p = 2
+}
+
+//export goDummy
+func goDummy() {
+}
+
+func testCthread(t *testing.T) {
+ if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && runtime.GOARCH == "arm64" {
+ t.Skip("the iOS exec wrapper is unable to properly handle the panic from Add")
+ }
+ sum.i = 0
+ C.doAdd(10, 6)
+
+ want := 10 * (10 - 1) / 2 * 6
+ if sum.i != want {
+ t.Fatalf("sum=%d, want %d", sum.i, want)
+ }
+}
+
+// Benchmark measuring overhead from C to Go in a C thread.
+// Create a new C thread and invoke Go function repeatedly in the new C thread.
+func benchCGoInCthread(b *testing.B) {
+ n := C.callGoInCThread(C.int(b.N))
+ if int(n) != b.N {
+ b.Fatal("unmatch loop times")
+ }
+}
+
+// issue 1328
+
+//export BackIntoGo
+func BackIntoGo() {
+ x := 1
+
+ for i := 0; i < 10000; i++ {
+ xvariadic(x)
+ if x != 1 {
+ panic("x is not 1?")
+ }
+ }
+}
+
+func xvariadic(x ...interface{}) {
+}
+
+func test1328(t *testing.T) {
+ C.IntoC()
+}
+
+// issue 1560
+// Test that C functions and Go functions run in parallel.
+
+var (
+ issue1560 int32
+
+ issue1560Ch = make(chan bool, 2)
+)
+
+//export Issue1560FromC
+func Issue1560FromC() {
+ for atomic.LoadInt32(&issue1560) != 1 {
+ runtime.Gosched()
+ }
+ atomic.AddInt32(&issue1560, 1)
+ for atomic.LoadInt32(&issue1560) != 3 {
+ runtime.Gosched()
+ }
+ issue1560Ch <- true
+}
+
+func Issue1560FromGo() {
+ atomic.AddInt32(&issue1560, 1)
+ for atomic.LoadInt32(&issue1560) != 2 {
+ runtime.Gosched()
+ }
+ atomic.AddInt32(&issue1560, 1)
+ issue1560Ch <- true
+}
+
+func test1560(t *testing.T) {
+ go Issue1560FromGo()
+ go C.Issue1560InC()
+ <-issue1560Ch
+ <-issue1560Ch
+}
+
+// issue 2462
+
+//export exportbyte
+func exportbyte() byte {
+ return 0
+}
+
+//export exportbool
+func exportbool() bool {
+ return false
+}
+
+//export exportrune
+func exportrune() rune {
+ return 0
+}
+
+//export exporterror
+func exporterror() error {
+ return nil
+}
+
+//export exportint
+func exportint() int {
+ return 0
+}
+
+//export exportuint
+func exportuint() uint {
+ return 0
+}
+
+//export exportuintptr
+func exportuintptr() uintptr {
+ return (uintptr)(0)
+}
+
+//export exportint8
+func exportint8() int8 {
+ return 0
+}
+
+//export exportuint8
+func exportuint8() uint8 {
+ return 0
+}
+
+//export exportint16
+func exportint16() int16 {
+ return 0
+}
+
+//export exportuint16
+func exportuint16() uint16 {
+ return 0
+}
+
+//export exportint32
+func exportint32() int32 {
+ return 0
+}
+
+//export exportuint32
+func exportuint32() uint32 {
+ return 0
+}
+
+//export exportint64
+func exportint64() int64 {
+ return 0
+}
+
+//export exportuint64
+func exportuint64() uint64 {
+ return 0
+}
+
+//export exportfloat32
+func exportfloat32() float32 {
+ return 0
+}
+
+//export exportfloat64
+func exportfloat64() float64 {
+ return 0
+}
+
+//export exportcomplex64
+func exportcomplex64() complex64 {
+ return 0
+}
+
+//export exportcomplex128
+func exportcomplex128() complex128 {
+ return 0
+}
+
+// issue 3741
+
+//export exportSliceIn
+func exportSliceIn(s []byte) bool {
+ return len(s) == cap(s)
+}
+
+//export exportSliceOut
+func exportSliceOut() []byte {
+ return []byte{1}
+}
+
+//export exportSliceInOut
+func exportSliceInOut(s []byte) []byte {
+ return s
+}
+
+// issue 3775
+
+func init() {
+ if runtime.GOOS == "android" {
+ return
+ }
+ // Same as test3775 but run during init so that
+ // there are two levels of internal runtime lock
+ // (1 for init, 1 for cgo).
+ // This would have been broken by CL 11663043.
+ C.lockOSThreadC()
+}
+
+func test3775(t *testing.T) {
+ if runtime.GOOS == "android" {
+ return
+ }
+ // Used to panic because of the UnlockOSThread below.
+ C.lockOSThreadC()
+}
+
+//export lockOSThreadCallback
+func lockOSThreadCallback() {
+ runtime.LockOSThread()
+ runtime.UnlockOSThread()
+ go C.usleep(10000)
+ runtime.Gosched()
+}
+
+// issue 4054 part 2 - part 1 in test.go
+
+var issue4054b = []int{C.A, C.B, C.C, C.D, C.E, C.F, C.G, C.H, C.II, C.J}
+
+//export issue5548FromC
+func issue5548FromC(s string, i int) int {
+ if len(s) == 4 && s == "test" && i == 42 {
+ return 12345
+ }
+ println("got", len(s), i)
+ return 9876
+}
+
+func test5548(t *testing.T) {
+ if x := C.issue5548_in_c(); x != 12345 {
+ t.Errorf("issue5548_in_c = %d, want %d", x, 12345)
+ }
+}
+
+// issue 6833
+
+//export GoIssue6833Func
+func GoIssue6833Func(aui uint, aui64 uint64) uint64 {
+ return aui64 + uint64(aui)
+}
+
+func test6833(t *testing.T) {
+ ui := 7
+ ull := uint64(0x4000300020001000)
+ v := uint64(C.issue6833Func(C.uint(ui), C.ulonglong(ull)))
+ exp := uint64(ui) + ull
+ if v != exp {
+ t.Errorf("issue6833Func() returns %x, expected %x", v, exp)
+ }
+}
+
+// issue 6907
+
+const CString = "C string"
+
+//export CheckIssue6907Go
+func CheckIssue6907Go(s string) C.int {
+ if s == CString {
+ return 1
+ }
+ return 0
+}
+
+func test6907Go(t *testing.T) {
+ if got := C.CheckIssue6907C(CString); got != 1 {
+ t.Errorf("C.CheckIssue6907C() == %d, want %d", got, 1)
+ }
+}
+
+// issue 7665
+
+var bad7665 unsafe.Pointer = C.f7665
+var good7665 uintptr = uintptr(C.f7665)
+
+func test7665(t *testing.T) {
+ if bad7665 == nil || uintptr(bad7665) != good7665 {
+ t.Errorf("ptrs = %p, %#x, want same non-nil pointer", bad7665, good7665)
+ }
+}
+
+// issue 7978
+
+var issue7978sync uint32
+
+func issue7978check(t *testing.T, wantFunc string, badFunc string, depth int) {
+ runtime.GC()
+ buf := make([]byte, 65536)
+ trace := string(buf[:runtime.Stack(buf, true)])
+ for _, goroutine := range strings.Split(trace, "\n\n") {
+ if strings.Contains(goroutine, "test.issue7978go") {
+ trace := strings.Split(goroutine, "\n")
+ // look for the expected function in the stack
+ for i := 0; i < depth; i++ {
+ if badFunc != "" && strings.Contains(trace[1+2*i], badFunc) {
+ t.Errorf("bad stack: found %s in the stack:\n%s", badFunc, goroutine)
+ return
+ }
+ if strings.Contains(trace[1+2*i], wantFunc) {
+ return
+ }
+ }
+ t.Errorf("bad stack: didn't find %s in the stack:\n%s", wantFunc, goroutine)
+ return
+ }
+ }
+ t.Errorf("bad stack: goroutine not found. Full stack dump:\n%s", trace)
+}
+
+func issue7978wait(store uint32, wait uint32) {
+ if store != 0 {
+ atomic.StoreUint32(&issue7978sync, store)
+ }
+ for atomic.LoadUint32(&issue7978sync) != wait {
+ runtime.Gosched()
+ }
+}
+
+//export issue7978cb
+func issue7978cb() {
+ // Force a stack growth from the callback to put extra
+ // pressure on the runtime. See issue #17785.
+ growStack(64)
+ issue7978wait(3, 4)
+}
+
+func growStack(n int) int {
+ var buf [128]int
+ if n == 0 {
+ return 0
+ }
+ return buf[growStack(n-1)]
+}
+
+func issue7978go() {
+ C.issue7978c((*C.uint32_t)(&issue7978sync))
+ issue7978wait(7, 8)
+}
+
+func test7978(t *testing.T) {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("gccgo can not do stack traces of C code")
+ }
+ debug.SetTraceback("2")
+ issue7978sync = 0
+ go issue7978go()
+ // test in c code, before callback
+ issue7978wait(0, 1)
+ issue7978check(t, "_Cfunc_issue7978c(", "", 1)
+ // test in go code, during callback
+ issue7978wait(2, 3)
+ issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3)
+ // test in c code, after callback
+ issue7978wait(4, 5)
+ issue7978check(t, "_Cfunc_issue7978c(", "_cgoexpwrap", 1)
+ // test in go code, after return from cgo
+ issue7978wait(6, 7)
+ issue7978check(t, "test.issue7978go(", "", 3)
+ atomic.StoreUint32(&issue7978sync, 8)
+}
+
+// issue 8331 part 2
+
+var issue8331Var C.issue8331
+
+// issue 8945
+
+//export Test8945
+func Test8945() {
+ _ = C.func8945
+}
+
+// issue 20910
+
+//export multi
+func multi() (*C.char, C.int) {
+ return C.CString("multi"), 0
+}
+
+func test20910(t *testing.T) {
+ C.callMulti()
+}
+
+// issue 28772 part 2
+
+const issue28772Constant2 = C.issue28772Constant2
+
+// issue 31891
+
+//export useIssue31891A
+func useIssue31891A(c *C.Issue31891A) {}
+
+//export useIssue31891B
+func useIssue31891B(c *C.Issue31891B) {}
+
+func test31891(t *testing.T) {
+ C.callIssue31891()
+}
+
+// issue 37033, check if cgo.Handle works properly
+
+var issue37033 = 42
+
+//export GoFunc37033
+func GoFunc37033(handle C.uintptr_t) {
+ h := cgo.Handle(handle)
+ ch := h.Value().(chan int)
+ ch <- issue37033
+}
+
+// issue 38408
+// A typedef pointer can be used as the element type.
+// No runtime test; just make sure it compiles.
+var _ C.PIssue38408 = &C.Issue38408{i: 1}
+
+// issue 49633, example use of cgo.Handle with void*
+
+type data49633 struct {
+ msg string
+}
+
+//export GoFunc49633
+func GoFunc49633(context unsafe.Pointer) {
+ h := *(*cgo.Handle)(context)
+ v := h.Value().(*data49633)
+ v.msg = "hello"
+}
+
+func test49633(t *testing.T) {
+ v := &data49633{}
+ h := cgo.NewHandle(v)
+ defer h.Delete()
+ C.cfunc49633(unsafe.Pointer(&h))
+ if v.msg != "hello" {
+ t.Errorf("msg = %q, want 'hello'", v.msg)
+ }
+}
diff --git a/src/cmd/cgo/internal/test/typeparam.go b/src/cmd/cgo/internal/test/typeparam.go
new file mode 100644
index 0000000..5f766c2
--- /dev/null
+++ b/src/cmd/cgo/internal/test/typeparam.go
@@ -0,0 +1,17 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+// #include <stddef.h>
+import "C"
+
+func generic[T, U any](t T, u U) {}
+
+func useGeneric() {
+ const zero C.size_t = 0
+
+ generic(zero, zero)
+ generic[C.size_t, C.size_t](0, 0)
+}
diff --git a/src/cmd/cgo/internal/testcarchive/carchive_test.go b/src/cmd/cgo/internal/testcarchive/carchive_test.go
new file mode 100644
index 0000000..b140a9c
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/carchive_test.go
@@ -0,0 +1,1399 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This test uses various syscall.SIG* constants that are defined on Unix
+// platforms and Windows.
+
+//go:build unix || windows
+
+package carchive_test
+
+import (
+ "bufio"
+ "bytes"
+ "cmd/cgo/internal/cgotest"
+ "debug/elf"
+ "flag"
+ "fmt"
+ "internal/testenv"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "syscall"
+ "testing"
+ "time"
+ "unicode"
+)
+
+var globalSkip = func(t *testing.T) {}
+
+// Program to run.
+var bin []string
+
+// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
+var cc []string
+
+// ".exe" on Windows.
+var exeSuffix string
+
+var GOOS, GOARCH, GOPATH string
+var libgodir string
+
+var testWork bool // If true, preserve temporary directories.
+
+func TestMain(m *testing.M) {
+ flag.BoolVar(&testWork, "testwork", false, "if true, log and preserve the test's temporary working directory")
+ flag.Parse()
+
+ log.SetFlags(log.Lshortfile)
+ os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+ if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
+ globalSkip = func(t *testing.T) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
+ return m.Run()
+ }
+ if runtime.GOOS == "linux" {
+ if _, err := os.Stat("/etc/alpine-release"); err == nil {
+ globalSkip = func(t *testing.T) { t.Skip("skipping failing test on alpine - go.dev/issue/19938") }
+ return m.Run()
+ }
+ }
+
+ // We need a writable GOPATH in which to run the tests.
+ // Construct one in a temporary directory.
+ var err error
+ GOPATH, err = os.MkdirTemp("", "carchive_test")
+ if err != nil {
+ log.Panic(err)
+ }
+ if testWork {
+ log.Println(GOPATH)
+ } else {
+ defer os.RemoveAll(GOPATH)
+ }
+ os.Setenv("GOPATH", GOPATH)
+
+ // Copy testdata into GOPATH/src/testarchive, along with a go.mod file
+ // declaring the same path.
+ modRoot := filepath.Join(GOPATH, "src", "testcarchive")
+ if err := cgotest.OverlayDir(modRoot, "testdata"); err != nil {
+ log.Panic(err)
+ }
+ if err := os.Chdir(modRoot); err != nil {
+ log.Panic(err)
+ }
+ os.Setenv("PWD", modRoot)
+ if err := os.WriteFile("go.mod", []byte("module testcarchive\n"), 0666); err != nil {
+ log.Panic(err)
+ }
+
+ GOOS = goEnv("GOOS")
+ GOARCH = goEnv("GOARCH")
+ bin = cmdToRun("./testp")
+
+ ccOut := goEnv("CC")
+ cc = []string{string(ccOut)}
+
+ out := goEnv("GOGCCFLAGS")
+ quote := '\000'
+ start := 0
+ lastSpace := true
+ backslash := false
+ s := string(out)
+ for i, c := range s {
+ if quote == '\000' && unicode.IsSpace(c) {
+ if !lastSpace {
+ cc = append(cc, s[start:i])
+ lastSpace = true
+ }
+ } else {
+ if lastSpace {
+ start = i
+ lastSpace = false
+ }
+ if quote == '\000' && !backslash && (c == '"' || c == '\'') {
+ quote = c
+ backslash = false
+ } else if !backslash && quote == c {
+ quote = '\000'
+ } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
+ backslash = true
+ } else {
+ backslash = false
+ }
+ }
+ }
+ if !lastSpace {
+ cc = append(cc, s[start:])
+ }
+
+ if GOOS == "aix" {
+ // -Wl,-bnoobjreorder is mandatory to keep the same layout
+ // in .text section.
+ cc = append(cc, "-Wl,-bnoobjreorder")
+ }
+ if GOOS == "ios" {
+ // Linking runtime/cgo on ios requires the CoreFoundation framework because
+ // x_cgo_init uses CoreFoundation APIs to switch directory to the app root.
+ //
+ // TODO(#58225): This special case probably should not be needed.
+ // runtime/cgo is a very low-level package, and should not provide
+ // high-level behaviors like changing the current working directory at init.
+ cc = append(cc, "-framework", "CoreFoundation")
+ }
+ libbase := GOOS + "_" + GOARCH
+ if runtime.Compiler == "gccgo" {
+ libbase = "gccgo_" + libgodir + "_fPIC"
+ } else {
+ switch GOOS {
+ case "darwin", "ios":
+ if GOARCH == "arm64" {
+ libbase += "_shared"
+ }
+ case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos":
+ libbase += "_shared"
+ }
+ }
+ libgodir = filepath.Join(GOPATH, "pkg", libbase, "testcarchive")
+ cc = append(cc, "-I", libgodir)
+
+ // Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc.
+ cc = cc[:len(cc):len(cc)]
+
+ if GOOS == "windows" {
+ exeSuffix = ".exe"
+ }
+
+ return m.Run()
+}
+
+func goEnv(key string) string {
+ out, err := exec.Command("go", "env", key).Output()
+ if err != nil {
+ if ee, ok := err.(*exec.ExitError); ok {
+ fmt.Fprintf(os.Stderr, "%s", ee.Stderr)
+ }
+ log.Panicf("go env %s failed:\n%s\n", key, err)
+ }
+ return strings.TrimSpace(string(out))
+}
+
+func cmdToRun(name string) []string {
+ execScript := "go_" + goEnv("GOOS") + "_" + goEnv("GOARCH") + "_exec"
+ executor, err := exec.LookPath(execScript)
+ if err != nil {
+ return []string{name}
+ }
+ return []string{executor, name}
+}
+
+// genHeader writes a C header file for the C-exported declarations found in .go
+// source files in dir.
+//
+// TODO(golang.org/issue/35715): This should be simpler.
+func genHeader(t *testing.T, header, dir string) {
+ t.Helper()
+
+ // The 'cgo' command generates a number of additional artifacts,
+ // but we're only interested in the header.
+ // Shunt the rest of the outputs to a temporary directory.
+ objDir, err := os.MkdirTemp(GOPATH, "_obj")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(objDir)
+
+ files, err := filepath.Glob(filepath.Join(dir, "*.go"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command("go", "tool", "cgo",
+ "-objdir", objDir,
+ "-exportheader", header)
+ cmd.Args = append(cmd.Args, files...)
+ t.Log(cmd.Args)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+}
+
+func testInstall(t *testing.T, exe, libgoa, libgoh string, buildcmd ...string) {
+ t.Helper()
+ cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
+ cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode
+ t.Log(buildcmd)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ if !testWork {
+ defer func() {
+ os.Remove(libgoa)
+ os.Remove(libgoh)
+ }()
+ }
+
+ ccArgs := append(cc, "-o", exe, "main.c")
+ if GOOS == "windows" {
+ ccArgs = append(ccArgs, "main_windows.c", libgoa, "-lntdll", "-lws2_32", "-lwinmm")
+ } else {
+ ccArgs = append(ccArgs, "main_unix.c", libgoa)
+ }
+ if runtime.Compiler == "gccgo" {
+ ccArgs = append(ccArgs, "-lgo")
+ }
+ t.Log(ccArgs)
+ if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ if !testWork {
+ defer os.Remove(exe)
+ }
+
+ binArgs := append(cmdToRun(exe), "arg1", "arg2")
+ cmd = exec.Command(binArgs[0], binArgs[1:]...)
+ if runtime.Compiler == "gccgo" {
+ cmd.Env = append(cmd.Environ(), "GCCGO=1")
+ }
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ checkLineComments(t, libgoh)
+}
+
+var badLineRegexp = regexp.MustCompile(`(?m)^#line [0-9]+ "/.*$`)
+
+// checkLineComments checks that the export header generated by
+// -buildmode=c-archive doesn't have any absolute paths in the #line
+// comments. We don't want those paths because they are unhelpful for
+// the user and make the files change based on details of the location
+// of GOPATH.
+func checkLineComments(t *testing.T, hdrname string) {
+ hdr, err := os.ReadFile(hdrname)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ t.Error(err)
+ }
+ return
+ }
+ if line := badLineRegexp.Find(hdr); line != nil {
+ t.Errorf("bad #line directive with absolute path in %s: %q", hdrname, line)
+ }
+}
+
+// checkArchive verifies that the created library looks OK.
+// We just check a couple of things now, we can add more checks as needed.
+func checkArchive(t *testing.T, arname string) {
+ t.Helper()
+
+ switch GOOS {
+ case "aix", "darwin", "ios", "windows":
+ // We don't have any checks for non-ELF libraries yet.
+ if _, err := os.Stat(arname); err != nil {
+ t.Errorf("archive %s does not exist: %v", arname, err)
+ }
+ default:
+ checkELFArchive(t, arname)
+ }
+}
+
+// checkELFArchive checks an ELF archive.
+func checkELFArchive(t *testing.T, arname string) {
+ t.Helper()
+
+ f, err := os.Open(arname)
+ if err != nil {
+ t.Errorf("archive %s does not exist: %v", arname, err)
+ return
+ }
+ defer f.Close()
+
+ // TODO(iant): put these in a shared package? But where?
+ const (
+ magic = "!<arch>\n"
+ fmag = "`\n"
+
+ namelen = 16
+ datelen = 12
+ uidlen = 6
+ gidlen = 6
+ modelen = 8
+ sizelen = 10
+ fmaglen = 2
+ hdrlen = namelen + datelen + uidlen + gidlen + modelen + sizelen + fmaglen
+ )
+
+ type arhdr struct {
+ name string
+ date string
+ uid string
+ gid string
+ mode string
+ size string
+ fmag string
+ }
+
+ var magbuf [len(magic)]byte
+ if _, err := io.ReadFull(f, magbuf[:]); err != nil {
+ t.Errorf("%s: archive too short", arname)
+ return
+ }
+ if string(magbuf[:]) != magic {
+ t.Errorf("%s: incorrect archive magic string %q", arname, magbuf)
+ }
+
+ off := int64(len(magic))
+ for {
+ if off&1 != 0 {
+ var b [1]byte
+ if _, err := f.Read(b[:]); err != nil {
+ if err == io.EOF {
+ break
+ }
+ t.Errorf("%s: error skipping alignment byte at %d: %v", arname, off, err)
+ }
+ off++
+ }
+
+ var hdrbuf [hdrlen]byte
+ if _, err := io.ReadFull(f, hdrbuf[:]); err != nil {
+ if err == io.EOF {
+ break
+ }
+ t.Errorf("%s: error reading archive header at %d: %v", arname, off, err)
+ return
+ }
+
+ var hdr arhdr
+ hdrslice := hdrbuf[:]
+ set := func(len int, ps *string) {
+ *ps = string(bytes.TrimSpace(hdrslice[:len]))
+ hdrslice = hdrslice[len:]
+ }
+ set(namelen, &hdr.name)
+ set(datelen, &hdr.date)
+ set(uidlen, &hdr.uid)
+ set(gidlen, &hdr.gid)
+ set(modelen, &hdr.mode)
+ set(sizelen, &hdr.size)
+ hdr.fmag = string(hdrslice[:fmaglen])
+ hdrslice = hdrslice[fmaglen:]
+ if len(hdrslice) != 0 {
+ t.Fatalf("internal error: len(hdrslice) == %d", len(hdrslice))
+ }
+
+ if hdr.fmag != fmag {
+ t.Errorf("%s: invalid fmagic value %q at %d", arname, hdr.fmag, off)
+ return
+ }
+
+ size, err := strconv.ParseInt(hdr.size, 10, 64)
+ if err != nil {
+ t.Errorf("%s: error parsing size %q at %d: %v", arname, hdr.size, off, err)
+ return
+ }
+
+ off += hdrlen
+
+ switch hdr.name {
+ case "__.SYMDEF", "/", "/SYM64/":
+ // The archive symbol map.
+ case "//", "ARFILENAMES/":
+ // The extended name table.
+ default:
+ // This should be an ELF object.
+ checkELFArchiveObject(t, arname, off, io.NewSectionReader(f, off, size))
+ }
+
+ off += size
+ if _, err := f.Seek(off, io.SeekStart); err != nil {
+ t.Errorf("%s: failed to seek to %d: %v", arname, off, err)
+ }
+ }
+}
+
+// checkELFArchiveObject checks an object in an ELF archive.
+func checkELFArchiveObject(t *testing.T, arname string, off int64, obj io.ReaderAt) {
+ t.Helper()
+
+ ef, err := elf.NewFile(obj)
+ if err != nil {
+ t.Errorf("%s: failed to open ELF file at %d: %v", arname, off, err)
+ return
+ }
+ defer ef.Close()
+
+ // Verify section types.
+ for _, sec := range ef.Sections {
+ want := elf.SHT_NULL
+ switch sec.Name {
+ case ".text", ".data":
+ want = elf.SHT_PROGBITS
+ case ".bss":
+ want = elf.SHT_NOBITS
+ case ".symtab":
+ want = elf.SHT_SYMTAB
+ case ".strtab":
+ want = elf.SHT_STRTAB
+ case ".init_array":
+ want = elf.SHT_INIT_ARRAY
+ case ".fini_array":
+ want = elf.SHT_FINI_ARRAY
+ case ".preinit_array":
+ want = elf.SHT_PREINIT_ARRAY
+ }
+ if want != elf.SHT_NULL && sec.Type != want {
+ t.Errorf("%s: incorrect section type in elf file at %d for section %q: got %v want %v", arname, off, sec.Name, sec.Type, want)
+ }
+ }
+}
+
+func TestInstall(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ if !testWork {
+ defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
+ }
+
+ libgoa := "libgo.a"
+ if runtime.Compiler == "gccgo" {
+ libgoa = "liblibgo.a"
+ }
+
+ // Generate the p.h header file.
+ //
+ // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that
+ // would also attempt to install transitive standard-library dependencies to
+ // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may
+ // be running this test in a GOROOT owned by root.)
+ genHeader(t, "p.h", "./p")
+
+ testInstall(t, "./testp1"+exeSuffix,
+ filepath.Join(libgodir, libgoa),
+ filepath.Join(libgodir, "libgo.h"),
+ "go", "install", "-buildmode=c-archive", "./libgo")
+
+ // Test building libgo other than installing it.
+ // Header files are now present.
+ testInstall(t, "./testp2"+exeSuffix, "libgo.a", "libgo.h",
+ "go", "build", "-buildmode=c-archive", filepath.Join(".", "libgo", "libgo.go"))
+
+ testInstall(t, "./testp3"+exeSuffix, "libgo.a", "libgo.h",
+ "go", "build", "-buildmode=c-archive", "-o", "libgo.a", "./libgo")
+}
+
+func TestEarlySignalHandler(t *testing.T) {
+ switch GOOS {
+ case "darwin", "ios":
+ switch GOARCH {
+ case "arm64":
+ t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
+ }
+ case "windows":
+ t.Skip("skipping signal test on Windows")
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ if !testWork {
+ defer func() {
+ os.Remove("libgo2.a")
+ os.Remove("libgo2.h")
+ os.Remove("testp" + exeSuffix)
+ os.RemoveAll(filepath.Join(GOPATH, "pkg"))
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo2.h")
+ checkArchive(t, "libgo2.a")
+
+ ccArgs := append(cc, "-o", "testp"+exeSuffix, "main2.c", "libgo2.a")
+ if runtime.Compiler == "gccgo" {
+ ccArgs = append(ccArgs, "-lgo")
+ }
+ if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ darwin := "0"
+ if runtime.GOOS == "darwin" {
+ darwin = "1"
+ }
+ cmd = exec.Command(bin[0], append(bin[1:], darwin)...)
+
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+}
+
+func TestSignalForwarding(t *testing.T) {
+ globalSkip(t)
+ checkSignalForwardingTest(t)
+ buildSignalForwardingTest(t)
+
+ cmd := exec.Command(bin[0], append(bin[1:], "1")...)
+
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v\n%s", cmd.Args, out)
+ expectSignal(t, err, syscall.SIGSEGV, 0)
+
+ // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
+ if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
+ // Test SIGPIPE forwarding
+ cmd = exec.Command(bin[0], append(bin[1:], "3")...)
+
+ out, err = cmd.CombinedOutput()
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ expectSignal(t, err, syscall.SIGPIPE, 0)
+ }
+}
+
+func TestSignalForwardingExternal(t *testing.T) {
+ if GOOS == "freebsd" || GOOS == "aix" {
+ t.Skipf("skipping on %s/%s; signal always goes to the Go runtime", GOOS, GOARCH)
+ } else if GOOS == "darwin" && GOARCH == "amd64" {
+ t.Skipf("skipping on %s/%s: runtime does not permit SI_USER SIGSEGV", GOOS, GOARCH)
+ }
+ globalSkip(t)
+ checkSignalForwardingTest(t)
+ buildSignalForwardingTest(t)
+
+ // We want to send the process a signal and see if it dies.
+ // Normally the signal goes to the C thread, the Go signal
+ // handler picks it up, sees that it is running in a C thread,
+ // and the program dies. Unfortunately, occasionally the
+ // signal is delivered to a Go thread, which winds up
+ // discarding it because it was sent by another program and
+ // there is no Go handler for it. To avoid this, run the
+ // program several times in the hopes that it will eventually
+ // fail.
+ const tries = 20
+ for i := 0; i < tries; i++ {
+ err := runSignalForwardingTest(t, "2")
+ if err == nil {
+ continue
+ }
+
+ // If the signal is delivered to a C thread, as expected,
+ // the Go signal handler will disable itself and re-raise
+ // the signal, causing the program to die with SIGSEGV.
+ //
+ // It is also possible that the signal will be
+ // delivered to a Go thread, such as a GC thread.
+ // Currently when the Go runtime sees that a SIGSEGV was
+ // sent from a different program, it first tries to send
+ // the signal to the os/signal API. If nothing is looking
+ // for (or explicitly ignoring) SIGSEGV, then it crashes.
+ // Because the Go runtime is invoked via a c-archive,
+ // it treats this as GOTRACEBACK=crash, meaning that it
+ // dumps a stack trace for all goroutines, which it does
+ // by raising SIGQUIT. The effect is that we will see the
+ // program die with SIGQUIT in that case, not SIGSEGV.
+ if expectSignal(t, err, syscall.SIGSEGV, syscall.SIGQUIT) {
+ return
+ }
+ }
+
+ t.Errorf("program succeeded unexpectedly %d times", tries)
+}
+
+func TestSignalForwardingGo(t *testing.T) {
+ // This test fails on darwin-amd64 because of the special
+ // handling of user-generated SIGSEGV signals in fixsigcode in
+ // runtime/signal_darwin_amd64.go.
+ if runtime.GOOS == "darwin" && runtime.GOARCH == "amd64" {
+ t.Skip("not supported on darwin-amd64")
+ }
+ globalSkip(t)
+
+ checkSignalForwardingTest(t)
+ buildSignalForwardingTest(t)
+ err := runSignalForwardingTest(t, "4")
+
+ // Occasionally the signal will be delivered to a C thread,
+ // and the program will crash with SIGSEGV.
+ expectSignal(t, err, syscall.SIGQUIT, syscall.SIGSEGV)
+}
+
+// checkSignalForwardingTest calls t.Skip if the SignalForwarding test
+// doesn't work on this platform.
+func checkSignalForwardingTest(t *testing.T) {
+ switch GOOS {
+ case "darwin", "ios":
+ switch GOARCH {
+ case "arm64":
+ t.Skipf("skipping on %s/%s; see https://golang.org/issue/13701", GOOS, GOARCH)
+ }
+ case "windows":
+ t.Skip("skipping signal test on Windows")
+ }
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+}
+
+// buildSignalForwardingTest builds the executable used by the various
+// signal forwarding tests.
+func buildSignalForwardingTest(t *testing.T) {
+ if !testWork {
+ t.Cleanup(func() {
+ os.Remove("libgo2.a")
+ os.Remove("libgo2.h")
+ os.Remove("testp" + exeSuffix)
+ os.RemoveAll(filepath.Join(GOPATH, "pkg"))
+ })
+ }
+
+ t.Log("go build -buildmode=c-archive -o libgo2.a ./libgo2")
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo2.a", "./libgo2")
+ out, err := cmd.CombinedOutput()
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ checkLineComments(t, "libgo2.h")
+ checkArchive(t, "libgo2.a")
+
+ ccArgs := append(cc, "-o", "testp"+exeSuffix, "main5.c", "libgo2.a")
+ if runtime.Compiler == "gccgo" {
+ ccArgs = append(ccArgs, "-lgo")
+ }
+ t.Log(ccArgs)
+ out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
+ if len(out) > 0 {
+ t.Logf("%s", out)
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func runSignalForwardingTest(t *testing.T, arg string) error {
+ t.Logf("%v %s", bin, arg)
+ cmd := exec.Command(bin[0], append(bin[1:], arg)...)
+
+ var out strings.Builder
+ cmd.Stdout = &out
+
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer stderr.Close()
+
+ r := bufio.NewReader(stderr)
+
+ err = cmd.Start()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Wait for trigger to ensure that process is started.
+ ok, err := r.ReadString('\n')
+
+ // Verify trigger.
+ if err != nil || ok != "OK\n" {
+ t.Fatal("Did not receive OK signal")
+ }
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+ var errsb strings.Builder
+ go func() {
+ defer wg.Done()
+ io.Copy(&errsb, r)
+ }()
+
+ // Give the program a chance to enter the function.
+ // If the program doesn't get there the test will still
+ // pass, although it doesn't quite test what we intended.
+ // This is fine as long as the program normally makes it.
+ time.Sleep(time.Millisecond)
+
+ cmd.Process.Signal(syscall.SIGSEGV)
+
+ err = cmd.Wait()
+
+ s := out.String()
+ if len(s) > 0 {
+ t.Log(s)
+ }
+ wg.Wait()
+ s = errsb.String()
+ if len(s) > 0 {
+ t.Log(s)
+ }
+
+ return err
+}
+
+// expectSignal checks that err, the exit status of a test program,
+// shows a failure due to a specific signal or two. Returns whether we
+// found an expected signal.
+func expectSignal(t *testing.T, err error, sig1, sig2 syscall.Signal) bool {
+ t.Helper()
+ if err == nil {
+ t.Error("test program succeeded unexpectedly")
+ } else if ee, ok := err.(*exec.ExitError); !ok {
+ t.Errorf("error (%v) has type %T; expected exec.ExitError", err, err)
+ } else if ws, ok := ee.Sys().(syscall.WaitStatus); !ok {
+ t.Errorf("error.Sys (%v) has type %T; expected syscall.WaitStatus", ee.Sys(), ee.Sys())
+ } else if !ws.Signaled() || (ws.Signal() != sig1 && ws.Signal() != sig2) {
+ if sig2 == 0 {
+ t.Errorf("got %q; expected signal %q", ee, sig1)
+ } else {
+ t.Errorf("got %q; expected signal %q or %q", ee, sig1, sig2)
+ }
+ } else {
+ return true
+ }
+ return false
+}
+
+func TestOsSignal(t *testing.T) {
+ switch GOOS {
+ case "windows":
+ t.Skip("skipping signal test on Windows")
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ if !testWork {
+ defer func() {
+ os.Remove("libgo3.a")
+ os.Remove("libgo3.h")
+ os.Remove("testp" + exeSuffix)
+ os.RemoveAll(filepath.Join(GOPATH, "pkg"))
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo3.a", "./libgo3")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo3.h")
+ checkArchive(t, "libgo3.a")
+
+ ccArgs := append(cc, "-o", "testp"+exeSuffix, "main3.c", "libgo3.a")
+ if runtime.Compiler == "gccgo" {
+ ccArgs = append(ccArgs, "-lgo")
+ }
+ if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+}
+
+func TestSigaltstack(t *testing.T) {
+ switch GOOS {
+ case "windows":
+ t.Skip("skipping signal test on Windows")
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ if !testWork {
+ defer func() {
+ os.Remove("libgo4.a")
+ os.Remove("libgo4.h")
+ os.Remove("testp" + exeSuffix)
+ os.RemoveAll(filepath.Join(GOPATH, "pkg"))
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo4.a", "./libgo4")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo4.h")
+ checkArchive(t, "libgo4.a")
+
+ ccArgs := append(cc, "-o", "testp"+exeSuffix, "main4.c", "libgo4.a")
+ if runtime.Compiler == "gccgo" {
+ ccArgs = append(ccArgs, "-lgo")
+ }
+ if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ if out, err := exec.Command(bin[0], bin[1:]...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+}
+
+const testar = `#!/usr/bin/env bash
+while [[ $1 == -* ]] >/dev/null; do
+ shift
+done
+echo "testar" > $1
+echo "testar" > PWD/testar.ran
+`
+
+func TestExtar(t *testing.T) {
+ switch GOOS {
+ case "windows":
+ t.Skip("skipping signal test on Windows")
+ }
+ if runtime.Compiler == "gccgo" {
+ t.Skip("skipping -extar test when using gccgo")
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+ testenv.MustHaveExecPath(t, "bash") // This test uses a bash script
+
+ if !testWork {
+ defer func() {
+ os.Remove("libgo4.a")
+ os.Remove("libgo4.h")
+ os.Remove("testar")
+ os.Remove("testar.ran")
+ os.RemoveAll(filepath.Join(GOPATH, "pkg"))
+ }()
+ }
+
+ os.Remove("testar")
+ dir, err := os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+ s := strings.Replace(testar, "PWD", dir, 1)
+ if err := os.WriteFile("testar", []byte(s), 0777); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-ldflags=-extar="+filepath.Join(dir, "testar"), "-o", "libgo4.a", "./libgo4")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo4.h")
+
+ if _, err := os.Stat("testar.ran"); err != nil {
+ if os.IsNotExist(err) {
+ t.Error("testar does not exist after go build")
+ } else {
+ t.Errorf("error checking testar: %v", err)
+ }
+ }
+}
+
+func TestPIE(t *testing.T) {
+ switch GOOS {
+ case "windows", "darwin", "ios", "plan9":
+ t.Skipf("skipping PIE test on %s", GOOS)
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ libgoa := "libgo.a"
+ if runtime.Compiler == "gccgo" {
+ libgoa = "liblibgo.a"
+ }
+
+ if !testWork {
+ defer func() {
+ os.Remove("testp" + exeSuffix)
+ os.Remove(libgoa)
+ os.RemoveAll(filepath.Join(GOPATH, "pkg"))
+ }()
+ }
+
+ // Generate the p.h header file.
+ //
+ // 'go install -i -buildmode=c-archive ./libgo' would do that too, but that
+ // would also attempt to install transitive standard-library dependencies to
+ // GOROOT, and we cannot assume that GOROOT is writable. (A non-root user may
+ // be running this test in a GOROOT owned by root.)
+ genHeader(t, "p.h", "./p")
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "./libgo")
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ ccArgs := append(cc, "-fPIE", "-pie", "-o", "testp"+exeSuffix, "main.c", "main_unix.c", libgoa)
+ if runtime.Compiler == "gccgo" {
+ ccArgs = append(ccArgs, "-lgo")
+ }
+ if out, err := exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ binArgs := append(bin, "arg1", "arg2")
+ cmd = exec.Command(binArgs[0], binArgs[1:]...)
+ if runtime.Compiler == "gccgo" {
+ cmd.Env = append(os.Environ(), "GCCGO=1")
+ }
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ if GOOS != "aix" {
+ f, err := elf.Open("testp" + exeSuffix)
+ if err != nil {
+ t.Fatal("elf.Open failed: ", err)
+ }
+ defer f.Close()
+ if hasDynTag(t, f, elf.DT_TEXTREL) {
+ t.Errorf("%s has DT_TEXTREL flag", "testp"+exeSuffix)
+ }
+ }
+}
+
+func hasDynTag(t *testing.T, f *elf.File, tag elf.DynTag) bool {
+ ds := f.SectionByType(elf.SHT_DYNAMIC)
+ if ds == nil {
+ t.Error("no SHT_DYNAMIC section")
+ return false
+ }
+ d, err := ds.Data()
+ if err != nil {
+ t.Errorf("can't read SHT_DYNAMIC contents: %v", err)
+ return false
+ }
+ for len(d) > 0 {
+ var t elf.DynTag
+ switch f.Class {
+ case elf.ELFCLASS32:
+ t = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
+ d = d[8:]
+ case elf.ELFCLASS64:
+ t = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
+ d = d[16:]
+ }
+ if t == tag {
+ return true
+ }
+ }
+ return false
+}
+
+func TestSIGPROF(t *testing.T) {
+ switch GOOS {
+ case "windows", "plan9":
+ t.Skipf("skipping SIGPROF test on %s", GOOS)
+ case "darwin", "ios":
+ t.Skipf("skipping SIGPROF test on %s; see https://golang.org/issue/19320", GOOS)
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ t.Parallel()
+
+ if !testWork {
+ defer func() {
+ os.Remove("testp6" + exeSuffix)
+ os.Remove("libgo6.a")
+ os.Remove("libgo6.h")
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo6.a", "./libgo6")
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v\n%s", cmd.Args, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo6.h")
+ checkArchive(t, "libgo6.a")
+
+ ccArgs := append(cc, "-o", "testp6"+exeSuffix, "main6.c", "libgo6.a")
+ if runtime.Compiler == "gccgo" {
+ ccArgs = append(ccArgs, "-lgo")
+ }
+ out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", ccArgs, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ argv := cmdToRun("./testp6")
+ cmd = exec.Command(argv[0], argv[1:]...)
+ out, err = cmd.CombinedOutput()
+ t.Logf("%v\n%s", argv, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// TestCompileWithoutShared tests that if we compile code without the
+// -shared option, we can put it into an archive. When we use the go
+// tool with -buildmode=c-archive, it passes -shared to the compiler,
+// so we override that. The go tool doesn't work this way, but Bazel
+// will likely do it in the future. And it ought to work. This test
+// was added because at one time it did not work on PPC Linux.
+func TestCompileWithoutShared(t *testing.T) {
+ globalSkip(t)
+ // For simplicity, reuse the signal forwarding test.
+ checkSignalForwardingTest(t)
+ testenv.MustHaveGoBuild(t)
+
+ if !testWork {
+ defer func() {
+ os.Remove("libgo2.a")
+ os.Remove("libgo2.h")
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-gcflags=-shared=false", "-o", "libgo2.a", "./libgo2")
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v\n%s", cmd.Args, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo2.h")
+ checkArchive(t, "libgo2.a")
+
+ exe := "./testnoshared" + exeSuffix
+
+ // In some cases, -no-pie is needed here, but not accepted everywhere. First try
+ // if -no-pie is accepted. See #22126.
+ ccArgs := append(cc, "-o", exe, "-no-pie", "main5.c", "libgo2.a")
+ if runtime.Compiler == "gccgo" {
+ ccArgs = append(ccArgs, "-lgo")
+ }
+ out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", ccArgs, out)
+
+ // If -no-pie unrecognized, try -nopie if this is possibly clang
+ if err != nil && bytes.Contains(out, []byte("unknown")) && !strings.Contains(cc[0], "gcc") {
+ ccArgs = append(cc, "-o", exe, "-nopie", "main5.c", "libgo2.a")
+ out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", ccArgs, out)
+ }
+
+ // Don't use either -no-pie or -nopie
+ if err != nil && bytes.Contains(out, []byte("unrecognized")) {
+ ccArgs = append(cc, "-o", exe, "main5.c", "libgo2.a")
+ out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", ccArgs, out)
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !testWork {
+ defer os.Remove(exe)
+ }
+
+ binArgs := append(cmdToRun(exe), "1")
+ out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", binArgs, out)
+ expectSignal(t, err, syscall.SIGSEGV, 0)
+
+ // SIGPIPE is never forwarded on darwin. See golang.org/issue/33384.
+ if runtime.GOOS != "darwin" && runtime.GOOS != "ios" {
+ binArgs := append(cmdToRun(exe), "3")
+ out, err = exec.Command(binArgs[0], binArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", binArgs, out)
+ expectSignal(t, err, syscall.SIGPIPE, 0)
+ }
+}
+
+// Test that installing a second time recreates the header file.
+func TestCachedInstall(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ if !testWork {
+ defer os.RemoveAll(filepath.Join(GOPATH, "pkg"))
+ }
+
+ h := filepath.Join(libgodir, "libgo.h")
+
+ buildcmd := []string{"go", "install", "-buildmode=c-archive", "./libgo"}
+
+ cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
+ cmd.Env = append(cmd.Environ(), "GO111MODULE=off") // 'go install' only works in GOPATH mode
+ t.Log(buildcmd)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ if _, err := os.Stat(h); err != nil {
+ t.Errorf("libgo.h not installed: %v", err)
+ }
+
+ if err := os.Remove(h); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
+ cmd.Env = append(cmd.Environ(), "GO111MODULE=off")
+ t.Log(buildcmd)
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+
+ if _, err := os.Stat(h); err != nil {
+ t.Errorf("libgo.h not installed in second run: %v", err)
+ }
+}
+
+// Issue 35294.
+func TestManyCalls(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ t.Parallel()
+
+ if !testWork {
+ defer func() {
+ os.Remove("testp7" + exeSuffix)
+ os.Remove("libgo7.a")
+ os.Remove("libgo7.h")
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo7.a", "./libgo7")
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v\n%s", cmd.Args, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo7.h")
+ checkArchive(t, "libgo7.a")
+
+ ccArgs := append(cc, "-o", "testp7"+exeSuffix, "main7.c", "libgo7.a")
+ if runtime.Compiler == "gccgo" {
+ ccArgs = append(ccArgs, "-lgo")
+ }
+ out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", ccArgs, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ argv := cmdToRun("./testp7")
+ cmd = exec.Command(argv[0], argv[1:]...)
+ sb := new(strings.Builder)
+ cmd.Stdout = sb
+ cmd.Stderr = sb
+ if err := cmd.Start(); err != nil {
+ t.Fatal(err)
+ }
+
+ timer := time.AfterFunc(time.Minute,
+ func() {
+ t.Error("test program timed out")
+ cmd.Process.Kill()
+ },
+ )
+ defer timer.Stop()
+
+ err = cmd.Wait()
+ t.Logf("%v\n%s", cmd.Args, sb)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// Issue 49288.
+func TestPreemption(t *testing.T) {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("skipping asynchronous preemption test with gccgo")
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ t.Parallel()
+
+ if !testWork {
+ defer func() {
+ os.Remove("testp8" + exeSuffix)
+ os.Remove("libgo8.a")
+ os.Remove("libgo8.h")
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo8.a", "./libgo8")
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v\n%s", cmd.Args, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo8.h")
+ checkArchive(t, "libgo8.a")
+
+ ccArgs := append(cc, "-o", "testp8"+exeSuffix, "main8.c", "libgo8.a")
+ out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", ccArgs, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ argv := cmdToRun("./testp8")
+ cmd = exec.Command(argv[0], argv[1:]...)
+ sb := new(strings.Builder)
+ cmd.Stdout = sb
+ cmd.Stderr = sb
+ if err := cmd.Start(); err != nil {
+ t.Fatal(err)
+ }
+
+ timer := time.AfterFunc(time.Minute,
+ func() {
+ t.Error("test program timed out")
+ cmd.Process.Kill()
+ },
+ )
+ defer timer.Stop()
+
+ err = cmd.Wait()
+ t.Logf("%v\n%s", cmd.Args, sb)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+// Issue 59294. Test calling Go function from C after using some
+// stack space.
+func TestDeepStack(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ t.Parallel()
+
+ if !testWork {
+ defer func() {
+ os.Remove("testp9" + exeSuffix)
+ os.Remove("libgo9.a")
+ os.Remove("libgo9.h")
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo9.a", "./libgo9")
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v\n%s", cmd.Args, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+ checkLineComments(t, "libgo9.h")
+ checkArchive(t, "libgo9.a")
+
+ // build with -O0 so the C compiler won't optimize out the large stack frame
+ ccArgs := append(cc, "-O0", "-o", "testp9"+exeSuffix, "main9.c", "libgo9.a")
+ out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", ccArgs, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ argv := cmdToRun("./testp9")
+ cmd = exec.Command(argv[0], argv[1:]...)
+ sb := new(strings.Builder)
+ cmd.Stdout = sb
+ cmd.Stderr = sb
+ if err := cmd.Start(); err != nil {
+ t.Fatal(err)
+ }
+
+ timer := time.AfterFunc(time.Minute,
+ func() {
+ t.Error("test program timed out")
+ cmd.Process.Kill()
+ },
+ )
+ defer timer.Stop()
+
+ err = cmd.Wait()
+ t.Logf("%v\n%s", cmd.Args, sb)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func TestSharedObject(t *testing.T) {
+ // Test that we can put a Go c-archive into a C shared object.
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-archive")
+
+ t.Parallel()
+
+ if !testWork {
+ defer func() {
+ os.Remove("libgo_s.a")
+ os.Remove("libgo_s.h")
+ os.Remove("libgo_s.so")
+ }()
+ }
+
+ cmd := exec.Command("go", "build", "-buildmode=c-archive", "-o", "libgo_s.a", "./libgo")
+ out, err := cmd.CombinedOutput()
+ t.Logf("%v\n%s", cmd.Args, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ccArgs := append(cc, "-shared", "-o", "libgo_s.so", "libgo_s.a")
+ out, err = exec.Command(ccArgs[0], ccArgs[1:]...).CombinedOutput()
+ t.Logf("%v\n%s", ccArgs, out)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/libgo/libgo.go b/src/cmd/cgo/internal/testcarchive/testdata/libgo/libgo.go
new file mode 100644
index 0000000..37b30c1
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/libgo/libgo.go
@@ -0,0 +1,53 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "syscall"
+ "time"
+
+ _ "testcarchive/p"
+)
+
+import "C"
+
+var initCh = make(chan int, 1)
+var ranMain bool
+
+func init() {
+ // emulate an exceedingly slow package initialization function
+ time.Sleep(100 * time.Millisecond)
+ initCh <- 42
+}
+
+func main() { ranMain = true }
+
+//export DidInitRun
+func DidInitRun() bool {
+ select {
+ case x := <-initCh:
+ if x != 42 {
+ // Just in case initCh was not correctly made.
+ println("want init value of 42, got: ", x)
+ syscall.Exit(2)
+ }
+ return true
+ default:
+ return false
+ }
+}
+
+//export DidMainRun
+func DidMainRun() bool { return ranMain }
+
+//export CheckArgs
+func CheckArgs() {
+ if len(os.Args) != 3 || os.Args[1] != "arg1" || os.Args[2] != "arg2" {
+ fmt.Printf("CheckArgs: want [_, arg1, arg2], got: %v\n", os.Args)
+ os.Exit(2)
+ }
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/libgo2/libgo2.go b/src/cmd/cgo/internal/testcarchive/testdata/libgo2/libgo2.go
new file mode 100644
index 0000000..b2e7731
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/libgo2/libgo2.go
@@ -0,0 +1,91 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+// Raise SIGPIPE.
+static void CRaiseSIGPIPE() {
+ int fds[2];
+
+ if (pipe(fds) == -1) {
+ perror("pipe");
+ exit(EXIT_FAILURE);
+ }
+ // Close the reader end
+ close(fds[0]);
+ // Write to the writer end to provoke a SIGPIPE
+ if (write(fds[1], "some data", 9) != -1) {
+ fprintf(stderr, "write to a closed pipe succeeded\n");
+ exit(EXIT_FAILURE);
+ }
+ close(fds[1]);
+}
+*/
+import "C"
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+)
+
+// RunGoroutines starts some goroutines that don't do anything.
+// The idea is to get some threads going, so that a signal will be delivered
+// to a thread started by Go.
+//
+//export RunGoroutines
+func RunGoroutines() {
+ for i := 0; i < 4; i++ {
+ go func() {
+ runtime.LockOSThread()
+ select {}
+ }()
+ }
+}
+
+// Block blocks the current thread while running Go code.
+//
+//export Block
+func Block() {
+ select {}
+}
+
+var P *byte
+
+// TestSEGV makes sure that an invalid address turns into a run-time Go panic.
+//
+//export TestSEGV
+func TestSEGV() {
+ defer func() {
+ if recover() == nil {
+ fmt.Fprintln(os.Stderr, "no panic from segv")
+ os.Exit(1)
+ }
+ }()
+ *P = 0
+ fmt.Fprintln(os.Stderr, "continued after segv")
+ os.Exit(1)
+}
+
+// Noop ensures that the Go runtime is initialized.
+//
+//export Noop
+func Noop() {
+}
+
+// Raise SIGPIPE.
+//
+//export GoRaiseSIGPIPE
+func GoRaiseSIGPIPE() {
+ C.CRaiseSIGPIPE()
+}
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/libgo3/libgo3.go b/src/cmd/cgo/internal/testcarchive/testdata/libgo3/libgo3.go
new file mode 100644
index 0000000..136695b
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/libgo3/libgo3.go
@@ -0,0 +1,60 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+)
+
+// The channel used to read SIGIO signals.
+var sigioChan chan os.Signal
+
+// CatchSIGIO starts catching SIGIO signals.
+//
+//export CatchSIGIO
+func CatchSIGIO() {
+ sigioChan = make(chan os.Signal, 1)
+ signal.Notify(sigioChan, syscall.SIGIO)
+}
+
+// ResetSIGIO stops catching SIGIO signals.
+//
+//export ResetSIGIO
+func ResetSIGIO() {
+ signal.Reset(syscall.SIGIO)
+}
+
+// SawSIGIO reports whether we saw a SIGIO.
+//
+//export SawSIGIO
+func SawSIGIO() C.int {
+ select {
+ case <-sigioChan:
+ return 1
+ case <-time.After(5 * time.Second):
+ return 0
+ }
+}
+
+// ProvokeSIGPIPE provokes a kernel-initiated SIGPIPE.
+//
+//export ProvokeSIGPIPE
+func ProvokeSIGPIPE() {
+ r, w, err := os.Pipe()
+ if err != nil {
+ panic(err)
+ }
+ r.Close()
+ defer w.Close()
+ w.Write([]byte("some data"))
+}
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/libgo4/libgo4.go b/src/cmd/cgo/internal/testcarchive/testdata/libgo4/libgo4.go
new file mode 100644
index 0000000..c81d3af
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/libgo4/libgo4.go
@@ -0,0 +1,55 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <signal.h>
+#include <pthread.h>
+
+// Raise SIGIO.
+static void CRaiseSIGIO(pthread_t* p) {
+ pthread_kill(*p, SIGIO);
+}
+*/
+import "C"
+
+import (
+ "os"
+ "os/signal"
+ "sync/atomic"
+ "syscall"
+)
+
+var sigioCount int32
+
+// Catch SIGIO.
+//
+//export GoCatchSIGIO
+func GoCatchSIGIO() {
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, syscall.SIGIO)
+ go func() {
+ for range c {
+ atomic.AddInt32(&sigioCount, 1)
+ }
+ }()
+}
+
+// Raise SIGIO.
+//
+//export GoRaiseSIGIO
+func GoRaiseSIGIO(p *C.pthread_t) {
+ C.CRaiseSIGIO(p)
+}
+
+// Return the number of SIGIO signals seen.
+//
+//export SIGIOCount
+func SIGIOCount() C.int {
+ return C.int(atomic.LoadInt32(&sigioCount))
+}
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/libgo6/sigprof.go b/src/cmd/cgo/internal/testcarchive/testdata/libgo6/sigprof.go
new file mode 100644
index 0000000..31527c5
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/libgo6/sigprof.go
@@ -0,0 +1,25 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "io"
+ "runtime/pprof"
+)
+
+import "C"
+
+//export go_start_profile
+func go_start_profile() {
+ pprof.StartCPUProfile(io.Discard)
+}
+
+//export go_stop_profile
+func go_stop_profile() {
+ pprof.StopCPUProfile()
+}
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/libgo7/sink.go b/src/cmd/cgo/internal/testcarchive/testdata/libgo7/sink.go
new file mode 100644
index 0000000..d61638b
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/libgo7/sink.go
@@ -0,0 +1,17 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+var sink []byte
+
+//export GoFunction7
+func GoFunction7() {
+ sink = make([]byte, 4096)
+}
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/libgo8/a.go b/src/cmd/cgo/internal/testcarchive/testdata/libgo8/a.go
new file mode 100644
index 0000000..718418e
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/libgo8/a.go
@@ -0,0 +1,36 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+import (
+ "os"
+ "runtime"
+ "sync/atomic"
+)
+
+var started int32
+
+// Start a goroutine that loops forever.
+func init() {
+ runtime.GOMAXPROCS(1)
+ go func() {
+ for {
+ atomic.StoreInt32(&started, 1)
+ }
+ }()
+}
+
+//export GoFunction8
+func GoFunction8() {
+ for atomic.LoadInt32(&started) == 0 {
+ runtime.Gosched()
+ }
+ os.Exit(0)
+}
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/libgo9/a.go b/src/cmd/cgo/internal/testcarchive/testdata/libgo9/a.go
new file mode 100644
index 0000000..acb08d9
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/libgo9/a.go
@@ -0,0 +1,14 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "runtime"
+
+import "C"
+
+func main() {}
+
+//export GoF
+func GoF() { runtime.GC() }
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main.c b/src/cmd/cgo/internal/testcarchive/testdata/main.c
new file mode 100644
index 0000000..163b539
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main.c
@@ -0,0 +1,48 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "p.h"
+#include "libgo.h"
+
+extern int install_handler();
+extern int check_handler();
+
+int main(void) {
+ int32_t res;
+
+ int r1 = install_handler();
+ if (r1!=0) {
+ return r1;
+ }
+
+ if (!DidInitRun()) {
+ fprintf(stderr, "ERROR: buildmode=c-archive init should run\n");
+ return 2;
+ }
+
+ if (DidMainRun()) {
+ fprintf(stderr, "ERROR: buildmode=c-archive should not run main\n");
+ return 2;
+ }
+
+ int r2 = check_handler();
+ if (r2!=0) {
+ return r2;
+ }
+
+ res = FromPkg();
+ if (res != 1024) {
+ fprintf(stderr, "ERROR: FromPkg()=%d, want 1024\n", res);
+ return 2;
+ }
+
+ CheckArgs();
+
+ fprintf(stderr, "PASS\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main2.c b/src/cmd/cgo/internal/testcarchive/testdata/main2.c
new file mode 100644
index 0000000..e82294d
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main2.c
@@ -0,0 +1,239 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test installing a signal handler before the Go code starts.
+// This is a lot like ../testcshared/main4.c.
+
+#include <setjmp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sched.h>
+#include <time.h>
+#include <errno.h>
+
+#include "libgo2.h"
+
+static void die(const char* msg) {
+ perror(msg);
+ exit(EXIT_FAILURE);
+}
+
+static volatile sig_atomic_t sigioSeen;
+static volatile sig_atomic_t sigpipeSeen;
+
+// Use up some stack space.
+static void recur(int i, char *p) {
+ char a[1024];
+
+ *p = '\0';
+ if (i > 0) {
+ recur(i - 1, a);
+ }
+}
+
+static void pipeHandler(int signo, siginfo_t* info, void* ctxt) {
+ sigpipeSeen = 1;
+}
+
+// Signal handler that uses up more stack space than a goroutine will have.
+static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
+ char a[1024];
+
+ recur(4, a);
+ sigioSeen = 1;
+}
+
+static jmp_buf jmp;
+static char* nullPointer;
+
+// An arbitrary function which requires proper stack alignment; see
+// http://golang.org/issue/17641.
+static void callWithVarargs(void* dummy, ...) {
+ va_list args;
+ va_start(args, dummy);
+ va_end(args);
+}
+
+// Signal handler for SIGSEGV on a C thread.
+static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
+ sigset_t mask;
+ int i;
+
+ // Call an arbitrary function that requires the stack to be properly aligned.
+ callWithVarargs("dummy arg", 3.1415);
+
+ if (sigemptyset(&mask) < 0) {
+ die("sigemptyset");
+ }
+ if (sigaddset(&mask, SIGSEGV) < 0) {
+ die("sigaddset");
+ }
+ i = sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ if (i != 0) {
+ fprintf(stderr, "sigprocmask: %s\n", strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ // Don't try this at home.
+ longjmp(jmp, signo);
+
+ // We should never get here.
+ abort();
+}
+
+// Set up the signal handlers in a high priority constructor,
+// so that they are installed before the Go code starts.
+
+static void init(void) __attribute__ ((constructor (200)));
+
+static void init() {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_sigaction = ioHandler;
+ if (sigemptyset(&sa.sa_mask) < 0) {
+ die("sigemptyset");
+ }
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGIO, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+
+ sa.sa_sigaction = segvHandler;
+ if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+
+ sa.sa_sigaction = pipeHandler;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+}
+
+int main(int argc, char** argv) {
+ int verbose;
+ sigset_t mask;
+ int i;
+ struct timespec ts;
+ int darwin;
+
+ darwin = atoi(argv[1]);
+
+ verbose = argc > 2;
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ // Call setsid so that we can use kill(0, SIGIO) below.
+ // Don't check the return value so that this works both from
+ // a job control shell and from a shell script.
+ setsid();
+
+ if (verbose) {
+ printf("calling RunGoroutines\n");
+ }
+
+ RunGoroutines();
+
+ // Block SIGIO in this thread to make it more likely that it
+ // will be delivered to a goroutine.
+
+ if (verbose) {
+ printf("calling pthread_sigmask\n");
+ }
+
+ if (sigemptyset(&mask) < 0) {
+ die("sigemptyset");
+ }
+ if (sigaddset(&mask, SIGIO) < 0) {
+ die("sigaddset");
+ }
+ i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
+ if (i != 0) {
+ fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling kill\n");
+ }
+
+ if (kill(0, SIGIO) < 0) {
+ die("kill");
+ }
+
+ if (verbose) {
+ printf("waiting for sigioSeen\n");
+ }
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (!sigioSeen) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for SIGIO\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (verbose) {
+ printf("provoking SIGPIPE\n");
+ }
+
+ // SIGPIPE is never forwarded on Darwin, see golang.org/issue/33384.
+ if (!darwin) {
+ GoRaiseSIGPIPE();
+
+ if (verbose) {
+ printf("waiting for sigpipeSeen\n");
+ }
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (!sigpipeSeen) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for SIGPIPE\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (verbose) {
+ printf("calling setjmp\n");
+ }
+
+ // Test that a SIGSEGV on this thread is delivered to us.
+ if (setjmp(jmp) == 0) {
+ if (verbose) {
+ printf("triggering SIGSEGV\n");
+ }
+
+ *nullPointer = '\0';
+
+ fprintf(stderr, "continued after address error\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling TestSEGV\n");
+ }
+
+ TestSEGV();
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main3.c b/src/cmd/cgo/internal/testcarchive/testdata/main3.c
new file mode 100644
index 0000000..983e1b6
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main3.c
@@ -0,0 +1,210 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test os/signal.Notify and os/signal.Reset.
+// This is a lot like ../testcshared/main5.c.
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sched.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include "libgo3.h"
+
+static void die(const char* msg) {
+ perror(msg);
+ exit(EXIT_FAILURE);
+}
+
+static volatile sig_atomic_t sigioSeen;
+
+static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
+ sigioSeen = 1;
+}
+
+// Set up the SIGPIPE signal handler in a high priority constructor, so
+// that it is installed before the Go code starts.
+
+static void pipeHandler(int signo, siginfo_t* info, void* ctxt) {
+ const char *s = "unexpected SIGPIPE\n";
+ write(2, s, strlen(s));
+ exit(EXIT_FAILURE);
+}
+
+static void init(void) __attribute__ ((constructor (200)));
+
+static void init() {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_sigaction = pipeHandler;
+ if (sigemptyset(&sa.sa_mask) < 0) {
+ die("sigemptyset");
+ }
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+}
+
+static void *provokeSIGPIPE(void *arg) {
+ ProvokeSIGPIPE();
+ return NULL;
+}
+
+int main(int argc, char** argv) {
+ int verbose;
+ struct sigaction sa;
+ int i;
+ struct timespec ts;
+ int res;
+ pthread_t tid;
+
+ verbose = argc > 2;
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ if (verbose) {
+ printf("raising SIGPIPE\n");
+ }
+
+ // Test that the Go runtime handles SIGPIPE, even if we installed
+ // a non-default SIGPIPE handler before the runtime initializes.
+ ProvokeSIGPIPE();
+
+ // Test that SIGPIPE on a non-main thread is also handled by Go.
+ res = pthread_create(&tid, NULL, provokeSIGPIPE, NULL);
+ if (res != 0) {
+ fprintf(stderr, "pthread_create: %s\n", strerror(res));
+ exit(EXIT_FAILURE);
+ }
+
+ res = pthread_join(tid, NULL);
+ if (res != 0) {
+ fprintf(stderr, "pthread_join: %s\n", strerror(res));
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling sigaction\n");
+ }
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_sigaction = ioHandler;
+ if (sigemptyset(&sa.sa_mask) < 0) {
+ die("sigemptyset");
+ }
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGIO, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+
+ // At this point there should not be a Go signal handler
+ // installed for SIGIO.
+
+ if (verbose) {
+ printf("raising SIGIO\n");
+ }
+
+ if (raise(SIGIO) < 0) {
+ die("raise");
+ }
+
+ if (verbose) {
+ printf("waiting for sigioSeen\n");
+ }
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (!sigioSeen) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for signal\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ sigioSeen = 0;
+
+ // Tell the Go code to catch SIGIO.
+
+ if (verbose) {
+ printf("calling CatchSIGIO\n");
+ }
+
+ CatchSIGIO();
+
+ if (verbose) {
+ printf("raising SIGIO\n");
+ }
+
+ if (raise(SIGIO) < 0) {
+ die("raise");
+ }
+
+ if (verbose) {
+ printf("calling SawSIGIO\n");
+ }
+
+ if (!SawSIGIO()) {
+ fprintf(stderr, "Go handler did not see SIGIO\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (sigioSeen != 0) {
+ fprintf(stderr, "C handler saw SIGIO when only Go handler should have\n");
+ exit(EXIT_FAILURE);
+ }
+
+ // Tell the Go code to stop catching SIGIO.
+
+ if (verbose) {
+ printf("calling ResetSIGIO\n");
+ }
+
+ ResetSIGIO();
+
+ if (verbose) {
+ printf("raising SIGIO\n");
+ }
+
+ if (raise(SIGIO) < 0) {
+ die("raise");
+ }
+
+ if (verbose) {
+ printf("calling SawSIGIO\n");
+ }
+
+ if (SawSIGIO()) {
+ fprintf(stderr, "Go handler saw SIGIO after Reset\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("waiting for sigioSeen\n");
+ }
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (!sigioSeen) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for signal\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main4.c b/src/cmd/cgo/internal/testcarchive/testdata/main4.c
new file mode 100644
index 0000000..04f7740
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main4.c
@@ -0,0 +1,204 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test a C thread that calls sigaltstack and then calls Go code.
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sched.h>
+#include <pthread.h>
+
+#include "libgo4.h"
+
+#ifdef _AIX
+// On AIX, CSIGSTKSZ is too small to handle Go sighandler.
+#define CSIGSTKSZ 0x4000
+#else
+#define CSIGSTKSZ SIGSTKSZ
+#endif
+
+static void die(const char* msg) {
+ perror(msg);
+ exit(EXIT_FAILURE);
+}
+
+static int ok = 1;
+
+static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
+}
+
+// Set up the SIGIO signal handler in a high priority constructor, so
+// that it is installed before the Go code starts.
+
+static void init(void) __attribute__ ((constructor (200)));
+
+static void init() {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_sigaction = ioHandler;
+ if (sigemptyset(&sa.sa_mask) < 0) {
+ die("sigemptyset");
+ }
+ sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ if (sigaction(SIGIO, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+}
+
+// Test raising SIGIO on a C thread with an alternate signal stack
+// when there is a Go signal handler for SIGIO.
+static void* thread1(void* arg __attribute__ ((unused))) {
+ stack_t ss;
+ int i;
+ stack_t nss;
+ struct timespec ts;
+
+ // Set up an alternate signal stack for this thread.
+ memset(&ss, 0, sizeof ss);
+ ss.ss_sp = malloc(CSIGSTKSZ);
+ if (ss.ss_sp == NULL) {
+ die("malloc");
+ }
+ ss.ss_flags = 0;
+ ss.ss_size = CSIGSTKSZ;
+ if (sigaltstack(&ss, NULL) < 0) {
+ die("sigaltstack");
+ }
+
+ // Send ourselves a SIGIO. This will be caught by the Go
+ // signal handler which should forward to the C signal
+ // handler.
+ i = pthread_kill(pthread_self(), SIGIO);
+ if (i != 0) {
+ fprintf(stderr, "pthread_kill: %s\n", strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (SIGIOCount() == 0) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for signal\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ // We should still be on the same signal stack.
+ if (sigaltstack(NULL, &nss) < 0) {
+ die("sigaltstack check");
+ }
+ if ((nss.ss_flags & SS_DISABLE) != 0) {
+ fprintf(stderr, "sigaltstack disabled on return from Go\n");
+ ok = 0;
+ } else if (nss.ss_sp != ss.ss_sp) {
+ fprintf(stderr, "sigaltstack changed on return from Go\n");
+ ok = 0;
+ }
+
+ return NULL;
+}
+
+// Test calling a Go function to raise SIGIO on a C thread with an
+// alternate signal stack when there is a Go signal handler for SIGIO.
+static void* thread2(void* arg __attribute__ ((unused))) {
+ stack_t ss;
+ int i;
+ int oldcount;
+ pthread_t tid;
+ struct timespec ts;
+ stack_t nss;
+
+ // Set up an alternate signal stack for this thread.
+ memset(&ss, 0, sizeof ss);
+ ss.ss_sp = malloc(CSIGSTKSZ);
+ if (ss.ss_sp == NULL) {
+ die("malloc");
+ }
+ ss.ss_flags = 0;
+ ss.ss_size = CSIGSTKSZ;
+ if (sigaltstack(&ss, NULL) < 0) {
+ die("sigaltstack");
+ }
+
+ oldcount = SIGIOCount();
+
+ // Call a Go function that will call a C function to send us a
+ // SIGIO.
+ tid = pthread_self();
+ GoRaiseSIGIO(&tid);
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (SIGIOCount() == oldcount) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for signal\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ // We should still be on the same signal stack.
+ if (sigaltstack(NULL, &nss) < 0) {
+ die("sigaltstack check");
+ }
+ if ((nss.ss_flags & SS_DISABLE) != 0) {
+ fprintf(stderr, "sigaltstack disabled on return from Go\n");
+ ok = 0;
+ } else if (nss.ss_sp != ss.ss_sp) {
+ fprintf(stderr, "sigaltstack changed on return from Go\n");
+ ok = 0;
+ }
+
+ return NULL;
+}
+
+int main(int argc, char **argv) {
+ pthread_t tid;
+ int i;
+
+ // Tell the Go library to start looking for SIGIO.
+ GoCatchSIGIO();
+
+ i = pthread_create(&tid, NULL, thread1, NULL);
+ if (i != 0) {
+ fprintf(stderr, "pthread_create: %s\n", strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ i = pthread_join(tid, NULL);
+ if (i != 0) {
+ fprintf(stderr, "pthread_join: %s\n", strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ i = pthread_create(&tid, NULL, thread2, NULL);
+ if (i != 0) {
+ fprintf(stderr, "pthread_create: %s\n", strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ i = pthread_join(tid, NULL);
+ if (i != 0) {
+ fprintf(stderr, "pthread_join: %s\n", strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ if (!ok) {
+ exit(EXIT_FAILURE);
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main5.c b/src/cmd/cgo/internal/testcarchive/testdata/main5.c
new file mode 100644
index 0000000..c64c246
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main5.c
@@ -0,0 +1,105 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test for verifying that the Go runtime properly forwards
+// signals when non-Go signals are raised.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+#include "libgo2.h"
+
+int *nilp;
+
+int main(int argc, char** argv) {
+ int verbose;
+ int test;
+
+ if (argc < 2) {
+ printf("Missing argument\n");
+ return 1;
+ }
+
+ test = atoi(argv[1]);
+
+ verbose = (argc > 2);
+
+ Noop();
+
+ switch (test) {
+ case 1: {
+ if (verbose) {
+ printf("attempting segfault\n");
+ }
+
+ *nilp = 0;
+ break;
+ }
+
+ case 2: {
+ struct timeval tv;
+
+ if (verbose) {
+ printf("attempting external signal test\n");
+ }
+
+ fprintf(stderr, "OK\n");
+ fflush(stderr);
+
+ // The program should be interrupted before
+ // this sleep finishes. We use select rather
+ // than sleep because in older versions of
+ // glibc the sleep function does some signal
+ // fiddling to handle SIGCHLD. If this
+ // program is fiddling signals just when the
+ // test program sends the signal, the signal
+ // may be delivered to a Go thread which will
+ // break this test.
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+ select(0, NULL, NULL, NULL, &tv);
+
+ break;
+ }
+ case 3: {
+ if (verbose) {
+ printf("attempting SIGPIPE\n");
+ }
+
+ int fd[2];
+ if (pipe(fd) != 0) {
+ printf("pipe(2) failed\n");
+ return 0;
+ }
+ // Close the reading end.
+ close(fd[0]);
+ // Expect that write(2) fails (EPIPE)
+ if (write(fd[1], "some data", 9) != -1) {
+ printf("write(2) unexpectedly succeeded\n");
+ return 0;
+ }
+ printf("did not receive SIGPIPE\n");
+ return 0;
+ }
+ case 4: {
+ fprintf(stderr, "OK\n");
+ fflush(stderr);
+
+ if (verbose) {
+ printf("calling Block\n");
+ }
+ Block();
+ }
+ default:
+ printf("Unknown test: %d\n", test);
+ return 0;
+ }
+
+ printf("FAIL\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main6.c b/src/cmd/cgo/internal/testcarchive/testdata/main6.c
new file mode 100644
index 0000000..2745eb9
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main6.c
@@ -0,0 +1,34 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that using the Go profiler in a C program does not crash.
+
+#include <stddef.h>
+#include <sys/time.h>
+
+#include "libgo6.h"
+
+int main(int argc, char **argv) {
+ struct timeval tvstart, tvnow;
+ int diff;
+
+ gettimeofday(&tvstart, NULL);
+
+ go_start_profile();
+
+ // Busy wait so we have something to profile.
+ // If we just sleep the profiling signal will never fire.
+ while (1) {
+ gettimeofday(&tvnow, NULL);
+ diff = (tvnow.tv_sec - tvstart.tv_sec) * 1000 * 1000 + (tvnow.tv_usec - tvstart.tv_usec);
+
+ // Profile frequency is 100Hz so we should definitely
+ // get a signal in 50 milliseconds.
+ if (diff > 50 * 1000)
+ break;
+ }
+
+ go_stop_profile();
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main7.c b/src/cmd/cgo/internal/testcarchive/testdata/main7.c
new file mode 100644
index 0000000..2c6d98d
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main7.c
@@ -0,0 +1,18 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that lots of calls don't deadlock.
+
+#include <stdio.h>
+
+#include "libgo7.h"
+
+int main() {
+ int i;
+
+ for (i = 0; i < 100000; i++) {
+ GoFunction7();
+ }
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main8.c b/src/cmd/cgo/internal/testcarchive/testdata/main8.c
new file mode 100644
index 0000000..95fb7a3
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main8.c
@@ -0,0 +1,16 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test preemption.
+
+#include <stdlib.h>
+
+#include "libgo8.h"
+
+int main() {
+ GoFunction8();
+
+ // That should have exited the program.
+ abort();
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main9.c b/src/cmd/cgo/internal/testcarchive/testdata/main9.c
new file mode 100644
index 0000000..95ad4de
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main9.c
@@ -0,0 +1,24 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "libgo9.h"
+
+void use(int *x) { (*x)++; }
+
+void callGoFWithDeepStack() {
+ int x[10000];
+
+ use(&x[0]);
+ use(&x[9999]);
+
+ GoF();
+
+ use(&x[0]);
+ use(&x[9999]);
+}
+
+int main() {
+ GoF(); // call GoF without using much stack
+ callGoFWithDeepStack(); // call GoF with a deep stack
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main_unix.c b/src/cmd/cgo/internal/testcarchive/testdata/main_unix.c
new file mode 100644
index 0000000..bd00f9d
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main_unix.c
@@ -0,0 +1,59 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct sigaction sa;
+struct sigaction osa;
+
+static void (*oldHandler)(int, siginfo_t*, void*);
+
+static void handler(int signo, siginfo_t* info, void* ctxt) {
+ if (oldHandler) {
+ oldHandler(signo, info, ctxt);
+ }
+}
+
+int install_handler() {
+ // Install our own signal handler.
+ memset(&sa, 0, sizeof sa);
+ sa.sa_sigaction = handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
+ memset(&osa, 0, sizeof osa);
+ sigemptyset(&osa.sa_mask);
+ if (sigaction(SIGSEGV, &sa, &osa) < 0) {
+ perror("sigaction");
+ return 2;
+ }
+ if (osa.sa_handler == SIG_DFL) {
+ fprintf(stderr, "Go runtime did not install signal handler\n");
+ return 2;
+ }
+ // gccgo does not set SA_ONSTACK for SIGSEGV.
+ if (getenv("GCCGO") == NULL && (osa.sa_flags&SA_ONSTACK) == 0) {
+ fprintf(stderr, "Go runtime did not install signal handler\n");
+ return 2;
+ }
+ oldHandler = osa.sa_sigaction;
+
+ return 0;
+}
+
+int check_handler() {
+ if (sigaction(SIGSEGV, NULL, &sa) < 0) {
+ perror("sigaction check");
+ return 2;
+ }
+ if (sa.sa_sigaction != handler) {
+ fprintf(stderr, "ERROR: wrong signal handler: %p != %p\n", sa.sa_sigaction, handler);
+ return 2;
+ }
+ return 0;
+}
+
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/main_windows.c b/src/cmd/cgo/internal/testcarchive/testdata/main_windows.c
new file mode 100644
index 0000000..eded8af
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/main_windows.c
@@ -0,0 +1,17 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * Dummy implementations for Windows, because Windows doesn't
+ * support Unix-style signal handling.
+ */
+
+int install_handler() {
+ return 0;
+}
+
+
+int check_handler() {
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcarchive/testdata/p/p.go b/src/cmd/cgo/internal/testcarchive/testdata/p/p.go
new file mode 100644
index 0000000..82b445c
--- /dev/null
+++ b/src/cmd/cgo/internal/testcarchive/testdata/p/p.go
@@ -0,0 +1,10 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import "C"
+
+//export FromPkg
+func FromPkg() int32 { return 1024 }
diff --git a/src/cmd/cgo/internal/testcshared/cshared_test.go b/src/cmd/cgo/internal/testcshared/cshared_test.go
new file mode 100644
index 0000000..7fe6782
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/cshared_test.go
@@ -0,0 +1,874 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cshared_test
+
+import (
+ "bufio"
+ "bytes"
+ "cmd/cgo/internal/cgotest"
+ "debug/elf"
+ "debug/pe"
+ "encoding/binary"
+ "flag"
+ "fmt"
+ "internal/testenv"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "sync"
+ "testing"
+ "unicode"
+)
+
+var globalSkip = func(t *testing.T) {}
+
+// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
+var cc []string
+
+// ".exe" on Windows.
+var exeSuffix string
+
+var GOOS, GOARCH, GOROOT string
+var installdir string
+var libgoname string
+
+func TestMain(m *testing.M) {
+ os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+ log.SetFlags(log.Lshortfile)
+ flag.Parse()
+ if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
+ globalSkip = func(t *testing.T) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
+ return m.Run()
+ }
+ if runtime.GOOS == "linux" {
+ if _, err := os.Stat("/etc/alpine-release"); err == nil {
+ globalSkip = func(t *testing.T) { t.Skip("skipping failing test on alpine - go.dev/issue/19938") }
+ return m.Run()
+ }
+ }
+ if !testenv.HasGoBuild() {
+ // Checking for "go build" is a proxy for whether or not we can run "go env".
+ globalSkip = func(t *testing.T) { t.Skip("no go build") }
+ return m.Run()
+ }
+
+ GOOS = goEnv("GOOS")
+ GOARCH = goEnv("GOARCH")
+ GOROOT = goEnv("GOROOT")
+
+ if _, err := os.Stat(GOROOT); os.IsNotExist(err) {
+ log.Fatalf("Unable able to find GOROOT at '%s'", GOROOT)
+ }
+
+ cc = []string{goEnv("CC")}
+
+ out := goEnv("GOGCCFLAGS")
+ quote := '\000'
+ start := 0
+ lastSpace := true
+ backslash := false
+ s := string(out)
+ for i, c := range s {
+ if quote == '\000' && unicode.IsSpace(c) {
+ if !lastSpace {
+ cc = append(cc, s[start:i])
+ lastSpace = true
+ }
+ } else {
+ if lastSpace {
+ start = i
+ lastSpace = false
+ }
+ if quote == '\000' && !backslash && (c == '"' || c == '\'') {
+ quote = c
+ backslash = false
+ } else if !backslash && quote == c {
+ quote = '\000'
+ } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
+ backslash = true
+ } else {
+ backslash = false
+ }
+ }
+ }
+ if !lastSpace {
+ cc = append(cc, s[start:])
+ }
+
+ switch GOOS {
+ case "darwin", "ios":
+ // For Darwin/ARM.
+ // TODO(crawshaw): can we do better?
+ cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
+ case "android":
+ cc = append(cc, "-pie")
+ }
+ libgodir := GOOS + "_" + GOARCH
+ switch GOOS {
+ case "darwin", "ios":
+ if GOARCH == "arm64" {
+ libgodir += "_shared"
+ }
+ case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris", "illumos":
+ libgodir += "_shared"
+ }
+ cc = append(cc, "-I", filepath.Join("pkg", libgodir))
+
+ // Force reallocation (and avoid aliasing bugs) for parallel tests that append to cc.
+ cc = cc[:len(cc):len(cc)]
+
+ if GOOS == "windows" {
+ exeSuffix = ".exe"
+ }
+
+ // Copy testdata into GOPATH/src/testcshared, along with a go.mod file
+ // declaring the same path.
+
+ GOPATH, err := os.MkdirTemp("", "cshared_test")
+ if err != nil {
+ log.Panic(err)
+ }
+ defer os.RemoveAll(GOPATH)
+ os.Setenv("GOPATH", GOPATH)
+
+ modRoot := filepath.Join(GOPATH, "src", "testcshared")
+ if err := cgotest.OverlayDir(modRoot, "testdata"); err != nil {
+ log.Panic(err)
+ }
+ if err := os.Chdir(modRoot); err != nil {
+ log.Panic(err)
+ }
+ os.Setenv("PWD", modRoot)
+ if err := os.WriteFile("go.mod", []byte("module testcshared\n"), 0666); err != nil {
+ log.Panic(err)
+ }
+
+ defer func() {
+ if installdir != "" {
+ err := os.RemoveAll(installdir)
+ if err != nil {
+ log.Panic(err)
+ }
+ }
+ }()
+
+ return m.Run()
+}
+
+func goEnv(key string) string {
+ out, err := exec.Command("go", "env", key).Output()
+ if err != nil {
+ log.Printf("go env %s failed:\n%s", key, err)
+ log.Panicf("%s", err.(*exec.ExitError).Stderr)
+ }
+ return strings.TrimSpace(string(out))
+}
+
+func cmdToRun(name string) string {
+ return "./" + name + exeSuffix
+}
+
+func run(t *testing.T, extraEnv []string, args ...string) string {
+ t.Helper()
+ cmd := exec.Command(args[0], args[1:]...)
+ if len(extraEnv) > 0 {
+ cmd.Env = append(os.Environ(), extraEnv...)
+ }
+
+ if GOOS != "windows" {
+ // TestUnexportedSymbols relies on file descriptor 30
+ // being closed when the program starts, so enforce
+ // that in all cases. (The first three descriptors are
+ // stdin/stdout/stderr, so we just need to make sure
+ // that cmd.ExtraFiles[27] exists and is nil.)
+ cmd.ExtraFiles = make([]*os.File, 28)
+ }
+
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out)
+ } else {
+ t.Logf("run: %v", args)
+ }
+ return string(out)
+}
+
+func runExe(t *testing.T, extraEnv []string, args ...string) string {
+ t.Helper()
+ return run(t, extraEnv, args...)
+}
+
+func runCC(t *testing.T, args ...string) string {
+ t.Helper()
+ // This function is run in parallel, so append to a copy of cc
+ // rather than cc itself.
+ return run(t, nil, append(append([]string(nil), cc...), args...)...)
+}
+
+func createHeaders() error {
+ // The 'cgo' command generates a number of additional artifacts,
+ // but we're only interested in the header.
+ // Shunt the rest of the outputs to a temporary directory.
+ objDir, err := os.MkdirTemp("", "testcshared_obj")
+ if err != nil {
+ return err
+ }
+ defer os.RemoveAll(objDir)
+
+ // Generate a C header file for p, which is a non-main dependency
+ // of main package libgo.
+ //
+ // TODO(golang.org/issue/35715): This should be simpler.
+ args := []string{"go", "tool", "cgo",
+ "-objdir", objDir,
+ "-exportheader", "p.h",
+ filepath.Join(".", "p", "p.go")}
+ cmd := exec.Command(args[0], args[1:]...)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
+ }
+
+ // Generate a C header file for libgo itself.
+ installdir, err = os.MkdirTemp("", "testcshared")
+ if err != nil {
+ return err
+ }
+ libgoname = "libgo.a"
+
+ args = []string{"go", "build", "-buildmode=c-shared", "-o", filepath.Join(installdir, libgoname), "./libgo"}
+ cmd = exec.Command(args[0], args[1:]...)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
+ }
+
+ args = []string{"go", "build", "-buildmode=c-shared",
+ "-installsuffix", "testcshared",
+ "-o", libgoname,
+ filepath.Join(".", "libgo", "libgo.go")}
+ if GOOS == "windows" && strings.HasSuffix(args[6], ".a") {
+ args[6] = strings.TrimSuffix(args[6], ".a") + ".dll"
+ }
+ cmd = exec.Command(args[0], args[1:]...)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
+ }
+ if GOOS == "windows" {
+ // We can't simply pass -Wl,--out-implib, because this relies on having imports from multiple packages,
+ // which results in the linkers output implib getting overwritten at each step. So instead build the
+ // import library the traditional way, using a def file.
+ err = os.WriteFile("libgo.def",
+ []byte("LIBRARY libgo.dll\nEXPORTS\n\tDidInitRun\n\tDidMainRun\n\tDivu\n\tFromPkg\n\t_cgo_dummy_export\n"),
+ 0644)
+ if err != nil {
+ return fmt.Errorf("unable to write def file: %v", err)
+ }
+ out, err = exec.Command(cc[0], append(cc[1:], "-print-prog-name=dlltool")...).CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("unable to find dlltool path: %v\n%s\n", err, out)
+ }
+ dlltoolpath := strings.TrimSpace(string(out))
+ if filepath.Ext(dlltoolpath) == "" {
+ // Some compilers report slash-separated paths without extensions
+ // instead of ordinary Windows paths.
+ // Try to find the canonical name for the path.
+ if lp, err := exec.LookPath(dlltoolpath); err == nil {
+ dlltoolpath = lp
+ }
+ }
+
+ args := []string{dlltoolpath, "-D", args[6], "-l", libgoname, "-d", "libgo.def"}
+
+ if filepath.Ext(dlltoolpath) == "" {
+ // This is an unfortunate workaround for
+ // https://github.com/mstorsjo/llvm-mingw/issues/205 in which
+ // we basically reimplement the contents of the dlltool.sh
+ // wrapper: https://git.io/JZFlU.
+ // TODO(thanm): remove this workaround once we can upgrade
+ // the compilers on the windows-arm64 builder.
+ dlltoolContents, err := os.ReadFile(args[0])
+ if err != nil {
+ return fmt.Errorf("unable to read dlltool: %v\n", err)
+ }
+ if bytes.HasPrefix(dlltoolContents, []byte("#!/bin/sh")) && bytes.Contains(dlltoolContents, []byte("llvm-dlltool")) {
+ base, name := filepath.Split(args[0])
+ args[0] = filepath.Join(base, "llvm-dlltool")
+ var machine string
+ switch prefix, _, _ := strings.Cut(name, "-"); prefix {
+ case "i686":
+ machine = "i386"
+ case "x86_64":
+ machine = "i386:x86-64"
+ case "armv7":
+ machine = "arm"
+ case "aarch64":
+ machine = "arm64"
+ }
+ if len(machine) > 0 {
+ args = append(args, "-m", machine)
+ }
+ }
+ }
+
+ out, err = exec.Command(args[0], args[1:]...).CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("unable to run dlltool to create import library: %v\n%s\n", err, out)
+ }
+ }
+
+ return nil
+}
+
+var (
+ headersOnce sync.Once
+ headersErr error
+)
+
+func createHeadersOnce(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-shared")
+
+ headersOnce.Do(func() {
+ headersErr = createHeaders()
+ })
+ if headersErr != nil {
+ t.Helper()
+ t.Fatal(headersErr)
+ }
+}
+
+// test0: exported symbols in shared lib are accessible.
+func TestExportedSymbols(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveExec(t)
+
+ t.Parallel()
+
+ cmd := "testp0"
+ bin := cmdToRun(cmd)
+
+ createHeadersOnce(t)
+
+ runCC(t, "-I", installdir, "-o", cmd, "main0.c", libgoname)
+
+ defer os.Remove(bin)
+
+ out := runExe(t, []string{"LD_LIBRARY_PATH=."}, bin)
+ if strings.TrimSpace(out) != "PASS" {
+ t.Error(out)
+ }
+}
+
+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() {
+}
+`
+
+ 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("go", 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)
+ })
+ t.Run("All", func(t *testing.T) {
+ checkNumberOfExportedFunctionsWindows(t, true)
+ })
+}
+
+// test1: shared library can be dynamically loaded and exported symbols are accessible.
+func TestExportedSymbolsWithDynamicLoad(t *testing.T) {
+ if GOOS == "windows" {
+ t.Skipf("Skipping on %s", GOOS)
+ }
+ globalSkip(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveExec(t)
+
+ t.Parallel()
+
+ cmd := "testp1"
+ bin := cmdToRun(cmd)
+
+ createHeadersOnce(t)
+
+ if GOOS != "freebsd" {
+ runCC(t, "-o", cmd, "main1.c", "-ldl")
+ } else {
+ runCC(t, "-o", cmd, "main1.c")
+ }
+
+ defer os.Remove(bin)
+
+ out := runExe(t, nil, bin, "./"+libgoname)
+ if strings.TrimSpace(out) != "PASS" {
+ t.Error(out)
+ }
+}
+
+// test2: tests libgo2 which does not export any functions.
+func TestUnexportedSymbols(t *testing.T) {
+ if GOOS == "windows" {
+ t.Skipf("Skipping on %s", GOOS)
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-shared")
+
+ t.Parallel()
+
+ cmd := "testp2"
+ bin := cmdToRun(cmd)
+ libname := "libgo2.a"
+
+ run(t,
+ nil,
+ "go", "build",
+ "-buildmode=c-shared",
+ "-installsuffix", "testcshared",
+ "-o", libname, "./libgo2",
+ )
+
+ linkFlags := "-Wl,--no-as-needed"
+ if GOOS == "darwin" || GOOS == "ios" {
+ linkFlags = ""
+ }
+
+ runCC(t, "-o", cmd, "main2.c", linkFlags, libname)
+
+ defer os.Remove(libname)
+ defer os.Remove(bin)
+
+ out := runExe(t, []string{"LD_LIBRARY_PATH=."}, bin)
+
+ if strings.TrimSpace(out) != "PASS" {
+ t.Error(out)
+ }
+}
+
+// test3: tests main.main is exported on android.
+func TestMainExportedOnAndroid(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveExec(t)
+
+ t.Parallel()
+
+ switch GOOS {
+ case "android":
+ break
+ default:
+ t.Logf("Skipping on %s", GOOS)
+ return
+ }
+
+ cmd := "testp3"
+ bin := cmdToRun(cmd)
+
+ createHeadersOnce(t)
+
+ runCC(t, "-o", cmd, "main3.c", "-ldl")
+
+ defer os.Remove(bin)
+
+ out := runExe(t, nil, bin, "./"+libgoname)
+ if strings.TrimSpace(out) != "PASS" {
+ t.Error(out)
+ }
+}
+
+func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) {
+ if GOOS == "windows" {
+ t.Skipf("Skipping on %s", GOOS)
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-shared")
+
+ libname := pkgname + ".a"
+ run(t,
+ nil,
+ "go", "build",
+ "-buildmode=c-shared",
+ "-installsuffix", "testcshared",
+ "-o", libname, pkgname,
+ )
+ if GOOS != "freebsd" {
+ runCC(t, "-pthread", "-o", cmd, cfile, "-ldl")
+ } else {
+ runCC(t, "-pthread", "-o", cmd, cfile)
+ }
+
+ bin := cmdToRun(cmd)
+
+ defer os.Remove(libname)
+ defer os.Remove(bin)
+ defer os.Remove(pkgname + ".h")
+
+ out := runExe(t, nil, bin, "./"+libname)
+ if strings.TrimSpace(out) != "PASS" {
+ t.Error(run(t, nil, bin, libname, "verbose"))
+ }
+}
+
+// test4: test signal handlers
+func TestSignalHandlers(t *testing.T) {
+ t.Parallel()
+ testSignalHandlers(t, "./libgo4", "main4.c", "testp4")
+}
+
+// test5: test signal handlers with os/signal.Notify
+func TestSignalHandlersWithNotify(t *testing.T) {
+ t.Parallel()
+ testSignalHandlers(t, "./libgo5", "main5.c", "testp5")
+}
+
+func TestPIE(t *testing.T) {
+ switch GOOS {
+ case "linux", "android":
+ break
+ default:
+ t.Skipf("Skipping on %s", GOOS)
+ }
+ globalSkip(t)
+
+ t.Parallel()
+
+ createHeadersOnce(t)
+
+ f, err := elf.Open(libgoname)
+ if err != nil {
+ t.Fatalf("elf.Open failed: %v", err)
+ }
+ defer f.Close()
+
+ ds := f.SectionByType(elf.SHT_DYNAMIC)
+ if ds == nil {
+ t.Fatalf("no SHT_DYNAMIC section")
+ }
+ d, err := ds.Data()
+ if err != nil {
+ t.Fatalf("can't read SHT_DYNAMIC contents: %v", err)
+ }
+ for len(d) > 0 {
+ var tag elf.DynTag
+ switch f.Class {
+ case elf.ELFCLASS32:
+ tag = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
+ d = d[8:]
+ case elf.ELFCLASS64:
+ tag = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
+ d = d[16:]
+ }
+ if tag == elf.DT_TEXTREL {
+ t.Fatalf("%s has DT_TEXTREL flag", libgoname)
+ }
+ }
+}
+
+// Test that installing a second time recreates the header file.
+func TestCachedInstall(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-shared")
+
+ tmpdir, err := os.MkdirTemp("", "cshared")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "go.mod"), "go.mod")
+ copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "libgo", "libgo.go"), filepath.Join("libgo", "libgo.go"))
+ copyFile(t, filepath.Join(tmpdir, "src", "testcshared", "p", "p.go"), filepath.Join("p", "p.go"))
+
+ buildcmd := []string{"go", "install", "-x", "-buildmode=c-shared", "-installsuffix", "testcshared", "./libgo"}
+
+ cmd := exec.Command(buildcmd[0], buildcmd[1:]...)
+ cmd.Dir = filepath.Join(tmpdir, "src", "testcshared")
+ env := append(cmd.Environ(),
+ "GOPATH="+tmpdir,
+ "GOBIN="+filepath.Join(tmpdir, "bin"),
+ "GO111MODULE=off", // 'go install' only works in GOPATH mode
+ )
+ cmd.Env = env
+ t.Log(buildcmd)
+ out, err := cmd.CombinedOutput()
+ t.Logf("%s", out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var libgoh, ph string
+
+ walker := func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ t.Fatal(err)
+ }
+ var ps *string
+ switch filepath.Base(path) {
+ case "libgo.h":
+ ps = &libgoh
+ case "p.h":
+ ps = &ph
+ }
+ if ps != nil {
+ if *ps != "" {
+ t.Fatalf("%s found again", *ps)
+ }
+ *ps = path
+ }
+ return nil
+ }
+
+ if err := filepath.Walk(tmpdir, walker); err != nil {
+ t.Fatal(err)
+ }
+
+ if libgoh == "" {
+ t.Fatal("libgo.h not installed")
+ }
+
+ if err := os.Remove(libgoh); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd = exec.Command(buildcmd[0], buildcmd[1:]...)
+ cmd.Dir = filepath.Join(tmpdir, "src", "testcshared")
+ cmd.Env = env
+ t.Log(buildcmd)
+ out, err = cmd.CombinedOutput()
+ t.Logf("%s", out)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err := os.Stat(libgoh); err != nil {
+ t.Errorf("libgo.h not installed in second run: %v", err)
+ }
+}
+
+// copyFile copies src to dst.
+func copyFile(t *testing.T, dst, src string) {
+ t.Helper()
+ data, err := os.ReadFile(src)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(dst, data, 0666); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestGo2C2Go(t *testing.T) {
+ switch GOOS {
+ case "darwin", "ios", "windows":
+ // Non-ELF shared libraries don't support the multiple
+ // copies of the runtime package implied by this test.
+ t.Skipf("linking c-shared into Go programs not supported on %s; issue 29061, 49457", GOOS)
+ case "android":
+ t.Skip("test fails on android; issue 29087")
+ }
+ globalSkip(t)
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-shared")
+
+ t.Parallel()
+
+ tmpdir, err := os.MkdirTemp("", "cshared-TestGo2C2Go")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ lib := filepath.Join(tmpdir, "libtestgo2c2go.a")
+ var env []string
+ if GOOS == "windows" && strings.HasSuffix(lib, ".a") {
+ env = append(env, "CGO_LDFLAGS=-Wl,--out-implib,"+lib, "CGO_LDFLAGS_ALLOW=.*")
+ lib = strings.TrimSuffix(lib, ".a") + ".dll"
+ }
+ run(t, env, "go", "build", "-buildmode=c-shared", "-o", lib, "./go2c2go/go")
+
+ cgoCflags := os.Getenv("CGO_CFLAGS")
+ if cgoCflags != "" {
+ cgoCflags += " "
+ }
+ cgoCflags += "-I" + tmpdir
+
+ cgoLdflags := os.Getenv("CGO_LDFLAGS")
+ if cgoLdflags != "" {
+ cgoLdflags += " "
+ }
+ cgoLdflags += "-L" + tmpdir + " -ltestgo2c2go"
+
+ goenv := []string{"CGO_CFLAGS=" + cgoCflags, "CGO_LDFLAGS=" + cgoLdflags}
+
+ ldLibPath := os.Getenv("LD_LIBRARY_PATH")
+ if ldLibPath != "" {
+ ldLibPath += ":"
+ }
+ ldLibPath += tmpdir
+
+ runenv := []string{"LD_LIBRARY_PATH=" + ldLibPath}
+
+ bin := filepath.Join(tmpdir, "m1") + exeSuffix
+ run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m1")
+ runExe(t, runenv, bin)
+
+ bin = filepath.Join(tmpdir, "m2") + exeSuffix
+ run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m2")
+ runExe(t, runenv, bin)
+}
+
+func TestIssue36233(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveCGO(t)
+
+ t.Parallel()
+
+ // Test that the export header uses GoComplex64 and GoComplex128
+ // for complex types.
+
+ tmpdir, err := os.MkdirTemp("", "cshared-TestIssue36233")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ const exportHeader = "issue36233.h"
+
+ run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue36233/issue36233.go")
+ data, err := os.ReadFile(exportHeader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ funcs := []struct{ name, signature string }{
+ {"exportComplex64", "GoComplex64 exportComplex64(GoComplex64 v)"},
+ {"exportComplex128", "GoComplex128 exportComplex128(GoComplex128 v)"},
+ {"exportComplexfloat", "GoComplex64 exportComplexfloat(GoComplex64 v)"},
+ {"exportComplexdouble", "GoComplex128 exportComplexdouble(GoComplex128 v)"},
+ }
+
+ scanner := bufio.NewScanner(bytes.NewReader(data))
+ var found int
+ for scanner.Scan() {
+ b := scanner.Bytes()
+ for _, fn := range funcs {
+ if bytes.Contains(b, []byte(fn.name)) {
+ found++
+ if !bytes.Contains(b, []byte(fn.signature)) {
+ t.Errorf("function signature mismatch; got %q, want %q", b, fn.signature)
+ }
+ }
+ }
+ }
+ if err = scanner.Err(); err != nil {
+ t.Errorf("scanner encountered error: %v", err)
+ }
+ if found != len(funcs) {
+ t.Error("missing functions")
+ }
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/go2c2go/go/shlib.go b/src/cmd/cgo/internal/testcshared/testdata/go2c2go/go/shlib.go
new file mode 100644
index 0000000..76a5323
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/go2c2go/go/shlib.go
@@ -0,0 +1,12 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+//export GoFunc
+func GoFunc() int { return 1 }
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/go2c2go/m1/c.c b/src/cmd/cgo/internal/testcshared/testdata/go2c2go/m1/c.c
new file mode 100644
index 0000000..0e8fac4
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/go2c2go/m1/c.c
@@ -0,0 +1,9 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "libtestgo2c2go.h"
+
+int CFunc(void) {
+ return (GoFunc() << 8) + 2;
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/go2c2go/m1/main.go b/src/cmd/cgo/internal/testcshared/testdata/go2c2go/m1/main.go
new file mode 100644
index 0000000..17ba1eb
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/go2c2go/m1/main.go
@@ -0,0 +1,22 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// extern int CFunc(void);
+import "C"
+
+import (
+ "fmt"
+ "os"
+)
+
+func main() {
+ got := C.CFunc()
+ const want = (1 << 8) | 2
+ if got != want {
+ fmt.Printf("got %#x, want %#x\n", got, want)
+ os.Exit(1)
+ }
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/go2c2go/m2/main.go b/src/cmd/cgo/internal/testcshared/testdata/go2c2go/m2/main.go
new file mode 100644
index 0000000..91bf308
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/go2c2go/m2/main.go
@@ -0,0 +1,22 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// #include "libtestgo2c2go.h"
+import "C"
+
+import (
+ "fmt"
+ "os"
+)
+
+func main() {
+ got := C.GoFunc()
+ const want = 1
+ if got != want {
+ fmt.Printf("got %#x, want %#x\n", got, want)
+ os.Exit(1)
+ }
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/issue36233/issue36233.go b/src/cmd/cgo/internal/testcshared/testdata/issue36233/issue36233.go
new file mode 100644
index 0000000..433bf5c
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/issue36233/issue36233.go
@@ -0,0 +1,30 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// #include <complex.h>
+import "C"
+
+//export exportComplex64
+func exportComplex64(v complex64) complex64 {
+ return v
+}
+
+//export exportComplex128
+func exportComplex128(v complex128) complex128 {
+ return v
+}
+
+//export exportComplexfloat
+func exportComplexfloat(v C.complexfloat) C.complexfloat {
+ return v
+}
+
+//export exportComplexdouble
+func exportComplexdouble(v C.complexdouble) C.complexdouble {
+ return v
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/libgo/libgo.go b/src/cmd/cgo/internal/testcshared/testdata/libgo/libgo.go
new file mode 100644
index 0000000..0634417
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/libgo/libgo.go
@@ -0,0 +1,46 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "syscall"
+ _ "testcshared/p"
+ "time"
+)
+
+import "C"
+
+var initCh = make(chan int, 1)
+var ranMain bool
+
+func init() {
+ // emulate an exceedingly slow package initialization function
+ time.Sleep(100 * time.Millisecond)
+ initCh <- 42
+}
+
+func main() {
+ ranMain = true
+}
+
+//export DidInitRun
+func DidInitRun() bool {
+ select {
+ case x := <-initCh:
+ if x != 42 {
+ // Just in case initCh was not correctly made.
+ println("want init value of 42, got: ", x)
+ syscall.Exit(2)
+ }
+ return true
+ default:
+ return false
+ }
+}
+
+//export DidMainRun
+func DidMainRun() bool {
+ return ranMain
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/libgo2/dup2.go b/src/cmd/cgo/internal/testcshared/testdata/libgo2/dup2.go
new file mode 100644
index 0000000..d50e0c4
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/libgo2/dup2.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin || dragonfly || freebsd || (linux && !arm64 && !loong64 && !riscv64) || netbsd || openbsd
+
+package main
+
+import "syscall"
+
+func dup2(oldfd, newfd int) error {
+ return syscall.Dup2(oldfd, newfd)
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/libgo2/dup3.go b/src/cmd/cgo/internal/testcshared/testdata/libgo2/dup3.go
new file mode 100644
index 0000000..ec4b5a7
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/libgo2/dup3.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build (linux && arm64) || (linux && loong64) || (linux && riscv64)
+
+package main
+
+import "syscall"
+
+func dup2(oldfd, newfd int) error {
+ return syscall.Dup3(oldfd, newfd, 0)
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/libgo2/libgo2.go b/src/cmd/cgo/internal/testcshared/testdata/libgo2/libgo2.go
new file mode 100644
index 0000000..5f6cfd0
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/libgo2/libgo2.go
@@ -0,0 +1,52 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
+
+package main
+
+// Test a shared library created by -buildmode=c-shared that does not
+// export anything.
+
+import (
+ "fmt"
+ "os"
+ "syscall"
+)
+
+// To test this we want to communicate between the main program and
+// the shared library without using any exported symbols. The init
+// function creates a pipe and Dups the read end to a known number
+// that the C code can also use.
+
+const (
+ fd = 30
+)
+
+func init() {
+ var p [2]int
+ if e := syscall.Pipe(p[0:]); e != nil {
+ fmt.Fprintf(os.Stderr, "pipe: %v\n", e)
+ os.Exit(2)
+ }
+
+ if e := dup2(p[0], fd); e != nil {
+ fmt.Fprintf(os.Stderr, "dup2: %v\n", e)
+ os.Exit(2)
+ }
+
+ const str = "PASS"
+ if n, e := syscall.Write(p[1], []byte(str)); e != nil || n != len(str) {
+ fmt.Fprintf(os.Stderr, "write: %d %v\n", n, e)
+ os.Exit(2)
+ }
+
+ if e := syscall.Close(p[1]); e != nil {
+ fmt.Fprintf(os.Stderr, "close: %v\n", e)
+ os.Exit(2)
+ }
+}
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/libgo4/libgo4.go b/src/cmd/cgo/internal/testcshared/testdata/libgo4/libgo4.go
new file mode 100644
index 0000000..9c30c85
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/libgo4/libgo4.go
@@ -0,0 +1,47 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+import (
+ "fmt"
+ "os"
+ "runtime"
+)
+
+// RunGoroutines starts some goroutines that don't do anything.
+// The idea is to get some threads going, so that a signal will be delivered
+// to a thread started by Go.
+//
+//export RunGoroutines
+func RunGoroutines() {
+ for i := 0; i < 4; i++ {
+ go func() {
+ runtime.LockOSThread()
+ select {}
+ }()
+ }
+}
+
+var P *byte
+
+// TestSEGV makes sure that an invalid address turns into a run-time Go panic.
+//
+//export TestSEGV
+func TestSEGV() {
+ defer func() {
+ if recover() == nil {
+ fmt.Fprintln(os.Stderr, "no panic from segv")
+ os.Exit(1)
+ }
+ }()
+ *P = 0
+ fmt.Fprintln(os.Stderr, "continued after segv")
+ os.Exit(1)
+}
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/libgo5/libgo5.go b/src/cmd/cgo/internal/testcshared/testdata/libgo5/libgo5.go
new file mode 100644
index 0000000..4ca44e5
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/libgo5/libgo5.go
@@ -0,0 +1,47 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+)
+
+// The channel used to read SIGIO signals.
+var sigioChan chan os.Signal
+
+// CatchSIGIO starts catching SIGIO signals.
+//
+//export CatchSIGIO
+func CatchSIGIO() {
+ sigioChan = make(chan os.Signal, 1)
+ signal.Notify(sigioChan, syscall.SIGIO)
+}
+
+// ResetSIGIO stops catching SIGIO signals.
+//
+//export ResetSIGIO
+func ResetSIGIO() {
+ signal.Reset(syscall.SIGIO)
+}
+
+// SawSIGIO returns whether we saw a SIGIO within a brief pause.
+//
+//export SawSIGIO
+func SawSIGIO() C.int {
+ select {
+ case <-sigioChan:
+ return 1
+ case <-time.After(100 * time.Millisecond):
+ return 0
+ }
+}
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/main0.c b/src/cmd/cgo/internal/testcshared/testdata/main0.c
new file mode 100644
index 0000000..39ef7e3
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/main0.c
@@ -0,0 +1,42 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "p.h"
+#include "libgo.h"
+
+// Tests libgo.so to export the following functions.
+// int8_t DidInitRun();
+// int8_t DidMainRun();
+// int32_t FromPkg();
+// uint32_t Divu(uint32_t, uint32_t);
+int main(void) {
+ int8_t ran_init = DidInitRun();
+ if (!ran_init) {
+ fprintf(stderr, "ERROR: DidInitRun returned unexpected results: %d\n",
+ ran_init);
+ return 1;
+ }
+ int8_t ran_main = DidMainRun();
+ if (ran_main) {
+ fprintf(stderr, "ERROR: DidMainRun returned unexpected results: %d\n",
+ ran_main);
+ return 1;
+ }
+ int32_t from_pkg = FromPkg();
+ if (from_pkg != 1024) {
+ fprintf(stderr, "ERROR: FromPkg=%d, want %d\n", from_pkg, 1024);
+ return 1;
+ }
+ uint32_t divu = Divu(2264, 31);
+ if (divu != 73) {
+ fprintf(stderr, "ERROR: Divu(2264, 31)=%d, want %d\n", divu, 73);
+ return 1;
+ }
+ // test.bash looks for "PASS" to ensure this program has reached the end.
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/main1.c b/src/cmd/cgo/internal/testcshared/testdata/main1.c
new file mode 100644
index 0000000..420dd1e
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/main1.c
@@ -0,0 +1,69 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <stdint.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+int check_int8(void* handle, const char* fname, int8_t want) {
+ int8_t (*fn)();
+ fn = (int8_t (*)())dlsym(handle, fname);
+ if (!fn) {
+ fprintf(stderr, "ERROR: missing %s: %s\n", fname, dlerror());
+ return 1;
+ }
+ signed char ret = fn();
+ if (ret != want) {
+ fprintf(stderr, "ERROR: %s=%d, want %d\n", fname, ret, want);
+ return 1;
+ }
+ return 0;
+}
+
+int check_int32(void* handle, const char* fname, int32_t want) {
+ int32_t (*fn)();
+ fn = (int32_t (*)())dlsym(handle, fname);
+ if (!fn) {
+ fprintf(stderr, "ERROR: missing %s: %s\n", fname, dlerror());
+ return 1;
+ }
+ int32_t ret = fn();
+ if (ret != want) {
+ fprintf(stderr, "ERROR: %s=%d, want %d\n", fname, ret, want);
+ return 1;
+ }
+ return 0;
+}
+
+// Tests libgo.so to export the following functions.
+// int8_t DidInitRun() // returns true
+// int8_t DidMainRun() // returns true
+// int32_t FromPkg() // returns 1024
+int main(int argc, char** argv) {
+ void* handle = dlopen(argv[1], RTLD_LAZY | RTLD_GLOBAL);
+ if (!handle) {
+ fprintf(stderr, "ERROR: failed to open the shared library: %s\n",
+ dlerror());
+ return 2;
+ }
+
+ int ret = 0;
+ ret = check_int8(handle, "DidInitRun", 1);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = check_int8(handle, "DidMainRun", 0);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = check_int32(handle, "FromPkg", 1024);
+ if (ret != 0) {
+ return ret;
+ }
+ // test.bash looks for "PASS" to ensure this program has reached the end.
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/main2.c b/src/cmd/cgo/internal/testcshared/testdata/main2.c
new file mode 100644
index 0000000..f89bcca
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/main2.c
@@ -0,0 +1,56 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define fd (30)
+
+// Tests libgo2.so, which does not export any functions.
+// Read a string from the file descriptor and print it.
+int main(void) {
+ int i;
+ ssize_t n;
+ char buf[20];
+ struct timespec ts;
+
+ // The descriptor will be initialized in a thread, so we have to
+ // give a chance to get opened.
+ for (i = 0; i < 200; i++) {
+ n = read(fd, buf, sizeof buf);
+ if (n >= 0)
+ break;
+ if (errno != EBADF && errno != EINVAL) {
+ fprintf(stderr, "BUG: read: %s\n", strerror(errno));
+ return 2;
+ }
+
+ // An EBADF error means that the shared library has not opened the
+ // descriptor yet.
+ ts.tv_sec = 0;
+ ts.tv_nsec = 10000000;
+ nanosleep(&ts, NULL);
+ }
+
+ if (n < 0) {
+ fprintf(stderr, "BUG: failed to read any data from pipe\n");
+ return 2;
+ }
+
+ if (n == 0) {
+ fprintf(stderr, "BUG: unexpected EOF\n");
+ return 2;
+ }
+
+ if (n == sizeof buf) {
+ n--;
+ }
+ buf[n] = '\0';
+ printf("%s\n", buf);
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/main3.c b/src/cmd/cgo/internal/testcshared/testdata/main3.c
new file mode 100644
index 0000000..49cc055
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/main3.c
@@ -0,0 +1,29 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <stdint.h>
+#include <stdio.h>
+#include <dlfcn.h>
+
+// Tests "main.main" is exported on android/arm,
+// which golang.org/x/mobile/app depends on.
+int main(int argc, char** argv) {
+ void* handle = dlopen(argv[1], RTLD_LAZY | RTLD_GLOBAL);
+ if (!handle) {
+ fprintf(stderr, "ERROR: failed to open the shared library: %s\n",
+ dlerror());
+ return 2;
+ }
+
+ uintptr_t main_fn = (uintptr_t)dlsym(handle, "main.main");
+ if (!main_fn) {
+ fprintf(stderr, "ERROR: missing main.main: %s\n", dlerror());
+ return 2;
+ }
+
+ // TODO(hyangah): check that main.main can run.
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/main4.c b/src/cmd/cgo/internal/testcshared/testdata/main4.c
new file mode 100644
index 0000000..6c16364
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/main4.c
@@ -0,0 +1,215 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that a signal handler that uses up stack space does not crash
+// if the signal is delivered to a thread running a goroutine.
+// This is a lot like ../testcarchive/main2.c.
+
+#include <setjmp.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sched.h>
+#include <time.h>
+#include <dlfcn.h>
+
+static void die(const char* msg) {
+ perror(msg);
+ exit(EXIT_FAILURE);
+}
+
+static volatile sig_atomic_t sigioSeen;
+
+// Use up some stack space.
+static void recur(int i, char *p) {
+ char a[1024];
+
+ *p = '\0';
+ if (i > 0) {
+ recur(i - 1, a);
+ }
+}
+
+// Signal handler that uses up more stack space than a goroutine will have.
+static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
+ char a[1024];
+
+ recur(4, a);
+ sigioSeen = 1;
+}
+
+static jmp_buf jmp;
+static char* nullPointer;
+
+// Signal handler for SIGSEGV on a C thread.
+static void segvHandler(int signo, siginfo_t* info, void* ctxt) {
+ sigset_t mask;
+ int i;
+
+ if (sigemptyset(&mask) < 0) {
+ die("sigemptyset");
+ }
+ if (sigaddset(&mask, SIGSEGV) < 0) {
+ die("sigaddset");
+ }
+ i = sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ if (i != 0) {
+ fprintf(stderr, "sigprocmask: %s\n", strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ // Don't try this at home.
+ longjmp(jmp, signo);
+
+ // We should never get here.
+ abort();
+}
+
+int main(int argc, char** argv) {
+ int verbose;
+ struct sigaction sa;
+ void* handle;
+ void (*fn)(void);
+ sigset_t mask;
+ int i;
+ struct timespec ts;
+
+ verbose = argc > 2;
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ // Call setsid so that we can use kill(0, SIGIO) below.
+ // Don't check the return value so that this works both from
+ // a job control shell and from a shell script.
+ setsid();
+
+ if (verbose) {
+ printf("calling sigaction\n");
+ }
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_sigaction = ioHandler;
+ if (sigemptyset(&sa.sa_mask) < 0) {
+ die("sigemptyset");
+ }
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGIO, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+
+ sa.sa_sigaction = segvHandler;
+ if (sigaction(SIGSEGV, &sa, NULL) < 0 || sigaction(SIGBUS, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+
+ if (verbose) {
+ printf("calling dlopen\n");
+ }
+
+ handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
+ if (handle == NULL) {
+ fprintf(stderr, "%s\n", dlerror());
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling dlsym\n");
+ }
+
+ // Start some goroutines.
+ fn = (void(*)(void))dlsym(handle, "RunGoroutines");
+ if (fn == NULL) {
+ fprintf(stderr, "%s\n", dlerror());
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling RunGoroutines\n");
+ }
+
+ fn();
+
+ // Block SIGIO in this thread to make it more likely that it
+ // will be delivered to a goroutine.
+
+ if (verbose) {
+ printf("calling pthread_sigmask\n");
+ }
+
+ if (sigemptyset(&mask) < 0) {
+ die("sigemptyset");
+ }
+ if (sigaddset(&mask, SIGIO) < 0) {
+ die("sigaddset");
+ }
+ i = pthread_sigmask(SIG_BLOCK, &mask, NULL);
+ if (i != 0) {
+ fprintf(stderr, "pthread_sigmask: %s\n", strerror(i));
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling kill\n");
+ }
+
+ if (kill(0, SIGIO) < 0) {
+ die("kill");
+ }
+
+ if (verbose) {
+ printf("waiting for sigioSeen\n");
+ }
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (!sigioSeen) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for signal\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (verbose) {
+ printf("calling setjmp\n");
+ }
+
+ // Test that a SIGSEGV on this thread is delivered to us.
+ if (setjmp(jmp) == 0) {
+ if (verbose) {
+ printf("triggering SIGSEGV\n");
+ }
+
+ *nullPointer = '\0';
+
+ fprintf(stderr, "continued after address error\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling dlsym\n");
+ }
+
+ // Make sure that a SIGSEGV in Go causes a run-time panic.
+ fn = (void (*)(void))dlsym(handle, "TestSEGV");
+ if (fn == NULL) {
+ fprintf(stderr, "%s\n", dlerror());
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling TestSEGV\n");
+ }
+
+ fn();
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/main5.c b/src/cmd/cgo/internal/testcshared/testdata/main5.c
new file mode 100644
index 0000000..e7bebab
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/main5.c
@@ -0,0 +1,199 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that a signal handler works in non-Go code when using
+// os/signal.Notify.
+// This is a lot like ../testcarchive/main3.c.
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sched.h>
+#include <dlfcn.h>
+
+static void die(const char* msg) {
+ perror(msg);
+ exit(EXIT_FAILURE);
+}
+
+static volatile sig_atomic_t sigioSeen;
+
+static void ioHandler(int signo, siginfo_t* info, void* ctxt) {
+ sigioSeen = 1;
+}
+
+int main(int argc, char** argv) {
+ int verbose;
+ struct sigaction sa;
+ void* handle;
+ void (*fn1)(void);
+ int (*sawSIGIO)(void);
+ int i;
+ struct timespec ts;
+
+ verbose = argc > 2;
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ if (verbose) {
+ printf("calling sigaction\n");
+ }
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_sigaction = ioHandler;
+ if (sigemptyset(&sa.sa_mask) < 0) {
+ die("sigemptyset");
+ }
+ sa.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGIO, &sa, NULL) < 0) {
+ die("sigaction");
+ }
+
+ if (verbose) {
+ printf("calling dlopen\n");
+ }
+
+ handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
+ if (handle == NULL) {
+ fprintf(stderr, "%s\n", dlerror());
+ exit(EXIT_FAILURE);
+ }
+
+ // At this point there should not be a Go signal handler
+ // installed for SIGIO.
+
+ if (verbose) {
+ printf("raising SIGIO\n");
+ }
+
+ if (raise(SIGIO) < 0) {
+ die("raise");
+ }
+
+ if (verbose) {
+ printf("waiting for sigioSeen\n");
+ }
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (!sigioSeen) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for signal\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ sigioSeen = 0;
+
+ // Tell the Go code to catch SIGIO.
+
+ if (verbose) {
+ printf("calling dlsym\n");
+ }
+
+ fn1 = (void(*)(void))dlsym(handle, "CatchSIGIO");
+ if (fn1 == NULL) {
+ fprintf(stderr, "%s\n", dlerror());
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling CatchSIGIO\n");
+ }
+
+ fn1();
+
+ if (verbose) {
+ printf("raising SIGIO\n");
+ }
+
+ if (raise(SIGIO) < 0) {
+ die("raise");
+ }
+
+ if (verbose) {
+ printf("calling dlsym\n");
+ }
+
+ // Check that the Go code saw SIGIO.
+ sawSIGIO = (int (*)(void))dlsym(handle, "SawSIGIO");
+ if (sawSIGIO == NULL) {
+ fprintf(stderr, "%s\n", dlerror());
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling SawSIGIO\n");
+ }
+
+ if (!sawSIGIO()) {
+ fprintf(stderr, "Go handler did not see SIGIO\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (sigioSeen != 0) {
+ fprintf(stderr, "C handler saw SIGIO when only Go handler should have\n");
+ exit(EXIT_FAILURE);
+ }
+
+ // Tell the Go code to stop catching SIGIO.
+
+ if (verbose) {
+ printf("calling dlsym\n");
+ }
+
+ fn1 = (void(*)(void))dlsym(handle, "ResetSIGIO");
+ if (fn1 == NULL) {
+ fprintf(stderr, "%s\n", dlerror());
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("calling ResetSIGIO\n");
+ }
+
+ fn1();
+
+ if (verbose) {
+ printf("raising SIGIO\n");
+ }
+
+ if (raise(SIGIO) < 0) {
+ die("raise");
+ }
+
+ if (verbose) {
+ printf("calling SawSIGIO\n");
+ }
+
+ if (sawSIGIO()) {
+ fprintf(stderr, "Go handler saw SIGIO after Reset\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (verbose) {
+ printf("waiting for sigioSeen\n");
+ }
+
+ // Wait until the signal has been delivered.
+ i = 0;
+ while (!sigioSeen) {
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ nanosleep(&ts, NULL);
+ i++;
+ if (i > 5000) {
+ fprintf(stderr, "looping too long waiting for signal\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ printf("PASS\n");
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testcshared/testdata/p/p.go b/src/cmd/cgo/internal/testcshared/testdata/p/p.go
new file mode 100644
index 0000000..0f02cf3
--- /dev/null
+++ b/src/cmd/cgo/internal/testcshared/testdata/p/p.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import "C"
+
+//export FromPkg
+func FromPkg() int32 { return 1024 }
+
+//export Divu
+func Divu(a, b uint32) uint32 { return a / b }
diff --git a/src/cmd/cgo/internal/testerrors/argposition_test.go b/src/cmd/cgo/internal/testerrors/argposition_test.go
new file mode 100644
index 0000000..0876dc4
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/argposition_test.go
@@ -0,0 +1,137 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 42580: cmd/cgo: shifting identifier position in ast
+
+package errorstest
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+type ShortPosition struct {
+ Line int
+ Column int
+ Visited bool
+}
+
+type IdentPositionInfo map[string][]ShortPosition
+
+type Visitor struct {
+ identPosInfo IdentPositionInfo
+ fset *token.FileSet
+ t *testing.T
+}
+
+func (v *Visitor) Visit(node ast.Node) ast.Visitor {
+ if ident, ok := node.(*ast.Ident); ok {
+ if expectedPositions, ok := v.identPosInfo[ident.Name]; ok {
+ gotMatch := false
+ var errorMessage strings.Builder
+ for caseIndex, expectedPos := range expectedPositions {
+ actualPosition := v.fset.PositionFor(ident.Pos(), true)
+ errorOccured := false
+ if expectedPos.Line != actualPosition.Line {
+ fmt.Fprintf(&errorMessage, "wrong line number for ident %s: expected: %d got: %d\n", ident.Name, expectedPos.Line, actualPosition.Line)
+ errorOccured = true
+ }
+ if expectedPos.Column != actualPosition.Column {
+ fmt.Fprintf(&errorMessage, "wrong column number for ident %s: expected: %d got: %d\n", ident.Name, expectedPos.Column, actualPosition.Column)
+ errorOccured = true
+ }
+ if errorOccured {
+ continue
+ }
+ gotMatch = true
+ expectedPositions[caseIndex].Visited = true
+ }
+
+ if !gotMatch {
+ v.t.Errorf(errorMessage.String())
+ }
+ }
+ }
+ return v
+}
+
+func TestArgumentsPositions(t *testing.T) {
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveExec(t)
+
+ testdata, err := filepath.Abs("testdata")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ tmpPath := t.TempDir()
+
+ dir := filepath.Join(tmpPath, "src", "testpositions")
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ t.Fatal(err)
+ }
+
+ cmd := exec.Command("go", "tool", "cgo",
+ "-srcdir", testdata,
+ "-objdir", dir,
+ "issue42580.go")
+ cmd.Stderr = new(bytes.Buffer)
+
+ err = cmd.Run()
+ if err != nil {
+ t.Fatalf("%s: %v\n%s", cmd, err, cmd.Stderr)
+ }
+ mainProcessed, err := os.ReadFile(filepath.Join(dir, "issue42580.cgo1.go"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", mainProcessed, parser.AllErrors)
+ if err != nil {
+ fmt.Println(err)
+ return
+ }
+
+ expectation := IdentPositionInfo{
+ "checkedPointer": []ShortPosition{
+ ShortPosition{
+ Line: 32,
+ Column: 56,
+ },
+ },
+ "singleInnerPointerChecked": []ShortPosition{
+ ShortPosition{
+ Line: 37,
+ Column: 91,
+ },
+ },
+ "doublePointerChecked": []ShortPosition{
+ ShortPosition{
+ Line: 42,
+ Column: 91,
+ },
+ },
+ }
+ for _, decl := range f.Decls {
+ if fdecl, ok := decl.(*ast.FuncDecl); ok {
+ ast.Walk(&Visitor{expectation, fset, t}, fdecl.Body)
+ }
+ }
+ for ident, positions := range expectation {
+ for _, position := range positions {
+ if !position.Visited {
+ t.Errorf("Position %d:%d missed for %s ident", position.Line, position.Column, ident)
+ }
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/testerrors/badsym_test.go b/src/cmd/cgo/internal/testerrors/badsym_test.go
new file mode 100644
index 0000000..6c87977
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/badsym_test.go
@@ -0,0 +1,231 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package errorstest
+
+import (
+ "bytes"
+ "cmd/internal/quoted"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+ "unicode"
+)
+
+// A manually modified object file could pass unexpected characters
+// into the files generated by cgo.
+
+const magicInput = "abcdefghijklmnopqrstuvwxyz0123"
+const magicReplace = "\n//go:cgo_ldflag \"-badflag\"\n//"
+
+const cSymbol = "BadSymbol" + magicInput + "Name"
+const cDefSource = "int " + cSymbol + " = 1;"
+const cRefSource = "extern int " + cSymbol + "; int F() { return " + cSymbol + "; }"
+
+// goSource is the source code for the trivial Go file we use.
+// We will replace TMPDIR with the temporary directory name.
+const goSource = `
+package main
+
+// #cgo LDFLAGS: TMPDIR/cbad.o TMPDIR/cbad.so
+// extern int F();
+import "C"
+
+func main() {
+ println(C.F())
+}
+`
+
+func TestBadSymbol(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ dir := t.TempDir()
+
+ mkdir := func(base string) string {
+ ret := filepath.Join(dir, base)
+ if err := os.Mkdir(ret, 0755); err != nil {
+ t.Fatal(err)
+ }
+ return ret
+ }
+
+ cdir := mkdir("c")
+ godir := mkdir("go")
+
+ makeFile := func(mdir, base, source string) string {
+ ret := filepath.Join(mdir, base)
+ if err := os.WriteFile(ret, []byte(source), 0644); err != nil {
+ t.Fatal(err)
+ }
+ return ret
+ }
+
+ cDefFile := makeFile(cdir, "cdef.c", cDefSource)
+ cRefFile := makeFile(cdir, "cref.c", cRefSource)
+
+ ccCmd := cCompilerCmd(t)
+
+ cCompile := func(arg, base, src string) string {
+ out := filepath.Join(cdir, base)
+ run := append(ccCmd, arg, "-o", out, src)
+ output, err := exec.Command(run[0], run[1:]...).CombinedOutput()
+ if err != nil {
+ t.Log(run)
+ t.Logf("%s", output)
+ t.Fatal(err)
+ }
+ if err := os.Remove(src); err != nil {
+ t.Fatal(err)
+ }
+ return out
+ }
+
+ // Build a shared library that defines a symbol whose name
+ // contains magicInput.
+
+ cShared := cCompile("-shared", "c.so", cDefFile)
+
+ // Build an object file that refers to the symbol whose name
+ // contains magicInput.
+
+ cObj := cCompile("-c", "c.o", cRefFile)
+
+ // Rewrite the shared library and the object file, replacing
+ // magicInput with magicReplace. This will have the effect of
+ // introducing a symbol whose name looks like a cgo command.
+ // The cgo tool will use that name when it generates the
+ // _cgo_import.go file, thus smuggling a magic //go:cgo_ldflag
+ // pragma into a Go file. We used to not check the pragmas in
+ // _cgo_import.go.
+
+ rewrite := func(from, to string) {
+ obj, err := os.ReadFile(from)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if bytes.Count(obj, []byte(magicInput)) == 0 {
+ t.Fatalf("%s: did not find magic string", from)
+ }
+
+ if len(magicInput) != len(magicReplace) {
+ t.Fatalf("internal test error: different magic lengths: %d != %d", len(magicInput), len(magicReplace))
+ }
+
+ obj = bytes.ReplaceAll(obj, []byte(magicInput), []byte(magicReplace))
+
+ if err := os.WriteFile(to, obj, 0644); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ cBadShared := filepath.Join(godir, "cbad.so")
+ rewrite(cShared, cBadShared)
+
+ cBadObj := filepath.Join(godir, "cbad.o")
+ rewrite(cObj, cBadObj)
+
+ goSourceBadObject := strings.ReplaceAll(goSource, "TMPDIR", godir)
+ makeFile(godir, "go.go", goSourceBadObject)
+
+ makeFile(godir, "go.mod", "module badsym")
+
+ // Try to build our little package.
+ cmd := exec.Command("go", "build", "-ldflags=-v")
+ cmd.Dir = godir
+ output, err := cmd.CombinedOutput()
+
+ // The build should fail, but we want it to fail because we
+ // detected the error, not because we passed a bad flag to the
+ // C linker.
+
+ if err == nil {
+ t.Errorf("go build succeeded unexpectedly")
+ }
+
+ t.Logf("%s", output)
+
+ for _, line := range bytes.Split(output, []byte("\n")) {
+ if bytes.Contains(line, []byte("dynamic symbol")) && bytes.Contains(line, []byte("contains unsupported character")) {
+ // This is the error from cgo.
+ continue
+ }
+
+ // We passed -ldflags=-v to see the external linker invocation,
+ // which should not include -badflag.
+ if bytes.Contains(line, []byte("-badflag")) {
+ t.Error("output should not mention -badflag")
+ }
+
+ // Also check for compiler errors, just in case.
+ // GCC says "unrecognized command line option".
+ // clang says "unknown argument".
+ if bytes.Contains(line, []byte("unrecognized")) || bytes.Contains(output, []byte("unknown")) {
+ t.Error("problem should have been caught before invoking C linker")
+ }
+ }
+}
+
+func cCompilerCmd(t *testing.T) []string {
+ cc, err := quoted.Split(goEnv(t, "CC"))
+ if err != nil {
+ t.Skipf("parsing go env CC: %s", err)
+ }
+ if len(cc) == 0 {
+ t.Skipf("no C compiler")
+ }
+ testenv.MustHaveExecPath(t, cc[0])
+
+ out := goEnv(t, "GOGCCFLAGS")
+ quote := '\000'
+ start := 0
+ lastSpace := true
+ backslash := false
+ s := string(out)
+ for i, c := range s {
+ if quote == '\000' && unicode.IsSpace(c) {
+ if !lastSpace {
+ cc = append(cc, s[start:i])
+ lastSpace = true
+ }
+ } else {
+ if lastSpace {
+ start = i
+ lastSpace = false
+ }
+ if quote == '\000' && !backslash && (c == '"' || c == '\'') {
+ quote = c
+ backslash = false
+ } else if !backslash && quote == c {
+ quote = '\000'
+ } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
+ backslash = true
+ } else {
+ backslash = false
+ }
+ }
+ }
+ if !lastSpace {
+ cc = append(cc, s[start:])
+ }
+
+ // Force reallocation (and avoid aliasing bugs) for tests that append to cc.
+ cc = cc[:len(cc):len(cc)]
+
+ return cc
+}
+
+func goEnv(t *testing.T, key string) string {
+ out, err := exec.Command("go", "env", key).CombinedOutput()
+ if err != nil {
+ t.Logf("go env %s\n", key)
+ t.Logf("%s", out)
+ t.Fatal(err)
+ }
+ return strings.TrimSpace(string(out))
+}
diff --git a/src/cmd/cgo/internal/testerrors/errors_test.go b/src/cmd/cgo/internal/testerrors/errors_test.go
new file mode 100644
index 0000000..870d05b
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/errors_test.go
@@ -0,0 +1,168 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package errorstest
+
+import (
+ "bytes"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func path(file string) string {
+ return filepath.Join("testdata", file)
+}
+
+func check(t *testing.T, file string) {
+ t.Run(file, func(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+
+ contents, err := os.ReadFile(path(file))
+ if err != nil {
+ t.Fatal(err)
+ }
+ var errors []*regexp.Regexp
+ for i, line := range bytes.Split(contents, []byte("\n")) {
+ if bytes.HasSuffix(line, []byte("ERROR HERE")) {
+ re := regexp.MustCompile(regexp.QuoteMeta(fmt.Sprintf("%s:%d:", file, i+1)))
+ errors = append(errors, re)
+ continue
+ }
+
+ _, frag, ok := bytes.Cut(line, []byte("ERROR HERE: "))
+ if !ok {
+ continue
+ }
+ re, err := regexp.Compile(fmt.Sprintf(":%d:.*%s", i+1, frag))
+ if err != nil {
+ t.Errorf("Invalid regexp after `ERROR HERE: `: %#q", frag)
+ continue
+ }
+ errors = append(errors, re)
+ }
+ if len(errors) == 0 {
+ t.Fatalf("cannot find ERROR HERE")
+ }
+ expect(t, file, errors)
+ })
+}
+
+func expect(t *testing.T, file string, errors []*regexp.Regexp) {
+ dir, err := os.MkdirTemp("", filepath.Base(t.Name()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(dir)
+
+ dst := filepath.Join(dir, strings.TrimSuffix(file, ".go"))
+ cmd := exec.Command("go", "build", "-gcflags=-L -e", "-o="+dst, path(file)) // TODO(gri) no need for -gcflags=-L if go tool is adjusted
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Errorf("expected cgo to fail but it succeeded")
+ }
+
+ lines := bytes.Split(out, []byte("\n"))
+ for _, re := range errors {
+ found := false
+ for _, line := range lines {
+ if re.Match(line) {
+ t.Logf("found match for %#q: %q", re, line)
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("expected error output to contain %#q", re)
+ }
+ }
+
+ if t.Failed() {
+ t.Logf("actual output:\n%s", out)
+ }
+}
+
+func sizeofLongDouble(t *testing.T) int {
+ testenv.MustHaveGoRun(t)
+ testenv.MustHaveCGO(t)
+ cmd := exec.Command("go", "run", path("long_double_size.go"))
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+
+ i, err := strconv.Atoi(strings.TrimSpace(string(out)))
+ if err != nil {
+ t.Fatalf("long_double_size.go printed invalid size: %s", out)
+ }
+ return i
+}
+
+func TestReportsTypeErrors(t *testing.T) {
+ for _, file := range []string{
+ "err1.go",
+ "err2.go",
+ "err5.go",
+ "issue11097a.go",
+ "issue11097b.go",
+ "issue18452.go",
+ "issue18889.go",
+ "issue28721.go",
+ "issue33061.go",
+ "issue50710.go",
+ } {
+ check(t, file)
+ }
+
+ if sizeofLongDouble(t) > 8 {
+ for _, file := range []string{
+ "err4.go",
+ "issue28069.go",
+ } {
+ check(t, file)
+ }
+ }
+}
+
+func TestToleratesOptimizationFlag(t *testing.T) {
+ for _, cflags := range []string{
+ "",
+ "-O",
+ } {
+ cflags := cflags
+ t.Run(cflags, func(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ t.Parallel()
+
+ cmd := exec.Command("go", "build", path("issue14669.go"))
+ cmd.Env = append(os.Environ(), "CGO_CFLAGS="+cflags)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("%#q: %v:\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+ })
+ }
+}
+
+func TestMallocCrashesOnNil(t *testing.T) {
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveGoRun(t)
+ t.Parallel()
+
+ cmd := exec.Command("go", "run", path("malloc.go"))
+ out, err := cmd.CombinedOutput()
+ if err == nil {
+ t.Logf("%#q:\n%s", strings.Join(cmd.Args, " "), out)
+ t.Fatalf("succeeded unexpectedly")
+ }
+}
diff --git a/src/cmd/cgo/internal/testerrors/ptr_test.go b/src/cmd/cgo/internal/testerrors/ptr_test.go
new file mode 100644
index 0000000..7f56501
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/ptr_test.go
@@ -0,0 +1,706 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Tests that cgo detects invalid pointer passing at runtime.
+
+package errorstest
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "slices"
+ "strings"
+ "sync/atomic"
+ "testing"
+)
+
+var tmp = flag.String("tmp", "", "use `dir` for temporary files and do not clean up")
+
+// ptrTest is the tests without the boilerplate.
+type ptrTest struct {
+ name string // for reporting
+ c string // the cgo comment
+ c1 string // cgo comment forced into non-export cgo file
+ imports []string // a list of imports
+ support string // supporting functions
+ body string // the body of the main function
+ extra []extra // extra files
+ fail bool // whether the test should fail
+ expensive bool // whether the test requires the expensive check
+}
+
+type extra struct {
+ name string
+ contents string
+}
+
+var ptrTests = []ptrTest{
+ {
+ // Passing a pointer to a struct that contains a Go pointer.
+ name: "ptr1",
+ c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`,
+ body: `C.f1(&C.s1{new(C.int)})`,
+ fail: true,
+ },
+ {
+ // Passing a pointer to a struct that contains a Go pointer.
+ name: "ptr2",
+ c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`,
+ body: `p := &C.s2{new(C.int)}; C.f2(p)`,
+ fail: true,
+ },
+ {
+ // Passing a pointer to an int field of a Go struct
+ // that (irrelevantly) contains a Go pointer.
+ name: "ok1",
+ c: `struct s3 { int i; int *p; }; void f3(int *p) {}`,
+ body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`,
+ fail: false,
+ },
+ {
+ // Passing a pointer to a pointer field of a Go struct.
+ name: "ptrfield",
+ c: `struct s4 { int i; int *p; }; void f4(int **p) {}`,
+ body: `p := &C.struct_s4{i: 0, p: new(C.int)}; C.f4(&p.p)`,
+ fail: true,
+ },
+ {
+ // Passing a pointer to a pointer field of a Go
+ // struct, where the field does not contain a Go
+ // pointer, but another field (irrelevantly) does.
+ name: "ptrfieldok",
+ c: `struct s5 { int *p1; int *p2; }; void f5(int **p) {}`,
+ body: `p := &C.struct_s5{p1: nil, p2: new(C.int)}; C.f5(&p.p1)`,
+ fail: false,
+ },
+ {
+ // Passing the address of a slice with no Go pointers.
+ name: "sliceok1",
+ c: `void f6(void **p) {}`,
+ imports: []string{"unsafe"},
+ body: `s := []unsafe.Pointer{nil}; C.f6(&s[0])`,
+ fail: false,
+ },
+ {
+ // Passing the address of a slice with a Go pointer.
+ name: "sliceptr1",
+ c: `void f7(void **p) {}`,
+ imports: []string{"unsafe"},
+ body: `i := 0; s := []unsafe.Pointer{unsafe.Pointer(&i)}; C.f7(&s[0])`,
+ fail: true,
+ },
+ {
+ // Passing the address of a slice with a Go pointer,
+ // where we are passing the address of an element that
+ // is not a Go pointer.
+ name: "sliceptr2",
+ c: `void f8(void **p) {}`,
+ imports: []string{"unsafe"},
+ body: `i := 0; s := []unsafe.Pointer{nil, unsafe.Pointer(&i)}; C.f8(&s[0])`,
+ fail: true,
+ },
+ {
+ // Passing the address of a slice that is an element
+ // in a struct only looks at the slice.
+ name: "sliceok2",
+ c: `void f9(void **p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S9 struct { p *int; s []unsafe.Pointer }`,
+ body: `i := 0; p := &S9{p:&i, s:[]unsafe.Pointer{nil}}; C.f9(&p.s[0])`,
+ fail: false,
+ },
+ {
+ // Passing the address of a slice of an array that is
+ // an element in a struct, with a type conversion.
+ name: "sliceok3",
+ c: `void f10(void* p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S10 struct { p *int; a [4]byte }`,
+ body: `i := 0; p := &S10{p:&i}; s := p.a[:]; C.f10(unsafe.Pointer(&s[0]))`,
+ fail: false,
+ },
+ {
+ // Passing the address of a slice of an array that is
+ // an element in a struct, with a type conversion.
+ name: "sliceok4",
+ c: `typedef void* PV11; void f11(PV11 p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S11 struct { p *int; a [4]byte }`,
+ body: `i := 0; p := &S11{p:&i}; C.f11(C.PV11(unsafe.Pointer(&p.a[0])))`,
+ fail: false,
+ },
+ {
+ // Passing the address of a static variable with no
+ // pointers doesn't matter.
+ name: "varok",
+ c: `void f12(char** parg) {}`,
+ support: `var hello12 = [...]C.char{'h', 'e', 'l', 'l', 'o'}`,
+ body: `parg := [1]*C.char{&hello12[0]}; C.f12(&parg[0])`,
+ fail: false,
+ },
+ {
+ // Passing the address of a static variable with
+ // pointers does matter.
+ name: "var1",
+ c: `void f13(char*** parg) {}`,
+ support: `var hello13 = [...]*C.char{new(C.char)}`,
+ body: `parg := [1]**C.char{&hello13[0]}; C.f13(&parg[0])`,
+ fail: true,
+ },
+ {
+ // Storing a Go pointer into C memory should fail.
+ name: "barrier",
+ c: `#include <stdlib.h>
+ char **f14a() { return malloc(sizeof(char*)); }
+ void f14b(char **p) {}`,
+ body: `p := C.f14a(); *p = new(C.char); C.f14b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Storing a pinned Go pointer into C memory should succeed.
+ name: "barrierpinnedok",
+ c: `#include <stdlib.h>
+ char **f14a2() { return malloc(sizeof(char*)); }
+ void f14b2(char **p) {}`,
+ imports: []string{"runtime"},
+ body: `var pinr runtime.Pinner; p := C.f14a2(); x := new(C.char); pinr.Pin(x); *p = x; C.f14b2(p); pinr.Unpin()`,
+ fail: false,
+ expensive: true,
+ },
+ {
+ // Storing a Go pointer into C memory by assigning a
+ // large value should fail.
+ name: "barrierstruct",
+ c: `#include <stdlib.h>
+ struct s15 { char *a[10]; };
+ struct s15 *f15() { return malloc(sizeof(struct s15)); }
+ void f15b(struct s15 *p) {}`,
+ body: `p := C.f15(); p.a = [10]*C.char{new(C.char)}; C.f15b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Storing a Go pointer into C memory using a slice
+ // copy should fail.
+ name: "barrierslice",
+ c: `#include <stdlib.h>
+ struct s16 { char *a[10]; };
+ struct s16 *f16() { return malloc(sizeof(struct s16)); }
+ void f16b(struct s16 *p) {}`,
+ body: `p := C.f16(); copy(p.a[:], []*C.char{new(C.char)}); C.f16b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // A very large value uses a GC program, which is a
+ // different code path.
+ name: "barriergcprogarray",
+ c: `#include <stdlib.h>
+ struct s17 { char *a[32769]; };
+ struct s17 *f17() { return malloc(sizeof(struct s17)); }
+ void f17b(struct s17 *p) {}`,
+ body: `p := C.f17(); p.a = [32769]*C.char{new(C.char)}; C.f17b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Similar case, with a source on the heap.
+ name: "barriergcprogarrayheap",
+ c: `#include <stdlib.h>
+ struct s18 { char *a[32769]; };
+ struct s18 *f18() { return malloc(sizeof(struct s18)); }
+ void f18b(struct s18 *p) {}
+ void f18c(void *p) {}`,
+ imports: []string{"unsafe"},
+ body: `p := C.f18(); n := &[32769]*C.char{new(C.char)}; p.a = *n; C.f18b(p); n[0] = nil; C.f18c(unsafe.Pointer(n))`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // A GC program with a struct.
+ name: "barriergcprogstruct",
+ c: `#include <stdlib.h>
+ struct s19a { char *a[32769]; };
+ struct s19b { struct s19a f; };
+ struct s19b *f19() { return malloc(sizeof(struct s19b)); }
+ void f19b(struct s19b *p) {}`,
+ body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Similar case, with a source on the heap.
+ name: "barriergcprogstructheap",
+ c: `#include <stdlib.h>
+ struct s20a { char *a[32769]; };
+ struct s20b { struct s20a f; };
+ struct s20b *f20() { return malloc(sizeof(struct s20b)); }
+ void f20b(struct s20b *p) {}
+ void f20c(void *p) {}`,
+ imports: []string{"unsafe"},
+ body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`,
+ fail: true,
+ expensive: true,
+ },
+ {
+ // Exported functions may not return Go pointers.
+ name: "export1",
+ c: `extern unsigned char *GoFn21();`,
+ support: `//export GoFn21
+ func GoFn21() *byte { return new(byte) }`,
+ body: `C.GoFn21()`,
+ fail: true,
+ },
+ {
+ // Returning a C pointer is fine.
+ name: "exportok",
+ c: `#include <stdlib.h>
+ extern unsigned char *GoFn22();`,
+ support: `//export GoFn22
+ func GoFn22() *byte { return (*byte)(C.malloc(1)) }`,
+ body: `C.GoFn22()`,
+ },
+ {
+ // Passing a Go string is fine.
+ name: "passstring",
+ c: `#include <stddef.h>
+ typedef struct { const char *p; ptrdiff_t n; } gostring23;
+ gostring23 f23(gostring23 s) { return s; }`,
+ imports: []string{"unsafe"},
+ body: `s := "a"; r := C.f23(*(*C.gostring23)(unsafe.Pointer(&s))); if *(*string)(unsafe.Pointer(&r)) != s { panic(r) }`,
+ },
+ {
+ // Passing a slice of Go strings fails.
+ name: "passstringslice",
+ c: `void f24(void *p) {}`,
+ imports: []string{"strings", "unsafe"},
+ support: `type S24 struct { a [1]string }`,
+ body: `s := S24{a:[1]string{strings.Repeat("a", 2)}}; C.f24(unsafe.Pointer(&s.a[0]))`,
+ fail: true,
+ },
+ {
+ // Exported functions may not return strings.
+ name: "retstring",
+ c: `extern void f25();`,
+ imports: []string{"strings"},
+ support: `//export GoStr25
+ func GoStr25() string { return strings.Repeat("a", 2) }`,
+ body: `C.f25()`,
+ c1: `#include <stddef.h>
+ typedef struct { const char *p; ptrdiff_t n; } gostring25;
+ extern gostring25 GoStr25();
+ void f25() { GoStr25(); }`,
+ fail: true,
+ },
+ {
+ // Don't check non-pointer data.
+ // Uses unsafe code to get a pointer we shouldn't check.
+ // Although we use unsafe, the uintptr represents an integer
+ // that happens to have the same representation as a pointer;
+ // that is, we are testing something that is not unsafe.
+ name: "ptrdata1",
+ c: `#include <stdlib.h>
+ void f26(void* p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S26 struct { p *int; a [8*8]byte; u uintptr }`,
+ body: `i := 0; p := &S26{u:uintptr(unsafe.Pointer(&i))}; q := (*S26)(C.malloc(C.size_t(unsafe.Sizeof(*p)))); *q = *p; C.f26(unsafe.Pointer(q))`,
+ fail: false,
+ },
+ {
+ // Like ptrdata1, but with a type that uses a GC program.
+ name: "ptrdata2",
+ c: `#include <stdlib.h>
+ void f27(void* p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S27 struct { p *int; a [32769*8]byte; q *int; u uintptr }`,
+ body: `i := 0; p := S27{u:uintptr(unsafe.Pointer(&i))}; q := (*S27)(C.malloc(C.size_t(unsafe.Sizeof(p)))); *q = p; C.f27(unsafe.Pointer(q))`,
+ fail: false,
+ },
+ {
+ // Check deferred pointers when they are used, not
+ // when the defer statement is run.
+ name: "defer1",
+ c: `typedef struct s28 { int *p; } s28; void f28(s28 *ps) {}`,
+ body: `p := &C.s28{}; defer C.f28(p); p.p = new(C.int)`,
+ fail: true,
+ },
+ {
+ // Check a pointer to a union if the union has any
+ // pointer fields.
+ name: "union1",
+ c: `typedef union { char **p; unsigned long i; } u29; void f29(u29 *pu) {}`,
+ imports: []string{"unsafe"},
+ body: `var b C.char; p := &b; C.f29((*C.u29)(unsafe.Pointer(&p)))`,
+ fail: true,
+ },
+ {
+ // Don't check a pointer to a union if the union does
+ // not have any pointer fields.
+ // Like ptrdata1 above, the uintptr represents an
+ // integer that happens to have the same
+ // representation as a pointer.
+ name: "union2",
+ c: `typedef union { unsigned long i; } u39; void f39(u39 *pu) {}`,
+ imports: []string{"unsafe"},
+ body: `var b C.char; p := &b; C.f39((*C.u39)(unsafe.Pointer(&p)))`,
+ fail: false,
+ },
+ {
+ // Test preemption while entering a cgo call. Issue #21306.
+ name: "preemptduringcall",
+ c: `void f30() {}`,
+ imports: []string{"runtime", "sync"},
+ body: `var wg sync.WaitGroup; wg.Add(100); for i := 0; i < 100; i++ { go func(i int) { for j := 0; j < 100; j++ { C.f30(); runtime.GOMAXPROCS(i) }; wg.Done() }(i) }; wg.Wait()`,
+ fail: false,
+ },
+ {
+ // Test poller deadline with cgocheck=2. Issue #23435.
+ name: "deadline",
+ c: `#define US31 10`,
+ imports: []string{"os", "time"},
+ body: `r, _, _ := os.Pipe(); r.SetDeadline(time.Now().Add(C.US31 * time.Microsecond))`,
+ fail: false,
+ },
+ {
+ // Test for double evaluation of channel receive.
+ name: "chanrecv",
+ c: `void f32(char** p) {}`,
+ imports: []string{"time"},
+ body: `c := make(chan []*C.char, 2); c <- make([]*C.char, 1); go func() { time.Sleep(10 * time.Second); panic("received twice from chan") }(); C.f32(&(<-c)[0]);`,
+ fail: false,
+ },
+ {
+ // Test that converting the address of a struct field
+ // to unsafe.Pointer still just checks that field.
+ // Issue #25941.
+ name: "structfield",
+ c: `void f33(void* p) {}`,
+ imports: []string{"unsafe"},
+ support: `type S33 struct { p *int; a [8]byte; u uintptr }`,
+ body: `s := &S33{p: new(int)}; C.f33(unsafe.Pointer(&s.a))`,
+ fail: false,
+ },
+ {
+ // Test that converting multiple struct field
+ // addresses to unsafe.Pointer still just checks those
+ // fields. Issue #25941.
+ name: "structfield2",
+ c: `void f34(void* p, int r, void* s) {}`,
+ imports: []string{"unsafe"},
+ support: `type S34 struct { a [8]byte; p *int; b int64; }`,
+ body: `s := &S34{p: new(int)}; C.f34(unsafe.Pointer(&s.a), 32, unsafe.Pointer(&s.b))`,
+ fail: false,
+ },
+ {
+ // Test that second argument to cgoCheckPointer is
+ // evaluated when a deferred function is deferred, not
+ // when it is run.
+ name: "defer2",
+ c: `void f35(char **pc) {}`,
+ support: `type S35a struct { s []*C.char }; type S35b struct { ps *S35a }`,
+ body: `p := &S35b{&S35a{[]*C.char{nil}}}; defer C.f35(&p.ps.s[0]); p.ps = nil`,
+ fail: false,
+ },
+ {
+ // Test that indexing into a function call still
+ // examines only the slice being indexed.
+ name: "buffer",
+ c: `void f36(void *p) {}`,
+ imports: []string{"bytes", "unsafe"},
+ body: `var b bytes.Buffer; b.WriteString("a"); C.f36(unsafe.Pointer(&b.Bytes()[0]))`,
+ fail: false,
+ },
+ {
+ // Test that bgsweep releasing a finalizer is OK.
+ name: "finalizer",
+ c: `// Nothing to declare.`,
+ imports: []string{"os"},
+ support: `func open37() { os.Open(os.Args[0]) }; var G37 [][]byte`,
+ body: `for i := 0; i < 10000; i++ { G37 = append(G37, make([]byte, 4096)); if i % 100 == 0 { G37 = nil; open37() } }`,
+ fail: false,
+ },
+ {
+ // Test that converting generated struct to interface is OK.
+ name: "structof",
+ c: `// Nothing to declare.`,
+ imports: []string{"reflect"},
+ support: `type MyInt38 int; func (i MyInt38) Get() int { return int(i) }; type Getter38 interface { Get() int }`,
+ body: `t := reflect.StructOf([]reflect.StructField{{Name: "MyInt38", Type: reflect.TypeOf(MyInt38(0)), Anonymous: true}}); v := reflect.New(t).Elem(); v.Interface().(Getter38).Get()`,
+ fail: false,
+ },
+ {
+ // Test that a converted address of a struct field results
+ // in a check for just that field and not the whole struct.
+ name: "structfieldcast",
+ c: `struct S40i { int i; int* p; }; void f40(struct S40i* p) {}`,
+ support: `type S40 struct { p *int; a C.struct_S40i }`,
+ body: `s := &S40{p: new(int)}; C.f40((*C.struct_S40i)(&s.a))`,
+ fail: false,
+ },
+ {
+ // Test that we handle unsafe.StringData.
+ name: "stringdata",
+ c: `void f41(void* p) {}`,
+ imports: []string{"unsafe"},
+ body: `s := struct { a [4]byte; p *int }{p: new(int)}; str := unsafe.String(&s.a[0], 4); C.f41(unsafe.Pointer(unsafe.StringData(str)))`,
+ fail: false,
+ },
+ {
+ name: "slicedata",
+ c: `void f42(void* p) {}`,
+ imports: []string{"unsafe"},
+ body: `s := []*byte{nil, new(byte)}; C.f42(unsafe.Pointer(unsafe.SliceData(s)))`,
+ fail: true,
+ },
+ {
+ name: "slicedata2",
+ c: `void f43(void* p) {}`,
+ imports: []string{"unsafe"},
+ body: `s := struct { a [4]byte; p *int }{p: new(int)}; C.f43(unsafe.Pointer(unsafe.SliceData(s.a[:])))`,
+ fail: false,
+ },
+}
+
+func TestPointerChecks(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ if runtime.GOOS == "windows" {
+ // TODO: Skip just the cases that fail?
+ t.Skipf("some tests fail to build on %s", runtime.GOOS)
+ }
+
+ var gopath string
+ var dir string
+ if *tmp != "" {
+ gopath = *tmp
+ dir = ""
+ } else {
+ d, err := os.MkdirTemp("", filepath.Base(t.Name()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ dir = d
+ gopath = d
+ }
+
+ exe := buildPtrTests(t, gopath, false)
+ exe2 := buildPtrTests(t, gopath, true)
+
+ // We (TestPointerChecks) return before the parallel subtest functions do,
+ // so we can't just defer os.RemoveAll(dir). Instead we have to wait for
+ // the parallel subtests to finish. This code looks racy but is not:
+ // the add +1 run in serial before testOne blocks. The -1 run in parallel
+ // after testOne finishes.
+ var pending int32
+ for _, pt := range ptrTests {
+ pt := pt
+ t.Run(pt.name, func(t *testing.T) {
+ atomic.AddInt32(&pending, +1)
+ defer func() {
+ if atomic.AddInt32(&pending, -1) == 0 {
+ os.RemoveAll(dir)
+ }
+ }()
+ testOne(t, pt, exe, exe2)
+ })
+ }
+}
+
+func buildPtrTests(t *testing.T, gopath string, cgocheck2 bool) (exe string) {
+
+ src := filepath.Join(gopath, "src", "ptrtest")
+ if err := os.MkdirAll(src, 0777); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(src, "go.mod"), []byte("module ptrtest\ngo 1.20"), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ // Prepare two cgo inputs: one for standard cgo and one for //export cgo.
+ // (The latter cannot have C definitions, only declarations.)
+ var cgo1, cgo2 bytes.Buffer
+ fmt.Fprintf(&cgo1, "package main\n\n/*\n")
+ fmt.Fprintf(&cgo2, "package main\n\n/*\n")
+
+ // C code
+ for _, pt := range ptrTests {
+ cgo := &cgo1
+ if strings.Contains(pt.support, "//export") {
+ cgo = &cgo2
+ }
+ fmt.Fprintf(cgo, "%s\n", pt.c)
+ fmt.Fprintf(&cgo1, "%s\n", pt.c1)
+ }
+ fmt.Fprintf(&cgo1, "*/\nimport \"C\"\n\n")
+ fmt.Fprintf(&cgo2, "*/\nimport \"C\"\n\n")
+
+ // Imports
+ did1 := make(map[string]bool)
+ did2 := make(map[string]bool)
+ did1["os"] = true // for ptrTestMain
+ fmt.Fprintf(&cgo1, "import \"os\"\n")
+
+ for _, pt := range ptrTests {
+ did := did1
+ cgo := &cgo1
+ if strings.Contains(pt.support, "//export") {
+ did = did2
+ cgo = &cgo2
+ }
+ for _, imp := range pt.imports {
+ if !did[imp] {
+ did[imp] = true
+ fmt.Fprintf(cgo, "import %q\n", imp)
+ }
+ }
+ }
+
+ // Func support and bodies.
+ for _, pt := range ptrTests {
+ cgo := &cgo1
+ if strings.Contains(pt.support, "//export") {
+ cgo = &cgo2
+ }
+ fmt.Fprintf(cgo, "%s\nfunc %s() {\n%s\n}\n", pt.support, pt.name, pt.body)
+ }
+
+ // Func list and main dispatch.
+ fmt.Fprintf(&cgo1, "var funcs = map[string]func() {\n")
+ for _, pt := range ptrTests {
+ fmt.Fprintf(&cgo1, "\t%q: %s,\n", pt.name, pt.name)
+ }
+ fmt.Fprintf(&cgo1, "}\n\n")
+ fmt.Fprintf(&cgo1, "%s\n", ptrTestMain)
+
+ if err := os.WriteFile(filepath.Join(src, "cgo1.go"), cgo1.Bytes(), 0666); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(src, "cgo2.go"), cgo2.Bytes(), 0666); err != nil {
+ t.Fatal(err)
+ }
+
+ exeName := "ptrtest.exe"
+ if cgocheck2 {
+ exeName = "ptrtest2.exe"
+ }
+ cmd := exec.Command("go", "build", "-o", exeName)
+ cmd.Dir = src
+ cmd.Env = append(os.Environ(), "GOPATH="+gopath)
+
+ // Set or remove cgocheck2 from the environment.
+ goexperiment := strings.Split(os.Getenv("GOEXPERIMENT"), ",")
+ if len(goexperiment) == 1 && goexperiment[0] == "" {
+ goexperiment = nil
+ }
+ i := slices.Index(goexperiment, "cgocheck2")
+ changed := false
+ if cgocheck2 && i < 0 {
+ goexperiment = append(goexperiment, "cgocheck2")
+ changed = true
+ } else if !cgocheck2 && i >= 0 {
+ goexperiment = append(goexperiment[:i], goexperiment[i+1:]...)
+ changed = true
+ }
+ if changed {
+ cmd.Env = append(cmd.Env, "GOEXPERIMENT="+strings.Join(goexperiment, ","))
+ }
+
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("go build: %v\n%s", err, out)
+ }
+
+ return filepath.Join(src, exeName)
+}
+
+const ptrTestMain = `
+func main() {
+ for _, arg := range os.Args[1:] {
+ f := funcs[arg]
+ if f == nil {
+ panic("missing func "+arg)
+ }
+ f()
+ }
+}
+`
+
+var csem = make(chan bool, 16)
+
+func testOne(t *testing.T, pt ptrTest, exe, exe2 string) {
+ t.Parallel()
+
+ // Run the tests in parallel, but don't run too many
+ // executions in parallel, to avoid overloading the system.
+ runcmd := func(cgocheck string) ([]byte, error) {
+ csem <- true
+ defer func() { <-csem }()
+ x := exe
+ if cgocheck == "2" {
+ x = exe2
+ cgocheck = "1"
+ }
+ cmd := exec.Command(x, pt.name)
+ cmd.Env = append(os.Environ(), "GODEBUG=cgocheck="+cgocheck)
+ return cmd.CombinedOutput()
+ }
+
+ if pt.expensive {
+ buf, err := runcmd("1")
+ if err != nil {
+ t.Logf("%s", buf)
+ if pt.fail {
+ t.Fatalf("test marked expensive, but failed when not expensive: %v", err)
+ } else {
+ t.Errorf("failed unexpectedly with GODEBUG=cgocheck=1: %v", err)
+ }
+ }
+
+ }
+
+ cgocheck := ""
+ if pt.expensive {
+ cgocheck = "2"
+ }
+
+ buf, err := runcmd(cgocheck)
+ if pt.fail {
+ if err == nil {
+ t.Logf("%s", buf)
+ t.Fatalf("did not fail as expected")
+ } else if !bytes.Contains(buf, []byte("Go pointer")) {
+ t.Logf("%s", buf)
+ t.Fatalf("did not print expected error (failed with %v)", err)
+ }
+ } else {
+ if err != nil {
+ t.Logf("%s", buf)
+ t.Fatalf("failed unexpectedly: %v", err)
+ }
+
+ if !pt.expensive {
+ // Make sure it passes with the expensive checks.
+ buf, err := runcmd("2")
+ if err != nil {
+ t.Logf("%s", buf)
+ t.Fatalf("failed unexpectedly with expensive checks: %v", err)
+ }
+ }
+ }
+
+ if pt.fail {
+ buf, err := runcmd("0")
+ if err != nil {
+ t.Logf("%s", buf)
+ t.Fatalf("failed unexpectedly with GODEBUG=cgocheck=0: %v", err)
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/err1.go b/src/cmd/cgo/internal/testerrors/testdata/err1.go
new file mode 100644
index 0000000..ced7443
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/err1.go
@@ -0,0 +1,22 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#cgo LDFLAGS: -L/nonexist
+
+void test() {
+ xxx; // ERROR HERE
+}
+
+// Issue 8442. Cgo output unhelpful error messages for
+// invalid C preambles.
+void issue8442foo(UNDEF*); // ERROR HERE
+*/
+import "C"
+
+func main() {
+ C.test()
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/err2.go b/src/cmd/cgo/internal/testerrors/testdata/err2.go
new file mode 100644
index 0000000..aa94158
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/err2.go
@@ -0,0 +1,110 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <stdio.h>
+
+typedef struct foo foo_t;
+typedef struct bar bar_t;
+
+foo_t *foop;
+
+long double x = 0;
+
+static int transform(int x) { return x; }
+
+typedef void v;
+void F(v** p) {}
+
+void fvi(void *p, int x) {}
+
+void fppi(int** p) {}
+
+int i;
+void fi(int i) {}
+*/
+import "C"
+import (
+ "unsafe"
+)
+
+func main() {
+ s := ""
+ _ = s
+ C.malloc(s) // ERROR HERE
+
+ x := (*C.bar_t)(nil)
+ C.foop = x // ERROR HERE
+
+ // issue 13129: used to output error about C.unsignedshort with CC=clang
+ var x1 C.ushort
+ x1 = int(0) // ERROR HERE: C\.ushort
+
+ // issue 13423
+ _ = C.fopen() // ERROR HERE
+
+ // issue 13467
+ var x2 rune = '✈'
+ var _ rune = C.transform(x2) // ERROR HERE: C\.int
+
+ // issue 13635: used to output error about C.unsignedchar.
+ // This test tests all such types.
+ var (
+ _ C.uchar = "uc" // ERROR HERE: C\.uchar
+ _ C.schar = "sc" // ERROR HERE: C\.schar
+ _ C.ushort = "us" // ERROR HERE: C\.ushort
+ _ C.uint = "ui" // ERROR HERE: C\.uint
+ _ C.ulong = "ul" // ERROR HERE: C\.ulong
+ _ C.longlong = "ll" // ERROR HERE: C\.longlong
+ _ C.ulonglong = "ull" // ERROR HERE: C\.ulonglong
+ _ C.complexfloat = "cf" // ERROR HERE: C\.complexfloat
+ _ C.complexdouble = "cd" // ERROR HERE: C\.complexdouble
+ )
+
+ // issue 13830
+ // cgo converts C void* to Go unsafe.Pointer, so despite appearances C
+ // void** is Go *unsafe.Pointer. This test verifies that we detect the
+ // problem at build time.
+ {
+ type v [0]byte
+
+ f := func(p **v) {
+ C.F((**C.v)(unsafe.Pointer(p))) // ERROR HERE
+ }
+ var p *v
+ f(&p)
+ }
+
+ // issue 16116
+ _ = C.fvi(1) // ERROR HERE
+
+ // Issue 16591: Test that we detect an invalid call that was being
+ // hidden by a type conversion inserted by cgo checking.
+ {
+ type x *C.int
+ var p *x
+ C.fppi(p) // ERROR HERE
+ }
+
+ // issue 26745
+ _ = func(i int) int {
+ // typecheck reports at column 14 ('+'), but types2 reports at
+ // column 10 ('C').
+ // TODO(mdempsky): Investigate why, and see if types2 can be
+ // updated to match typecheck behavior.
+ return C.i + 1 // ERROR HERE: \b(10|14)\b
+ }
+ _ = func(i int) {
+ // typecheck reports at column 7 ('('), but types2 reports at
+ // column 8 ('i'). The types2 position is more correct, but
+ // updating typecheck here is fundamentally challenging because of
+ // IR limitations.
+ C.fi(i) // ERROR HERE: \b(7|8)\b
+ }
+
+ C.fi = C.fi // ERROR HERE
+
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/err4.go b/src/cmd/cgo/internal/testerrors/testdata/err4.go
new file mode 100644
index 0000000..8e5f78e
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/err4.go
@@ -0,0 +1,15 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+long double x = 0;
+*/
+import "C"
+
+func main() {
+ _ = C.x // ERROR HERE
+ _ = C.x
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/err5.go b/src/cmd/cgo/internal/testerrors/testdata/err5.go
new file mode 100644
index 0000000..779d745
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/err5.go
@@ -0,0 +1,10 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+//line /tmp/_cgo_.go:1
+//go:cgo_dynamic_linker "/elf/interp" // ERROR HERE: only allowed in cgo-generated code
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue11097a.go b/src/cmd/cgo/internal/testerrors/testdata/issue11097a.go
new file mode 100644
index 0000000..028d10c
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue11097a.go
@@ -0,0 +1,15 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+//enum test { foo, bar };
+*/
+import "C"
+
+func main() {
+ var a = C.enum_test(1) // ERROR HERE
+ _ = a
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue11097b.go b/src/cmd/cgo/internal/testerrors/testdata/issue11097b.go
new file mode 100644
index 0000000..b00f24f
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue11097b.go
@@ -0,0 +1,15 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+//enum test { foo, bar };
+*/
+import "C"
+
+func main() {
+ p := new(C.enum_test) // ERROR HERE
+ _ = p
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue14669.go b/src/cmd/cgo/internal/testerrors/testdata/issue14669.go
new file mode 100644
index 0000000..04d2bcb
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue14669.go
@@ -0,0 +1,23 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 14669: test that fails when build with CGO_CFLAGS selecting
+// optimization.
+
+package p
+
+/*
+const int E = 1;
+
+typedef struct s {
+ int c;
+} s;
+*/
+import "C"
+
+func F() {
+ _ = C.s{
+ c: C.E,
+ }
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue18452.go b/src/cmd/cgo/internal/testerrors/testdata/issue18452.go
new file mode 100644
index 0000000..0386d76
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue18452.go
@@ -0,0 +1,18 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 18452: show pos info in undefined name errors
+
+package p
+
+import (
+ "C"
+ "fmt"
+)
+
+func a() {
+ fmt.Println("Hello, world!")
+ C.function_that_does_not_exist() // ERROR HERE
+ C.pi // ERROR HERE
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue18889.go b/src/cmd/cgo/internal/testerrors/testdata/issue18889.go
new file mode 100644
index 0000000..bba6b8f
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue18889.go
@@ -0,0 +1,7 @@
+package main
+
+import "C"
+
+func main() {
+ _ = C.malloc // ERROR HERE
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue28069.go b/src/cmd/cgo/internal/testerrors/testdata/issue28069.go
new file mode 100644
index 0000000..e19a3b4
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue28069.go
@@ -0,0 +1,26 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that the error message for an unrepresentable typedef in a
+// union appears on the right line. This test is only run if the size
+// of long double is larger than 64.
+
+package main
+
+/*
+typedef long double Float128;
+
+typedef struct SV {
+ union {
+ Float128 float128;
+ } value;
+} SV;
+*/
+import "C"
+
+type ts struct {
+ tv *C.SV // ERROR HERE
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue28721.go b/src/cmd/cgo/internal/testerrors/testdata/issue28721.go
new file mode 100644
index 0000000..0eb2a92
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue28721.go
@@ -0,0 +1,29 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// cgo should reject the use of mangled C names.
+
+package main
+
+/*
+typedef struct a {
+ int i;
+} a;
+void fn(void) {}
+*/
+import "C"
+
+type B _Ctype_struct_a // ERROR HERE
+
+var a _Ctype_struct_a // ERROR HERE
+
+type A struct {
+ a *_Ctype_struct_a // ERROR HERE
+}
+
+var notExist _Ctype_NotExist // ERROR HERE
+
+func main() {
+ _Cfunc_fn() // ERROR HERE
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue33061.go b/src/cmd/cgo/internal/testerrors/testdata/issue33061.go
new file mode 100644
index 0000000..77d5f7a
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue33061.go
@@ -0,0 +1,17 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// cgo shouldn't crash if there is an extra argument with a C reference.
+
+package main
+
+// void F(void* p) {};
+import "C"
+
+import "unsafe"
+
+func F() {
+ var i int
+ C.F(unsafe.Pointer(&i), C.int(0)) // ERROR HERE
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue42580.go b/src/cmd/cgo/internal/testerrors/testdata/issue42580.go
new file mode 100644
index 0000000..aba80df
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue42580.go
@@ -0,0 +1,44 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 42580: cmd/cgo: shifting identifier position in ast
+
+package cgotest
+
+// typedef int (*intFunc) ();
+//
+// char* strarg = "";
+//
+// int func_with_char(char* arg, void* dummy)
+// {return 5;}
+//
+// int* get_arr(char* arg, void* dummy)
+// {return NULL;}
+import "C"
+import "unsafe"
+
+// Test variables
+var (
+ checkedPointer = []byte{1}
+ doublePointerChecked = []byte{1}
+ singleInnerPointerChecked = []byte{1}
+)
+
+// This test checks the positions of variable identifiers.
+// Changing the positions of the test variables idents after this point will break the test.
+
+func TestSingleArgumentCast() C.int {
+ retcode := C.func_with_char((*C.char)(unsafe.Pointer(&checkedPointer[0])), unsafe.Pointer(C.strarg))
+ return retcode
+}
+
+func TestSingleArgumentCastRecFuncAsSimpleArg() C.int {
+ retcode := C.func_with_char((*C.char)(unsafe.Pointer(C.get_arr((*C.char)(unsafe.Pointer(&singleInnerPointerChecked[0])), unsafe.Pointer(C.strarg)))), nil)
+ return retcode
+}
+
+func TestSingleArgumentCastRecFunc() C.int {
+ retcode := C.func_with_char((*C.char)(unsafe.Pointer(C.get_arr((*C.char)(unsafe.Pointer(&doublePointerChecked[0])), unsafe.Pointer(C.strarg)))), unsafe.Pointer(C.strarg))
+ return retcode
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/issue50710.go b/src/cmd/cgo/internal/testerrors/testdata/issue50710.go
new file mode 100644
index 0000000..dffea22
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/issue50710.go
@@ -0,0 +1,14 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// size_t StrLen(_GoString_ s) {
+// return _GoStringLen(s);
+// }
+import "C"
+
+func main() {
+ C.StrLen1() // ERROR HERE
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/long_double_size.go b/src/cmd/cgo/internal/testerrors/testdata/long_double_size.go
new file mode 100644
index 0000000..8b797f8
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/long_double_size.go
@@ -0,0 +1,16 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+const int sizeofLongDouble = sizeof(long double);
+*/
+import "C"
+
+import "fmt"
+
+func main() {
+ fmt.Println(C.sizeofLongDouble)
+}
diff --git a/src/cmd/cgo/internal/testerrors/testdata/malloc.go b/src/cmd/cgo/internal/testerrors/testdata/malloc.go
new file mode 100644
index 0000000..65da020
--- /dev/null
+++ b/src/cmd/cgo/internal/testerrors/testdata/malloc.go
@@ -0,0 +1,34 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that C.malloc does not return nil.
+
+package main
+
+// #include <stdlib.h>
+import "C"
+
+import (
+ "fmt"
+ "runtime"
+)
+
+func main() {
+ var size C.size_t
+ size--
+
+ // The Dragonfly libc succeeds when asked to allocate
+ // 0xffffffffffffffff bytes, so pass a different value that
+ // causes it to fail.
+ if runtime.GOOS == "dragonfly" {
+ size = C.size_t(0x7fffffff << (32 * (^uintptr(0) >> 63)))
+ }
+
+ p := C.malloc(size)
+ if p == nil {
+ fmt.Println("malloc: C.malloc returned nil")
+ // Just exit normally--the test script expects this
+ // program to crash, so exiting normally indicates failure.
+ }
+}
diff --git a/src/cmd/cgo/internal/testfortran/fortran_test.go b/src/cmd/cgo/internal/testfortran/fortran_test.go
new file mode 100644
index 0000000..eaa36ac
--- /dev/null
+++ b/src/cmd/cgo/internal/testfortran/fortran_test.go
@@ -0,0 +1,85 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package fortran
+
+import (
+ "fmt"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestFortran(t *testing.T) {
+ testenv.MustHaveGoRun(t)
+ testenv.MustHaveCGO(t)
+
+ // Find the FORTRAN compiler.
+ fc := os.Getenv("FC")
+ if fc == "" {
+ fc, _ = exec.LookPath("gfortran")
+ }
+ if fc == "" {
+ t.Skip("fortran compiler not found (try setting $FC)")
+ }
+
+ var fcExtra []string
+ if strings.Contains(fc, "gfortran") {
+ // TODO: This duplicates but also diverges from logic from cmd/go
+ // itself. For example, cmd/go merely adds -lgfortran without the extra
+ // library path work. If this is what's necessary to run gfortran, we
+ // should reconcile the logic here and in cmd/go.. Maybe this should
+ // become a cmd/go script test to share that logic.
+
+ // Add -m32 if we're targeting 386, in case this is a cross-compile.
+ if runtime.GOARCH == "386" {
+ fcExtra = append(fcExtra, "-m32")
+ }
+
+ // Find libgfortran. If the FORTRAN compiler isn't bundled
+ // with the C linker, this may be in a path the C linker can't
+ // find on its own. (See #14544)
+ libExt := "so"
+ switch runtime.GOOS {
+ case "darwin":
+ libExt = "dylib"
+ case "aix":
+ libExt = "a"
+ }
+ libPath, err := exec.Command(fc, append([]string{"-print-file-name=libgfortran." + libExt}, fcExtra...)...).CombinedOutput()
+ if err != nil {
+ t.Errorf("error invoking %s: %s", fc, err)
+ }
+ libDir := filepath.Dir(string(libPath))
+ cgoLDFlags := os.Getenv("CGO_LDFLAGS")
+ cgoLDFlags += " -L " + libDir
+ if runtime.GOOS != "aix" {
+ cgoLDFlags += " -Wl,-rpath," + libDir
+ }
+ t.Logf("CGO_LDFLAGS=%s", cgoLDFlags)
+ os.Setenv("CGO_LDFLAGS", cgoLDFlags)
+
+ }
+
+ // Do a test build that doesn't involve Go FORTRAN support.
+ fcArgs := append([]string{"testdata/helloworld/helloworld.f90", "-o", "/dev/null"}, fcExtra...)
+ t.Logf("%s %s", fc, fcArgs)
+ if err := exec.Command(fc, fcArgs...).Run(); err != nil {
+ t.Skipf("skipping Fortran test: could not build helloworld.f90 with %s: %s", fc, err)
+ }
+
+ // Finally, run the actual test.
+ t.Log("go", "run", "./testdata/testprog")
+ out, err := exec.Command("go", "run", "./testdata/testprog").CombinedOutput()
+ if err == nil && string(out) != "ok\n" {
+ err = fmt.Errorf("expected ok")
+ }
+ if err != nil {
+ t.Errorf("%s\nOutput:\n%s", err, string(out))
+ }
+}
diff --git a/src/cmd/cgo/internal/testfortran/testdata/helloworld/helloworld.f90 b/src/cmd/cgo/internal/testfortran/testdata/helloworld/helloworld.f90
new file mode 100644
index 0000000..cbc34c1
--- /dev/null
+++ b/src/cmd/cgo/internal/testfortran/testdata/helloworld/helloworld.f90
@@ -0,0 +1,3 @@
+ program HelloWorldF90
+ write(*,*) "Hello World!"
+ end program HelloWorldF90
diff --git a/src/cmd/cgo/internal/testfortran/testdata/testprog/answer.f90 b/src/cmd/cgo/internal/testfortran/testdata/testprog/answer.f90
new file mode 100644
index 0000000..b3717ee
--- /dev/null
+++ b/src/cmd/cgo/internal/testfortran/testdata/testprog/answer.f90
@@ -0,0 +1,9 @@
+! Copyright 2016 The Go Authors. All rights reserved.
+! Use of this source code is governed by a BSD-style
+! license that can be found in the LICENSE file.
+
+function the_answer() result(j) bind(C)
+ use iso_c_binding, only: c_int
+ integer(c_int) :: j ! output
+ j = 42
+end function the_answer
diff --git a/src/cmd/cgo/internal/testfortran/testdata/testprog/fortran.go b/src/cmd/cgo/internal/testfortran/testdata/testprog/fortran.go
new file mode 100644
index 0000000..d8004ce
--- /dev/null
+++ b/src/cmd/cgo/internal/testfortran/testdata/testprog/fortran.go
@@ -0,0 +1,21 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// int the_answer();
+import "C"
+import "os"
+
+func TheAnswer() int {
+ return int(C.the_answer())
+}
+
+func main() {
+ if a := TheAnswer(); a != 42 {
+ println("Unexpected result for The Answer. Got:", a, " Want: 42")
+ os.Exit(1)
+ }
+ println("ok")
+}
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/anonunion.go b/src/cmd/cgo/internal/testgodefs/testdata/anonunion.go
new file mode 100644
index 0000000..2c86c5c
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/anonunion.go
@@ -0,0 +1,26 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+// This file tests that when cgo -godefs sees a struct with a field
+// that is an anonymous union, the first field in the union is
+// promoted to become a field of the struct. See issue 6677 for
+// background.
+
+/*
+typedef struct {
+ union {
+ long l;
+ int c;
+ };
+} t;
+*/
+import "C"
+
+// Input for cgo -godefs.
+
+type T C.t
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/bitfields.go b/src/cmd/cgo/internal/testgodefs/testdata/bitfields.go
new file mode 100644
index 0000000..431ffc0
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/bitfields.go
@@ -0,0 +1,31 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+// This file tests that we don't generate an incorrect field location
+// for a bitfield that appears aligned.
+
+/*
+struct bitfields {
+ unsigned int B1 : 5;
+ unsigned int B2 : 1;
+ unsigned int B3 : 1;
+ unsigned int B4 : 1;
+ unsigned int Short1 : 16; // misaligned on 8 bit boundary
+ unsigned int B5 : 1;
+ unsigned int B6 : 1;
+ unsigned int B7 : 1;
+ unsigned int B8 : 1;
+ unsigned int B9 : 1;
+ unsigned int B10 : 3;
+ unsigned int Short2 : 16; // alignment is OK
+ unsigned int Short3 : 16; // alignment is OK
+};
+*/
+import "C"
+
+type bitfields C.struct_bitfields
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go b/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go
new file mode 100644
index 0000000..b0c5074
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/fieldtypedef.go
@@ -0,0 +1,18 @@
+// Copyright 2018 The Go Authors. All rights reserve d.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+/*
+struct S1 { int f1; };
+struct S2 { struct S1 s1; };
+typedef struct S1 S1Type;
+typedef struct S2 S2Type;
+*/
+import "C"
+
+type S1 C.S1Type
+type S2 C.S2Type
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/issue37479.go b/src/cmd/cgo/internal/testgodefs/testdata/issue37479.go
new file mode 100644
index 0000000..d545310
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/issue37479.go
@@ -0,0 +1,33 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+/*
+typedef struct A A;
+
+typedef struct {
+ struct A *next;
+ struct A **prev;
+} N;
+
+struct A
+{
+ N n;
+};
+
+typedef struct B
+{
+ A* a;
+} B;
+*/
+import "C"
+
+type N C.N
+
+type A C.A
+
+type B C.B
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/issue37621.go b/src/cmd/cgo/internal/testgodefs/testdata/issue37621.go
new file mode 100644
index 0000000..655e8ae
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/issue37621.go
@@ -0,0 +1,23 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+/*
+struct tt {
+ long long a;
+ long long b;
+};
+
+struct s {
+ struct tt ts[3];
+};
+*/
+import "C"
+
+type TT C.struct_tt
+
+type S C.struct_s
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/issue38649.go b/src/cmd/cgo/internal/testgodefs/testdata/issue38649.go
new file mode 100644
index 0000000..78b5f78
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/issue38649.go
@@ -0,0 +1,15 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+/*
+struct Issue38649 { int x; };
+#define issue38649 struct Issue38649
+*/
+import "C"
+
+type issue38649 C.issue38649
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/issue39534.go b/src/cmd/cgo/internal/testgodefs/testdata/issue39534.go
new file mode 100644
index 0000000..af730e9
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/issue39534.go
@@ -0,0 +1,12 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+// enum { ENUMVAL = 0x1 };
+import "C"
+
+const ENUMVAL = C.ENUMVAL
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/issue48396.go b/src/cmd/cgo/internal/testgodefs/testdata/issue48396.go
new file mode 100644
index 0000000..81dd2fe
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/issue48396.go
@@ -0,0 +1,18 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+/*
+// from <linux/kcm.h>
+struct issue48396 {
+ int fd;
+ int bpf_fd;
+};
+*/
+import "C"
+
+type Issue48396 C.struct_issue48396
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/issue8478.go b/src/cmd/cgo/internal/testgodefs/testdata/issue8478.go
new file mode 100644
index 0000000..f4ef164
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/issue8478.go
@@ -0,0 +1,20 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+// Issue 8478. Test that void* is consistently mapped to *byte.
+
+/*
+typedef struct {
+ void *p;
+ void **q;
+ void ***r;
+} s;
+*/
+import "C"
+
+type Issue8478 C.s
diff --git a/src/cmd/cgo/internal/testgodefs/testdata/main.go b/src/cmd/cgo/internal/testgodefs/testdata/main.go
new file mode 100644
index 0000000..5c670f3
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testdata/main.go
@@ -0,0 +1,57 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "reflect"
+)
+
+// Test that the struct field in anonunion.go was promoted.
+var v1 T
+var v2 = v1.L
+
+// Test that P, Q, and R all point to byte.
+var v3 = Issue8478{P: (*byte)(nil), Q: (**byte)(nil), R: (***byte)(nil)}
+
+// Test that N, A and B are fully defined
+var v4 = N{}
+var v5 = A{}
+var v6 = B{}
+
+// Test that S is fully defined
+var v7 = S{}
+
+// Test that #define'd type is fully defined
+var _ = issue38649{X: 0}
+
+// Test that prefixes do not cause duplicate field names.
+var _ = Issue48396{Fd: 1, Bpf_fd: 2}
+
+func main() {
+ pass := true
+
+ // The Go translation of bitfields should not have any of the
+ // bitfield types. The order in which bitfields are laid out
+ // in memory is implementation defined, so we can't easily
+ // know how a bitfield should correspond to a Go type, even if
+ // it appears to be aligned correctly.
+ bitfieldType := reflect.TypeOf(bitfields{})
+ check := func(name string) {
+ _, ok := bitfieldType.FieldByName(name)
+ if ok {
+ fmt.Fprintf(os.Stderr, "found unexpected bitfields field %s\n", name)
+ pass = false
+ }
+ }
+ check("Short1")
+ check("Short2")
+ check("Short3")
+
+ if !pass {
+ os.Exit(1)
+ }
+}
diff --git a/src/cmd/cgo/internal/testgodefs/testgodefs_test.go b/src/cmd/cgo/internal/testgodefs/testgodefs_test.go
new file mode 100644
index 0000000..8138b7f
--- /dev/null
+++ b/src/cmd/cgo/internal/testgodefs/testgodefs_test.go
@@ -0,0 +1,116 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package testgodefs
+
+import (
+ "bytes"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+// We are testing cgo -godefs, which translates Go files that use
+// import "C" into Go files with Go definitions of types defined in the
+// import "C" block. Add more tests here.
+var filePrefixes = []string{
+ "anonunion",
+ "bitfields",
+ "issue8478",
+ "fieldtypedef",
+ "issue37479",
+ "issue37621",
+ "issue38649",
+ "issue39534",
+ "issue48396",
+}
+
+func TestGoDefs(t *testing.T) {
+ testenv.MustHaveGoRun(t)
+ testenv.MustHaveCGO(t)
+
+ testdata, err := filepath.Abs("testdata")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gopath, err := os.MkdirTemp("", "testgodefs-gopath")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(gopath)
+
+ dir := filepath.Join(gopath, "src", "testgodefs")
+ if err := os.MkdirAll(dir, 0755); err != nil {
+ t.Fatal(err)
+ }
+
+ for _, fp := range filePrefixes {
+ cmd := exec.Command("go", "tool", "cgo",
+ "-godefs",
+ "-srcdir", testdata,
+ "-objdir", dir,
+ fp+".go")
+ cmd.Stderr = new(bytes.Buffer)
+
+ out, err := cmd.Output()
+ if err != nil {
+ t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
+ }
+
+ fn := fp + "_defs.go"
+ if err := os.WriteFile(filepath.Join(dir, fn), out, 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ // Verify that command line arguments are not rewritten in the generated comment,
+ // see go.dev/issue/52063
+ hasGeneratedByComment := false
+ for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
+ cgoExe := "cgo"
+ if runtime.GOOS == "windows" {
+ cgoExe = "cgo.exe"
+ }
+ if !strings.HasPrefix(line, "// "+cgoExe+" -godefs") {
+ continue
+ }
+ if want := "// " + cgoExe + " " + strings.Join(cmd.Args[3:], " "); line != want {
+ t.Errorf("%s: got generated comment %q, want %q", fn, line, want)
+ }
+ hasGeneratedByComment = true
+ break
+ }
+
+ if !hasGeneratedByComment {
+ t.Errorf("%s: comment with generating cgo -godefs command not found", fn)
+ }
+ }
+
+ main, err := os.ReadFile(filepath.Join("testdata", "main.go"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := os.WriteFile(filepath.Join(dir, "main.go"), main, 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ if err := os.WriteFile(filepath.Join(dir, "go.mod"), []byte("module testgodefs\ngo 1.14\n"), 0644); err != nil {
+ t.Fatal(err)
+ }
+
+ // Use 'go run' to build and run the resulting binary in a single step,
+ // instead of invoking 'go build' and the resulting binary separately, so that
+ // this test can pass on mobile builders, which do not copy artifacts back
+ // from remote invocations.
+ cmd := exec.Command("go", "run", ".")
+ cmd.Env = append(os.Environ(), "GOPATH="+gopath)
+ cmd.Dir = dir
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("%s [%s]: %v\n%s", strings.Join(cmd.Args, " "), dir, err, out)
+ }
+}
diff --git a/src/cmd/cgo/internal/testlife/life_test.go b/src/cmd/cgo/internal/testlife/life_test.go
new file mode 100644
index 0000000..e93d29c
--- /dev/null
+++ b/src/cmd/cgo/internal/testlife/life_test.go
@@ -0,0 +1,65 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package life_test
+
+import (
+ "bytes"
+ "cmd/cgo/internal/cgotest"
+ "internal/testenv"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "testing"
+)
+
+func TestMain(m *testing.M) {
+ log.SetFlags(log.Lshortfile)
+ os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+ GOPATH, err := os.MkdirTemp("", "cgolife")
+ if err != nil {
+ log.Panic(err)
+ }
+ defer os.RemoveAll(GOPATH)
+ os.Setenv("GOPATH", GOPATH)
+
+ // Copy testdata into GOPATH/src/cgolife, along with a go.mod file
+ // declaring the same path.
+ modRoot := filepath.Join(GOPATH, "src", "cgolife")
+ if err := cgotest.OverlayDir(modRoot, "testdata"); err != nil {
+ log.Panic(err)
+ }
+ if err := os.Chdir(modRoot); err != nil {
+ log.Panic(err)
+ }
+ os.Setenv("PWD", modRoot)
+ if err := os.WriteFile("go.mod", []byte("module cgolife\n"), 0666); err != nil {
+ log.Panic(err)
+ }
+
+ return m.Run()
+}
+
+// TestTestRun runs a test case for cgo //export.
+func TestTestRun(t *testing.T) {
+ testenv.MustHaveGoRun(t)
+ testenv.MustHaveCGO(t)
+
+ cmd := exec.Command("go", "run", "main.go")
+ got, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%v: %s\n%s", cmd, err, got)
+ }
+ want, err := os.ReadFile("main.out")
+ if err != nil {
+ t.Fatal("reading golden output:", err)
+ }
+ if !bytes.Equal(got, want) {
+ t.Errorf("'%v' output does not match expected in main.out. Instead saw:\n%s", cmd, got)
+ }
+}
diff --git a/src/cmd/cgo/internal/testlife/testdata/c-life.c b/src/cmd/cgo/internal/testlife/testdata/c-life.c
new file mode 100644
index 0000000..f853163
--- /dev/null
+++ b/src/cmd/cgo/internal/testlife/testdata/c-life.c
@@ -0,0 +1,56 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <assert.h>
+#include "life.h"
+#include "_cgo_export.h"
+
+const int MYCONST = 0;
+
+// Do the actual manipulation of the life board in C. This could be
+// done easily in Go, we are just using C for demonstration
+// purposes.
+void
+Step(int x, int y, int *a, int *n)
+{
+ struct GoStart_return r;
+
+ // Use Go to start 4 goroutines each of which handles 1/4 of the
+ // board.
+ r = GoStart(0, x, y, 0, x / 2, 0, y / 2, a, n);
+ assert(r.r0 == 0 && r.r1 == 100); // test multiple returns
+ r = GoStart(1, x, y, x / 2, x, 0, y / 2, a, n);
+ assert(r.r0 == 1 && r.r1 == 101); // test multiple returns
+ GoStart(2, x, y, 0, x / 2, y / 2, y, a, n);
+ GoStart(3, x, y, x / 2, x, y / 2, y, a, n);
+ GoWait(0);
+ GoWait(1);
+ GoWait(2);
+ GoWait(3);
+}
+
+// The actual computation. This is called in parallel.
+void
+DoStep(int xdim, int ydim, int xstart, int xend, int ystart, int yend, int *a, int *n)
+{
+ int x, y, c, i, j;
+
+ for(x = xstart; x < xend; x++) {
+ for(y = ystart; y < yend; y++) {
+ c = 0;
+ for(i = -1; i <= 1; i++) {
+ for(j = -1; j <= 1; j++) {
+ if(x+i >= 0 && x+i < xdim &&
+ y+j >= 0 && y+j < ydim &&
+ (i != 0 || j != 0))
+ c += a[(x+i)*xdim + (y+j)] != 0;
+ }
+ }
+ if(c == 3 || (c == 2 && a[x*xdim + y] != 0))
+ n[x*xdim + y] = 1;
+ else
+ n[x*xdim + y] = 0;
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/testlife/testdata/life.go b/src/cmd/cgo/internal/testlife/testdata/life.go
new file mode 100644
index 0000000..7231140
--- /dev/null
+++ b/src/cmd/cgo/internal/testlife/testdata/life.go
@@ -0,0 +1,40 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgolife
+
+// #include "life.h"
+import "C"
+
+import "unsafe"
+
+func Run(gen, x, y int, a []int32) {
+ n := make([]int32, x*y)
+ for i := 0; i < gen; i++ {
+ C.Step(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&a[0])), (*C.int)(unsafe.Pointer(&n[0])))
+ copy(a, n)
+ }
+}
+
+// Keep the channels visible from Go.
+var chans [4]chan bool
+
+// Double return value is just for testing.
+//
+//export GoStart
+func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) {
+ c := make(chan bool, int(C.MYCONST))
+ go func() {
+ C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n)
+ c <- true
+ }()
+ chans[i] = c
+ return int(i), int(i + 100)
+}
+
+//export GoWait
+func GoWait(i C.int) {
+ <-chans[i]
+ chans[i] = nil
+}
diff --git a/src/cmd/cgo/internal/testlife/testdata/life.h b/src/cmd/cgo/internal/testlife/testdata/life.h
new file mode 100644
index 0000000..11d2b97
--- /dev/null
+++ b/src/cmd/cgo/internal/testlife/testdata/life.h
@@ -0,0 +1,7 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+extern void Step(int, int, int *, int *);
+extern void DoStep(int, int, int, int, int, int, int *, int *);
+extern const int MYCONST;
diff --git a/src/cmd/cgo/internal/testlife/testdata/main.go b/src/cmd/cgo/internal/testlife/testdata/main.go
new file mode 100644
index 0000000..e9d19be
--- /dev/null
+++ b/src/cmd/cgo/internal/testlife/testdata/main.go
@@ -0,0 +1,47 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build test_run
+
+// Run the game of life in C using Go for parallelization.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+
+ "cgolife"
+)
+
+const MAXDIM = 100
+
+var dim = flag.Int("dim", 16, "board dimensions")
+var gen = flag.Int("gen", 10, "generations")
+
+func main() {
+ flag.Parse()
+
+ var a [MAXDIM * MAXDIM]int32
+ for i := 2; i < *dim; i += 8 {
+ for j := 2; j < *dim-3; j += 8 {
+ for y := 0; y < 3; y++ {
+ a[i**dim+j+y] = 1
+ }
+ }
+ }
+
+ cgolife.Run(*gen, *dim, *dim, a[:])
+
+ for i := 0; i < *dim; i++ {
+ for j := 0; j < *dim; j++ {
+ if a[i**dim+j] == 0 {
+ fmt.Print(" ")
+ } else {
+ fmt.Print("X")
+ }
+ }
+ fmt.Print("\n")
+ }
+}
diff --git a/src/cmd/cgo/internal/testlife/testdata/main.out b/src/cmd/cgo/internal/testlife/testdata/main.out
new file mode 100644
index 0000000..26fc9c6
--- /dev/null
+++ b/src/cmd/cgo/internal/testlife/testdata/main.out
@@ -0,0 +1,16 @@
+
+
+ XXX XXX
+
+
+
+
+
+
+
+ XXX XXX
+
+
+
+
+
diff --git a/src/cmd/cgo/internal/testnocgo/nocgo.go b/src/cmd/cgo/internal/testnocgo/nocgo.go
new file mode 100644
index 0000000..00ae5e9
--- /dev/null
+++ b/src/cmd/cgo/internal/testnocgo/nocgo.go
@@ -0,0 +1,22 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that -static works when not using cgo. This test is in
+// misc/cgo to take advantage of the testing framework support for
+// when -static is expected to work.
+
+package nocgo
+
+func NoCgo() int {
+ c := make(chan int)
+
+ // The test is run with external linking, which means that
+ // goroutines will be created via the runtime/cgo package.
+ // Make sure that works.
+ go func() {
+ c <- 42
+ }()
+
+ return <-c
+}
diff --git a/src/cmd/cgo/internal/testnocgo/nocgo_test.go b/src/cmd/cgo/internal/testnocgo/nocgo_test.go
new file mode 100644
index 0000000..45d247c
--- /dev/null
+++ b/src/cmd/cgo/internal/testnocgo/nocgo_test.go
@@ -0,0 +1,14 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package nocgo
+
+import "testing"
+
+func TestNop(t *testing.T) {
+ i := NoCgo()
+ if i != 42 {
+ t.Errorf("got %d, want %d", i, 42)
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/altpath/testdata/common/common.go b/src/cmd/cgo/internal/testplugin/altpath/testdata/common/common.go
new file mode 100644
index 0000000..505ba02
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/altpath/testdata/common/common.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package common
+
+var X int
+
+func init() {
+ X = 4
+}
diff --git a/src/cmd/cgo/internal/testplugin/altpath/testdata/plugin-mismatch/main.go b/src/cmd/cgo/internal/testplugin/altpath/testdata/plugin-mismatch/main.go
new file mode 100644
index 0000000..bfb4ba4
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/altpath/testdata/plugin-mismatch/main.go
@@ -0,0 +1,17 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// // No C code required.
+import "C"
+
+// The common package imported here does not match the common package
+// imported by plugin1. A program that attempts to load plugin1 and
+// plugin-mismatch should produce an error.
+import "testplugin/common"
+
+func ReadCommonX() int {
+ return common.X
+}
diff --git a/src/cmd/cgo/internal/testplugin/plugin_test.go b/src/cmd/cgo/internal/testplugin/plugin_test.go
new file mode 100644
index 0000000..7f5b1bf
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/plugin_test.go
@@ -0,0 +1,406 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package plugin_test
+
+import (
+ "bytes"
+ "cmd/cgo/internal/cgotest"
+ "context"
+ "flag"
+ "fmt"
+ "internal/platform"
+ "internal/testenv"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+)
+
+var globalSkip = func(t *testing.T) {}
+
+var gcflags string = os.Getenv("GO_GCFLAGS")
+var goroot string
+
+func TestMain(m *testing.M) {
+ flag.Parse()
+ log.SetFlags(log.Lshortfile)
+ os.Exit(testMain(m))
+}
+
+// tmpDir is used to cleanup logged commands -- s/tmpDir/$TMPDIR/
+var tmpDir string
+
+// prettyPrintf prints lines with tmpDir sanitized.
+func prettyPrintf(format string, args ...interface{}) {
+ s := fmt.Sprintf(format, args...)
+ if tmpDir != "" {
+ s = strings.ReplaceAll(s, tmpDir, "$TMPDIR")
+ }
+ fmt.Print(s)
+}
+
+func testMain(m *testing.M) int {
+ if testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
+ globalSkip = func(t *testing.T) { t.Skip("short mode and $GO_BUILDER_NAME not set") }
+ return m.Run()
+ }
+ if !platform.BuildModeSupported(runtime.Compiler, "plugin", runtime.GOOS, runtime.GOARCH) {
+ globalSkip = func(t *testing.T) { t.Skip("plugin build mode not supported") }
+ return m.Run()
+ }
+ if !testenv.HasCGO() {
+ globalSkip = func(t *testing.T) { t.Skip("cgo not supported") }
+ return m.Run()
+ }
+
+ cwd, err := os.Getwd()
+ if err != nil {
+ log.Fatal(err)
+ }
+ goroot = filepath.Join(cwd, "../../../../..")
+
+ // Copy testdata into GOPATH/src/testplugin, along with a go.mod file
+ // declaring the same path.
+
+ GOPATH, err := os.MkdirTemp("", "plugin_test")
+ if err != nil {
+ log.Panic(err)
+ }
+ defer os.RemoveAll(GOPATH)
+ tmpDir = GOPATH
+
+ modRoot := filepath.Join(GOPATH, "src", "testplugin")
+ altRoot := filepath.Join(GOPATH, "alt", "src", "testplugin")
+ for srcRoot, dstRoot := range map[string]string{
+ "testdata": modRoot,
+ filepath.Join("altpath", "testdata"): altRoot,
+ } {
+ if err := cgotest.OverlayDir(dstRoot, srcRoot); err != nil {
+ log.Panic(err)
+ }
+ prettyPrintf("mkdir -p %s\n", dstRoot)
+ prettyPrintf("rsync -a %s/ %s\n", srcRoot, dstRoot)
+
+ if err := os.WriteFile(filepath.Join(dstRoot, "go.mod"), []byte("module testplugin\n"), 0666); err != nil {
+ log.Panic(err)
+ }
+ prettyPrintf("echo 'module testplugin' > %s/go.mod\n", dstRoot)
+ }
+
+ os.Setenv("GOPATH", filepath.Join(GOPATH, "alt"))
+ if err := os.Chdir(altRoot); err != nil {
+ log.Panic(err)
+ } else {
+ prettyPrintf("cd %s\n", altRoot)
+ }
+ os.Setenv("PWD", altRoot)
+ goCmd(nil, "build", "-buildmode=plugin", "-o", filepath.Join(modRoot, "plugin-mismatch.so"), "./plugin-mismatch")
+
+ os.Setenv("GOPATH", GOPATH)
+ if err := os.Chdir(modRoot); err != nil {
+ log.Panic(err)
+ } else {
+ prettyPrintf("cd %s\n", modRoot)
+ }
+ os.Setenv("PWD", modRoot)
+
+ os.Setenv("LD_LIBRARY_PATH", modRoot)
+
+ goCmd(nil, "build", "-buildmode=plugin", "./plugin1")
+ goCmd(nil, "build", "-buildmode=plugin", "./plugin2")
+ so, err := os.ReadFile("plugin2.so")
+ if err != nil {
+ log.Panic(err)
+ }
+ if err := os.WriteFile("plugin2-dup.so", so, 0444); err != nil {
+ log.Panic(err)
+ }
+ prettyPrintf("cp plugin2.so plugin2-dup.so\n")
+
+ goCmd(nil, "build", "-buildmode=plugin", "-o=sub/plugin1.so", "./sub/plugin1")
+ goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed1.so", "./unnamed1/main.go")
+ goCmd(nil, "build", "-buildmode=plugin", "-o=unnamed2.so", "./unnamed2/main.go")
+ goCmd(nil, "build", "-o", "host.exe", "./host")
+
+ return m.Run()
+}
+
+func goCmd(t *testing.T, op string, args ...string) string {
+ if t != nil {
+ t.Helper()
+ }
+ var flags []string
+ if op != "tool" {
+ flags = []string{"-gcflags", gcflags}
+ }
+ return run(t, filepath.Join(goroot, "bin", "go"), append(append([]string{op}, flags...), args...)...)
+}
+
+// escape converts a string to something suitable for a shell command line.
+func escape(s string) string {
+ s = strings.Replace(s, "\\", "\\\\", -1)
+ s = strings.Replace(s, "'", "\\'", -1)
+ // Conservative guess at characters that will force quoting
+ if s == "" || strings.ContainsAny(s, "\\ ;#*&$~?!|[]()<>{}`") {
+ s = "'" + s + "'"
+ }
+ return s
+}
+
+// asCommandLine renders cmd as something that could be copy-and-pasted into a command line
+func asCommandLine(cwd string, cmd *exec.Cmd) string {
+ s := "("
+ if cmd.Dir != "" && cmd.Dir != cwd {
+ s += "cd" + escape(cmd.Dir) + ";"
+ }
+ for _, e := range cmd.Env {
+ if !strings.HasPrefix(e, "PATH=") &&
+ !strings.HasPrefix(e, "HOME=") &&
+ !strings.HasPrefix(e, "USER=") &&
+ !strings.HasPrefix(e, "SHELL=") {
+ s += " "
+ s += escape(e)
+ }
+ }
+ // These EVs are relevant to this test.
+ for _, e := range os.Environ() {
+ if strings.HasPrefix(e, "PWD=") ||
+ strings.HasPrefix(e, "GOPATH=") ||
+ strings.HasPrefix(e, "LD_LIBRARY_PATH=") {
+ s += " "
+ s += escape(e)
+ }
+ }
+ for _, a := range cmd.Args {
+ s += " "
+ s += escape(a)
+ }
+ s += " )"
+ return s
+}
+
+func run(t *testing.T, bin string, args ...string) string {
+ cmd := exec.Command(bin, args...)
+ cmdLine := asCommandLine(".", cmd)
+ prettyPrintf("%s\n", cmdLine)
+ cmd.Stderr = new(strings.Builder)
+ out, err := cmd.Output()
+ if err != nil {
+ if t == nil {
+ log.Panicf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
+ } else {
+ t.Helper()
+ t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
+ }
+ }
+
+ return string(bytes.TrimSpace(out))
+}
+
+func TestDWARFSections(t *testing.T) {
+ // test that DWARF sections are emitted for plugins and programs importing "plugin"
+ globalSkip(t)
+ goCmd(t, "run", "./checkdwarf/main.go", "plugin2.so", "plugin2.UnexportedNameReuse")
+ goCmd(t, "run", "./checkdwarf/main.go", "./host.exe", "main.main")
+}
+
+func TestBuildID(t *testing.T) {
+ // check that plugin has build ID.
+ globalSkip(t)
+ b := goCmd(t, "tool", "buildid", "plugin1.so")
+ if len(b) == 0 {
+ t.Errorf("build id not found")
+ }
+}
+
+func TestRunHost(t *testing.T) {
+ globalSkip(t)
+ run(t, "./host.exe")
+}
+
+func TestUniqueTypesAndItabs(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "./iface_a")
+ goCmd(t, "build", "-buildmode=plugin", "./iface_b")
+ goCmd(t, "build", "-o", "iface.exe", "./iface")
+ run(t, "./iface.exe")
+}
+
+func TestIssue18676(t *testing.T) {
+ // make sure we don't add the same itab twice.
+ // The buggy code hangs forever, so use a timeout to check for that.
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18676/plugin.go")
+ goCmd(t, "build", "-o", "issue18676.exe", "./issue18676/main.go")
+
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+ cmd := exec.CommandContext(ctx, "./issue18676.exe")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+}
+
+func TestIssue19534(t *testing.T) {
+ // Test that we can load a plugin built in a path with non-alpha characters.
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-gcflags=-p=issue.19534", "-ldflags=-pluginpath=issue.19534", "-o", "plugin.so", "./issue19534/plugin.go")
+ goCmd(t, "build", "-o", "issue19534.exe", "./issue19534/main.go")
+ run(t, "./issue19534.exe")
+}
+
+func TestIssue18584(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue18584/plugin.go")
+ goCmd(t, "build", "-o", "issue18584.exe", "./issue18584/main.go")
+ run(t, "./issue18584.exe")
+}
+
+func TestIssue19418(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-ldflags=-X main.Val=linkstr", "-o", "plugin.so", "./issue19418/plugin.go")
+ goCmd(t, "build", "-o", "issue19418.exe", "./issue19418/main.go")
+ run(t, "./issue19418.exe")
+}
+
+func TestIssue19529(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./issue19529/plugin.go")
+}
+
+func TestIssue22175(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin1.so", "./issue22175/plugin1.go")
+ goCmd(t, "build", "-buildmode=plugin", "-o", "issue22175_plugin2.so", "./issue22175/plugin2.go")
+ goCmd(t, "build", "-o", "issue22175.exe", "./issue22175/main.go")
+ run(t, "./issue22175.exe")
+}
+
+func TestIssue22295(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "issue.22295.so", "./issue22295.pkg")
+ goCmd(t, "build", "-o", "issue22295.exe", "./issue22295.pkg/main.go")
+ run(t, "./issue22295.exe")
+}
+
+func TestIssue24351(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "issue24351.so", "./issue24351/plugin.go")
+ goCmd(t, "build", "-o", "issue24351.exe", "./issue24351/main.go")
+ run(t, "./issue24351.exe")
+}
+
+func TestIssue25756(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
+ goCmd(t, "build", "-o", "issue25756.exe", "./issue25756/main.go")
+ // Fails intermittently, but 20 runs should cause the failure
+ for n := 20; n > 0; n-- {
+ t.Run(fmt.Sprint(n), func(t *testing.T) {
+ t.Parallel()
+ run(t, "./issue25756.exe")
+ })
+ }
+}
+
+// Test with main using -buildmode=pie with plugin for issue #43228
+func TestIssue25756pie(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "life.so", "./issue25756/plugin")
+ goCmd(t, "build", "-buildmode=pie", "-o", "issue25756pie.exe", "./issue25756/main.go")
+ run(t, "./issue25756pie.exe")
+}
+
+func TestMethod(t *testing.T) {
+ // Exported symbol's method must be live.
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "plugin.so", "./method/plugin.go")
+ goCmd(t, "build", "-o", "method.exe", "./method/main.go")
+ run(t, "./method.exe")
+}
+
+func TestMethod2(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "method2.so", "./method2/plugin.go")
+ goCmd(t, "build", "-o", "method2.exe", "./method2/main.go")
+ run(t, "./method2.exe")
+}
+
+func TestMethod3(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "method3.so", "./method3/plugin.go")
+ goCmd(t, "build", "-o", "method3.exe", "./method3/main.go")
+ run(t, "./method3.exe")
+}
+
+func TestIssue44956(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p1.so", "./issue44956/plugin1.go")
+ goCmd(t, "build", "-buildmode=plugin", "-o", "issue44956p2.so", "./issue44956/plugin2.go")
+ goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
+ run(t, "./issue44956.exe")
+}
+
+func TestIssue52937(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "issue52937.so", "./issue52937/main.go")
+}
+
+func TestIssue53989(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "issue53989.so", "./issue53989/plugin.go")
+ goCmd(t, "build", "-o", "issue53989.exe", "./issue53989/main.go")
+ run(t, "./issue53989.exe")
+}
+
+func TestForkExec(t *testing.T) {
+ // Issue 38824: importing the plugin package causes it hang in forkExec on darwin.
+ globalSkip(t)
+
+ t.Parallel()
+ goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go")
+
+ var cmd *exec.Cmd
+ done := make(chan int, 1)
+
+ go func() {
+ for i := 0; i < 100; i++ {
+ cmd = exec.Command("./forkexec.exe", "1")
+ err := cmd.Run()
+ if err != nil {
+ t.Errorf("running command failed: %v", err)
+ break
+ }
+ }
+ done <- 1
+ }()
+ select {
+ case <-done:
+ case <-time.After(5 * time.Minute):
+ cmd.Process.Kill()
+ t.Fatalf("subprocess hang")
+ }
+}
+
+func TestSymbolNameMangle(t *testing.T) {
+ // Issue 58800: generic function name may contain weird characters
+ // that confuse the external linker.
+ // Issue 62098: the name mangling code doesn't handle some string
+ // symbols correctly.
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "mangle.so", "./mangle/plugin.go")
+}
+
+func TestIssue62430(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=plugin", "-o", "issue62430.so", "./issue62430/plugin.go")
+ goCmd(t, "build", "-o", "issue62430.exe", "./issue62430/main.go")
+ run(t, "./issue62430.exe")
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/checkdwarf/main.go b/src/cmd/cgo/internal/testplugin/testdata/checkdwarf/main.go
new file mode 100644
index 0000000..7886c83
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/checkdwarf/main.go
@@ -0,0 +1,106 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Usage:
+//
+// checkdwarf <exe> <suffix>
+//
+// Opens <exe>, which must be an executable or a library and checks that
+// there is an entry in .debug_info whose name ends in <suffix>
+
+package main
+
+import (
+ "debug/dwarf"
+ "debug/elf"
+ "debug/macho"
+ "debug/pe"
+ "fmt"
+ "os"
+ "strings"
+)
+
+func usage() {
+ fmt.Fprintf(os.Stderr, "checkdwarf executable-or-library DIE-suffix\n")
+}
+
+type dwarfer interface {
+ DWARF() (*dwarf.Data, error)
+}
+
+func openElf(path string) dwarfer {
+ exe, err := elf.Open(path)
+ if err != nil {
+ return nil
+ }
+ return exe
+}
+
+func openMacho(path string) dwarfer {
+ exe, err := macho.Open(path)
+ if err != nil {
+ return nil
+ }
+ return exe
+}
+
+func openPE(path string) dwarfer {
+ exe, err := pe.Open(path)
+ if err != nil {
+ return nil
+ }
+ return exe
+}
+
+func main() {
+ if len(os.Args) != 3 {
+ usage()
+ }
+
+ exePath := os.Args[1]
+ dieSuffix := os.Args[2]
+
+ var exe dwarfer
+
+ for _, openfn := range []func(string) dwarfer{openMacho, openPE, openElf} {
+ exe = openfn(exePath)
+ if exe != nil {
+ break
+ }
+ }
+
+ if exe == nil {
+ fmt.Fprintf(os.Stderr, "could not open %s\n", exePath)
+ os.Exit(1)
+ }
+
+ data, err := exe.DWARF()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: error opening DWARF: %v\n", exePath, err)
+ os.Exit(1)
+ }
+
+ rdr := data.Reader()
+ for {
+ e, err := rdr.Next()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "%s: error reading DWARF: %v\n", exePath, err)
+ os.Exit(1)
+ }
+ if e == nil {
+ break
+ }
+ name, hasname := e.Val(dwarf.AttrName).(string)
+ if !hasname {
+ continue
+ }
+ if strings.HasSuffix(name, dieSuffix) {
+ // found
+ os.Exit(0)
+ }
+ }
+
+ fmt.Fprintf(os.Stderr, "%s: no entry with a name ending in %q was found\n", exePath, dieSuffix)
+ os.Exit(1)
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/common/common.go b/src/cmd/cgo/internal/testplugin/testdata/common/common.go
new file mode 100644
index 0000000..b064e6b
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/common/common.go
@@ -0,0 +1,11 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package common
+
+var X int
+
+func init() {
+ X = 3
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/forkexec/main.go b/src/cmd/cgo/internal/testplugin/testdata/forkexec/main.go
new file mode 100644
index 0000000..3169ff5
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/forkexec/main.go
@@ -0,0 +1,30 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "os"
+ "os/exec"
+ _ "plugin"
+ "sync"
+)
+
+func main() {
+ if os.Args[1] != "1" {
+ return
+ }
+
+ var wg sync.WaitGroup
+ for i := 0; i < 8; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ // does not matter what we exec, just exec itself
+ cmd := exec.Command("./forkexec.exe", "0")
+ cmd.Run()
+ }()
+ }
+ wg.Wait()
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/host/host.go b/src/cmd/cgo/internal/testplugin/testdata/host/host.go
new file mode 100644
index 0000000..a379932
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/host/host.go
@@ -0,0 +1,176 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "log"
+ "path/filepath"
+ "plugin"
+ "strings"
+
+ "testplugin/common"
+)
+
+func init() {
+ common.X *= 5
+}
+
+// testUnnamed tests that two plugins built with .go files passed on
+// the command line do not have overlapping symbols. That is,
+// unnamed1.so/FuncInt and unnamed2.so/FuncInt should be distinct functions.
+func testUnnamed() {
+ p, err := plugin.Open("unnamed1.so")
+ if err != nil {
+ log.Fatalf(`plugin.Open("unnamed1.so"): %v`, err)
+ }
+ fn, err := p.Lookup("FuncInt")
+ if err != nil {
+ log.Fatalf(`unnamed1.so: Lookup("FuncInt") failed: %v`, err)
+ }
+ if got, want := fn.(func() int)(), 1; got != want {
+ log.Fatalf("unnamed1.so: FuncInt()=%d, want %d", got, want)
+ }
+
+ p, err = plugin.Open("unnamed2.so")
+ if err != nil {
+ log.Fatalf(`plugin.Open("unnamed2.so"): %v`, err)
+ }
+ fn, err = p.Lookup("FuncInt")
+ if err != nil {
+ log.Fatalf(`unnamed2.so: Lookup("FuncInt") failed: %v`, err)
+ }
+ if got, want := fn.(func() int)(), 2; got != want {
+ log.Fatalf("unnamed2.so: FuncInt()=%d, want %d", got, want)
+ }
+}
+
+func main() {
+ if got, want := common.X, 3*5; got != want {
+ log.Fatalf("before plugin load common.X=%d, want %d", got, want)
+ }
+
+ p, err := plugin.Open("plugin1.so")
+ if err != nil {
+ log.Fatalf("plugin.Open failed: %v", err)
+ }
+
+ const wantX = 3 * 5 * 7
+ if got := common.X; got != wantX {
+ log.Fatalf("after plugin load common.X=%d, want %d", got, wantX)
+ }
+
+ seven, err := p.Lookup("Seven")
+ if err != nil {
+ log.Fatalf(`Lookup("Seven") failed: %v`, err)
+ }
+ if got, want := *seven.(*int), 7; got != want {
+ log.Fatalf("plugin1.Seven=%d, want %d", got, want)
+ }
+
+ readFunc, err := p.Lookup("ReadCommonX")
+ if err != nil {
+ log.Fatalf(`plugin1.Lookup("ReadCommonX") failed: %v`, err)
+ }
+ if got := readFunc.(func() int)(); got != wantX {
+ log.Fatalf("plugin1.ReadCommonX()=%d, want %d", got, wantX)
+ }
+
+ // sub/plugin1.so is a different plugin with the same name as
+ // the already loaded plugin. It also depends on common. Test
+ // that we can load the different plugin, it is actually
+ // different, and that it sees the same common package.
+ subpPath, err := filepath.Abs("sub/plugin1.so")
+ if err != nil {
+ log.Fatalf("filepath.Abs(%q) failed: %v", subpPath, err)
+ }
+ subp, err := plugin.Open(subpPath)
+ if err != nil {
+ log.Fatalf("plugin.Open(%q) failed: %v", subpPath, err)
+ }
+
+ funcVar, err := subp.Lookup("FuncVar")
+ if err != nil {
+ log.Fatalf(`sub/plugin1.Lookup("FuncVar") failed: %v`, err)
+ }
+ called := false
+ *funcVar.(*func()) = func() {
+ called = true
+ }
+
+ readFunc, err = subp.Lookup("ReadCommonX")
+ if err != nil {
+ log.Fatalf(`sub/plugin1.Lookup("ReadCommonX") failed: %v`, err)
+ }
+ if got := readFunc.(func() int)(); got != wantX {
+ log.Fatalf("sub/plugin1.ReadCommonX()=%d, want %d", got, wantX)
+ }
+ if !called {
+ log.Fatal("calling ReadCommonX did not call FuncVar")
+ }
+
+ subf, err := subp.Lookup("F")
+ if err != nil {
+ log.Fatalf(`sub/plugin1.Lookup("F") failed: %v`, err)
+ }
+ if gotf := subf.(func() int)(); gotf != 17 {
+ log.Fatalf(`sub/plugin1.F()=%d, want 17`, gotf)
+ }
+ f, err := p.Lookup("F")
+ if err != nil {
+ log.Fatalf(`plugin1.Lookup("F") failed: %v`, err)
+ }
+ if gotf := f.(func() int)(); gotf != 3 {
+ log.Fatalf(`plugin1.F()=%d, want 17`, gotf)
+ }
+
+ p2, err := plugin.Open("plugin2.so")
+ if err != nil {
+ log.Fatalf("plugin.Open failed: %v", err)
+ }
+ // Check that plugin2's init function was called, and
+ // that it modifies the same global variable as the host.
+ if got, want := common.X, 2; got != want {
+ log.Fatalf("after loading plugin2, common.X=%d, want %d", got, want)
+ }
+
+ _, err = plugin.Open("plugin2-dup.so")
+ if err == nil {
+ log.Fatal(`plugin.Open("plugin2-dup.so"): duplicate open should have failed`)
+ }
+ if s := err.Error(); !strings.Contains(s, "already loaded") {
+ log.Fatal(`plugin.Open("plugin2.so"): error does not mention "already loaded"`)
+ }
+
+ _, err = plugin.Open("plugin-mismatch.so")
+ if err == nil {
+ log.Fatal(`plugin.Open("plugin-mismatch.so"): should have failed`)
+ }
+ if s := err.Error(); !strings.Contains(s, "different version") {
+ log.Fatalf(`plugin.Open("plugin-mismatch.so"): error does not mention "different version": %v`, s)
+ }
+
+ _, err = plugin.Open("plugin2-dup.so")
+ if err == nil {
+ log.Fatal(`plugin.Open("plugin2-dup.so"): duplicate open after bad plugin should have failed`)
+ }
+ _, err = plugin.Open("plugin2.so")
+ if err != nil {
+ log.Fatalf(`plugin.Open("plugin2.so"): second open with same name failed: %v`, err)
+ }
+
+ // Test that unexported types with the same names in
+ // different plugins do not interfere with each other.
+ //
+ // See Issue #21386.
+ UnexportedNameReuse, _ := p.Lookup("UnexportedNameReuse")
+ UnexportedNameReuse.(func())()
+ UnexportedNameReuse, _ = p2.Lookup("UnexportedNameReuse")
+ UnexportedNameReuse.(func())()
+
+ testUnnamed()
+
+ fmt.Println("PASS")
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/iface/main.go b/src/cmd/cgo/internal/testplugin/testdata/iface/main.go
new file mode 100644
index 0000000..c04f288
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/iface/main.go
@@ -0,0 +1,47 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "log"
+ "plugin"
+
+ "testplugin/iface_i"
+)
+
+func main() {
+ a, err := plugin.Open("iface_a.so")
+ if err != nil {
+ log.Fatalf(`plugin.Open("iface_a.so"): %v`, err)
+ }
+ b, err := plugin.Open("iface_b.so")
+ if err != nil {
+ log.Fatalf(`plugin.Open("iface_b.so"): %v`, err)
+ }
+
+ af, err := a.Lookup("F")
+ if err != nil {
+ log.Fatalf(`a.Lookup("F") failed: %v`, err)
+ }
+ bf, err := b.Lookup("F")
+ if err != nil {
+ log.Fatalf(`b.Lookup("F") failed: %v`, err)
+ }
+ if af.(func() interface{})() != bf.(func() interface{})() {
+ panic("empty interfaces not equal")
+ }
+
+ ag, err := a.Lookup("G")
+ if err != nil {
+ log.Fatalf(`a.Lookup("G") failed: %v`, err)
+ }
+ bg, err := b.Lookup("G")
+ if err != nil {
+ log.Fatalf(`b.Lookup("G") failed: %v`, err)
+ }
+ if ag.(func() iface_i.I)() != bg.(func() iface_i.I)() {
+ panic("nonempty interfaces not equal")
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/iface_a/a.go b/src/cmd/cgo/internal/testplugin/testdata/iface_a/a.go
new file mode 100644
index 0000000..357f7e8
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/iface_a/a.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "testplugin/iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/iface_b/b.go b/src/cmd/cgo/internal/testplugin/testdata/iface_b/b.go
new file mode 100644
index 0000000..357f7e8
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/iface_b/b.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "testplugin/iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/iface_i/i.go b/src/cmd/cgo/internal/testplugin/testdata/iface_i/i.go
new file mode 100644
index 0000000..31c8038
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/iface_i/i.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package iface_i
+
+type I interface {
+ M()
+}
+
+type T struct {
+}
+
+func (t *T) M() {
+}
+
+// *T implements I
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue18584/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue18584/main.go
new file mode 100644
index 0000000..c280fd4
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue18584/main.go
@@ -0,0 +1,23 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "plugin"
+
+func main() {
+ p, err := plugin.Open("plugin.so")
+ if err != nil {
+ panic(err)
+ }
+
+ sym, err := p.Lookup("G")
+ if err != nil {
+ panic(err)
+ }
+ g := sym.(func() bool)
+ if !g() {
+ panic("expected types to match, Issue #18584")
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue18584/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue18584/plugin.go
new file mode 100644
index 0000000..be0868d
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue18584/plugin.go
@@ -0,0 +1,19 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "reflect"
+
+type C struct {
+}
+
+func F(c *C) *C {
+ return nil
+}
+
+func G() bool {
+ var c *C
+ return reflect.TypeOf(F).Out(0) == reflect.TypeOf(c)
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue18676/dynamodbstreamsevt/definition.go b/src/cmd/cgo/internal/testplugin/testdata/issue18676/dynamodbstreamsevt/definition.go
new file mode 100644
index 0000000..70fd054
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue18676/dynamodbstreamsevt/definition.go
@@ -0,0 +1,13 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package dynamodbstreamsevt
+
+import "encoding/json"
+
+var foo json.RawMessage
+
+type Event struct{}
+
+func (e *Event) Dummy() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue18676/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue18676/main.go
new file mode 100644
index 0000000..471f3d9
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue18676/main.go
@@ -0,0 +1,32 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The bug happened like this:
+// 1. The main binary adds an itab for *json.UnsupportedValueError / error
+// (concrete type / interface type). This itab goes in hash bucket 0x111.
+// 2. The plugin adds that same itab again. That makes a cycle in the itab
+// chain rooted at hash bucket 0x111.
+// 3. The main binary then asks for the itab for *dynamodbstreamsevt.Event /
+// json.Unmarshaler. This itab happens to also live in bucket 0x111.
+// The lookup code goes into an infinite loop searching for this itab.
+//
+// The code is carefully crafted so that the two itabs are both from the
+// same bucket, and so that the second itab doesn't exist in
+// the itab hashmap yet (so the entire linked list must be searched).
+package main
+
+import (
+ "encoding/json"
+ "plugin"
+ "testplugin/issue18676/dynamodbstreamsevt"
+)
+
+func main() {
+ plugin.Open("plugin.so")
+
+ var x interface{} = (*dynamodbstreamsevt.Event)(nil)
+ if _, ok := x.(json.Unmarshaler); !ok {
+ println("something")
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue18676/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue18676/plugin.go
new file mode 100644
index 0000000..e7fc74f
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue18676/plugin.go
@@ -0,0 +1,11 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "C"
+
+import "testplugin/issue18676/dynamodbstreamsevt"
+
+func F(evt *dynamodbstreamsevt.Event) {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue19418/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue19418/main.go
new file mode 100644
index 0000000..2ec9f9a
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue19418/main.go
@@ -0,0 +1,29 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "plugin"
+)
+
+func main() {
+ p, err := plugin.Open("plugin.so")
+ if err != nil {
+ panic(err)
+ }
+
+ val, err := p.Lookup("Val")
+ if err != nil {
+ panic(err)
+ }
+ got := *val.(*string)
+ const want = "linkstr"
+ if got != want {
+ fmt.Fprintf(os.Stderr, "issue19418 value is %q, want %q\n", got, want)
+ os.Exit(2)
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue19418/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue19418/plugin.go
new file mode 100644
index 0000000..fe93b16
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue19418/plugin.go
@@ -0,0 +1,7 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+var Val = "val-unset"
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue19529/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue19529/plugin.go
new file mode 100644
index 0000000..ad2df6c
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue19529/plugin.go
@@ -0,0 +1,15 @@
+package main
+
+import (
+ "reflect"
+)
+
+type Foo struct {
+ Bar string `json:"Bar@baz,omitempty"`
+}
+
+func F() {
+ println(reflect.TypeOf(Foo{}).Field(0).Tag)
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue19534/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue19534/main.go
new file mode 100644
index 0000000..de263b6
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue19534/main.go
@@ -0,0 +1,23 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "plugin"
+
+func main() {
+ p, err := plugin.Open("plugin.so")
+ if err != nil {
+ panic(err)
+ }
+
+ sym, err := p.Lookup("Foo")
+ if err != nil {
+ panic(err)
+ }
+ f := sym.(func() int)
+ if f() != 42 {
+ panic("expected f() == 42")
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue19534/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue19534/plugin.go
new file mode 100644
index 0000000..582d333
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue19534/plugin.go
@@ -0,0 +1,9 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func Foo() int {
+ return 42
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue22175/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue22175/main.go
new file mode 100644
index 0000000..9be9bab
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue22175/main.go
@@ -0,0 +1,28 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "plugin"
+)
+
+func main() {
+ p2, err := plugin.Open("issue22175_plugin1.so")
+ if err != nil {
+ panic(err)
+ }
+ f, err := p2.Lookup("F")
+ if err != nil {
+ panic(err)
+ }
+ got := f.(func() int)()
+ const want = 971
+ if got != want {
+ fmt.Fprintf(os.Stderr, "issue22175: F()=%d, want %d", got, want)
+ os.Exit(1)
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue22175/plugin1.go b/src/cmd/cgo/internal/testplugin/testdata/issue22175/plugin1.go
new file mode 100644
index 0000000..5ae6cb6
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue22175/plugin1.go
@@ -0,0 +1,21 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "plugin"
+
+func F() int {
+ p2, err := plugin.Open("issue22175_plugin2.so")
+ if err != nil {
+ panic(err)
+ }
+ g, err := p2.Lookup("G")
+ if err != nil {
+ panic(err)
+ }
+ return g.(func() int)()
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue22175/plugin2.go b/src/cmd/cgo/internal/testplugin/testdata/issue22175/plugin2.go
new file mode 100644
index 0000000..f387a19
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue22175/plugin2.go
@@ -0,0 +1,9 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func G() int { return 971 }
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue22295.pkg/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue22295.pkg/main.go
new file mode 100644
index 0000000..44b2a21
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue22295.pkg/main.go
@@ -0,0 +1,28 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+import (
+ "log"
+ "plugin"
+)
+
+func main() {
+ p, err := plugin.Open("issue.22295.so")
+ if err != nil {
+ log.Fatal(err)
+ }
+ f, err := p.Lookup("F")
+ if err != nil {
+ log.Fatal(err)
+ }
+ const want = 2503
+ got := f.(func() int)()
+ if got != want {
+ log.Fatalf("got %d, want %d", got, want)
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue22295.pkg/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue22295.pkg/plugin.go
new file mode 100644
index 0000000..46b08a4
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue22295.pkg/plugin.go
@@ -0,0 +1,16 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+var f *int
+
+func init() {
+ f = new(int)
+ *f = 2503
+}
+
+func F() int { return *f }
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue24351/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue24351/main.go
new file mode 100644
index 0000000..4107adf
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue24351/main.go
@@ -0,0 +1,21 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "plugin"
+
+func main() {
+ p, err := plugin.Open("issue24351.so")
+ if err != nil {
+ panic(err)
+ }
+ f, err := p.Lookup("B")
+ if err != nil {
+ panic(err)
+ }
+ c := make(chan bool)
+ f.(func(chan bool))(c)
+ <-c
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue24351/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue24351/plugin.go
new file mode 100644
index 0000000..db17e0a
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue24351/plugin.go
@@ -0,0 +1,14 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "fmt"
+
+func B(c chan bool) {
+ go func() {
+ fmt.Println(1.5)
+ c <- true
+ }()
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue25756/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue25756/main.go
new file mode 100644
index 0000000..817daf4
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue25756/main.go
@@ -0,0 +1,52 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Run the game of life in C using Go for parallelization.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "plugin"
+)
+
+const MAXDIM = 100
+
+var dim = flag.Int("dim", 16, "board dimensions")
+var gen = flag.Int("gen", 10, "generations")
+
+func main() {
+ flag.Parse()
+
+ var a [MAXDIM * MAXDIM]int32
+ for i := 2; i < *dim; i += 8 {
+ for j := 2; j < *dim-3; j += 8 {
+ for y := 0; y < 3; y++ {
+ a[i**dim+j+y] = 1
+ }
+ }
+ }
+
+ p, err := plugin.Open("life.so")
+ if err != nil {
+ panic(err)
+ }
+ f, err := p.Lookup("Run")
+ if err != nil {
+ panic(err)
+ }
+ f.(func(int, int, int, []int32))(*gen, *dim, *dim, a[:])
+
+ for i := 0; i < *dim; i++ {
+ for j := 0; j < *dim; j++ {
+ if a[i**dim+j] == 0 {
+ fmt.Print(" ")
+ } else {
+ fmt.Print("X")
+ }
+ }
+ fmt.Print("\n")
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/c-life.c b/src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/c-life.c
new file mode 100644
index 0000000..f853163
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/c-life.c
@@ -0,0 +1,56 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <assert.h>
+#include "life.h"
+#include "_cgo_export.h"
+
+const int MYCONST = 0;
+
+// Do the actual manipulation of the life board in C. This could be
+// done easily in Go, we are just using C for demonstration
+// purposes.
+void
+Step(int x, int y, int *a, int *n)
+{
+ struct GoStart_return r;
+
+ // Use Go to start 4 goroutines each of which handles 1/4 of the
+ // board.
+ r = GoStart(0, x, y, 0, x / 2, 0, y / 2, a, n);
+ assert(r.r0 == 0 && r.r1 == 100); // test multiple returns
+ r = GoStart(1, x, y, x / 2, x, 0, y / 2, a, n);
+ assert(r.r0 == 1 && r.r1 == 101); // test multiple returns
+ GoStart(2, x, y, 0, x / 2, y / 2, y, a, n);
+ GoStart(3, x, y, x / 2, x, y / 2, y, a, n);
+ GoWait(0);
+ GoWait(1);
+ GoWait(2);
+ GoWait(3);
+}
+
+// The actual computation. This is called in parallel.
+void
+DoStep(int xdim, int ydim, int xstart, int xend, int ystart, int yend, int *a, int *n)
+{
+ int x, y, c, i, j;
+
+ for(x = xstart; x < xend; x++) {
+ for(y = ystart; y < yend; y++) {
+ c = 0;
+ for(i = -1; i <= 1; i++) {
+ for(j = -1; j <= 1; j++) {
+ if(x+i >= 0 && x+i < xdim &&
+ y+j >= 0 && y+j < ydim &&
+ (i != 0 || j != 0))
+ c += a[(x+i)*xdim + (y+j)] != 0;
+ }
+ }
+ if(c == 3 || (c == 2 && a[x*xdim + y] != 0))
+ n[x*xdim + y] = 1;
+ else
+ n[x*xdim + y] = 0;
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/life.go b/src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/life.go
new file mode 100644
index 0000000..468bc6f
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/life.go
@@ -0,0 +1,40 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// #include "life.h"
+import "C"
+
+import "unsafe"
+
+func Run(gen, x, y int, a []int32) {
+ n := make([]int32, x*y)
+ for i := 0; i < gen; i++ {
+ C.Step(C.int(x), C.int(y), (*C.int)(unsafe.Pointer(&a[0])), (*C.int)(unsafe.Pointer(&n[0])))
+ copy(a, n)
+ }
+}
+
+// Keep the channels visible from Go.
+var chans [4]chan bool
+
+// Double return value is just for testing.
+//
+//export GoStart
+func GoStart(i, xdim, ydim, xstart, xend, ystart, yend C.int, a *C.int, n *C.int) (int, int) {
+ c := make(chan bool, int(C.MYCONST))
+ go func() {
+ C.DoStep(xdim, ydim, xstart, xend, ystart, yend, a, n)
+ c <- true
+ }()
+ chans[i] = c
+ return int(i), int(i + 100)
+}
+
+//export GoWait
+func GoWait(i C.int) {
+ <-chans[i]
+ chans[i] = nil
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/life.h b/src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/life.h
new file mode 100644
index 0000000..11d2b97
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue25756/plugin/life.h
@@ -0,0 +1,7 @@
+// Copyright 2010 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+extern void Step(int, int, int *, int *);
+extern void DoStep(int, int, int, int, int, int, int *, int *);
+extern const int MYCONST;
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue44956/base/base.go b/src/cmd/cgo/internal/testplugin/testdata/issue44956/base/base.go
new file mode 100644
index 0000000..609aa0d
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue44956/base/base.go
@@ -0,0 +1,7 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package base
+
+var X = &map[int]int{123: 456}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue44956/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue44956/main.go
new file mode 100644
index 0000000..287a605
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue44956/main.go
@@ -0,0 +1,47 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 44956: writable static temp is not exported correctly.
+// In the test below, package base is
+//
+// X = &map{...}
+//
+// which compiles to
+//
+// X = &stmp // static
+// stmp = makemap(...) // in init function
+//
+// plugin1 and plugin2 both import base. plugin1 doesn't use
+// base.X, so that symbol is deadcoded in plugin1.
+//
+// plugin1 is loaded first. base.init runs at that point, which
+// initialize base.stmp.
+//
+// plugin2 is then loaded. base.init already ran, so it doesn't run
+// again. When base.stmp is not exported, plugin2's base.X points to
+// its own private base.stmp, which is not initialized, fail.
+
+package main
+
+import "plugin"
+
+func main() {
+ _, err := plugin.Open("issue44956p1.so")
+ if err != nil {
+ panic("FAIL")
+ }
+
+ p2, err := plugin.Open("issue44956p2.so")
+ if err != nil {
+ panic("FAIL")
+ }
+ f, err := p2.Lookup("F")
+ if err != nil {
+ panic("FAIL")
+ }
+ x := f.(func() *map[int]int)()
+ if x == nil || (*x)[123] != 456 {
+ panic("FAIL")
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue44956/plugin1.go b/src/cmd/cgo/internal/testplugin/testdata/issue44956/plugin1.go
new file mode 100644
index 0000000..499fa31
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue44956/plugin1.go
@@ -0,0 +1,9 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import _ "testplugin/issue44956/base"
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue44956/plugin2.go b/src/cmd/cgo/internal/testplugin/testdata/issue44956/plugin2.go
new file mode 100644
index 0000000..a73542c
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue44956/plugin2.go
@@ -0,0 +1,11 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "testplugin/issue44956/base"
+
+func F() *map[int]int { return base.X }
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue52937/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue52937/main.go
new file mode 100644
index 0000000..66f09ef
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue52937/main.go
@@ -0,0 +1,9 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func main() {}
+func F[T any]() {}
+func G[T any](T) {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue53989/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue53989/main.go
new file mode 100644
index 0000000..6907dfd
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue53989/main.go
@@ -0,0 +1,32 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 53989: the use of jump table caused a function
+// from the plugin jumps in the middle of the function
+// to the function with the same name in the main
+// executable. As these two functions may be compiled
+// differently as plugin needs to be PIC, this causes
+// crash.
+
+package main
+
+import (
+ "plugin"
+
+ "testplugin/issue53989/p"
+)
+
+func main() {
+ p.Square(7) // call the function in main executable
+
+ p, err := plugin.Open("issue53989.so")
+ if err != nil {
+ panic(err)
+ }
+ f, err := p.Lookup("Square")
+ if err != nil {
+ panic(err)
+ }
+ f.(func(int))(7) // call the plugin one
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue53989/p/p.go b/src/cmd/cgo/internal/testplugin/testdata/issue53989/p/p.go
new file mode 100644
index 0000000..02567c1
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue53989/p/p.go
@@ -0,0 +1,52 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+import (
+ "fmt"
+ "runtime"
+)
+
+var y int
+
+//go:noinline
+func Square(x int) {
+ var pc0, pc1 [1]uintptr
+ runtime.Callers(1, pc0[:]) // get PC at entry
+
+ // a switch using jump table
+ switch x {
+ case 1:
+ y = 1
+ case 2:
+ y = 4
+ case 3:
+ y = 9
+ case 4:
+ y = 16
+ case 5:
+ y = 25
+ case 6:
+ y = 36
+ case 7:
+ y = 49
+ case 8:
+ y = 64
+ default:
+ panic("too large")
+ }
+
+ // check PC is in the same function
+ runtime.Callers(1, pc1[:])
+ if pc1[0] < pc0[0] || pc1[0] > pc0[0]+1000000 {
+ fmt.Printf("jump across DSO boundary. pc0=%x, pc1=%x\n", pc0[0], pc1[0])
+ panic("FAIL")
+ }
+
+ if y != x*x {
+ fmt.Printf("x=%d y=%d!=%d\n", x, y, x*x)
+ panic("FAIL")
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue53989/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue53989/plugin.go
new file mode 100644
index 0000000..a753ee4
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue53989/plugin.go
@@ -0,0 +1,13 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "testplugin/issue53989/p"
+
+func Square(x int) { // export Square for plugin
+ p.Square(x)
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue62430/main.go b/src/cmd/cgo/internal/testplugin/testdata/issue62430/main.go
new file mode 100644
index 0000000..8010840
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue62430/main.go
@@ -0,0 +1,35 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 62430: a program that uses plugins may appear
+// to have no references to an initialized global map variable defined
+// in some stdlib package (ex: unicode), however there
+// may be references to that map var from a plugin that
+// gets loaded.
+
+package main
+
+import (
+ "fmt"
+ "plugin"
+ "unicode"
+)
+
+func main() {
+ p, err := plugin.Open("issue62430.so")
+ if err != nil {
+ panic(err)
+ }
+ s, err := p.Lookup("F")
+ if err != nil {
+ panic(err)
+ }
+
+ f := s.(func(string) *unicode.RangeTable)
+ if f("C") == nil {
+ panic("unicode.Categories not properly initialized")
+ } else {
+ fmt.Println("unicode.Categories properly initialized")
+ }
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/issue62430/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/issue62430/plugin.go
new file mode 100644
index 0000000..e42cd8b
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/issue62430/plugin.go
@@ -0,0 +1,11 @@
+package main
+
+import (
+ "unicode"
+)
+
+func F(s string) *unicode.RangeTable {
+ return unicode.Categories[s]
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/mangle/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/mangle/plugin.go
new file mode 100644
index 0000000..e1ccb70
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/mangle/plugin.go
@@ -0,0 +1,38 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test cases for symbol name mangling.
+
+package main
+
+import (
+ "fmt"
+ "strings"
+)
+
+// Issue 58800:
+// Instantiated function name may contain weird characters
+// that confuse the external linker, so it needs to be
+// mangled.
+type S struct {
+ X int `parser:"|@@)"`
+}
+
+//go:noinline
+func F[T any]() {}
+
+func P() {
+ F[S]()
+}
+
+// Issue 62098: the name mangling code doesn't handle some string
+// symbols correctly.
+func G(id string) error {
+ if strings.ContainsAny(id, "&$@;/:+,?\\{^}%`]\">[~<#|") {
+ return fmt.Errorf("invalid")
+ }
+ return nil
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/method/main.go b/src/cmd/cgo/internal/testplugin/testdata/method/main.go
new file mode 100644
index 0000000..5e9189b
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/method/main.go
@@ -0,0 +1,26 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 42579: methods of symbols exported from plugin must be live.
+
+package main
+
+import (
+ "plugin"
+ "reflect"
+)
+
+func main() {
+ p, err := plugin.Open("plugin.so")
+ if err != nil {
+ panic(err)
+ }
+
+ x, err := p.Lookup("X")
+ if err != nil {
+ panic(err)
+ }
+
+ reflect.ValueOf(x).Elem().MethodByName("M").Call(nil)
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/method/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/method/plugin.go
new file mode 100644
index 0000000..240edd3
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/method/plugin.go
@@ -0,0 +1,13 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+func main() {}
+
+type T int
+
+func (T) M() { println("M") }
+
+var X T
diff --git a/src/cmd/cgo/internal/testplugin/testdata/method2/main.go b/src/cmd/cgo/internal/testplugin/testdata/method2/main.go
new file mode 100644
index 0000000..89afbda
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/method2/main.go
@@ -0,0 +1,32 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// A type can be passed to a plugin and converted to interface
+// there. So its methods need to be live.
+
+package main
+
+import (
+ "plugin"
+
+ "testplugin/method2/p"
+)
+
+var t p.T
+
+type I interface{ M() }
+
+func main() {
+ pl, err := plugin.Open("method2.so")
+ if err != nil {
+ panic(err)
+ }
+
+ f, err := pl.Lookup("F")
+ if err != nil {
+ panic(err)
+ }
+
+ f.(func(p.T) interface{})(t).(I).M()
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/method2/p/p.go b/src/cmd/cgo/internal/testplugin/testdata/method2/p/p.go
new file mode 100644
index 0000000..acb526a
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/method2/p/p.go
@@ -0,0 +1,9 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type T int
+
+func (T) M() { println("M") }
diff --git a/src/cmd/cgo/internal/testplugin/testdata/method2/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/method2/plugin.go
new file mode 100644
index 0000000..6198e76
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/method2/plugin.go
@@ -0,0 +1,11 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "testplugin/method2/p"
+
+func main() {}
+
+func F(t p.T) interface{} { return t }
diff --git a/src/cmd/cgo/internal/testplugin/testdata/method3/main.go b/src/cmd/cgo/internal/testplugin/testdata/method3/main.go
new file mode 100644
index 0000000..a3a5171
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/method3/main.go
@@ -0,0 +1,32 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// An unexported method can be reachable from the plugin via interface
+// when a package is shared. So it need to be live.
+
+package main
+
+import (
+ "plugin"
+
+ "testplugin/method3/p"
+)
+
+var i p.I
+
+func main() {
+ pl, err := plugin.Open("method3.so")
+ if err != nil {
+ panic(err)
+ }
+
+ f, err := pl.Lookup("F")
+ if err != nil {
+ panic(err)
+ }
+
+ f.(func())()
+
+ i = p.T(123)
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/method3/p/p.go b/src/cmd/cgo/internal/testplugin/testdata/method3/p/p.go
new file mode 100644
index 0000000..f72f7c7
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/method3/p/p.go
@@ -0,0 +1,17 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type T int
+
+func (T) m() { println("m") }
+
+type I interface{ m() }
+
+func F() {
+ i.m()
+}
+
+var i I = T(123)
diff --git a/src/cmd/cgo/internal/testplugin/testdata/method3/plugin.go b/src/cmd/cgo/internal/testplugin/testdata/method3/plugin.go
new file mode 100644
index 0000000..bd25b31
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/method3/plugin.go
@@ -0,0 +1,11 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "testplugin/method3/p"
+
+func main() {}
+
+func F() { p.F() }
diff --git a/src/cmd/cgo/internal/testplugin/testdata/plugin1/plugin1.go b/src/cmd/cgo/internal/testplugin/testdata/plugin1/plugin1.go
new file mode 100644
index 0000000..d29d674
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/plugin1/plugin1.go
@@ -0,0 +1,57 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// // No C code required.
+import "C"
+
+import (
+ "reflect"
+
+ "testplugin/common"
+)
+
+func F() int {
+ _ = make([]byte, 1<<21) // trigger stack unwind, Issue #18190.
+ return 3
+}
+
+func ReadCommonX() int {
+ return common.X
+}
+
+var Seven int
+
+func call(fn func()) {
+ fn()
+}
+
+func g() {
+ common.X *= Seven
+}
+
+func init() {
+ Seven = 7
+ call(g)
+}
+
+type sameNameReusedInPlugins struct {
+ X string
+}
+
+type sameNameHolder struct {
+ F *sameNameReusedInPlugins
+}
+
+func UnexportedNameReuse() {
+ h := sameNameHolder{}
+ v := reflect.ValueOf(&h).Elem().Field(0)
+ newval := reflect.New(v.Type().Elem())
+ v.Set(newval)
+}
+
+func main() {
+ panic("plugin1.main called")
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/plugin2/plugin2.go b/src/cmd/cgo/internal/testplugin/testdata/plugin2/plugin2.go
new file mode 100644
index 0000000..31ed642
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/plugin2/plugin2.go
@@ -0,0 +1,44 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+//#include <errno.h>
+//#include <string.h>
+import "C"
+
+// #include
+// void cfunc() {} // uses cgo_topofstack
+
+import (
+ "reflect"
+ "strings"
+
+ "testplugin/common"
+)
+
+func init() {
+ _ = strings.NewReplacer() // trigger stack unwind, Issue #18190.
+ C.strerror(C.EIO) // uses cgo_topofstack
+ common.X = 2
+}
+
+type sameNameReusedInPlugins struct {
+ X string
+}
+
+type sameNameHolder struct {
+ F *sameNameReusedInPlugins
+}
+
+func UnexportedNameReuse() {
+ h := sameNameHolder{}
+ v := reflect.ValueOf(&h).Elem().Field(0)
+ newval := reflect.New(v.Type().Elem())
+ v.Set(newval)
+}
+
+func main() {
+ panic("plugin1.main called")
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/sub/plugin1/plugin1.go b/src/cmd/cgo/internal/testplugin/testdata/sub/plugin1/plugin1.go
new file mode 100644
index 0000000..5f891b0
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/sub/plugin1/plugin1.go
@@ -0,0 +1,23 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// // No C code required.
+import "C"
+
+import "testplugin/common"
+
+func F() int { return 17 }
+
+var FuncVar = func() {}
+
+func ReadCommonX() int {
+ FuncVar()
+ return common.X
+}
+
+func main() {
+ panic("plugin1.main called")
+}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/unnamed1/main.go b/src/cmd/cgo/internal/testplugin/testdata/unnamed1/main.go
new file mode 100644
index 0000000..1620dc4
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/unnamed1/main.go
@@ -0,0 +1,25 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+// // No C code required.
+import "C"
+
+func FuncInt() int { return 1 }
+
+// Add a recursive type to check that type equality across plugins doesn't
+// crash. See https://golang.org/issues/19258
+func FuncRecursive() X { return X{} }
+
+type Y struct {
+ X *X
+}
+type X struct {
+ Y Y
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testplugin/testdata/unnamed2/main.go b/src/cmd/cgo/internal/testplugin/testdata/unnamed2/main.go
new file mode 100644
index 0000000..027ef64
--- /dev/null
+++ b/src/cmd/cgo/internal/testplugin/testdata/unnamed2/main.go
@@ -0,0 +1,23 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+// // No C code required.
+import "C"
+
+func FuncInt() int { return 2 }
+
+func FuncRecursive() X { return X{} }
+
+type Y struct {
+ X *X
+}
+type X struct {
+ Y Y
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testsanitizers/asan_test.go b/src/cmd/cgo/internal/testsanitizers/asan_test.go
new file mode 100644
index 0000000..7db3562
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/asan_test.go
@@ -0,0 +1,149 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux || (freebsd && amd64)
+
+package sanitizers_test
+
+import (
+ "fmt"
+ "internal/platform"
+ "internal/testenv"
+ "strings"
+ "testing"
+)
+
+func TestASAN(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ goos, err := goEnv("GOOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+ goarch, err := goEnv("GOARCH")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // The asan tests require support for the -asan option.
+ if !platform.ASanSupported(goos, goarch) {
+ t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch)
+ }
+ // The current implementation is only compatible with the ASan library from version
+ // v7 to v9 (See the description in src/runtime/asan/asan.go). Therefore, using the
+ // -asan option must use a compatible version of ASan library, which requires that
+ // the gcc version is not less than 7 and the clang version is not less than 9,
+ // otherwise a segmentation fault will occur.
+ if !compilerRequiredAsanVersion(goos, goarch) {
+ t.Skipf("skipping on %s/%s: too old version of compiler", goos, goarch)
+ }
+
+ t.Parallel()
+ requireOvercommit(t)
+ config := configure("address")
+ config.skipIfCSanitizerBroken(t)
+
+ mustRun(t, config.goCmd("build", "std"))
+
+ cases := []struct {
+ src string
+ memoryAccessError string
+ errorLocation string
+ experiments []string
+ }{
+ {src: "asan1_fail.go", memoryAccessError: "heap-use-after-free", errorLocation: "asan1_fail.go:25"},
+ {src: "asan2_fail.go", memoryAccessError: "heap-buffer-overflow", errorLocation: "asan2_fail.go:31"},
+ {src: "asan3_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan3_fail.go:13"},
+ {src: "asan4_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan4_fail.go:13"},
+ {src: "asan5_fail.go", memoryAccessError: "use-after-poison", errorLocation: "asan5_fail.go:18"},
+ {src: "asan_useAfterReturn.go"},
+ {src: "asan_unsafe_fail1.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail1.go:25"},
+ {src: "asan_unsafe_fail2.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail2.go:25"},
+ {src: "asan_unsafe_fail3.go", memoryAccessError: "use-after-poison", errorLocation: "asan_unsafe_fail3.go:18"},
+ {src: "asan_global1_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global1_fail.go:12"},
+ {src: "asan_global2_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global2_fail.go:19"},
+ {src: "asan_global3_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global3_fail.go:13"},
+ {src: "asan_global4_fail.go", memoryAccessError: "global-buffer-overflow", errorLocation: "asan_global4_fail.go:21"},
+ {src: "asan_global5.go"},
+ {src: "arena_fail.go", memoryAccessError: "use-after-poison", errorLocation: "arena_fail.go:26", experiments: []string{"arenas"}},
+ }
+ for _, tc := range cases {
+ tc := tc
+ name := strings.TrimSuffix(tc.src, ".go")
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ dir := newTempDir(t)
+ defer dir.RemoveAll(t)
+
+ outPath := dir.Join(name)
+ mustRun(t, config.goCmdWithExperiments("build", []string{"-o", outPath, srcPath(tc.src)}, tc.experiments))
+
+ cmd := hangProneCmd(outPath)
+ if tc.memoryAccessError != "" {
+ outb, err := cmd.CombinedOutput()
+ out := string(outb)
+ if err != nil && strings.Contains(out, tc.memoryAccessError) {
+ // This string is output if the
+ // sanitizer library needs a
+ // symbolizer program and can't find it.
+ const noSymbolizer = "external symbolizer"
+ // Check if -asan option can correctly print where the error occurred.
+ if tc.errorLocation != "" &&
+ !strings.Contains(out, tc.errorLocation) &&
+ !strings.Contains(out, noSymbolizer) &&
+ compilerSupportsLocation() {
+
+ t.Errorf("%#q exited without expected location of the error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.errorLocation, out)
+ }
+ return
+ }
+ t.Fatalf("%#q exited without expected memory access error\n%s; got failure\n%s", strings.Join(cmd.Args, " "), tc.memoryAccessError, out)
+ }
+ mustRun(t, cmd)
+ })
+ }
+}
+
+func TestASANLinkerX(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ // Test ASAN with linker's -X flag (see issue 56175).
+ goos, err := goEnv("GOOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+ goarch, err := goEnv("GOARCH")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // The asan tests require support for the -asan option.
+ if !platform.ASanSupported(goos, goarch) {
+ t.Skipf("skipping on %s/%s; -asan option is not supported.", goos, goarch)
+ }
+ if !compilerRequiredAsanVersion(goos, goarch) {
+ t.Skipf("skipping on %s/%s: too old version of compiler", goos, goarch)
+ }
+
+ t.Parallel()
+ requireOvercommit(t)
+ config := configure("address")
+ config.skipIfCSanitizerBroken(t)
+
+ dir := newTempDir(t)
+ defer dir.RemoveAll(t)
+
+ var ldflags string
+ for i := 1; i <= 10; i++ {
+ ldflags += fmt.Sprintf("-X=main.S%d=%d -X=cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/p.S%d=%d ", i, i, i, i)
+ }
+
+ // build the binary
+ outPath := dir.Join("main.exe")
+ cmd := config.goCmd("build", "-ldflags="+ldflags, "-o", outPath)
+ cmd.Dir = srcPath("asan_linkerx")
+ mustRun(t, cmd)
+
+ // run the binary
+ mustRun(t, hangProneCmd(outPath))
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/cc_test.go b/src/cmd/cgo/internal/testsanitizers/cc_test.go
new file mode 100644
index 0000000..4ee51dd
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/cc_test.go
@@ -0,0 +1,582 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This test uses the Pdeathsig field of syscall.SysProcAttr, so it only works
+// on platforms that support that.
+
+//go:build linux || (freebsd && amd64)
+
+// sanitizers_test checks the use of Go with sanitizers like msan, asan, etc.
+// See https://github.com/google/sanitizers.
+package sanitizers_test
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "os/user"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "sync"
+ "syscall"
+ "testing"
+ "time"
+ "unicode"
+)
+
+var overcommit struct {
+ sync.Once
+ value int
+ err error
+}
+
+// requireOvercommit skips t if the kernel does not allow overcommit.
+func requireOvercommit(t *testing.T) {
+ t.Helper()
+
+ overcommit.Once.Do(func() {
+ var out []byte
+ out, overcommit.err = os.ReadFile("/proc/sys/vm/overcommit_memory")
+ if overcommit.err != nil {
+ return
+ }
+ overcommit.value, overcommit.err = strconv.Atoi(string(bytes.TrimSpace(out)))
+ })
+
+ if overcommit.err != nil {
+ t.Skipf("couldn't determine vm.overcommit_memory (%v); assuming no overcommit", overcommit.err)
+ }
+ if overcommit.value == 2 {
+ t.Skip("vm.overcommit_memory=2")
+ }
+}
+
+var env struct {
+ sync.Once
+ m map[string]string
+ err error
+}
+
+// goEnv returns the output of $(go env) as a map.
+func goEnv(key string) (string, error) {
+ env.Once.Do(func() {
+ var out []byte
+ out, env.err = exec.Command("go", "env", "-json").Output()
+ if env.err != nil {
+ return
+ }
+
+ env.m = make(map[string]string)
+ env.err = json.Unmarshal(out, &env.m)
+ })
+ if env.err != nil {
+ return "", env.err
+ }
+
+ v, ok := env.m[key]
+ if !ok {
+ return "", fmt.Errorf("`go env`: no entry for %v", key)
+ }
+ return v, nil
+}
+
+// replaceEnv sets the key environment variable to value in cmd.
+func replaceEnv(cmd *exec.Cmd, key, value string) {
+ if cmd.Env == nil {
+ cmd.Env = cmd.Environ()
+ }
+ cmd.Env = append(cmd.Env, key+"="+value)
+}
+
+// appendExperimentEnv appends comma-separated experiments to GOEXPERIMENT.
+func appendExperimentEnv(cmd *exec.Cmd, experiments []string) {
+ if cmd.Env == nil {
+ cmd.Env = cmd.Environ()
+ }
+ exps := strings.Join(experiments, ",")
+ for _, evar := range cmd.Env {
+ c := strings.SplitN(evar, "=", 2)
+ if c[0] == "GOEXPERIMENT" {
+ exps = c[1] + "," + exps
+ }
+ }
+ cmd.Env = append(cmd.Env, "GOEXPERIMENT="+exps)
+}
+
+// mustRun executes t and fails cmd with a well-formatted message if it fails.
+func mustRun(t *testing.T, cmd *exec.Cmd) {
+ t.Helper()
+ out := new(strings.Builder)
+ cmd.Stdout = out
+ cmd.Stderr = out
+
+ err := cmd.Start()
+ if err != nil {
+ t.Fatalf("%v: %v", cmd, err)
+ }
+
+ if deadline, ok := t.Deadline(); ok {
+ timeout := time.Until(deadline)
+ timeout -= timeout / 10 // Leave 10% headroom for logging and cleanup.
+ timer := time.AfterFunc(timeout, func() {
+ cmd.Process.Signal(syscall.SIGQUIT)
+ })
+ defer timer.Stop()
+ }
+
+ if err := cmd.Wait(); err != nil {
+ t.Fatalf("%v exited with %v\n%s", cmd, err, out)
+ }
+}
+
+// cc returns a cmd that executes `$(go env CC) $(go env GOGCCFLAGS) $args`.
+func cc(args ...string) (*exec.Cmd, error) {
+ CC, err := goEnv("CC")
+ if err != nil {
+ return nil, err
+ }
+
+ GOGCCFLAGS, err := goEnv("GOGCCFLAGS")
+ if err != nil {
+ return nil, err
+ }
+
+ // Split GOGCCFLAGS, respecting quoting.
+ //
+ // TODO(bcmills): This code also appears in
+ // cmd/cgo/internal/testcarchive/carchive_test.go, and perhaps ought to go in
+ // src/cmd/dist/test.go as well. Figure out where to put it so that it can be
+ // shared.
+ var flags []string
+ quote := '\000'
+ start := 0
+ lastSpace := true
+ backslash := false
+ for i, c := range GOGCCFLAGS {
+ if quote == '\000' && unicode.IsSpace(c) {
+ if !lastSpace {
+ flags = append(flags, GOGCCFLAGS[start:i])
+ lastSpace = true
+ }
+ } else {
+ if lastSpace {
+ start = i
+ lastSpace = false
+ }
+ if quote == '\000' && !backslash && (c == '"' || c == '\'') {
+ quote = c
+ backslash = false
+ } else if !backslash && quote == c {
+ quote = '\000'
+ } else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
+ backslash = true
+ } else {
+ backslash = false
+ }
+ }
+ }
+ if !lastSpace {
+ flags = append(flags, GOGCCFLAGS[start:])
+ }
+
+ cmd := exec.Command(CC, flags...)
+ cmd.Args = append(cmd.Args, args...)
+ return cmd, nil
+}
+
+type version struct {
+ name string
+ major, minor int
+}
+
+var compiler struct {
+ sync.Once
+ version
+ err error
+}
+
+// compilerVersion detects the version of $(go env CC).
+//
+// It returns a non-nil error if the compiler matches a known version schema but
+// the version could not be parsed, or if $(go env CC) could not be determined.
+func compilerVersion() (version, error) {
+ compiler.Once.Do(func() {
+ compiler.err = func() error {
+ compiler.name = "unknown"
+
+ cmd, err := cc("--version")
+ if err != nil {
+ return err
+ }
+ out, err := cmd.Output()
+ if err != nil {
+ // Compiler does not support "--version" flag: not Clang or GCC.
+ return nil
+ }
+
+ var match [][]byte
+ if bytes.HasPrefix(out, []byte("gcc")) {
+ compiler.name = "gcc"
+ cmd, err := cc("-dumpfullversion", "-dumpversion")
+ if err != nil {
+ return err
+ }
+ out, err := cmd.Output()
+ if err != nil {
+ // gcc, but does not support gcc's "-v" flag?!
+ return err
+ }
+ gccRE := regexp.MustCompile(`(\d+)\.(\d+)`)
+ match = gccRE.FindSubmatch(out)
+ } else {
+ clangRE := regexp.MustCompile(`clang version (\d+)\.(\d+)`)
+ if match = clangRE.FindSubmatch(out); len(match) > 0 {
+ compiler.name = "clang"
+ }
+ }
+
+ if len(match) < 3 {
+ return nil // "unknown"
+ }
+ if compiler.major, err = strconv.Atoi(string(match[1])); err != nil {
+ return err
+ }
+ if compiler.minor, err = strconv.Atoi(string(match[2])); err != nil {
+ return err
+ }
+ return nil
+ }()
+ })
+ return compiler.version, compiler.err
+}
+
+// compilerSupportsLocation reports whether the compiler should be
+// able to provide file/line information in backtraces.
+func compilerSupportsLocation() bool {
+ compiler, err := compilerVersion()
+ if err != nil {
+ return false
+ }
+ switch compiler.name {
+ case "gcc":
+ return compiler.major >= 10
+ case "clang":
+ // TODO(65606): The clang toolchain on the LUCI builders is not built against
+ // zlib, the ASAN runtime can't actually symbolize its own stack trace. Once
+ // this is resolved, one way or another, switch this back to 'true'. We still
+ // have coverage from the 'gcc' case above.
+ if inLUCIBuild() {
+ return false
+ }
+ return true
+ default:
+ return false
+ }
+}
+
+// inLUCIBuild returns true if we're currently executing in a LUCI build.
+func inLUCIBuild() bool {
+ u, err := user.Current()
+ if err != nil {
+ return false
+ }
+ return testenv.Builder() != "" && u.Username == "swarming"
+}
+
+// compilerRequiredTsanVersion reports whether the compiler is the version required by Tsan.
+// Only restrictions for ppc64le are known; otherwise return true.
+func compilerRequiredTsanVersion(goos, goarch string) bool {
+ compiler, err := compilerVersion()
+ if err != nil {
+ return false
+ }
+ if compiler.name == "gcc" && goarch == "ppc64le" {
+ return compiler.major >= 9
+ }
+ return true
+}
+
+// compilerRequiredAsanVersion reports whether the compiler is the version required by Asan.
+func compilerRequiredAsanVersion(goos, goarch string) bool {
+ compiler, err := compilerVersion()
+ if err != nil {
+ return false
+ }
+ switch compiler.name {
+ case "gcc":
+ if goarch == "ppc64le" {
+ return compiler.major >= 9
+ }
+ return compiler.major >= 7
+ case "clang":
+ return compiler.major >= 9
+ default:
+ return false
+ }
+}
+
+type compilerCheck struct {
+ once sync.Once
+ err error
+ skip bool // If true, skip with err instead of failing with it.
+}
+
+type config struct {
+ sanitizer string
+
+ cFlags, ldFlags, goFlags []string
+
+ sanitizerCheck, runtimeCheck compilerCheck
+}
+
+var configs struct {
+ sync.Mutex
+ m map[string]*config
+}
+
+// configure returns the configuration for the given sanitizer.
+func configure(sanitizer string) *config {
+ configs.Lock()
+ defer configs.Unlock()
+ if c, ok := configs.m[sanitizer]; ok {
+ return c
+ }
+
+ c := &config{
+ sanitizer: sanitizer,
+ cFlags: []string{"-fsanitize=" + sanitizer},
+ ldFlags: []string{"-fsanitize=" + sanitizer},
+ }
+
+ if testing.Verbose() {
+ c.goFlags = append(c.goFlags, "-x")
+ }
+
+ switch sanitizer {
+ case "memory":
+ c.goFlags = append(c.goFlags, "-msan")
+
+ case "thread":
+ c.goFlags = append(c.goFlags, "--installsuffix=tsan")
+ compiler, _ := compilerVersion()
+ if compiler.name == "gcc" {
+ c.cFlags = append(c.cFlags, "-fPIC")
+ c.ldFlags = append(c.ldFlags, "-fPIC", "-static-libtsan")
+ }
+
+ case "address":
+ c.goFlags = append(c.goFlags, "-asan")
+ // Set the debug mode to print the C stack trace.
+ c.cFlags = append(c.cFlags, "-g")
+
+ case "fuzzer":
+ c.goFlags = append(c.goFlags, "-tags=libfuzzer", "-gcflags=-d=libfuzzer")
+
+ default:
+ panic(fmt.Sprintf("unrecognized sanitizer: %q", sanitizer))
+ }
+
+ if configs.m == nil {
+ configs.m = make(map[string]*config)
+ }
+ configs.m[sanitizer] = c
+ return c
+}
+
+// goCmd returns a Cmd that executes "go $subcommand $args" with appropriate
+// additional flags and environment.
+func (c *config) goCmd(subcommand string, args ...string) *exec.Cmd {
+ return c.goCmdWithExperiments(subcommand, args, nil)
+}
+
+// goCmdWithExperiments returns a Cmd that executes
+// "GOEXPERIMENT=$experiments go $subcommand $args" with appropriate
+// additional flags and CGO-related environment variables.
+func (c *config) goCmdWithExperiments(subcommand string, args []string, experiments []string) *exec.Cmd {
+ cmd := exec.Command("go", subcommand)
+ cmd.Args = append(cmd.Args, c.goFlags...)
+ cmd.Args = append(cmd.Args, args...)
+ replaceEnv(cmd, "CGO_CFLAGS", strings.Join(c.cFlags, " "))
+ replaceEnv(cmd, "CGO_LDFLAGS", strings.Join(c.ldFlags, " "))
+ appendExperimentEnv(cmd, experiments)
+ return cmd
+}
+
+// skipIfCSanitizerBroken skips t if the C compiler does not produce working
+// binaries as configured.
+func (c *config) skipIfCSanitizerBroken(t *testing.T) {
+ check := &c.sanitizerCheck
+ check.once.Do(func() {
+ check.skip, check.err = c.checkCSanitizer()
+ })
+ if check.err != nil {
+ t.Helper()
+ if check.skip {
+ t.Skip(check.err)
+ }
+ t.Fatal(check.err)
+ }
+}
+
+var cMain = []byte(`
+int main() {
+ return 0;
+}
+`)
+
+var cLibFuzzerInput = []byte(`
+#include <stddef.h>
+int LLVMFuzzerTestOneInput(char *data, size_t size) {
+ return 0;
+}
+`)
+
+func (c *config) checkCSanitizer() (skip bool, err error) {
+ dir, err := os.MkdirTemp("", c.sanitizer)
+ if err != nil {
+ return false, fmt.Errorf("failed to create temp directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+
+ src := filepath.Join(dir, "return0.c")
+ cInput := cMain
+ if c.sanitizer == "fuzzer" {
+ // libFuzzer generates the main function itself, and uses a different input.
+ cInput = cLibFuzzerInput
+ }
+ if err := os.WriteFile(src, cInput, 0600); err != nil {
+ return false, fmt.Errorf("failed to write C source file: %v", err)
+ }
+
+ dst := filepath.Join(dir, "return0")
+ cmd, err := cc(c.cFlags...)
+ if err != nil {
+ return false, err
+ }
+ cmd.Args = append(cmd.Args, c.ldFlags...)
+ cmd.Args = append(cmd.Args, "-o", dst, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ if bytes.Contains(out, []byte("-fsanitize")) &&
+ (bytes.Contains(out, []byte("unrecognized")) ||
+ bytes.Contains(out, []byte("unsupported"))) {
+ return true, errors.New(string(out))
+ }
+ return true, fmt.Errorf("%#q failed: %v\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+
+ if c.sanitizer == "fuzzer" {
+ // For fuzzer, don't try running the test binary. It never finishes.
+ return false, nil
+ }
+
+ if out, err := exec.Command(dst).CombinedOutput(); err != nil {
+ if os.IsNotExist(err) {
+ return true, fmt.Errorf("%#q failed to produce executable: %v", strings.Join(cmd.Args, " "), err)
+ }
+ snippet, _, _ := bytes.Cut(out, []byte("\n"))
+ return true, fmt.Errorf("%#q generated broken executable: %v\n%s", strings.Join(cmd.Args, " "), err, snippet)
+ }
+
+ return false, nil
+}
+
+// skipIfRuntimeIncompatible skips t if the Go runtime is suspected not to work
+// with cgo as configured.
+func (c *config) skipIfRuntimeIncompatible(t *testing.T) {
+ check := &c.runtimeCheck
+ check.once.Do(func() {
+ check.skip, check.err = c.checkRuntime()
+ })
+ if check.err != nil {
+ t.Helper()
+ if check.skip {
+ t.Skip(check.err)
+ }
+ t.Fatal(check.err)
+ }
+}
+
+func (c *config) checkRuntime() (skip bool, err error) {
+ if c.sanitizer != "thread" {
+ return false, nil
+ }
+
+ // libcgo.h sets CGO_TSAN if it detects TSAN support in the C compiler.
+ // Dump the preprocessor defines to check that works.
+ // (Sometimes it doesn't: see https://golang.org/issue/15983.)
+ cmd, err := cc(c.cFlags...)
+ if err != nil {
+ return false, err
+ }
+ cmd.Args = append(cmd.Args, "-dM", "-E", "../../../../runtime/cgo/libcgo.h")
+ cmdStr := strings.Join(cmd.Args, " ")
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ return false, fmt.Errorf("%#q exited with %v\n%s", cmdStr, err, out)
+ }
+ if !bytes.Contains(out, []byte("#define CGO_TSAN")) {
+ return true, fmt.Errorf("%#q did not define CGO_TSAN", cmdStr)
+ }
+ return false, nil
+}
+
+// srcPath returns the path to the given file relative to this test's source tree.
+func srcPath(path string) string {
+ return filepath.Join("testdata", path)
+}
+
+// A tempDir manages a temporary directory within a test.
+type tempDir struct {
+ base string
+}
+
+func (d *tempDir) RemoveAll(t *testing.T) {
+ t.Helper()
+ if d.base == "" {
+ return
+ }
+ if err := os.RemoveAll(d.base); err != nil {
+ t.Fatalf("Failed to remove temp dir: %v", err)
+ }
+}
+
+func (d *tempDir) Base() string {
+ return d.base
+}
+
+func (d *tempDir) Join(name string) string {
+ return filepath.Join(d.base, name)
+}
+
+func newTempDir(t *testing.T) *tempDir {
+ t.Helper()
+ dir, err := os.MkdirTemp("", filepath.Dir(t.Name()))
+ if err != nil {
+ t.Fatalf("Failed to create temp dir: %v", err)
+ }
+ return &tempDir{base: dir}
+}
+
+// hangProneCmd returns an exec.Cmd for a command that is likely to hang.
+//
+// If one of these tests hangs, the caller is likely to kill the test process
+// using SIGINT, which will be sent to all of the processes in the test's group.
+// Unfortunately, TSAN in particular is prone to dropping signals, so the SIGINT
+// may terminate the test binary but leave the subprocess running. hangProneCmd
+// configures subprocess to receive SIGKILL instead to ensure that it won't
+// leak.
+func hangProneCmd(name string, arg ...string) *exec.Cmd {
+ cmd := exec.Command(name, arg...)
+ cmd.SysProcAttr = &syscall.SysProcAttr{
+ Pdeathsig: syscall.SIGKILL,
+ }
+ return cmd
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/cshared_test.go b/src/cmd/cgo/internal/testsanitizers/cshared_test.go
new file mode 100644
index 0000000..f26c50a
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/cshared_test.go
@@ -0,0 +1,98 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux || (freebsd && amd64)
+
+package sanitizers_test
+
+import (
+ "fmt"
+ "internal/platform"
+ "internal/testenv"
+ "os"
+ "strings"
+ "testing"
+)
+
+func TestShared(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "c-shared")
+
+ t.Parallel()
+ requireOvercommit(t)
+
+ GOOS, err := goEnv("GOOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ GOARCH, err := goEnv("GOARCH")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ libExt := "so"
+ if GOOS == "darwin" {
+ libExt = "dylib"
+ }
+
+ cases := []struct {
+ src string
+ sanitizer string
+ }{
+ {
+ src: "msan_shared.go",
+ sanitizer: "memory",
+ },
+ {
+ src: "tsan_shared.go",
+ sanitizer: "thread",
+ },
+ }
+
+ for _, tc := range cases {
+ tc := tc
+ name := strings.TrimSuffix(tc.src, ".go")
+ //The memory sanitizer tests require support for the -msan option.
+ if tc.sanitizer == "memory" && !platform.MSanSupported(GOOS, GOARCH) {
+ t.Logf("skipping %s test on %s/%s; -msan option is not supported.", name, GOOS, GOARCH)
+ continue
+ }
+ if tc.sanitizer == "thread" && !compilerRequiredTsanVersion(GOOS, GOARCH) {
+ t.Logf("skipping %s test on %s/%s; compiler version too old for -tsan.", name, GOOS, GOARCH)
+ continue
+ }
+
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+ config := configure(tc.sanitizer)
+ config.skipIfCSanitizerBroken(t)
+
+ dir := newTempDir(t)
+ defer dir.RemoveAll(t)
+
+ lib := dir.Join(fmt.Sprintf("lib%s.%s", name, libExt))
+ mustRun(t, config.goCmd("build", "-buildmode=c-shared", "-o", lib, srcPath(tc.src)))
+
+ cSrc := dir.Join("main.c")
+ if err := os.WriteFile(cSrc, cMain, 0600); err != nil {
+ t.Fatalf("failed to write C source file: %v", err)
+ }
+
+ dstBin := dir.Join(name)
+ cmd, err := cc(config.cFlags...)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cmd.Args = append(cmd.Args, config.ldFlags...)
+ cmd.Args = append(cmd.Args, "-o", dstBin, cSrc, lib)
+ mustRun(t, cmd)
+
+ cmd = hangProneCmd(dstBin)
+ replaceEnv(cmd, "LD_LIBRARY_PATH", ".")
+ mustRun(t, cmd)
+ })
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/empty_test.go b/src/cmd/cgo/internal/testsanitizers/empty_test.go
new file mode 100644
index 0000000..e7fed99
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/empty_test.go
@@ -0,0 +1,8 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// All of the actual test files have limited build constraints. This file
+// ensures there's at least one test file on every platform.
+
+package sanitizers_test
diff --git a/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go
new file mode 100644
index 0000000..f84c9f3
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/libfuzzer_test.go
@@ -0,0 +1,93 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux || (freebsd && amd64)
+
+package sanitizers_test
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestLibFuzzer(t *testing.T) {
+ goos, err := goEnv("GOOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+ goarch, err := goEnv("GOARCH")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !libFuzzerSupported(goos, goarch) {
+ t.Skipf("skipping on %s/%s; libfuzzer option is not supported.", goos, goarch)
+ }
+ config := configure("fuzzer")
+ config.skipIfCSanitizerBroken(t)
+
+ cases := []struct {
+ goSrc string
+ cSrc string
+ expectedError string
+ }{
+ {goSrc: "libfuzzer1.go", expectedError: "panic: found it"},
+ {goSrc: "libfuzzer2.go", cSrc: "libfuzzer2.c", expectedError: "panic: found it"},
+ }
+ for _, tc := range cases {
+ tc := tc
+ name := strings.TrimSuffix(tc.goSrc, ".go")
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ dir := newTempDir(t)
+ defer dir.RemoveAll(t)
+
+ // build Go code in libfuzzer mode to a c-archive
+ outPath := dir.Join(name)
+ archivePath := dir.Join(name + ".a")
+ mustRun(t, config.goCmd("build", "-buildmode=c-archive", "-o", archivePath, srcPath(tc.goSrc)))
+
+ // build C code (if any) and link with Go code
+ cmd, err := cc(config.cFlags...)
+ if err != nil {
+ t.Fatalf("error running cc: %v", err)
+ }
+ cmd.Args = append(cmd.Args, config.ldFlags...)
+ cmd.Args = append(cmd.Args, "-o", outPath, "-I", dir.Base())
+ if tc.cSrc != "" {
+ cmd.Args = append(cmd.Args, srcPath(tc.cSrc))
+ }
+ cmd.Args = append(cmd.Args, archivePath)
+ mustRun(t, cmd)
+
+ cmd = hangProneCmd(outPath)
+ cmd.Dir = dir.Base()
+ outb, err := cmd.CombinedOutput()
+ out := string(outb)
+ if err == nil {
+ t.Fatalf("fuzzing succeeded unexpectedly; output:\n%s", out)
+ }
+ if !strings.Contains(out, tc.expectedError) {
+ t.Errorf("exited without expected error %q; got\n%s", tc.expectedError, out)
+ }
+ })
+ }
+}
+
+// libFuzzerSupported is a copy of the function internal/platform.FuzzInstrumented,
+// because the internal package can't be used here.
+func libFuzzerSupported(goos, goarch string) bool {
+ switch goarch {
+ case "amd64", "arm64":
+ // TODO(#14565): support more architectures.
+ switch goos {
+ case "darwin", "freebsd", "linux", "windows":
+ return true
+ default:
+ return false
+ }
+ default:
+ return false
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/msan_test.go b/src/cmd/cgo/internal/testsanitizers/msan_test.go
new file mode 100644
index 0000000..1a22b52
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/msan_test.go
@@ -0,0 +1,84 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux || (freebsd && amd64)
+
+package sanitizers_test
+
+import (
+ "internal/platform"
+ "strings"
+ "testing"
+)
+
+func TestMSAN(t *testing.T) {
+ goos, err := goEnv("GOOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+ goarch, err := goEnv("GOARCH")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // The msan tests require support for the -msan option.
+ if !platform.MSanSupported(goos, goarch) {
+ t.Skipf("skipping on %s/%s; -msan option is not supported.", goos, goarch)
+ }
+
+ t.Parallel()
+ // Overcommit is enabled by default on FreeBSD (vm.overcommit=0, see tuning(7)).
+ // Do not skip tests with stricter overcommit settings unless testing shows that FreeBSD has similar issues.
+ if goos == "linux" {
+ requireOvercommit(t)
+ }
+ config := configure("memory")
+ config.skipIfCSanitizerBroken(t)
+
+ mustRun(t, config.goCmd("build", "std"))
+
+ cases := []struct {
+ src string
+ wantErr bool
+ experiments []string
+ }{
+ {src: "msan.go"},
+ {src: "msan2.go"},
+ {src: "msan2_cmsan.go"},
+ {src: "msan3.go"},
+ {src: "msan4.go"},
+ {src: "msan5.go"},
+ {src: "msan6.go"},
+ {src: "msan7.go"},
+ {src: "msan8.go"},
+ {src: "msan_fail.go", wantErr: true},
+ // This may not always fail specifically due to MSAN. It may sometimes
+ // fail because of a fault. However, we don't care what kind of error we
+ // get here, just that we get an error. This is an MSAN test because without
+ // MSAN it would not fail deterministically.
+ {src: "arena_fail.go", wantErr: true, experiments: []string{"arenas"}},
+ }
+ for _, tc := range cases {
+ tc := tc
+ name := strings.TrimSuffix(tc.src, ".go")
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ dir := newTempDir(t)
+ defer dir.RemoveAll(t)
+
+ outPath := dir.Join(name)
+ mustRun(t, config.goCmdWithExperiments("build", []string{"-o", outPath, srcPath(tc.src)}, tc.experiments))
+
+ cmd := hangProneCmd(outPath)
+ if tc.wantErr {
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ return
+ }
+ t.Fatalf("%#q exited without error; want MSAN failure\n%s", strings.Join(cmd.Args, " "), out)
+ }
+ mustRun(t, cmd)
+ })
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/arena_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/arena_fail.go
new file mode 100644
index 0000000..5b6c52e
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/arena_fail.go
@@ -0,0 +1,27 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build goexperiment.arenas
+
+package main
+
+import "arena"
+
+func main() {
+ a := arena.NewArena()
+ x := arena.New[[200]byte](a)
+ x[0] = 9
+ a.Free()
+ // Use after free.
+ //
+ // ASAN should detect this deterministically as Free
+ // should poison the arena memory.
+ //
+ // MSAN should detect that this access is to freed
+ // memory. This may crash with an "accessed freed arena
+ // memory" error before MSAN gets a chance, but if MSAN
+ // was not enabled there would be a chance that this
+ // could fail to crash on its own.
+ println(x[0])
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan1_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan1_fail.go
new file mode 100644
index 0000000..80289e5
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan1_fail.go
@@ -0,0 +1,28 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+int *p;
+int* test() {
+ p = (int *)malloc(2 * sizeof(int));
+ free(p);
+ return p;
+}
+*/
+import "C"
+import "fmt"
+
+func main() {
+ // C passes Go an invalid pointer.
+ a := C.test()
+ // Use after free
+ *a = 2 // BOOM
+ // We shouldn't get here; asan should stop us first.
+ fmt.Println(*a)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan2_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan2_fail.go
new file mode 100644
index 0000000..3ab0608
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan2_fail.go
@@ -0,0 +1,34 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+int *p;
+int* f() {
+ int i;
+ p = (int *)malloc(5*sizeof(int));
+ for (i = 0; i < 5; i++) {
+ p[i] = i+10;
+ }
+ return p;
+}
+*/
+import "C"
+import (
+ "fmt"
+ "unsafe"
+)
+
+func main() {
+ a := C.f()
+ q5 := (*C.int)(unsafe.Add(unsafe.Pointer(a), 4*5))
+ // Access to C pointer out of bounds.
+ *q5 = 100 // BOOM
+ // We shouldn't get here; asan should stop us first.
+ fmt.Printf("q5: %d, %x\n", *q5, q5)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan3_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan3_fail.go
new file mode 100644
index 0000000..9f6d26d
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan3_fail.go
@@ -0,0 +1,23 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+void test(int *a) {
+ // Access Go pointer out of bounds.
+ int c = a[5]; // BOOM
+ // We shouldn't get here; asan should stop us first.
+ printf("a[5]=%d\n", c);
+}
+*/
+import "C"
+
+func main() {
+ cIntSlice := []C.int{200, 201, 203, 203, 204}
+ C.test(&cIntSlice[0])
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan4_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan4_fail.go
new file mode 100644
index 0000000..1209845
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan4_fail.go
@@ -0,0 +1,22 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+void test(int* a) {
+ // Access Go pointer out of bounds.
+ a[3] = 300; // BOOM
+ // We shouldn't get here; asan should stop us first.
+ printf("a[3]=%d\n", a[3]);
+}*/
+import "C"
+
+func main() {
+ var cIntArray [2]C.int
+ C.test(&cIntArray[0]) // cIntArray is moved to heap.
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan5_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan5_fail.go
new file mode 100644
index 0000000..d6853ea
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan5_fail.go
@@ -0,0 +1,21 @@
+package main
+
+import (
+ "fmt"
+ "runtime"
+ "unsafe"
+)
+
+func main() {
+ p := new([1024 * 1000]int)
+ p[0] = 10
+ r := bar(&p[1024*1000-1])
+ fmt.Printf("r value is %d", r)
+}
+
+func bar(a *int) int {
+ p := unsafe.Add(unsafe.Pointer(a), 2*unsafe.Sizeof(int(1)))
+ runtime.ASanWrite(p, 8) // BOOM
+ *((*int)(p)) = 10
+ return *((*int)(p))
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_global1_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global1_fail.go
new file mode 100644
index 0000000..6cfc0b7
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global1_fail.go
@@ -0,0 +1,25 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+int test(int *a) {
+ a[2] = 300; // BOOM
+ return a[2];
+}
+*/
+import "C"
+
+import "fmt"
+
+var cIntArray [2]C.int
+
+func main() {
+ r := C.test(&cIntArray[0])
+ fmt.Println("r value = ", r)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_global2_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global2_fail.go
new file mode 100644
index 0000000..1932633
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global2_fail.go
@@ -0,0 +1,31 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+struct ss {
+ int *p;
+ int len;
+ int cap;
+};
+
+int test(struct ss *a) {
+ struct ss *t = a + 1;
+ t->len = 100; // BOOM
+ return t->len;
+}
+*/
+import "C"
+import "fmt"
+
+var tt C.struct_ss
+
+func main() {
+ r := C.test(&tt)
+ fmt.Println("r value = ", r)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_global3_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global3_fail.go
new file mode 100644
index 0000000..9ab026c
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global3_fail.go
@@ -0,0 +1,28 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <stdlib.h>
+#include <stdio.h>
+
+int test(int *a) {
+ int* p = a+1;
+ *p = 10; // BOOM
+ return *p;
+}
+*/
+import "C"
+import (
+ "fmt"
+ "unsafe"
+)
+
+var cIntV C.int
+
+func main() {
+ r := C.test((*C.int)(unsafe.Pointer(&cIntV)))
+ fmt.Printf("r value is %d", r)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_global4_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global4_fail.go
new file mode 100644
index 0000000..d593598
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global4_fail.go
@@ -0,0 +1,25 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+var intGlo int
+
+func main() {
+ r := bar(&intGlo)
+ fmt.Printf("r value is %d", r)
+}
+
+func bar(a *int) int {
+ p := (*int)(unsafe.Add(unsafe.Pointer(a), 1*unsafe.Sizeof(int(1))))
+ if *p == 10 { // BOOM
+ fmt.Println("its value is 10")
+ }
+ return *p
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_global5.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global5.go
new file mode 100644
index 0000000..0ed103d
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_global5.go
@@ -0,0 +1,22 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+)
+
+type Any struct {
+ s string
+ b int64
+}
+
+var Sg = []interface{}{
+ Any{"a", 10},
+}
+
+func main() {
+ fmt.Println(Sg[0])
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/main.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/main.go
new file mode 100644
index 0000000..290b588
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/main.go
@@ -0,0 +1,28 @@
+package main
+
+import "cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/p"
+
+func pstring(s *string) {
+ println(*s)
+}
+
+func main() {
+ all := []*string{
+ &S1, &S2, &S3, &S4, &S5, &S6, &S7, &S8, &S9, &S10,
+ &p.S1, &p.S2, &p.S3, &p.S4, &p.S5, &p.S6, &p.S7, &p.S8, &p.S9, &p.S10,
+ }
+ for _, ps := range all {
+ pstring(ps)
+ }
+}
+
+var S1 string
+var S2 string
+var S3 string
+var S4 string
+var S5 string
+var S6 string
+var S7 string
+var S8 string
+var S9 string
+var S10 string
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/p/p.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/p/p.go
new file mode 100644
index 0000000..c31f001
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_linkerx/p/p.go
@@ -0,0 +1,12 @@
+package p
+
+var S1 string
+var S2 string
+var S3 string
+var S4 string
+var S5 string
+var S6 string
+var S7 string
+var S8 string
+var S9 string
+var S10 string
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail1.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail1.go
new file mode 100644
index 0000000..ec54a66
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail1.go
@@ -0,0 +1,27 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+func main() {
+ a := 1
+ b := 2
+ c := add(a, b)
+ d := a + b
+ fmt.Println(c, d)
+}
+
+//go:noinline
+func add(a1, b1 int) int {
+ // The arguments.
+ // When -asan is enabled, unsafe.Pointer(&a1) conversion is escaping.
+ var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a1), 1*unsafe.Sizeof(int(1))))
+ *p = 10 // BOOM
+ return a1 + b1
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail2.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail2.go
new file mode 100644
index 0000000..70f2127
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail2.go
@@ -0,0 +1,28 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+func main() {
+ a := 1
+ b := 2
+ c := add(a, b)
+ d := a + b
+ fmt.Println(c, d)
+}
+
+//go:noinline
+func add(a1, b1 int) (ret int) {
+ // The return value
+ // When -asan is enabled, the unsafe.Pointer(&ret) conversion is escaping.
+ var p *int = (*int)(unsafe.Add(unsafe.Pointer(&ret), 1*unsafe.Sizeof(int(1))))
+ *p = 123 // BOOM
+ ret = a1 + b1
+ return
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail3.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail3.go
new file mode 100644
index 0000000..47a8a07
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_unsafe_fail3.go
@@ -0,0 +1,21 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "fmt"
+ "unsafe"
+)
+
+func main() {
+ a := 1
+ b := 2
+ // The local variables.
+ // When -asan is enabled, the unsafe.Pointer(&a) conversion is escaping.
+ var p *int = (*int)(unsafe.Add(unsafe.Pointer(&a), 1*unsafe.Sizeof(int(1))))
+ *p = 20 // BOOM
+ d := a + b
+ fmt.Println(d)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/asan_useAfterReturn.go b/src/cmd/cgo/internal/testsanitizers/testdata/asan_useAfterReturn.go
new file mode 100644
index 0000000..3d3d5a6
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/asan_useAfterReturn.go
@@ -0,0 +1,26 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// The -fsanitize=address option of C compier can detect stack-use-after-return bugs.
+// In the following program, the local variable 'local' was moved to heap by the Go
+// compiler because foo() is returning the reference to 'local', and return stack of
+// foo() will be invalid. Thus for main() to use the reference to 'local', the 'local'
+// must be available even after foo() has finished. Therefore, Go has no such issue.
+
+import "fmt"
+
+var ptr *int
+
+func main() {
+ foo()
+ fmt.Printf("ptr=%x, %v", *ptr, ptr)
+}
+
+func foo() {
+ var local int
+ local = 1
+ ptr = &local // local is moved to heap.
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer1.go b/src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer1.go
new file mode 100644
index 0000000..d178fb1
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer1.go
@@ -0,0 +1,16 @@
+package main
+
+import "C"
+
+import "unsafe"
+
+//export LLVMFuzzerTestOneInput
+func LLVMFuzzerTestOneInput(p unsafe.Pointer, sz C.int) C.int {
+ b := C.GoBytes(p, sz)
+ if len(b) >= 6 && b[0] == 'F' && b[1] == 'u' && b[2] == 'z' && b[3] == 'z' && b[4] == 'M' && b[5] == 'e' {
+ panic("found it")
+ }
+ return 0
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer2.c b/src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer2.c
new file mode 100644
index 0000000..567ff5a
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer2.c
@@ -0,0 +1,11 @@
+#include <stddef.h>
+
+#include "libfuzzer2.h"
+
+int LLVMFuzzerTestOneInput(char *data, size_t size) {
+ if (size > 0 && data[0] == 'H')
+ if (size > 1 && data[1] == 'I')
+ if (size > 2 && data[2] == '!')
+ FuzzMe(data, size);
+ return 0;
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer2.go b/src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer2.go
new file mode 100644
index 0000000..c7a4325
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/libfuzzer2.go
@@ -0,0 +1,16 @@
+package main
+
+import "C"
+
+import "unsafe"
+
+//export FuzzMe
+func FuzzMe(p unsafe.Pointer, sz C.int) {
+ b := C.GoBytes(p, sz)
+ b = b[3:]
+ if len(b) >= 4 && b[0] == 'f' && b[1] == 'u' && b[2] == 'z' && b[3] == 'z' {
+ panic("found it")
+ }
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan.go
new file mode 100644
index 0000000..5d73c38
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan.go
@@ -0,0 +1,35 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <stdint.h>
+
+void f(int32_t *p, int n) {
+ int i;
+
+ for (i = 0; i < n; i++) {
+ p[i] = (int32_t)i;
+ }
+}
+*/
+import "C"
+
+import (
+ "fmt"
+ "os"
+ "unsafe"
+)
+
+func main() {
+ a := make([]int32, 10)
+ C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a)))
+ for i, v := range a {
+ if i != int(v) {
+ fmt.Printf("bad %d: %v\n", i, a)
+ os.Exit(1)
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan2.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan2.go
new file mode 100644
index 0000000..6690cb0
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan2.go
@@ -0,0 +1,35 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+void f(int32_t *p, int n) {
+ int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n);
+ memcpy(p, q, n * sizeof(*p));
+ free(q);
+}
+
+void g(int32_t *p, int n) {
+ if (p[4] != 1) {
+ abort();
+ }
+}
+*/
+import "C"
+
+import (
+ "unsafe"
+)
+
+func main() {
+ a := make([]int32, 10)
+ C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a)))
+ a[4] = 1
+ C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a)))
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan2_cmsan.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan2_cmsan.go
new file mode 100644
index 0000000..8fdaea9
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan2_cmsan.go
@@ -0,0 +1,38 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#cgo LDFLAGS: -fsanitize=memory
+#cgo CPPFLAGS: -fsanitize=memory
+
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+void f(int32_t *p, int n) {
+ int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n);
+ memcpy(p, q, n * sizeof(*p));
+ free(q);
+}
+
+void g(int32_t *p, int n) {
+ if (p[4] != 1) {
+ abort();
+ }
+}
+*/
+import "C"
+
+import (
+ "unsafe"
+)
+
+func main() {
+ a := make([]int32, 10)
+ C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a)))
+ a[4] = 1
+ C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a)))
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan3.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan3.go
new file mode 100644
index 0000000..61a9c29
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan3.go
@@ -0,0 +1,33 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+extern int *GoFn(int *);
+
+// Yes, you can have definitions if you use //export, as long as they are weak.
+int f(void) __attribute__ ((weak));
+
+int f() {
+ int i;
+ int *p = GoFn(&i);
+ if (*p != 12345)
+ return 0;
+ return 1;
+}
+*/
+import "C"
+
+//export GoFn
+func GoFn(p *C.int) *C.int {
+ *p = C.int(12345)
+ return p
+}
+
+func main() {
+ if r := C.f(); r != 1 {
+ panic(r)
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan4.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan4.go
new file mode 100644
index 0000000..6c91ff5
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan4.go
@@ -0,0 +1,50 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// The memory profiler can call copy from a slice on the system stack,
+// which msan used to think meant a reference to uninitialized memory.
+
+/*
+#include <time.h>
+#include <unistd.h>
+
+extern void Nop(char*);
+
+// Use weak as a hack to permit defining a function even though we use export.
+void poison() __attribute__ ((weak));
+
+// Poison the stack.
+void poison() {
+ char a[1024];
+ Nop(&a[0]);
+}
+
+*/
+import "C"
+
+import (
+ "runtime"
+)
+
+func main() {
+ runtime.MemProfileRate = 1
+ start(100)
+}
+
+func start(i int) {
+ if i == 0 {
+ return
+ }
+ C.poison()
+ // Tie up a thread.
+ // We won't actually wait for this sleep to complete.
+ go func() { C.sleep(1) }()
+ start(i - 1)
+}
+
+//export Nop
+func Nop(*C.char) {
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan5.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan5.go
new file mode 100644
index 0000000..f1479eb
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan5.go
@@ -0,0 +1,57 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Using reflect to set a value was not seen by msan.
+
+/*
+#include <stdlib.h>
+
+extern void Go1(int*);
+extern void Go2(char*);
+
+// Use weak as a hack to permit defining a function even though we use export.
+void C1() __attribute__ ((weak));
+void C2() __attribute__ ((weak));
+
+void C1() {
+ int i;
+ Go1(&i);
+ if (i != 42) {
+ abort();
+ }
+}
+
+void C2() {
+ char a[2];
+ a[1] = 42;
+ Go2(a);
+ if (a[0] != 42) {
+ abort();
+ }
+}
+*/
+import "C"
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+//export Go1
+func Go1(p *C.int) {
+ reflect.ValueOf(p).Elem().Set(reflect.ValueOf(C.int(42)))
+}
+
+//export Go2
+func Go2(p *C.char) {
+ a := (*[2]byte)(unsafe.Pointer(p))
+ reflect.Copy(reflect.ValueOf(a[:1]), reflect.ValueOf(a[1:]))
+}
+
+func main() {
+ C.C1()
+ C.C2()
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan6.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan6.go
new file mode 100644
index 0000000..e96e8f9
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan6.go
@@ -0,0 +1,75 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// A C function returning a value on the Go stack could leave the Go
+// stack marked as uninitialized, potentially causing a later error
+// when the stack is used for something else. Issue 26209.
+
+/*
+#cgo LDFLAGS: -fsanitize=memory
+#cgo CPPFLAGS: -fsanitize=memory
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct {
+ uintptr_t a[20];
+} S;
+
+S f() {
+ S *p;
+
+ p = (S *)(malloc(sizeof(S)));
+ p->a[0] = 0;
+ return *p;
+}
+*/
+import "C"
+
+// allocateStack extends the stack so that stack copying doesn't
+// confuse the msan data structures.
+//
+//go:noinline
+func allocateStack(i int) int {
+ if i == 0 {
+ return i
+ }
+ return allocateStack(i - 1)
+}
+
+// F1 marks a chunk of stack as uninitialized.
+// C.f returns an uninitialized struct on the stack, so msan will mark
+// the stack as uninitialized.
+//
+//go:noinline
+func F1() uintptr {
+ s := C.f()
+ return uintptr(s.a[0])
+}
+
+// F2 allocates a struct on the stack and converts it to an empty interface,
+// which will call msanread and see that the data appears uninitialized.
+//
+//go:noinline
+func F2() interface{} {
+ return C.S{}
+}
+
+func poisonStack(i int) int {
+ if i == 0 {
+ return int(F1())
+ }
+ F1()
+ r := poisonStack(i - 1)
+ F2()
+ return r
+}
+
+func main() {
+ allocateStack(16384)
+ poisonStack(128)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan7.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan7.go
new file mode 100644
index 0000000..2f29fd2
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan7.go
@@ -0,0 +1,38 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Test passing C struct to exported Go function.
+
+/*
+#include <stdint.h>
+#include <stdlib.h>
+
+// T is a C struct with alignment padding after b.
+// The padding bytes are not considered initialized by MSAN.
+// It is big enough to be passed on stack in C ABI (and least
+// on AMD64).
+typedef struct { char b; uintptr_t x, y; } T;
+
+extern void F(T);
+
+// Use weak as a hack to permit defining a function even though we use export.
+void CF(int x) __attribute__ ((weak));
+void CF(int x) {
+ T *t = malloc(sizeof(T));
+ t->b = (char)x;
+ t->x = x;
+ t->y = x;
+ F(*t);
+}
+*/
+import "C"
+
+//export F
+func F(t C.T) { println(t.b, t.x, t.y) }
+
+func main() {
+ C.CF(C.int(0))
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan8.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan8.go
new file mode 100644
index 0000000..1cb5c56
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan8.go
@@ -0,0 +1,109 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <pthread.h>
+#include <signal.h>
+#include <stdint.h>
+
+#include <sanitizer/msan_interface.h>
+
+// cgoTracebackArg is the type of the argument passed to msanGoTraceback.
+struct cgoTracebackArg {
+ uintptr_t context;
+ uintptr_t sigContext;
+ uintptr_t* buf;
+ uintptr_t max;
+};
+
+// msanGoTraceback is registered as the cgo traceback function.
+// This will be called when a signal occurs.
+void msanGoTraceback(void* parg) {
+ struct cgoTracebackArg* arg = (struct cgoTracebackArg*)(parg);
+ arg->buf[0] = 0;
+}
+
+// msanGoWait will be called with all registers undefined as far as
+// msan is concerned. It just waits for a signal.
+// Because the registers are msan-undefined, the signal handler will
+// be invoked with all registers msan-undefined.
+__attribute__((noinline))
+void msanGoWait(unsigned long a1, unsigned long a2, unsigned long a3, unsigned long a4, unsigned long a5, unsigned long a6) {
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigsuspend(&mask);
+}
+
+// msanGoSignalThread is the thread ID of the msanGoLoop thread.
+static pthread_t msanGoSignalThread;
+
+// msanGoSignalThreadSet is used to record that msanGoSignalThread
+// has been initialized. This is accessed atomically.
+static int32_t msanGoSignalThreadSet;
+
+// uninit is explicitly poisoned, so that we can make all registers
+// undefined by calling msanGoWait.
+static unsigned long uninit;
+
+// msanGoLoop loops calling msanGoWait, with the arguments passed
+// such that msan thinks that they are undefined. msan permits
+// undefined values to be used as long as they are not used to
+// for conditionals or for memory access.
+void msanGoLoop() {
+ int i;
+
+ msanGoSignalThread = pthread_self();
+ __atomic_store_n(&msanGoSignalThreadSet, 1, __ATOMIC_SEQ_CST);
+
+ // Force uninit to be undefined for msan.
+ __msan_poison(&uninit, sizeof uninit);
+ for (i = 0; i < 100; i++) {
+ msanGoWait(uninit, uninit, uninit, uninit, uninit, uninit);
+ }
+}
+
+// msanGoReady returns whether msanGoSignalThread is set.
+int msanGoReady() {
+ return __atomic_load_n(&msanGoSignalThreadSet, __ATOMIC_SEQ_CST) != 0;
+}
+
+// msanGoSendSignal sends a signal to the msanGoLoop thread.
+void msanGoSendSignal() {
+ pthread_kill(msanGoSignalThread, SIGWINCH);
+}
+*/
+import "C"
+
+import (
+ "runtime"
+ "time"
+)
+
+func main() {
+ runtime.SetCgoTraceback(0, C.msanGoTraceback, nil, nil)
+
+ c := make(chan bool)
+ go func() {
+ defer func() { c <- true }()
+ C.msanGoLoop()
+ }()
+
+ for C.msanGoReady() == 0 {
+ time.Sleep(time.Microsecond)
+ }
+
+loop:
+ for {
+ select {
+ case <-c:
+ break loop
+ default:
+ C.msanGoSendSignal()
+ time.Sleep(time.Microsecond)
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan_fail.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan_fail.go
new file mode 100644
index 0000000..4c8dab3
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan_fail.go
@@ -0,0 +1,36 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+/*
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+void f(int32_t *p, int n) {
+ int32_t * volatile q = (int32_t *)malloc(sizeof(int32_t) * n);
+ memcpy(p, q, n * sizeof(*p));
+ free(q);
+}
+
+void g(int32_t *p, int n) {
+ if (p[4] != 1) {
+ // We shouldn't get here; msan should stop us first.
+ exit(0);
+ }
+}
+*/
+import "C"
+
+import (
+ "unsafe"
+)
+
+func main() {
+ a := make([]int32, 10)
+ C.f((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a)))
+ a[3] = 1
+ C.g((*C.int32_t)(unsafe.Pointer(&a[0])), C.int(len(a)))
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/msan_shared.go b/src/cmd/cgo/internal/testsanitizers/testdata/msan_shared.go
new file mode 100644
index 0000000..966947c
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/msan_shared.go
@@ -0,0 +1,12 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This program segfaulted during libpreinit when built with -msan:
+// http://golang.org/issue/18707
+
+package main
+
+import "C"
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan.go
new file mode 100644
index 0000000..6c377a7
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan.go
@@ -0,0 +1,44 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program produced false race reports when run under the C/C++
+// ThreadSanitizer, as it did not understand the synchronization in
+// the Go code.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+
+int val;
+
+int getVal() {
+ return val;
+}
+
+void setVal(int i) {
+ val = i;
+}
+*/
+import "C"
+
+import (
+ "runtime"
+)
+
+func main() {
+ runtime.LockOSThread()
+ C.setVal(1)
+ c := make(chan bool)
+ go func() {
+ runtime.LockOSThread()
+ C.setVal(2)
+ c <- true
+ }()
+ <-c
+ if v := C.getVal(); v != 2 {
+ panic(v)
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan10.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan10.go
new file mode 100644
index 0000000..a40f245
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan10.go
@@ -0,0 +1,31 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program hung when run under the C/C++ ThreadSanitizer.
+// TSAN defers asynchronous signals until the signaled thread calls into libc.
+// Since the Go runtime makes direct futex syscalls, Go runtime threads could
+// run for an arbitrarily long time without triggering the libc interceptors.
+// See https://golang.org/issue/18717.
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+)
+
+/*
+#cgo CFLAGS: -g -fsanitize=thread
+#cgo LDFLAGS: -g -fsanitize=thread
+*/
+import "C"
+
+func main() {
+ c := make(chan os.Signal, 1)
+ signal.Notify(c, syscall.SIGUSR1)
+ defer signal.Stop(c)
+ syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
+ <-c
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan11.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan11.go
new file mode 100644
index 0000000..189e10f
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan11.go
@@ -0,0 +1,55 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program hung when run under the C/C++ ThreadSanitizer. TSAN defers
+// asynchronous signals until the signaled thread calls into libc. The runtime's
+// sysmon goroutine idles itself using direct usleep syscalls, so it could
+// run for an arbitrarily long time without triggering the libc interceptors.
+// See https://golang.org/issue/18717.
+
+import (
+ "os"
+ "os/signal"
+ "syscall"
+)
+
+/*
+#cgo CFLAGS: -g -fsanitize=thread
+#cgo LDFLAGS: -g -fsanitize=thread
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void raise_usr2(int signo) {
+ raise(SIGUSR2);
+}
+
+static void register_handler(int signo) {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_ONSTACK;
+ sa.sa_handler = raise_usr2;
+
+ if (sigaction(SIGUSR1, &sa, NULL) != 0) {
+ perror("failed to register SIGUSR1 handler");
+ exit(EXIT_FAILURE);
+ }
+}
+*/
+import "C"
+
+func main() {
+ ch := make(chan os.Signal, 1)
+ signal.Notify(ch, syscall.SIGUSR2)
+
+ C.register_handler(C.int(syscall.SIGUSR1))
+ syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
+
+ <-ch
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan12.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan12.go
new file mode 100644
index 0000000..0ef545d
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan12.go
@@ -0,0 +1,35 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program hung when run under the C/C++ ThreadSanitizer. TSAN installs a
+// libc interceptor that writes signal handlers to a global variable within the
+// TSAN runtime instead of making a sigaction system call. A bug in
+// syscall.runtime_AfterForkInChild corrupted TSAN's signal forwarding table
+// during calls to (*os/exec.Cmd).Run, causing the parent process to fail to
+// invoke signal handlers.
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "os/signal"
+ "syscall"
+)
+
+import "C"
+
+func main() {
+ ch := make(chan os.Signal, 1)
+ signal.Notify(ch, syscall.SIGUSR1)
+
+ if err := exec.Command("true").Run(); err != nil {
+ fmt.Fprintf(os.Stderr, "Unexpected error from `true`: %v", err)
+ os.Exit(1)
+ }
+
+ syscall.Kill(syscall.Getpid(), syscall.SIGUSR1)
+ <-ch
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan13.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan13.go
new file mode 100644
index 0000000..ebdf635
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan13.go
@@ -0,0 +1,90 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program failed when run under the C/C++ ThreadSanitizer.
+// There was no TSAN synchronization for the call to the cgo
+// traceback routine.
+
+/*
+#cgo CFLAGS: -g -fsanitize=thread
+#cgo LDFLAGS: -g -fsanitize=thread
+
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+struct tracebackArg {
+ uintptr_t Context;
+ uintptr_t SigContext;
+ uintptr_t* Buf;
+ uintptr_t Max;
+};
+
+void tsanTraceback(struct tracebackArg *arg) {
+ arg->Buf[0] = 0;
+}
+
+static void* spin(void *arg) {
+ size_t n;
+ struct timeval tvstart, tvnow;
+ int diff;
+ void *prev;
+ void *cur;
+
+ prev = NULL;
+ gettimeofday(&tvstart, NULL);
+ for (n = 0; n < 1<<20; n++) {
+ cur = malloc(n);
+ free(prev);
+ prev = cur;
+
+ gettimeofday(&tvnow, NULL);
+ diff = (tvnow.tv_sec - tvstart.tv_sec) * 1000 * 1000 + (tvnow.tv_usec - tvstart.tv_usec);
+
+ // Profile frequency is 100Hz so we should definitely
+ // get some signals in 50 milliseconds.
+ if (diff > 50 * 1000) {
+ break;
+ }
+ }
+
+ free(prev);
+
+ return NULL;
+}
+
+static void runThreads(int n) {
+ pthread_t ids[64];
+ int i;
+
+ if (n > 64) {
+ n = 64;
+ }
+ for (i = 0; i < n; i++) {
+ pthread_create(&ids[i], NULL, spin, NULL);
+ }
+ for (i = 0; i < n; i++) {
+ pthread_join(ids[i], NULL);
+ }
+}
+*/
+import "C"
+
+import (
+ "io"
+ "runtime"
+ "runtime/pprof"
+ "unsafe"
+)
+
+func main() {
+ runtime.SetCgoTraceback(0, unsafe.Pointer(C.tsanTraceback), nil, nil)
+ pprof.StartCPUProfile(io.Discard)
+ C.runThreads(C.int(runtime.GOMAXPROCS(0)))
+ pprof.StopCPUProfile()
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan14.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan14.go
new file mode 100644
index 0000000..d594ffb
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan14.go
@@ -0,0 +1,53 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program failed when run under the C/C++ ThreadSanitizer.
+//
+// cgocallback on a new thread calls into runtime.needm -> _cgo_getstackbound
+// to update gp.stack.lo with the stack bounds. If the G itself is passed to
+// _cgo_getstackbound, then writes to the same G can be seen on multiple
+// threads (when the G is reused after thread exit). This would trigger TSAN.
+
+/*
+#include <pthread.h>
+
+void go_callback();
+
+static void *thr(void *arg) {
+ go_callback();
+ return 0;
+}
+
+static void foo() {
+ pthread_t th;
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 256 << 10);
+ pthread_create(&th, &attr, thr, 0);
+ pthread_join(th, 0);
+}
+*/
+import "C"
+
+import (
+ "time"
+)
+
+//export go_callback
+func go_callback() {
+}
+
+func main() {
+ for i := 0; i < 2; i++ {
+ go func() {
+ for {
+ C.foo()
+ }
+ }()
+ }
+
+ time.Sleep(1000*time.Millisecond)
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan2.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan2.go
new file mode 100644
index 0000000..5018a19
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan2.go
@@ -0,0 +1,55 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program produced false race reports when run under the C/C++
+// ThreadSanitizer, as it did not understand the synchronization in
+// the Go code.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+
+extern void GoRun(void);
+
+// Yes, you can have definitions if you use //export, as long as they are weak.
+
+int val __attribute__ ((weak));
+
+int run(void) __attribute__ ((weak));
+
+int run() {
+ val = 1;
+ GoRun();
+ return val;
+}
+
+void setVal(int) __attribute__ ((weak));
+
+void setVal(int i) {
+ val = i;
+}
+*/
+import "C"
+
+import "runtime"
+
+//export GoRun
+func GoRun() {
+ runtime.LockOSThread()
+ c := make(chan bool)
+ go func() {
+ runtime.LockOSThread()
+ C.setVal(2)
+ c <- true
+ }()
+ <-c
+}
+
+func main() {
+ if v := C.run(); v != 2 {
+ panic(v)
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan3.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan3.go
new file mode 100644
index 0000000..87f6c80
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan3.go
@@ -0,0 +1,40 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// The stubs for the C functions read and write the same slot on the
+// g0 stack when copying arguments in and out.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+
+int Func1() {
+ return 0;
+}
+
+void Func2(int x) {
+ (void)x;
+}
+*/
+import "C"
+
+func main() {
+ const N = 10000
+ done := make(chan bool, N)
+ for i := 0; i < N; i++ {
+ go func() {
+ C.Func1()
+ done <- true
+ }()
+ go func() {
+ C.Func2(0)
+ done <- true
+ }()
+ }
+ for i := 0; i < 2*N; i++ {
+ <-done
+ }
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan4.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan4.go
new file mode 100644
index 0000000..f0c76d8
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan4.go
@@ -0,0 +1,34 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Check that calls to C.malloc/C.free do not trigger TSAN false
+// positive reports.
+
+// #cgo CFLAGS: -fsanitize=thread
+// #cgo LDFLAGS: -fsanitize=thread
+// #include <stdlib.h>
+import "C"
+
+import (
+ "runtime"
+ "sync"
+)
+
+func main() {
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 100; i++ {
+ p := C.malloc(C.size_t(i * 10))
+ runtime.Gosched()
+ C.free(p)
+ }
+ }()
+ }
+ wg.Wait()
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan5.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan5.go
new file mode 100644
index 0000000..1214a77
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan5.go
@@ -0,0 +1,51 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Check that calls to C.malloc/C.free do not collide with the calls
+// made by the os/user package.
+
+// #cgo CFLAGS: -fsanitize=thread
+// #cgo LDFLAGS: -fsanitize=thread
+// #include <stdlib.h>
+import "C"
+
+import (
+ "fmt"
+ "os"
+ "os/user"
+ "runtime"
+ "sync"
+)
+
+func main() {
+ u, err := user.Current()
+ if err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ // Let the test pass.
+ os.Exit(0)
+ }
+
+ var wg sync.WaitGroup
+ for i := 0; i < 20; i++ {
+ wg.Add(2)
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 1000; i++ {
+ user.Lookup(u.Username)
+ runtime.Gosched()
+ }
+ }()
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 1000; i++ {
+ p := C.malloc(C.size_t(len(u.Username) + 1))
+ runtime.Gosched()
+ C.free(p)
+ }
+ }()
+ }
+ wg.Wait()
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan6.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan6.go
new file mode 100644
index 0000000..c96f08d
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan6.go
@@ -0,0 +1,49 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Check that writes to Go allocated memory, with Go synchronization,
+// do not look like a race.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+
+void f(char *p) {
+ *p = 1;
+}
+*/
+import "C"
+
+import (
+ "runtime"
+ "sync"
+)
+
+func main() {
+ var wg sync.WaitGroup
+ var mu sync.Mutex
+ c := make(chan []C.char, 100)
+ for i := 0; i < 10; i++ {
+ wg.Add(2)
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 100; i++ {
+ c <- make([]C.char, 4096)
+ runtime.Gosched()
+ }
+ }()
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 100; i++ {
+ p := &(<-c)[0]
+ mu.Lock()
+ C.f(p)
+ mu.Unlock()
+ }
+ }()
+ }
+ wg.Wait()
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan7.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan7.go
new file mode 100644
index 0000000..2fb9e45
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan7.go
@@ -0,0 +1,40 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Setting an environment variable in a cgo program changes the C
+// environment. Test that this does not confuse the race detector.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+*/
+import "C"
+
+import (
+ "fmt"
+ "os"
+ "sync"
+ "time"
+)
+
+func main() {
+ var wg sync.WaitGroup
+ var mu sync.Mutex
+ f := func() {
+ defer wg.Done()
+ for i := 0; i < 100; i++ {
+ time.Sleep(time.Microsecond)
+ mu.Lock()
+ s := fmt.Sprint(i)
+ os.Setenv("TSAN_TEST"+s, s)
+ mu.Unlock()
+ }
+ }
+ wg.Add(2)
+ go f()
+ go f()
+ wg.Wait()
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan8.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan8.go
new file mode 100644
index 0000000..88d82a6
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan8.go
@@ -0,0 +1,60 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program failed when run under the C/C++ ThreadSanitizer. The TSAN
+// sigaction function interceptor returned SIG_DFL instead of the Go runtime's
+// handler in registerSegvForwarder.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct sigaction prev_sa;
+
+void forwardSignal(int signo, siginfo_t *info, void *context) {
+ // One of sa_sigaction and/or sa_handler
+ if ((prev_sa.sa_flags&SA_SIGINFO) != 0) {
+ prev_sa.sa_sigaction(signo, info, context);
+ return;
+ }
+ if (prev_sa.sa_handler != SIG_IGN && prev_sa.sa_handler != SIG_DFL) {
+ prev_sa.sa_handler(signo);
+ return;
+ }
+
+ fprintf(stderr, "No Go handler to forward to!\n");
+ abort();
+}
+
+void registerSegvFowarder() {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
+ sa.sa_sigaction = forwardSignal;
+
+ if (sigaction(SIGSEGV, &sa, &prev_sa) != 0) {
+ perror("failed to register SEGV forwarder");
+ exit(EXIT_FAILURE);
+ }
+}
+*/
+import "C"
+
+func main() {
+ C.registerSegvFowarder()
+
+ defer func() {
+ recover()
+ }()
+ var nilp *int
+ *nilp = 42
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan9.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan9.go
new file mode 100644
index 0000000..06304be
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan9.go
@@ -0,0 +1,67 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program failed when run under the C/C++ ThreadSanitizer. The
+// TSAN library was not keeping track of whether signals should be
+// delivered on the alternate signal stack, and the Go signal handler
+// was not preserving callee-saved registers from C callers.
+
+/*
+#cgo CFLAGS: -g -fsanitize=thread
+#cgo LDFLAGS: -g -fsanitize=thread
+
+#include <stdlib.h>
+#include <sys/time.h>
+
+void spin() {
+ size_t n;
+ struct timeval tvstart, tvnow;
+ int diff;
+ void *prev = NULL, *cur;
+
+ gettimeofday(&tvstart, NULL);
+ for (n = 0; n < 1<<20; n++) {
+ cur = malloc(n);
+ free(prev);
+ prev = cur;
+
+ gettimeofday(&tvnow, NULL);
+ diff = (tvnow.tv_sec - tvstart.tv_sec) * 1000 * 1000 + (tvnow.tv_usec - tvstart.tv_usec);
+
+ // Profile frequency is 100Hz so we should definitely
+ // get a signal in 50 milliseconds.
+ if (diff > 50 * 1000) {
+ break;
+ }
+ }
+
+ free(prev);
+}
+*/
+import "C"
+
+import (
+ "io"
+ "runtime/pprof"
+ "time"
+)
+
+func goSpin() {
+ start := time.Now()
+ for n := 0; n < 1<<20; n++ {
+ _ = make([]byte, n)
+ if time.Since(start) > 50*time.Millisecond {
+ break
+ }
+ }
+}
+
+func main() {
+ pprof.StartCPUProfile(io.Discard)
+ go C.spin()
+ goSpin()
+ pprof.StopCPUProfile()
+}
diff --git a/src/cmd/cgo/internal/testsanitizers/testdata/tsan_shared.go b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_shared.go
new file mode 100644
index 0000000..55ff67e
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/testdata/tsan_shared.go
@@ -0,0 +1,63 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// This program failed with SIGSEGV when run under the C/C++ ThreadSanitizer.
+// The Go runtime had re-registered the C handler with the wrong flags due to a
+// typo, resulting in null pointers being passed for the info and context
+// parameters to the handler.
+
+/*
+#cgo CFLAGS: -fsanitize=thread
+#cgo LDFLAGS: -fsanitize=thread
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ucontext.h>
+
+void check_params(int signo, siginfo_t *info, void *context) {
+ ucontext_t* uc = (ucontext_t*)(context);
+
+ if (info->si_signo != signo) {
+ fprintf(stderr, "info->si_signo does not match signo.\n");
+ abort();
+ }
+
+ if (uc->uc_stack.ss_size == 0) {
+ fprintf(stderr, "uc_stack has size 0.\n");
+ abort();
+ }
+}
+
+
+// Set up the signal handler in a high priority constructor, so
+// that it is installed before the Go code starts.
+
+static void register_handler(void) __attribute__ ((constructor (200)));
+
+static void register_handler() {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_SIGINFO;
+ sa.sa_sigaction = check_params;
+
+ if (sigaction(SIGUSR1, &sa, NULL) != 0) {
+ perror("failed to register SIGUSR1 handler");
+ exit(EXIT_FAILURE);
+ }
+}
+*/
+import "C"
+
+import "syscall"
+
+func init() {
+ C.raise(C.int(syscall.SIGUSR1))
+}
+
+func main() {}
diff --git a/src/cmd/cgo/internal/testsanitizers/tsan_test.go b/src/cmd/cgo/internal/testsanitizers/tsan_test.go
new file mode 100644
index 0000000..8e758e6
--- /dev/null
+++ b/src/cmd/cgo/internal/testsanitizers/tsan_test.go
@@ -0,0 +1,80 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build linux || (freebsd && amd64)
+
+package sanitizers_test
+
+import (
+ "internal/testenv"
+ "strings"
+ "testing"
+)
+
+func TestTSAN(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveCGO(t)
+
+ goos, err := goEnv("GOOS")
+ if err != nil {
+ t.Fatal(err)
+ }
+ goarch, err := goEnv("GOARCH")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // The msan tests require support for the -msan option.
+ if !compilerRequiredTsanVersion(goos, goarch) {
+ t.Skipf("skipping on %s/%s; compiler version for -tsan option is too old.", goos, goarch)
+ }
+
+ t.Parallel()
+ requireOvercommit(t)
+ config := configure("thread")
+ config.skipIfCSanitizerBroken(t)
+
+ mustRun(t, config.goCmd("build", "std"))
+
+ cases := []struct {
+ src string
+ needsRuntime bool
+ }{
+ {src: "tsan.go"},
+ {src: "tsan2.go"},
+ {src: "tsan3.go"},
+ {src: "tsan4.go"},
+ {src: "tsan5.go", needsRuntime: true},
+ {src: "tsan6.go", needsRuntime: true},
+ {src: "tsan7.go", needsRuntime: true},
+ {src: "tsan8.go"},
+ {src: "tsan9.go"},
+ {src: "tsan10.go", needsRuntime: true},
+ {src: "tsan11.go", needsRuntime: true},
+ {src: "tsan12.go", needsRuntime: true},
+ {src: "tsan13.go", needsRuntime: true},
+ {src: "tsan14.go", needsRuntime: true},
+ }
+ for _, tc := range cases {
+ tc := tc
+ name := strings.TrimSuffix(tc.src, ".go")
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ dir := newTempDir(t)
+ defer dir.RemoveAll(t)
+
+ outPath := dir.Join(name)
+ mustRun(t, config.goCmd("build", "-o", outPath, srcPath(tc.src)))
+
+ cmd := hangProneCmd(outPath)
+ if tc.needsRuntime {
+ config.skipIfRuntimeIncompatible(t)
+ }
+ // If we don't see halt_on_error, the program
+ // will only exit non-zero if we call C.exit.
+ cmd.Env = append(cmd.Environ(), "TSAN_OPTIONS=halt_on_error=1")
+ mustRun(t, cmd)
+ })
+ }
+}
diff --git a/src/cmd/cgo/internal/testshared/shared_test.go b/src/cmd/cgo/internal/testshared/shared_test.go
new file mode 100644
index 0000000..796c46b
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/shared_test.go
@@ -0,0 +1,1174 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package shared_test
+
+import (
+ "bufio"
+ "bytes"
+ "cmd/cgo/internal/cgotest"
+ "debug/elf"
+ "encoding/binary"
+ "flag"
+ "fmt"
+ "go/build"
+ "internal/platform"
+ "internal/testenv"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "testing"
+ "time"
+)
+
+var globalSkip = func(t testing.TB) {}
+
+var gopathInstallDir, gorootInstallDir string
+var oldGOROOT string
+
+// This is the smallest set of packages we can link into a shared
+// library (runtime/cgo is built implicitly).
+var minpkgs = []string{"runtime", "sync/atomic"}
+var soname = "libruntime,sync-atomic.so"
+
+var testX = flag.Bool("testx", false, "if true, pass -x to 'go' subcommands invoked by the test")
+var testWork = flag.Bool("testwork", false, "if true, log and do not delete the temporary working directory")
+
+// run runs a command and calls t.Errorf if it fails.
+func run(t *testing.T, msg string, args ...string) {
+ runWithEnv(t, msg, nil, args...)
+}
+
+// runWithEnv runs a command under the given environment and calls t.Errorf if it fails.
+func runWithEnv(t *testing.T, msg string, env []string, args ...string) {
+ c := exec.Command(args[0], args[1:]...)
+ if len(env) != 0 {
+ c.Env = append(os.Environ(), env...)
+ }
+ if output, err := c.CombinedOutput(); err != nil {
+ t.Errorf("executing %s (%s) failed %s:\n%s", strings.Join(args, " "), msg, err, output)
+ }
+}
+
+// goCmd invokes the go tool with the installsuffix set up by TestMain. It calls
+// t.Fatalf if the command fails.
+func goCmd(t *testing.T, args ...string) string {
+ newargs := []string{args[0]}
+ if *testX && args[0] != "env" {
+ newargs = append(newargs, "-x", "-ldflags=-v")
+ }
+ newargs = append(newargs, args[1:]...)
+ c := exec.Command(filepath.Join(oldGOROOT, "bin", "go"), newargs...)
+ stderr := new(strings.Builder)
+ c.Stderr = stderr
+
+ if testing.Verbose() && t == nil {
+ fmt.Fprintf(os.Stderr, "+ go %s\n", strings.Join(args, " "))
+ c.Stderr = os.Stderr
+ }
+ output, err := c.Output()
+
+ if err != nil {
+ if t != nil {
+ t.Helper()
+ t.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr)
+ } else {
+ // Panic instead of using log.Fatalf so that deferred cleanup may run in testMain.
+ log.Panicf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, stderr)
+ }
+ }
+ if testing.Verbose() && t != nil {
+ t.Logf("go %s", strings.Join(args, " "))
+ if stderr.Len() > 0 {
+ t.Logf("%s", stderr)
+ }
+ }
+ return string(bytes.TrimSpace(output))
+}
+
+// TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit).
+func testMain(m *testing.M) (int, error) {
+ if !platform.BuildModeSupported(runtime.Compiler, "shared", runtime.GOOS, runtime.GOARCH) {
+ globalSkip = func(t testing.TB) { t.Skip("shared build mode not supported") }
+ return m.Run(), nil
+ }
+ if !testenv.HasCGO() {
+ globalSkip = testenv.MustHaveCGO
+ return m.Run(), nil
+ }
+
+ cwd, err := os.Getwd()
+ if err != nil {
+ log.Fatal(err)
+ }
+ oldGOROOT = filepath.Join(cwd, "../../../../..")
+
+ workDir, err := os.MkdirTemp("", "shared_test")
+ if err != nil {
+ return 0, err
+ }
+ if *testWork || testing.Verbose() {
+ fmt.Printf("+ mkdir -p %s\n", workDir)
+ }
+ if !*testWork {
+ defer os.RemoveAll(workDir)
+ }
+
+ // -buildmode=shared fundamentally does not work in module mode.
+ // (It tries to share package dependencies across builds, but in module mode
+ // each module has its own distinct set of dependency versions.)
+ // We would like to eliminate it (see https://go.dev/issue/47788),
+ // but first need to figure out a replacement that covers the small subset
+ // of use-cases where -buildmode=shared still works today.
+ // For now, run the tests in GOPATH mode only.
+ os.Setenv("GO111MODULE", "off")
+
+ // Some tests need to edit the source in GOPATH, so copy this directory to a
+ // temporary directory and chdir to that.
+ gopath := filepath.Join(workDir, "gopath")
+ modRoot, err := cloneTestdataModule(gopath)
+ if err != nil {
+ return 0, err
+ }
+ if testing.Verbose() {
+ fmt.Printf("+ export GOPATH=%s\n", gopath)
+ fmt.Printf("+ cd %s\n", modRoot)
+ }
+ os.Setenv("GOPATH", gopath)
+ // Explicitly override GOBIN as well, in case it was set through a GOENV file.
+ os.Setenv("GOBIN", filepath.Join(gopath, "bin"))
+ os.Chdir(modRoot)
+ os.Setenv("PWD", modRoot)
+
+ // The test also needs to install libraries into GOROOT/pkg, so copy the
+ // subset of GOROOT that we need.
+ //
+ // TODO(golang.org/issue/28553): Rework -buildmode=shared so that it does not
+ // need to write to GOROOT.
+ goroot := filepath.Join(workDir, "goroot")
+ if err := cloneGOROOTDeps(goroot); err != nil {
+ return 0, err
+ }
+ if testing.Verbose() {
+ fmt.Fprintf(os.Stderr, "+ export GOROOT=%s\n", goroot)
+ }
+ os.Setenv("GOROOT", goroot)
+
+ myContext := build.Default
+ myContext.GOROOT = goroot
+ myContext.GOPATH = gopath
+
+ // All tests depend on runtime being built into a shared library. Because
+ // that takes a few seconds, do it here and have all tests use the version
+ // built here.
+ goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs...)...)
+
+ shlib := goCmd(nil, "list", "-linkshared", "-f={{.Shlib}}", "runtime")
+ if shlib != "" {
+ gorootInstallDir = filepath.Dir(shlib)
+ }
+
+ myContext.InstallSuffix = "_dynlink"
+ depP, err := myContext.Import("./depBase", ".", build.ImportComment)
+ if err != nil {
+ return 0, fmt.Errorf("import failed: %v", err)
+ }
+ if depP.PkgTargetRoot == "" {
+ gopathInstallDir = filepath.Dir(goCmd(nil, "list", "-buildmode=shared", "-f", "{{.Target}}", "./depBase"))
+ } else {
+ gopathInstallDir = filepath.Join(depP.PkgTargetRoot, "testshared")
+ }
+ return m.Run(), nil
+}
+
+func TestMain(m *testing.M) {
+ log.SetFlags(log.Lshortfile)
+ flag.Parse()
+
+ exitCode, err := testMain(m)
+ if err != nil {
+ log.Fatal(err)
+ }
+ os.Exit(exitCode)
+}
+
+// cloneTestdataModule clones the packages from src/testshared into gopath.
+// It returns the directory within gopath at which the module root is located.
+func cloneTestdataModule(gopath string) (string, error) {
+ modRoot := filepath.Join(gopath, "src", "testshared")
+ if err := cgotest.OverlayDir(modRoot, "testdata"); err != nil {
+ return "", err
+ }
+ if err := os.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module testshared\n"), 0644); err != nil {
+ return "", err
+ }
+ return modRoot, nil
+}
+
+// cloneGOROOTDeps copies (or symlinks) the portions of GOROOT/src and
+// GOROOT/pkg relevant to this test into the given directory.
+// It must be run from within the testdata module.
+func cloneGOROOTDeps(goroot string) error {
+ // Before we clone GOROOT, figure out which packages we need to copy over.
+ listArgs := []string{
+ "list",
+ "-deps",
+ "-f", "{{if and .Standard (not .ForTest)}}{{.ImportPath}}{{end}}",
+ }
+ stdDeps := goCmd(nil, append(listArgs, minpkgs...)...)
+ testdataDeps := goCmd(nil, append(listArgs, "-test", "./...")...)
+
+ pkgs := append(strings.Split(strings.TrimSpace(stdDeps), "\n"),
+ strings.Split(strings.TrimSpace(testdataDeps), "\n")...)
+ sort.Strings(pkgs)
+ var pkgRoots []string
+ for _, pkg := range pkgs {
+ parentFound := false
+ for _, prev := range pkgRoots {
+ if pkg == prev || strings.HasPrefix(pkg, prev+"/") {
+ // We will copy in the source for pkg when we copy in prev.
+ parentFound = true
+ break
+ }
+ }
+ if !parentFound {
+ pkgRoots = append(pkgRoots, pkg)
+ }
+ }
+
+ gorootDirs := []string{
+ "pkg/tool",
+ "pkg/include",
+ }
+ for _, pkg := range pkgRoots {
+ gorootDirs = append(gorootDirs, filepath.Join("src", pkg))
+ }
+
+ for _, dir := range gorootDirs {
+ if testing.Verbose() {
+ fmt.Fprintf(os.Stderr, "+ cp -r %s %s\n", filepath.Join(oldGOROOT, dir), filepath.Join(goroot, dir))
+ }
+ if err := cgotest.OverlayDir(filepath.Join(goroot, dir), filepath.Join(oldGOROOT, dir)); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// The shared library was built at the expected location.
+func TestSOBuilt(t *testing.T) {
+ globalSkip(t)
+ _, err := os.Stat(filepath.Join(gorootInstallDir, soname))
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func hasDynTag(f *elf.File, tag elf.DynTag) bool {
+ ds := f.SectionByType(elf.SHT_DYNAMIC)
+ if ds == nil {
+ return false
+ }
+ d, err := ds.Data()
+ if err != nil {
+ return false
+ }
+ for len(d) > 0 {
+ var t elf.DynTag
+ switch f.Class {
+ case elf.ELFCLASS32:
+ t = elf.DynTag(f.ByteOrder.Uint32(d[0:4]))
+ d = d[8:]
+ case elf.ELFCLASS64:
+ t = elf.DynTag(f.ByteOrder.Uint64(d[0:8]))
+ d = d[16:]
+ }
+ if t == tag {
+ return true
+ }
+ }
+ return false
+}
+
+// The shared library does not have relocations against the text segment.
+func TestNoTextrel(t *testing.T) {
+ globalSkip(t)
+ sopath := filepath.Join(gorootInstallDir, soname)
+ f, err := elf.Open(sopath)
+ if err != nil {
+ t.Fatal("elf.Open failed: ", err)
+ }
+ defer f.Close()
+ if hasDynTag(f, elf.DT_TEXTREL) {
+ t.Errorf("%s has DT_TEXTREL set", soname)
+ }
+}
+
+// The shared library does not contain symbols called ".dup"
+// (See golang.org/issue/14841.)
+func TestNoDupSymbols(t *testing.T) {
+ globalSkip(t)
+ sopath := filepath.Join(gorootInstallDir, soname)
+ f, err := elf.Open(sopath)
+ if err != nil {
+ t.Fatal("elf.Open failed: ", err)
+ }
+ defer f.Close()
+ syms, err := f.Symbols()
+ if err != nil {
+ t.Errorf("error reading symbols %v", err)
+ return
+ }
+ for _, s := range syms {
+ if s.Name == ".dup" {
+ t.Fatalf("%s contains symbol called .dup", sopath)
+ }
+ }
+}
+
+// The install command should have created a "shlibname" file for the
+// listed packages (and runtime/cgo, and math on arm) indicating the
+// name of the shared library containing it.
+func TestShlibnameFiles(t *testing.T) {
+ globalSkip(t)
+ pkgs := append([]string{}, minpkgs...)
+ pkgs = append(pkgs, "runtime/cgo")
+ if runtime.GOARCH == "arm" {
+ pkgs = append(pkgs, "math")
+ }
+ for _, pkg := range pkgs {
+ shlibnamefile := filepath.Join(gorootInstallDir, pkg+".shlibname")
+ contentsb, err := os.ReadFile(shlibnamefile)
+ if err != nil {
+ t.Errorf("error reading shlibnamefile for %s: %v", pkg, err)
+ continue
+ }
+ contents := strings.TrimSpace(string(contentsb))
+ if contents != soname {
+ t.Errorf("shlibnamefile for %s has wrong contents: %q", pkg, contents)
+ }
+ }
+}
+
+// Is a given offset into the file contained in a loaded segment?
+func isOffsetLoaded(f *elf.File, offset uint64) bool {
+ for _, prog := range f.Progs {
+ if prog.Type == elf.PT_LOAD {
+ if prog.Off <= offset && offset < prog.Off+prog.Filesz {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+func rnd(v int32, r int32) int32 {
+ if r <= 0 {
+ return v
+ }
+ v += r - 1
+ c := v % r
+ if c < 0 {
+ c += r
+ }
+ v -= c
+ return v
+}
+
+func readwithpad(r io.Reader, sz int32) ([]byte, error) {
+ data := make([]byte, rnd(sz, 4))
+ _, err := io.ReadFull(r, data)
+ if err != nil {
+ return nil, err
+ }
+ data = data[:sz]
+ return data, nil
+}
+
+type note struct {
+ name string
+ tag int32
+ desc string
+ section *elf.Section
+}
+
+// Read all notes from f. As ELF section names are not supposed to be special, one
+// looks for a particular note by scanning all SHT_NOTE sections looking for a note
+// with a particular "name" and "tag".
+func readNotes(f *elf.File) ([]*note, error) {
+ var notes []*note
+ for _, sect := range f.Sections {
+ if sect.Type != elf.SHT_NOTE {
+ continue
+ }
+ r := sect.Open()
+ for {
+ var namesize, descsize, tag int32
+ err := binary.Read(r, f.ByteOrder, &namesize)
+ if err != nil {
+ if err == io.EOF {
+ break
+ }
+ return nil, fmt.Errorf("read namesize failed: %v", err)
+ }
+ err = binary.Read(r, f.ByteOrder, &descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read descsize failed: %v", err)
+ }
+ err = binary.Read(r, f.ByteOrder, &tag)
+ if err != nil {
+ return nil, fmt.Errorf("read type failed: %v", err)
+ }
+ name, err := readwithpad(r, namesize)
+ if err != nil {
+ return nil, fmt.Errorf("read name failed: %v", err)
+ }
+ desc, err := readwithpad(r, descsize)
+ if err != nil {
+ return nil, fmt.Errorf("read desc failed: %v", err)
+ }
+ notes = append(notes, &note{name: string(name), tag: tag, desc: string(desc), section: sect})
+ }
+ }
+ return notes, nil
+}
+
+func dynStrings(t *testing.T, path string, flag elf.DynTag) []string {
+ t.Helper()
+ f, err := elf.Open(path)
+ if err != nil {
+ t.Fatalf("elf.Open(%q) failed: %v", path, err)
+ }
+ defer f.Close()
+ dynstrings, err := f.DynString(flag)
+ if err != nil {
+ t.Fatalf("DynString(%s) failed on %s: %v", flag, path, err)
+ }
+ return dynstrings
+}
+
+func AssertIsLinkedToRegexp(t *testing.T, path string, re *regexp.Regexp) {
+ t.Helper()
+ for _, dynstring := range dynStrings(t, path, elf.DT_NEEDED) {
+ if re.MatchString(dynstring) {
+ return
+ }
+ }
+ t.Errorf("%s is not linked to anything matching %v", path, re)
+}
+
+func AssertIsLinkedTo(t *testing.T, path, lib string) {
+ t.Helper()
+ AssertIsLinkedToRegexp(t, path, regexp.MustCompile(regexp.QuoteMeta(lib)))
+}
+
+func AssertHasRPath(t *testing.T, path, dir string) {
+ t.Helper()
+ for _, tag := range []elf.DynTag{elf.DT_RPATH, elf.DT_RUNPATH} {
+ for _, dynstring := range dynStrings(t, path, tag) {
+ for _, rpath := range strings.Split(dynstring, ":") {
+ if filepath.Clean(rpath) == filepath.Clean(dir) {
+ return
+ }
+ }
+ }
+ }
+ t.Errorf("%s does not have rpath %s", path, dir)
+}
+
+// Build a trivial program that links against the shared runtime and check it runs.
+func TestTrivialExecutable(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-linkshared", "./trivial")
+ run(t, "trivial executable", "../../bin/trivial")
+ AssertIsLinkedTo(t, "../../bin/trivial", soname)
+ AssertHasRPath(t, "../../bin/trivial", gorootInstallDir)
+ // It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment
+ // 4*64k should be enough, but this might need revision eventually.
+ checkSize(t, "../../bin/trivial", 256000)
+}
+
+// Build a trivial program in PIE mode that links against the shared runtime and check it runs.
+func TestTrivialExecutablePIE(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "build", "-buildmode=pie", "-o", "trivial.pie", "-linkshared", "./trivial")
+ run(t, "trivial executable", "./trivial.pie")
+ AssertIsLinkedTo(t, "./trivial.pie", soname)
+ AssertHasRPath(t, "./trivial.pie", gorootInstallDir)
+ // It is 19K on linux/amd64, with separate-code in binutils ld and 64k being most common alignment
+ // 4*64k should be enough, but this might need revision eventually.
+ checkSize(t, "./trivial.pie", 256000)
+}
+
+// Check that the file size does not exceed a limit.
+func checkSize(t *testing.T, f string, limit int64) {
+ fi, err := os.Stat(f)
+ if err != nil {
+ t.Fatalf("stat failed: %v", err)
+ }
+ if sz := fi.Size(); sz > limit {
+ t.Errorf("file too large: got %d, want <= %d", sz, limit)
+ }
+}
+
+// Build a division test program and check it runs.
+func TestDivisionExecutable(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-linkshared", "./division")
+ run(t, "division executable", "../../bin/division")
+}
+
+// Build an executable that uses cgo linked against the shared runtime and check it
+// runs.
+func TestCgoExecutable(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-linkshared", "./execgo")
+ run(t, "cgo executable", "../../bin/execgo")
+}
+
+func checkPIE(t *testing.T, name string) {
+ f, err := elf.Open(name)
+ if err != nil {
+ t.Fatal("elf.Open failed: ", err)
+ }
+ defer f.Close()
+ if f.Type != elf.ET_DYN {
+ t.Errorf("%s has type %v, want ET_DYN", name, f.Type)
+ }
+ if hasDynTag(f, elf.DT_TEXTREL) {
+ t.Errorf("%s has DT_TEXTREL set", name)
+ }
+}
+
+func TestTrivialPIE(t *testing.T) {
+ if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-alpine") {
+ t.Skip("skipping on alpine until issue #54354 resolved")
+ }
+ globalSkip(t)
+ testenv.MustHaveBuildMode(t, "pie")
+ name := "trivial_pie"
+ goCmd(t, "build", "-buildmode=pie", "-o="+name, "./trivial")
+ defer os.Remove(name)
+ run(t, name, "./"+name)
+ checkPIE(t, name)
+}
+
+func TestCgoPIE(t *testing.T) {
+ globalSkip(t)
+ testenv.MustHaveCGO(t)
+ testenv.MustHaveBuildMode(t, "pie")
+ name := "cgo_pie"
+ goCmd(t, "build", "-buildmode=pie", "-o="+name, "./execgo")
+ defer os.Remove(name)
+ run(t, name, "./"+name)
+ checkPIE(t, name)
+}
+
+// Build a GOPATH package into a shared library that links against the goroot runtime
+// and an executable that links against both.
+func TestGopathShlib(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
+ shlib := goCmd(t, "list", "-f", "{{.Shlib}}", "-buildmode=shared", "-linkshared", "./depBase")
+ AssertIsLinkedTo(t, shlib, soname)
+ goCmd(t, "install", "-linkshared", "./exe")
+ AssertIsLinkedTo(t, "../../bin/exe", soname)
+ AssertIsLinkedTo(t, "../../bin/exe", filepath.Base(shlib))
+ AssertHasRPath(t, "../../bin/exe", gorootInstallDir)
+ AssertHasRPath(t, "../../bin/exe", filepath.Dir(gopathInstallDir))
+ // And check it runs.
+ run(t, "executable linked to GOPATH library", "../../bin/exe")
+}
+
+// The shared library contains a note listing the packages it contains in a section
+// that is not mapped into memory.
+func testPkgListNote(t *testing.T, f *elf.File, note *note) {
+ if note.section.Flags != 0 {
+ t.Errorf("package list section has flags %v, want 0", note.section.Flags)
+ }
+ if isOffsetLoaded(f, note.section.Offset) {
+ t.Errorf("package list section contained in PT_LOAD segment")
+ }
+ if note.desc != "testshared/depBase\n" {
+ t.Errorf("incorrect package list %q, want %q", note.desc, "testshared/depBase\n")
+ }
+}
+
+// The shared library contains a note containing the ABI hash that is mapped into
+// memory and there is a local symbol called go.link.abihashbytes that points 16
+// bytes into it.
+func testABIHashNote(t *testing.T, f *elf.File, note *note) {
+ if note.section.Flags != elf.SHF_ALLOC {
+ t.Errorf("abi hash section has flags %v, want SHF_ALLOC", note.section.Flags)
+ }
+ if !isOffsetLoaded(f, note.section.Offset) {
+ t.Errorf("abihash section not contained in PT_LOAD segment")
+ }
+ var hashbytes elf.Symbol
+ symbols, err := f.Symbols()
+ if err != nil {
+ t.Errorf("error reading symbols %v", err)
+ return
+ }
+ for _, sym := range symbols {
+ if sym.Name == "go:link.abihashbytes" {
+ hashbytes = sym
+ }
+ }
+ if hashbytes.Name == "" {
+ t.Errorf("no symbol called go:link.abihashbytes")
+ return
+ }
+ if elf.ST_BIND(hashbytes.Info) != elf.STB_LOCAL {
+ t.Errorf("%s has incorrect binding %v, want STB_LOCAL", hashbytes.Name, elf.ST_BIND(hashbytes.Info))
+ }
+ if f.Sections[hashbytes.Section] != note.section {
+ t.Errorf("%s has incorrect section %v, want %s", hashbytes.Name, f.Sections[hashbytes.Section].Name, note.section.Name)
+ }
+ if hashbytes.Value-note.section.Addr != 16 {
+ t.Errorf("%s has incorrect offset into section %d, want 16", hashbytes.Name, hashbytes.Value-note.section.Addr)
+ }
+}
+
+// A Go shared library contains a note indicating which other Go shared libraries it
+// was linked against in an unmapped section.
+func testDepsNote(t *testing.T, f *elf.File, note *note) {
+ if note.section.Flags != 0 {
+ t.Errorf("package list section has flags %v, want 0", note.section.Flags)
+ }
+ if isOffsetLoaded(f, note.section.Offset) {
+ t.Errorf("package list section contained in PT_LOAD segment")
+ }
+ // libdepBase.so just links against the lib containing the runtime.
+ if note.desc != soname {
+ t.Errorf("incorrect dependency list %q, want %q", note.desc, soname)
+ }
+}
+
+// The shared library contains notes with defined contents; see above.
+func TestNotes(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
+ shlib := goCmd(t, "list", "-f", "{{.Shlib}}", "-buildmode=shared", "-linkshared", "./depBase")
+ f, err := elf.Open(shlib)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ notes, err := readNotes(f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkgListNoteFound := false
+ abiHashNoteFound := false
+ depsNoteFound := false
+ for _, note := range notes {
+ if note.name != "Go\x00\x00" {
+ continue
+ }
+ switch note.tag {
+ case 1: // ELF_NOTE_GOPKGLIST_TAG
+ if pkgListNoteFound {
+ t.Error("multiple package list notes")
+ }
+ testPkgListNote(t, f, note)
+ pkgListNoteFound = true
+ case 2: // ELF_NOTE_GOABIHASH_TAG
+ if abiHashNoteFound {
+ t.Error("multiple abi hash notes")
+ }
+ testABIHashNote(t, f, note)
+ abiHashNoteFound = true
+ case 3: // ELF_NOTE_GODEPS_TAG
+ if depsNoteFound {
+ t.Error("multiple dependency list notes")
+ }
+ testDepsNote(t, f, note)
+ depsNoteFound = true
+ }
+ }
+ if !pkgListNoteFound {
+ t.Error("package list note not found")
+ }
+ if !abiHashNoteFound {
+ t.Error("abi hash note not found")
+ }
+ if !depsNoteFound {
+ t.Error("deps note not found")
+ }
+}
+
+// Build a GOPATH package (depBase) into a shared library that links against the goroot
+// runtime, another package (dep2) that links against the first, and an
+// executable that links against dep2.
+func TestTwoGopathShlibs(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep2")
+ goCmd(t, "install", "-linkshared", "./exe2")
+ run(t, "executable linked to GOPATH library", "../../bin/exe2")
+}
+
+func TestThreeGopathShlibs(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep2")
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./dep3")
+ goCmd(t, "install", "-linkshared", "./exe3")
+ run(t, "executable linked to GOPATH library", "../../bin/exe3")
+}
+
+// If gccgo is not available or not new enough, call t.Skip.
+func requireGccgo(t *testing.T) {
+ t.Helper()
+
+ if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" {
+ t.Skip("gccgo test skipped on PPC64 until issue #60798 is resolved")
+ }
+
+ gccgoName := os.Getenv("GCCGO")
+ if gccgoName == "" {
+ gccgoName = "gccgo"
+ }
+ gccgoPath, err := exec.LookPath(gccgoName)
+ if err != nil {
+ t.Skip("gccgo not found")
+ }
+ cmd := exec.Command(gccgoPath, "-dumpversion")
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%s -dumpversion failed: %v\n%s", gccgoPath, err, output)
+ }
+ dot := bytes.Index(output, []byte{'.'})
+ if dot > 0 {
+ output = output[:dot]
+ }
+ major, err := strconv.Atoi(strings.TrimSpace(string(output)))
+ if err != nil {
+ t.Skipf("can't parse gccgo version number %s", output)
+ }
+ if major < 5 {
+ t.Skipf("gccgo too old (%s)", strings.TrimSpace(string(output)))
+ }
+
+ gomod, err := exec.Command("go", "env", "GOMOD").Output()
+ if err != nil {
+ t.Fatalf("go env GOMOD: %v", err)
+ }
+ if len(bytes.TrimSpace(gomod)) > 0 {
+ t.Skipf("gccgo not supported in module mode; see golang.org/issue/30344")
+ }
+}
+
+// Build a GOPATH package into a shared library with gccgo and an executable that
+// links against it.
+func TestGoPathShlibGccgo(t *testing.T) {
+ globalSkip(t)
+ requireGccgo(t)
+
+ libgoRE := regexp.MustCompile("libgo.so.[0-9]+")
+
+ goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./depBase")
+
+ // Run 'go list' after 'go install': with gccgo, we apparently don't know the
+ // shlib location until after we've installed it.
+ shlib := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./depBase")
+
+ AssertIsLinkedToRegexp(t, shlib, libgoRE)
+ goCmd(t, "install", "-compiler=gccgo", "-linkshared", "./exe")
+ AssertIsLinkedToRegexp(t, "../../bin/exe", libgoRE)
+ AssertIsLinkedTo(t, "../../bin/exe", filepath.Base(shlib))
+ AssertHasRPath(t, "../../bin/exe", filepath.Dir(shlib))
+ // And check it runs.
+ run(t, "gccgo-built", "../../bin/exe")
+}
+
+// The gccgo version of TestTwoGopathShlibs: build a GOPATH package into a shared
+// library with gccgo, another GOPATH package that depends on the first and an
+// executable that links the second library.
+func TestTwoGopathShlibsGccgo(t *testing.T) {
+ globalSkip(t)
+ requireGccgo(t)
+
+ libgoRE := regexp.MustCompile("libgo.so.[0-9]+")
+
+ goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./depBase")
+ goCmd(t, "install", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "./dep2")
+ goCmd(t, "install", "-compiler=gccgo", "-linkshared", "./exe2")
+
+ // Run 'go list' after 'go install': with gccgo, we apparently don't know the
+ // shlib location until after we've installed it.
+ dep2 := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./dep2")
+ depBase := goCmd(t, "list", "-compiler=gccgo", "-buildmode=shared", "-linkshared", "-f", "{{.Shlib}}", "./depBase")
+
+ AssertIsLinkedToRegexp(t, depBase, libgoRE)
+ AssertIsLinkedToRegexp(t, dep2, libgoRE)
+ AssertIsLinkedTo(t, dep2, filepath.Base(depBase))
+ AssertIsLinkedToRegexp(t, "../../bin/exe2", libgoRE)
+ AssertIsLinkedTo(t, "../../bin/exe2", filepath.Base(dep2))
+ AssertIsLinkedTo(t, "../../bin/exe2", filepath.Base(depBase))
+
+ // And check it runs.
+ run(t, "gccgo-built", "../../bin/exe2")
+}
+
+// Testing rebuilding of shared libraries when they are stale is a bit more
+// complicated that it seems like it should be. First, we make everything "old": but
+// only a few seconds old, or it might be older than gc (or the runtime source) and
+// everything will get rebuilt. Then define a timestamp slightly newer than this
+// time, which is what we set the mtime to of a file to cause it to be seen as new,
+// and finally another slightly even newer one that we can compare files against to
+// see if they have been rebuilt.
+var oldTime = time.Now().Add(-9 * time.Second)
+var nearlyNew = time.Now().Add(-6 * time.Second)
+var stampTime = time.Now().Add(-3 * time.Second)
+
+// resetFileStamps makes "everything" (bin, src, pkg from GOPATH and the
+// test-specific parts of GOROOT) appear old.
+func resetFileStamps() {
+ chtime := func(path string, info os.FileInfo, err error) error {
+ return os.Chtimes(path, oldTime, oldTime)
+ }
+ reset := func(path string) {
+ if err := filepath.Walk(path, chtime); err != nil {
+ log.Panicf("resetFileStamps failed: %v", err)
+ }
+
+ }
+ reset("../../bin")
+ reset("../../pkg")
+ reset("../../src")
+ reset(gorootInstallDir)
+}
+
+// touch changes path and returns a function that changes it back.
+// It also sets the time of the file, so that we can see if it is rewritten.
+func touch(t *testing.T, path string) (cleanup func()) {
+ t.Helper()
+ data, err := os.ReadFile(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ old := make([]byte, len(data))
+ copy(old, data)
+ if bytes.HasPrefix(data, []byte("!<arch>\n")) {
+ // Change last digit of build ID.
+ // (Content ID in the new content-based build IDs.)
+ const marker = `build id "`
+ i := bytes.Index(data, []byte(marker))
+ if i < 0 {
+ t.Fatal("cannot find build id in archive")
+ }
+ j := bytes.IndexByte(data[i+len(marker):], '"')
+ if j < 0 {
+ t.Fatal("cannot find build id in archive")
+ }
+ i += len(marker) + j - 1
+ if data[i] == 'a' {
+ data[i] = 'b'
+ } else {
+ data[i] = 'a'
+ }
+ } else {
+ // assume it's a text file
+ data = append(data, '\n')
+ }
+
+ // If the file is still a symlink from an overlay, delete it so that we will
+ // replace it with a regular file instead of overwriting the symlinked one.
+ fi, err := os.Lstat(path)
+ if err == nil && !fi.Mode().IsRegular() {
+ fi, err = os.Stat(path)
+ if err := os.Remove(path); err != nil {
+ t.Fatal(err)
+ }
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // If we're replacing a symlink to a read-only file, make the new file
+ // user-writable.
+ perm := fi.Mode().Perm() | 0200
+
+ if err := os.WriteFile(path, data, perm); err != nil {
+ t.Fatal(err)
+ }
+ if err := os.Chtimes(path, nearlyNew, nearlyNew); err != nil {
+ t.Fatal(err)
+ }
+ return func() {
+ if err := os.WriteFile(path, old, perm); err != nil {
+ t.Fatal(err)
+ }
+ }
+}
+
+// isNew returns if the path is newer than the time stamp used by touch.
+func isNew(t *testing.T, path string) bool {
+ t.Helper()
+ fi, err := os.Stat(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return fi.ModTime().After(stampTime)
+}
+
+// Fail unless path has been rebuilt (i.e. is newer than the time stamp used by
+// isNew)
+func AssertRebuilt(t *testing.T, msg, path string) {
+ t.Helper()
+ if !isNew(t, path) {
+ t.Errorf("%s was not rebuilt (%s)", msg, path)
+ }
+}
+
+// Fail if path has been rebuilt (i.e. is newer than the time stamp used by isNew)
+func AssertNotRebuilt(t *testing.T, msg, path string) {
+ t.Helper()
+ if isNew(t, path) {
+ t.Errorf("%s was rebuilt (%s)", msg, path)
+ }
+}
+
+func TestRebuilding(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
+ goCmd(t, "install", "-linkshared", "./exe")
+ info := strings.Fields(goCmd(t, "list", "-buildmode=shared", "-linkshared", "-f", "{{.Target}} {{.Shlib}}", "./depBase"))
+ if len(info) != 2 {
+ t.Fatalf("go list failed to report Target and/or Shlib")
+ }
+ target := info[0]
+ shlib := info[1]
+
+ // If the source is newer than both the .a file and the .so, both are rebuilt.
+ t.Run("newsource", func(t *testing.T) {
+ resetFileStamps()
+ cleanup := touch(t, "./depBase/dep.go")
+ defer func() {
+ cleanup()
+ goCmd(t, "install", "-linkshared", "./exe")
+ }()
+ goCmd(t, "install", "-linkshared", "./exe")
+ AssertRebuilt(t, "new source", target)
+ AssertRebuilt(t, "new source", shlib)
+ })
+
+ // If the .a file is newer than the .so, the .so is rebuilt (but not the .a)
+ t.Run("newarchive", func(t *testing.T) {
+ resetFileStamps()
+ AssertNotRebuilt(t, "new .a file before build", target)
+ goCmd(t, "list", "-linkshared", "-f={{.ImportPath}} {{.Stale}} {{.StaleReason}} {{.Target}}", "./depBase")
+ AssertNotRebuilt(t, "new .a file before build", target)
+ cleanup := touch(t, target)
+ defer func() {
+ cleanup()
+ goCmd(t, "install", "-v", "-linkshared", "./exe")
+ }()
+ goCmd(t, "install", "-v", "-linkshared", "./exe")
+ AssertNotRebuilt(t, "new .a file", target)
+ AssertRebuilt(t, "new .a file", shlib)
+ })
+}
+
+func appendFile(t *testing.T, path, content string) {
+ t.Helper()
+ f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0660)
+ if err != nil {
+ t.Fatalf("os.OpenFile failed: %v", err)
+ }
+ defer func() {
+ err := f.Close()
+ if err != nil {
+ t.Fatalf("f.Close failed: %v", err)
+ }
+ }()
+ _, err = f.WriteString(content)
+ if err != nil {
+ t.Fatalf("f.WriteString failed: %v", err)
+ }
+}
+
+func createFile(t *testing.T, path, content string) {
+ t.Helper()
+ f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
+ if err != nil {
+ t.Fatalf("os.OpenFile failed: %v", err)
+ }
+ _, err = f.WriteString(content)
+ if closeErr := f.Close(); err == nil {
+ err = closeErr
+ }
+ if err != nil {
+ t.Fatalf("WriteString failed: %v", err)
+ }
+}
+
+func TestABIChecking(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
+ goCmd(t, "install", "-linkshared", "./exe")
+
+ // If we make an ABI-breaking change to depBase and rebuild libp.so but not exe,
+ // exe will abort with a complaint on startup.
+ // This assumes adding an exported function breaks ABI, which is not true in
+ // some senses but suffices for the narrow definition of ABI compatibility the
+ // toolchain uses today.
+ resetFileStamps()
+
+ createFile(t, "./depBase/break.go", "package depBase\nfunc ABIBreak() {}\n")
+ defer os.Remove("./depBase/break.go")
+
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
+ c := exec.Command("../../bin/exe")
+ output, err := c.CombinedOutput()
+ if err == nil {
+ t.Fatal("executing exe did not fail after ABI break")
+ }
+ scanner := bufio.NewScanner(bytes.NewReader(output))
+ foundMsg := false
+ const wantPrefix = "abi mismatch detected between the executable and lib"
+ for scanner.Scan() {
+ if strings.HasPrefix(scanner.Text(), wantPrefix) {
+ foundMsg = true
+ break
+ }
+ }
+ if err = scanner.Err(); err != nil {
+ t.Errorf("scanner encountered error: %v", err)
+ }
+ if !foundMsg {
+ t.Fatalf("exe failed, but without line %q; got output:\n%s", wantPrefix, output)
+ }
+
+ // Rebuilding exe makes it work again.
+ goCmd(t, "install", "-linkshared", "./exe")
+ run(t, "rebuilt exe", "../../bin/exe")
+
+ // If we make a change which does not break ABI (such as adding an unexported
+ // function) and rebuild libdepBase.so, exe still works, even if new function
+ // is in a file by itself.
+ resetFileStamps()
+ createFile(t, "./depBase/dep2.go", "package depBase\nfunc noABIBreak() {}\n")
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./depBase")
+ run(t, "after non-ABI breaking change", "../../bin/exe")
+}
+
+// If a package 'explicit' imports a package 'implicit', building
+// 'explicit' into a shared library implicitly includes implicit in
+// the shared library. Building an executable that imports both
+// explicit and implicit builds the code from implicit into the
+// executable rather than fetching it from the shared library. The
+// link still succeeds and the executable still runs though.
+func TestImplicitInclusion(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./explicit")
+ goCmd(t, "install", "-linkshared", "./implicitcmd")
+ run(t, "running executable linked against library that contains same package as it", "../../bin/implicitcmd")
+}
+
+// Tests to make sure that the type fields of empty interfaces and itab
+// fields of nonempty interfaces are unique even across modules,
+// so that interface equality works correctly.
+func TestInterface(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./iface_a")
+ // Note: iface_i gets installed implicitly as a dependency of iface_a.
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./iface_b")
+ goCmd(t, "install", "-linkshared", "./iface")
+ run(t, "running type/itab uniqueness tester", "../../bin/iface")
+}
+
+// Access a global variable from a library.
+func TestGlobal(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./globallib")
+ goCmd(t, "install", "-linkshared", "./global")
+ run(t, "global executable", "../../bin/global")
+ AssertIsLinkedTo(t, "../../bin/global", soname)
+ AssertHasRPath(t, "../../bin/global", gorootInstallDir)
+}
+
+// Run a test using -linkshared of an installed shared package.
+// Issue 26400.
+func TestTestInstalledShared(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "test", "-linkshared", "-test.short", "sync/atomic")
+}
+
+// Test generated pointer method with -linkshared.
+// Issue 25065.
+func TestGeneratedMethod(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue25065")
+}
+
+// Test use of shared library struct with generated hash function.
+// Issue 30768.
+func TestGeneratedHash(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib")
+ goCmd(t, "test", "-linkshared", "./issue30768")
+}
+
+// Test that packages can be added not in dependency order (here a depends on b, and a adds
+// before b). This could happen with e.g. go build -buildmode=shared std. See issue 39777.
+func TestPackageOrder(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue39777/a", "./issue39777/b")
+}
+
+// Test that GC data are generated correctly by the linker when it needs a type defined in
+// a shared library. See issue 39927.
+func TestGCData(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./gcdata/p")
+ goCmd(t, "build", "-linkshared", "./gcdata/main")
+ runWithEnv(t, "running gcdata/main", []string{"GODEBUG=clobberfree=1"}, "./main")
+}
+
+// Test that we don't decode type symbols from shared libraries (which has no data,
+// causing panic). See issue 44031.
+func TestIssue44031(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/a")
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue44031/b")
+ goCmd(t, "run", "-linkshared", "./issue44031/main")
+}
+
+// Test that we use a variable from shared libraries (which implement an
+// interface in shared libraries.). A weak reference is used in the itab
+// in main process. It can cause unreachable panic. See issue 47873.
+func TestIssue47873(t *testing.T) {
+ globalSkip(t)
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./issue47837/a")
+ goCmd(t, "run", "-linkshared", "./issue47837/main")
+}
+
+// Test that we can build std in shared mode.
+func TestStd(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skip in short mode")
+ }
+ globalSkip(t)
+ t.Parallel()
+ tmpDir := t.TempDir()
+ // Use a temporary pkgdir to not interfere with other tests, and not write to GOROOT.
+ // Cannot use goCmd as it runs with cloned GOROOT which is incomplete.
+ runWithEnv(t, "building std", []string{"GOROOT=" + oldGOROOT},
+ filepath.Join(oldGOROOT, "bin", "go"), "install", "-buildmode=shared", "-pkgdir="+tmpDir, "std")
+
+ // Issue #58966.
+ runWithEnv(t, "testing issue #58966", []string{"GOROOT=" + oldGOROOT},
+ filepath.Join(oldGOROOT, "bin", "go"), "run", "-linkshared", "-pkgdir="+tmpDir, "./issue58966/main.go")
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/dep2/dep2.go b/src/cmd/cgo/internal/testshared/testdata/dep2/dep2.go
new file mode 100644
index 0000000..94f38cf
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/dep2/dep2.go
@@ -0,0 +1,15 @@
+package dep2
+
+import "testshared/depBase"
+
+var W int = 1
+
+var hasProg depBase.HasProg
+
+type Dep2 struct {
+ depBase.Dep
+}
+
+func G() int {
+ return depBase.F() + 1
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/dep3/dep3.go b/src/cmd/cgo/internal/testshared/testdata/dep3/dep3.go
new file mode 100644
index 0000000..6b02ad2
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/dep3/dep3.go
@@ -0,0 +1,22 @@
+package dep3
+
+// The point of this test file is that it references a type from
+// depBase that is also referenced in dep2, but dep2 is loaded by the
+// linker before depBase (because it is earlier in the import list).
+// There was a bug in the linker where it would not correctly read out
+// the type data in this case and later crash.
+
+import (
+ "testshared/dep2"
+ "testshared/depBase"
+)
+
+type Dep3 struct {
+ dep depBase.Dep
+ dep2 dep2.Dep2
+}
+
+func D3() int {
+ var x Dep3
+ return x.dep.X + x.dep2.X
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/depBase/asm.s b/src/cmd/cgo/internal/testshared/testdata/depBase/asm.s
new file mode 100644
index 0000000..51adca3
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/depBase/asm.s
@@ -0,0 +1,10 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gc
+
+#include "textflag.h"
+
+TEXT ·ImplementedInAsm(SB),NOSPLIT,$0-0
+ RET
diff --git a/src/cmd/cgo/internal/testshared/testdata/depBase/dep.go b/src/cmd/cgo/internal/testshared/testdata/depBase/dep.go
new file mode 100644
index 0000000..e7cc7c8
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/depBase/dep.go
@@ -0,0 +1,37 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package depBase
+
+import (
+ "os"
+ "reflect"
+)
+
+var SlicePtr interface{} = &[]int{}
+
+var V int = 1
+
+var HasMask []string = []string{"hi"}
+
+type HasProg struct {
+ array [1024]*byte
+}
+
+type Dep struct {
+ X int
+}
+
+func (d *Dep) Method() int {
+ // This code below causes various go.itab.* symbols to be generated in
+ // the shared library. Similar code in ../exe/exe.go results in
+ // exercising https://golang.org/issues/17594
+ reflect.TypeOf(os.Stdout).Elem()
+ return 10
+}
+
+func F() int {
+ defer func() {}()
+ return V
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/depBase/gccgo.go b/src/cmd/cgo/internal/testshared/testdata/depBase/gccgo.go
new file mode 100644
index 0000000..a59d0b8
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/depBase/gccgo.go
@@ -0,0 +1,9 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gccgo
+
+package depBase
+
+func ImplementedInAsm() {}
diff --git a/src/cmd/cgo/internal/testshared/testdata/depBase/stubs.go b/src/cmd/cgo/internal/testshared/testdata/depBase/stubs.go
new file mode 100644
index 0000000..c15e4e9
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/depBase/stubs.go
@@ -0,0 +1,9 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build gc
+
+package depBase
+
+func ImplementedInAsm()
diff --git a/src/cmd/cgo/internal/testshared/testdata/division/division.go b/src/cmd/cgo/internal/testshared/testdata/division/division.go
new file mode 100644
index 0000000..bb5fc98
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/division/division.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+//go:noinline
+func div(x, y uint32) uint32 {
+ return x / y
+}
+
+func main() {
+ a := div(97, 11)
+ if a != 8 {
+ panic("FAIL")
+ }
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/exe/exe.go b/src/cmd/cgo/internal/testshared/testdata/exe/exe.go
new file mode 100644
index 0000000..ee95f97
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/exe/exe.go
@@ -0,0 +1,45 @@
+package main
+
+import (
+ "os"
+ "reflect"
+ "runtime"
+
+ "testshared/depBase"
+)
+
+// Having a function declared in the main package triggered
+// golang.org/issue/18250
+func DeclaredInMain() {
+}
+
+type C struct {
+}
+
+func F() *C {
+ return nil
+}
+
+var slicePtr interface{} = &[]int{}
+
+func main() {
+ defer depBase.ImplementedInAsm()
+ // This code below causes various go.itab.* symbols to be generated in
+ // the executable. Similar code in ../depBase/dep.go results in
+ // exercising https://golang.org/issues/17594
+ reflect.TypeOf(os.Stdout).Elem()
+ runtime.GC()
+ depBase.V = depBase.F() + 1
+
+ var c *C
+ if reflect.TypeOf(F).Out(0) != reflect.TypeOf(c) {
+ panic("bad reflection results, see golang.org/issue/18252")
+ }
+
+ sp := reflect.New(reflect.TypeOf(slicePtr).Elem())
+ s := sp.Interface()
+
+ if reflect.TypeOf(s) != reflect.TypeOf(slicePtr) {
+ panic("bad reflection results, see golang.org/issue/18729")
+ }
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/exe2/exe2.go b/src/cmd/cgo/internal/testshared/testdata/exe2/exe2.go
new file mode 100644
index 0000000..433f331
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/exe2/exe2.go
@@ -0,0 +1,8 @@
+package main
+
+import "testshared/dep2"
+
+func main() {
+ d := &dep2.Dep2{}
+ dep2.W = dep2.G() + 1 + d.Method()
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/exe3/exe3.go b/src/cmd/cgo/internal/testshared/testdata/exe3/exe3.go
new file mode 100644
index 0000000..533e3a9
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/exe3/exe3.go
@@ -0,0 +1,7 @@
+package main
+
+import "testshared/dep3"
+
+func main() {
+ dep3.D3()
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/execgo/exe.go b/src/cmd/cgo/internal/testshared/testdata/execgo/exe.go
new file mode 100644
index 0000000..0427be8
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/execgo/exe.go
@@ -0,0 +1,8 @@
+package main
+
+/*
+ */
+import "C"
+
+func main() {
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/explicit/explicit.go b/src/cmd/cgo/internal/testshared/testdata/explicit/explicit.go
new file mode 100644
index 0000000..af969fc
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/explicit/explicit.go
@@ -0,0 +1,9 @@
+package explicit
+
+import (
+ "testshared/implicit"
+)
+
+func E() int {
+ return implicit.I()
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/gcdata/main/main.go b/src/cmd/cgo/internal/testshared/testdata/gcdata/main/main.go
new file mode 100644
index 0000000..394862f
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/gcdata/main/main.go
@@ -0,0 +1,37 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that GC data is generated correctly for global
+// variables with types defined in a shared library.
+// See issue 39927.
+
+// This test run under GODEBUG=clobberfree=1. The check
+// *x[i] == 12345 depends on this debug mode to clobber
+// the value if the object is freed prematurely.
+
+package main
+
+import (
+ "fmt"
+ "runtime"
+ "testshared/gcdata/p"
+)
+
+var x p.T
+
+func main() {
+ for i := range x {
+ x[i] = new(int)
+ *x[i] = 12345
+ }
+ runtime.GC()
+ runtime.GC()
+ runtime.GC()
+ for i := range x {
+ if *x[i] != 12345 {
+ fmt.Printf("x[%d] == %d, want 12345\n", i, *x[i])
+ panic("FAIL")
+ }
+ }
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/gcdata/p/p.go b/src/cmd/cgo/internal/testshared/testdata/gcdata/p/p.go
new file mode 100644
index 0000000..1fee754
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/gcdata/p/p.go
@@ -0,0 +1,7 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type T [10]*int
diff --git a/src/cmd/cgo/internal/testshared/testdata/global/main.go b/src/cmd/cgo/internal/testshared/testdata/global/main.go
new file mode 100644
index 0000000..f43e7c3
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/global/main.go
@@ -0,0 +1,71 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "testshared/globallib"
+)
+
+//go:noinline
+func testLoop() {
+ for i, s := range globallib.Data {
+ if s != int64(i) {
+ panic("testLoop: mismatch")
+ }
+ }
+}
+
+//go:noinline
+func ptrData() *[1<<20 + 10]int64 {
+ return &globallib.Data
+}
+
+//go:noinline
+func testMediumOffset() {
+ for i, s := range globallib.Data[1<<16-2:] {
+ if s != int64(i)+1<<16-2 {
+ panic("testMediumOffset: index mismatch")
+ }
+ }
+
+ x := globallib.Data[1<<16-1]
+ if x != 1<<16-1 {
+ panic("testMediumOffset: direct mismatch")
+ }
+
+ y := &globallib.Data[1<<16-3]
+ if y != &ptrData()[1<<16-3] {
+ panic("testMediumOffset: address mismatch")
+ }
+}
+
+//go:noinline
+func testLargeOffset() {
+ for i, s := range globallib.Data[1<<20:] {
+ if s != int64(i)+1<<20 {
+ panic("testLargeOffset: index mismatch")
+ }
+ }
+
+ x := globallib.Data[1<<20+1]
+ if x != 1<<20+1 {
+ panic("testLargeOffset: direct mismatch")
+ }
+
+ y := &globallib.Data[1<<20+2]
+ if y != &ptrData()[1<<20+2] {
+ panic("testLargeOffset: address mismatch")
+ }
+}
+
+func main() {
+ testLoop()
+
+ // SSA rules commonly merge offsets into addresses. These
+ // tests access global data in different ways to try
+ // and exercise different SSA rules.
+ testMediumOffset()
+ testLargeOffset()
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/globallib/global.go b/src/cmd/cgo/internal/testshared/testdata/globallib/global.go
new file mode 100644
index 0000000..b4372a2
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/globallib/global.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package globallib
+
+// Data is large enough to that offsets into it do not fit into
+// 16-bit or 20-bit immediates. Ideally we'd also try and overrun
+// 32-bit immediates, but that requires the test machine to have
+// too much memory.
+var Data [1<<20 + 10]int64
+
+func init() {
+ for i := range Data {
+ Data[i] = int64(i)
+ }
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/iface/main.go b/src/cmd/cgo/internal/testshared/testdata/iface/main.go
new file mode 100644
index 0000000..d26ebbc
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/iface/main.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "testshared/iface_a"
+import "testshared/iface_b"
+
+func main() {
+ if iface_a.F() != iface_b.F() {
+ panic("empty interfaces not equal")
+ }
+ if iface_a.G() != iface_b.G() {
+ panic("non-empty interfaces not equal")
+ }
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/iface_a/a.go b/src/cmd/cgo/internal/testshared/testdata/iface_a/a.go
new file mode 100644
index 0000000..e2cef1e
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/iface_a/a.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package iface_a
+
+import "testshared/iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/iface_b/b.go b/src/cmd/cgo/internal/testshared/testdata/iface_b/b.go
new file mode 100644
index 0000000..dd3e027
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/iface_b/b.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package iface_b
+
+import "testshared/iface_i"
+
+//go:noinline
+func F() interface{} {
+ return (*iface_i.T)(nil)
+}
+
+//go:noinline
+func G() iface_i.I {
+ return (*iface_i.T)(nil)
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/iface_i/i.go b/src/cmd/cgo/internal/testshared/testdata/iface_i/i.go
new file mode 100644
index 0000000..31c8038
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/iface_i/i.go
@@ -0,0 +1,17 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package iface_i
+
+type I interface {
+ M()
+}
+
+type T struct {
+}
+
+func (t *T) M() {
+}
+
+// *T implements I
diff --git a/src/cmd/cgo/internal/testshared/testdata/implicit/implicit.go b/src/cmd/cgo/internal/testshared/testdata/implicit/implicit.go
new file mode 100644
index 0000000..5360188
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/implicit/implicit.go
@@ -0,0 +1,5 @@
+package implicit
+
+func I() int {
+ return 42
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/implicitcmd/implicitcmd.go b/src/cmd/cgo/internal/testshared/testdata/implicitcmd/implicitcmd.go
new file mode 100644
index 0000000..4d42967
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/implicitcmd/implicitcmd.go
@@ -0,0 +1,10 @@
+package main
+
+import (
+ "testshared/explicit"
+ "testshared/implicit"
+)
+
+func main() {
+ println(implicit.I() + explicit.E())
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue25065/a.go b/src/cmd/cgo/internal/testshared/testdata/issue25065/a.go
new file mode 100644
index 0000000..646de4e
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue25065/a.go
@@ -0,0 +1,21 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package issue25065 has a type with a method that is
+// 1. referenced in a method expression
+// 2. not called
+// 3. not converted to an interface
+// 4. is a value method but the reference is to the pointer method
+//
+// These cases avoid the call to makefuncsym from typecheckfunc, but we
+// still need to call makefuncsym somehow or the symbol will not be defined.
+package issue25065
+
+type T int
+
+func (t T) M() {}
+
+func F() func(*T) {
+ return (*T).M
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue30768/issue30768lib/lib.go b/src/cmd/cgo/internal/testshared/testdata/issue30768/issue30768lib/lib.go
new file mode 100644
index 0000000..9e45ebe
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue30768/issue30768lib/lib.go
@@ -0,0 +1,11 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue30768lib
+
+// S is a struct that requires a generated hash function.
+type S struct {
+ A string
+ B int
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue30768/x_test.go b/src/cmd/cgo/internal/testshared/testdata/issue30768/x_test.go
new file mode 100644
index 0000000..1bbd139
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue30768/x_test.go
@@ -0,0 +1,22 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package issue30768_test
+
+import (
+ "testing"
+
+ "testshared/issue30768/issue30768lib"
+)
+
+type s struct {
+ s issue30768lib.S
+}
+
+func Test30768(t *testing.T) {
+ // Calling t.Log will convert S to an empty interface,
+ // which will force a reference to the generated hash function,
+ // defined in the shared library.
+ t.Log(s{})
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue39777/a/a.go b/src/cmd/cgo/internal/testshared/testdata/issue39777/a/a.go
new file mode 100644
index 0000000..c7bf835
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue39777/a/a.go
@@ -0,0 +1,9 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+import "testshared/issue39777/b"
+
+func F() { b.F() }
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue39777/b/b.go b/src/cmd/cgo/internal/testshared/testdata/issue39777/b/b.go
new file mode 100644
index 0000000..4e68196
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue39777/b/b.go
@@ -0,0 +1,7 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package b
+
+func F() {}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue44031/a/a.go b/src/cmd/cgo/internal/testshared/testdata/issue44031/a/a.go
new file mode 100644
index 0000000..48827e6
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue44031/a/a.go
@@ -0,0 +1,9 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+type ATypeWithALoooooongName interface { // a long name, so the type descriptor symbol name is mangled
+ M()
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue44031/b/b.go b/src/cmd/cgo/internal/testshared/testdata/issue44031/b/b.go
new file mode 100644
index 0000000..ad3ebec
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue44031/b/b.go
@@ -0,0 +1,17 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package b
+
+import "testshared/issue44031/a"
+
+type T int
+
+func (T) M() {}
+
+var i = a.ATypeWithALoooooongName(T(0))
+
+func F() {
+ i.M()
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue44031/main/main.go b/src/cmd/cgo/internal/testshared/testdata/issue44031/main/main.go
new file mode 100644
index 0000000..47f2e3a
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue44031/main/main.go
@@ -0,0 +1,20 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "testshared/issue44031/b"
+
+type t int
+
+func (t) m() {}
+
+type i interface{ m() } // test that unexported method is correctly marked
+
+var v interface{} = t(0)
+
+func main() {
+ b.F()
+ v.(i).m()
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue47837/a/a.go b/src/cmd/cgo/internal/testshared/testdata/issue47837/a/a.go
new file mode 100644
index 0000000..68588ed
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue47837/a/a.go
@@ -0,0 +1,19 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package a
+
+type A interface {
+ M()
+}
+
+//go:noinline
+func TheFuncWithArgA(a A) {
+ a.M()
+}
+
+type ImplA struct{}
+
+//go:noinline
+func (A *ImplA) M() {}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue47837/main/main.go b/src/cmd/cgo/internal/testshared/testdata/issue47837/main/main.go
new file mode 100644
index 0000000..77c6f34
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue47837/main/main.go
@@ -0,0 +1,14 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "testshared/issue47837/a"
+)
+
+func main() {
+ var vara a.ImplA
+ a.TheFuncWithArgA(&vara)
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/issue58966/main.go b/src/cmd/cgo/internal/testshared/testdata/issue58966/main.go
new file mode 100644
index 0000000..2d923c3
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/issue58966/main.go
@@ -0,0 +1,15 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "crypto/elliptic"
+
+var curve elliptic.Curve
+
+func main() {
+ switch curve {
+ case elliptic.P224():
+ }
+}
diff --git a/src/cmd/cgo/internal/testshared/testdata/trivial/trivial.go b/src/cmd/cgo/internal/testshared/testdata/trivial/trivial.go
new file mode 100644
index 0000000..6ade47c
--- /dev/null
+++ b/src/cmd/cgo/internal/testshared/testdata/trivial/trivial.go
@@ -0,0 +1,9 @@
+package main
+
+func main() {
+ // This is enough to make sure that the executable references
+ // a type descriptor, which was the cause of
+ // https://golang.org/issue/25970.
+ c := make(chan int)
+ _ = c
+}
diff --git a/src/cmd/cgo/internal/testso/so_test.go b/src/cmd/cgo/internal/testso/so_test.go
new file mode 100644
index 0000000..e011167
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/so_test.go
@@ -0,0 +1,137 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package so_test
+
+import (
+ "cmd/cgo/internal/cgotest"
+ "internal/testenv"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+)
+
+func TestSO(t *testing.T) {
+ testSO(t, "so")
+}
+
+func TestSOVar(t *testing.T) {
+ testSO(t, "sovar")
+}
+
+func testSO(t *testing.T, dir string) {
+ if runtime.GOOS == "ios" {
+ t.Skip("iOS disallows dynamic loading of user libraries")
+ }
+ testenv.MustHaveGoBuild(t)
+ testenv.MustHaveExec(t)
+ testenv.MustHaveCGO(t)
+
+ GOPATH, err := os.MkdirTemp("", "cgosotest")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer os.RemoveAll(GOPATH)
+
+ modRoot := filepath.Join(GOPATH, "src", "cgosotest")
+ if err := cgotest.OverlayDir(modRoot, filepath.Join("testdata", dir)); err != nil {
+ log.Panic(err)
+ }
+ if err := os.WriteFile(filepath.Join(modRoot, "go.mod"), []byte("module cgosotest\n"), 0666); err != nil {
+ log.Panic(err)
+ }
+
+ cmd := exec.Command("go", "env", "CC", "GOGCCFLAGS")
+ cmd.Dir = modRoot
+ cmd.Stderr = new(strings.Builder)
+ cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
+ out, err := cmd.Output()
+ if err != nil {
+ t.Fatalf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, cmd.Stderr)
+ }
+ lines := strings.Split(string(out), "\n")
+ if len(lines) != 3 || lines[2] != "" {
+ t.Fatalf("Unexpected output from %s:\n%s", strings.Join(cmd.Args, " "), lines)
+ }
+
+ cc := lines[0]
+ if cc == "" {
+ t.Fatal("CC environment variable (go env CC) cannot be empty")
+ }
+ gogccflags := strings.Split(lines[1], " ")
+
+ // build shared object
+ ext := "so"
+ args := append(gogccflags, "-shared")
+ switch runtime.GOOS {
+ case "darwin", "ios":
+ ext = "dylib"
+ args = append(args, "-undefined", "suppress", "-flat_namespace")
+ case "windows":
+ ext = "dll"
+ args = append(args, "-DEXPORT_DLL")
+ // At least in mingw-clang it is not permitted to just name a .dll
+ // on the command line. You must name the corresponding import
+ // library instead, even though the dll is used when the executable is run.
+ args = append(args, "-Wl,-out-implib,libcgosotest.a")
+ case "aix":
+ ext = "so.1"
+ }
+ sofname := "libcgosotest." + ext
+ args = append(args, "-o", sofname, "cgoso_c.c")
+
+ cmd = exec.Command(cc, args...)
+ cmd.Dir = modRoot
+ cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+ t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
+
+ if runtime.GOOS == "aix" {
+ // Shared object must be wrapped by an archive
+ cmd = exec.Command("ar", "-X64", "-q", "libcgosotest.a", "libcgosotest.so.1")
+ cmd.Dir = modRoot
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+ }
+
+ cmd = exec.Command("go", "build", "-o", "main.exe", "main.go")
+ cmd.Dir = modRoot
+ cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+ t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
+
+ cmd = exec.Command("./main.exe")
+ cmd.Dir = modRoot
+ cmd.Env = append(os.Environ(), "GOPATH="+GOPATH)
+ if runtime.GOOS != "windows" {
+ s := "LD_LIBRARY_PATH"
+ if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
+ s = "DYLD_LIBRARY_PATH"
+ }
+ cmd.Env = append(os.Environ(), s+"=.")
+
+ // On FreeBSD 64-bit architectures, the 32-bit linker looks for
+ // different environment variables.
+ if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" {
+ cmd.Env = append(cmd.Env, "LD_32_LIBRARY_PATH=.")
+ }
+ }
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%s: %s\n%s", strings.Join(cmd.Args, " "), err, out)
+ }
+ t.Logf("%s:\n%s", strings.Join(cmd.Args, " "), out)
+}
diff --git a/src/cmd/cgo/internal/testso/testdata/so/cgoso.c b/src/cmd/cgo/internal/testso/testdata/so/cgoso.c
new file mode 100644
index 0000000..612e5d3
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/testdata/so/cgoso.c
@@ -0,0 +1,14 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "_cgo_export.h"
+
+#if defined(WIN32) || defined(_AIX)
+extern void setCallback(void *);
+void init() {
+ setCallback(goCallback);
+}
+#else
+void init() {}
+#endif
diff --git a/src/cmd/cgo/internal/testso/testdata/so/cgoso.go b/src/cmd/cgo/internal/testso/testdata/so/cgoso.go
new file mode 100644
index 0000000..b59b2a8
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/testdata/so/cgoso.go
@@ -0,0 +1,32 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgosotest
+
+/*
+// intentionally write the same LDFLAGS differently
+// to test correct handling of LDFLAGS.
+#cgo linux LDFLAGS: -L. -lcgosotest
+#cgo dragonfly LDFLAGS: -L. -l cgosotest
+#cgo freebsd LDFLAGS: -L. -l cgosotest
+#cgo openbsd LDFLAGS: -L. -l cgosotest
+#cgo solaris LDFLAGS: -L. -lcgosotest
+#cgo netbsd LDFLAGS: -L. libcgosotest.so
+#cgo darwin LDFLAGS: -L. libcgosotest.dylib
+#cgo windows LDFLAGS: -L. libcgosotest.a
+#cgo aix LDFLAGS: -L. -l cgosotest
+
+void init(void);
+void sofunc(void);
+*/
+import "C"
+
+func Test() {
+ C.init()
+ C.sofunc()
+}
+
+//export goCallback
+func goCallback() {
+}
diff --git a/src/cmd/cgo/internal/testso/testdata/so/cgoso_c.c b/src/cmd/cgo/internal/testso/testdata/so/cgoso_c.c
new file mode 100644
index 0000000..d5fb559
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/testdata/so/cgoso_c.c
@@ -0,0 +1,39 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+#ifdef WIN32
+// A Windows DLL is unable to call an arbitrary function in
+// the main executable. Work around that by making the main
+// executable pass the callback function pointer to us.
+void (*goCallback)(void);
+__declspec(dllexport) void setCallback(void *f)
+{
+ goCallback = (void (*)())f;
+}
+__declspec(dllexport) void sofunc(void);
+#elif defined(_AIX)
+// AIX doesn't allow the creation of a shared object with an
+// undefined symbol. It's possible to bypass this problem by
+// using -Wl,-G and -Wl,-brtl option which allows run-time linking.
+// However, that's not how most of AIX shared object works.
+// Therefore, it's better to consider goCallback as a pointer and
+// to set up during an init function.
+void (*goCallback)(void);
+void setCallback(void *f) { goCallback = f; }
+#else
+extern void goCallback(void);
+void setCallback(void *f) { (void)f; }
+#endif
+
+// OpenBSD and older Darwin lack TLS support
+#if !defined(__OpenBSD__) && !defined(__APPLE__)
+__thread int tlsvar = 12345;
+#endif
+
+void sofunc(void)
+{
+ goCallback();
+}
diff --git a/src/cmd/cgo/internal/testso/testdata/so/cgoso_unix.go b/src/cmd/cgo/internal/testso/testdata/so/cgoso_unix.go
new file mode 100644
index 0000000..ea9cb0a
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/testdata/so/cgoso_unix.go
@@ -0,0 +1,20 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build aix || dragonfly || freebsd || linux || netbsd || solaris
+
+package cgosotest
+
+/*
+extern int __thread tlsvar;
+int *getTLS() { return &tlsvar; }
+*/
+import "C"
+
+func init() {
+ if v := *C.getTLS(); v != 12345 {
+ println("got", v)
+ panic("BAD TLS value")
+ }
+}
diff --git a/src/cmd/cgo/internal/testso/testdata/so/main.go b/src/cmd/cgo/internal/testso/testdata/so/main.go
new file mode 100644
index 0000000..84382f7
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/testdata/so/main.go
@@ -0,0 +1,13 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+import "cgosotest"
+
+func main() {
+ cgosotest.Test()
+}
diff --git a/src/cmd/cgo/internal/testso/testdata/sovar/cgoso.go b/src/cmd/cgo/internal/testso/testdata/sovar/cgoso.go
new file mode 100644
index 0000000..d9deb55
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/testdata/sovar/cgoso.go
@@ -0,0 +1,44 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgosotest
+
+// This test verifies that Go can access C variables
+// in shared object file via cgo.
+
+/*
+// intentionally write the same LDFLAGS differently
+// to test correct handling of LDFLAGS.
+#cgo windows CFLAGS: -DIMPORT_DLL
+#cgo linux LDFLAGS: -L. -lcgosotest
+#cgo dragonfly LDFLAGS: -L. -l cgosotest
+#cgo freebsd LDFLAGS: -L. -l cgosotest
+#cgo openbsd LDFLAGS: -L. -l cgosotest
+#cgo solaris LDFLAGS: -L. -lcgosotest
+#cgo netbsd LDFLAGS: -L. libcgosotest.so
+#cgo darwin LDFLAGS: -L. libcgosotest.dylib
+#cgo windows LDFLAGS: -L. libcgosotest.a
+#cgo aix LDFLAGS: -L. -l cgosotest
+
+#include "cgoso_c.h"
+
+const char* getVar() {
+ return exported_var;
+}
+*/
+import "C"
+
+import "fmt"
+
+func Test() {
+ const want = "Hello world"
+ got := C.GoString(C.getVar())
+ if got != want {
+ panic(fmt.Sprintf("testExportedVar: got %q, but want %q", got, want))
+ }
+ got = C.GoString(C.exported_var)
+ if got != want {
+ panic(fmt.Sprintf("testExportedVar: got %q, but want %q", got, want))
+ }
+}
diff --git a/src/cmd/cgo/internal/testso/testdata/sovar/cgoso_c.c b/src/cmd/cgo/internal/testso/testdata/sovar/cgoso_c.c
new file mode 100644
index 0000000..36f4d57
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/testdata/sovar/cgoso_c.c
@@ -0,0 +1,7 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+const char *exported_var = "Hello world";
diff --git a/src/cmd/cgo/internal/testso/testdata/sovar/cgoso_c.h b/src/cmd/cgo/internal/testso/testdata/sovar/cgoso_c.h
new file mode 100644
index 0000000..eccd8c0
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/testdata/sovar/cgoso_c.h
@@ -0,0 +1,17 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+#ifdef WIN32
+#if defined(EXPORT_DLL)
+# define VAR __declspec(dllexport)
+#elif defined(IMPORT_DLL)
+# define VAR __declspec(dllimport)
+#endif
+#else
+# define VAR extern
+#endif
+
+VAR const char *exported_var;
diff --git a/src/cmd/cgo/internal/testso/testdata/sovar/main.go b/src/cmd/cgo/internal/testso/testdata/sovar/main.go
new file mode 100644
index 0000000..018b835
--- /dev/null
+++ b/src/cmd/cgo/internal/testso/testdata/sovar/main.go
@@ -0,0 +1,13 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build ignore
+
+package main
+
+import "cgosotest"
+
+func main() {
+ cgosotest.Test()
+}
diff --git a/src/cmd/cgo/internal/teststdio/stdio_test.go b/src/cmd/cgo/internal/teststdio/stdio_test.go
new file mode 100644
index 0000000..3883422
--- /dev/null
+++ b/src/cmd/cgo/internal/teststdio/stdio_test.go
@@ -0,0 +1,77 @@
+// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package stdio_test
+
+import (
+ "bytes"
+ "cmd/cgo/internal/cgotest"
+ "internal/testenv"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestMain(m *testing.M) {
+ log.SetFlags(log.Lshortfile)
+ os.Exit(testMain(m))
+}
+
+func testMain(m *testing.M) int {
+ GOPATH, err := os.MkdirTemp("", "cgostdio")
+ if err != nil {
+ log.Panic(err)
+ }
+ defer os.RemoveAll(GOPATH)
+ os.Setenv("GOPATH", GOPATH)
+
+ // Copy testdata into GOPATH/src/cgostdio, along with a go.mod file
+ // declaring the same path.
+ modRoot := filepath.Join(GOPATH, "src", "cgostdio")
+ if err := cgotest.OverlayDir(modRoot, "testdata"); err != nil {
+ log.Panic(err)
+ }
+ if err := os.Chdir(modRoot); err != nil {
+ log.Panic(err)
+ }
+ os.Setenv("PWD", modRoot)
+ if err := os.WriteFile("go.mod", []byte("module cgostdio\n"), 0666); err != nil {
+ log.Panic(err)
+ }
+
+ return m.Run()
+}
+
+// TestTestRun runs a cgo test that doesn't depend on non-standard libraries.
+func TestTestRun(t *testing.T) {
+ testenv.MustHaveGoRun(t)
+ testenv.MustHaveCGO(t)
+
+ for _, file := range [...]string{
+ "chain.go",
+ "fib.go",
+ "hello.go",
+ } {
+ file := file
+ wantFile := strings.Replace(file, ".go", ".out", 1)
+ t.Run(file, func(t *testing.T) {
+ cmd := exec.Command("go", "run", file)
+ got, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("%v: %s\n%s", cmd, err, got)
+ }
+ got = bytes.ReplaceAll(got, []byte("\r\n"), []byte("\n"))
+ want, err := os.ReadFile(wantFile)
+ if err != nil {
+ t.Fatal("reading golden output:", err)
+ }
+ if !bytes.Equal(got, want) {
+ t.Errorf("'%v' output does not match expected in %s. Instead saw:\n%s", cmd, wantFile, got)
+ }
+ })
+ }
+}
diff --git a/src/cmd/cgo/internal/teststdio/testdata/chain.go b/src/cmd/cgo/internal/teststdio/testdata/chain.go
new file mode 100644
index 0000000..c7163f5
--- /dev/null
+++ b/src/cmd/cgo/internal/teststdio/testdata/chain.go
@@ -0,0 +1,46 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build test_run
+
+// Pass numbers along a chain of threads.
+
+package main
+
+import (
+ "runtime"
+ "strconv"
+
+ "cgostdio/stdio"
+)
+
+const N = 10
+const R = 5
+
+func link(left chan<- int, right <-chan int) {
+ // Keep the links in dedicated operating system
+ // threads, so that this program tests coordination
+ // between pthreads and not just goroutines.
+ runtime.LockOSThread()
+ for {
+ v := <-right
+ stdio.Stdout.WriteString(strconv.Itoa(v) + "\n")
+ left <- 1 + v
+ }
+}
+
+func main() {
+ leftmost := make(chan int)
+ var left chan int
+ right := leftmost
+ for i := 0; i < N; i++ {
+ left, right = right, make(chan int)
+ go link(left, right)
+ }
+ for i := 0; i < R; i++ {
+ right <- 0
+ x := <-leftmost
+ stdio.Stdout.WriteString(strconv.Itoa(x) + "\n")
+ }
+}
diff --git a/src/cmd/cgo/internal/teststdio/testdata/chain.out b/src/cmd/cgo/internal/teststdio/testdata/chain.out
new file mode 100644
index 0000000..963cf9b
--- /dev/null
+++ b/src/cmd/cgo/internal/teststdio/testdata/chain.out
@@ -0,0 +1,55 @@
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
diff --git a/src/cmd/cgo/internal/teststdio/testdata/fib.go b/src/cmd/cgo/internal/teststdio/testdata/fib.go
new file mode 100644
index 0000000..9617368
--- /dev/null
+++ b/src/cmd/cgo/internal/teststdio/testdata/fib.go
@@ -0,0 +1,50 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build test_run
+
+// Compute Fibonacci numbers with two goroutines
+// that pass integers back and forth. No actual
+// concurrency, just threads and synchronization
+// and foreign code on multiple pthreads.
+
+package main
+
+import (
+ "runtime"
+ "strconv"
+
+ "cgostdio/stdio"
+)
+
+func fibber(c, out chan int64, i int64) {
+ // Keep the fibbers in dedicated operating system
+ // threads, so that this program tests coordination
+ // between pthreads and not just goroutines.
+ runtime.LockOSThread()
+
+ if i == 0 {
+ c <- i
+ }
+ for {
+ j := <-c
+ stdio.Stdout.WriteString(strconv.FormatInt(j, 10) + "\n")
+ out <- j
+ <-out
+ i += j
+ c <- i
+ }
+}
+
+func main() {
+ c := make(chan int64)
+ out := make(chan int64)
+ go fibber(c, out, 0)
+ go fibber(c, out, 1)
+ <-out
+ for i := 0; i < 90; i++ {
+ out <- 1
+ <-out
+ }
+}
diff --git a/src/cmd/cgo/internal/teststdio/testdata/fib.out b/src/cmd/cgo/internal/teststdio/testdata/fib.out
new file mode 100644
index 0000000..17ff503
--- /dev/null
+++ b/src/cmd/cgo/internal/teststdio/testdata/fib.out
@@ -0,0 +1,91 @@
+0
+1
+1
+2
+3
+5
+8
+13
+21
+34
+55
+89
+144
+233
+377
+610
+987
+1597
+2584
+4181
+6765
+10946
+17711
+28657
+46368
+75025
+121393
+196418
+317811
+514229
+832040
+1346269
+2178309
+3524578
+5702887
+9227465
+14930352
+24157817
+39088169
+63245986
+102334155
+165580141
+267914296
+433494437
+701408733
+1134903170
+1836311903
+2971215073
+4807526976
+7778742049
+12586269025
+20365011074
+32951280099
+53316291173
+86267571272
+139583862445
+225851433717
+365435296162
+591286729879
+956722026041
+1548008755920
+2504730781961
+4052739537881
+6557470319842
+10610209857723
+17167680177565
+27777890035288
+44945570212853
+72723460248141
+117669030460994
+190392490709135
+308061521170129
+498454011879264
+806515533049393
+1304969544928657
+2111485077978050
+3416454622906707
+5527939700884757
+8944394323791464
+14472334024676221
+23416728348467685
+37889062373143906
+61305790721611591
+99194853094755497
+160500643816367088
+259695496911122585
+420196140727489673
+679891637638612258
+1100087778366101931
+1779979416004714189
+2880067194370816120
diff --git a/src/cmd/cgo/internal/teststdio/testdata/hello.go b/src/cmd/cgo/internal/teststdio/testdata/hello.go
new file mode 100644
index 0000000..c0b52bf
--- /dev/null
+++ b/src/cmd/cgo/internal/teststdio/testdata/hello.go
@@ -0,0 +1,13 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build test_run
+
+package main
+
+import "cgostdio/stdio"
+
+func main() {
+ stdio.Stdout.WriteString(stdio.Greeting + "\n")
+}
diff --git a/src/cmd/cgo/internal/teststdio/testdata/hello.out b/src/cmd/cgo/internal/teststdio/testdata/hello.out
new file mode 100644
index 0000000..4b5fa63
--- /dev/null
+++ b/src/cmd/cgo/internal/teststdio/testdata/hello.out
@@ -0,0 +1 @@
+hello, world
diff --git a/src/cmd/cgo/internal/teststdio/testdata/stdio/file.go b/src/cmd/cgo/internal/teststdio/testdata/stdio/file.go
new file mode 100644
index 0000000..d97ee4c
--- /dev/null
+++ b/src/cmd/cgo/internal/teststdio/testdata/stdio/file.go
@@ -0,0 +1,42 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+A trivial example of wrapping a C library in Go.
+For a more complex example and explanation,
+see misc/cgo/gmp/gmp.go.
+*/
+
+package stdio
+
+/*
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+char* greeting = "hello, world";
+*/
+import "C"
+import "unsafe"
+
+type File C.FILE
+
+// Test reference to library symbol.
+// Stdout and stderr are too special to be a reliable test.
+//var = C.environ
+
+func (f *File) WriteString(s string) {
+ p := C.CString(s)
+ C.fputs(p, (*C.FILE)(f))
+ C.free(unsafe.Pointer(p))
+ f.Flush()
+}
+
+func (f *File) Flush() {
+ C.fflush((*C.FILE)(f))
+}
+
+var Greeting = C.GoString(C.greeting)
+var Gbytes = C.GoBytes(unsafe.Pointer(C.greeting), C.int(len(Greeting)))
diff --git a/src/cmd/cgo/internal/teststdio/testdata/stdio/stdio.go b/src/cmd/cgo/internal/teststdio/testdata/stdio/stdio.go
new file mode 100644
index 0000000..08286d4
--- /dev/null
+++ b/src/cmd/cgo/internal/teststdio/testdata/stdio/stdio.go
@@ -0,0 +1,20 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package stdio
+
+/*
+#include <stdio.h>
+
+// on mingw, stderr and stdout are defined as &_iob[FILENO]
+// on netbsd, they are defined as &__sF[FILENO]
+// and cgo doesn't recognize them, so write a function to get them,
+// instead of depending on internals of libc implementation.
+FILE *getStdout(void) { return stdout; }
+FILE *getStderr(void) { return stderr; }
+*/
+import "C"
+
+var Stdout = (*File)(C.getStdout())
+var Stderr = (*File)(C.getStderr())
diff --git a/src/cmd/cgo/internal/testtls/tls.c b/src/cmd/cgo/internal/testtls/tls.c
new file mode 100644
index 0000000..8839cc8
--- /dev/null
+++ b/src/cmd/cgo/internal/testtls/tls.c
@@ -0,0 +1,47 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <stddef.h>
+
+#if __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
+
+// Mingw seems not to have threads.h, so we use the _Thread_local keyword rather
+// than the thread_local macro.
+static _Thread_local int tls;
+
+const char *
+checkTLS() {
+ return NULL;
+}
+
+void
+setTLS(int v)
+{
+ tls = v;
+}
+
+int
+getTLS()
+{
+ return tls;
+}
+
+#else
+
+const char *
+checkTLS() {
+ return "_Thread_local requires C11 and not __STDC_NO_THREADS__";
+}
+
+void
+setTLS(int v) {
+}
+
+int
+getTLS()
+{
+ return 0;
+}
+
+#endif
diff --git a/src/cmd/cgo/internal/testtls/tls.go b/src/cmd/cgo/internal/testtls/tls.go
new file mode 100644
index 0000000..78628f5
--- /dev/null
+++ b/src/cmd/cgo/internal/testtls/tls.go
@@ -0,0 +1,34 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotlstest
+
+// extern const char *checkTLS();
+// extern void setTLS(int);
+// extern int getTLS();
+import "C"
+
+import (
+ "runtime"
+ "testing"
+)
+
+func testTLS(t *testing.T) {
+ if skip := C.checkTLS(); skip != nil {
+ t.Skipf("%s", C.GoString(skip))
+ }
+
+ runtime.LockOSThread()
+ defer runtime.UnlockOSThread()
+
+ if val := C.getTLS(); val != 0 {
+ t.Fatalf("at start, C.getTLS() = %#x, want 0", val)
+ }
+
+ const keyVal = 0x1234
+ C.setTLS(keyVal)
+ if val := C.getTLS(); val != keyVal {
+ t.Fatalf("at end, C.getTLS() = %#x, want %#x", val, keyVal)
+ }
+}
diff --git a/src/cmd/cgo/internal/testtls/tls_none.go b/src/cmd/cgo/internal/testtls/tls_none.go
new file mode 100644
index 0000000..b6033fb
--- /dev/null
+++ b/src/cmd/cgo/internal/testtls/tls_none.go
@@ -0,0 +1,13 @@
+// Copyright 2023 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+//go:build !cgo
+
+package cgotlstest
+
+import "testing"
+
+func testTLS(t *testing.T) {
+ t.Skip("cgo not supported")
+}
diff --git a/src/cmd/cgo/internal/testtls/tls_test.go b/src/cmd/cgo/internal/testtls/tls_test.go
new file mode 100644
index 0000000..8e14add
--- /dev/null
+++ b/src/cmd/cgo/internal/testtls/tls_test.go
@@ -0,0 +1,11 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotlstest
+
+import "testing"
+
+func TestTLS(t *testing.T) {
+ testTLS(t)
+}
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
new file mode 100644
index 0000000..78020ae
--- /dev/null
+++ b/src/cmd/cgo/main.go
@@ -0,0 +1,501 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Cgo; see doc.go for an overview.
+
+// TODO(rsc):
+// Emit correct line number annotations.
+// Make gc understand the annotations.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/printer"
+ "go/token"
+ "internal/buildcfg"
+ "io"
+ "os"
+ "path/filepath"
+ "reflect"
+ "runtime"
+ "sort"
+ "strings"
+
+ "cmd/internal/edit"
+ "cmd/internal/notsha256"
+ "cmd/internal/objabi"
+)
+
+// A Package collects information about the package we're going to write.
+type Package struct {
+ PackageName string // name of package
+ PackagePath string
+ PtrSize int64
+ IntSize int64
+ GccOptions []string
+ GccIsClang bool
+ CgoFlags map[string][]string // #cgo flags (CFLAGS, LDFLAGS)
+ Written map[string]bool
+ Name map[string]*Name // accumulated Name from Files
+ ExpFunc []*ExpFunc // accumulated ExpFunc from Files
+ Decl []ast.Decl
+ GoFiles []string // list of Go files
+ GccFiles []string // list of gcc output files
+ Preamble string // collected preamble for _cgo_export.h
+ typedefs map[string]bool // type names that appear in the types of the objects we're interested in
+ typedefList []typedefInfo
+}
+
+// A typedefInfo is an element on Package.typedefList: a typedef name
+// and the position where it was required.
+type typedefInfo struct {
+ typedef string
+ pos token.Pos
+}
+
+// A File collects information about a single Go input file.
+type File struct {
+ AST *ast.File // parsed AST
+ Comments []*ast.CommentGroup // comments from file
+ Package string // Package name
+ Preamble string // C preamble (doc comment on import "C")
+ Ref []*Ref // all references to C.xxx in AST
+ Calls []*Call // all calls to C.xxx in AST
+ ExpFunc []*ExpFunc // exported functions for this file
+ Name map[string]*Name // map from Go name to Name
+ NamePos map[*Name]token.Pos // map from Name to position of the first reference
+ Edit *edit.Buffer
+}
+
+func (f *File) offset(p token.Pos) int {
+ return fset.Position(p).Offset
+}
+
+func nameKeys(m map[string]*Name) []string {
+ var ks []string
+ for k := range m {
+ ks = append(ks, k)
+ }
+ sort.Strings(ks)
+ return ks
+}
+
+// A Call refers to a call of a C.xxx function in the AST.
+type Call struct {
+ Call *ast.CallExpr
+ Deferred bool
+ Done bool
+}
+
+// A Ref refers to an expression of the form C.xxx in the AST.
+type Ref struct {
+ Name *Name
+ Expr *ast.Expr
+ Context astContext
+ Done bool
+}
+
+func (r *Ref) Pos() token.Pos {
+ return (*r.Expr).Pos()
+}
+
+var nameKinds = []string{"iconst", "fconst", "sconst", "type", "var", "fpvar", "func", "macro", "not-type"}
+
+// A Name collects information about C.xxx.
+type Name struct {
+ Go string // name used in Go referring to package C
+ Mangle string // name used in generated Go
+ C string // name used in C
+ Define string // #define expansion
+ Kind string // one of the nameKinds
+ Type *Type // the type of xxx
+ FuncType *FuncType
+ AddError bool
+ Const string // constant definition
+}
+
+// IsVar reports whether Kind is either "var" or "fpvar"
+func (n *Name) IsVar() bool {
+ return n.Kind == "var" || n.Kind == "fpvar"
+}
+
+// IsConst reports whether Kind is either "iconst", "fconst" or "sconst"
+func (n *Name) IsConst() bool {
+ return strings.HasSuffix(n.Kind, "const")
+}
+
+// An ExpFunc is an exported function, callable from C.
+// Such functions are identified in the Go input file
+// by doc comments containing the line //export ExpName
+type ExpFunc struct {
+ Func *ast.FuncDecl
+ ExpName string // name to use from C
+ Doc string
+}
+
+// A TypeRepr contains the string representation of a type.
+type TypeRepr struct {
+ Repr string
+ FormatArgs []interface{}
+}
+
+// A Type collects information about a type in both the C and Go worlds.
+type Type struct {
+ Size int64
+ Align int64
+ C *TypeRepr
+ Go ast.Expr
+ EnumValues map[string]int64
+ Typedef string
+ BadPointer bool // this pointer type should be represented as a uintptr (deprecated)
+}
+
+// A FuncType collects information about a function type in both the C and Go worlds.
+type FuncType struct {
+ Params []*Type
+ Result *Type
+ Go *ast.FuncType
+}
+
+func usage() {
+ fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
+ flag.PrintDefaults()
+ os.Exit(2)
+}
+
+var ptrSizeMap = map[string]int64{
+ "386": 4,
+ "alpha": 8,
+ "amd64": 8,
+ "arm": 4,
+ "arm64": 8,
+ "loong64": 8,
+ "m68k": 4,
+ "mips": 4,
+ "mipsle": 4,
+ "mips64": 8,
+ "mips64le": 8,
+ "nios2": 4,
+ "ppc": 4,
+ "ppc64": 8,
+ "ppc64le": 8,
+ "riscv": 4,
+ "riscv64": 8,
+ "s390": 4,
+ "s390x": 8,
+ "sh": 4,
+ "shbe": 4,
+ "sparc": 4,
+ "sparc64": 8,
+}
+
+var intSizeMap = map[string]int64{
+ "386": 4,
+ "alpha": 8,
+ "amd64": 8,
+ "arm": 4,
+ "arm64": 8,
+ "loong64": 8,
+ "m68k": 4,
+ "mips": 4,
+ "mipsle": 4,
+ "mips64": 8,
+ "mips64le": 8,
+ "nios2": 4,
+ "ppc": 4,
+ "ppc64": 8,
+ "ppc64le": 8,
+ "riscv": 4,
+ "riscv64": 8,
+ "s390": 4,
+ "s390x": 8,
+ "sh": 4,
+ "shbe": 4,
+ "sparc": 4,
+ "sparc64": 8,
+}
+
+var cPrefix string
+
+var fset = token.NewFileSet()
+
+var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
+var dynout = flag.String("dynout", "", "write -dynimport output to this file")
+var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
+var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")
+
+// This flag is for bootstrapping a new Go implementation,
+// to generate Go types that match the data layout and
+// constant values used in the host's C libraries and system calls.
+var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
+
+var srcDir = flag.String("srcdir", "", "source directory")
+var objDir = flag.String("objdir", "", "object directory")
+var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
+var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
+
+var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
+var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
+var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
+var gccgoMangler func(string) string
+var gccgoDefineCgoIncomplete = flag.Bool("gccgo_define_cgoincomplete", false, "define cgo.Incomplete for older gccgo/GoLLVM")
+var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
+var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
+var trimpath = flag.String("trimpath", "", "applies supplied rewrites or trims prefixes to recorded source file paths")
+
+var goarch, goos, gomips, gomips64 string
+var gccBaseCmd []string
+
+func main() {
+ objabi.AddVersionFlag() // -V
+ objabi.Flagparse(usage)
+
+ if *gccgoDefineCgoIncomplete {
+ if !*gccgo {
+ fmt.Fprintf(os.Stderr, "cgo: -gccgo_define_cgoincomplete without -gccgo\n")
+ os.Exit(2)
+ }
+ incomplete = "_cgopackage_Incomplete"
+ }
+
+ if *dynobj != "" {
+ // cgo -dynimport is essentially a separate helper command
+ // built into the cgo binary. It scans a gcc-produced executable
+ // and dumps information about the imported symbols and the
+ // imported libraries. The 'go build' rules for cgo prepare an
+ // appropriate executable and then use its import information
+ // instead of needing to make the linkers duplicate all the
+ // specialized knowledge gcc has about where to look for imported
+ // symbols and which ones to use.
+ dynimport(*dynobj)
+ return
+ }
+
+ if *godefs {
+ // Generating definitions pulled from header files,
+ // to be checked into Go repositories.
+ // Line numbers are just noise.
+ conf.Mode &^= printer.SourcePos
+ }
+
+ args := flag.Args()
+ if len(args) < 1 {
+ usage()
+ }
+
+ // Find first arg that looks like a go file and assume everything before
+ // that are options to pass to gcc.
+ var i int
+ for i = len(args); i > 0; i-- {
+ if !strings.HasSuffix(args[i-1], ".go") {
+ break
+ }
+ }
+ if i == len(args) {
+ usage()
+ }
+
+ // Save original command line arguments for the godefs generated comment. Relative file
+ // paths in os.Args will be rewritten to absolute file paths in the loop below.
+ osArgs := make([]string, len(os.Args))
+ copy(osArgs, os.Args[:])
+ goFiles := args[i:]
+
+ for _, arg := range args[:i] {
+ if arg == "-fsanitize=thread" {
+ tsanProlog = yesTsanProlog
+ }
+ if arg == "-fsanitize=memory" {
+ msanProlog = yesMsanProlog
+ }
+ }
+
+ p := newPackage(args[:i])
+
+ // We need a C compiler to be available. Check this.
+ var err error
+ gccBaseCmd, err = checkGCCBaseCmd()
+ if err != nil {
+ fatalf("%v", err)
+ os.Exit(2)
+ }
+
+ // Record CGO_LDFLAGS from the environment for external linking.
+ if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
+ args, err := splitQuoted(ldflags)
+ if err != nil {
+ fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
+ }
+ p.addToFlag("LDFLAGS", args)
+ }
+
+ // Need a unique prefix for the global C symbols that
+ // we use to coordinate between gcc and ourselves.
+ // We already put _cgo_ at the beginning, so the main
+ // concern is other cgo wrappers for the same functions.
+ // Use the beginning of the notsha256 of the input to disambiguate.
+ h := notsha256.New()
+ io.WriteString(h, *importPath)
+ fs := make([]*File, len(goFiles))
+ for i, input := range goFiles {
+ if *srcDir != "" {
+ input = filepath.Join(*srcDir, input)
+ }
+
+ // Create absolute path for file, so that it will be used in error
+ // messages and recorded in debug line number information.
+ // This matches the rest of the toolchain. See golang.org/issue/5122.
+ if aname, err := filepath.Abs(input); err == nil {
+ input = aname
+ }
+
+ b, err := os.ReadFile(input)
+ if err != nil {
+ fatalf("%s", err)
+ }
+ if _, err = h.Write(b); err != nil {
+ fatalf("%s", err)
+ }
+
+ // Apply trimpath to the file path. The path won't be read from after this point.
+ input, _ = objabi.ApplyRewrites(input, *trimpath)
+ if strings.ContainsAny(input, "\r\n") {
+ // ParseGo, (*Package).writeOutput, and printer.Fprint in SourcePos mode
+ // all emit line directives, which don't permit newlines in the file path.
+ // Bail early if we see anything newline-like in the trimmed path.
+ fatalf("input path contains newline character: %q", input)
+ }
+ goFiles[i] = input
+
+ f := new(File)
+ f.Edit = edit.NewBuffer(b)
+ f.ParseGo(input, b)
+ f.DiscardCgoDirectives()
+ fs[i] = f
+ }
+
+ cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])
+
+ if *objDir == "" {
+ // make sure that _obj directory exists, so that we can write
+ // all the output files there.
+ os.Mkdir("_obj", 0777)
+ *objDir = "_obj"
+ }
+ *objDir += string(filepath.Separator)
+
+ for i, input := range goFiles {
+ f := fs[i]
+ p.Translate(f)
+ for _, cref := range f.Ref {
+ switch cref.Context {
+ case ctxCall, ctxCall2:
+ if cref.Name.Kind != "type" {
+ break
+ }
+ old := *cref.Expr
+ *cref.Expr = cref.Name.Type.Go
+ f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), gofmt(cref.Name.Type.Go))
+ }
+ }
+ if nerrors > 0 {
+ os.Exit(2)
+ }
+ p.PackagePath = f.Package
+ p.Record(f)
+ if *godefs {
+ os.Stdout.WriteString(p.godefs(f, osArgs))
+ } else {
+ p.writeOutput(f, input)
+ }
+ }
+
+ if !*godefs {
+ p.writeDefs()
+ }
+ if nerrors > 0 {
+ os.Exit(2)
+ }
+}
+
+// newPackage returns a new Package that will invoke
+// gcc with the additional arguments specified in args.
+func newPackage(args []string) *Package {
+ goarch = runtime.GOARCH
+ if s := os.Getenv("GOARCH"); s != "" {
+ goarch = s
+ }
+ goos = runtime.GOOS
+ if s := os.Getenv("GOOS"); s != "" {
+ goos = s
+ }
+ buildcfg.Check()
+ gomips = buildcfg.GOMIPS
+ gomips64 = buildcfg.GOMIPS64
+ ptrSize := ptrSizeMap[goarch]
+ if ptrSize == 0 {
+ fatalf("unknown ptrSize for $GOARCH %q", goarch)
+ }
+ intSize := intSizeMap[goarch]
+ if intSize == 0 {
+ fatalf("unknown intSize for $GOARCH %q", goarch)
+ }
+
+ // Reset locale variables so gcc emits English errors [sic].
+ os.Setenv("LANG", "en_US.UTF-8")
+ os.Setenv("LC_ALL", "C")
+
+ p := &Package{
+ PtrSize: ptrSize,
+ IntSize: intSize,
+ CgoFlags: make(map[string][]string),
+ Written: make(map[string]bool),
+ }
+ p.addToFlag("CFLAGS", args)
+ return p
+}
+
+// Record what needs to be recorded about f.
+func (p *Package) Record(f *File) {
+ if p.PackageName == "" {
+ p.PackageName = f.Package
+ } else if p.PackageName != f.Package {
+ error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
+ }
+
+ if p.Name == nil {
+ p.Name = f.Name
+ } else {
+ for k, v := range f.Name {
+ if p.Name[k] == nil {
+ p.Name[k] = v
+ } else if p.incompleteTypedef(p.Name[k].Type) {
+ p.Name[k] = v
+ } else if p.incompleteTypedef(v.Type) {
+ // Nothing to do.
+ } else if _, ok := nameToC[k]; ok {
+ // Names we predefine may appear inconsistent
+ // if some files typedef them and some don't.
+ // Issue 26743.
+ } else if !reflect.DeepEqual(p.Name[k], v) {
+ error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
+ }
+ }
+ }
+
+ if f.ExpFunc != nil {
+ p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
+ p.Preamble += "\n" + f.Preamble
+ }
+ p.Decl = append(p.Decl, f.AST.Decls...)
+}
+
+// incompleteTypedef reports whether t appears to be an incomplete
+// typedef definition.
+func (p *Package) incompleteTypedef(t *Type) bool {
+ return t == nil || (t.Size == 0 && t.Align == -1)
+}
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
new file mode 100644
index 0000000..b2933e2
--- /dev/null
+++ b/src/cmd/cgo/out.go
@@ -0,0 +1,1988 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "cmd/internal/pkgpath"
+ "debug/elf"
+ "debug/macho"
+ "debug/pe"
+ "fmt"
+ "go/ast"
+ "go/printer"
+ "go/token"
+ "internal/xcoff"
+ "io"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "regexp"
+ "sort"
+ "strings"
+ "unicode"
+)
+
+var (
+ conf = printer.Config{Mode: printer.SourcePos, Tabwidth: 8}
+ noSourceConf = printer.Config{Tabwidth: 8}
+)
+
+// writeDefs creates output files to be compiled by gc and gcc.
+func (p *Package) writeDefs() {
+ var fgo2, fc io.Writer
+ f := creat(*objDir + "_cgo_gotypes.go")
+ defer f.Close()
+ fgo2 = f
+ if *gccgo {
+ f := creat(*objDir + "_cgo_defun.c")
+ defer f.Close()
+ fc = f
+ }
+ fm := creat(*objDir + "_cgo_main.c")
+
+ var gccgoInit strings.Builder
+
+ fflg := creat(*objDir + "_cgo_flags")
+ for k, v := range p.CgoFlags {
+ for _, arg := range v {
+ fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, arg)
+ }
+ if k == "LDFLAGS" && !*gccgo {
+ for _, arg := range v {
+ fmt.Fprintf(fgo2, "//go:cgo_ldflag %q\n", arg)
+ }
+ }
+ }
+ fflg.Close()
+
+ // Write C main file for using gcc to resolve imports.
+ fmt.Fprintf(fm, "#include <stddef.h>\n") // For size_t below.
+ fmt.Fprintf(fm, "int main() { return 0; }\n")
+ if *importRuntimeCgo {
+ fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*) __attribute__((unused)), void *a __attribute__((unused)), int c __attribute__((unused)), size_t ctxt __attribute__((unused))) { }\n")
+ fmt.Fprintf(fm, "size_t _cgo_wait_runtime_init_done(void) { return 0; }\n")
+ fmt.Fprintf(fm, "void _cgo_release_context(size_t ctxt __attribute__((unused))) { }\n")
+ fmt.Fprintf(fm, "char* _cgo_topofstack(void) { return (char*)0; }\n")
+ } else {
+ // If we're not importing runtime/cgo, we *are* runtime/cgo,
+ // which provides these functions. We just need a prototype.
+ fmt.Fprintf(fm, "void crosscall2(void(*fn)(void*), void *a, int c, size_t ctxt);\n")
+ fmt.Fprintf(fm, "size_t _cgo_wait_runtime_init_done(void);\n")
+ fmt.Fprintf(fm, "void _cgo_release_context(size_t);\n")
+ }
+ fmt.Fprintf(fm, "void _cgo_allocate(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n")
+ fmt.Fprintf(fm, "void _cgo_panic(void *a __attribute__((unused)), int c __attribute__((unused))) { }\n")
+ fmt.Fprintf(fm, "void _cgo_reginit(void) { }\n")
+
+ // Write second Go output: definitions of _C_xxx.
+ // In a separate file so that the import of "unsafe" does not
+ // pollute the original file.
+ fmt.Fprintf(fgo2, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n")
+ fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName)
+ fmt.Fprintf(fgo2, "import \"unsafe\"\n\n")
+ if *importSyscall {
+ fmt.Fprintf(fgo2, "import \"syscall\"\n\n")
+ }
+ if *importRuntimeCgo {
+ if !*gccgoDefineCgoIncomplete {
+ fmt.Fprintf(fgo2, "import _cgopackage \"runtime/cgo\"\n\n")
+ fmt.Fprintf(fgo2, "type _ _cgopackage.Incomplete\n") // prevent import-not-used error
+ } else {
+ fmt.Fprintf(fgo2, "//go:notinheap\n")
+ fmt.Fprintf(fgo2, "type _cgopackage_Incomplete struct{ _ struct{ _ struct{} } }\n")
+ }
+ }
+ if *importSyscall {
+ fmt.Fprintf(fgo2, "var _ syscall.Errno\n")
+ }
+ fmt.Fprintf(fgo2, "func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }\n\n")
+
+ if !*gccgo {
+ fmt.Fprintf(fgo2, "//go:linkname _Cgo_always_false runtime.cgoAlwaysFalse\n")
+ fmt.Fprintf(fgo2, "var _Cgo_always_false bool\n")
+ fmt.Fprintf(fgo2, "//go:linkname _Cgo_use runtime.cgoUse\n")
+ fmt.Fprintf(fgo2, "func _Cgo_use(interface{})\n")
+ }
+
+ typedefNames := make([]string, 0, len(typedef))
+ for name := range typedef {
+ if name == "_Ctype_void" {
+ // We provide an appropriate declaration for
+ // _Ctype_void below (#39877).
+ continue
+ }
+ typedefNames = append(typedefNames, name)
+ }
+ sort.Strings(typedefNames)
+ for _, name := range typedefNames {
+ def := typedef[name]
+ fmt.Fprintf(fgo2, "type %s ", name)
+ // We don't have source info for these types, so write them out without source info.
+ // Otherwise types would look like:
+ //
+ // type _Ctype_struct_cb struct {
+ // //line :1
+ // on_test *[0]byte
+ // //line :1
+ // }
+ //
+ // Which is not useful. Moreover we never override source info,
+ // so subsequent source code uses the same source info.
+ // Moreover, empty file name makes compile emit no source debug info at all.
+ var buf bytes.Buffer
+ noSourceConf.Fprint(&buf, fset, def.Go)
+ if bytes.HasPrefix(buf.Bytes(), []byte("_Ctype_")) ||
+ strings.HasPrefix(name, "_Ctype_enum_") ||
+ strings.HasPrefix(name, "_Ctype_union_") {
+ // This typedef is of the form `typedef a b` and should be an alias.
+ fmt.Fprintf(fgo2, "= ")
+ }
+ fmt.Fprintf(fgo2, "%s", buf.Bytes())
+ fmt.Fprintf(fgo2, "\n\n")
+ }
+ if *gccgo {
+ fmt.Fprintf(fgo2, "type _Ctype_void byte\n")
+ } else {
+ fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n")
+ }
+
+ if *gccgo {
+ fmt.Fprint(fgo2, gccgoGoProlog)
+ fmt.Fprint(fc, p.cPrologGccgo())
+ } else {
+ fmt.Fprint(fgo2, goProlog)
+ }
+
+ if fc != nil {
+ fmt.Fprintf(fc, "#line 1 \"cgo-generated-wrappers\"\n")
+ }
+ if fm != nil {
+ fmt.Fprintf(fm, "#line 1 \"cgo-generated-wrappers\"\n")
+ }
+
+ gccgoSymbolPrefix := p.gccgoSymbolPrefix()
+
+ cVars := make(map[string]bool)
+ for _, key := range nameKeys(p.Name) {
+ n := p.Name[key]
+ if !n.IsVar() {
+ continue
+ }
+
+ if !cVars[n.C] {
+ if *gccgo {
+ fmt.Fprintf(fc, "extern byte *%s;\n", n.C)
+ } else {
+ // Force a reference to all symbols so that
+ // the external linker will add DT_NEEDED
+ // entries as needed on ELF systems.
+ // Treat function variables differently
+ // to avoid type conflict errors from LTO
+ // (Link Time Optimization).
+ if n.Kind == "fpvar" {
+ fmt.Fprintf(fm, "extern void %s();\n", n.C)
+ } else {
+ fmt.Fprintf(fm, "extern char %s[];\n", n.C)
+ fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
+ }
+ fmt.Fprintf(fgo2, "//go:linkname __cgo_%s %s\n", n.C, n.C)
+ fmt.Fprintf(fgo2, "//go:cgo_import_static %s\n", n.C)
+ fmt.Fprintf(fgo2, "var __cgo_%s byte\n", n.C)
+ }
+ cVars[n.C] = true
+ }
+
+ var node ast.Node
+ if n.Kind == "var" {
+ node = &ast.StarExpr{X: n.Type.Go}
+ } else if n.Kind == "fpvar" {
+ node = n.Type.Go
+ } else {
+ panic(fmt.Errorf("invalid var kind %q", n.Kind))
+ }
+ if *gccgo {
+ fmt.Fprintf(fc, `extern void *%s __asm__("%s.%s");`, n.Mangle, gccgoSymbolPrefix, gccgoToSymbol(n.Mangle))
+ fmt.Fprintf(&gccgoInit, "\t%s = &%s;\n", n.Mangle, n.C)
+ fmt.Fprintf(fc, "\n")
+ }
+
+ fmt.Fprintf(fgo2, "var %s ", n.Mangle)
+ conf.Fprint(fgo2, fset, node)
+ if !*gccgo {
+ fmt.Fprintf(fgo2, " = (")
+ conf.Fprint(fgo2, fset, node)
+ fmt.Fprintf(fgo2, ")(unsafe.Pointer(&__cgo_%s))", n.C)
+ }
+ fmt.Fprintf(fgo2, "\n")
+ }
+ if *gccgo {
+ fmt.Fprintf(fc, "\n")
+ }
+
+ for _, key := range nameKeys(p.Name) {
+ n := p.Name[key]
+ if n.Const != "" {
+ fmt.Fprintf(fgo2, "const %s = %s\n", n.Mangle, n.Const)
+ }
+ }
+ fmt.Fprintf(fgo2, "\n")
+
+ callsMalloc := false
+ for _, key := range nameKeys(p.Name) {
+ n := p.Name[key]
+ if n.FuncType != nil {
+ p.writeDefsFunc(fgo2, n, &callsMalloc)
+ }
+ }
+
+ fgcc := creat(*objDir + "_cgo_export.c")
+ fgcch := creat(*objDir + "_cgo_export.h")
+ if *gccgo {
+ p.writeGccgoExports(fgo2, fm, fgcc, fgcch)
+ } else {
+ p.writeExports(fgo2, fm, fgcc, fgcch)
+ }
+
+ if callsMalloc && !*gccgo {
+ fmt.Fprint(fgo2, strings.Replace(cMallocDefGo, "PREFIX", cPrefix, -1))
+ fmt.Fprint(fgcc, strings.Replace(strings.Replace(cMallocDefC, "PREFIX", cPrefix, -1), "PACKED", p.packedAttribute(), -1))
+ }
+
+ if err := fgcc.Close(); err != nil {
+ fatalf("%s", err)
+ }
+ if err := fgcch.Close(); err != nil {
+ fatalf("%s", err)
+ }
+
+ if *exportHeader != "" && len(p.ExpFunc) > 0 {
+ fexp := creat(*exportHeader)
+ fgcch, err := os.Open(*objDir + "_cgo_export.h")
+ if err != nil {
+ fatalf("%s", err)
+ }
+ defer fgcch.Close()
+ _, err = io.Copy(fexp, fgcch)
+ if err != nil {
+ fatalf("%s", err)
+ }
+ if err = fexp.Close(); err != nil {
+ fatalf("%s", err)
+ }
+ }
+
+ init := gccgoInit.String()
+ if init != "" {
+ // The init function does nothing but simple
+ // assignments, so it won't use much stack space, so
+ // it's OK to not split the stack. Splitting the stack
+ // can run into a bug in clang (as of 2018-11-09):
+ // this is a leaf function, and when clang sees a leaf
+ // function it won't emit the split stack prologue for
+ // the function. However, if this function refers to a
+ // non-split-stack function, which will happen if the
+ // cgo code refers to a C function not compiled with
+ // -fsplit-stack, then the linker will think that it
+ // needs to adjust the split stack prologue, but there
+ // won't be one. Marking the function explicitly
+ // no_split_stack works around this problem by telling
+ // the linker that it's OK if there is no split stack
+ // prologue.
+ fmt.Fprintln(fc, "static void init(void) __attribute__ ((constructor, no_split_stack));")
+ fmt.Fprintln(fc, "static void init(void) {")
+ fmt.Fprint(fc, init)
+ fmt.Fprintln(fc, "}")
+ }
+}
+
+// elfImportedSymbols is like elf.File.ImportedSymbols, but it
+// includes weak symbols.
+//
+// A bug in some versions of LLD (at least LLD 8) cause it to emit
+// several pthreads symbols as weak, but we need to import those. See
+// issue #31912 or https://bugs.llvm.org/show_bug.cgi?id=42442.
+//
+// When doing external linking, we hand everything off to the external
+// linker, which will create its own dynamic symbol tables. For
+// internal linking, this may turn weak imports into strong imports,
+// which could cause dynamic linking to fail if a symbol really isn't
+// defined. However, the standard library depends on everything it
+// imports, and this is the primary use of dynamic symbol tables with
+// internal linking.
+func elfImportedSymbols(f *elf.File) []elf.ImportedSymbol {
+ syms, _ := f.DynamicSymbols()
+ var imports []elf.ImportedSymbol
+ for _, s := range syms {
+ if (elf.ST_BIND(s.Info) == elf.STB_GLOBAL || elf.ST_BIND(s.Info) == elf.STB_WEAK) && s.Section == elf.SHN_UNDEF {
+ imports = append(imports, elf.ImportedSymbol{
+ Name: s.Name,
+ Library: s.Library,
+ Version: s.Version,
+ })
+ }
+ }
+ return imports
+}
+
+func dynimport(obj string) {
+ stdout := os.Stdout
+ if *dynout != "" {
+ f, err := os.Create(*dynout)
+ if err != nil {
+ fatalf("%s", err)
+ }
+ stdout = f
+ }
+
+ fmt.Fprintf(stdout, "package %s\n", *dynpackage)
+
+ if f, err := elf.Open(obj); err == nil {
+ if *dynlinker {
+ // Emit the cgo_dynamic_linker line.
+ if sec := f.Section(".interp"); sec != nil {
+ if data, err := sec.Data(); err == nil && len(data) > 1 {
+ // skip trailing \0 in data
+ fmt.Fprintf(stdout, "//go:cgo_dynamic_linker %q\n", string(data[:len(data)-1]))
+ }
+ }
+ }
+ sym := elfImportedSymbols(f)
+ for _, s := range sym {
+ targ := s.Name
+ if s.Version != "" {
+ targ += "#" + s.Version
+ }
+ checkImportSymName(s.Name)
+ checkImportSymName(targ)
+ fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library)
+ }
+ lib, _ := f.ImportedLibraries()
+ for _, l := range lib {
+ fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
+ }
+ return
+ }
+
+ if f, err := macho.Open(obj); err == nil {
+ sym, _ := f.ImportedSymbols()
+ for _, s := range sym {
+ if len(s) > 0 && s[0] == '_' {
+ s = s[1:]
+ }
+ checkImportSymName(s)
+ fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "")
+ }
+ lib, _ := f.ImportedLibraries()
+ for _, l := range lib {
+ fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
+ }
+ return
+ }
+
+ if f, err := pe.Open(obj); err == nil {
+ sym, _ := f.ImportedSymbols()
+ for _, s := range sym {
+ ss := strings.Split(s, ":")
+ name := strings.Split(ss[0], "@")[0]
+ checkImportSymName(name)
+ checkImportSymName(ss[0])
+ fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1]))
+ }
+ return
+ }
+
+ if f, err := xcoff.Open(obj); err == nil {
+ sym, err := f.ImportedSymbols()
+ if err != nil {
+ fatalf("cannot load imported symbols from XCOFF file %s: %v", obj, err)
+ }
+ for _, s := range sym {
+ if s.Name == "runtime_rt0_go" || s.Name == "_rt0_ppc64_aix_lib" {
+ // These symbols are imported by runtime/cgo but
+ // must not be added to _cgo_import.go as there are
+ // Go symbols.
+ continue
+ }
+ checkImportSymName(s.Name)
+ fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, s.Name, s.Library)
+ }
+ lib, err := f.ImportedLibraries()
+ if err != nil {
+ fatalf("cannot load imported libraries from XCOFF file %s: %v", obj, err)
+ }
+ for _, l := range lib {
+ fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
+ }
+ return
+ }
+
+ fatalf("cannot parse %s as ELF, Mach-O, PE or XCOFF", obj)
+}
+
+// checkImportSymName checks a symbol name we are going to emit as part
+// of a //go:cgo_import_dynamic pragma. These names come from object
+// files, so they may be corrupt. We are going to emit them unquoted,
+// so while they don't need to be valid symbol names (and in some cases,
+// involving symbol versions, they won't be) they must contain only
+// graphic characters and must not contain Go comments.
+func checkImportSymName(s string) {
+ for _, c := range s {
+ if !unicode.IsGraphic(c) || unicode.IsSpace(c) {
+ fatalf("dynamic symbol %q contains unsupported character", s)
+ }
+ }
+ if strings.Contains(s, "//") || strings.Contains(s, "/*") {
+ fatalf("dynamic symbol %q contains Go comment")
+ }
+}
+
+// Construct a gcc struct matching the gc argument frame.
+// Assumes that in gcc, char is 1 byte, short 2 bytes, int 4 bytes, long long 8 bytes.
+// These assumptions are checked by the gccProlog.
+// Also assumes that gc convention is to word-align the
+// input and output parameters.
+func (p *Package) structType(n *Name) (string, int64) {
+ var buf strings.Builder
+ fmt.Fprint(&buf, "struct {\n")
+ off := int64(0)
+ for i, t := range n.FuncType.Params {
+ if off%t.Align != 0 {
+ pad := t.Align - off%t.Align
+ fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
+ off += pad
+ }
+ c := t.Typedef
+ if c == "" {
+ c = t.C.String()
+ }
+ fmt.Fprintf(&buf, "\t\t%s p%d;\n", c, i)
+ off += t.Size
+ }
+ if off%p.PtrSize != 0 {
+ pad := p.PtrSize - off%p.PtrSize
+ fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
+ off += pad
+ }
+ if t := n.FuncType.Result; t != nil {
+ if off%t.Align != 0 {
+ pad := t.Align - off%t.Align
+ fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
+ off += pad
+ }
+ fmt.Fprintf(&buf, "\t\t%s r;\n", t.C)
+ off += t.Size
+ }
+ if off%p.PtrSize != 0 {
+ pad := p.PtrSize - off%p.PtrSize
+ fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad)
+ off += pad
+ }
+ if off == 0 {
+ fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct
+ }
+ fmt.Fprintf(&buf, "\t}")
+ return buf.String(), off
+}
+
+func (p *Package) writeDefsFunc(fgo2 io.Writer, n *Name, callsMalloc *bool) {
+ name := n.Go
+ gtype := n.FuncType.Go
+ void := gtype.Results == nil || len(gtype.Results.List) == 0
+ if n.AddError {
+ // Add "error" to return type list.
+ // Type list is known to be 0 or 1 element - it's a C function.
+ err := &ast.Field{Type: ast.NewIdent("error")}
+ l := gtype.Results.List
+ if len(l) == 0 {
+ l = []*ast.Field{err}
+ } else {
+ l = []*ast.Field{l[0], err}
+ }
+ t := new(ast.FuncType)
+ *t = *gtype
+ t.Results = &ast.FieldList{List: l}
+ gtype = t
+ }
+
+ // Go func declaration.
+ d := &ast.FuncDecl{
+ Name: ast.NewIdent(n.Mangle),
+ Type: gtype,
+ }
+
+ // Builtins defined in the C prolog.
+ inProlog := builtinDefs[name] != ""
+ cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle)
+ paramnames := []string(nil)
+ if d.Type.Params != nil {
+ for i, param := range d.Type.Params.List {
+ paramName := fmt.Sprintf("p%d", i)
+ param.Names = []*ast.Ident{ast.NewIdent(paramName)}
+ paramnames = append(paramnames, paramName)
+ }
+ }
+
+ if *gccgo {
+ // Gccgo style hooks.
+ fmt.Fprint(fgo2, "\n")
+ conf.Fprint(fgo2, fset, d)
+ fmt.Fprint(fgo2, " {\n")
+ if !inProlog {
+ fmt.Fprint(fgo2, "\tdefer syscall.CgocallDone()\n")
+ fmt.Fprint(fgo2, "\tsyscall.Cgocall()\n")
+ }
+ if n.AddError {
+ fmt.Fprint(fgo2, "\tsyscall.SetErrno(0)\n")
+ }
+ fmt.Fprint(fgo2, "\t")
+ if !void {
+ fmt.Fprint(fgo2, "r := ")
+ }
+ fmt.Fprintf(fgo2, "%s(%s)\n", cname, strings.Join(paramnames, ", "))
+
+ if n.AddError {
+ fmt.Fprint(fgo2, "\te := syscall.GetErrno()\n")
+ fmt.Fprint(fgo2, "\tif e != 0 {\n")
+ fmt.Fprint(fgo2, "\t\treturn ")
+ if !void {
+ fmt.Fprint(fgo2, "r, ")
+ }
+ fmt.Fprint(fgo2, "e\n")
+ fmt.Fprint(fgo2, "\t}\n")
+ fmt.Fprint(fgo2, "\treturn ")
+ if !void {
+ fmt.Fprint(fgo2, "r, ")
+ }
+ fmt.Fprint(fgo2, "nil\n")
+ } else if !void {
+ fmt.Fprint(fgo2, "\treturn r\n")
+ }
+
+ fmt.Fprint(fgo2, "}\n")
+
+ // declare the C function.
+ fmt.Fprintf(fgo2, "//extern %s\n", cname)
+ d.Name = ast.NewIdent(cname)
+ if n.AddError {
+ l := d.Type.Results.List
+ d.Type.Results.List = l[:len(l)-1]
+ }
+ conf.Fprint(fgo2, fset, d)
+ fmt.Fprint(fgo2, "\n")
+
+ return
+ }
+
+ if inProlog {
+ fmt.Fprint(fgo2, builtinDefs[name])
+ if strings.Contains(builtinDefs[name], "_cgo_cmalloc") {
+ *callsMalloc = true
+ }
+ return
+ }
+
+ // Wrapper calls into gcc, passing a pointer to the argument frame.
+ fmt.Fprintf(fgo2, "//go:cgo_import_static %s\n", cname)
+ fmt.Fprintf(fgo2, "//go:linkname __cgofn_%s %s\n", cname, cname)
+ fmt.Fprintf(fgo2, "var __cgofn_%s byte\n", cname)
+ fmt.Fprintf(fgo2, "var %s = unsafe.Pointer(&__cgofn_%s)\n", cname, cname)
+
+ nret := 0
+ if !void {
+ d.Type.Results.List[0].Names = []*ast.Ident{ast.NewIdent("r1")}
+ nret = 1
+ }
+ if n.AddError {
+ d.Type.Results.List[nret].Names = []*ast.Ident{ast.NewIdent("r2")}
+ }
+
+ fmt.Fprint(fgo2, "\n")
+ fmt.Fprint(fgo2, "//go:cgo_unsafe_args\n")
+ conf.Fprint(fgo2, fset, d)
+ fmt.Fprint(fgo2, " {\n")
+
+ // NOTE: Using uintptr to hide from escape analysis.
+ arg := "0"
+ if len(paramnames) > 0 {
+ arg = "uintptr(unsafe.Pointer(&p0))"
+ } else if !void {
+ arg = "uintptr(unsafe.Pointer(&r1))"
+ }
+
+ prefix := ""
+ if n.AddError {
+ prefix = "errno := "
+ }
+ fmt.Fprintf(fgo2, "\t%s_cgo_runtime_cgocall(%s, %s)\n", prefix, cname, arg)
+ if n.AddError {
+ fmt.Fprintf(fgo2, "\tif errno != 0 { r2 = syscall.Errno(errno) }\n")
+ }
+ fmt.Fprintf(fgo2, "\tif _Cgo_always_false {\n")
+ if d.Type.Params != nil {
+ for i := range d.Type.Params.List {
+ fmt.Fprintf(fgo2, "\t\t_Cgo_use(p%d)\n", i)
+ }
+ }
+ fmt.Fprintf(fgo2, "\t}\n")
+ fmt.Fprintf(fgo2, "\treturn\n")
+ fmt.Fprintf(fgo2, "}\n")
+}
+
+// writeOutput creates stubs for a specific source file to be compiled by gc
+func (p *Package) writeOutput(f *File, srcfile string) {
+ base := srcfile
+ base = strings.TrimSuffix(base, ".go")
+ base = filepath.Base(base)
+ fgo1 := creat(*objDir + base + ".cgo1.go")
+ fgcc := creat(*objDir + base + ".cgo2.c")
+
+ p.GoFiles = append(p.GoFiles, base+".cgo1.go")
+ p.GccFiles = append(p.GccFiles, base+".cgo2.c")
+
+ // Write Go output: Go input with rewrites of C.xxx to _C_xxx.
+ fmt.Fprintf(fgo1, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n")
+ if strings.ContainsAny(srcfile, "\r\n") {
+ // This should have been checked when the file path was first resolved,
+ // but we double check here just to be sure.
+ fatalf("internal error: writeOutput: srcfile contains unexpected newline character: %q", srcfile)
+ }
+ fmt.Fprintf(fgo1, "//line %s:1:1\n", srcfile)
+ fgo1.Write(f.Edit.Bytes())
+
+ // While we process the vars and funcs, also write gcc output.
+ // Gcc output starts with the preamble.
+ fmt.Fprintf(fgcc, "%s\n", builtinProlog)
+ fmt.Fprintf(fgcc, "%s\n", f.Preamble)
+ fmt.Fprintf(fgcc, "%s\n", gccProlog)
+ fmt.Fprintf(fgcc, "%s\n", tsanProlog)
+ fmt.Fprintf(fgcc, "%s\n", msanProlog)
+
+ for _, key := range nameKeys(f.Name) {
+ n := f.Name[key]
+ if n.FuncType != nil {
+ p.writeOutputFunc(fgcc, n)
+ }
+ }
+
+ fgo1.Close()
+ fgcc.Close()
+}
+
+// fixGo converts the internal Name.Go field into the name we should show
+// to users in error messages. There's only one for now: on input we rewrite
+// C.malloc into C._CMalloc, so change it back here.
+func fixGo(name string) string {
+ if name == "_CMalloc" {
+ return "malloc"
+ }
+ return name
+}
+
+var isBuiltin = map[string]bool{
+ "_Cfunc_CString": true,
+ "_Cfunc_CBytes": true,
+ "_Cfunc_GoString": true,
+ "_Cfunc_GoStringN": true,
+ "_Cfunc_GoBytes": true,
+ "_Cfunc__CMalloc": true,
+}
+
+func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
+ name := n.Mangle
+ if isBuiltin[name] || p.Written[name] {
+ // The builtins are already defined in the C prolog, and we don't
+ // want to duplicate function definitions we've already done.
+ return
+ }
+ p.Written[name] = true
+
+ if *gccgo {
+ p.writeGccgoOutputFunc(fgcc, n)
+ return
+ }
+
+ ctype, _ := p.structType(n)
+
+ // Gcc wrapper unpacks the C argument struct
+ // and calls the actual C function.
+ fmt.Fprintf(fgcc, "CGO_NO_SANITIZE_THREAD\n")
+ if n.AddError {
+ fmt.Fprintf(fgcc, "int\n")
+ } else {
+ fmt.Fprintf(fgcc, "void\n")
+ }
+ fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle)
+ fmt.Fprintf(fgcc, "{\n")
+ if n.AddError {
+ fmt.Fprintf(fgcc, "\tint _cgo_errno;\n")
+ }
+ // We're trying to write a gcc struct that matches gc's layout.
+ // Use packed attribute to force no padding in this struct in case
+ // gcc has different packing requirements.
+ fmt.Fprintf(fgcc, "\t%s %v *_cgo_a = v;\n", ctype, p.packedAttribute())
+ if n.FuncType.Result != nil {
+ // Save the stack top for use below.
+ fmt.Fprintf(fgcc, "\tchar *_cgo_stktop = _cgo_topofstack();\n")
+ }
+ tr := n.FuncType.Result
+ if tr != nil {
+ fmt.Fprintf(fgcc, "\t__typeof__(_cgo_a->r) _cgo_r;\n")
+ }
+ fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
+ if n.AddError {
+ fmt.Fprintf(fgcc, "\terrno = 0;\n")
+ }
+ fmt.Fprintf(fgcc, "\t")
+ if tr != nil {
+ fmt.Fprintf(fgcc, "_cgo_r = ")
+ if c := tr.C.String(); c[len(c)-1] == '*' {
+ fmt.Fprint(fgcc, "(__typeof__(_cgo_a->r)) ")
+ }
+ }
+ if n.Kind == "macro" {
+ fmt.Fprintf(fgcc, "%s;\n", n.C)
+ } else {
+ fmt.Fprintf(fgcc, "%s(", n.C)
+ for i := range n.FuncType.Params {
+ if i > 0 {
+ fmt.Fprintf(fgcc, ", ")
+ }
+ fmt.Fprintf(fgcc, "_cgo_a->p%d", i)
+ }
+ fmt.Fprintf(fgcc, ");\n")
+ }
+ if n.AddError {
+ fmt.Fprintf(fgcc, "\t_cgo_errno = errno;\n")
+ }
+ fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n")
+ if n.FuncType.Result != nil {
+ // The cgo call may have caused a stack copy (via a callback).
+ // Adjust the return value pointer appropriately.
+ fmt.Fprintf(fgcc, "\t_cgo_a = (void*)((char*)_cgo_a + (_cgo_topofstack() - _cgo_stktop));\n")
+ // Save the return value.
+ fmt.Fprintf(fgcc, "\t_cgo_a->r = _cgo_r;\n")
+ // The return value is on the Go stack. If we are using msan,
+ // and if the C value is partially or completely uninitialized,
+ // the assignment will mark the Go stack as uninitialized.
+ // The Go compiler does not update msan for changes to the
+ // stack. It is possible that the stack will remain
+ // uninitialized, and then later be used in a way that is
+ // visible to msan, possibly leading to a false positive.
+ // Mark the stack space as written, to avoid this problem.
+ // See issue 26209.
+ fmt.Fprintf(fgcc, "\t_cgo_msan_write(&_cgo_a->r, sizeof(_cgo_a->r));\n")
+ }
+ if n.AddError {
+ fmt.Fprintf(fgcc, "\treturn _cgo_errno;\n")
+ }
+ fmt.Fprintf(fgcc, "}\n")
+ fmt.Fprintf(fgcc, "\n")
+}
+
+// Write out a wrapper for a function when using gccgo. This is a
+// simple wrapper that just calls the real function. We only need a
+// wrapper to support static functions in the prologue--without a
+// wrapper, we can't refer to the function, since the reference is in
+// a different file.
+func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
+ fmt.Fprintf(fgcc, "CGO_NO_SANITIZE_THREAD\n")
+ if t := n.FuncType.Result; t != nil {
+ fmt.Fprintf(fgcc, "%s\n", t.C.String())
+ } else {
+ fmt.Fprintf(fgcc, "void\n")
+ }
+ fmt.Fprintf(fgcc, "_cgo%s%s(", cPrefix, n.Mangle)
+ for i, t := range n.FuncType.Params {
+ if i > 0 {
+ fmt.Fprintf(fgcc, ", ")
+ }
+ c := t.Typedef
+ if c == "" {
+ c = t.C.String()
+ }
+ fmt.Fprintf(fgcc, "%s p%d", c, i)
+ }
+ fmt.Fprintf(fgcc, ")\n")
+ fmt.Fprintf(fgcc, "{\n")
+ if t := n.FuncType.Result; t != nil {
+ fmt.Fprintf(fgcc, "\t%s _cgo_r;\n", t.C.String())
+ }
+ fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
+ fmt.Fprintf(fgcc, "\t")
+ if t := n.FuncType.Result; t != nil {
+ fmt.Fprintf(fgcc, "_cgo_r = ")
+ // Cast to void* to avoid warnings due to omitted qualifiers.
+ if c := t.C.String(); c[len(c)-1] == '*' {
+ fmt.Fprintf(fgcc, "(void*)")
+ }
+ }
+ if n.Kind == "macro" {
+ fmt.Fprintf(fgcc, "%s;\n", n.C)
+ } else {
+ fmt.Fprintf(fgcc, "%s(", n.C)
+ for i := range n.FuncType.Params {
+ if i > 0 {
+ fmt.Fprintf(fgcc, ", ")
+ }
+ fmt.Fprintf(fgcc, "p%d", i)
+ }
+ fmt.Fprintf(fgcc, ");\n")
+ }
+ fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n")
+ if t := n.FuncType.Result; t != nil {
+ fmt.Fprintf(fgcc, "\treturn ")
+ // Cast to void* to avoid warnings due to omitted qualifiers
+ // and explicit incompatible struct types.
+ if c := t.C.String(); c[len(c)-1] == '*' {
+ fmt.Fprintf(fgcc, "(void*)")
+ }
+ fmt.Fprintf(fgcc, "_cgo_r;\n")
+ }
+ fmt.Fprintf(fgcc, "}\n")
+ fmt.Fprintf(fgcc, "\n")
+}
+
+// packedAttribute returns host compiler struct attribute that will be
+// used to match gc's struct layout. For example, on 386 Windows,
+// gcc wants to 8-align int64s, but gc does not.
+// Use __gcc_struct__ to work around https://gcc.gnu.org/PR52991 on x86,
+// and https://golang.org/issue/5603.
+func (p *Package) packedAttribute() string {
+ s := "__attribute__((__packed__"
+ if !p.GccIsClang && (goarch == "amd64" || goarch == "386") {
+ s += ", __gcc_struct__"
+ }
+ return s + "))"
+}
+
+// exportParamName returns the value of param as it should be
+// displayed in a c header file. If param contains any non-ASCII
+// characters, this function will return the character p followed by
+// the value of position; otherwise, this function will return the
+// value of param.
+func exportParamName(param string, position int) string {
+ if param == "" {
+ return fmt.Sprintf("p%d", position)
+ }
+
+ pname := param
+
+ for i := 0; i < len(param); i++ {
+ if param[i] > unicode.MaxASCII {
+ pname = fmt.Sprintf("p%d", position)
+ break
+ }
+ }
+
+ return pname
+}
+
+// Write out the various stubs we need to support functions exported
+// from Go so that they are callable from C.
+func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
+ p.writeExportHeader(fgcch)
+
+ fmt.Fprintf(fgcc, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n")
+ fmt.Fprintf(fgcc, "#include <stdlib.h>\n")
+ fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n\n")
+
+ // We use packed structs, but they are always aligned.
+ // The pragmas and address-of-packed-member are only recognized as
+ // warning groups in clang 4.0+, so ignore unknown pragmas first.
+ fmt.Fprintf(fgcc, "#pragma GCC diagnostic ignored \"-Wunknown-pragmas\"\n")
+ fmt.Fprintf(fgcc, "#pragma GCC diagnostic ignored \"-Wpragmas\"\n")
+ fmt.Fprintf(fgcc, "#pragma GCC diagnostic ignored \"-Waddress-of-packed-member\"\n")
+
+ fmt.Fprintf(fgcc, "extern void crosscall2(void (*fn)(void *), void *, int, size_t);\n")
+ fmt.Fprintf(fgcc, "extern size_t _cgo_wait_runtime_init_done(void);\n")
+ fmt.Fprintf(fgcc, "extern void _cgo_release_context(size_t);\n\n")
+ fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);")
+ fmt.Fprintf(fgcc, "%s\n", tsanProlog)
+ fmt.Fprintf(fgcc, "%s\n", msanProlog)
+
+ for _, exp := range p.ExpFunc {
+ fn := exp.Func
+
+ // Construct a struct that will be used to communicate
+ // arguments from C to Go. The C and Go definitions
+ // just have to agree. The gcc struct will be compiled
+ // with __attribute__((packed)) so all padding must be
+ // accounted for explicitly.
+ ctype := "struct {\n"
+ gotype := new(bytes.Buffer)
+ fmt.Fprintf(gotype, "struct {\n")
+ off := int64(0)
+ npad := 0
+ argField := func(typ ast.Expr, namePat string, args ...interface{}) {
+ name := fmt.Sprintf(namePat, args...)
+ t := p.cgoType(typ)
+ if off%t.Align != 0 {
+ pad := t.Align - off%t.Align
+ ctype += fmt.Sprintf("\t\tchar __pad%d[%d];\n", npad, pad)
+ off += pad
+ npad++
+ }
+ ctype += fmt.Sprintf("\t\t%s %s;\n", t.C, name)
+ fmt.Fprintf(gotype, "\t\t%s ", name)
+ noSourceConf.Fprint(gotype, fset, typ)
+ fmt.Fprintf(gotype, "\n")
+ off += t.Size
+ }
+ if fn.Recv != nil {
+ argField(fn.Recv.List[0].Type, "recv")
+ }
+ fntype := fn.Type
+ forFieldList(fntype.Params,
+ func(i int, aname string, atype ast.Expr) {
+ argField(atype, "p%d", i)
+ })
+ forFieldList(fntype.Results,
+ func(i int, aname string, atype ast.Expr) {
+ argField(atype, "r%d", i)
+ })
+ if ctype == "struct {\n" {
+ ctype += "\t\tchar unused;\n" // avoid empty struct
+ }
+ ctype += "\t}"
+ fmt.Fprintf(gotype, "\t}")
+
+ // Get the return type of the wrapper function
+ // compiled by gcc.
+ gccResult := ""
+ if fntype.Results == nil || len(fntype.Results.List) == 0 {
+ gccResult = "void"
+ } else if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 {
+ gccResult = p.cgoType(fntype.Results.List[0].Type).C.String()
+ } else {
+ fmt.Fprintf(fgcch, "\n/* Return type for %s */\n", exp.ExpName)
+ fmt.Fprintf(fgcch, "struct %s_return {\n", exp.ExpName)
+ forFieldList(fntype.Results,
+ func(i int, aname string, atype ast.Expr) {
+ fmt.Fprintf(fgcch, "\t%s r%d;", p.cgoType(atype).C, i)
+ if len(aname) > 0 {
+ fmt.Fprintf(fgcch, " /* %s */", aname)
+ }
+ fmt.Fprint(fgcch, "\n")
+ })
+ fmt.Fprintf(fgcch, "};\n")
+ gccResult = "struct " + exp.ExpName + "_return"
+ }
+
+ // Build the wrapper function compiled by gcc.
+ gccExport := ""
+ if goos == "windows" {
+ gccExport = "__declspec(dllexport) "
+ }
+ s := fmt.Sprintf("%s%s %s(", gccExport, gccResult, exp.ExpName)
+ if fn.Recv != nil {
+ s += p.cgoType(fn.Recv.List[0].Type).C.String()
+ s += " recv"
+ }
+ forFieldList(fntype.Params,
+ func(i int, aname string, atype ast.Expr) {
+ if i > 0 || fn.Recv != nil {
+ s += ", "
+ }
+ s += fmt.Sprintf("%s %s", p.cgoType(atype).C, exportParamName(aname, i))
+ })
+ s += ")"
+
+ if len(exp.Doc) > 0 {
+ fmt.Fprintf(fgcch, "\n%s", exp.Doc)
+ if !strings.HasSuffix(exp.Doc, "\n") {
+ fmt.Fprint(fgcch, "\n")
+ }
+ }
+ fmt.Fprintf(fgcch, "extern %s;\n", s)
+
+ fmt.Fprintf(fgcc, "extern void _cgoexp%s_%s(void *);\n", cPrefix, exp.ExpName)
+ fmt.Fprintf(fgcc, "\nCGO_NO_SANITIZE_THREAD")
+ fmt.Fprintf(fgcc, "\n%s\n", s)
+ fmt.Fprintf(fgcc, "{\n")
+ fmt.Fprintf(fgcc, "\tsize_t _cgo_ctxt = _cgo_wait_runtime_init_done();\n")
+ // The results part of the argument structure must be
+ // initialized to 0 so the write barriers generated by
+ // the assignments to these fields in Go are safe.
+ //
+ // We use a local static variable to get the zeroed
+ // value of the argument type. This avoids including
+ // string.h for memset, and is also robust to C++
+ // types with constructors. Both GCC and LLVM optimize
+ // this into just zeroing _cgo_a.
+ fmt.Fprintf(fgcc, "\ttypedef %s %v _cgo_argtype;\n", ctype, p.packedAttribute())
+ fmt.Fprintf(fgcc, "\tstatic _cgo_argtype _cgo_zero;\n")
+ fmt.Fprintf(fgcc, "\t_cgo_argtype _cgo_a = _cgo_zero;\n")
+ if gccResult != "void" && (len(fntype.Results.List) > 1 || len(fntype.Results.List[0].Names) > 1) {
+ fmt.Fprintf(fgcc, "\t%s r;\n", gccResult)
+ }
+ if fn.Recv != nil {
+ fmt.Fprintf(fgcc, "\t_cgo_a.recv = recv;\n")
+ }
+ forFieldList(fntype.Params,
+ func(i int, aname string, atype ast.Expr) {
+ fmt.Fprintf(fgcc, "\t_cgo_a.p%d = %s;\n", i, exportParamName(aname, i))
+ })
+ fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n")
+ fmt.Fprintf(fgcc, "\tcrosscall2(_cgoexp%s_%s, &_cgo_a, %d, _cgo_ctxt);\n", cPrefix, exp.ExpName, off)
+ fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
+ fmt.Fprintf(fgcc, "\t_cgo_release_context(_cgo_ctxt);\n")
+ if gccResult != "void" {
+ if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 {
+ fmt.Fprintf(fgcc, "\treturn _cgo_a.r0;\n")
+ } else {
+ forFieldList(fntype.Results,
+ func(i int, aname string, atype ast.Expr) {
+ fmt.Fprintf(fgcc, "\tr.r%d = _cgo_a.r%d;\n", i, i)
+ })
+ fmt.Fprintf(fgcc, "\treturn r;\n")
+ }
+ }
+ fmt.Fprintf(fgcc, "}\n")
+
+ // In internal linking mode, the Go linker sees both
+ // the C wrapper written above and the Go wrapper it
+ // references. Hence, export the C wrapper (e.g., for
+ // if we're building a shared object). The Go linker
+ // will resolve the C wrapper's reference to the Go
+ // wrapper without a separate export.
+ fmt.Fprintf(fgo2, "//go:cgo_export_dynamic %s\n", exp.ExpName)
+ // cgo_export_static refers to a symbol by its linker
+ // name, so set the linker name of the Go wrapper.
+ fmt.Fprintf(fgo2, "//go:linkname _cgoexp%s_%s _cgoexp%s_%s\n", cPrefix, exp.ExpName, cPrefix, exp.ExpName)
+ // In external linking mode, the Go linker sees the Go
+ // wrapper, but not the C wrapper. For this case,
+ // export the Go wrapper so the host linker can
+ // resolve the reference from the C wrapper to the Go
+ // wrapper.
+ fmt.Fprintf(fgo2, "//go:cgo_export_static _cgoexp%s_%s\n", cPrefix, exp.ExpName)
+
+ // Build the wrapper function compiled by cmd/compile.
+ // This unpacks the argument struct above and calls the Go function.
+ fmt.Fprintf(fgo2, "func _cgoexp%s_%s(a *%s) {\n", cPrefix, exp.ExpName, gotype)
+
+ fmt.Fprintf(fm, "void _cgoexp%s_%s(void* p){}\n", cPrefix, exp.ExpName)
+
+ fmt.Fprintf(fgo2, "\t")
+
+ if gccResult != "void" {
+ // Write results back to frame.
+ forFieldList(fntype.Results,
+ func(i int, aname string, atype ast.Expr) {
+ if i > 0 {
+ fmt.Fprintf(fgo2, ", ")
+ }
+ fmt.Fprintf(fgo2, "a.r%d", i)
+ })
+ fmt.Fprintf(fgo2, " = ")
+ }
+ if fn.Recv != nil {
+ fmt.Fprintf(fgo2, "a.recv.")
+ }
+ fmt.Fprintf(fgo2, "%s(", exp.Func.Name)
+ forFieldList(fntype.Params,
+ func(i int, aname string, atype ast.Expr) {
+ if i > 0 {
+ fmt.Fprint(fgo2, ", ")
+ }
+ fmt.Fprintf(fgo2, "a.p%d", i)
+ })
+ fmt.Fprint(fgo2, ")\n")
+ if gccResult != "void" {
+ // Verify that any results don't contain any
+ // Go pointers.
+ forFieldList(fntype.Results,
+ func(i int, aname string, atype ast.Expr) {
+ if !p.hasPointer(nil, atype, false) {
+ return
+ }
+ fmt.Fprintf(fgo2, "\t_cgoCheckResult(a.r%d)\n", i)
+ })
+ }
+ fmt.Fprint(fgo2, "}\n")
+ }
+
+ fmt.Fprintf(fgcch, "%s", gccExportHeaderEpilog)
+}
+
+// Write out the C header allowing C code to call exported gccgo functions.
+func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
+ gccgoSymbolPrefix := p.gccgoSymbolPrefix()
+
+ p.writeExportHeader(fgcch)
+
+ fmt.Fprintf(fgcc, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n")
+ fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n")
+
+ fmt.Fprintf(fgcc, "%s\n", gccgoExportFileProlog)
+ fmt.Fprintf(fgcc, "%s\n", tsanProlog)
+ fmt.Fprintf(fgcc, "%s\n", msanProlog)
+
+ for _, exp := range p.ExpFunc {
+ fn := exp.Func
+ fntype := fn.Type
+
+ cdeclBuf := new(strings.Builder)
+ resultCount := 0
+ forFieldList(fntype.Results,
+ func(i int, aname string, atype ast.Expr) { resultCount++ })
+ switch resultCount {
+ case 0:
+ fmt.Fprintf(cdeclBuf, "void")
+ case 1:
+ forFieldList(fntype.Results,
+ func(i int, aname string, atype ast.Expr) {
+ t := p.cgoType(atype)
+ fmt.Fprintf(cdeclBuf, "%s", t.C)
+ })
+ default:
+ // Declare a result struct.
+ fmt.Fprintf(fgcch, "\n/* Return type for %s */\n", exp.ExpName)
+ fmt.Fprintf(fgcch, "struct %s_return {\n", exp.ExpName)
+ forFieldList(fntype.Results,
+ func(i int, aname string, atype ast.Expr) {
+ t := p.cgoType(atype)
+ fmt.Fprintf(fgcch, "\t%s r%d;", t.C, i)
+ if len(aname) > 0 {
+ fmt.Fprintf(fgcch, " /* %s */", aname)
+ }
+ fmt.Fprint(fgcch, "\n")
+ })
+ fmt.Fprintf(fgcch, "};\n")
+ fmt.Fprintf(cdeclBuf, "struct %s_return", exp.ExpName)
+ }
+
+ cRet := cdeclBuf.String()
+
+ cdeclBuf = new(strings.Builder)
+ fmt.Fprintf(cdeclBuf, "(")
+ if fn.Recv != nil {
+ fmt.Fprintf(cdeclBuf, "%s recv", p.cgoType(fn.Recv.List[0].Type).C.String())
+ }
+ // Function parameters.
+ forFieldList(fntype.Params,
+ func(i int, aname string, atype ast.Expr) {
+ if i > 0 || fn.Recv != nil {
+ fmt.Fprintf(cdeclBuf, ", ")
+ }
+ t := p.cgoType(atype)
+ fmt.Fprintf(cdeclBuf, "%s p%d", t.C, i)
+ })
+ fmt.Fprintf(cdeclBuf, ")")
+ cParams := cdeclBuf.String()
+
+ if len(exp.Doc) > 0 {
+ fmt.Fprintf(fgcch, "\n%s", exp.Doc)
+ }
+
+ fmt.Fprintf(fgcch, "extern %s %s%s;\n", cRet, exp.ExpName, cParams)
+
+ // We need to use a name that will be exported by the
+ // Go code; otherwise gccgo will make it static and we
+ // will not be able to link against it from the C
+ // code.
+ goName := "Cgoexp_" + exp.ExpName
+ fmt.Fprintf(fgcc, `extern %s %s %s __asm__("%s.%s");`, cRet, goName, cParams, gccgoSymbolPrefix, gccgoToSymbol(goName))
+ fmt.Fprint(fgcc, "\n")
+
+ fmt.Fprint(fgcc, "\nCGO_NO_SANITIZE_THREAD\n")
+ fmt.Fprintf(fgcc, "%s %s %s {\n", cRet, exp.ExpName, cParams)
+ if resultCount > 0 {
+ fmt.Fprintf(fgcc, "\t%s r;\n", cRet)
+ }
+ fmt.Fprintf(fgcc, "\tif(_cgo_wait_runtime_init_done)\n")
+ fmt.Fprintf(fgcc, "\t\t_cgo_wait_runtime_init_done();\n")
+ fmt.Fprintf(fgcc, "\t_cgo_tsan_release();\n")
+ fmt.Fprint(fgcc, "\t")
+ if resultCount > 0 {
+ fmt.Fprint(fgcc, "r = ")
+ }
+ fmt.Fprintf(fgcc, "%s(", goName)
+ if fn.Recv != nil {
+ fmt.Fprint(fgcc, "recv")
+ }
+ forFieldList(fntype.Params,
+ func(i int, aname string, atype ast.Expr) {
+ if i > 0 || fn.Recv != nil {
+ fmt.Fprintf(fgcc, ", ")
+ }
+ fmt.Fprintf(fgcc, "p%d", i)
+ })
+ fmt.Fprint(fgcc, ");\n")
+ fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
+ if resultCount > 0 {
+ fmt.Fprint(fgcc, "\treturn r;\n")
+ }
+ fmt.Fprint(fgcc, "}\n")
+
+ // Dummy declaration for _cgo_main.c
+ fmt.Fprintf(fm, `char %s[1] __asm__("%s.%s");`, goName, gccgoSymbolPrefix, gccgoToSymbol(goName))
+ fmt.Fprint(fm, "\n")
+
+ // For gccgo we use a wrapper function in Go, in order
+ // to call CgocallBack and CgocallBackDone.
+
+ // This code uses printer.Fprint, not conf.Fprint,
+ // because we don't want //line comments in the middle
+ // of the function types.
+ fmt.Fprint(fgo2, "\n")
+ fmt.Fprintf(fgo2, "func %s(", goName)
+ if fn.Recv != nil {
+ fmt.Fprint(fgo2, "recv ")
+ printer.Fprint(fgo2, fset, fn.Recv.List[0].Type)
+ }
+ forFieldList(fntype.Params,
+ func(i int, aname string, atype ast.Expr) {
+ if i > 0 || fn.Recv != nil {
+ fmt.Fprintf(fgo2, ", ")
+ }
+ fmt.Fprintf(fgo2, "p%d ", i)
+ printer.Fprint(fgo2, fset, atype)
+ })
+ fmt.Fprintf(fgo2, ")")
+ if resultCount > 0 {
+ fmt.Fprintf(fgo2, " (")
+ forFieldList(fntype.Results,
+ func(i int, aname string, atype ast.Expr) {
+ if i > 0 {
+ fmt.Fprint(fgo2, ", ")
+ }
+ printer.Fprint(fgo2, fset, atype)
+ })
+ fmt.Fprint(fgo2, ")")
+ }
+ fmt.Fprint(fgo2, " {\n")
+ fmt.Fprint(fgo2, "\tsyscall.CgocallBack()\n")
+ fmt.Fprint(fgo2, "\tdefer syscall.CgocallBackDone()\n")
+ fmt.Fprint(fgo2, "\t")
+ if resultCount > 0 {
+ fmt.Fprint(fgo2, "return ")
+ }
+ if fn.Recv != nil {
+ fmt.Fprint(fgo2, "recv.")
+ }
+ fmt.Fprintf(fgo2, "%s(", exp.Func.Name)
+ forFieldList(fntype.Params,
+ func(i int, aname string, atype ast.Expr) {
+ if i > 0 {
+ fmt.Fprint(fgo2, ", ")
+ }
+ fmt.Fprintf(fgo2, "p%d", i)
+ })
+ fmt.Fprint(fgo2, ")\n")
+ fmt.Fprint(fgo2, "}\n")
+ }
+
+ fmt.Fprintf(fgcch, "%s", gccExportHeaderEpilog)
+}
+
+// writeExportHeader writes out the start of the _cgo_export.h file.
+func (p *Package) writeExportHeader(fgcch io.Writer) {
+ fmt.Fprintf(fgcch, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n")
+ pkg := *importPath
+ if pkg == "" {
+ pkg = p.PackagePath
+ }
+ fmt.Fprintf(fgcch, "/* package %s */\n\n", pkg)
+ fmt.Fprintf(fgcch, "%s\n", builtinExportProlog)
+
+ // Remove absolute paths from #line comments in the preamble.
+ // They aren't useful for people using the header file,
+ // and they mean that the header files change based on the
+ // exact location of GOPATH.
+ re := regexp.MustCompile(`(?m)^(#line\s+\d+\s+")[^"]*[/\\]([^"]*")`)
+ preamble := re.ReplaceAllString(p.Preamble, "$1$2")
+
+ fmt.Fprintf(fgcch, "/* Start of preamble from import \"C\" comments. */\n\n")
+ fmt.Fprintf(fgcch, "%s\n", preamble)
+ fmt.Fprintf(fgcch, "\n/* End of preamble from import \"C\" comments. */\n\n")
+
+ fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog())
+}
+
+// gccgoToSymbol converts a name to a mangled symbol for gccgo.
+func gccgoToSymbol(ppath string) string {
+ if gccgoMangler == nil {
+ var err error
+ cmd := os.Getenv("GCCGO")
+ if cmd == "" {
+ cmd, err = exec.LookPath("gccgo")
+ if err != nil {
+ fatalf("unable to locate gccgo: %v", err)
+ }
+ }
+ gccgoMangler, err = pkgpath.ToSymbolFunc(cmd, *objDir)
+ if err != nil {
+ fatalf("%v", err)
+ }
+ }
+ return gccgoMangler(ppath)
+}
+
+// Return the package prefix when using gccgo.
+func (p *Package) gccgoSymbolPrefix() string {
+ if !*gccgo {
+ return ""
+ }
+
+ if *gccgopkgpath != "" {
+ return gccgoToSymbol(*gccgopkgpath)
+ }
+ if *gccgoprefix == "" && p.PackageName == "main" {
+ return "main"
+ }
+ prefix := gccgoToSymbol(*gccgoprefix)
+ if prefix == "" {
+ prefix = "go"
+ }
+ return prefix + "." + p.PackageName
+}
+
+// Call a function for each entry in an ast.FieldList, passing the
+// index into the list, the name if any, and the type.
+func forFieldList(fl *ast.FieldList, fn func(int, string, ast.Expr)) {
+ if fl == nil {
+ return
+ }
+ i := 0
+ for _, r := range fl.List {
+ if r.Names == nil {
+ fn(i, "", r.Type)
+ i++
+ } else {
+ for _, n := range r.Names {
+ fn(i, n.Name, r.Type)
+ i++
+ }
+ }
+ }
+}
+
+func c(repr string, args ...interface{}) *TypeRepr {
+ return &TypeRepr{repr, args}
+}
+
+// Map predeclared Go types to Type.
+var goTypes = map[string]*Type{
+ "bool": {Size: 1, Align: 1, C: c("GoUint8")},
+ "byte": {Size: 1, Align: 1, C: c("GoUint8")},
+ "int": {Size: 0, Align: 0, C: c("GoInt")},
+ "uint": {Size: 0, Align: 0, C: c("GoUint")},
+ "rune": {Size: 4, Align: 4, C: c("GoInt32")},
+ "int8": {Size: 1, Align: 1, C: c("GoInt8")},
+ "uint8": {Size: 1, Align: 1, C: c("GoUint8")},
+ "int16": {Size: 2, Align: 2, C: c("GoInt16")},
+ "uint16": {Size: 2, Align: 2, C: c("GoUint16")},
+ "int32": {Size: 4, Align: 4, C: c("GoInt32")},
+ "uint32": {Size: 4, Align: 4, C: c("GoUint32")},
+ "int64": {Size: 8, Align: 8, C: c("GoInt64")},
+ "uint64": {Size: 8, Align: 8, C: c("GoUint64")},
+ "float32": {Size: 4, Align: 4, C: c("GoFloat32")},
+ "float64": {Size: 8, Align: 8, C: c("GoFloat64")},
+ "complex64": {Size: 8, Align: 4, C: c("GoComplex64")},
+ "complex128": {Size: 16, Align: 8, C: c("GoComplex128")},
+}
+
+// Map an ast type to a Type.
+func (p *Package) cgoType(e ast.Expr) *Type {
+ switch t := e.(type) {
+ case *ast.StarExpr:
+ x := p.cgoType(t.X)
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("%s*", x.C)}
+ case *ast.ArrayType:
+ if t.Len == nil {
+ // Slice: pointer, len, cap.
+ return &Type{Size: p.PtrSize * 3, Align: p.PtrSize, C: c("GoSlice")}
+ }
+ // Non-slice array types are not supported.
+ case *ast.StructType:
+ // Not supported.
+ case *ast.FuncType:
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")}
+ case *ast.InterfaceType:
+ return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")}
+ case *ast.MapType:
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoMap")}
+ case *ast.ChanType:
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoChan")}
+ case *ast.Ident:
+ goTypesFixup := func(r *Type) *Type {
+ if r.Size == 0 { // int or uint
+ rr := new(Type)
+ *rr = *r
+ rr.Size = p.IntSize
+ rr.Align = p.IntSize
+ r = rr
+ }
+ if r.Align > p.PtrSize {
+ r.Align = p.PtrSize
+ }
+ return r
+ }
+ // Look up the type in the top level declarations.
+ // TODO: Handle types defined within a function.
+ for _, d := range p.Decl {
+ gd, ok := d.(*ast.GenDecl)
+ if !ok || gd.Tok != token.TYPE {
+ continue
+ }
+ for _, spec := range gd.Specs {
+ ts, ok := spec.(*ast.TypeSpec)
+ if !ok {
+ continue
+ }
+ if ts.Name.Name == t.Name {
+ return p.cgoType(ts.Type)
+ }
+ }
+ }
+ if def := typedef[t.Name]; def != nil {
+ if defgo, ok := def.Go.(*ast.Ident); ok {
+ switch defgo.Name {
+ case "complex64", "complex128":
+ // MSVC does not support the _Complex keyword
+ // nor the complex macro.
+ // Use GoComplex64 and GoComplex128 instead,
+ // which are typedef-ed to a compatible type.
+ // See go.dev/issues/36233.
+ return goTypesFixup(goTypes[defgo.Name])
+ }
+ }
+ return def
+ }
+ if t.Name == "uintptr" {
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoUintptr")}
+ }
+ if t.Name == "string" {
+ // The string data is 1 pointer + 1 (pointer-sized) int.
+ return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoString")}
+ }
+ if t.Name == "error" {
+ return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")}
+ }
+ if r, ok := goTypes[t.Name]; ok {
+ return goTypesFixup(r)
+ }
+ error_(e.Pos(), "unrecognized Go type %s", t.Name)
+ return &Type{Size: 4, Align: 4, C: c("int")}
+ case *ast.SelectorExpr:
+ id, ok := t.X.(*ast.Ident)
+ if ok && id.Name == "unsafe" && t.Sel.Name == "Pointer" {
+ return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")}
+ }
+ }
+ error_(e.Pos(), "Go type not supported in export: %s", gofmt(e))
+ return &Type{Size: 4, Align: 4, C: c("int")}
+}
+
+const gccProlog = `
+#line 1 "cgo-gcc-prolog"
+/*
+ If x and y are not equal, the type will be invalid
+ (have a negative array count) and an inscrutable error will come
+ out of the compiler and hopefully mention "name".
+*/
+#define __cgo_compile_assert_eq(x, y, name) typedef char name[(x-y)*(x-y)*-2UL+1UL];
+
+/* Check at compile time that the sizes we use match our expectations. */
+#define __cgo_size_assert(t, n) __cgo_compile_assert_eq(sizeof(t), (size_t)n, _cgo_sizeof_##t##_is_not_##n)
+
+__cgo_size_assert(char, 1)
+__cgo_size_assert(short, 2)
+__cgo_size_assert(int, 4)
+typedef long long __cgo_long_long;
+__cgo_size_assert(__cgo_long_long, 8)
+__cgo_size_assert(float, 4)
+__cgo_size_assert(double, 8)
+
+extern char* _cgo_topofstack(void);
+
+/*
+ We use packed structs, but they are always aligned.
+ The pragmas and address-of-packed-member are only recognized as warning
+ groups in clang 4.0+, so ignore unknown pragmas first.
+*/
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Wpragmas"
+#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
+
+#include <errno.h>
+#include <string.h>
+`
+
+// Prologue defining TSAN functions in C.
+const noTsanProlog = `
+#define CGO_NO_SANITIZE_THREAD
+#define _cgo_tsan_acquire()
+#define _cgo_tsan_release()
+`
+
+// This must match the TSAN code in runtime/cgo/libcgo.h.
+// This is used when the code is built with the C/C++ Thread SANitizer,
+// which is not the same as the Go race detector.
+// __tsan_acquire tells TSAN that we are acquiring a lock on a variable,
+// in this case _cgo_sync. __tsan_release releases the lock.
+// (There is no actual lock, we are just telling TSAN that there is.)
+//
+// When we call from Go to C we call _cgo_tsan_acquire.
+// When the C function returns we call _cgo_tsan_release.
+// Similarly, when C calls back into Go we call _cgo_tsan_release
+// and then call _cgo_tsan_acquire when we return to C.
+// These calls tell TSAN that there is a serialization point at the C call.
+//
+// This is necessary because TSAN, which is a C/C++ tool, can not see
+// the synchronization in the Go code. Without these calls, when
+// multiple goroutines call into C code, TSAN does not understand
+// that the calls are properly synchronized on the Go side.
+//
+// To be clear, if the calls are not properly synchronized on the Go side,
+// we will be hiding races. But when using TSAN on mixed Go C/C++ code
+// it is more important to avoid false positives, which reduce confidence
+// in the tool, than to avoid false negatives.
+const yesTsanProlog = `
+#line 1 "cgo-tsan-prolog"
+#define CGO_NO_SANITIZE_THREAD __attribute__ ((no_sanitize_thread))
+
+long long _cgo_sync __attribute__ ((common));
+
+extern void __tsan_acquire(void*);
+extern void __tsan_release(void*);
+
+__attribute__ ((unused))
+static void _cgo_tsan_acquire() {
+ __tsan_acquire(&_cgo_sync);
+}
+
+__attribute__ ((unused))
+static void _cgo_tsan_release() {
+ __tsan_release(&_cgo_sync);
+}
+`
+
+// Set to yesTsanProlog if we see -fsanitize=thread in the flags for gcc.
+var tsanProlog = noTsanProlog
+
+// noMsanProlog is a prologue defining an MSAN function in C.
+// This is used when not compiling with -fsanitize=memory.
+const noMsanProlog = `
+#define _cgo_msan_write(addr, sz)
+`
+
+// yesMsanProlog is a prologue defining an MSAN function in C.
+// This is used when compiling with -fsanitize=memory.
+// See the comment above where _cgo_msan_write is called.
+const yesMsanProlog = `
+extern void __msan_unpoison(const volatile void *, size_t);
+
+#define _cgo_msan_write(addr, sz) __msan_unpoison((addr), (sz))
+`
+
+// msanProlog is set to yesMsanProlog if we see -fsanitize=memory in the flags
+// for the C compiler.
+var msanProlog = noMsanProlog
+
+const builtinProlog = `
+#line 1 "cgo-builtin-prolog"
+#include <stddef.h>
+
+/* Define intgo when compiling with GCC. */
+typedef ptrdiff_t intgo;
+
+#define GO_CGO_GOSTRING_TYPEDEF
+typedef struct { const char *p; intgo n; } _GoString_;
+typedef struct { char *p; intgo n; intgo c; } _GoBytes_;
+_GoString_ GoString(char *p);
+_GoString_ GoStringN(char *p, int l);
+_GoBytes_ GoBytes(void *p, int n);
+char *CString(_GoString_);
+void *CBytes(_GoBytes_);
+void *_CMalloc(size_t);
+
+__attribute__ ((unused))
+static size_t _GoStringLen(_GoString_ s) { return (size_t)s.n; }
+
+__attribute__ ((unused))
+static const char *_GoStringPtr(_GoString_ s) { return s.p; }
+`
+
+const goProlog = `
+//go:linkname _cgo_runtime_cgocall runtime.cgocall
+func _cgo_runtime_cgocall(unsafe.Pointer, uintptr) int32
+
+//go:linkname _cgoCheckPointer runtime.cgoCheckPointer
+func _cgoCheckPointer(interface{}, interface{})
+
+//go:linkname _cgoCheckResult runtime.cgoCheckResult
+func _cgoCheckResult(interface{})
+`
+
+const gccgoGoProlog = `
+func _cgoCheckPointer(interface{}, interface{})
+
+func _cgoCheckResult(interface{})
+`
+
+const goStringDef = `
+//go:linkname _cgo_runtime_gostring runtime.gostring
+func _cgo_runtime_gostring(*_Ctype_char) string
+
+// GoString converts the C string p into a Go string.
+func _Cfunc_GoString(p *_Ctype_char) string {
+ return _cgo_runtime_gostring(p)
+}
+`
+
+const goStringNDef = `
+//go:linkname _cgo_runtime_gostringn runtime.gostringn
+func _cgo_runtime_gostringn(*_Ctype_char, int) string
+
+// GoStringN converts the C data p with explicit length l to a Go string.
+func _Cfunc_GoStringN(p *_Ctype_char, l _Ctype_int) string {
+ return _cgo_runtime_gostringn(p, int(l))
+}
+`
+
+const goBytesDef = `
+//go:linkname _cgo_runtime_gobytes runtime.gobytes
+func _cgo_runtime_gobytes(unsafe.Pointer, int) []byte
+
+// GoBytes converts the C data p with explicit length l to a Go []byte.
+func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte {
+ return _cgo_runtime_gobytes(p, int(l))
+}
+`
+
+const cStringDef = `
+// CString converts the Go string s to a C string.
+//
+// The C string is allocated in the C heap using malloc.
+// It is the caller's responsibility to arrange for it to be
+// freed, such as by calling C.free (be sure to include stdlib.h
+// if C.free is needed).
+func _Cfunc_CString(s string) *_Ctype_char {
+ if len(s)+1 <= 0 {
+ panic("string too large")
+ }
+ p := _cgo_cmalloc(uint64(len(s)+1))
+ sliceHeader := struct {
+ p unsafe.Pointer
+ len int
+ cap int
+ }{p, len(s)+1, len(s)+1}
+ b := *(*[]byte)(unsafe.Pointer(&sliceHeader))
+ copy(b, s)
+ b[len(s)] = 0
+ return (*_Ctype_char)(p)
+}
+`
+
+const cBytesDef = `
+// CBytes converts the Go []byte slice b to a C array.
+//
+// The C array is allocated in the C heap using malloc.
+// It is the caller's responsibility to arrange for it to be
+// freed, such as by calling C.free (be sure to include stdlib.h
+// if C.free is needed).
+func _Cfunc_CBytes(b []byte) unsafe.Pointer {
+ p := _cgo_cmalloc(uint64(len(b)))
+ sliceHeader := struct {
+ p unsafe.Pointer
+ len int
+ cap int
+ }{p, len(b), len(b)}
+ s := *(*[]byte)(unsafe.Pointer(&sliceHeader))
+ copy(s, b)
+ return p
+}
+`
+
+const cMallocDef = `
+func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer {
+ return _cgo_cmalloc(uint64(n))
+}
+`
+
+var builtinDefs = map[string]string{
+ "GoString": goStringDef,
+ "GoStringN": goStringNDef,
+ "GoBytes": goBytesDef,
+ "CString": cStringDef,
+ "CBytes": cBytesDef,
+ "_CMalloc": cMallocDef,
+}
+
+// Definitions for C.malloc in Go and in C. We define it ourselves
+// since we call it from functions we define, such as C.CString.
+// Also, we have historically ensured that C.malloc does not return
+// nil even for an allocation of 0.
+
+const cMallocDefGo = `
+//go:cgo_import_static _cgoPREFIX_Cfunc__Cmalloc
+//go:linkname __cgofn__cgoPREFIX_Cfunc__Cmalloc _cgoPREFIX_Cfunc__Cmalloc
+var __cgofn__cgoPREFIX_Cfunc__Cmalloc byte
+var _cgoPREFIX_Cfunc__Cmalloc = unsafe.Pointer(&__cgofn__cgoPREFIX_Cfunc__Cmalloc)
+
+//go:linkname runtime_throw runtime.throw
+func runtime_throw(string)
+
+//go:cgo_unsafe_args
+func _cgo_cmalloc(p0 uint64) (r1 unsafe.Pointer) {
+ _cgo_runtime_cgocall(_cgoPREFIX_Cfunc__Cmalloc, uintptr(unsafe.Pointer(&p0)))
+ if r1 == nil {
+ runtime_throw("runtime: C malloc failed")
+ }
+ return
+}
+`
+
+// cMallocDefC defines the C version of C.malloc for the gc compiler.
+// It is defined here because C.CString and friends need a definition.
+// We define it by hand, rather than simply inventing a reference to
+// C.malloc, because <stdlib.h> may not have been included.
+// This is approximately what writeOutputFunc would generate, but
+// skips the cgo_topofstack code (which is only needed if the C code
+// calls back into Go). This also avoids returning nil for an
+// allocation of 0 bytes.
+const cMallocDefC = `
+CGO_NO_SANITIZE_THREAD
+void _cgoPREFIX_Cfunc__Cmalloc(void *v) {
+ struct {
+ unsigned long long p0;
+ void *r1;
+ } PACKED *a = v;
+ void *ret;
+ _cgo_tsan_acquire();
+ ret = malloc(a->p0);
+ if (ret == 0 && a->p0 == 0) {
+ ret = malloc(1);
+ }
+ a->r1 = ret;
+ _cgo_tsan_release();
+}
+`
+
+func (p *Package) cPrologGccgo() string {
+ r := strings.NewReplacer(
+ "PREFIX", cPrefix,
+ "GCCGOSYMBOLPREF", p.gccgoSymbolPrefix(),
+ "_cgoCheckPointer", gccgoToSymbol("_cgoCheckPointer"),
+ "_cgoCheckResult", gccgoToSymbol("_cgoCheckResult"))
+ return r.Replace(cPrologGccgo)
+}
+
+const cPrologGccgo = `
+#line 1 "cgo-c-prolog-gccgo"
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef unsigned char byte;
+typedef intptr_t intgo;
+
+struct __go_string {
+ const unsigned char *__data;
+ intgo __length;
+};
+
+typedef struct __go_open_array {
+ void* __values;
+ intgo __count;
+ intgo __capacity;
+} Slice;
+
+struct __go_string __go_byte_array_to_string(const void* p, intgo len);
+struct __go_open_array __go_string_to_byte_array (struct __go_string str);
+
+extern void runtime_throw(const char *);
+
+const char *_cgoPREFIX_Cfunc_CString(struct __go_string s) {
+ char *p = malloc(s.__length+1);
+ if(p == NULL)
+ runtime_throw("runtime: C malloc failed");
+ memmove(p, s.__data, s.__length);
+ p[s.__length] = 0;
+ return p;
+}
+
+void *_cgoPREFIX_Cfunc_CBytes(struct __go_open_array b) {
+ char *p = malloc(b.__count);
+ if(p == NULL)
+ runtime_throw("runtime: C malloc failed");
+ memmove(p, b.__values, b.__count);
+ return p;
+}
+
+struct __go_string _cgoPREFIX_Cfunc_GoString(char *p) {
+ intgo len = (p != NULL) ? strlen(p) : 0;
+ return __go_byte_array_to_string(p, len);
+}
+
+struct __go_string _cgoPREFIX_Cfunc_GoStringN(char *p, int32_t n) {
+ return __go_byte_array_to_string(p, n);
+}
+
+Slice _cgoPREFIX_Cfunc_GoBytes(char *p, int32_t n) {
+ struct __go_string s = { (const unsigned char *)p, n };
+ return __go_string_to_byte_array(s);
+}
+
+void *_cgoPREFIX_Cfunc__CMalloc(size_t n) {
+ void *p = malloc(n);
+ if(p == NULL && n == 0)
+ p = malloc(1);
+ if(p == NULL)
+ runtime_throw("runtime: C malloc failed");
+ return p;
+}
+
+struct __go_type_descriptor;
+typedef struct __go_empty_interface {
+ const struct __go_type_descriptor *__type_descriptor;
+ void *__object;
+} Eface;
+
+extern void runtimeCgoCheckPointer(Eface, Eface)
+ __asm__("runtime.cgoCheckPointer")
+ __attribute__((weak));
+
+extern void localCgoCheckPointer(Eface, Eface)
+ __asm__("GCCGOSYMBOLPREF._cgoCheckPointer");
+
+void localCgoCheckPointer(Eface ptr, Eface arg) {
+ if(runtimeCgoCheckPointer) {
+ runtimeCgoCheckPointer(ptr, arg);
+ }
+}
+
+extern void runtimeCgoCheckResult(Eface)
+ __asm__("runtime.cgoCheckResult")
+ __attribute__((weak));
+
+extern void localCgoCheckResult(Eface)
+ __asm__("GCCGOSYMBOLPREF._cgoCheckResult");
+
+void localCgoCheckResult(Eface val) {
+ if(runtimeCgoCheckResult) {
+ runtimeCgoCheckResult(val);
+ }
+}
+`
+
+// builtinExportProlog is a shorter version of builtinProlog,
+// to be put into the _cgo_export.h file.
+// For historical reasons we can't use builtinProlog in _cgo_export.h,
+// because _cgo_export.h defines GoString as a struct while builtinProlog
+// defines it as a function. We don't change this to avoid unnecessarily
+// breaking existing code.
+// The test of GO_CGO_GOSTRING_TYPEDEF avoids a duplicate definition
+// error if a Go file with a cgo comment #include's the export header
+// generated by a different package.
+const builtinExportProlog = `
+#line 1 "cgo-builtin-export-prolog"
+
+#include <stddef.h>
+
+#ifndef GO_CGO_EXPORT_PROLOGUE_H
+#define GO_CGO_EXPORT_PROLOGUE_H
+
+#ifndef GO_CGO_GOSTRING_TYPEDEF
+typedef struct { const char *p; ptrdiff_t n; } _GoString_;
+#endif
+
+#endif
+`
+
+func (p *Package) gccExportHeaderProlog() string {
+ return strings.Replace(gccExportHeaderProlog, "GOINTBITS", fmt.Sprint(8*p.IntSize), -1)
+}
+
+// gccExportHeaderProlog is written to the exported header, after the
+// import "C" comment preamble but before the generated declarations
+// of exported functions. This permits the generated declarations to
+// use the type names that appear in goTypes, above.
+//
+// The test of GO_CGO_GOSTRING_TYPEDEF avoids a duplicate definition
+// error if a Go file with a cgo comment #include's the export header
+// generated by a different package. Unfortunately GoString means two
+// different things: in this prolog it means a C name for the Go type,
+// while in the prolog written into the start of the C code generated
+// from a cgo-using Go file it means the C.GoString function. There is
+// no way to resolve this conflict, but it also doesn't make much
+// difference, as Go code never wants to refer to the latter meaning.
+const gccExportHeaderProlog = `
+/* Start of boilerplate cgo prologue. */
+#line 1 "cgo-gcc-export-header-prolog"
+
+#ifndef GO_CGO_PROLOGUE_H
+#define GO_CGO_PROLOGUE_H
+
+typedef signed char GoInt8;
+typedef unsigned char GoUint8;
+typedef short GoInt16;
+typedef unsigned short GoUint16;
+typedef int GoInt32;
+typedef unsigned int GoUint32;
+typedef long long GoInt64;
+typedef unsigned long long GoUint64;
+typedef GoIntGOINTBITS GoInt;
+typedef GoUintGOINTBITS GoUint;
+typedef size_t GoUintptr;
+typedef float GoFloat32;
+typedef double GoFloat64;
+#ifdef _MSC_VER
+#include <complex.h>
+typedef _Fcomplex GoComplex64;
+typedef _Dcomplex GoComplex128;
+#else
+typedef float _Complex GoComplex64;
+typedef double _Complex GoComplex128;
+#endif
+
+/*
+ static assertion to make sure the file is being used on architecture
+ at least with matching size of GoInt.
+*/
+typedef char _check_for_GOINTBITS_bit_pointer_matching_GoInt[sizeof(void*)==GOINTBITS/8 ? 1:-1];
+
+#ifndef GO_CGO_GOSTRING_TYPEDEF
+typedef _GoString_ GoString;
+#endif
+typedef void *GoMap;
+typedef void *GoChan;
+typedef struct { void *t; void *v; } GoInterface;
+typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
+
+#endif
+
+/* End of boilerplate cgo prologue. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+`
+
+// gccExportHeaderEpilog goes at the end of the generated header file.
+const gccExportHeaderEpilog = `
+#ifdef __cplusplus
+}
+#endif
+`
+
+// gccgoExportFileProlog is written to the _cgo_export.c file when
+// using gccgo.
+// We use weak declarations, and test the addresses, so that this code
+// works with older versions of gccgo.
+const gccgoExportFileProlog = `
+#line 1 "cgo-gccgo-export-file-prolog"
+extern _Bool runtime_iscgo __attribute__ ((weak));
+
+static void GoInit(void) __attribute__ ((constructor));
+static void GoInit(void) {
+ if(&runtime_iscgo)
+ runtime_iscgo = 1;
+}
+
+extern size_t _cgo_wait_runtime_init_done(void) __attribute__ ((weak));
+`
diff --git a/src/cmd/cgo/util.go b/src/cmd/cgo/util.go
new file mode 100644
index 0000000..054cd6c
--- /dev/null
+++ b/src/cmd/cgo/util.go
@@ -0,0 +1,114 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+ "os"
+ "os/exec"
+)
+
+// run runs the command argv, feeding in stdin on standard input.
+// It returns the output to standard output and standard error.
+// ok indicates whether the command exited successfully.
+func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) {
+ if i := find(argv, "-xc"); i >= 0 && argv[len(argv)-1] == "-" {
+ // Some compilers have trouble with standard input.
+ // Others have trouble with -xc.
+ // Avoid both problems by writing a file with a .c extension.
+ f, err := os.CreateTemp("", "cgo-gcc-input-")
+ if err != nil {
+ fatalf("%s", err)
+ }
+ name := f.Name()
+ f.Close()
+ if err := os.WriteFile(name+".c", stdin, 0666); err != nil {
+ os.Remove(name)
+ fatalf("%s", err)
+ }
+ defer os.Remove(name)
+ defer os.Remove(name + ".c")
+
+ // Build new argument list without -xc and trailing -.
+ new := append(argv[:i:i], argv[i+1:len(argv)-1]...)
+
+ // Since we are going to write the file to a temporary directory,
+ // we will need to add -I . explicitly to the command line:
+ // any #include "foo" before would have looked in the current
+ // directory as the directory "holding" standard input, but now
+ // the temporary directory holds the input.
+ // We've also run into compilers that reject "-I." but allow "-I", ".",
+ // so be sure to use two arguments.
+ // This matters mainly for people invoking cgo -godefs by hand.
+ new = append(new, "-I", ".")
+
+ // Finish argument list with path to C file.
+ new = append(new, name+".c")
+
+ argv = new
+ stdin = nil
+ }
+
+ p := exec.Command(argv[0], argv[1:]...)
+ p.Stdin = bytes.NewReader(stdin)
+ var bout, berr bytes.Buffer
+ p.Stdout = &bout
+ p.Stderr = &berr
+ // Disable escape codes in clang error messages.
+ p.Env = append(os.Environ(), "TERM=dumb")
+ err := p.Run()
+ if _, ok := err.(*exec.ExitError); err != nil && !ok {
+ fatalf("exec %s: %s", argv[0], err)
+ }
+ ok = p.ProcessState.Success()
+ stdout, stderr = bout.Bytes(), berr.Bytes()
+ return
+}
+
+func find(argv []string, target string) int {
+ for i, arg := range argv {
+ if arg == target {
+ return i
+ }
+ }
+ return -1
+}
+
+func lineno(pos token.Pos) string {
+ return fset.Position(pos).String()
+}
+
+// Die with an error message.
+func fatalf(msg string, args ...interface{}) {
+ // If we've already printed other errors, they might have
+ // caused the fatal condition. Assume they're enough.
+ if nerrors == 0 {
+ fmt.Fprintf(os.Stderr, "cgo: "+msg+"\n", args...)
+ }
+ os.Exit(2)
+}
+
+var nerrors int
+
+func error_(pos token.Pos, msg string, args ...interface{}) {
+ nerrors++
+ if pos.IsValid() {
+ fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String())
+ } else {
+ fmt.Fprintf(os.Stderr, "cgo: ")
+ }
+ fmt.Fprintf(os.Stderr, msg, args...)
+ fmt.Fprintf(os.Stderr, "\n")
+}
+
+func creat(name string) *os.File {
+ f, err := os.Create(name)
+ if err != nil {
+ fatalf("%s", err)
+ }
+ return f
+}