As have been pointed out, you are violating the one-definition rule. This is not the end of the world, but in this case there are no guarantees from the C++-standard what will happens and the behavior depends on the implementation details of the linker and loader.
Tool-chains and operating systems are quite different so the above will not even link on Windows. But if your are speaking about Linux with the usual linker/loader pair, then the behavior will be to use the changed version - and it will be the for every Linux-installation.
That is the way the linker/loader are working on Linux (and this behavior is widely used for example for LD_PRELOAD-trick):
- The symbols in
*.so
are weak and so the definition from *.so
are just neglected if linker finds another definition somewhere else (in your case in the updated version of f1.o
).
- during the run time, the loader neglects the definitions from the shared object, if the symbol is already bound, i.e. another definition is known. In your case the symbol
f1
(ok, because of the name-mangling it will have a different name, but let's ignore that for the sake of simplicity) is already bound to the definition which is in the main-program and thus will be used when f1
is called in *.so
.
However, this way of doing things is very brittle and some minor changes can lead to a different result.
A: changing the visibility to hidden.
It's recommended to hide symbols which are not part of the public interface, i.e.
__attribute__ ((visibility ("hidden")))
int f1() {return 1;}
In this case, not the overwritten version is used but the old. The difference is, that when the linker sees a hidden symbol being used, it no longer delegates it to the loader to resolve the address of the symbol, but uses the address at hand directly. Later on, there is no way we could change which definition is called.
B: making f1
were an inline-function.
That would lead to really funny things, because in some parts the shared-object the old version would be used and in some part the new version.
-fPIC
prevents the inlining of the function which are not marked with inline
, so the above holds only for function which are marked as inline explicitly.
In a nutshell: This trick is can be used on Linux. However in bigger projects you don't want to have additional complexity and try to stick the more sustainable and simple one-definition-rule framework.