{"id":112140,"date":"2026-03-16T07:00:00","date_gmt":"2026-03-16T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112140"},"modified":"2026-03-16T07:29:47","modified_gmt":"2026-03-16T14:29:47","slug":"20260316-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260316-00\/?p=112140","title":{"rendered":"Windows stack limit checking retrospective: PowerPC"},"content":{"rendered":"<p>We continue our historical survey of Windows stack-checking functions by looking at the PowerPC.<\/p>\n<p>The weird thing here is that on PowerPC, you ask for the <i>negative<\/i> of the stack frame size. We&#8217;ll see why soon.<\/p>\n<pre>; on entry, r12 is the *negative* of the number of bytes to allocate\r\n; on exit, stack has been validated (but not adjusted)\r\n\r\nchkstk:\r\n    subi    r0, r12, PAGE_SIZE - 1 ; expand by another page to make sure we get it all\r\n\r\n    ; get the stack limit for the current stack\r\n    cmpwi   sp, 0           ; check what kind of stack we are on\u00b9\r\n    add     r0, r0, sp      ; r0 = proposed new stack limit\r\n    bge+    usermode        ; nonnegative means user mode\r\n\r\n    mfsprg  r11, 1          ; get kernel thread state\r\n    lwz     r11, StackStart(r11) ; where the stack started\r\n    subi    r11, r11, KERNEL_STACK_SIZE ; where the stack ends\r\n    b       havelimit\r\n\r\nusermode:\r\n    lwz     r11, StackLimit(r13) ; get stack limit from TEB\r\n\r\nhavelimit:\r\n    sub     r0, r11, r0     ; r0 = bytes of stack growth needed\r\n    srawi.  r0, r0, 12      ; r0 = pages of stack growth needed\r\n    blelr                   ; if \u2264 0, then nothing to do\r\n\r\n    mtctr   r0              ; prepare to loop\r\n\r\nprobe:\r\n    lwzu    r0, -PAGE_SIZE(r11) ; touch a page and adjust r11\r\n    bdnz    probe           ; keep touching\r\n\r\n    blr                     ; return\r\n<\/pre>\n<p>As with the MIPS version, this code short-circuits the case where the stack has already grown enough to accommodate the allocation, but in order to do the calculations, it has to know where the stack limit is, which in turn means sniffing at the stack pointer to see whether it is a user-mode stack or a kernel-mode stack. This relies on the fact that on the PowerPC, the kernel\/user split is architectural at the midpoint of the address space.<\/p>\n<p>You would call this by doing something like<\/p>\n<pre>    mflr    r0              ; move return address to r0\r\n    stw     r29, -12(r1)    ; save non-volatile register\r\n    stw     r30, -8(r1)     ; save non-volatile register\r\n    stw     r31, -4(r1)     ; save non-volatile register\r\n    stw     r0,  -16(r1)    ; save return address\r\n    li      r12, -17320     ; large stack frame (negative)\r\n    bl      _chkstk         ; fault pages in if necessary\r\n    stwxu   r1, r12, r1     ; create stack frame and link\r\n                            ; store r1 to memory at r12 + r1\r\n                            ; r1 = r12 + r1\r\n<\/pre>\n<p>And now we see why the <code>chkstk<\/code> function wants the stack frame size as a negative number: A negative number allows the caller to use the atomic <code>stwxu<\/code> indexed store and update instruction. The indexed store instructions add two registers to calculate the effective address. There is no variant that <i>subtracts<\/i> two registers, so using a negative number lets us get the effect of a subtraction while still formally performing an addition.<\/p>\n<p>Next time, we&#8217;ll look at changes to the 80386 stack limit checker.<\/p>\n<p>\u00b9 I think there&#8217;s a micro-optimization opportunity here: Instead of<\/p>\n<pre>    cmpwi   sp, 0           ; check what kind of stack we are on\u00b9\r\n    add     r0, r0, sp      ; r0 = proposed new stack limit\r\n    bge+    usermode        ; nonnegative means user mode\r\n<\/pre>\n<p>we could ask the <code>add<\/code> to update the flags and use that result.<\/p>\n<pre>    add.    r0, r0, sp      ; r0 = proposed new stack limit\r\n                            ; and see what kind of stack it is\r\n    bge+    usermode        ; nonnegative means user mode\r\n<\/pre>\n<p>This produces a different result if the value in <code>t8<\/code> was so large that it crossed from user mode to kernel mode or vice versa, but that&#8217;s okay. The old code didn&#8217;t handle that case either!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Doing the math backwards.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-112140","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Doing the math backwards.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112140","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=112140"}],"version-history":[{"count":1,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112140\/revisions"}],"predecessor-version":[{"id":112141,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112140\/revisions\/112141"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=112140"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112140"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112140"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}