Bug 25940 - ld.bfd, clang’s ubsan, shared libraries, and virtual tables do not work together
Summary: ld.bfd, clang’s ubsan, shared libraries, and virtual tables do not work together
Status: RESOLVED MOVED
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.35
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-05-07 14:04 UTC by dilyan.palauzov@aegee.org
Modified: 2020-05-15 19:49 UTC (History)
0 users

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description dilyan.palauzov@aegee.org 2020-05-07 14:04:54 UTC
I have ld.bfd 2.34.50.20200506, ld.gold 2.34.50.20200506, gcc/g++ 9.3.1 20200506, ld.lld 10.0.0, clang(++) 10.0.0, z.cpp:
  #include <stdio.h>
  #include <stdbool.h>
  #include <string>

  struct x {
    std::string x;
  };

  struct z: virtual x {
    z() {
      bool b = 99;
      printf("a %i\n", b);
    }
  };

  extern "C" {
    void y();
  }

  void y() {
    const x x1 = z();
  }

and a.c:
  void y();

  int main() {
    y();
  }

With --- CLANG ---

> clang++ -shared -fsanitize=address,undefined z.cpp -fpic -o libz.so
> nm -D libz.so|grep san
<                 U __asan_init
<                 U __asan_option_detect_stack_use_after_return
<                 U __asan_register_globals
<                 U __asan_report_load8
<                 U __asan_report_store8
<                 U __asan_stack_malloc_2
<                 U __asan_unregister_globals
<                 U __asan_version_mismatch_check_v8
<                 U __ubsan_handle_dynamic_type_cache_miss
<                 U __ubsan_handle_load_invalid_value
<                 U __ubsan_handle_type_mismatch_v1
<                 U __ubsan_vptr_type_cache
> clang -fsanitize=address,undefined -o b b.c -L. -lz -fuse-ld=bfd
< /usr/local/bin/ld.bfd: ./libz.so: undefined reference to `__ubsan_vptr_type_cache'
< /usr/local/bin/ld.bfd: ./libz.so: undefined reference to `__ubsan_handle_dynamic_type_cache_miss'
<  clang-10: error: linker command failed with exit code 1 (use -v to see invocation)

But if I remove the class conversions from z.cpp, then libz.so does not contains __ubsan_vptr_type_cache as Undefined symbol, while it contains __ubsan_handle_load_invalid_value, and then the linking clang+ld.bfd does work
> clang -fsanitize=address,undefined -o b b.c -L. -lz -fuse-ld=bfd -lubsan
< (No error, no warning)  
> LD_LIBRARY_PATH=. ./b
< a 1
> clang -fsanitize=address,undefined -o b b.c -L. -lz -fuse-ld=gold
< /usr/local/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_area'
> LD_LIBRARY_PATH=. ./b
< ./b: symbol lookup error: ./libz.so: undefined symbol: __ubsan_vptr_type_cache
> clang -fsanitize=address,undefined -o b b.c -L. -lz -fuse-ld=gold -lubsan
< /usr/local/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_area'
> LD_LIBRARY_PATH=. ./b
< a 1
> clang -fsanitize=address,undefined -o b b.c -L. -lz -fuse-ld=lld
< (No error, no warning)
> LD_LIBRARY_PATH=. ./b
< ./b: symbol lookup error: ./libz.so: undefined symbol: __ubsan_vptr_type_cache
> clang -fsanitize=address,undefined -o b b.c -L. -lz -fuse-ld=lld -lubsan
< (No error, no warning)
> LD_LIBRARY_PATH=. ./b
< a 1

--- GCC ---
> g++ -shared -fsanitize=address,undefined z.cpp -fpic -o libz.so
> nm -D libz.so|grep san
<                 U __asan_handle_no_return
<                 U __asan_init
<                 U __asan_option_detect_stack_use_after_return
<                 U __asan_register_globals
<                 U __asan_report_load8
<                 U __asan_report_store8
<                 U __asan_stack_malloc_2
<                 U __asan_unregister_globals
<                 U __asan_version_mismatch_check_v8
<                 U __ubsan_handle_dynamic_type_cache_miss
<                 U __ubsan_handle_pointer_overflow
<                 U __ubsan_handle_type_mismatch_v1
<                 U __ubsan_vptr_type_cache
> gcc -fsanitize=address,undefined -o b b.c -L. -lz -fuse-ld=bfd
< (No error, no warning)
> LD_LIBRARY_PATH=. ./b
< a 1
> gcc -fsanitize=address,undefined -o b b.c -L. -lz -fuse-ld=gold
< (No error, no warning)
> LD_LIBRARY_PATH=. ./b
< a 1
> gcc -fsanitize=address,undefined -o b b.c -L. -lz -fuse-ld=lld
< (No error, no warning)
> LD_LIBRARY_PATH=. ./b
< a 1

• Why does clang+ld.bfd produce an error when using ubsan with class conversions?
• Why do I have to add in clang+ld.bfd -lubsan to get rid of the warning?
• Why does clang+ld.bfd does not produce an error when ubsan does no class conversions?
• Why does clang+ld.gold produce a warning?

Note that I have LLVMGold.so in /usr/local/lib, but not in /usr/local/lib/bfd-plugins.  It is therefore not used by the linker (and this LLVMGold.so is for LLVM 8, as I forgot te complice LLVM 10 with the linker plugin).
Comment 1 Andreas Schwab 2020-05-07 14:31:08 UTC
Looks like you are mixing incompatible versions of libubsan.
Comment 2 dilyan.palauzov@aegee.org 2020-05-07 14:48:23 UTC
Indeed, libubsan is from gcc.  Does clang have its own libubsan and do make install of gcc and make install of clang both write libubsan under /usr/local/lib64?
Comment 3 dilyan.palauzov@aegee.org 2020-05-08 20:15:28 UTC
libubsan comes from gcc: after compiling and installig clang/llvm, no libubsan is installed.  So it is not possible to link with the wrong libubsan, when using clang, as no such linking is supposed to be done.

I have installed the LLVMgold.so plugin for clang 10 under ${libdir}/bfd-plugins and I created one more file, e.cpp:
  #include <stdio.h>
  #include <stdbool.h>
  #include <string>

  struct x {
    std::string x;
  };

  struct z : virtual x {
    z() {
      bool b = 99;
      printf("a %i\n", b);
    }
  };

  extern "C" {
    void y();
  }

  void y() {
    const z x1 = z();
  }

It differs from z.cpp only in:
e.cpp:   const z x1 = z();
z.cpp:   const x x1 = z();

The implication of this difference is, that after compiling e.cpp and z.cpp into a DSO with
clang++ -fsanitize=address,undefined -shared -fpic -o libz.so z.cpp
clang++ -fsanitize=address,undefined -shared -fpic -o libe.so e.cpp
and then comparing the output of "nm -CDP libz.so" with "nm -CDP libe.so",
only in libz.so:
__asan_report_load8 U
__asan_stack_malloc_2 U
__ubsan_handle_dynamic_type_cache_miss U
__ubsan_vptr_type_cache U
Only in libe.so
__asan_stack_malloc_1 U
and other differences, not related to sanitizers.

Then I call:
  #!/usr/local/bin/bash
  for i in e z; do
    for linker in bfd lld gold; do
      for sanitizer in address undefined "address,undefined"; do
        echo "input $i clang linker $linker sanitizer $sanitizer"
        rm lib$i.so b -f
        clang++  -fsanitize=$sanitizer -fuse-ld=$linker -shared -fpic -o lib$i.so $i.cpp \
        && clang -fsanitize=$sanitizer -fuse-ld=$linker -L. -l$i b.c -o b && LD_LIBRARY_PATH=. ./b
        echo "input $i gcc   linker $linker sanitizer $sanitizer"
        rm lib$i.so b -f
        g++      -fsanitize=$sanitizer -fuse-ld=$linker -shared -fpic -o lib$i.so $i.cpp \
        && gcc   -fsanitize=$sanitizer -fuse-ld=$linker -L. -l$i b.c -o b && LD_LIBRARY_PATH=. ./b
      done
    done
  done

The gcc and llvm linker plugins under libdir/bfd-plugins play no role for the output, which is:

input e clang linker bfd sanitizer address
a 1
input e gcc   linker bfd sanitizer address
a 1
input e clang linker bfd sanitizer undefined
a 1
input e gcc   linker bfd sanitizer undefined
a 1
input e clang linker bfd sanitizer address,undefined
a 1
input e gcc   linker bfd sanitizer address,undefined
a 1
input e clang linker lld sanitizer address
a 1
input e gcc   linker lld sanitizer address
a 1
input e clang linker lld sanitizer undefined
a 1
input e gcc   linker lld sanitizer undefined
a 1
input e clang linker lld sanitizer address,undefined
a 1
input e gcc   linker lld sanitizer address,undefined
a 1
input e clang linker gold sanitizer address
/usr/local/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_area'
a 1
input e gcc   linker gold sanitizer address
a 1
input e clang linker gold sanitizer undefined
a 1
input e gcc   linker gold sanitizer undefined
a 1
input e clang linker gold sanitizer address,undefined
/usr/local/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_area'
a 1
input e gcc   linker gold sanitizer address,undefined
a 1
input z clang linker bfd sanitizer address
a 1
input z gcc   linker bfd sanitizer address
a 1
input z clang linker bfd sanitizer undefined
/usr/local/bin/ld.bfd: ./libz.so: undefined reference to `__ubsan_vptr_type_cache'
/usr/local/bin/ld.bfd: ./libz.so: undefined reference to `__ubsan_handle_dynamic_type_cache_miss'
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)
input z gcc   linker bfd sanitizer undefined
a 1
input z clang linker bfd sanitizer address,undefined
/usr/local/bin/ld.bfd: ./libz.so: undefined reference to `__ubsan_vptr_type_cache'
/usr/local/bin/ld.bfd: ./libz.so: undefined reference to `__ubsan_handle_dynamic_type_cache_miss'
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)
input z gcc   linker bfd sanitizer address,undefined
a 1
input z clang linker lld sanitizer address
a 1
input z gcc   linker lld sanitizer address
a 1
input z clang linker lld sanitizer undefined
./b: symbol lookup error: ./libz.so: undefined symbol: __ubsan_vptr_type_cache
input z gcc   linker lld sanitizer undefined
a 1
input z clang linker lld sanitizer address,undefined
./b: symbol lookup error: ./libz.so: undefined symbol: __ubsan_vptr_type_cache
input z gcc   linker lld sanitizer address,undefined
a 1
input z clang linker gold sanitizer address
/usr/local/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_area'
a 1
input z gcc   linker gold sanitizer address
a 1
input z clang linker gold sanitizer undefined
./b: symbol lookup error: ./libz.so: undefined symbol: __ubsan_vptr_type_cache
input z gcc   linker gold sanitizer undefined
a 1
input z clang linker gold sanitizer address,undefined
/usr/local/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_area'
./b: symbol lookup error: ./libz.so: undefined symbol: __ubsan_vptr_type_cache
input z gcc   linker gold sanitizer address,undefined
a 1

Thus, without the type conversions 'const x x1 = z();' the only problem is the GOLD report “/usr/local/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_area'".

What does this mean?

GCC is always clear.

With the type conversion “const x x1 = z();" and clang and undefined (with or without address):

With gold is reported “./b: symbol lookup error: ./libz.so: undefined symbol: __ubsan_vptr_type_cache” at execution time.

With lld is reported “./b: symbol lookup error: ./libz.so: undefined symbol: __ubsan_vptr_type_cache” at execution time.

With bfd is reported “
/usr/local/bin/ld.bfd: ./libz.so: undefined reference to `__ubsan_vptr_type_cache'
/usr/local/bin/ld.bfd: ./libz.so: undefined reference to `__ubsan_handle_dynamic_type_cache_miss'
clang-10: error: linker command failed with exit code 1 (use -v to see invocation)” at link time.

To be honest, I think this is a problem with clang, as with the same parameters gcc works, and gold and lld misbehave in supressing errors, but I am not 100% sure, as I do not understand ELF.  So where is the problem exactly?
Comment 4 dilyan.palauzov@aegee.org 2020-05-08 20:41:18 UTC
I created one more file, b.cpp:

extern "C" {
void y();
}

int main() {
  y();
}

It differs from b.c only in the file extension and y() is under “extern "C"”.

Doing the iterations with z.cpp and e.cpp, but replacing the executable source from b.c to b.cpp makes all ubsan warning disappear, except:

input z clang linker gold sanitizer address
/usr/local/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_area'

To sum up, when a DSO is written in C++ and does class conversions, then the DSO has at the end undefined symbols “__ubsan_vptr_type_cache” and “`__ubsan_handle_dynamic_type_cache_miss”.  These symbols are resolved by clang++, when the DSO is linked towards a C++ code, but not resolved by clang (withouth ++) when the DSO is linked towards C code.  Moreover, ld.bfd report problem a problem at link time, while with gold and lld the report is at runtime. 

With the address sanitizer and clang, gold reports one additional warning, which cause I cannot figure out.

So why does the linker resolve “__ubsan_vptr_type_cache” and “`__ubsan_handle_dynamic_type_cache_miss” when it is called from clang++, but not when called from clang? It is valid to link a DSO with extern "C" exported functions with a .c code.
Comment 5 dilyan.palauzov@aegee.org 2020-05-11 20:00:56 UTC
I asked at https://sourceware.org/bugzilla/show_bug.cgi?id=25975 why gold prints “/usr/local/bin/ld.gold: warning: Cannot export local symbol '__asan_extra_spill_
area'” when used by clangs’ address sanitizer.
Comment 6 dilyan.palauzov@aegee.org 2020-05-15 19:49:08 UTC
Closing, as I think this is a problem of clang: https://bugs.llvm.org/show_bug.cgi?id=45948 

In particular, while

  $ clang -fsanitize=undefined -L. -lz a.c

does not work, calling

  $ clang   -fsanitize=undefined -c -o a.o a.c
  $ clang++ -fsanitize=undefined -L. -lz a.o

works!