<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/71196>71196</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            dynamic_cast to final class from thinLTO shared library incorrectly returns nullptr
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          nbronson
      </td>
    </tr>
</table>

<pre>
    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)`
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzUV0tv4zYQ_jX0ZWCDoqzXwQc_YmDbANlDFjkuKIqy2FKkQFJOvIf97QUpO7YcJ02LtkAXWDmiZj7ODD_Og1ordorzBUpWKNlMaO8abRaqNFpZrSalrg6LLzVQ6LQ8tNp0jWBg-5JJai0ICxWvheIVaCUPIBRQaDituIFaSI7IGmxDDa9AitJQI7iFshfSwbNwDaAUT2vpNIo3rhEKpRh2Ys_BNRz2jpaSgz20pZZw_7Be3sNeWFEKKdxhBo-NsNByqiy4hjqvYzi09AAlh1Ybj0IVaPUKFawrhaLmMKj0lntlYcdGBnAO1UHRVrDvjFoHunOiFT-oE1pZjyTlvoUoQySzXo29vNBOACJ541xnUbxEZIvI1vC94M925sVn2uwQ2W6iOMc4QaSASitEMgeW8xacBv7SceZO_ggLjCpoaNdxBbX2MVVUQog9tz64jPZWqN3YVqdBKKaN4czJAxjueqNA9VJ2zswQ3iC8HJ6PGgzvjPbmhgU6ay6_AwBYZ3rmYKs1oGx1XgfYC-N6KuHnVmtEckQKFF8IoGwzfr-GXNGTRyheQteXUrCrfd7HCHsuoaW_8xU1V9ufnGHs2htEYqGY7CsOiBDvLiGnvUbY8RLFy7NnwaZs81lLrkN1OgP-DG-tPQXrwtTyT0yP18y6SmgU330gYITajSU-E4eRnFAOWioUIrn_k5odC7xrqAFElogsqdntb3h8jEoNKN68c0qDYFhfQhkEL3mM4vXwzbtA8npQheFeQQnPQsoR0Ut-IvmIpVpUfoP9ed-Wt6w7BGvSfchRjrrzrkeNi229iPjBdY1Ivg8L1150RihXB0iCSNLB6wMlaxUW11D7R-kf-7cIFZfccajf4TscQ_kOhahtYa8ldUL6Ex3MICENEURWJvwcnbne-bOh_19H73gD8Vng6sqtddv54PnCdH31mKRqh8gKkdU0yuCyZsH0IYZp_fXLGqYMphrozP8_Xd9b2meFEc6xBk21ryd0ZgPUEeUWhIYSfJ6A6ZNEZD01HXUNImtE5l-fNieQS1eeGq7A9MrH0JfYWhjrxrVjCJMdX6TLtDGHGSLb8ryEX5KkTMq4YhEpseeeEtInBPyS1UmSMBxnFcvfVXjnNc6qKsejA2qo2vliN5h-rIRqB0LtNQuV2Ze-UahSoJ6PNugIa3s-qn_GtyqyBtvo52MbEXif2VPPsOPOvmk-IGRDKLVrjib4jVQV-oECnn2Yb7Q2wvqOowo07h083S1_9Z2EN63iNe2lA0YtvxXzk6HTJwnTqT209pUkKFsDG_ytfWPl33eG--uT-Z5hcOjyKgBEka-4-OIfwTgJnxJ4WP1yt36EwT6Azd12-e3-0WvNwR06LlStQdGWwwl-jD2_xo6rKvdfyHyMfYFcnJFvgsbJW9AygMbkBDqc0hj1eIo3MefFvxeEJPtsEOCzYfA0eD-TjFIPIJIikn4gPs4135X-Lp0ep5yPmXeh8l8R8JP0i28wBY-ZcoN-HxElukG-Co8P86-dZaDfG4r8g_S7dV1uBuEz9-UmmfHfJvPw_AK0hWF4QSn-VvbK9cdkuufG-mQeZTM8mw_djGcXwSSOMEkiEhdJgsiK1iWPk3lB63k1jX7yly76eRaaY1zMEp-RUYon1SKuirigE76I0qLAJEtjMmkWeVQVeU4TTFNWJimfk4jWSZxWeZLGOeYTsQiQEY6jFGe4mGUJy6L5PMqLlKRRRNAc85YK-TrjTUKhWWRRVKQTSUsubZivCfHNW_joW5pkMzELrzMt-51FcyyFdedJceKEk3xxPdldjIBQG936OVHdPz5cjbA3JsDXoj7pjVyMx9SdcE1fzphuEdl6C44_087o3zhziGyD3RaRbfDrjwAAAP__4fKBUg">