Skip to content

[libunwind][AIX] Fix up TOC register if unw_getcontext is called from a different module #66549

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 19, 2023

Conversation

xingxue-ibm
Copy link
Contributor

@xingxue-ibm xingxue-ibm commented Sep 15, 2023

unw_getcontext saves the caller's registers in the context. However, if the caller of unw_getcontext is in a different module, the glue code of unw_getcontext sets the TOC register (r2) with the new TOC base and saves the original TOC register value in the stack frame. This causes the incorrect TOC value is used when the caller steps up frames, which fails libunwind LIT test case unw_resume.pass.cpp. This PR fixes the problem by using the original TOC register value saved in the stack if the caller is in a different module and enables unw_resume.pass.cpp on AIX.

is called from a different module. Fix it up by using the original TOC
register saved in the stack frame.
@llvmbot
Copy link
Member

llvmbot commented Sep 15, 2023

@llvm/pr-subscribers-libunwind

Changes

unw_getcontext saves the caller's registers in the context. However, if the caller of unw_getcontext is in a different module, the glue code of unw_getcontext sets the TOC register (r2) with the new TOC base and saves the original TOC register value in the stack frame. This causes the incorrect TOC value is used when the caller steps up frames, which fails libunwind LIT test case unw_resume.pass.cpp'. This PR fixes the problem by using the original TOC register value saved in the stack if the caller is in a different module and enables unw_resume.pass.cpp' on AIX.

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

2 Files Affected:

  • (modified) libunwind/src/UnwindRegistersSave.S (+27-2)
  • (modified) libunwind/test/unw_resume.pass.cpp (-3)
diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S
index 58ffd1b9e1fb35a..f47b38ff848f729 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -305,9 +305,22 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
   mflr  0
   std   0, PPC64_OFFS_SRR0(3) // store lr as ssr0
   PPC64_STR(1)
+  PPC64_STR(4)      // Save r4 first since it will be used for fixing r2.
+#if defined(_AIX)
+  // The TOC register (r2) was changed by the glue code if unw_getcontext
+  // is called from a different module. Save the original TOC register
+  // in the context if this is the case.
+  mflr  4
+  lwz   4, 0(4)     // Get the first instruction at the return address.
+  lis   0, 0xe841   // Is it reloading the TOC register "ld 2,40(1)"?
+  ori   0, 0, 0x28
+  cmpw  0, 0, 4
+  bne   0, LnoR2Fix // No need to fix up r2 if it is not.
+  ld    2, 40(1)    // Use the saved TOC register in the stack.
+LnoR2Fix:
+#endif
   PPC64_STR(2)
   PPC64_STR(3)
-  PPC64_STR(4)
   PPC64_STR(5)
   PPC64_STR(6)
   PPC64_STR(7)
@@ -547,9 +560,21 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
   mflr    0
   stw     0,   0(3) // store lr as ssr0
   stw     1,  12(3)
+  stw     4,  24(3) // Save r4 first since it will be used for fixing r2.
+#if defined(_AIX)
+  // The TOC register (r2) was changed by the glue code if unw_getcontext
+  // is called from a different module. Save the original TOC register
+  // in the context if this is the case.
+  mflr    4
+  lwz     4,  0(4)      // Get the instruction at the return address.
+  xoris   4,  4, 0x8041 // Is it reloading the TOC register "lwz r2,20(r1)"?
+  cmplwi  4,  0x14
+  bne     0,  LnoR2Fix  // No need to fix up r2 if it is not.
+  lwz     2,  20(1)     // Use the saved TOC register in the stack.
+LnoR2Fix:
+#endif
   stw     2,  16(3)
   stw     3,  20(3)
-  stw     4,  24(3)
   stw     5,  28(3)
   stw     6,  32(3)
   stw     7,  36(3)
diff --git a/libunwind/test/unw_resume.pass.cpp b/libunwind/test/unw_resume.pass.cpp
index 76273e4a8ef0a71..08e8d4edeaf2927 100644
--- a/libunwind/test/unw_resume.pass.cpp
+++ b/libunwind/test/unw_resume.pass.cpp
@@ -10,9 +10,6 @@
 // Ensure that unw_resume() resumes execution at the stack frame identified by
 // cursor.
 
-// TODO: Investigate this failure on AIX system.
-// XFAIL: target={{.*}}-aix{{.*}}
-
 // TODO: Figure out why this fails with Memory Sanitizer.
 // XFAIL: msan
 

- use the same code for 32- and 64-bit comparison.
Copy link
Contributor

@stephenpeckham stephenpeckham left a comment

Choose a reason for hiding this comment

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

No further comments

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.

3 participants