{"id":107043,"date":"2022-08-24T07:00:00","date_gmt":"2022-08-24T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107043"},"modified":"2022-11-29T09:23:12","modified_gmt":"2022-11-29T17:23:12","slug":"20220824-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220824-00\/?p=107043","title":{"rendered":"The AArch64 processor (aka arm64), part 21: Classic function prologues and epilogues"},"content":{"rendered":"<p>Classic function prologues in Windows on AArch64 follow a common pattern. I&#8217;ll present the full prologue, and then we&#8217;ll take it apart instruction by instruction.<\/p>\n<div id=\"p20220823_head\" style=\"display: none;\">\u00a0<\/div>\n<pre>    ; return address protection\r\n    pacibsp\r\n\r\n    ; saving registers\r\n    stp     fp, lr, [sp, #-0x30]!\r\n    stp     x19, x20, [sp, #0x10]\r\n    str     x21, [sp, #0x20]\r\n\r\n    ; establishing frame chain\r\n    mov     fp, sp\r\n\r\n    ; initializing <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/gs-buffer-security-check\">GS cookie<\/a>\r\n    bl      __security_push_cookie\r\n\r\n    ; local variables and outbound parameters\r\n    sub     sp, sp, #0x80\r\n<\/pre>\n<p>The prologue breaks up into five sections, as marked off by comments above.<\/p>\n<p>On entry to the function, we have this:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px gray; border-top: none; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">previous <var>fp<\/var><\/td>\n<td>\u2190 <var>fp<\/var> (frame chain)<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">\u22ee<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">stack param<\/td>\n<td>\u2190 <var>sp<\/var><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The first order of business is to apply return address protection.<\/p>\n<p>Next, we save nonvolatile registers. We build the next stack frame by pushing <var>fp<\/var> and <var>lr<\/var> onto the stack into adjacent locations. The frame pointer and link register are always stored next to each other because that&#8217;s what stack walking requires. The &#8220;push&#8221; onto the stack is done by using a pre-incrementing store,\u00b9 so that the stack pointer is adjusted, and then the values written to the adjusted stack pointer.<\/p>\n<p>Let&#8217;s walk through that &#8220;push&#8221; again:<\/p>\n<pre>    stp     fp, lr, [sp, #-0x30]!\r\n<\/pre>\n<p>The effective address is <var>sp<\/var> \u2212 <code>0x30<\/code>, which is <code>0x30<\/code> bytes below the current stack pointer. At that location, we store the <var>fp<\/var> and <var>lr<\/var> registers, and then the effective address is written back to the base register <var>sp<\/var>.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px gray; border-top: none; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">previous <var>fp<\/var><\/td>\n<td>\u2190 <var>fp<\/var> (frame chain)<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">\u22ee<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">stack param<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid gray; border-width: 5px 5px 1px 5px; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid gray; border-width: 1px 5px; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid gray; border-width: 1px 5px; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid gray; border-width: 1px 5px; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid gray; border-width: 1px 5px; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid gray; border-width: 1px 5px 5px 5px; text-align: center;\">previous <var>fp<\/var><\/td>\n<td>\u2190 <var>sp<\/var><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The next instruction stores the <var>x19<\/var> and <var>x20<\/var> registers into the register save area we just created.<\/p>\n<pre>    stp     x19, x20, [sp, #0x10]\r\n<\/pre>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px gray; border-top: none; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">previous <var>fp<\/var><\/td>\n<td>\u2190 <var>fp<\/var> (frame chain)<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">\u22ee<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">stack param<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid gray; border-width: 5px 5px 1px 5px; text-align: center;\">saved <var>x20<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid gray; border-width: 1px 5px 5px 5px; text-align: center;\">saved <var>x19<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">previous <var>fp<\/var><\/td>\n<td>\u2190 <var>sp<\/var><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>And the last instruction in the set saves the lone <var>x21<\/var> register.<\/p>\n<pre>    str     x21, [sp, #0x20]\r\n<\/pre>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px gray; border-top: none; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">previous <var>fp<\/var><\/td>\n<td>\u2190 <var>fp<\/var> (frame chain)<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">\u22ee<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">stack param<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 5px gray; text-align: center;\">saved <var>x21<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">saved <var>x20<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">saved <var>x19<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px gray; text-align: center;\">previous <var>fp<\/var><\/td>\n<td>\u2190 <var>sp<\/var><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The stack must remain 16-byte aligned, which means that space must be reserved in multiples of 2 registers. We have an odd number of registers to save, so one of the spaces we reserved for the register save area goes to waste. (In theory, the compiler could decide to use it to record a local variable, but in practice it doesn&#8217;t.)<\/p>\n<p>The second part of the prologue re-establishes the frame chain.<\/p>\n<pre>    ; link this frame onto the frame chain\r\n    mov     fp, sp\r\n<\/pre>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; border-top: none; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"line-height: 50%;\">\u00a0<\/td>\n<td rowspan=\"2\">\u25b6\ufe0e<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\" rowspan=\"2\">previous <var>fp<\/var><\/td>\n<td rowspan=\"2\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px gray; border-style: solid none none solid; line-height: 50%;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">\u22ee<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">stack param<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">saved <var>x21<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">saved <var>x20<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">saved <var>x19<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px gray; border-style: none none solid solid; line-height: 50%;\">\u00a0<\/td>\n<td rowspan=\"2\">\u00a0<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\" rowspan=\"2\">previous <var>fp<\/var><\/td>\n<td rowspan=\"2\">\u2190 <var>fp<\/var> (frame chain), sp<\/td>\n<\/tr>\n<tr>\n<td style=\"line-height: 50%;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If the function contains a stack-based array, then the prologue inserts the <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/gs-buffer-security-check\"> GS cookie<\/a> onto the stack so that a buffer overflow from the stack-based array is likely to corrupt the cookie before it gets to the saved return address.<\/p>\n<pre>    bl      __security_push_cookie\r\n<\/pre>\n<p>Even though the cookie is only the size of a register, the function pushes 16 bytes onto the stack due to the requirement that the stack remain 16-byte aligned.<\/p>\n<p>The cookie management functions use the <var>xip0<\/var> and <var>xip1<\/var> registers to do the work of calculating or validating the cookie. These registers are volatile and are assumed to be modified by any branch instruction, and we used a branch instruction to get to the start of the prologue, so we know that <var>xip0<\/var> and <var>xip1<\/var> cannot be used to pass information from the caller to the callee, not even for a nonstandard calling convention.<\/p>\n<p>Finally, the prologue allocates space for stack-based local variables and outbound parameters.<\/p>\n<pre>    sub     sp, sp, #0x80\r\n<\/pre>\n<p>We then reach the function body with this stack layout:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; border-top: none; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td rowspan=\"6\">\u00a0<\/td>\n<td rowspan=\"6\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"line-height: 50%;\">\u00a0<\/td>\n<td rowspan=\"2\">\u25b6\ufe0e<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\" rowspan=\"2\">previous <var>fp<\/var><\/td>\n<td rowspan=\"2\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px gray; border-style: solid none none solid; line-height: 50%;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">\u22ee<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">stack param<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; border-left: none;\" rowspan=\"7\">\u00a0<\/td>\n<td rowspan=\"7\" valign=\"center\">saved<br \/>\nregister<br \/>\narea<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">saved <var>x21<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">saved <var>x20<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">saved <var>x19<\/var><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-left: solid 1px gray;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">return address<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px gray; border-style: none none solid solid; line-height: 50%;\">\u00a0<\/td>\n<td rowspan=\"2\">\u00a0<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\" rowspan=\"2\">previous <var>fp<\/var><\/td>\n<td rowspan=\"2\">\u2190 <var>fp<\/var> (frame chain)<\/td>\n<\/tr>\n<tr>\n<td style=\"line-height: 50%;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">\u00a0<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; border-left: none;\" rowspan=\"2\">\u00a0<\/td>\n<td rowspan=\"2\" valign=\"center\">GS<br \/>\narea<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center;\">GS cookie<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center; height: 4em;\">local<br \/>\nvariables<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; border-left: none;\" rowspan=\"2\">\u00a0<\/td>\n<td rowspan=\"2\" valign=\"center\">local<br \/>\nstack<br \/>\narea<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px gray; text-align: center; height: 4em;\">outbound<br \/>\nstack<br \/>\nparameters<\/td>\n<td valign=\"bottom\">\u2190 <var>sp<\/var><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>When the function returns, the above steps are reversed.<\/p>\n<pre>    add     sp, sp, #0x80           ; discard local stack area\r\n    bl      __security_pop_cookie   ; validate and pop GS cookie\r\n    ldr     x21, [sp, #0x20]        ; restore register\r\n    ldp     x19, x20, [sp, #0x10]   ; restore registers\r\n    ldp     fp, lr, [sp], #0x30     ; restore register and pop\r\n    autibsp                         ; validate return address\r\n    ret                             ; return\r\n<\/pre>\n<p>The final <code>ldp<\/code> uses the post-increment addressing mode so that the stack pointer is increased by <code>0x30<\/code> after the registers are loaded.<\/p>\n<p>Restoring the <var>fp<\/var> register unlinks the current stack frame from the frame chain. And restoring the <var>lr<\/var> register puts the return address back into <var>lr<\/var>, which we validate, and then use in the <code>ret<\/code> instruction to return to the caller.\u00b2<\/p>\n<p>Not all of these steps will be present in all function prologues. A function that has no stack-based local variables or outbound parameters will not create a local stack area. A function that has no stack-based arrays will not create a GS cookie. And a lightweight leaf function won&#8217;t even bother saving any registers or protecting the return address.<\/p>\n<p>Next time, we&#8217;ll look at special cases that will diverge from this traditional prologue\/epilogue pattern.<\/p>\n<p>\u00b9 Though since the offset is negative, you can think of it as a pre-<i>decrementing<\/i> store.<\/p>\n<p>\u00b2 In AArch64, the program counter <var>pc<\/var> is not a general-purpose register, so you don&#8217;t see the trick popular in AArch32 where the return address is popped into the <var>pc<\/var> register to return to the caller. For AArch64, we see the more traditional pattern of restoring the return address into <var>lr<\/var> and then explicitly returning to it.<\/p>\n<p>\n<script>\nwindow.addEventListener(\"load\", function() {\n  var fullFF = getComputedStyle(document.body).fontFamily;\n  var simpleFF = fullFF.replace(\/ Emoji\/g, \"\");\n  \/\/ break up \"style\" to prevent wordpress from injecting random junk\n  document.getElementById(\"p20220823_head\").innerHTML =\n`<s` + `tyle>\nbody { font-family: ${simpleFF}; }\n.emoji { font-family: ${fullFF}; }\n.entry-content th { padding: 1px; } \/* stylesheet workaround *\/\n.entry-content td { padding: 1px; } \/* stylesheet workaround *\/\n<\/s` + `tyle>`;\n});\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Implementing the ABI.<\/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":[2],"class_list":["post-107043","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-history"],"acf":[],"blog_post_summary":"<p>Implementing the ABI.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107043","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=107043"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107043\/revisions"}],"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=107043"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107043"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107043"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}