This is an archive of the discontinued LLVM Phabricator instance.

[clang][alias|ifunc]: Add a diagnostic for mangled names
ClosedPublic

Authored by 0xdc03 on Feb 10 2023, 8:47 PM.

Details

Summary

When an alias or ifunc attribute refers to a function name that is
mangled, a diagnostic is emitted to suggest the mangled name as a
replacement for the given function name for every matching name in the
current TU.

Fixes #59164

Diff Detail

Event Timeline

0xdc03 created this revision.Feb 10 2023, 8:47 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 10 2023, 8:47 PM
0xdc03 requested review of this revision.Feb 10 2023, 8:47 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 10 2023, 8:47 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript

Note that as it stands currently, this patch cannot be committed because the test clang/test/SemaCXX/externc-ifunc-resolver.cpp fails to run. The contents of the test are as follows:

// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-linux-gnu -verify %s

extern "C" {
__attribute__((used)) static void *resolve_foo() { return 0; }
namespace NS {
__attribute__((used)) static void *resolve_foo() { return 0; }
} // namespace NS

// FIXME: This diagnostic is pretty confusing, the issue is that the existence
// of the two functions suppresses the 'alias' creation, and thus the ifunc
// resolution via the alias as well. In the future we should probably find
// some way to improve this diagnostic (likely by diagnosing when we decide
// this case suppresses alias creation).
__attribute__((ifunc("resolve_foo"))) void foo(); // expected-error{{ifunc must point to a defined function}}
}

The error that I get is as follows:

Command Output (stderr):
--
+ : 'RUN: at line 1'
+ /mnt/entschuldigung/LLVM/llvm-main/build-release/bin/clang -cc1 -internal-isystem /mnt/entschuldigung/LLVM/llvm-main/build-release/lib/clang/17/include -nostdsysteminc -emit-llvm-only -triple x86_64-linux-gnu -verify /mnt/entschuldigung/LLVM/llvm-main/clang/test/SemaCXX/externc-ifunc-resolver.cpp
error: 'note' diagnostics seen but not expected:
  File /mnt/entschuldigung/LLVM/llvm-main/clang/test/SemaCXX/externc-ifunc-resolver.cpp Line 14: 'resolve_foo' exists as a mangled name, did you mean to use '_ZL11resolve_foov'?
  File /mnt/entschuldigung/LLVM/llvm-main/clang/test/SemaCXX/externc-ifunc-resolver.cpp Line 14: 'resolve_foo' exists as a mangled name, did you mean to use '_ZN2NSL11resolve_fooEv'?
2 errors generated.

--

which confuses me because an extern "C" block is not supposed to mangle any names, right? Appreciate any inputs on this.

0xdc03 edited the summary of this revision. (Show Details)Feb 10 2023, 8:55 PM

Note that as it stands currently, this patch cannot be committed because the test clang/test/SemaCXX/externc-ifunc-resolver.cpp fails to run. The contents of the test are as follows:

// RUN: %clang_cc1 -emit-llvm-only -triple x86_64-linux-gnu -verify %s

extern "C" {
__attribute__((used)) static void *resolve_foo() { return 0; }
namespace NS {
__attribute__((used)) static void *resolve_foo() { return 0; }
} // namespace NS

// FIXME: This diagnostic is pretty confusing, the issue is that the existence
// of the two functions suppresses the 'alias' creation, and thus the ifunc
// resolution via the alias as well. In the future we should probably find
// some way to improve this diagnostic (likely by diagnosing when we decide
// this case suppresses alias creation).
__attribute__((ifunc("resolve_foo"))) void foo(); // expected-error{{ifunc must point to a defined function}}
}

The error that I get is as follows:

Command Output (stderr):
--
+ : 'RUN: at line 1'
+ /mnt/entschuldigung/LLVM/llvm-main/build-release/bin/clang -cc1 -internal-isystem /mnt/entschuldigung/LLVM/llvm-main/build-release/lib/clang/17/include -nostdsysteminc -emit-llvm-only -triple x86_64-linux-gnu -verify /mnt/entschuldigung/LLVM/llvm-main/clang/test/SemaCXX/externc-ifunc-resolver.cpp
error: 'note' diagnostics seen but not expected:
  File /mnt/entschuldigung/LLVM/llvm-main/clang/test/SemaCXX/externc-ifunc-resolver.cpp Line 14: 'resolve_foo' exists as a mangled name, did you mean to use '_ZL11resolve_foov'?
  File /mnt/entschuldigung/LLVM/llvm-main/clang/test/SemaCXX/externc-ifunc-resolver.cpp Line 14: 'resolve_foo' exists as a mangled name, did you mean to use '_ZN2NSL11resolve_fooEv'?
2 errors generated.

--

which confuses me because an extern "C" block is not supposed to mangle any names, right? Appreciate any inputs on this.

Looks like maybe a Clang feature, that since they're "static"/internal linkage, Clang decides it can still mangle them: https://godbolt.org/z/6oMfjced1 - GCC doesn't do this, and fails if you try to overload them, whereas clang doesn't.

That does surprise me - if only for GCC/Clang compatibility, regardless of what the spec says, exactly...

nudge! at anyone who knows what to do here

Adding Erich as he's more familiar with ifunc and friends.

clang/lib/CodeGen/CodeGenModule.cpp
342

Does clang-format do this? It looks far beyond the usual 80 col limit we use.

355
359–360

Should this come with a fix-it to switch the attribute to using the mangled name instead?

Btw, these changes should come with a release note as well.

which confuses me because an extern "C" block is not supposed to mangle any names, right? Appreciate any inputs on this.

That IS strange, that has internal linkage, but so the 'extern "C"' doesn't do anything to it, so we choose to just mangle it. I guess there is some sense in that. GCC doesn't mangle EITHER of those (so leaving them both causes a multiple definition error!), but there is apparently a runtime error even trying to use an ifunc to those functions.

We COULD just prohibit ifuncs to functions without external linkage, but that is a bigger change than I'm sure I would want to commit to just yet.

As far as this patch, I think there is value with improving this diagnostic, but I'm not sure this is the one. I don't have a great idea of an alternate, BUT:

error: ifunc must point to a defined function
note: the name specified in an ifunc must refer to the <linkage name> (needs better words here for what we mean by the name it needs to call)
note: function by that name is mangled as %0
<and notes for each similar name>

I will try to come up with a better diagnostic for this issue and will try and update the patch as soon as I can.

Btw, these changes should come with a release note as well.

Are all functional changes logged in the release notes? I am not really aware of how they work.

clang/lib/CodeGen/CodeGenModule.cpp
342

It is beyond the 80 column limit because I formatted it by hand. The linting part of arc diff did not complain so it seemed fine to me. I suppose I was supposed to use git clang-format, I was not aware of it. I will use that command from now on.

359–360

I did consider it, however I have no idea how to implement that 😅

Btw, these changes should come with a release note as well.

Are all functional changes logged in the release notes? I am not really aware of how they work.

Not quite all, but somewhat close to it -- if the changes are "user facing", we usually try to write a release note for it: https://llvm.org/docs/DeveloperPolicy.html#release-notes

clang/lib/CodeGen/CodeGenModule.cpp
342
359–360

We have some documentation on that: https://clang.llvm.org/docs/InternalsManual.html#fix-it-hints

The basic idea here is that you'd create a replacement range so you can replace the old name with the new one.

erichkeane added inline comments.Feb 24 2023, 8:35 AM
clang/lib/CodeGen/CodeGenModule.cpp
359–360

Do we have to do some work to make sure we dont suggest multiple fixits? Or is that OK?

aaron.ballman added inline comments.Feb 24 2023, 8:38 AM
clang/lib/CodeGen/CodeGenModule.cpp
359–360

So long as each fix-it is associated with its own note, that's fine. We don't allow fix-its attached to notes to be automatically applied (specifically because we're either not certain the fix is correct or because there may be multiple fixes for the user to pick from).

0xdc03 updated this revision to Diff 500715.Feb 27 2023, 2:28 AM
0xdc03 edited the summary of this revision. (Show Details)
  • Update to address reviewer comments
  • Add release note
  • Redo notes to match reviewer comments
  • Make the fix-it highlight the whole attribute
  • Update tests to check new diagnostics

Okay, I have now modified the diagnostic to look something like (as per the discussion at https://discord.com/channels/636084430946959380/636732781086638081/1079356357024694363):

../../bug/ifunc-#59164.cpp:17:16: error: ifunc must point to a defined function
__attribute__((ifunc("resolver")))
               ^
../../bug/ifunc-#59164.cpp:17:16: note: the name specified in an ifunc must refer to the mangled name
../../bug/ifunc-#59164.cpp:17:16: note: function by that name is mangled as "_ZL8resolverv"
__attribute__((ifunc("resolver")))
               ^~~~~~~~~~~~~~~~~
               ifunc("_ZL8resolverv")
../../bug/ifunc-#59164.cpp:20:16: error: alias must point to a defined variable or function
__attribute__((alias("resolver")))
               ^
../../bug/ifunc-#59164.cpp:20:16: note: the name specified in an alias must refer to the mangled name
../../bug/ifunc-#59164.cpp:20:16: note: function by that name is mangled as "_ZL8resolverv"
__attribute__((alias("resolver")))
               ^~~~~~~~~~~~~~~~~
               alias("_ZL8resolverv")
../../bug/ifunc-#59164.cpp:23:24: error: ifunc must point to a defined function
__attribute__((unused, ifunc("resolver"), deprecated("hahahaha, isn't C great?"))) void func();
                       ^
../../bug/ifunc-#59164.cpp:23:24: note: the name specified in an ifunc must refer to the mangled name
../../bug/ifunc-#59164.cpp:23:24: note: function by that name is mangled as "_ZL8resolverv"
__attribute__((unused, ifunc("resolver"), deprecated("hahahaha, isn't C great?"))) void func();
                       ^~~~~~~~~~~~~~~~~
                       ifunc("_ZL8resolverv")
3 errors generated.

However, clang/test/SemaCXX/externc-ifunc-resolver.cpp still fails, and I am not entirely sure what to do with it. Should I update it to check the diagnostic? Also, it seems that the code I wrote prints "must refer to the mangled name" everywhere, even when the resolver or aliasee is not a function. Should I try to fix this? Or is it OK as it is?

Okay, I have now modified the diagnostic to look something like (as per the discussion at https://discord.com/channels/636084430946959380/636732781086638081/1079356357024694363):

../../bug/ifunc-#59164.cpp:17:16: error: ifunc must point to a defined function
__attribute__((ifunc("resolver")))
               ^
../../bug/ifunc-#59164.cpp:17:16: note: the name specified in an ifunc must refer to the mangled name
../../bug/ifunc-#59164.cpp:17:16: note: function by that name is mangled as "_ZL8resolverv"
__attribute__((ifunc("resolver")))
               ^~~~~~~~~~~~~~~~~
               ifunc("_ZL8resolverv")
../../bug/ifunc-#59164.cpp:20:16: error: alias must point to a defined variable or function
__attribute__((alias("resolver")))
               ^
../../bug/ifunc-#59164.cpp:20:16: note: the name specified in an alias must refer to the mangled name
../../bug/ifunc-#59164.cpp:20:16: note: function by that name is mangled as "_ZL8resolverv"
__attribute__((alias("resolver")))
               ^~~~~~~~~~~~~~~~~
               alias("_ZL8resolverv")
../../bug/ifunc-#59164.cpp:23:24: error: ifunc must point to a defined function
__attribute__((unused, ifunc("resolver"), deprecated("hahahaha, isn't C great?"))) void func();
                       ^
../../bug/ifunc-#59164.cpp:23:24: note: the name specified in an ifunc must refer to the mangled name
../../bug/ifunc-#59164.cpp:23:24: note: function by that name is mangled as "_ZL8resolverv"
__attribute__((unused, ifunc("resolver"), deprecated("hahahaha, isn't C great?"))) void func();
                       ^~~~~~~~~~~~~~~~~
                       ifunc("_ZL8resolverv")
3 errors generated.

However, clang/test/SemaCXX/externc-ifunc-resolver.cpp still fails, and I am not entirely sure what to do with it. Should I update it to check the diagnostic? Also, it seems that the code I wrote prints "must refer to the mangled name" everywhere, even when the resolver or aliasee is not a function. Should I try to fix this? Or is it OK as it is?

I think updating that test with this additional note is the right thing to do. As far as that note, saying 'mangled name' is perhaps not correct there, since what we really care is that it is the name-as-emitted to the linker. I don't have an idea on exactly what to call it. If we could come up with a better phrase, it probably makes the diagnostic on the thing not being a function.

ALSO, it would be worth updating the error here to mention that it must point to a defined function, <x, y, Z> (where x,y,z are the things that it is allowed to target).

I think updating that test with this additional note is the right thing to do.

Will do, will also add checks for the fix-its.

As far as that note, saying 'mangled name' is perhaps not correct there, since what we really care is that it is the name-as-emitted to the linker. I don't have an idea on exactly what to call it. If we could come up with a better phrase, it probably makes the diagnostic on the thing not being a function.

First few that come to mind:

  • ABI-mangled symbol
  • Linked symbol
  • Externally-visible name

It may also be worth splitting the diagnostic for ifuncs and aliases to be something like:

  • ifunc: the function specified in an ifunc must refer to its <mangled name>
  • alias: the function or variable specified in an alias must refer to its <mangled name>

I feel externally-visible name fits the best, though it doesn't really make sense with internal linkage.

ALSO, it would be worth updating the error here to mention that it must point to a defined function, <x, y, Z> (where x,y,z are the things that it is allowed to target).

Hmm, I do not really understand this point, could you please clarify it?

I think updating that test with this additional note is the right thing to do.

Will do, will also add checks for the fix-its.

As far as that note, saying 'mangled name' is perhaps not correct there, since what we really care is that it is the name-as-emitted to the linker. I don't have an idea on exactly what to call it. If we could come up with a better phrase, it probably makes the diagnostic on the thing not being a function.

First few that come to mind:

  • ABI-mangled symbol
  • Linked symbol
  • Externally-visible name

I don't think any of those are accurate with internal names. ABI-mangled is incorrect as the ABI says nothing about these. "Linked Symbol" isnt really accurate, as these aren't really 'linked'. AND, they aren't externally-visible. I wish I was better at this part :D Perhaps @shafik might have a better ability to bikeshed here?

It may also be worth splitting the diagnostic for ifuncs and aliases to be something like:

  • ifunc: the function specified in an ifunc must refer to its <mangled name>
  • alias: the function or variable specified in an alias must refer to its <mangled name>

I feel externally-visible name fits the best, though it doesn't really make sense with internal linkage.

ALSO, it would be worth updating the error here to mention that it must point to a defined function, <x, y, Z> (where x,y,z are the things that it is allowed to target).

Hmm, I do not really understand this point, could you please clarify it?

Exactly what you were saying above with the 'splitting the diagnostic', I was saying in less accurate words, that I'd prefer that.

I think "mangled name" is probably the closest without being overly verbose for the average user.

We do use "mangled name" in a few places already, some which look not too unrelated to this one:

clang/include/clang/Basic/DiagnosticSemaKinds.td:  "mangled name of %0 will change in C++17 due to non-throwing exception "
clang/include/clang/Basic/DiagnosticSemaKinds.td-  "specification in function signature">, InGroup<CXX17CompatMangling>;
clang/include/clang/Basic/DiagnosticSemaKinds.td-
--
clang/include/clang/Basic/DiagnosticSemaKinds.td:def err_hip_invalid_args_builtin_mangled_name : Error<
clang/include/clang/Basic/DiagnosticSemaKinds.td-    "invalid argument: symbol must be a device-side function or global variable">;
--
clang/include/clang/Basic/DiagnosticFrontendKinds.td:def err_duplicate_mangled_name : Error<
clang/include/clang/Basic/DiagnosticFrontendKinds.td:  "definition with same mangled name '%0' as another definition">;
0xdc03 updated this revision to Diff 502660.Mar 6 2023, 8:07 AM
  • Reword the diagnostic messages
  • Refactor tests to match updated diagnostic
erichkeane accepted this revision.Mar 6 2023, 8:09 AM

Not thrilled about 'mangled name' still, but I can't think of anything better after all this time, so LGTM.

This revision is now accepted and ready to land.Mar 6 2023, 8:09 AM
0xdc03 added a comment.Mar 6 2023, 8:11 AM

Okay, I have now modified the diagnostic to look like this:

../bug/ifunc-#59164.cpp:17:16: error: ifunc must point to a defined function
__attribute__((ifunc("resolver")))
               ^
../bug/ifunc-#59164.cpp:17:16: note: the function specified in an ifunc must refer to its mangled name
../bug/ifunc-#59164.cpp:17:16: note: function by that name is mangled as "_ZL8resolverv"
__attribute__((ifunc("resolver")))
               ^~~~~~~~~~~~~~~~~
               ifunc("_ZL8resolverv")
../bug/ifunc-#59164.cpp:20:16: error: alias must point to a defined variable or function
__attribute__((alias("resolver")))
               ^
../bug/ifunc-#59164.cpp:20:16: note: the function or variable specified in an alias must refer to its mangled name
../bug/ifunc-#59164.cpp:20:16: note: function by that name is mangled as "_ZL8resolverv"
__attribute__((alias("resolver")))
               ^~~~~~~~~~~~~~~~~
               alias("_ZL8resolverv")
../bug/ifunc-#59164.cpp:23:24: error: ifunc must point to a defined function
__attribute__((unused, ifunc("resolver"), deprecated("hahahaha, isn't C great?"))) void func();
                       ^
../bug/ifunc-#59164.cpp:23:24: note: the function specified in an ifunc must refer to its mangled name
../bug/ifunc-#59164.cpp:23:24: note: function by that name is mangled as "_ZL8resolverv"
__attribute__((unused, ifunc("resolver"), deprecated("hahahaha, isn't C great?"))) void func();
                       ^~~~~~~~~~~~~~~~~
                       ifunc("_ZL8resolverv")
3 errors generated.

Honestly I think this is good enough, I don't really think its worth bikeshedding over <mangled name>. I would anyways expect a user of these features to know how mangling works, so :shrug: from me.

I have also added checks for the fix-its.

0xdc03 added a comment.Mar 6 2023, 8:12 AM

Not thrilled about 'mangled name' still, but I can't think of anything better after all this time, so LGTM.

Oh wow your reply was much faster than I expected 😅. If you can, please land this patch on my behalf as I do not have commit access, using "Dhruv Chawla <dhruv263.dc@gmail.com>"

This revision was landed with ongoing or failed builds.Mar 6 2023, 8:59 AM
This revision was automatically updated to reflect the committed changes.