nvlink error: "Multiple definition" errors when linking to the same library twice

Hi,

I found an issue with device linking with nvcc. If you link your binary against the same static library more than once, then you get the error “Multiple definition of X”. This does not occur with g++.

There is some documentation about it:
6.5.5. Device Code in Libraries
If a device function with non-weak external linkage is defined in a library as well as a non-library object, the device linker will complain about the multiple definitions (this differs from traditional host linkers that may ignore the function definition from the library object, if it was already found in an earlier object).

However it talks about library-object incompatibility, but here the problem occurs even with library-library.

CUDA version: V9.0.176
CUDA arch: sm_61
CMake version: 3.11.4
g++ version: 5.4.0
OS: Ubuntu 16.04

How to reproduce

  1. Download this example code of CUDA + CMake:
    https://devblogs.nvidia.com/building-cuda-applications-cmake/
    code-samples/posts/cmake at master · robertmaynard/code-samples · GitHub

  2. In the CMakeLists.txt, replace:

target_link_libraries(particle_test PRIVATE particles)

with:

target_link_libraries(particle_test PRIVATE particles particles)
  1. Compile:
mkdir build
cd build
make VERBOSE=1

This change generates the following error:

[ 85%] Linking CUDA device code CMakeFiles/particle_test.dir/cmake_device_link.o
/opt/cmake-3.11.4-Linux-x86_64/bin/cmake -E cmake_link_script CMakeFiles/particle_test.dir/dlink.txt --verbose=1
/usr/local/cuda/bin/nvcc    -Xcompiler=-fPIC -Wno-deprecated-gpu-targets -shared -dlink CMakeFiles/particle_test.dir/test.cu.o -o CMakeFiles/particle_test.dir/cmake_device_link.o  -L/usr/local/cuda/targets/x86_64-linux/lib libparticles.a libparticles.a 
nvlink error   : Multiple definition of '_ZN8particle7advanceEf' in 'libparticles.a:particle.cu.o', first defined in 'libparticles.a:particle.cu.o'
nvlink error   : Multiple definition of '_ZN2v38scrambleEv' in 'libparticles.a:v3.cu.o', first defined in 'libparticles.a:v3.cu.o'
nvlink error   : Multiple definition of '_ZN2v39normalizeEv' in 'libparticles.a:v3.cu.o', first defined in 'libparticles.a:v3.cu.o'
nvlink fatal   : Internal error: duplicate relocations at same address
CMakeFiles/particle_test.dir/build.make:85: recipe for target 'CMakeFiles/particle_test.dir/cmake_device_link.o' failed

Use case

A possible use case is as follows (pseudocode in CMake):

add_library(base_library STATIC ...)

add_library(library_a ...)
target_link_libraries(library_a STATIC base_library)

add_library(library_b ...)
target_link_libraries(library_b STATIC base_library)

add_executable(main ...)
target_link_libraries(main library_a library_b)

In this example, the main binary links against 2 libraries, library_a and library_b, both of which link against the base_library.

Now, CMake will generate the NVCC linking command so that main links against:

library_a, base_library, library_b, base_library

And since we have base_library included twice, it will complain about multiple definitions.

Is this intended? How could we solve it?
Thanks!

This is expected behavior.

It’s necessary to device-link against a particular piece of code only once. Multiple device links associated with a final object (library, or executable) are possible as long as there is no overlap between the device-linked pieces.

Otherwise it is necessary to reformulate your build process to only device-link once. I recognize this may present challenges, and don’t have a worked example or solution heuristic to offer for every imaginable case.

In this case, you could trivially solve this by instead of creating base_library, library_a, and library_b, you instead create a single library_c, which incorporates all the functionality of library_a and library_b. Provide library_c to your main routine. I recognize that there are any number of possible objections/complaints with this approach, in which case I refer you to my previous paragraph.