Issue 71196
Summary dynamic_cast to final class from thinLTO shared library incorrectly returns nullptr
Labels new issue
Assignees
Reporter nbronson
    If a polymorphic subclass is defined only in a header file, shared libraries built with `-flto=thin` give the vtable symbol LOCAL visibility. This means that there may be more than one vtable in a binary that uses this shared library. The dynamic_cast optimizations in llvm 17's libcxxapi (https://reviews.llvm.org/D138005) don't seem to expect that this can happen for final classes, causing dynamic_cast to incorrectly return nullptr.

To repro:
a.h

    struct Foo {
      virtual ~Foo();
    };
    
    struct Bar final : public Foo {
 };
    
    Foo* makeBar();

a.cc

    #include "a.h"
 
    Foo::~Foo() {}
    
    Foo* makeBar() {
      return new Bar();
    }

b.cc

    #include <cstdio>
    #include <cstring>
    
    #include "a.h"
    
    int main(int argc, char **argv) {
      Foo* f = makeBar();
      Bar* b = dynamic_cast<Bar*>(f); // b will incorrectly be nullptr
      void* v;
 memcpy(&v, static_cast<void*>(f), sizeof(v));
      printf("%p %p %p\n", f, b, v);
      delete f;
    
      f = new Bar();
 asm volatile ("":"+r"(f));
      b = dynamic_cast<Bar*>(f);
 memcpy(&v, static_cast<void*>(f), sizeof(v));
      printf("%p %p %p\n", f, b, v);
      delete f;
    
      return 0;
 }

Compile with

    clang++-17 -flto=thin -O3 -fPIC -c -o a.o a.cc
    clang++-17 -O3 -fPIC -flto=thin -shared -o liba.so a.o
 clang++-17 -O3 -o b b.cc -Wl,-rpath,$PWD liba.so

When run, the first dynamic_cast returns nullptr

    $ ./b
    0x55b5b3dc12b0 (nil) 0x7f555c037dc8
    0x55b5b3dc12b0 0x55b5b3dc12b0 0x55b5b337dd80

Changing the final clang invocation to clang++-16 avoids the issue.

readelf shows that Bar's vtable gets LOCAL visibility (in both clang 16 and 17) when `-flto=thin` is used, but WEAK in the default case

    $ readelf -Wl --syms liba.so | c++filt | grep 'for Bar'
        11: 0000000000002005     5 OBJECT  WEAK   DEFAULT   14 typeinfo name for Bar
        14: 0000000000003dd8    24 OBJECT  WEAK DEFAULT   19 typeinfo for Bar
        35: 0000000000003db8    32 OBJECT LOCAL  DEFAULT   19 vtable for Bar
        49: 0000000000002005     5 OBJECT  WEAK   DEFAULT   14 typeinfo name for Bar
        57: 0000000000003dd8    24 OBJECT  WEAK   DEFAULT   19 typeinfo for Bar
    $ clang++-17 -O3 -fPIC -c -o a.o a.cc && clang++-17 -O3 -fPIC -shared -o liba_no_lto.so a.o
    $ readelf -Wl --syms liba_no_lto.so | c++filt | grep 'for Bar'
        11: 0000000000002005     5 OBJECT  WEAK   DEFAULT 14 typeinfo name for Bar
        13: 0000000000003db0    32 OBJECT  WEAK DEFAULT   19 vtable for Bar
        15: 0000000000003dd0    24 OBJECT WEAK   DEFAULT   19 typeinfo for Bar
        47: 0000000000002005     5 OBJECT  WEAK   DEFAULT   14 typeinfo name for Bar
        55: 0000000000003db0    32 OBJECT  WEAK   DEFAULT   19 vtable for Bar
 57: 0000000000003dd0    24 OBJECT  WEAK   DEFAULT   19 typeinfo for Bar

I am using `Ubuntu clang version 17.0.4 (++20231025123955+afbe3549af4d-1~exp1~20231025124009.57)`
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to