libcxx: support typeids across DLL boundary

Reid Kleckner rnk at google.com
Mon Nov 25 11:02:11 PST 2013


On Sat, Nov 23, 2013 at 2:49 AM, Yaron Keren <yaron.keren at gmail.com> wrote:

> In MSVC raw_name does return a unique name:
>
> tu1().raw_name: .?AUFoo@?A0x08cd7644@@
> tu2().raw_name: .?AUFoo@?A0x8e5904ea@@
>
> In gcc these are the same.
>
> libsupc++ code compares pointer to type, pointers to the name or the name
> itself depending upon defines. It says:
>
> // Determine whether typeinfo names for the same type are merged (in which
> // case comparison can just compare pointers) or not (in which case strings
> // must be compared), and whether comparison is to be implemented inline or
> // not.  We used to do inline pointer comparison by default if weak symbols
> // are available, but even with weak symbols sometimes names are not merged
> // when objects are loaded with RTLD_LOCAL, so now we always use strcmp by
> // default.  For ABI compatibility, we do the strcmp inline if weak symbols
> // are available, and out-of-line if not.  Out-of-line pointer comparison
> ...
> // Even with the new abi, on systems that support dlopen
> // we can run into cases where type_info names aren't merged,
> // so we still need to do string comparison.
>
> The problem goes beyond anonymous namespace. Without them, the catch
> handler will catch based on  name only, leading to interesting C-like bugs.
>

Without the anonymous namespace, you have an ODR violation in your program.


>  x1.cpp:
>
> #include <typeinfo>
> struct Foo { int a; int b; Foo(int a_, int b_) : a(a_), b(b_) {} };
> extern "C" void printf(const char *, ...);
> const std::type_info &tu1() { return typeid(Foo); }
> __declspec(dllexport) void x1() {
>   printf("tu1().name: %s,%x\n", tu1().name(), (unsigned)tu1().name());
>   throw Foo(12,34);
> }
>
> x2.cpp:
>
> #include <typeinfo>
> struct Foo { double x; };
> extern void x1();
> extern "C" void printf(const char *, ...);
> const std::type_info &tu2() { return typeid(Foo); }
> int main() {
>   printf("tu2().name: %s,%x\n", tu2().name(), (unsigned)tu2().name());
>   try { x1(); }
>   catch (Foo F) { printf("Caught Foo x=%g\n", F.x); }
>   return 0;
> }
>
> with either gcc 4.8.1 or Visual C++ 2012 gets bad result:
>
> gcc -shared x1.cpp -o x1.dll -lstdc++ && gcc x2.cpp x1.dll -o x1.exe
> -lstdc++ && ./x1
> tu2().name: 3Foo,4088b8
> tu1().name: 3Foo,69404188
> Caught Foo x=7.21479e-313
>
> cl -EHsc  -LD x1.cpp -Fex1.dll && cl -EHsc x2.cpp x1.lib && x2
> tu2().name: struct Foo,5be448
> tu1().name: struct Foo,5be460
> Caught Foo x=7.21479e-313
>
> (without a namespace, VC raw_name() are identical)
>
> The problem is nothing encodes the internal structure of Foo. Maybe the
> typeid could be a 64-bit hash based on everything relevant: namespace,
> name, members and then it would be the same even for different TU and could
> be used in all situations.
>
> Yaron
>
>
>
>
>
>
>
> 2013/11/23 Reid Kleckner <rnk at google.com>
>
>> On Fri, Nov 22, 2013 at 3:08 PM, Yaron Keren <yaron.keren at gmail.com>wrote:
>>
>>> MSVC is confused by the same-named source file!
>>> If the code is split into  t1.cpp:
>>>
>>
>> Oh, right, I discovered that yesterday.  It's really annoying,
>> considering that my standard testing methodology is to use the same file
>> compiled twice.  :(
>>
>>
>>> However, your point IS valid, since when the actual names are printed
>>> (names which will be used in the strcmp):
>>>
>>>   printf("tu1().name: %s\n", tu1().name());
>>>   printf("tu2().name: %s\n", tu2().name());
>>>
>>> with MSVC we get
>>>
>>> tu1() == tu2(): 0
>>> tu1().name: struct `anonymous namespace'::Foo
>>> tu2().name: struct `anonymous namespace'::Foo
>>>
>>> and with gcc:
>>>
>>> tu1() == tu2(): 0
>>> tu1().name: *N12_GLOBAL__N_13FooE
>>> tu2().name: *N12_GLOBAL__N_13FooE
>>>
>>> So when comparing type ids only, we'll miss exceptions from DLLs, while
>>> if we compare type names as well we may catch a same-named class coming
>>> from the same namespace even it's actually something different.
>>>
>>> Given only these two options (maybe there is a better solution?), I'd
>>> still go with the first, since many C++ DLL libraries throw exceptions
>>> which *must* be caught, else the library is not usable. This is a
>>> showstopper.
>>>
>>> OTOH, same-named classes - in the same namespace - in different TU are
>>> probably uncommon and throwing them around where they might be confused is
>>> probably even less common.
>>>
>>
>> MSDN says MSVC lets you get at the mangled/decorated name with
>> .raw_name(), and this should be good enough, barring people like me
>> recompiling the same source file with different macros.  :)
>>
>> I don't see how to fix this for gcc, though, if they don't surface a
>> unique mangled name for types in anonymous namespaces.
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20131125/9135df06/attachment.html>


More information about the cfe-commits mailing list