shared.cpp
#include "shared.h" Foo::Foo() { int *a = 0; *a = 33; }
shared.h
class Foo { public: Foo(); };
main.cpp
#include "shared.h" int main(int, char **) { Foo f; return 0; }
We do the initial compilation:
g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3 g++ main.cpp -lshared -L . -g3 LD_LIBRARY_PATH=. ./a.outand it segfaults :)
Now we want to use ASAN to find out what's wrong, where do we add the -fsanitize=address -fno-omit-frame-pointer? Logic would say that we add it to both places, but actually it's only necessary to add it to the final binary, i.e
g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3 g++ -fno-omit-frame-pointer -fsanitize=address main.cpp -lshared -L . -g3 LD_LIBRARY_PATH=. ./a.outand we get
==10226== ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7f497563f66a sp 0x7fff77cc4310 bp 0x7fff77cc4310 T0) AddressSanitizer can not provide additional info. #0 0x7f497563f669 in Foo::Foo() /home/tsdgeos/test/shared.cpp:6 #1 0x40074a in main /home/tsdgeos/test/main.cpp:5 #2 0x7f497529aec4 (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4) #3 0x400638 in _start (/home/tsdgeos/test/a.out+0x400638) SUMMARY: AddressSanitizer: SEGV /home/tsdgeos/test/shared.cpp:6 Foo::Foo()
There you go, no need to add the -fsanitize=address flag to the library compilation :)
Now, until a few minutes ago I did not know this, I actually was adding -fsanitize=address to the library itself; that comes with a few problems that i'll explain how i solved (just for completeness, since the above example shows you don't need it)
My logic was that by looking at main.cpp i knew nothing could be wrong there so i decided i wanted sanitize the library, my first attempt was:
g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3 \ -fno-omit-frame-pointer -fsanitize=addresswhich gives a linking error
/tmp/cc18Wen3.o: In function `Foo::Foo()': /home/tsdgeos/test/shared.cpp:6: undefined reference to `__asan_report_store4' /usr/bin/ld: /tmp/cc18Wen3.o: relocation R_X86_64_PC32 against undefined symbol `__asan_report_store4' can not be used when making a shared object; recompile with -fPIC /usr/bin/ld: final link failed: Bad value
OK, so we are told to recompile with -fPIC, easy peasy
g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3 \ -fno-omit-frame-pointer -fsanitize=address -fPICWhich still fails to link!
/tmp/ccNkpRZo.o: In function `Foo::Foo()': /home/tsdgeos/test/shared.cpp:6: undefined reference to `__asan_report_store4' /tmp/ccNkpRZo.o: In function `_GLOBAL__sub_I_00099_0_shared.cpp': /home/tsdgeos/test/shared.cpp:7: undefined reference to `__asan_init_v1' collect2: error: ld returned 1 exit status
OK, so if ASAN symbols are missing, let's just compile libasan in
g++ -Wl,--no-undefined -shared -o libshared.so shared.cpp -g3 \ -fno-omit-frame-pointer -fsanitize=address -fPIC -lasanLinks!
Next since I know nothing is wrong in main.cpp I don't need ASAN in there
g++ main.cpp -lshared -L . -g3Compiles!
But when run it segfaults :-/ And if you debug it you'll see a stack trace like
#0 0x0000000000000000 in ?? () #1 0x00007ffff4894bf1 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0 #2 0x00007ffff4894e32 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0 #3 0x00007ffff4895d9b in __asan_init_v1 () from /usr/lib/x86_64-linux-gnu/libasan.so.0 #4 0x00007ffff7bd8776 in _GLOBAL__sub_I_00099_0_shared.cpp(void) () at shared.cpp:7 #5 0x00007ffff7dea13a in call_init (l=, argc=argc@entry=1, argv=argv@entry=0x7fffffffde18, env=env@entry=0x7fffffffde28) at dl-init.c:78 #6 0x00007ffff7dea223 in call_init (env= , argv= , argc= , l= ) at dl-init.c:36 #7 _dl_init (main_map=0x7ffff7ffe1c8, argc=1, argv=0x7fffffffde18, env=0x7fffffffde28) at dl-init.c:126 #8 0x00007ffff7ddb30a in _dl_start_user () from /lib64/ld-linux-x86-64.so.2 #9 0x0000000000000001 in ?? () #10 0x00007fffffffe1ca in ?? () #11 0x0000000000000000 in ?? ()
So something weird in ASAN thing is going on, let's compile -lasan in also
g++ main.cpp -lshared -L . -g3 -lasan
Same crash :-/
Ok, so then you think, well, let's just add the fsanitize in. And then it works, but as demonstrated a few lines ago, the whole thing of adding -fsanitize=address and -lasan to the library was unneeded since all that was required was just adding -fsanitize=address to the binary when it's compiled and that's it :)
Hey Albert,
ReplyDeletewhat about plugins? They are shared libraries, so do I really only need to compile the main executable with asan which then loads the plugins as-is?
KDevelop e.g. is a very small executable which loads tons of plugins and I'd love to run the sanitizers on our codebase somehow.
If you answer this, please notify me or write a new blog entry. Thanks, much appreciated!
It doesn't help with memory leaks, and I find it doesn't provide as many details as valgrind does.
ReplyDeleteSo we still need valgrind. But considering it doesn't strike the performance down so much, it's definitely a good tool for allowing beta-tester to provide better feedback.
Note the contents of this blogpost are not totally correct. In some cases ASAN is needed also in the library, see http://tsdgeos.blogspot.com/2014/03/asan-and-libraries-2nd-part.html for more information.
ReplyDeleteHere's what Asan FAQ has to say on the topic:
ReplyDeleteQ: When I link my shared library with -fsanitize=address, it fails due to some undefined ASan symbols (e.g. asan_init_v4)?
A: Most probably you link with -Wl,-z,defs or -Wl,--no-undefined. These flags don't work with ASan.
Source: https://code.google.com/p/address-sanitizer/wiki/AddressSanitizer
So looks like "-Wl,--no-undefined" was the culprit.
Revisiting this 4 years later. The only correct way to sanitize code is to compile and link it with -fsanitize=address (if it breaks for you, please report bug at https://github.com/google/sanitizers). In particular explicit linking with -lasan is wrong.
ReplyDeleteAs a side note, if you need to run sanitize library with unsanitized executable, do
Q: I've built my shared library with ASan. Can I run it with unsanitized executable?
A: Yes! You'll need to build your library with dynamic version of ASan and then run
executable with LD_PRELOAD=path/to/asan/runtime/lib.
(from FAQ at https://github.com/google/sanitizers/wiki/AddressSanitizer).
I get 'cannot find -lasan' when compiling using -fsanitize=address.
ReplyDeleteNDK r10, gcc 4.8.
Can somebody help?