[V2 18/36] [SFrame-V3] gas: sframe: output new FDE type SFRAME_FDE_TYPE_FLEX

Jens Remus jremus@linux.ibm.com
Tue Jan 13 08:31:30 GMT 2026


On 1/13/2026 3:06 AM, Indu Bhagat wrote:
> On 1/12/26 2:28 AM, Jens Remus wrote:
>> Hello Indu, Jose, Steven, et al.,
>>
>> On 1/7/2026 9:42 AM, Indu Bhagat wrote:
>>> This patch updates the routines for emission of the new FDE type
>>> SFRAME_FDE_TYPE_FLEX in the SFrame output section.  The support for
>>> generating these flex FDEs themselves is added in a subsequent commit.
>>>
>>> For flex FDE type, output_sframe_row_entry_offsets () emits two
>>> offsets for RA tracking, irrespective of the ABI/arch, i.e.,
>>> irrespecitve of whether RA-tracking is enabled.  This is because, for
>>
>> Please mention that only a single FP/RA padding "offset" is used when
>> FP location info without RA location info is encoded.
>>
>>> flex FDE type, RA is allowed "non-standard" recovery rules,
>>> e.g., RA = REG or RA = *(REG+offset).  In a follow-up patch, when
>>> handling for .cfi_register RA, REG is added, emission code will be
>>> patched up accodingly.
>>
>> TL;DR:  Recap of why a single padding offset of zero is acceptable.
>>          Skip to stack tracer implications below.
>>
>> Regarding whether a single RA padding offset of zero (instead of two) is
>> sufficient to represent FP without RA saved:
>>
>> reg_data = SFRAME_V3_FLEX_FDE_REG_ENCODE (0 /* regnum */, 0 /* deref_p */, 0 /* reg_p */)
>>           = SFRAME_FRE_RA_OFFSET_INVALID
>>           = 0
>>
>> We established that this encoding rules out to represent the CFI
>> directive .cfi_val_offset RA, <offset> (means RA = CFA + <offset>) in
>> SFrame V3 without any additional flag in reg_data.
>>
>> This would not be an issue for RA, as .cfi_val_offset specifies an
>> address on the stack (as long as CFA points somewhere to the stack),
>> which should never be valid for the return address.
>> For FP this should not be useful either, as the FP value at entry
>> should not be known and thus not be representable related to CFA.
>> For SP, if SFrame would ever be extended to track SP, the only
>> sensible rule should be the one from the CFA definition
>> (e.g. CFA = SP at call site or CFA = SP at call site + 160 on s390x),
>> which would not need to be explicitly encoded, as it is already
>> implicitly assumed to unwind SP.
>>
>> If the need to represent .cfi_val_offset {SP|FP|RA} would still arise,
>> the spare unused bit in reg_data could possibly be used.  It may be
>> required after all, as .cfi_val_offset {SP|FP|RA}, <offset> encodes as:
>>
>>    reg_data = 0;
>>    offset_data = <offset>;
>>
> 
> Currently, .cfi_val_offset {SP|FP|RA} is not handled for SFrame.
> 
> Your understanding is correct that: for enabling (generation) representation
> of of .cfi_val_offset {SP|FP|RA}, <offset> in future,  we can use the
> indicated unused bit.
> 
> The thinking was what we could _set_ that unused bit to 1 when wanting to
> indicate the semantics of .cfi_val_offset.
> 
> That is,
> 
>    reg_data = SFRAME_V3_FLEX_FDE_REG_ENCODE (0,1,0,0);
>    offset_data = <offset>
> 
> would indicate the semantics of .cfi_val_offset in future.
> 
> However, I now realise that for the above then, we will have to have
> (in future):
> 
> #define SFRAME_V3_FLEX_FDE_REG_ENCODE(reg, not_invalid_p, deref_p, reg_p)  \
>   ((((reg) << 0x3) | (((not_invalid_p) & 0x1) << 0x2) | (((deref_p) & 0x1) << 0x1) | ((reg_p) & 0x1)))
> 
> Perhaps its my general lack of skills in naming things appropriately,
> but I cannot think of an alternative to "not_invalid_p".  That choice
> of name will be generally confusing.
> 
> [The PADDING or invalid reg_data will need to be always be defined as zero.]
> 
> Perhaps its better to define that unused bit now to adopt the semantics
> of valid_p.  IOW, we start to generate reg_data as:
> 
>    reg_data = SFRAME_V3_FLEX_FDE_REG_ENCODE (reg,1,deref_p,reg_p);
>    offset_data = <offset>
> 
> Thoughts ?

I would then use the LSB for valid_p, but that is only my personal
preference:

((reg) << 3) | (deref_p << 3) | (reg_p << 2) | (valid_p)

It would leave no spare bit for any future use.  Currently I can't see why
.cfi_val_offset {SP|FP|RA} would be important.


>> An offset value of zero (on architectures such as s390x that do not use
>> zero as default), would need explicit indication in reg_data to be
>> differentiated from two padding offsets of zero.
>>
>>
>>
>> Stack tracer implications of using single padding offsets in flexible
>> SFrame FDEs (at the example of WIP Linux Kernel unwind user sframe):
>>
>> TL;DR: Disallowing trailing padding offsets would slightly simplify the
>>         flex FDE FRE offset decoding logic, as it allows to test for
>>         (offset_count >= 2) once instead of (offset_count > 0) twice.
>>
>> A) Flex FDE FRE offset decoding logic when using two padding offsets:
>>
>>          /* offset_count >= 2, as it is neither zero nor odd */
>>          UNSAFE_GET_USER_INC(cfa_reg, cur, offset_size, Efault);
>>          UNSAFE_GET_USER_INC(cfa_off, cur, offset_size, Efault);
>>          offset_count -= 2;
>>          cfa_off = sframe_cfa_offset_decode(cfa_off);
>>
>>          ra_off = sec->fp_off;
>>          ra_reg = ra_off ? 2 : 0; /* deref_p=(ra_off != 0) */
>>          if (offset_count >= 2) {
>>                  UNSAFE_GET_USER_INC(ra_reg, cur, offset_size, Efault);   <-- unconditionally read two offsets
>>                  UNSAFE_GET_USER_INC(ra_off, cur, offset_size, Efault);
>>                  offset_count -= 2;
>>                  if (!ra_reg && !ra_off) {
>>                          /* Padding RA location info */
>>                          ra_off = sec->ra_off;
>>                          if (ra_off)
>>                                  ra_reg = 2; /* deref_p=(ra_off != 0) */
>>                  }
>>          }
>>
>>     [... likewise for FP ...]
>>
>>          if (offset_count)
>>                  return -EFAULT;
>>
>> B) Flex FDE FRE offset decoding logic when using single padding offset
>>     and disallowing trailing padding offsets:
>>
>>          if (offset_count < 2)
>>                  return -EFAULT;
>>          UNSAFE_GET_USER_INC(cfa_reg, cur, offset_size, Efault);
>>          UNSAFE_GET_USER_INC(cfa_off, cur, offset_size, Efault);
>>          offset_count -= 2;
>>          cfa_off = sframe_cfa_offset_decode(cfa_off);
>>
>>          ra_off = sec->fp_off;
>>          ra_reg = ra_off ? 2 : 0; /* deref_p=(ra_off != 0) */
>>          if (offset_count >= 2) {   <-- either [ra_reg_data, ra_offset] or [SFRAME_FRE_RA_OFFSET_INVALID, fp_reg_data]
>>                  UNSAFE_GET_USER_INC(ra_reg, cur, offset_size, Efault);
>>                  offset_count--;
>>                  if (ra_reg) {
>>                          UNSAFE_GET_USER_INC(ra_off, cur, offset_size, Efault);
>>                          offset_count--;
>>                  } else {
>>                          /* Padding RA location info */
>>                          ra_off = sec->ra_off;
>>                          if (ra_off)
>>                                  ra_reg = 2; /* deref_p=(ra_off != 0) */
>>                  }
>>          }
>>
>>     [... likewise for FP ...]
>>
>>          if (offset_count)
>>                  return -EFAULT;
>>
>> C) Flex FDE FRE offset decoding logic when using single padding offset
>>     but allowing trailing padding offsets:
>>
>>          if (offset_count < 2)
>>                  return -EFAULT;
>>          UNSAFE_GET_USER_INC(cfa_reg, cur, offset_size, Efault);
>>          UNSAFE_GET_USER_INC(cfa_off, cur, offset_size, Efault);

Above reads CFA control data and offset into cfa_reg and cfa_off.

>>          offset_count -= 2;
>>          cfa_off = sframe_cfa_offset_decode(cfa_off);
>>
>>          ra_off = sec->fp_off;
>>          ra_reg = ra_off ? 2 : 0; /* deref_p=(ra_off != 0) */

Above initializes RA offset in ra_off to fixed RA offset and deduces
RA control data in ra_reg to have deref_p set if the RA offset is not
SFRAME_CFA_FIXED_RA_INVALID (= zero).

>>          if (offset_count) {
>>                  UNSAFE_GET_USER_INC(ra_reg, cur, offset_size, Efault);

Above reads RA contol data into ra_reg.

>>                  offset_count--;
>>                  if (ra_reg) {
>>                          if (!offset_count)   <-- additional test required
>>                                  return -EFAULT;
>>                          UNSAFE_GET_USER_INC(ra_off, cur, offset_size, Efault);

Above reads RA offset into ra_off.

>>                          offset_count--;
>>                  } else {
>>                          /* Padding RA location info */
>>                          ra_off = sec->ra_off;
>>                          if (ra_off)
>>                                  ra_reg = 2; /* deref_p=(ra_off != 0) */

Above sets RA offset to fixed RA offset and deduces RA control data.
I realized that setting ra_off is not required as it was not touched
since initialization to the fixed RA offset.  Only the RA control
data needs to be re-deduced, as it got overwritten with the padding
"offset".

>>                  }
>>          }
>>
> 
> (I assume the comments /* deref_p=(ra_off != 0) */ are merely some
> stub/testing comment.  We do need to get
> SFRAME_V3_FLEX_FDE_OFFSET_REG_DEREF_P (ra_reg) for deref_p...)

No, they are not.  Whenever the fixed default RA/FP offset is used
the control data in ra_reg needs to be deduced, specifically deref_p
from whether the offset not SFRAME_CFA_FIXED_{FP|RA}_INVALID (= zero).
reg_p is always cleared when a fixed FP/RA offset is used.

Whenever control data is read from the FDE location data that is used
of course.

regnum, deref_p, and reg_p for CFA/FP/RA are later extracted from
cfa_reg/cfa_off, ra_reg/ra_off, and fp_reg/fp_off.

> 
>>     [... likewise for FP ...]
>>
>>          if (offset_count)   <-- TODO: test for additional padding offsets?
>>                  return -EFAULT;
>>
>>
> 
> If the kernel implements this check of additional offsets as error path
> now, and suppose a future backward-compatible  errata of the format uses
> more data (say for SP), other changes in the sframe stack tracer will be
> necessary anyway.
> 
> So implementing this as error path now at least ensures safety as it is
> essentially a sanity check.

Makes sense.

>> What are your thoughts regarding the single padding offsets and whether
>> to allow for trailing padding offsets?  Why would they be useful?
>>
> 
> So far in GAS, we do not emit trailing padding offsets.  As far as I can
> reason, I do not see a use to emitting trailing padding offsets.
> 
> If there is agreement, I'd say its better to add this expectation (Trailing
> padding offsets should not exist) in the specification.
> 
> So B) looks preferable to me:
>  - Compared to A), B is compact in terms of section size.
>  - Compared to C), I dont see the advantage in allowing trailing offsets ATM.

I very much agree.  Somehow I was mislead that you preferred to allow
trailing padding offsets.

>> If I find some time besides getting the Kernel unwind user sframe
>> series into shape for review, I plan to do some SFrame size comparison
>> on s390x of the following:
>>
>> SFrame V2
>> SFrame V3, flex FDE disabled, use SFrame V2 register as offset encoding
>> SFrame V3, flex FDE disabled, use SFrame V3 register as offset encoding
>> SFrame V3, flex FDE enabled only for .cfi_register
>> SFrame V3, flex FDE fully enabled (this patch series)
>>
> 
> In SFrame V3, we have one additional function info byte per FDE.  Just a
> reminder that a size increase proportional to the number of FDEs is
> expected (when comparing V3 over V2).
Regards,
Jens
-- 
Jens Remus
Linux on Z Development (D3303)
jremus@de.ibm.com / jremus@linux.ibm.com

IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats: Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft: Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/



More information about the Binutils mailing list