13

I am trying to link a C++ module using GCC, essentially like this:

gcc -c hello.c
g++ -c world.cpp
gcc -ohello -lstdc++ hello.o world.o

Note that I use -lstdc++ to link the C++ module in, so that I can use gcc instead of g++. The problem is that I'm getting the error:

undefined reference to `operator new(unsigned long)'

(Assuming that world.cpp contains at least one call to new.)

This error is fixed if I put -lstdc++ at the end of the linker line, like this:

gcc -ohello hello.o world.o -lstdc++

I am aware that this question has been asked many times here, but I have a special requirement. I am not directly calling GCC. I am using a build system for a different programming language (Mercury) which is calling GCC on my behalf, and I can't easily modify the way it calls GCC (though I can specify additional libraries using the LDFLAGS environment variable). So I have two additional requirements:

  • I cannot use g++ to link (only gcc) -- that is why I am doing the -lstdc++ trick above rather than simply linking with g++).
  • I don't think that I can control the order of the linker commands -- Mercury will put the .o files on the command-line after any libraries.

I understand the basic reason why the order is important, but what is baffling me is why did this break now? I just updated to Ubuntu 11.10 / GCC 4.6.1. I have been successfully compiling this program for years using precisely the above technique (putting -lstdc++ first). Only now has this error come up. An unrelated program of mine links against OpenGL using -lgl and that too broke when I upgraded and I had to move -lgl to the end of the command-line. I'm probably going to discover that dozens of my programs no longer compile. Why did this change? Is there something wrong with my new system or is that the way it is now? Note that these are ordinary shared libraries, not statically linked.

Is there anything I can do to make GCC go back to the old way, where the order of libraries doesn't matter? Is there any other way I can convince GCC to link libstdc++ properly without moving it after the .o files on the command-line?

mgiuca
  • 20,958
  • 7
  • 54
  • 70

1 Answers1

15

If Mercury puts object files after libraries, Mercury is broken. Libraries belong after object files - always. You may sometimes get away with the reverse order, but not reliably. (Static libraries must go after the object files that reference symbols in the static library. Sometimes, a linker will note the symbols defined by a shared library even when none of the symbols are used; sometimes, the linker will only note the shared library symbols if the shared library provides at least one symbol.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 3
    So basically this "regression" is just that for my entire development career, I have been relying on the fact that it "accidentally worked" and now it has stopped working? Seems like a pretty major change -- I have been putting libraries before object files for the better part of a decade now because I had no idea this was an issue :( Thanks for the advice. It looks like the problem wasn't Mercury putting objects after libraries, but that there are more complex dependencies within my LDFLAGS command. I am making progress, so I'll give you a tick. Thanks. – mgiuca Dec 27 '11 at 02:41
  • 1
    Unproven hypothesis: there may have been a change in `ld` that affects you. The goal may well be to avoid loading unused shared libraries. If you use static libraries, you only get the stuff you need. It used to be the case that if you listed a shared library on the link line, it would be loaded at runtime, even if the library provided no symbols whatsoever. Only loading the library if it satisfies at least one symbol can reduce the startup time for the program. But it also means that if the only outstanding symbol when the library is scanned is `main()`, then the library won't be loaded. – Jonathan Leffler Dec 27 '11 at 02:55
  • See comments about `--as-needed` for version 2.20 of `ld` in the [NEWS](http://sourceware.org/cgi-bin/cvsweb.cgi/~checkout~/src/ld/NEWS?rev=1.121&content-type=text/plain&cvsroot=src&only_with_tag=binutils-binutils-2_22) file. See also the [manual](http://sourceware.org/binutils/docs-2.22/ld/Options.html#Options) and the `--copy-dt-needed-entries` and `--no-copy-dt-needed-entries` options. The previous _unproven hypothesis_ is at least somewhat proven, I believe. – Jonathan Leffler Dec 27 '11 at 03:17
  • Oh nice call. I did go through the differences between the gcc and g++ collect2 call line, and I noticed that gcc uses --as-needed whereas g++ does not. So that might explain it. In any case, I have solved it now (by carefully reordering the dependencies within LDFLAGS). Thanks very much for looking into this. – mgiuca Dec 27 '11 at 06:05