Search code examples
ubuntulinkerdynamic-library

undefined reference when linking dynamic library in Ubuntu system


i have written such a code in my Ubuntu system:

my.h

#include <stdio.h>
int a;
int set(void);

lib.c

#include "my.h"
int set(void) {
  a = 100;
  return 0;
}

main.c

#include "my.h"

int main(void){
  set();
  printf("a = %d\n", a);
  return 0;
}

and then i use follow commands to build them:

gcc -shared -fPIC -o libmy.so -I. lib.c

gcc -L. -lmy -I. -o test main.c

when i build the test, i get the error information :

main.c:(.text+0x5):undefined reference to 'set'
collect2: error: ld returned 1 exit status

but when i use the same code run in Fedora23 and Fedora24, it works well.

so i want to know why can this happen? is there any limitation in Ubuntu system?


Solution

  • You have fallen foul of a difference between the linkage conventions of Fedora's GCC builds and Debian/Ubuntu's GCC builds.

    When you invoke gcc to perform linkage of a C executable it in turn invokes the system linker ld, passing it your commandline linkage options and silently adds to them a large number of "boilerplate" linkage options which are invariant for C language linkages (similarly for g++ and C++ language linkages).

    Those invariant linkage options are decided by your distro and configured into their build of GCC. So they are not invariant across distros.

    Debian/Ubuntu GCC silently adds --as-needed to the linkage options at a position before your input files and libraries. Fedora's GCC does not.

    The effect of --as-needed is to make the linker link a shared library that it finds in the linkage sequence only if that library provides a definition of one or more symbols for which the linker has already found undefined references (i.e. within object files or libraries earlier in the linkage sequence). This behaviour applies in any case for static libraries. So --as-needed makes the linkage rules similar for both static and shared libraries - which might be considered helpful to the average user.

    This difference is a linkage policy difference between the distros. What it means to you is that to link successfully on Ubuntu, your linkage commandline must mention any library after any object file or other library that depends on it. And if you are compiling-and-linking in one command, then you must mention any library after any source file whose corresponding object file depends on that library. So on ubuntu, your problem commandline should be:

    gcc -I. -o test main.c -L. -lmy
    

    to succeed. This will also of course work for Fedora.

    If you are interested in inspecting the hidden differences in the linkage options between the two distros you can reveal them by add -Wl,-v to your compile-and-link command to get verbose linker output.