This is an archive of the discontinued LLVM Phabricator instance.

[Itanium] Emit type info names with external linkage.
ClosedPublic

Authored by EricWF on May 9 2018, 4:32 PM.

Details

Summary

The Itanium ABI requires that the type info for pointer-to-incomplete types to have internal linkage, so that it doesn't interfere with the type info once completed. Currently it also marks the type info name as internal as well. However, this causes a bug with the STL implementations, which use the type info name pointer to perform ordering and hashing of type infos.
For example:

// header.h
struct T;
extern std::type_info const& Info;

// tu_one.cpp
#include "header.h"
std::type_info const& Info = typeid(T*);

// tu_two.cpp
#include "header.h"
struct T {};
int main() {
  auto &TI1 = Info;
  auto &TI2 = typeid(T*);
  assert(TI1 == TI2); // Fails
  assert(TI1.hash_code() == TI2.hash_code()); // Fails
}

This patch fixes the STL bug by emitting the type info name as linkonce_odr when the type-info is for a pointer-to-incomplete type.

Note that libc++ could fix this without a compiler change, but the quality of fix would be poor. The library would either have to:

(A) Always perform strcmp/string hashes.
(B) Determine if we have a pointer-to-incomplete type, and only do strcmp then. This would require an ABI break for libc++.

Diff Detail

Repository
rC Clang

Event Timeline

EricWF created this revision.May 9 2018, 4:32 PM
smeenai added a subscriber: smeenai.May 9 2018, 4:40 PM
EricWF updated this revision to Diff 146034.May 9 2018, 4:54 PM
  • Correct copy-paste error.
rsmith added inline comments.May 9 2018, 5:05 PM
lib/CodeGen/ItaniumCXXABI.cpp
3033

Shouldn't this be based on the type's linkage? Eg:

namespace { struct Incomplete; }
auto x = typeid(Incomplete*);

should have an internal type info name, not a linkonce_odr name. I think we should compute the linkage per the code below and then demote the type's linkage to internal in the incomplete-class case.

3034–3035

Should we promote the type info name in this case too? (We get here for the incomplete-class type info we generate as a detail of the pointer type info, but the names of such types are accessible via the <cxx_abi.h> interface, so it seems reasonable to apply the same global-uniqueness to them too.)

EricWF marked an inline comment as done.May 9 2018, 6:08 PM

@rsmith What should the visibility of the type name be in cases where the type info is hidden?

lib/CodeGen/ItaniumCXXABI.cpp
3033

That sounds correct to me.

3034–3035

Hmm. I think it seems reasonable.

I don't think there exists a case where a user can perform type_info equality or hashing on an incomplete class type, so in terms of solving the libc++ bug I think it's unneeded. (which is why I omitted it).

I also don't see exactly what part of cxxabi.h might expose the type_info to users in a way where type name pointer equality is important to the user.

What would you rather?

EricWF updated this revision to Diff 146053.May 9 2018, 7:14 PM
EricWF marked an inline comment as done.

Address @rsmith's inline comments.

  • Calculate the linkage for the type name using the linkage for the type.
  • Promote the type name visibility for the incomplete class types as well as for pointer and member pointers.
rjmccall added inline comments.May 9 2018, 8:35 PM
lib/CodeGen/ItaniumCXXABI.cpp
3098

I think we could probably just have the function return both instead of requiring the callee to make two calls.

3098

"caller" instead of "callee", of course.

3110

This should be the name linkage, I think. The targets using non-unique RTTI names (i.e. Darwin ARM64) would want this bit to be set for the types they use non-unique RTTI names for, i.e. for external types with default visibility, even if the type is incomplete in the current translation unit and thus the type_info object has been demoted to internal linkage. (A complete type with internal linkage should not have the bit set.)

EricWF updated this revision to Diff 146065.May 9 2018, 10:03 PM
EricWF marked 3 inline comments as done.

Address @rjmccall's comments.

rjmccall accepted this revision.May 9 2018, 10:08 PM

Looks really good, thanks.

This revision is now accepted and ready to land.May 9 2018, 10:08 PM

@rjmccall Thank you for the quick review!

This revision was automatically updated to reflect the committed changes.
EricWF reopened this revision.May 10 2018, 1:23 AM

Re-opening. I had to revert the last commit since it seemed to cause error (which looked unrelated?) on ppc64le-linux.

This revision is now accepted and ready to land.May 10 2018, 1:23 AM
EricWF marked 3 inline comments as done.May 10 2018, 1:24 AM
This revision was automatically updated to reflect the committed changes.

This is causing ASan's ODR violation detector to fire. I think the problematic case looks like this:

TU 1:

struct X;
/*...*/ typeid(X*); /*...*/

TU 2:

struct X { virtual void f(); };
void X::f() {}

Now, TU 1 will emit a linkonce_odr definition of the _ZTS symbol, and TU 2 will emit a strong definition of it. ASan doesn't like that.

There's no way we can know that there will be a strong definition of the typeinfo name at the point where we emit the tentative copy for the pointer type_info. It seems to me there are two things we could do about this:

  1. decide this is not a bug and suppress the ASan ODR checker report, or
  2. decide this is a bug, and only emit linkonce_odr typeinfo names for pointers to incomplete classes, not for incomplete classes themselves (this would mean that operator== etc would do the wrong thing for users who use the <cxxabi.h> to inspect the pointee type of a pointer type info; in particular, we'd need to check that ubsan's vptr sanitizer can cope with this -- right now it assumes that it can compare the type name address except on iOS64, just like libc++'s <type_info> does).

I believe static and dynamic linkers — at least on ELF and Mach-O — will always drop weak symbols for strong ones. Now, I think that isn't LLVM's posted semantics for linkonce_odr, but to me that means that LLVM's semantics are inadequate, not that we should decline to take advantage of them.

If we can't rely on that, it probably means that the type name symbol for class types always has to be linkonce_odr, even if it's for a type with a key function.

I believe static and dynamic linkers — at least on ELF and Mach-O — will always drop weak symbols for strong ones. Now, I think that isn't LLVM's posted semantics for linkonce_odr, but to me that means that LLVM's semantics are inadequate, not that we should decline to take advantage of them.

If we can't rely on that, it probably means that the type name symbol for class types always has to be linkonce_odr, even if it's for a type with a key function.

That all makes sense to me. (But I think weak_odr would be more formally correct, even though the symbol will never actually be discarded as it's referenced from the type_info and vtable.)

The rule that ASan is using is that if there is an external definition for a global variable, there should not be any other definitions for that symbol, which seems right given LLVM's semantics, but wrong here.

For now, the simplest thing to do seem to be to weaken external linkage to weak_odr for the type info name.

The only difference between weak_odr and linkonce_odr is that the LLVM optimizers can discard linkonce_odr globals. From your description, you want to remove the odr-ness, by changing the linkage to "linkonce", I think?

That said, I don't think the usage here violates LangRef's definition of linkonce_odr; the "odr"-ness refers to the global's initializer, not its linkage.

The only difference between weak_odr and linkonce_odr is that the LLVM optimizers can discard linkonce_odr globals. From your description, you want to remove the odr-ness, by changing the linkage to "linkonce", I think?

The odr-ness is fine; the definitions are all the same. The symbol emitted with the key function should be non-discardable, which is why I'm suggesting we change the linkage to weak_odr rather than changing it to linkonce_odr.

What exactly is the asan odr checker actually checking for? The distinction between common/linkonce_odr/linkonce/weak_odr/weak only affects IR optimizers, not code generation.

What exactly is the asan odr checker actually checking for? The distinction between common/linkonce_odr/linkonce/weak_odr/weak only affects IR optimizers, not code generation.

It's checking that there is only one definition of each global variable for which only one definition is expected; it believes that if there is an external definition, then there should not be any other definitions.

I believe static and dynamic linkers — at least on ELF and Mach-O — will always drop weak symbols for strong ones. Now, I think that isn't LLVM's posted semantics for linkonce_odr, but to me that means that LLVM's semantics are inadequate, not that we should decline to take advantage of them.

If we can't rely on that, it probably means that the type name symbol for class types always has to be linkonce_odr, even if it's for a type with a key function.

That all makes sense to me. (But I think weak_odr would be more formally correct, even though the symbol will never actually be discarded as it's referenced from the type_info and vtable.)

Yes, of course.

The rule that ASan is using is that if there is an external definition for a global variable, there should not be any other definitions for that symbol, which seems right given LLVM's semantics, but wrong here.

I agree.

For now, the simplest thing to do seem to be to weaken external linkage to weak_odr for the type info name.

Yes.

Please see https://reviews.llvm.org/D47092 for the external -> weak_odr change.

Note: this commit was re-reverted in

commit bbb2655de0049f8e6cf4482702aa616011c851c1
Author: Richard Smith <richard-llvm@metafoo.co.uk>
Date:   Mon May 21 20:10:54 2018 +0000

    Revert r332028; see PR37545 for details.

    llvm-svn: 332879