Skip to content

Conversation

arrowd
Copy link
Contributor

@arrowd arrowd commented Sep 26, 2025

As it was explained to me in https://discourse.llvm.org/t/libunwinds-raison-detre/88283/2 the LLVM version of libunwind is mostly compatible with nongnu one. This change improves the compatibility a bit further.

@arrowd arrowd requested a review from a team as a code owner September 26, 2025 13:40
@llvmbot
Copy link
Member

llvmbot commented Sep 26, 2025

@llvm/pr-subscribers-libunwind

Author: Gleb Popov (arrowd)

Changes

As it was explained to me in https://discourse.llvm.org/t/libunwinds-raison-detre/88283/2 the LLVM version of libunwind is mostly compatible with nongnu one. This change improves the compatibility a bit further.


Full diff: https://github.com/llvm/llvm-project/pull/160887.diff

3 Files Affected:

  • (modified) libunwind/include/libunwind.h (+1)
  • (modified) libunwind/src/libunwind.cpp (+23)
  • (modified) libunwind/src/libunwind_ext.h (+1)
diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h
index 94928f436025a..9d2ccc4dda7fa 100644
--- a/libunwind/include/libunwind.h
+++ b/libunwind/include/libunwind.h
@@ -130,6 +130,7 @@ extern int unw_is_fpreg(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL;
 extern int unw_is_signal_frame(unw_cursor_t *) LIBUNWIND_AVAIL;
 extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *) LIBUNWIND_AVAIL;
 //extern int       unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*);
+extern const char *unw_strerror (int) LIBUNWIND_AVAIL;
 
 extern unw_addr_space_t unw_local_addr_space;
 
diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index cf39ec5f7dbdf..23335ba577e52 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -347,6 +347,29 @@ void __unw_remove_dynamic_eh_frame_section(unw_word_t eh_frame_start) {
 }
 
 #endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND)
+
+/// Maps the UNW_* error code to a textual representation
+_LIBUNWIND_HIDDEN const char *__unw_strerror(int error_code) {
+  switch (error_code) {
+  case UNW_ESUCCESS: return "no error";
+  case UNW_EUNSPEC: return "unspecified (general) error";
+  case UNW_ENOMEM: return "out of memory";
+  case UNW_EBADREG: return "bad register number";
+  case UNW_EREADONLYREG: return "attempt to write read-only register";
+  case UNW_ESTOPUNWIND: return "stop unwinding";
+  case UNW_EINVALIDIP: return "invalid IP";
+  case UNW_EBADFRAME: return "bad frame";
+  case UNW_EINVAL: return "unsupported operation or bad value";
+  case UNW_EBADVERSION: return "unwind info has unsupported version";
+  case UNW_ENOINFO: return "no unwind info found";
+#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY)
+  case UNW_ECROSSRASIGNING: return "return address require authentication";
+#endif
+  }
+  return "invalid error code";
+}
+_LIBUNWIND_WEAK_ALIAS(__unw_strerror, unw_strerror)
+
 #endif // !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__wasm__)
 
 #ifdef __APPLE__
diff --git a/libunwind/src/libunwind_ext.h b/libunwind/src/libunwind_ext.h
index 28db43a4f6eef..ed503ceb70c5a 100644
--- a/libunwind/src/libunwind_ext.h
+++ b/libunwind/src/libunwind_ext.h
@@ -42,6 +42,7 @@ extern int __unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *);
 extern int __unw_is_fpreg(unw_cursor_t *, unw_regnum_t);
 extern int __unw_is_signal_frame(unw_cursor_t *);
 extern int __unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *);
+extern const char *__unw_strerror(int);
 
 #if defined(_AIX)
 extern uintptr_t __unw_get_data_rel_base(unw_cursor_t *);

Copy link

github-actions bot commented Sep 26, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@arichardson arichardson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me.

@jroelofs
Copy link
Contributor

mind adding a test? you could probably piggyback on bad_unwind_info.pass.cpp's second assert.

@arrowd
Copy link
Contributor Author

arrowd commented Oct 1, 2025

you could probably piggyback on bad_unwind_info.pass.cpp's second assert.

Turns out it isn't possible. unw_step returns a positive value in case of success or 0 if there is nowhere to step. The nongnu libunwind's manpage says that it also may return some error codes, but it is impossible for LLVM libunwind if I read the code correctly.

Would you like me to create a separate testcase or let's just skip it?

return "no unwind info found";
#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY)
case UNW_ECROSSRASIGNING:
return "return address require authentication";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't grammatically correct. Was it copied verbatim as the error message used by another libunwind implementation or did you introduce this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, English isn't my native. I took message strings from nongnu libunwind: https://github.com/libunwind/libunwind/blob/bfc0d618f72ba4b725c3735188e0a6d8c710411c/src/mi/strerror.c#L31

Copy link
Contributor

@jroelofs jroelofs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot just lift code verbatim from another project with a different license.

@jroelofs jroelofs closed this Oct 7, 2025
@arrowd
Copy link
Contributor Author

arrowd commented Oct 7, 2025

I beg my pardon, why did you close this? Reformulating string constants is not an option?

@jroelofs
Copy link
Contributor

jroelofs commented Oct 7, 2025

The other libunwind has a different license. Copying code from that one into this one is not something we should do: #160887 (comment)

@arrowd
Copy link
Contributor Author

arrowd commented Oct 7, 2025

All right, and once again, why did you close the PR? Is it rejected? I shouldn't try to fix raised comments and improve it?

@jrtc27
Copy link
Collaborator

jrtc27 commented Oct 7, 2025

There seems to be some confusion here. As far as I can tell (and Gleb, please give explicit confirmation as to whether this is 100% accurate or not), no code has been copied (the variable names and formatting are different, it uses early return rather than variable assignment, and this even has the default case after the end of the switch rather than inside it), only the English text used for most of the error messages (possibly all but the arm64 PAC one?).

@arrowd
Copy link
Contributor Author

arrowd commented Oct 7, 2025

As far as I can tell (and Gleb, please give explicit confirmation as to whether this is 100% accurate or not), no code has been copied

That's right and it is pretty obvious from the diff.

the variable names and formatting are different, it uses early return rather than variable assignment, and this even has the default case after the end of the switch rather than inside it

It also has a LLVM-specific UNW_ECROSSRASIGNING case, which is absent in nongnu libunwind.

only the English text used for most of the error messages (possibly all but the arm64 PAC one?).

Yes, I copied string constants from nongnu libunwind, mainly because I'm unsure if there is software in the wild that depends on them. Of course, I don't mind reformulating them.

@jrtc27
Copy link
Collaborator

jrtc27 commented Oct 7, 2025

the variable names and formatting are different, it uses early return rather than variable assignment, and this even has the default case after the end of the switch rather than inside it

It also has a LLVM-specific UNW_ECROSSRASIGNING case, which is absent in nongnu libunwind.

I deliberately didn't talk about that in this part of my message because it's not relevant; adding code does not affect whether the rest of the code is a license violation.

@arrowd
Copy link
Contributor Author

arrowd commented Oct 7, 2025

I deliberately didn't talk about that in this part of my message because it's not relevant; adding code does not affect whether the rest of the code is a license violation.

Yes, but it is also a sort of evidence that I didn't just blindly copy the nongnu code.

@jrtc27
Copy link
Collaborator

jrtc27 commented Oct 7, 2025

I deliberately didn't talk about that in this part of my message because it's not relevant; adding code does not affect whether the rest of the code is a license violation.

Yes, but it is also a sort of evidence that I didn't just blindly copy the nongnu code.

Not really. If you'd copied the nongnu code verbatim and added a single case UNW_ECROSSRASIGNING: cp = "..."; break; line (well, with ifdefs) then it wouldn't have done anything to help your case. What matters is everything else.

@arrowd
Copy link
Contributor Author

arrowd commented Oct 7, 2025

I don't think this little detail is worth arguing. What's more important (at least to me) is why the PR is closed and if it is now rejected or still has a chance to get merged.

@jroelofs
Copy link
Contributor

jroelofs commented Oct 7, 2025

I closed it out of an abundance of caution because you wrote:

I took message strings from nongnu libunwind: https://github.com/libunwind/libunwind/blob/bfc0d618f72ba4b725c3735188e0a6d8c710411c/src/mi/strerror.c#L3

I want to make it clear that we CANNOT copy code out of another implementation with another license.

@jroelofs
Copy link
Contributor

jroelofs commented Oct 7, 2025

I think it is okay to implement this interface, but doing so by lifting details out of another implementation is not fine.

@arichardson
Copy link
Member

I'm not convinced that reusing the error strings counts as some form of copyright violation especially since the same value may in theory be required for API compatibility and therefore should be exempt. But I'm not a copyright lawyer.

@arrowd
Copy link
Contributor Author

arrowd commented Oct 9, 2025

I just remembered that these strings are already present in the LLVM codebase:

UNW_ESUCCESS = 0, /* no error */
UNW_EUNSPEC = -6540, /* unspecified (general) error */
UNW_ENOMEM = -6541, /* out of memory */
UNW_EBADREG = -6542, /* bad register number */
UNW_EREADONLYREG = -6543, /* attempt to write read-only register */
UNW_ESTOPUNWIND = -6544, /* stop unwinding */
UNW_EINVALIDIP = -6545, /* invalid IP */
UNW_EBADFRAME = -6546, /* bad frame */
UNW_EINVAL = -6547, /* unsupported operation or bad value */
UNW_EBADVERSION = -6548, /* unwind info has unsupported version */
UNW_ENOINFO = -6549 /* no unwind info found */
#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY)
, UNW_ECROSSRASIGNING = -6550 /* cross unwind with return address signing */
#endif

@jroelofs jroelofs reopened this Oct 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants