Re: [RFC dwarves 1/4] dwarf_loader: mark functions that do not use expected registers for params

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 20/02/2023 19:03, Alexei Starovoitov wrote:
> On Fri, Feb 17, 2023 at 11:10:30PM +0000, Alan Maguire wrote:
>> Calling conventions dictate which registers are used for
>> function parameters.
>>
>> When a function is optimized however, we need to ensure that
>> the non-optimized parameters do not violate expectations about
>> register use as this would violate expectations for tracing.
>> At CU initialization, create a mapping from parameter index
>> to expected DW_OP_reg, and use it to validate parameters
>> match with expectations.  A parameter which is passed via
>> the stack, as a constant, or uses an unexpected register,
>> violates these expectations and it (and the associated
>> function) are marked as having unexpected register mapping.
>>
>> Note though that there is as exception here that needs to
>> be handled; when a (typedef) struct is passed as a parameter,
>> it can use multiple registers so will throw off later register
>> expectations.  Exempt functions that have unexpected
>> register usage _and_ struct parameters (examples are found
>> in the "tracing_struct" test).
>>
>> Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx>
>> ---
>>  dwarf_loader.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++---
>>  dwarves.h      |   5 +++
>>  2 files changed, 109 insertions(+), 5 deletions(-)
>>
>> diff --git a/dwarf_loader.c b/dwarf_loader.c
>> index acdb68d..014e130 100644
>> --- a/dwarf_loader.c
>> +++ b/dwarf_loader.c
>> @@ -1022,6 +1022,51 @@ static int arch__nr_register_params(const GElf_Ehdr *ehdr)
>>  	return 0;
>>  }
>>  
>> +/* map from parameter index (0 for first, ...) to expected DW_OP_reg.
>> + * This will allow us to identify cases where optimized-out parameters
>> + * interfere with expectations about register contents on function
>> + * entry.
>> + */
>> +static void arch__set_register_params(const GElf_Ehdr *ehdr, struct cu *cu)
>> +{
>> +	memset(cu->register_params, -1, sizeof(cu->register_params));
>> +
>> +	switch (ehdr->e_machine) {
>> +	case EM_S390:
>> +		/* https://github.com/IBM/s390x-abi/releases/download/v1.6/lzsabi_s390x.pdf */
>> +		cu->register_params[0] = DW_OP_reg2;	// %r2
>> +		cu->register_params[1] = DW_OP_reg3;	// %r3
>> +		cu->register_params[2] = DW_OP_reg4;	// %r4
>> +		cu->register_params[3] = DW_OP_reg5;	// %r5
>> +		cu->register_params[4] = DW_OP_reg6;	// %r6
>> +		return;
>> +	case EM_X86_64:
>> +		/* //en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI */
>> +		cu->register_params[0] = DW_OP_reg5;	// %rdi
>> +		cu->register_params[1] = DW_OP_reg4;	// %rsi
>> +		cu->register_params[2] = DW_OP_reg1;	// %rdx
>> +		cu->register_params[3] = DW_OP_reg2;	// %rcx
>> +		cu->register_params[4] = DW_OP_reg8;	// %r8
>> +		cu->register_params[5] = DW_OP_reg9;	// %r9
>> +		return;
>> +	case EM_ARM:
>> +		/* https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#machine-registers */
>> +	case EM_AARCH64:
>> +		/* https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#machine-registers */
>> +		cu->register_params[0] = DW_OP_reg0;
>> +		cu->register_params[1] = DW_OP_reg1;
>> +		cu->register_params[2] = DW_OP_reg2;
>> +		cu->register_params[3] = DW_OP_reg3;
>> +		cu->register_params[4] = DW_OP_reg4;
>> +		cu->register_params[5] = DW_OP_reg5;
>> +		cu->register_params[6] = DW_OP_reg6;
>> +		cu->register_params[7] = DW_OP_reg7;
>> +		return;
>> +	default:
>> +		return;
>> +	}
>> +}
>> +
>>  static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu,
>>  					struct conf_load *conf, int param_idx)
>>  {
>> @@ -1075,18 +1120,28 @@ static struct parameter *parameter__new(Dwarf_Die *die, struct cu *cu,
>>  		if (parm->has_loc &&
>>  		    attr_location(die, &loc.expr, &loc.exprlen) == 0 &&
>>  			loc.exprlen != 0) {
>> +			int expected_reg = cu->register_params[param_idx];
>>  			Dwarf_Op *expr = loc.expr;
>>  
>>  			switch (expr->atom) {
>>  			case DW_OP_reg0 ... DW_OP_reg31:
>> +				/* mark parameters that use an unexpected
>> +				 * register to hold a parameter; these will
>> +				 * be problematic for users of BTF as they
>> +				 * violate expectations about register
>> +				 * contents.
>> +				 */
>> +				if (expected_reg >= 0 && expected_reg != expr->atom)
>> +					parm->unexpected_reg = 1;
>> +				break;
> 
> Overall I guess it's a step forward, since it addresses the immediate issue,
> but probably too fragile long term.
> 
> Your earlier example:
>  __bpf_kfunc void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked)
> 
> had
> 0x0891dabe:     DW_TAG_formal_parameter
>                   DW_AT_location        (indexed (0x7a) loclist = 0x00f50eb1:
>                      [0xffffffff82031185, 0xffffffff8203119e): DW_OP_reg5 RDI
>                      [0xffffffff8203119e, 0xffffffff820311cc): DW_OP_reg3 RBX
>                      [0xffffffff820311cc, 0xffffffff820311d1): DW_OP_reg5 RDI
>                      [0xffffffff820311d1, 0xffffffff820311d2): DW_OP_reg3 RBX
>                      [0xffffffff820311d2, 0xffffffff820311d8): DW_OP_entry_value(DW_OP_reg5 RDI), DW_OP_stack_value)
> 
> 0x0891dad4:     DW_TAG_formal_parameter
>                   DW_AT_location        (indexed (0x7b) loclist = 0x00f50eda:
>                      [0xffffffff82031185, 0xffffffff820311bc): DW_OP_reg1 RDX
>                      [0xffffffff820311bc, 0xffffffff820311c8): DW_OP_reg0 RAX
>                      [0xffffffff820311c8, 0xffffffff820311d1): DW_OP_reg1 RDX)
>                   DW_AT_name    ("acked")
> 
> Both args will fail above check. If I'm reading above code correctly.
> It checks that every reg in DW_AT_location matches ?

It checks location info for those that have it; so in this case the location
lists specify rdi on entry for the first parameter (sk)


0x068a0f3b:     DW_TAG_formal_parameter
                  DW_AT_location        (indexed (0x74) loclist = 0x00a4c5a0:
                     [0xffffffff81b87849, 0xffffffff81b87866): DW_OP_reg5 RDI
                     [0xffffffff81b87866, 0xffffffff81b87899): DW_OP_reg3 RBX
                     [0xffffffff81b87899, 0xffffffff81b878a0): DW_OP_entry_value(DW_OP_reg5 RDI), DW_OP_stack_value)
                  DW_AT_name    ("sk")
                  DW_AT_decl_file       ("/home/opc/src/clang/bpf-next/net/ipv4/tcp_cong.c")
                  DW_AT_decl_line       (446)
                  DW_AT_type    (0x06886461 "sock *")


no location info for the second (ack):

0x068a0f47:     DW_TAG_formal_parameter
                  DW_AT_name    ("ack")
                  DW_AT_decl_file       ("/home/opc/src/clang/bpf-next/net/ipv4/tcp_cong.c")
                  DW_AT_decl_line       (446)
                  DW_AT_type    (0x06886451 "u32")

...so matching it is skipped, and rdx as the first element in the location list
for the third parameter (acked):

0x068a0f52:     DW_TAG_formal_parameter
                  DW_AT_location        (indexed (0x75) loclist = 0x00a4c5bb:
                     [0xffffffff81b87849, 0xffffffff81b87884): DW_OP_reg1 RDX
                     [0xffffffff81b87884, 0xffffffff81b87890): DW_OP_reg0 RAX
                     [0xffffffff81b87890, 0xffffffff81b87898): DW_OP_reg1 RDX)
                  DW_AT_name    ("acked")
                  DW_AT_decl_file       ("/home/opc/src/clang/bpf-next/net/ipv4/tcp_cong.c")
                  DW_AT_decl_line       (446)
                  DW_AT_type    (0x06886451 "u32")


So this would be okay using the register-checking approach.

> Or just first ?
> 
>>  			case DW_OP_breg0 ... DW_OP_breg31:
>>  				break;
>>  			default:
>> -				parm->optimized = 1;
>> +				parm->unexpected_reg = 1;
>>  				break;
>>  			}
>>  		} else if (has_const_value) {
>> -			parm->optimized = 1;
>> +			parm->unexpected_reg = 1;
> 
> Is this part too restrictive as well?
> Just because one arg is constant it doesn't mean that the calling convention
> is not correct for this and other args.
> 

Great catch; this part is wrong; should just be parm->optimized = 1 as it
was before.



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux