-
Notifications
You must be signed in to change notification settings - Fork 18.8k
Description
On 386, amd64, arm, and arm64, stacksplit appends a "CALL morestack; JMP f(SB)" call sequence to the end of f, but the pcsp table entries at the end of f default to f's full frame size rather than 0.
I think this can cause a problem in the following situation on x86:
- The user sets a breakpoint on the "JMP f(SB)" instruction.
- That causes rewindmorestack to not rewind the PC, so gp.sched.pc will continue to correspond to the bad pcsp entry.
- Within copystack, the call to gentraceback(^uintptr(0), ^uintptr(0), 0, gp, ...) will use gp.sched.pc and gp.sched.sp to walk the stack.
- But because of the bad pcsp entry, it'll misinterpret the stack frames.
I briefly explored three ways to fix this and measured the file size impact on cmd/gofmt on linux/amd64:
text data bss dec hex filename
2687573 74416 129816 2891805 2c201d gofmt.before
2688597 74416 129816 2892829 2c241d gofmt.after1
2686430 74416 129816 2890662 2c1ba6 gofmt.after2
2684162 74416 129816 2888394 2c12ca gofmt.after3
gofmt.after1 is leaving the generated instruction sequence alone, but setting Spadj accordingly so extra pcsp table entries will be generated for the morestack calls.
gofmt.after2 is to insert the "CALL morestack; JMP f(SB)" call sequence after the last RET instruction in a function, so that it gets to share a pcsp table entry with the RET instruction. (And for functions with no RET instructions, to continue appending to the end like in gofmt.after1.)
gofmt.after3 is the same as gofmt.after2, except inserting after the first RET instruction. The additional text savings are from more opportunities to use the 2-byte near-JMP instruction encoding for "JMP f(SB)" instead of the 5-byte encoding.