[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