golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.33k stars 17.58k forks source link

cmd/internal/obj: rework stack unwind metadata on LR machines #53609

Open cherrymui opened 2 years ago

cherrymui commented 2 years ago

On LR machines, our function prologue generally looks like (stack bounds check and frame pointer handling omitted): store the LR at -frame_size(SP), then decrement SP to SP-frame_size (if they cannot be done on the same instruction, either due to the lack of the instruction on the architecture or the stack frame too large).

If we decrement the SP first, if a signal arrives immediately after the instruction before saving the LR, the runtime will see the junk value at the LR slot and will fail to unwind the stack. So we store the LR first then decrement the SP.

This generally works. But in some cases if the signal stack is not set (e.g. #53374 , also iOS doesn't support sigaltstack), the signal will arrive on the current stack and the kernel will push the signal frame immediately below the SP, clobbering our saved LR. In those cases we have to store the LR again after decrementing the SP. This makes our function prologue a bit inefficient.

We can avoid the problem if we expand the stack unwind metadata to express "the SP has decremented but the return address is still in the LR register". Currently, the metadata is just about SP delta and the runtime always assumes the return address can be found at 0(SP) (for non-leaf functions). With the expanded metadata, we can decrement SP and then store the LR.

This also makes our prologue more similar to C functions.

I plan to do it in Go 1.20.

gopherbot commented 2 years ago

Change https://go.dev/cl/412474 mentions this issue: cmd/internal/obj/arm64: save LR and SP in one instruction for small frames

gopherbot commented 2 years ago

Change https://go.dev/cl/424254 mentions this issue: runtime: drop function context from traceback

cherrymui commented 1 year ago

Didn't get chance to do it in 1.20. Will do in 1.21.