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

Indu Bhagat indu.bhagat@oracle.com
Tue Jan 13 09:13:45 GMT 2026


On 1/13/26 12:31 AM, Jens Remus wrote:
> 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.
> 

My judgement is the same that supporting .cfi_val_offset {SP|FP|RA} is 
not crucial.

(I see now that your intention was to get further inputs from folks.  I 
am fine to keep the bit unused then.  The worst in future, in this case, 
will be to deal with a awkwardly named field it seems.  We can wait for 
others to chime in..)

Until then, I will keep the status quo (unused bit in 3 position) and 
post V3 soon.

>>> 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_off = sec->ra_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.
> 

Thanks for explaining. Makes sense.

>>
>>>      [... 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



More information about the Binutils mailing list