views:

198

answers:

4

On my linux box, I have 2 libraries:

libfoo1.a and libfoo2.a

and they both contain an implementation of

void foo(int)

and my main program calls foo:

int main() { foo(1); return 0; }

I compiled the program two ways using g++

g++ main.cpp libfoo1.a libfoo2.a -o a1.out
g++ main.cpp libfoo2.a libfoo1.a -o a2.out

When I run the programs, a1 is clearly using the foo() implementation from libfoo1.a, while a2 clearly used libfoo2. That is, g++ linked whichever foo() that it saw first.

My question (finally) is, is this "greedy" linking policy actually specified in the C++ standard? Or would a different compiler / platform behave differently in an implementation-defined way?

PS: To put the question in practical context, I really like the way this g++ example works. In my real application, I have a legacy libfoo2 that implements many (many!) functions, but I want to provide new implementations for a handful of them in libfoo1. On one hand I could write a whole new interface in libfoo1, implement my handful, then delegate to libfoo2 for the remainder. But I would rather void writing all that delegation code, if I can rely linker to do it for me (even for non-g++ compilers like icc).

PPS: To put it in real practical context, libfoo2 is the blas, and libfoo1 is a homebrew OpenMP implementation of a few of its routines. I'm not ready to shell out for MKL. ATLAS does not multithread the functions I want to call. It is very good at multithreading GEMM, but I need some of the quirkier routines from LAPACK to be fast too (zsptrf / zsptrs / zspr). It appears that the my cache-ignorant OpenMP implementation of these routines can do better than it's cache-tuned sequential implementation.

Sorry for the length of post.

+3  A: 

The standard doesn't say anything about linking order. I would say it's not good practice to rely on whatever order your compiler uses.

Andreas Brinck
As a followup, am I at least guaranteed that the linker will not crash when it sees two definitions for foo() in different libraries? If so, I don't mind just sending users both .a's, and letting them fiddle with their linker (or library archiver) to get the application running.
RAC
@RAC I don't think there's a linker that will crash; some linkers will fail to link if there's multiple definitions though.
Andreas Brinck
+2  A: 

Pretty much any linker will behave as you've described.

The traditional behavior of linkers is to search for external functions from left to right in the libraries specified on the command line. This means that a library containing the definition of a function should appear after any source files or object files which use it.

From An Introduction to GCC.

Andrew Aylett
standard != tradition ;)
Andreas Brinck
This is good news at least. In reality, the spectrum of target platforms is not all that varied.
RAC
@Andreas, indeed not, but that doesn't make what I said untrue. Tradition can be a strong motivator.
Andrew Aylett
+1  A: 

From what I can tell, the C++ standard does not involve the linker. That is why, for instance, mingw32 and microsoft's C++ compilers can get away with using different name mangling schemes.

e8johan
Very good point about name mangling! I can see how that implies linking cannot be standardized.For what it's worth, the foo libraries in question will be using extern "C" linkage. Does this make my problem any easier or change the answer?
RAC
The two concepts of name mangling and name resolution order are not strongly related. Implementations are encouraged to use different mangling techniques to avoid linking together objects which will have different exception and vtable semantics. That's all supposed to be hidden from the build system though, and having consistent linker behaviour makes it easier to completely swap out one toolchain for another.
Andrew Aylett
+2  A: 

According to the standard, you have two definitions of the same function, which violates the One definition rule. The result is undefined behavior.

Probably the "right" way to handle this would be to take the original archive and extract all the object files from it. Then copy your new object files into the directory, and create a new library containing only the versions of the object files that you want. Finally, link with your combined library (and hope nothing in the original library depended on anything undocumented about any functions you replaced).

Jerry Coffin