-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
runtime: frame pointer stored past the stack pointer on arm64, resulting in inaccurate backtraces in LLDB #69032
Comments
The ARM64 calling convention is to store the frame pointer below the link register. As the LR is stored at 0(SP), we had to store FP at -8(SP). It is unfortunate, but we cannot change the calling convention at this point. However, we probably can change the libc function trampolines to use a different convention. They already don't follow the Go calling convention and the Go stack unwinder probably never looks at them. |
Yea, but couldn't the (FP, LR) pair be stored at the end (highest address) of the stack frame, like a normal C function would do? That wouldn't alter the calling convention would it? |
User assembly code depend on LR saved at 0(SP). We can't change it now. Note that for regular Go stacks, it is also unnecessary: Go functions know this and would not write to the very top word in the frame to clobber the caller's FP. The only time it matters is to call C functions. |
Ugh, that's unfortunate. Well if y'all ever make an ABI1 I guess it could be fixed there :/ |
Note that backtraces will be also be wrong when taken with sample, stackshot, ktrace, Instruments, or just about any debugging or perf tool on Mac OS. |
We could conceivably change ABIinternal and leave things as is for ABI0. Not sure how tricky that would be. |
It seems to me that the thing blocking fixing this is not the calling convention, but the expectations of assembly code. couldn't we add a flag to the TEXT declaration similar to NOFRAME, that informs the assembler that it is allowed to place the FP,LR pair in the correct place? The resulting code would still be valid ABI0, and also valid according to platform backtracers. functions without the flag would get the old behavior, so nothing should break. |
I don't think we need to change the convention for regular Go functions and Go assembly code. The system ABI requires FP be saved at a word below the LR, but it does not require it to be saved at a specific place in the stack frame, except the general assumption that the space below SP is scratch. Most tools generally don't care about the SP. Go generated code already doesn't clobber the saved frame pointer. It only matters when calling into C code, which can be handled specifically. |
Yes, the system ABI does not require the FP,LR be saved at a specific place, but it does require that it be saved inside the functions actual stack frame, not in the red zone. The red zone can be used for scratch, yes, but it must be assumed to be clobbered every time a function is called. Yes, it only matters when caling into C (or other non-go) code. Another way to mitigate would be to adjust SP down by 8 before calling any C function. |
Go version
go version go1.22.6 darwin/arm64
Output of
go env
in your module/workspace:What did you do?
Built a hello world program.
run it in a debugger.
Breakpoint on a system function which is called by the go runtime before main.
Take a backtrace
Take a look at
osinit_hack_trampoline
. It saves the frame pointer past the end of the stack.Take a look at the C function it calls. It's function prelude clobbers the frame pointer that go's prelude saved.
Continue past the prelude.
Now the backtrace is no longer valid. It has lost frames because the FP got clobbered.
What did you see happen?
Backtraces were not accurate because go's function prelude stored the frame pointer past the edge of the stack.
What did you expect to see?
Accurate backtraces.
The text was updated successfully, but these errors were encountered: