Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cgo: C++ static initialization not working #1486

Open
cstrahan opened this issue May 4, 2018 · 16 comments
Open

cgo: C++ static initialization not working #1486

cstrahan opened this issue May 4, 2018 · 16 comments

Comments

@cstrahan
Copy link

cstrahan commented May 4, 2018

Here's the scenario:

  • You have a go_binary which depends on a cc_library, let's call it cxxlib
  • cxxlib in turn has a dependency on a second cc_library, let's call it cxxdep
  • cxxdep does one thing: it uses static initialization for some sort of side effect (say, registering some type in a plugin system)
  • Both uses of cc_library use linkalways = 1, as we want to ensure that our symbols remain, despite the fact that some may not appear to the linker as used.

What we expect:

  • The initializer(s) should run.

What happens:

  • The initializers are not run (presumably because cxxdep wasn't actually linked.

I have a minimal example that reproduces this here: https://github.com/cstrahan/rules_go_repro

Instructions:

  1. $ bazel build :demogo
  2. $ bazel-bin/linux_amd64_stripped/demogo

In theory, you should see the following two lines, but you'll find that only the second line is printed:

Hello from cxxdep!
Hello from cxxlib!

You can compare that to what happens with cc_binary like so:

  1. $ bazel build :democc
  2. $ bazel-bin/democc

You'll see:

Hello from cxxdep!
Hello from cxxlib!
@cstrahan
Copy link
Author

cstrahan commented May 4, 2018

Note that this also applies to code like:

__attribute__((constructor))
static void init() {
    puts("Hello from init!");
}

@cstrahan
Copy link
Author

cstrahan commented May 22, 2018

Here's the build log, along with some observations paced inline:

$ bazel build -s ":demogo" --verbose_explanations
WARNING: --verbose_explanations has no effect when --explain=<file> is not enabled
INFO: Analysed target //:demogo (14 packages loaded).
INFO: Found 1 target...
SUBCOMMAND: # //:cxxlib [action 'Compiling lib.cc']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -B/usr/bin -B/usr/bin -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer '-std=c++0x' -MD -MF bazel-out/k8-fastbuild/bin/_objs/cxxlib/lib.pic.d '-frandom-seed=bazel-out/k8-fastbuild/bin/_objs/cxxlib/lib.pic.o' -fPIC -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools -fno-canonical-system-headers -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -c lib.cc -o bazel-out/k8-fastbuild/bin/_objs/cxxlib/lib.pic.o)
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:stdlib [action 'Creating runfiles tree bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib.runfiles [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PATH=/home/cstrahan/.nix-profile/bin:/home/cstrahan/.nix-profile/lib/kde4/libexec:/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default:/nix/var/nix/profiles/default/lib/kde4/libexec:/home/cstrahan/.nix-profile/bin:/home/cstrahan/.nix-profile/lib/kde4/libexec:/nix/var/nix/profiles/default/bin:/nix/var/nix/profiles/default:/nix/var/nix/profiles/default/lib/kde4/libexec:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/go/bin \
  _bin/build-runfiles bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib.runfiles_manifest bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib.runfiles)
SUBCOMMAND: # //:demogo [action 'Creating runfiles tree bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.runfiles']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  _bin/build-runfiles bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.runfiles_manifest bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.runfiles)
SUBCOMMAND: # //:cxxdep [action 'Compiling dep.cc']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -B/usr/bin -B/usr/bin -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer '-std=c++0x' -MD -MF bazel-out/k8-fastbuild/bin/_objs/cxxdep/dep.pic.d '-frandom-seed=bazel-out/k8-fastbuild/bin/_objs/cxxdep/dep.pic.o' -fPIC -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools -fno-canonical-system-headers -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -c dep.cc -o bazel-out/k8-fastbuild/bin/_objs/cxxdep/dep.pic.o)
SUBCOMMAND: # //:cxxlib [action 'Linking libcxxlib.lo']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/ar @bazel-out/k8-fastbuild/bin/libcxxlib.lo-2.params)
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:link [action 'GoCompile external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/link~/go/tools/builders/link.a [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool compile -trimpath $(pwd) -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/link~/go/tools/builders/link.a external/io_bazel_rules_go/go/tools/builders/ar.go external/io_bazel_rules_go/go/tools/builders/env.go external/io_bazel_rules_go/go/tools/builders/flags.go external/io_bazel_rules_go/go/tools/builders/link.go')
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:pack [action 'GoCompile external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/pack~/go/tools/builders/pack.a [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool compile -trimpath $(pwd) -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/pack~/go/tools/builders/pack.a external/io_bazel_rules_go/go/tools/builders/env.go external/io_bazel_rules_go/go/tools/builders/flags.go external/io_bazel_rules_go/go/tools/builders/pack.go')
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:cgo [action 'GoCompile external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/cgo~/go/tools/builders/cgo.a [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool compile -trimpath $(pwd) -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/cgo~/go/tools/builders/cgo.a external/io_bazel_rules_go/go/tools/builders/cgo.go external/io_bazel_rules_go/go/tools/builders/env.go external/io_bazel_rules_go/go/tools/builders/extract.go external/io_bazel_rules_go/go/tools/builders/filter.go external/io_bazel_rules_go/go/tools/builders/flags.go')
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:stdlib [action 'GoCompile external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib~/go/tools/builders/stdlib.a [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool compile -trimpath $(pwd) -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib~/go/tools/builders/stdlib.a external/io_bazel_rules_go/go/tools/builders/env.go external/io_bazel_rules_go/go/tools/builders/flags.go external/io_bazel_rules_go/go/tools/builders/replicate.go external/io_bazel_rules_go/go/tools/builders/stdlib.go')
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:compile [action 'GoCompile external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/compile~/go/tools/builders/compile.a [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool compile -trimpath $(pwd) -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/compile~/go/tools/builders/compile.a external/io_bazel_rules_go/go/tools/builders/compile.go external/io_bazel_rules_go/go/tools/builders/env.go external/io_bazel_rules_go/go/tools/builders/filter.go external/io_bazel_rules_go/go/tools/builders/flags.go')
SUBCOMMAND: # //:cxxdep [action 'Linking libcxxdep.lo']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/ar @bazel-out/k8-fastbuild/bin/libcxxdep.lo-2.params)
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:link [action 'GoLink external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/link [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool link -s -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/link bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/link~/go/tools/builders/link.a')
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:compile [action 'GoLink external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/compile [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool link -s -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/compile bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/compile~/go/tools/builders/compile.a')
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:stdlib [action 'GoLink external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool link -s -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib~/go/tools/builders/stdlib.a')
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:cgo [action 'GoLink external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/cgo [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool link -s -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/cgo bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/cgo~/go/tools/builders/cgo.a')
SUBCOMMAND: # @io_bazel_rules_go//go/tools/builders:pack [action 'GoLink external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/pack [for host]']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  /bin/bash -c 'export GOROOT=$(pwd)/external/go_sdk && export GOROOT_FINAL=GOROOT && external/go_sdk/bin/go tool link -s -o bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/pack bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/pack~/go/tools/builders/pack.a')
SUBCOMMAND: # @io_bazel_rules_go//:stdlib [action 'GoStdlib external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/pkg']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib -go external/go_sdk/bin/go -root_file external/go_sdk/packages.txt -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -out 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~')
SUBCOMMAND: # //:demogo.cgo_codegen [action 'CGoCodeGen //:demogo.cgo_codegen']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/cgo -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -cc /usr/bin/gcc -objdir 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~' -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo1.go=main.go' -ld_flag -Wl,--no-as-needed -ld_flag bazel-out/k8-fastbuild/bin/libcxxlib.lo -ld_flag bazel-out/k8-fastbuild/bin/libcxxdep.lo -ld_flag bazel-out/k8-fastbuild/bin/libcxxdep.lo -- -- -U_FORTIFY_SOURCE -fstack-protector -B/usr/bin -B/usr/bin -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -fno-canonical-system-headers -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -I . -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools)
SUBCOMMAND: # //:demogo.cgo_c_lib [action 'Compiling linux_amd64_stripped/demogo.cgo_codegen~/_cgo_export.c']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -B/usr/bin -B/usr/bin -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -MD -MF 'bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_export.pic.d' -fPIC -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools -I . -pthread -Wno-unused-variable -fno-canonical-system-headers -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -c 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_export.c' -o 'bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_export.pic.o')
SUBCOMMAND: # //:demogo.cgo_c_lib [action 'Compiling linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.c']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -B/usr/bin -B/usr/bin -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -MD -MF 'bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.pic.d' -fPIC -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools -I . -pthread -Wno-unused-variable -fno-canonical-system-headers -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -c 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.c' -o 'bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.pic.o')
SUBCOMMAND: # //:demogo._cgo_.o [action 'Compiling linux_amd64_stripped/demogo.cgo_codegen~/_cgo_main.c']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -B/usr/bin -B/usr/bin -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -MD -MF 'bazel-out/k8-fastbuild/bin/_objs/demogo._cgo_.o/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_main.pic.d' -fPIC -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools -I . -fno-canonical-system-headers -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -c 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_main.c' -o 'bazel-out/k8-fastbuild/bin/_objs/demogo._cgo_.o/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_main.pic.o')
SUBCOMMAND: # //:demogo.cgo_c_lib [action 'Linking libdemogo.cgo_c_lib.lo']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/ar @bazel-out/k8-fastbuild/bin/libdemogo.cgo_c_lib.lo-2.params)

Let's look at those params:

$ cat bazel-out/k8-fastbuild/bin/libdemogo.cgo_c_lib.lo-2.params
rcsD
bazel-out/k8-fastbuild/bin/libdemogo.cgo_c_lib.lo
bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_export.pic.o
bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.pic.o

And the symbols:

$ nm bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_export.pic.o | c++filt
$ nm bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.pic.o | c++filt
                 U cc_main
0000000000000036 T _cgo_4607ac09f280_Cfunc_cc_main
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 t _GoStringLen
000000000000001b t _GoStringPtr

So libdemogo.cgo_c_lib.lo is pretty minimal -- it doesn't have (among many other things) the static initializer we're expecting. What's odd, though, is that later on we'll see that this is linked in the final binary, instead of demogo._cgo_.o from the next subcommand (which itself actually does have the symbols we're expecting):

SUBCOMMAND: # //:demogo._cgo_.o [action 'Linking demogo._cgo_.o']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/gcc -o bazel-out/k8-fastbuild/bin/demogo._cgo_.o -Wl,--no-as-needed -Wl,--no-as-needed -pthread '-fuse-ld=gold' -Wl,-no-as-needed -Wl,-z,relro,-z,now -B/usr/bin -B/usr/bin -pass-exit-codes -Wl,-S -Wl,@bazel-out/k8-fastbuild/bin/demogo._cgo_.o-2.params)

Here's where things get interesting. Let's look at the param file:

$ cat bazel-out/k8-fastbuild/bin/demogo._cgo_.o-2.params
bazel-out/k8-fastbuild/bin/_objs/demogo._cgo_.o/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_main.pic.o
bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_export.pic.o
bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.pic.o
bazel-out/k8-fastbuild/bin/_objs/cxxlib/lib.pic.o
bazel-out/k8-fastbuild/bin/_objs/cxxdep/dep.pic.o
-lstdc++
-lm

So this is where we're linking cxxlib and cxxdep. To confirm that all the expected symbols are present:

$ nm bazel-out/k8-fastbuild/bin/demogo._cgo_.o | c++filt
0000000000002010 T __bss_start
0000000000000776 T cc_main
0000000000000755 T _cgo_4607ac09f280_Cfunc_cc_main
00000000000006fc T _cgo_allocate
000000000000070a T _cgo_panic
0000000000000718 T _cgo_reginit
00000000000006e6 T _cgo_release_context
00000000000006f1 T _cgo_topofstack
00000000000006db T _cgo_wait_runtime_init_done
0000000000002010 b completed.7641
00000000000006c5 T crosscall2
                 w __cxa_finalize
0000000000002000 D __data_start
0000000000002000 W data_start
00000000000005e0 t deregister_tm_clones
0000000000000670 t __do_global_dtors_aux
0000000000001d60 t __do_global_dtors_aux_fini_array_entry
0000000000002008 d __dso_handle
0000000000001d80 d _DYNAMIC
0000000000002010 T _edata
0000000000002012 T _end
0000000000000874 T _fini
00000000000006b0 t frame_dummy
0000000000001d68 t __frame_dummy_init_array_entry
0000000000000b98 r __FRAME_END__
0000000000001fd8 d _GLOBAL_OFFSET_TABLE_
00000000000007c8 t _GLOBAL__sub_I_dep.cc
                 w __gmon_start__
000000000000071f t _GoStringLen
000000000000073a t _GoStringPtr
0000000000000560 T _init
0000000000001d80 t __init_array_end
0000000000001d68 t __init_array_start
0000000000000880 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000000870 T __libc_csu_fini
0000000000000800 T __libc_csu_init
                 U __libc_start_main
00000000000006ba T main
                 U puts
0000000000000620 t register_tm_clones
00000000000005b0 T _start
0000000000002010 d __TMC_END__
0000000000002010 d __TMC_LIST__
000000000000079c t __static_initialization_and_destruction_0(int, int)
0000000000000789 t init()
0000000000002011 b _bloop
00000000000007de W Bloop::Bloop()
00000000000007de W Bloop::Bloop()

Looks good!

SUBCOMMAND: # //:demogo.cgo_import [action 'CGoImportGen linux_amd64_stripped/demogo.cgo_import~/_cgo_import.go']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/cgo -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -dynout 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_import~/_cgo_import.go' -dynimport bazel-out/k8-fastbuild/bin/demogo._cgo_.o -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo1.go')

Curiously, the subcommand above is the last time we'll see any use of demogo._cgo_.o -- it's used for generating cgo imports, but I don't see it being linked anywhere.

SUBCOMMAND: # //:demogo [action 'GoCompile linux_amd64_stripped/demogo~/demogo.a~partial.a']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/compile -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -package_list external/go_sdk/packages.txt -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo1.go' -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_gotypes.go' -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_import~/_cgo_import.go' -o 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo~/demogo.a~partial.a' -trimpath . -I . -- -p demogo)
SUBCOMMAND: # //:demogo [action 'GoPack linux_amd64_stripped/demogo~/demogo.a']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/pack -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -in 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo~/demogo.a~partial.a' -out 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo~/demogo.a' -arc bazel-out/k8-fastbuild/bin/libdemogo.cgo_c_lib.lo)
SUBCOMMAND: # //:demogo [action 'GoLink linux_amd64_stripped/demogo']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/link -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -ld_flag -U_FORTIFY_SOURCE -ld_flag -fstack-protector -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -Wunused-but-set-parameter -ld_flag -Wno-free-nonheap-object -ld_flag -fno-omit-frame-pointer -ld_flag -fno-canonical-system-headers -ld_flag -Wno-builtin-macro-redefined -ld_flag '-D__DATE__="redacted"' -ld_flag '-D__TIMESTAMP__="redacted"' -ld_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -ld_flag '-Wl,-rpath,$ORIGIN/../' -ld_flag -Wl,--no-as-needed -out bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo -- -linkmode external -w -extld /usr/bin/gcc 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo~/demogo.a')
Target //:demogo up-to-date:
  bazel-bin/linux_amd64_stripped/demogo
INFO: Elapsed time: 18.619s, Critical Path: 12.94s
INFO: Build completed successfully, 35 total actions

This concludes our spelunking.


On a hunch, I tried using the following configuration:

package(default_visibility = ["//visibility:public"])

load(
    "@io_bazel_rules_go//go:def.bzl",
    "go_library",
    "go_binary",
    "go_test",
)

cc_library(
    name = "cxxdep",
    srcs = ["dep.cc"],
    linkstatic = True,
    alwayslink = 1,
)

cc_library(
    name = "cxxlib",
    srcs = ["lib.cc"],
    hdrs = ["lib.h"],
    linkstatic = True,
    deps = [
        "cxxdep",
    ],
    alwayslink = 1,
)

cc_binary(
    name = "democc",
    srcs = ["main.cc"],
    deps = [
        "cxxlib",
    ],
)

go_binary(
    name = "demogo",
    srcs = [
        "main.go",
    ],
    cdeps = [
        ":cxxlib",
        ":cxxdep",
    ],
    cgo = True,
    gc_linkopts = [
        "-linkmode",
        "external",
        "-extldflags",
        "-Wl,bazel-out/k8-fastbuild/bin/demogo._cgo_.o" # <==== HACK!
    ],
)

And with the sandbox disabled so we can refer to bazel-out:

$ bazel build -s ":demogo" --verbose_explanations --genrule_strategy=standalone --spawn_strategy=standalone
WARNING: --verbose_explanations has no effect when --explain=<file> is not enabled
INFO: Analysed target //:demogo (1 packages loaded).
INFO: Found 1 target...
SUBCOMMAND: # //:demogo.cgo_codegen [action 'CGoCodeGen //:demogo.cgo_codegen']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/cgo -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -cc /usr/bin/gcc -objdir 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~' -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo1.go=main.go' -ld_flag bazel-out/k8-fastbuild/bin/libcxxlib.lo -ld_flag bazel-out/k8-fastbuild/bin/libcxxdep.lo -ld_flag bazel-out/k8-fastbuild/bin/libcxxdep.lo -- -- -U_FORTIFY_SOURCE -fstack-protector -B/usr/bin -B/usr/bin -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -fno-canonical-system-headers -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -I . -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools)
SUBCOMMAND: # //:demogo.cgo_c_lib [action 'Compiling linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.c']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -B/usr/bin -B/usr/bin -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -MD -MF 'bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.pic.d' -fPIC -iquote . -iquote bazel-out/k8-fastbuild/genfiles -iquote external/bazel_tools -iquote bazel-out/k8-fastbuild/genfiles/external/bazel_tools -I . -pthread -Wno-unused-variable -fno-canonical-system-headers -Wno-builtin-macro-redefined '-D__DATE__="redacted"' '-D__TIMESTAMP__="redacted"' '-D__TIME__="redacted"' -c 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.c' -o 'bazel-out/k8-fastbuild/bin/_objs/demogo.cgo_c_lib/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo2.pic.o')
SUBCOMMAND: # //:demogo._cgo_.o [action 'Linking demogo._cgo_.o']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
    PWD=/proc/self/cwd \
  /usr/bin/gcc -o bazel-out/k8-fastbuild/bin/demogo._cgo_.o -pthread '-fuse-ld=gold' -Wl,-no-as-needed -Wl,-z,relro,-z,now -B/usr/bin -B/usr/bin -pass-exit-codes -Wl,-S -Wl,@bazel-out/k8-fastbuild/bin/demogo._cgo_.o-2.params)
SUBCOMMAND: # //:demogo [action 'GoCompile linux_amd64_stripped/demogo~/demogo.a~partial.a']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/compile -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -package_list external/go_sdk/packages.txt -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo1.go' -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_gotypes.go' -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_import~/_cgo_import.go' -o 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo~/demogo.a~partial.a' -trimpath . -I . -- -p demogo)
SUBCOMMAND: # //:demogo [action 'GoPack linux_amd64_stripped/demogo~/demogo.a']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/pack -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -in 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo~/demogo.a~partial.a' -out 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo~/demogo.a' -arc bazel-out/k8-fastbuild/bin/libdemogo.cgo_c_lib.lo)
SUBCOMMAND: # //:demogo [action 'GoLink linux_amd64_stripped/demogo']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/link -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -ld_flag -U_FORTIFY_SOURCE -ld_flag -fstack-protector -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -Wunused-but-set-parameter -ld_flag -Wno-free-nonheap-object -ld_flag -fno-omit-frame-pointer -ld_flag -fno-canonical-system-headers -ld_flag -Wno-builtin-macro-redefined -ld_flag '-D__DATE__="redacted"' -ld_flag '-D__TIMESTAMP__="redacted"' -ld_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -ld_flag '-Wl,-rpath,$ORIGIN/../' -ld_flag -Wl,bazel-out/k8-fastbuild/bin/demogo._cgo_.o -out bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo -- -linkmode external -w -extld /usr/bin/gcc 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo~/demogo.a')
Target //:demogo up-to-date:
  bazel-bin/linux_amd64_stripped/demogo
INFO: Elapsed time: 0.527s, Critical Path: 0.26s
INFO: Build completed successfully, 7 total actions

We see that our binary works as expected:

$ bazel-bin/linux_amd64_stripped/demogo
Hello from initializer!
Hello from cxxdep!
Hello from cxxlib!

So what's the deal here? Should the _cgo_.o file always be linked, and this is just an oversight? Or should the cgo_c_lib.lo step be modified such that the params to ar look more like those used when linking _cgo_.o (that is, refer to lib.pic.o and dep.pic.o)?

If someone can identify and outline what needs to be done, I can whip up a pull request.

@cstrahan
Copy link
Author

I should note that the aforementioned hack doesn't work if we're building a static binary:

go_binary(
    name = "demogo",
    srcs = [
        "main.go",
    ],
    cdeps = [
        ":cxxlib",
        ":cxxdep",
    ],
    cgo = True,
    gc_linkopts = [
        "-linkmode",
        "external",
        "-extldflags",
        "-Wl,bazel-out/k8-fastbuild/bin/demogo._cgo_.o"
    ],
    static = "on", # <==== !!!
)
$ bazel build -s ":demogo" --verbose_explanations --genrule_strategy=standalone --spawn_strategy=standalone
WARNING: --verbose_explanations has no effect when --explain=<file> is not enabled
INFO: Analysed target //:demogo (1 packages loaded).
INFO: Found 1 target...
SUBCOMMAND: # //:demogo [action 'Creating runfiles tree bazel-out/k8-fastbuild/bin/linux_amd64_static_stripped/demogo.runfiles']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  _bin/build-runfiles bazel-out/k8-fastbuild/bin/linux_amd64_static_stripped/demogo.runfiles_manifest bazel-out/k8-fastbuild/bin/linux_amd64_static_stripped/demogo.runfiles)
SUBCOMMAND: # @io_bazel_rules_go//:stdlib [action 'GoStdlib external/io_bazel_rules_go/linux_amd64_static_stripped/stdlib~/pkg']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/stdlib -go external/go_sdk/bin/go -root_file external/go_sdk/packages.txt -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -out 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_static_stripped/stdlib~')
SUBCOMMAND: # //:demogo [action 'GoCompile linux_amd64_static_stripped/demogo~/demogo.a~partial.a']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/compile -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_static_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -package_list external/go_sdk/packages.txt -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/main.cgo1.go' -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_codegen~/_cgo_gotypes.go' -src 'bazel-out/k8-fastbuild/bin/linux_amd64_stripped/demogo.cgo_import~/_cgo_import.go' -o 'bazel-out/k8-fastbuild/bin/linux_amd64_static_stripped/demogo~/demogo.a~partial.a' -trimpath . -I . -- -p demogo)
SUBCOMMAND: # //:demogo [action 'GoPack linux_amd64_static_stripped/demogo~/demogo.a']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/pack -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_static_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -in 'bazel-out/k8-fastbuild/bin/linux_amd64_static_stripped/demogo~/demogo.a~partial.a' -out 'bazel-out/k8-fastbuild/bin/linux_amd64_static_stripped/demogo~/demogo.a' -arc bazel-out/k8-fastbuild/bin/libdemogo.cgo_c_lib.lo)
SUBCOMMAND: # //:demogo [action 'GoLink linux_amd64_static_stripped/demogo']
(cd /home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo && \
  exec env - \
  bazel-out/host/bin/external/io_bazel_rules_go/go/tools/builders/linux_amd64_stripped/link -go external/go_sdk/bin/go -root_file 'bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_static_stripped/stdlib~/ROOT' -goos linux -goarch amd64 '-cgo=1' -compiler_path /usr/bin -cc /usr/bin/gcc -cpp_flag -U_FORTIFY_SOURCE -cpp_flag -fstack-protector -cpp_flag -B/usr/bin -cpp_flag -B/usr/bin -cpp_flag -Wunused-but-set-parameter -cpp_flag -Wno-free-nonheap-object -cpp_flag -fno-omit-frame-pointer -cpp_flag -fno-canonical-system-headers -cpp_flag -Wno-builtin-macro-redefined -cpp_flag '-D__DATE__="redacted"' -cpp_flag '-D__TIMESTAMP__="redacted"' -cpp_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -ld_flag -U_FORTIFY_SOURCE -ld_flag -fstack-protector -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -Wunused-but-set-parameter -ld_flag -Wno-free-nonheap-object -ld_flag -fno-omit-frame-pointer -ld_flag -fno-canonical-system-headers -ld_flag -Wno-builtin-macro-redefined -ld_flag '-D__DATE__="redacted"' -ld_flag '-D__TIMESTAMP__="redacted"' -ld_flag '-D__TIME__="redacted"' -ld_flag '-fuse-ld=gold' -ld_flag -Wl,-no-as-needed -ld_flag -Wl,-z,relro,-z,now -ld_flag -B/usr/bin -ld_flag -B/usr/bin -ld_flag -pass-exit-codes -ld_flag -lstdc++ -ld_flag -lm -ld_flag '-Wl,-rpath,$ORIGIN/../' -ld_flag -Wl,bazel-out/k8-fastbuild/bin/demogo._cgo_.o -ld_flag -static -out bazel-out/k8-fastbuild/bin/linux_amd64_static_stripped/demogo -- -linkmode external -w -extld /usr/bin/gcc 'bazel-out/k8-fastbuild/bin/linux_amd64_static_stripped/demogo~/demogo.a')
ERROR: /home/cstrahan/go/src/github.com/cstrahan/go-proto-binding/BUILD:36:1: GoLink linux_amd64_static_stripped/demogo failed (Exit 1)
/home/cstrahan/.cache/bazel/_bazel_cstrahan/f02058a447d81a94bd1b377d77d4c78d/execroot/demo/bazel-out/k8-fastbuild/bin/external/io_bazel_rules_go/linux_amd64_static_stripped/stdlib~/pkg/tool/linux_amd64/link: running /usr/bin/gcc failed: exit status 1
/usr/bin/ld.gold: error: cannot mix -static with dynamic object bazel-out/k8-fastbuild/bin/demogo._cgo_.o
collect2: error: ld returned 1 exit status

GoLink: error running linker: exit status 1
Target //:demogo failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 7.095s, Critical Path: 6.79s
FAILED: Build did NOT complete successfully

@cstrahan
Copy link
Author

@jayconrod If you could lend me a hand with this, you would be my hero ❤️.

Puss in Boots

@jayconrod
Copy link
Contributor

@cstrahan I'll look into this as soon as I can. Sorry I haven't gotten to this already, but I've been swamped with the Go summit last week and some travel afterward.

@jayconrod
Copy link
Contributor

Ok, I looked into this a bit. The library with the static initializer does get passed to the linker, but the linker doesn't include it in the final binary because it doesn't provide any symbols that are undefined at that point. In order for this to work correctly, go_binary would need to know about alwayslink from cc_library, and it would need to emit -Wl,--whole-archive and -Wl,--no-whole-archive around libraries with that flag set. I don't believe there is any way for us to know whether alwayslink is set for a particular library though, but I'll check with the Bazel folks to be sure. If not, I'll file an issue.

To work around this, you could try defining a dummy symbol in the library with the static initializer and referencing it somewhere. For example:

// in dep.cc
extern "C" {
  int dummy = 42;
}
// in main.go
/*
extern void cc_main();
extern int dummy;
int* pdummy = &dummy;
*/
import "C"

A long-winded explanation may be useful here.

When we build a Go package that contains cgo, we follow these steps (roughly):

  • Split .go files containing cgo into .go and .c files with go tool cgo. This generates a few extra files like _cgo_export.c, _cgo_main.c.
  • Compile all the .c files (including other static files in the directory) into .o files using a C compiler. In rules_go, we use cc_library to do this. It produces an archive (.a or .lo) containing .o files.
  • Link the .o files into a binary named _cgo_.o. Despite its name, this is an executable binary, not an object file. It does not get linked with the final binary (as you discovered).
  • Extract symbols from _cgo_.o using go tool cgo and generate _cgo_gotypes.go. This file ends up containing linker flags encoded as Go compiler pragmas.
  • Compile the .go files (including non-cgo static files in the directory) with the Go compiler. This produces an .a file.
  • Pack the .o files from the C code into the .a file.

When we link a Go binary containing cgo:

  • The Go linker produces an ELF file (at least on Linux) with all of the compiled Go code.
  • The Go linker passes this to the C linker, along with all of compiled .o files there were extracted from various .a files. Linker flags that were encoded as pragmas are passed to the linker at some point.

In order for us to handle this case correctly, we would need to patch the code that go tool cgo generates and add -Wl,--whole-archive and -Wl,--no-whole-archive around the libraries that need it in the link flag pragmas. Knowing which libraries require that is the challenge, but I think that fix would be relatively simple, at least on Linux and Darwin.

@cstrahan
Copy link
Author

@jayconrod Thanks for the pointers! Here's my (admittedly gross) hack:

diff --git a/go/tools/builders/cgo.go b/go/tools/builders/cgo.go
index 61f3f80..6285b19 100644
--- a/go/tools/builders/cgo.go
+++ b/go/tools/builders/cgo.go
@@ -30,10 +30,13 @@ import (
 	"log"
 	"os"
 	"path/filepath"
+	"regexp"
 	"strings"
 	"unicode"
 )
 
+var linkLine = regexp.MustCompile(`^//go:cgo_ldflag ".*\.(o|lo|a)"$`)
+
 func run(args []string) error {
 	builderArgs, toolArgs := splitArgs(args)
 	sources := multiFlag{}
@@ -57,6 +60,7 @@ func run(args []string) error {
 		if err != nil {
 			return err
 		}
+
 		goargs := append([]string{"tool", "cgo", "-dynpackage", dynpackage}, toolArgs...)
 		return goenv.runGoCommand(goargs)
 	}
@@ -195,6 +199,44 @@ func run(args []string) error {
 			return err
 		}
 	}
+
+	// Begin hack
+	// 1. Determine path to _cgo_gotypes.go
+	objdir := ""
+	for i, a := range toolArgs {
+		if a == "-objdir" {
+			objdir = toolArgs[i+1]
+			break
+		}
+	}
+	if objdir == "" {
+		return errors.New("could not determine -objdir")
+	}
+	gotypesPath := objdir + "/_cgo_gotypes.go"
+
+	dat, err := ioutil.ReadFile(gotypesPath)
+	if err != nil {
+		return err
+	}
+
+	// 2. Add --{,no-}whole-archive around each linked object/archive
+	lines := []string{}
+	for _, line := range strings.Split(string(dat), "\n") {
+		if linkLine.MatchString(line) {
+			lines = append(lines, `//go:cgo_ldflag "-Wl,--whole-archive"`)
+			lines = append(lines, line)
+			lines = append(lines, `//go:cgo_ldflag "-Wl,--no-whole-archive"`)
+		} else {
+			lines = append(lines, line)
+		}
+	}
+
+	err = ioutil.WriteFile(gotypesPath, []byte(strings.Join(lines, "\n")), 0555)
+	if err != nil {
+		return err
+	}
+	// End hack
+
 	return nil
 }
 

That makes my minimal repro work, and I can't see any reason it wouldn't work for my actual project (though I now need to go test it out).

@cstrahan
Copy link
Author

cstrahan commented Oct 5, 2018

@jayconrod

I don't believe there is any way for us to know whether alwayslink is set for a particular library though, but I'll check with the Bazel folks to be sure. If not, I'll file an issue.

Did you get a chance to confirm either way? If this is surfaced somewhere, I can try to implement and submit a fix this weekend.

@jayconrod
Copy link
Contributor

@cstrahan When I asked about this earlier, I was told there was no way to know, but passing -Wl,--whole-archive and -Wl,--no-whole-archive would probably be the best thing to try. Seems like that worked for you above.

Since that time, the C/C++ compile and link API has been overhauled (see cc_common). I expect to migrate to the new API in the next couple weeks because the old API is going away. #1744 is the tracking issue for that. I'll link this issue.

@cstrahan
Copy link
Author

cstrahan commented Nov 9, 2018

@jayconrod Should this be resolved as of #1744? I see this quote there:

Make sure to fix #1486 as part of this.

@jayconrod
Copy link
Contributor

Sadly I don't think this is fixed. I originally intended to rewrite all of the cgo logic and fix this bug in the process. Now that cc_common is available, it should be possible to consolidate the logic in a single rule or action.

Unfortunately, I haven't had time to do a proper rewrite. #1768 ended up being a quick fix to avoid being broken by incompatible Bazel changes and nothing more.

@steeve
Copy link
Contributor

steeve commented Jan 2, 2019

Knowing which libraries require that is the challenge, but I think that fix would be relatively simple, at least on Linux and Darwin.

Wouldn't that be relatively easy with an aspect ?

@steeve
Copy link
Contributor

steeve commented Jan 2, 2019

I did a wip PR based on an aspect, it works well for us: #1879

@cstrahan
Copy link
Author

@jayconrod Can you share any update on this? Is rules_go in a state where I might be able to help with resolving this (with some guidance)? Earlier, you stated:

I don't believe there is any way for us to know whether alwayslink is set for a particular library though, but I'll check with the Bazel folks to be sure. If not, I'll file an issue.

Do you know if this has changed? My understanding is that the Bazel developers are attempting reach version 1.0 in September; I'd like to see if we can get that surfaced by then (if it's not already).

@steeve
Copy link
Contributor

steeve commented Aug 21, 2019

I think this is now fixed with CcInfo, and can be used yes.

Confirmed: https://docs.bazel.build/versions/0.28.0/skylark/lib/LibraryToLink.html#alwayslink

@jayconrod
Copy link
Contributor

@cstrahan The blocking issue for this was bazelbuild/bazel#7033, which is now fixed. So we'd need to pass -Wl,--whole-archive / -Wl,--no-whole-archive around each library with the alwayslink flag set. I think the flags are probably different for GCC and clang, too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants