[PATCH 1/3] libsframe: fix issue in finding FRE in SFRAME_FDE_TYPE_PCMASK type SFrame FDEs
Indu Bhagat
indu.bhagat@oracle.com
Thu May 22 06:00:45 GMT 2025
On 5/20/25 11:15 PM, Jan Beulich wrote:
> On 20.05.2025 21:03, Indu Bhagat wrote:
>> Lookup of information from SFrame FDEs of type SFRAME_FDE_TYPE_PCMASK,
>> typically used for pltN entries had an issue: the current code will only
>> work for certain placements of .plt and .sframe. Fix it, and add a
>> testcase.
>>
>> Make the testcase itself easier to follow by adding appropriate vars
>> where applicable.
>>
>> libsframe/
>> * sframe.c (sframe_fre_check_range_p): Fix logic for
>> SFRAME_FDE_TYPE_PCMASK type FDE.
>> libsframe/testsuite/
>> * libsframe.find/plt-findfre-1.c: Adjust the test for a variety
>> of placements of .sframe and .plt.
>> ---
>> libsframe/sframe.c | 11 ++--
>> .../testsuite/libsframe.find/plt-findfre-1.c | 59 ++++++++++++-------
>> 2 files changed, 45 insertions(+), 25 deletions(-)
>>
>> diff --git a/libsframe/sframe.c b/libsframe/sframe.c
>> index c2693b978ec..85457e4d660 100644
>> --- a/libsframe/sframe.c
>> +++ b/libsframe/sframe.c
>> @@ -398,10 +398,13 @@ sframe_fre_check_range_p (sframe_func_desc_entry *fdep,
>> }
>> else
>> {
>> - /* For FDEs for repetitive pattern of insns, we need to return the FRE
>> - where pc % rep_block_size is between start_ip_offset and
>> - end_ip_offset. */
>> - masked_pc = pc % rep_block_size;
>> + /* For SFrame FDEs encoding information for repetitive pattern of insns,
>> + masking with the rep_block_size is necessary to find the matching FRE.
>> + start_ip_offset and end_ip_offset are _unsigned_values_ identifying
>> + the SFrame FRE. So, perform a calculation to first get the distance
>> + between the pc and func_start_addr, followed by modulo to find the
>> + fitting range. */
>> + masked_pc = (pc - func_start_addr) % rep_block_size;
>> ret = ((start_ip_offset <= masked_pc) && (end_ip_offset >= masked_pc));
>> }
>
> Sadly the description only says that there was an issue, but not what that issue
> was. The comment and code change here leaves me guessing, too: Is the problem
> with the block of repetitive patterns not being aligned to a multiple of the
> block size? Contextual information may also be necessary (I'm sorry for my
> continued lack of SFram knowledge): Such a block (PLT being an example) is all
> covered by a single FDE? Rather than each PLT entry having its own? That's kind
> of unexpected considering that the first 2(?) PLT entries can be slightly
> different from the rest, iirc.
>
Background:
A block of repetitive instructions, e.g. in case of pltN entries, can be
represented using an SFrame FDE of type SFRAME_FDE_TYPE_PCMASK. Such
FDEs, apart from carrying the regular "sfde_func_size" (which indicates
the number of bytes of instructions in the pltN entries), also carry
"sfde_func_rep_size" (which is the size of a single pltN entry). This
concept is used to condense the stack trace information for the pltN
entries.
This representation allows for: stack trace information for one pltN
entry is repeatedly used for a PC range of [pltn_start_addr,
pltn_start_addr + sfde_func_size).
To your question on the various types of PLT entries: For non-pltN
entries, sframe FDE of type SFRAME_FDE_TYPE_PCINC ("regular" SFrame FDE)
are used. For a toy program on x86_64, e.g., I see:
0000000000401020 <puts@plt-0x10>:
401020: ff 35 ca 2f 00 00 push 0x2fca(%rip) #
403ff0 <_GLOBAL_OFFSET_TABLE_+0x8>
401026: ff 25 cc 2f 00 00 jmp *0x2fcc(%rip) #
403ff8 <_GLOBAL_OFFSET_TABLE_+0x10>
40102c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000401030 <puts@plt>:
401030: ff 25 ca 2f 00 00 jmp *0x2fca(%rip) #
404000 <puts@GLIBC_2.2.5>
401036: 68 00 00 00 00 push $0x0
40103b: e9 e0 ff ff ff jmp 401020 <_init+0x20>
0000000000401040 <printf@plt>:
401040: ff 25 c2 2f 00 00 jmp *0x2fc2(%rip) #
404008 <printf@GLIBC_2.2.5>
401046: 68 01 00 00 00 push $0x1
40104b: e9 d0 ff ff ff jmp 401020 <_init+0x20>
0000000000401050 <gettimeofday@plt>:
401050: ff 25 ba 2f 00 00 jmp *0x2fba(%rip) #
404010 <gettimeofday@GLIBC_2.2.5>
401056: 68 02 00 00 00 push $0x2
40105b: e9 c0 ff ff ff jmp 401020 <_init+0x20>
0000000000401060 <rand@plt>:
401060: ff 25 b2 2f 00 00 jmp *0x2fb2(%rip) #
404018 <rand@GLIBC_2.2.5>
401066: 68 03 00 00 00 push $0x3
40106b: e9 b0 ff ff ff jmp 401020 <_init+0x20>
SFrame excerpt as follows. See the "[m]" which denotes the
SFRAME_FDE_TYPE_PCMASK type FDE; the sfde_func_rep_size is not shown in
dump.
func idx [0]: pc = 0x401020, size = 16 bytes
STARTPC CFA FP RA
0000000000401020 sp+16 u f
0000000000401026 sp+24 u f
func idx [1]: pc = 0x401030, size = 64 bytes
STARTPC[m] CFA FP RA
0000000000000000 sp+8 u f
000000000000000b sp+16 u f
Now, what is the problem in doing a "lookup_pc % rep_block_size":
(Let me use "lookup_pc" here instead of "pc" used in the code block
above. I think it may aid in communicating the matter of interest.)
As you see above, for the second FDE (of type SFRAME_FDE_TYPE_PCMASK),
there are two FREs:
- FRE1 covering range [0, 0xa)
- FRE2 covering range [0xb, sfde_func_rep_size = 16)
We will want to locate the relevant FRE for all PCs in range [0,
sfde_func_size = 64). Hence the "% rep_block_size" in the calculation.
Doing a "lookup_pc - func_start_addr" before a modulo operation brings
the lookup range in the [0, sfde_func_size), making the calculation
independent of any relative placement of .plt/.sframe.
Additional paragraph, which may not shed more light, but here it is:
Note that "lookup_pc" (or argument "pc" in the sframe_find_fre ()) is
lookup_pc_vaddr - sframe_vaddr. E.g., a user will do a 0x401040 -
0x402228 (if looking up SFrame information for 0x401040 and SFrame is
known to be placed at 0x402228). And "func_start_addr" is the
"sfde_func_start_address" data field (SFrame FDE function start addr),
which on master is func_start_pc_vaddr - sframe_vaddr.
>> +int main (void)
>> +{
>> + unsigned int sframe_vaddr = 0x402220;
>> + unsigned int plt_vaddr = 0x401020;
>> + printf ("Testing with plt_vaddr = 0x%x; sframe_vaddr = 0x%x\n", plt_vaddr,
>> + sframe_vaddr);
>> + test_plt_findfre (plt_vaddr, sframe_vaddr);
>> +
>> + sframe_vaddr = 0x401020;
>> + plt_vaddr = 0x402220;
>> + printf ("Testing with plt_vaddr = 0x%x; sframe_vaddr = 0x%x\n", plt_vaddr,
>> + sframe_vaddr);
>> + test_plt_findfre (plt_vaddr, sframe_vaddr);
>> }
>
> While in a testcase it doesn't matter as much, I think it is generally a good
> idea to prefer %#x and alike over 0x%x even there, just to be consistent
> throughout.
Right. Done.
Thanks for reviewing
More information about the Binutils
mailing list