diff mbox series

x86/elf: Remove ASM_CALL_CONSTRAINT from elf_core_save_regs()

Message ID 20250325180005.275552-1-andrew.cooper3@citrix.com (mailing list archive)
State New
Headers show
Series x86/elf: Remove ASM_CALL_CONSTRAINT from elf_core_save_regs() | expand

Commit Message

Andrew Cooper March 25, 2025, 6 p.m. UTC
I was mistaken about when ASM_CALL_CONSTRAINT is applicable.  It is not
applicable for plain pushes/pops, so remove it from the flags logic.

Clarify the description of ASM_CALL_CONSTRAINT to be explicit about unwinding
using framepointers.

Fixes: 0754534b8a38 ("x86/elf: Improve code generation in elf_core_save_regs()")
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
---
CC: Jan Beulich <JBeulich@suse.com>
CC: Roger Pau Monné <roger.pau@citrix.com>
---
 xen/arch/x86/include/asm/asm_defns.h  | 5 +++--
 xen/arch/x86/include/asm/x86_64/elf.h | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)


base-commit: 28fa31d6bb7835be530c2855dd6cf4e77438ae12

Comments

Jan Beulich March 26, 2025, 9 a.m. UTC | #1
On 25.03.2025 19:00, Andrew Cooper wrote:
> I was mistaken about when ASM_CALL_CONSTRAINT is applicable.  It is not
> applicable for plain pushes/pops, so remove it from the flags logic.
> 
> Clarify the description of ASM_CALL_CONSTRAINT to be explicit about unwinding
> using framepointers.
> 
> Fixes: 0754534b8a38 ("x86/elf: Improve code generation in elf_core_save_regs()")
> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
> ---
> CC: Jan Beulich <JBeulich@suse.com>
> CC: Roger Pau Monné <roger.pau@citrix.com>
> ---
>  xen/arch/x86/include/asm/asm_defns.h  | 5 +++--
>  xen/arch/x86/include/asm/x86_64/elf.h | 2 +-
>  2 files changed, 4 insertions(+), 3 deletions(-)
> 
> diff --git a/xen/arch/x86/include/asm/asm_defns.h b/xen/arch/x86/include/asm/asm_defns.h
> index 92b4116a1564..689d1dcbf754 100644
> --- a/xen/arch/x86/include/asm/asm_defns.h
> +++ b/xen/arch/x86/include/asm/asm_defns.h
> @@ -28,8 +28,9 @@ asm ( "\t.equ CONFIG_INDIRECT_THUNK, "
>  
>  /*
>   * This output constraint should be used for any inline asm which has a "call"
> - * instruction.  Otherwise the asm may be inserted before the frame pointer
> - * gets set up by the containing function.
> + * instruction, which forces the stack frame to be set up prior to the asm
> + * block.  This matters when unwinding using framepointers, where the asm's
> + * function can get skipped over.

Does "forces the stack frame to be set up" really mean the stack frame, or the
frame pointer (if one is in use)? In the latter case I can see how the asm()
being moved ahead of that point could cause problems. In the former case I
apparently still don't understand (yet) what the issue is that
ASM_CALL_CONSTRAINT ultimately is to help with.

Jan
Andrew Cooper March 26, 2025, 9:17 a.m. UTC | #2
On 26/03/2025 9:00 am, Jan Beulich wrote:
> On 25.03.2025 19:00, Andrew Cooper wrote:
>> I was mistaken about when ASM_CALL_CONSTRAINT is applicable.  It is not
>> applicable for plain pushes/pops, so remove it from the flags logic.
>>
>> Clarify the description of ASM_CALL_CONSTRAINT to be explicit about unwinding
>> using framepointers.
>>
>> Fixes: 0754534b8a38 ("x86/elf: Improve code generation in elf_core_save_regs()")
>> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
>> ---
>> CC: Jan Beulich <JBeulich@suse.com>
>> CC: Roger Pau Monné <roger.pau@citrix.com>
>> ---
>>  xen/arch/x86/include/asm/asm_defns.h  | 5 +++--
>>  xen/arch/x86/include/asm/x86_64/elf.h | 2 +-
>>  2 files changed, 4 insertions(+), 3 deletions(-)
>>
>> diff --git a/xen/arch/x86/include/asm/asm_defns.h b/xen/arch/x86/include/asm/asm_defns.h
>> index 92b4116a1564..689d1dcbf754 100644
>> --- a/xen/arch/x86/include/asm/asm_defns.h
>> +++ b/xen/arch/x86/include/asm/asm_defns.h
>> @@ -28,8 +28,9 @@ asm ( "\t.equ CONFIG_INDIRECT_THUNK, "
>>  
>>  /*
>>   * This output constraint should be used for any inline asm which has a "call"
>> - * instruction.  Otherwise the asm may be inserted before the frame pointer
>> - * gets set up by the containing function.
>> + * instruction, which forces the stack frame to be set up prior to the asm
>> + * block.  This matters when unwinding using framepointers, where the asm's
>> + * function can get skipped over.
> Does "forces the stack frame to be set up" really mean the stack frame, or the
> frame pointer (if one is in use)?

What do you consider to be the difference, given how frame pointers work
in our ABI?

It is the frame pointer which needs setting up, which at a minimum
involves spilling registers to the stack and getting %rsp into it's
eventual position.

>  In the latter case I can see how the asm()
> being moved ahead of that point could cause problems. In the former case I
> apparently still don't understand (yet) what the issue is that
> ASM_CALL_CONSTRAINT ultimately is to help with.

The specific bug is from a sequence of functions a, b and c, where b
uses an asm() to call c.

a() pushes old %rbp sets up new %rbp, b() fails to do so early enough,
and c() pushes a()'s frame pointer, not b()'s.  Then unwinding via frame
pointer skips b() in the backtrace.

I don't know what the precise effects of the constraint are.  The
compiler maintainers can't agree either, and say it's fragile and can't
be relied upon, but it seems to be the only way to have the desired
effect on emitted code.

~Andrew
Jan Beulich March 26, 2025, 9:28 a.m. UTC | #3
On 26.03.2025 10:17, Andrew Cooper wrote:
> On 26/03/2025 9:00 am, Jan Beulich wrote:
>> On 25.03.2025 19:00, Andrew Cooper wrote:
>>> I was mistaken about when ASM_CALL_CONSTRAINT is applicable.  It is not
>>> applicable for plain pushes/pops, so remove it from the flags logic.
>>>
>>> Clarify the description of ASM_CALL_CONSTRAINT to be explicit about unwinding
>>> using framepointers.
>>>
>>> Fixes: 0754534b8a38 ("x86/elf: Improve code generation in elf_core_save_regs()")
>>> Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
>>> ---
>>> CC: Jan Beulich <JBeulich@suse.com>
>>> CC: Roger Pau Monné <roger.pau@citrix.com>
>>> ---
>>>  xen/arch/x86/include/asm/asm_defns.h  | 5 +++--
>>>  xen/arch/x86/include/asm/x86_64/elf.h | 2 +-
>>>  2 files changed, 4 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/xen/arch/x86/include/asm/asm_defns.h b/xen/arch/x86/include/asm/asm_defns.h
>>> index 92b4116a1564..689d1dcbf754 100644
>>> --- a/xen/arch/x86/include/asm/asm_defns.h
>>> +++ b/xen/arch/x86/include/asm/asm_defns.h
>>> @@ -28,8 +28,9 @@ asm ( "\t.equ CONFIG_INDIRECT_THUNK, "
>>>  
>>>  /*
>>>   * This output constraint should be used for any inline asm which has a "call"
>>> - * instruction.  Otherwise the asm may be inserted before the frame pointer
>>> - * gets set up by the containing function.
>>> + * instruction, which forces the stack frame to be set up prior to the asm
>>> + * block.  This matters when unwinding using framepointers, where the asm's
>>> + * function can get skipped over.
>> Does "forces the stack frame to be set up" really mean the stack frame, or the
>> frame pointer (if one is in use)?
> 
> What do you consider to be the difference, given how frame pointers work
> in our ABI?

My point is that frame pointers are an optional part. Sufficiently high
optimization levels omit them by default, iirc. And depending on
CONFIG_FRAME_POINTER we may explicitly pass -fno-omit-frame-pointer. Even
in that case there is a stack frame that the compiler is setting up. Yet
in that case the effect of ASM_CALL_CONSTRAINT is not relevant. Hence
also why the construct expands to nothing in that case. The comment,
however, is placed outside if the #ifdef, and hence applies to both forms
(according to the way I read such, at least).

> It is the frame pointer which needs setting up, which at a minimum
> involves spilling registers to the stack and getting %rsp into it's
> eventual position.

Right, and all I'm effectively asking for is s/stack frame/frame pointer/
in the new comment text. Then:
Reviewed-by: Jan Beulich <jbeulich@suse.com>

Alternatively part or all of the comment could be moved inside the #ifdef.

Jan
diff mbox series

Patch

diff --git a/xen/arch/x86/include/asm/asm_defns.h b/xen/arch/x86/include/asm/asm_defns.h
index 92b4116a1564..689d1dcbf754 100644
--- a/xen/arch/x86/include/asm/asm_defns.h
+++ b/xen/arch/x86/include/asm/asm_defns.h
@@ -28,8 +28,9 @@  asm ( "\t.equ CONFIG_INDIRECT_THUNK, "
 
 /*
  * This output constraint should be used for any inline asm which has a "call"
- * instruction.  Otherwise the asm may be inserted before the frame pointer
- * gets set up by the containing function.
+ * instruction, which forces the stack frame to be set up prior to the asm
+ * block.  This matters when unwinding using framepointers, where the asm's
+ * function can get skipped over.
  */
 #ifdef CONFIG_FRAME_POINTER
 register unsigned long current_stack_pointer asm("rsp");
diff --git a/xen/arch/x86/include/asm/x86_64/elf.h b/xen/arch/x86/include/asm/x86_64/elf.h
index f33be46ddec9..e7bec7327aa2 100644
--- a/xen/arch/x86/include/asm/x86_64/elf.h
+++ b/xen/arch/x86/include/asm/x86_64/elf.h
@@ -56,7 +56,7 @@  static inline void elf_core_save_regs(ELF_Gregset *core_regs,
     /* orig_rax not filled in for now */
     asm ( "lea (%%rip), %0" : "=r" (core_regs->rip) );
     asm ( "mov %%cs, %0" : "=m" (core_regs->cs) );
-    asm ( "pushfq; popq %0" : "=m" (core_regs->rflags) ASM_CALL_CONSTRAINT );
+    asm ( "pushfq; popq %0" : "=m" (core_regs->rflags) );
     asm ( "movq %%rsp, %0" : "=m" (core_regs->rsp) );
     asm ( "mov %%ss, %0" : "=m" (core_regs->ss) );
     rdmsrl(MSR_FS_BASE, core_regs->thread_fs);