6

I'm working on a project which has a "util" library containing stuff like logging, assertion handling etc. This is compiled into a static library with -fPIC added. I also have a plugin system, where the plugins are shared libraries loaded at runtime via dlopen. Those plugins and the main executable both use the static util library.

The problem: Now I'm getting AddressSanitizer: odr-violation errors when using ASAN. The issue is size=40 'vtable for StdStreamWriter' reported twice where StdStreamWriter is an implementation of an interface used internally in the static library.

I tried really hard to reproduce this in a MWE:

  • Create a static library exposing some function
  • Inside that use an interface and implementation in a std::shared_ptr
  • create a shared library linking against that
  • create an executable linking against the static lib and dlopen the shared library

CMakeLists.txt

cmake_minimum_required(VERSION 3.8)
project(proj)

set(sanitizer_flags "-fsanitize=address,undefined -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS " ${sanitizer_flags}")
string(APPEND CMAKE_EXE_LINKER_FLAGS " ${sanitizer_flags}")
string(APPEND CMAKE_MODULE_LINKER_FLAGS " ${sanitizer_flags}")

add_library(foo STATIC foo.cpp)
target_compile_features(foo PUBLIC cxx_std_14)
set_target_properties(foo PROPERTIES CXX_EXTENSIONS OFF POSITION_INDEPENDENT_CODE ON)
add_library(lib SHARED lib.cpp)
target_link_libraries(lib foo)
add_executable(main main.cpp)
target_link_libraries(main foo dl)

However the issue does not occur in the MWE no matter how hard I try.

I traced the difference down to differing results in nm -C liblib.so | grep vtable:

  • The MWE (no ODR error) shows V vtable for Impl
  • The acutal program shows D vtable for StdStreamWriter

I guess the error stems from the difference D vs V which results in the vtables not being merged.

Where does this difference come from? When is this decided? I stripped down the link command for the shared library to the bare essentials (clang++-8 -shared -fsanitize=address,undefined -o <..> <all *.o and *.so>) but still get the D vtable instead of V vtable.

What else can I try to fix this?

Community
  • 1
  • 1
Flamefire
  • 5,313
  • 3
  • 35
  • 70
  • I suggest to file a bug in [Asan tracker](https://github.com/google/sanitizers) (they'll ask you to repro w/ latest Clang). – yugr Aug 07 '19 at 09:49
  • I wish I could create a repro that would help me investigate and probably solve the issue. But any MWE I tried doesn't run into that `D vtable` thing – Flamefire Aug 07 '19 at 11:10
  • I know it's been a year but was this Clang or GCC? – yugr May 24 '20 at 06:04
  • It is Clang, see the second last paragraph – Flamefire May 24 '20 at 10:18
  • 1
    Could you try compiling with `-mllvm -asan-use-private-alias=1` then? I'll prepare full answer if it works. – yugr May 24 '20 at 10:29
  • It seems to work in our real codebase – Flamefire May 25 '20 at 12:02
  • Could you `+1` the answer then? BTW I've deleted my answer in [another question](https://stackoverflow.com/questions/58541216/transitive-symbol-visiblity-in-c/61990271) and voted to close as dup. – yugr May 25 '20 at 16:36

1 Answers1

5

This is most likely caused by a known issue in Clang's implementation of Asan which causes it to detect false ODR violations for static data with vague linkage (normally class vtables or typeinfos).

As a workaround, try compiling with -mllvm -asan-use-private-alias=1 and maybe doing export ASAN_OPTIONS=use_odr_indicator=1 before running your code.

If this fixes your problem, please post a comment in the aforementioned issue to increase the chance that it's fixed once and for all in upstream.

yugr
  • 19,769
  • 3
  • 51
  • 96