Re: [PATCH bpf-next v2 2/2] tools/resolve_btfids: fix cross-compilation to non-host endianness

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

 



On 2/1/24 17:36, Daniel Borkmann wrote:
> On 1/31/24 5:24 PM, Viktor Malik wrote:
>> The .BTF_ids section is pre-filled with zeroed BTF ID entries during the
>> build and afterwards patched by resolve_btfids with correct values.
>> Since resolve_btfids always writes in host-native endianness, it relies
>> on libelf to do the translation when the target ELF is cross-compiled to
>> a different endianness (this was introduced in commit 61e8aeda9398
>> ("bpf: Fix libelf endian handling in resolv_btfids")).
>>
>> Unfortunately, the translation will corrupt the flags fields of SET8
>> entries because these were written during vmlinux compilation and are in
>> the correct endianness already. This will lead to numerous selftests
>> failures such as:
>>
>>      $ sudo ./test_verifier 502 502
>>      #502/p sleepable fentry accept FAIL
>>      Failed to load prog 'Invalid argument'!
>>      bpf_fentry_test1 is not sleepable
>>      verification time 34 usec
>>      stack depth 0
>>      processed 0 insns (limit 1000000) max_states_per_insn 0 total_states 0 peak_states 0 mark_read 0
>>      Summary: 0 PASSED, 0 SKIPPED, 1 FAILED
>>
>> Since it's not possible to instruct libelf to translate just certain
>> values, let's manually bswap the flags in resolve_btfids when needed, so
>> that libelf then translates everything correctly.
>>
>> Fixes: ef2c6f370a63 ("tools/resolve_btfids: Add support for 8-byte BTF sets")
>> Signed-off-by: Viktor Malik <vmalik@xxxxxxxxxx>
>> ---
>>   tools/bpf/resolve_btfids/main.c | 27 ++++++++++++++++++++++++++-
>>   1 file changed, 26 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>> index 7badf1557e5c..d01603ef6283 100644
>> --- a/tools/bpf/resolve_btfids/main.c
>> +++ b/tools/bpf/resolve_btfids/main.c
>> @@ -652,13 +652,23 @@ static int sets_patch(struct object *obj)
>>   	Elf_Data *data = obj->efile.idlist;
>>   	int *ptr = data->d_buf;
>>   	struct rb_node *next;
>> +	GElf_Ehdr ehdr;
>> +	int need_bswap;
>> +
>> +	if (gelf_getehdr(obj->efile.elf, &ehdr) == NULL) {
>> +		pr_err("FAILED cannot get ELF header: %s\n",
>> +			elf_errmsg(-1));
>> +		return -1;
>> +	}
>> +	need_bswap = (__BYTE_ORDER == __LITTLE_ENDIAN) !=
>> +		     (ehdr.e_ident[EI_DATA] == ELFDATA2LSB);
>>   
>>   	next = rb_first(&obj->sets);
>>   	while (next) {
>>   		unsigned long addr, idx;
>>   		struct btf_id *id;
>>   		void *base;
>> -		int cnt, size;
>> +		int cnt, size, i;
>>   
>>   		id   = rb_entry(next, struct btf_id, rb_node);
>>   		addr = id->addr[0];
>> @@ -686,6 +696,21 @@ static int sets_patch(struct object *obj)
>>   			base = set8->pairs;
>>   			cnt = set8->cnt;
>>   			size = sizeof(set8->pairs[0]);
>> +
>> +			/*
>> +			 * When ELF endianness does not match endianness of the
>> +			 * host, libelf will do the translation when updating
>> +			 * the ELF. This, however, corrupts SET8 flags which are
>> +			 * already in the target endianness. So, let's bswap
>> +			 * them to the host endianness and libelf will then
>> +			 * correctly translate everything.
>> +			 */
>> +			if (need_bswap) {
>> +				for (i = 0; i < cnt; i++) {
>> +					set8->pairs[i].flags =
>> +						bswap_32(set8->pairs[i].flags);
>> +				}
>> +			}
>>   		}
>>   
> 
> Could we improve that somewhat, e.g. gathering endianness could be moved into
> elf_collect() and the test could also be simplified (if I'm not missing sth) ?
> 
> Like the below (not even compile-tested ...) :

Thanks for the suggestion, that looks nicer than my version. I'll use
the below, it should work pretty much out of the box.

Viktor

> 
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index 7badf1557e5c..7b5f592fe79c 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -90,6 +90,14 @@
> 
>   #define ADDR_CNT	100
> 
> +#if __BYTE_ORDER == __LITTLE_ENDIAN
> +# define ELFDATANATIVE	ELFDATA2LSB
> +#elif __BYTE_ORDER == __BIG_ENDIAN
> +# define ELFDATANATIVE	ELFDATA2MSB
> +#else
> +# error "Unknown machine endianness!"
> +#endif
> +
>   struct btf_id {
>   	struct rb_node	 rb_node;
>   	char		*name;
> @@ -117,6 +125,7 @@ struct object {
>   		int		 idlist_shndx;
>   		size_t		 strtabidx;
>   		unsigned long	 idlist_addr;
> +		int		 encoding;
>   	} efile;
> 
>   	struct rb_root	sets;
> @@ -320,6 +329,7 @@ static int elf_collect(struct object *obj)
>   {
>   	Elf_Scn *scn = NULL;
>   	size_t shdrstrndx;
> +	GElf_Ehdr ehdr;
>   	int idx = 0;
>   	Elf *elf;
>   	int fd;
> @@ -351,6 +361,13 @@ static int elf_collect(struct object *obj)
>   		return -1;
>   	}
> 
> +	if (gelf_getehdr(obj->efile.elf, &ehdr) == NULL) {
> +		pr_err("FAILED cannot get ELF header: %s\n", elf_errmsg(-1));
> +		return -1;
> +	}
> +
> +	obj->efile.encoding = ehdr.e_ident[EI_DATA];
> +
>   	/*
>   	 * Scan all the elf sections and look for save data
>   	 * from .BTF_ids section and symbols.
> @@ -649,6 +666,7 @@ static int cmp_id(const void *pa, const void *pb)
> 
>   static int sets_patch(struct object *obj)
>   {
> +	bool need_bswap = obj->efile.encoding != ELFDATANATIVE;
>   	Elf_Data *data = obj->efile.idlist;
>   	int *ptr = data->d_buf;
>   	struct rb_node *next;
> @@ -658,7 +676,7 @@ static int sets_patch(struct object *obj)
>   		unsigned long addr, idx;
>   		struct btf_id *id;
>   		void *base;
> -		int cnt, size;
> +		int cnt, size, i;
> 
>   		id   = rb_entry(next, struct btf_id, rb_node);
>   		addr = id->addr[0];
> @@ -686,6 +704,21 @@ static int sets_patch(struct object *obj)
>   			base = set8->pairs;
>   			cnt = set8->cnt;
>   			size = sizeof(set8->pairs[0]);
> +
> +			/*
> +			 * When ELF endianness does not match endianness of the
> +			 * host, libelf will do the translation when updating
> +			 * the ELF. This, however, corrupts SET8 flags which are
> +			 * already in the target endianness. So, let's bswap
> +			 * them to the host endianness and libelf will then
> +			 * correctly translate everything.
> +			 */
> +			if (need_bswap) {
> +				for (i = 0; i < cnt; i++) {
> +					set8->pairs[i].flags =
> +						bswap_32(set8->pairs[i].flags);
> +				}
> +			}
>   		}
> 
>   		pr_debug("sorting  addr %5lu: cnt %6d [%s]\n",





[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