This example consists of multiple files:
// baz.cxx
int wat = 0;
int counter = ++wat;
// foo.cxx (empty)
// bar.cxx (empty)
// main.cxx
#include <iostream>
extern int wat;
int main() {
std::cout << wat << '\n';
}
// makefile
run : main.cxx foo.so bar.so
g++ -std=c++11 $^ -o $@
baz.a : baz.cxx
g++ -std=c++11 -c $^ -o baz.o -fPIC
ar rcs $@ baz.o
%.so : %.cxx baz.a
g++ -std=c++11 $< -Wl,--whole-archive baz.a -Wl,--no-whole-archive -o $@ -shared -fPIC
As-is, if you just run make && LD_LIBRARY_PATH=. ./run
, everything with compile, build, link, run and output 2
. This is because both foo.so
and bar.so
provide wat
, and the initialization for counter
is run twice.
Is there a way to somehow force run
to fail to link with a multiple definition error in this case, while still ensuring that both foo.so
and bar.so
have a definition for wat
?
You could use linker scripts libfoo.so
and libbar.so
with a static part which creates a symbol conflict (and install the actual DSOs in some non-obvious place, so that -lfoo
and -lbar
do not pick them up).
Something like this:
libfoo.so
INPUT(conflict.o)
INPUT(./foo.so)
libbar.so
INPUT(conflict.o)
INPUT(./bar.so)
conflict.cxx
int conflict;
This results in:
g++ -std=c++11 main.cxx -o run -L. -lfoo -lbar
conflict.o:(.bss+0x0): multiple definition of `conflict'
conflict.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status
This will only detect conflicts within the same run of the link editor. You could still link two different DSOs with -lfoo
and -lbar
, and link those two into the application, without an error from the link editor.
A similar problem is solved by ABI annotations, but those need specific ld
changes, I think.
If a run-time check is acceptable, you could have a list of weak symbols defined by each DSOs which needs to conflict, and implement an ELF constructor which aborts the process if more than one of them is defined. I'm not aware of anything which achieves something equally reliable at static link time with the GNU toolchain. Maybe this is worth an RFE bug against binutils.
I think that the only proper way to solve the problem is to move content of bar.a
solution to some shared library and thus resolving diamond inheritance problem... but i think that's out of scope of this problem.
The only thing i can think for you is to create "custom" validator executed just before linking final executable. Stg like this:
nm *.so | \ # get all symbols
grep " [B|T] " | \ # only exported ones
egrep -v "__bss_start|_end|_fini|_init" | \ # filter out some commons, probably to be extended
awk '{print $3}' | \ # get only symbol name
sort | uniq -c | \ # sort and count
egrep -v "^[ ]+1 " # get only those that have multiple definitions
This prints all strong (exported) symbols from libraries that are defined more than once. You can easily wrap it in a script that returns error status code if output is not empty with meaningful message.
Experimental version of your patched makefile would look like this:
run: main.cxx foo.so bar.so
! nm foo.so bar.so | grep " [B|T] " | egrep -v "__bss_start|_end|_fini|_init" | awk '{print $3}' | sort | uniq -c | egrep -v "^[ ]+1 "
g++ -std=c++11 $^ -o $@
(note !
to reverse exit code of final grep that searches for any uniq -c
output that doesn't start with bare 1)
I know that it's really hackish, ugly and non-portable solution but i thought it may be of some value in corner cases like yours.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With