<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">