[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