I'm trying to globally configure AddressSanitizer (ASan) for multiple executables in my C++ project by defining the __asan_default_options() function, like this:
extern "C" const char* __asan_default_options() {
return "allow_user_poisoning=false";
}
I placed this function in a .cpp file, built it into a static library, and then linked that library into an executable. However, ASan does not seem to respect the options.
Note: If I instead add the .cpp file directly to the sources of each executable, it works as expected.
Note: I also found that using a CMake OBJECT library works. For example:
add_library(configAsan OBJECT asan_flags.cpp)
target_link_libraries(my_exec PRIVATE configAsan)
Question: Why does defining __asan_default_options() in a static library not work ?
I tried
add_library(configAsan STATIC asan_flags.cpp)
target_link_libraries(my_exec PRIVATE configAsan)
where my executable looks like this:
int main(int argc, char* argv[]) {
int* p = new int[4];
__asan_poison_memory_region(p + 1, 3 * sizeof(int));
std::cout << "Accessing poisoned memory: " << p[1] << "\n";
delete[] p;
return 0;
}
Expected No use after poison error when running my_exec but got: Error message
Refer to the Stackoverflow tag wiki on static libraries
From that you will learn that if an object file file.o
is archived in a static library
libstat.a
and then libstat.a
is input to the linkage of an executable, the linker
by default will extract file.o
from libstat.a
and link file.o
into the executable only if
file.o
provides a definition of at least one symbol that has been referenced
in some object file already linked into the executable, but not yet defined by any
file that has been linked. In short, file.o
will be extracted and linked only
if it provides a definition that the linkage already needs to resolve some symbol reference.
The file asan_flags.o
, compiled from your asan_flags.cpp
and archived as libconfigAsan.a(asanFlags.o)
,
defines no symbol that has been referenced in any file already linked when the linker examines your
libconfigAsan.a
, so asan_flags.o
is not extracted and linked. It might
as well not exist.
Later in the linkage of your executable, the linker consumes libasan.so
itself, which
serves to resolve your reference to __asan_poison_memory_region
and introduces a
call to __asan_default_options
into the linkage, which must be resolved. But
by now your definition of that function in libconfigAsan.a(asan_flags.o)
has been passed over and ignored. The linker finds a default definition of
__asan_default_options
in libasan.so
itself and binds the reference to that
definition - which is in fact a no-op. Hence the outcome you observe.
To override that no-op default definition with your own, you must make asan_flags.o
be linked before libasan.so
is consumed. One good way to do that is simply to input asan_flags.o
explicitly to the linkage, not as an archive member of a static library. This is what you
have achieved in CMake by building asan_flags.cpp
as a CMake object library: that just compiles it
to asan_flags.o
. Any object file that is input to the linker explicitly is linked into
the executable unconditionally. If an explicit object file was not unconditionally linked
then no symbol references could get into the executable unconditionally and linkage
could never get started.
This way, when libasan.so
is consumed, your definition of __asan_default_options
is already linked and references to that symbol will be bound to that definition: the
linker will not seek another definition of a symbol that is already defined. (Even if
you chose to link libasan
statically, its default definition of __asan_default_options
cannot possibly provoke a multiple definition error in collision with your own, because the
default definition is weak and will still
be overriden by your own.)