Skip to content
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

[PowerPC] missing CFI for ELF32 to unwind cr2, cr3, cr4 #83094

Closed
kernigh opened this issue Feb 27, 2024 · 1 comment · Fixed by #83098
Closed

[PowerPC] missing CFI for ELF32 to unwind cr2, cr3, cr4 #83094

kernigh opened this issue Feb 27, 2024 · 1 comment · Fixed by #83098

Comments

@kernigh
Copy link
Contributor

kernigh commented Feb 27, 2024

For PowerPC ELF 32-bit targets, LLVM forgets to provide CFI like .cfi_offset cr2, -88 for fields cr2, cr3, cr4 in the condition register. Function calls must preserve cr2, cr3, cr4. Because the CFI is missing, an unwinder (like libunwind) can't restore cr2, cr3, cr4, when it unwinds a function call.

I found the bug after OpenBSD/macppc's clang 16.0.6 miscompiled gdb, when a C++ exception crashed gdb by not restoring cr2. I reproduced the bug in 19.0.0git with this example,

struct flip {
  int *p;
  flip(int *a): p(a) { if (p) *p = 1; }
  ~flip() { if (p) *p = 0; }
};
#define N __attribute__((noinline))
int i;
N void flip2(int *p) { flip f(p); throw 0; }
N void flip1(int *p) { flip f(p); flip2(&i); }
int main() { try { flip1(nullptr); } catch(int) {} }

I compiled it with clang++ -O2. The example is small enough to optimize out cr2; I added noinline to keep cr2 in the code. The functions flip1 and flip2 each use cr2 to test some pointer p, which is null in flip1 and non-null in flip2. The C++ exception from throw 0 unwinds flip2 without restoring cr2, then runs the C++ destructor ~flip in flip1 with a wrong cr2, so if (p) *p = 0 crashes by writing to a null p.

I will soon add a pull request.

@llvmbot
Copy link
Member

llvmbot commented Feb 27, 2024

@llvm/issue-subscribers-backend-powerpc

Author: George Koehler (kernigh)

For PowerPC ELF 32-bit targets, LLVM forgets to provide CFI like `.cfi_offset cr2, -88` for fields cr2, cr3, cr4 in the condition register. Function calls must preserve cr2, cr3, cr4. Because the CFI is missing, an unwinder (like libunwind) can't restore cr2, cr3, cr4, when it unwinds a function call.

I found the bug after OpenBSD/macppc's clang 16.0.6 miscompiled gdb, when a C++ exception crashed gdb by not restoring cr2. I reproduced the bug in 19.0.0git with this example,

struct flip {
  int *p;
  flip(int *a): p(a) { if (p) *p = 1; }
  ~flip() { if (p) *p = 0; }
};
#define N __attribute__((noinline))
int i;
N void flip2(int *p) { flip f(p); throw 0; }
N void flip1(int *p) { flip f(p); flip2(&i); }
int main() { try { flip1(nullptr); } catch(int) {} }

I compiled it with clang++ -O2. The example is small enough to optimize out cr2; I added noinline to keep cr2 in the code. The functions flip1 and flip2 each use cr2 to test some pointer p, which is null in flip1 and non-null in flip2. The C++ exception from throw 0 unwinds flip2 without restoring cr2, then runs the C++ destructor ~flip in flip1 with a wrong cr2, so if (p) *p = 0 crashes by writing to a null p.

I will soon add a pull request.

kernigh added a commit to kernigh/llvm-project that referenced this issue Mar 2, 2024
Delete the code that skips the CFI for the condition register on
ELF32.  The code checked !MustSaveCR, which happened only when
Subtarget.is32BitELFABI(), where spillCalleeSavedRegisters is spilling
cr in a different way.  The spill was missing CFI.  After deleting
this code, a spill of cr2 to cr4 gets CFI in the same way as a spill
of r14 to r31.

Fixes llvm#83094
brad0 pushed a commit that referenced this issue Mar 3, 2024
Delete the code that skips the CFI for the condition register on ELF32.
The code checked !MustSaveCR, which happened only when
Subtarget.is32BitELFABI(), where spillCalleeSavedRegisters is spilling
cr in a different way. The spill was missing CFI. After deleting this
code, a spill of cr2 to cr4 gets CFI in the same way as a spill of r14
to r31.

Fixes #83094
llvmbot pushed a commit to llvmbot/llvm-project that referenced this issue Mar 23, 2024
Delete the code that skips the CFI for the condition register on ELF32.
The code checked !MustSaveCR, which happened only when
Subtarget.is32BitELFABI(), where spillCalleeSavedRegisters is spilling
cr in a different way. The spill was missing CFI. After deleting this
code, a spill of cr2 to cr4 gets CFI in the same way as a spill of r14
to r31.

Fixes llvm#83094

(cherry picked from commit 6b70c5d)
tstellar pushed a commit to llvmbot/llvm-project that referenced this issue Mar 27, 2024
Delete the code that skips the CFI for the condition register on ELF32.
The code checked !MustSaveCR, which happened only when
Subtarget.is32BitELFABI(), where spillCalleeSavedRegisters is spilling
cr in a different way. The spill was missing CFI. After deleting this
code, a spill of cr2 to cr4 gets CFI in the same way as a spill of r14
to r31.

Fixes llvm#83094

(cherry picked from commit 6b70c5d)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants