Duplicate symbols - how to trace where these are coming from?

I have just run into a conundrum: in a large program I have to include a call to an extra subroutine. But if I do that I all of a sudden get error messages two symbols are duplicately defined. As all routines are contains in modules I am rather surprised at that and the error messages point to libraries that do not directly contain these symbols. How can I find out where these symbols are coming from? Right now I have a hard time figuring out what to do.

Not sure how your object files and libraries are structured but on Linux I would try using nm to list symbols in object files. I would probably write all the .o files generated into one directory and then use ar to build a static library. Then use nm with grep to try to narrow down where the symbols are occuring. I doubt this is fool proof but in the absence of compiler options to build a cross-reference of symbols (something old time compilers like the UNIVAC compilers would do for you) I’m unaware of a more automated way of finding duplicate symbols. They probably exist and I’m just not aware of them.

Qualifying use statements with only could fix the problem or help to diagnose it. For example, the following program

module kind_mod
implicit none
integer, parameter :: wp = kind(1.0d0)
end module kind_mod
!
module sub_mod
implicit none
integer, parameter :: wp = kind(1.0d0)
contains
subroutine hi()
print*,"hi"
end subroutine hi
end module sub_mod
!
program main
use kind_mod
use sub_mod
implicit none
call hi()
print*,"wp =",wp
end program main

does not compile, with gfortran saying

xxmod.f90:20:16:

   20 | print*,"wp =",wp
      |                1
Error: Name 'wp' at (1) is an ambiguous reference to 'wp' from module 'kind_mod'

If use sub_mod is replaced with use sub_mod, only: hi the problem is solved, and if

use kind_mod
use sub_mod

are replaced with

use kind_mod, only: wp
use sub_mod, only: wp, hi

the problem is apparent.

I have been thinking about that sort of methods and causes. As it is this one routine that causes the problem, it should be straightforward to find the underlying cause. However, it is the linker that complains, not the compiler. And right now I am suspicious about the “!DEC$ ATTRIBUTES DLLEXPORT” statements in the implementation of the two routines that figure in the “duplicate symbols” error. The names in these messages changed, when I removed their “bind(C)” attributes.

Well, weekend now. Time enough next week for renewed worrying.

Depending on your platform there are typically options to display the origin point of symbols. For example, on a Linux system with gfortran and the ld loader the gfortran option

-Wl,--trace-symbol=foo

added to the gfortran(1) command will show the files foo is defined in.

ld(1) has many useful options. The man-page describes them in detail.

For example, this program calls SIN(). What file is SIN() defined in?

program testit
   write(*,*)sin(0.0)
end program testit
 gfortran xx.f90 -Wl,--trace-symbol=sin
 /usr/bin/ld: /lib/x86_64-linux-gnu/libm.so.6: definition of sin
 /usr/bin/ld: /lib/x86_64-linux-gnu/libm.so.6: definition of sin

nm(1), ldd(1) ,objdump(1),readelf(1),ar(1), and strings(1) can also be useful.

You can allow multiple definitions to not be an error as well.

# allow multiple definitions; NOTE: make sure the one you want is loaded first
gfortran xx.f90 -Wl,--trace-symbol=sin,--allow-multiple-definition
# list all the libraries used on the load
gfortran xx.f90 -Wl,--trace|sort|uniq|cat -n

When you get a list of all file files loaded you can then use file-oriented commands
like nm(1) on the files of interest.

     1  /dev/shm/cceZr6TL.o
     2  /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
     3  /lib/x86_64-linux-gnu/libc.so.6
     4  /lib/x86_64-linux-gnu/libm.so.6
     5  /lib/x86_64-linux-gnu/libmvec.so.1
     6  /usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o
     7  /usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o
     8  /usr/lib/gcc/x86_64-linux-gnu/13/libgcc.a
     9  /usr/lib/gcc/x86_64-linux-gnu/13/libgcc_s.so
    10  /usr/lib/gcc/x86_64-linux-gnu/13/libgfortran.so
    11  /usr/lib/gcc/x86_64-linux-gnu/13/libquadmath.so
    12  /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crti.o
    13  /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crtn.o
    14  /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/libc.so
    15  /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/libgcc_s.so.1
    16  /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/libm.so
    17  /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o
    18  /usr/lib/x86_64-linux-gnu/libc_nonshared.a

There are other very powerful options like --swap=symbol which among other things lets you call custom versions to trap out-of-range values or record malloc() calls too. MSWindows users might like --force-exe-suffix; and so on but those get into other topics.

1 Like

My development platform is Windows (though Linux is the second on which it should all work). The curious thing is that the linker complains about two routines with names of the form MODULENAME_mp_SUBROUTINE (note the capitals). Before, with the BIND(C) clause in effect, the names were “subroutine”(so, lowercase). These names are getting in independently of the USE statement.

It definitely has to do with the “!DEC$ ATTRIBUTES DLLEXPORT” directive. Commenting that out solves the problem (but other programs in the VS solution do need them). I guess splitting off the culprits into a separate source file and ditto module might help here.

I had some similar issues in the past when working on Windows. Maybe what you could try is to remove the ‘bind(C)’ and define your interface more explicitly:

!DEC$ ATTRIBUTES DLLEXPORT, STDCALL, REFERENCE, ALIAS: "subroutine" :: subroutine

In addition, if your code does not always require the DLLEXPORT, and that you are not completely hostile to preprocessing, you could play with preprocessor variable to have a conditional export. Personally, I am using this kind of approach for my unit tests. Usually I would compile test cases as dll, and my test driver loads them and call the exported tests subroutines. But, when using fpm for instance, I also use the tests cases statically, and in this case I do not export them.

#ifndef _NOEXPORT
    !DEC$ ATTRIBUTES DLLEXPORT, STDCALL, REFERENCE, ALIAS: "test_1" :: test_1
#endif

The only issue with this approach is that you need to make sure to recompile all your objects because sometimes the linker would complain about subroutines with stdcall convention when statically bound to the main program.

The BIND(C) statement changes the name used by the linker/loader, particularly with optional “NAME=” specifier. I think it is working as expected.

Can you share your full “!DEC$ ATTRIBUTES DLLEXPORT” directive. An ALIAS in this directive may duplicate the symbol name in the BIND(C). I am unclear what happens when you use both, and don’t have time to experiment today.

Sure, here are the lines of code for one of them:

subroutine set_progress_c_callback(c_callback) bind(C, name="set_progress_c_callback")
#ifdef _WIN32
  !DEC$ ATTRIBUTES DLLEXPORT:: set_progress_c_callback
#endif

I figured out that this directive was the culprit because the name of the duplicate symbol changed with removing the bind() attribute.

@Arjen, you are definitely not the only one to experience strange thing with dll export :thinking:. I copy here a comment from another post:

And this one is from the intel forum:

What you are seeing is that MSVC is ALSO emitting a name without the suffix - and Intel Fortran does this too. Why? Fortran does it because that allows you to declare a name simply EXTERNAL and pass it as an argument. I am not sure why MSVC is doing it as well.

It may be that when you mix bind(c) and !DEC$ directives the compiler gets ‘confused’ and exports twice the subroutine.
Can you make a dump of the .lib using dumpbin? (dumpbin /exports **.lib)?