@@ -190,6 +190,7 @@ static const char * const map_type_name[] = {
190190	[BPF_MAP_TYPE_USER_RINGBUF ]             =  "user_ringbuf" ,
191191	[BPF_MAP_TYPE_CGRP_STORAGE ]		=  "cgrp_storage" ,
192192	[BPF_MAP_TYPE_ARENA ]			=  "arena" ,
193+ 	[BPF_MAP_TYPE_INSN_ARRAY ]		=  "insn_array" ,
193194};
194195
195196static  const  char  *  const  prog_type_name [] =  {
@@ -369,6 +370,7 @@ enum reloc_type {
369370	RELO_EXTERN_CALL ,
370371	RELO_SUBPROG_ADDR ,
371372	RELO_CORE ,
373+ 	RELO_INSN_ARRAY ,
372374};
373375
374376struct  reloc_desc  {
@@ -379,7 +381,16 @@ struct reloc_desc {
379381		struct  {
380382			int  map_idx ;
381383			int  sym_off ;
382- 			int  ext_idx ;
384+ 			/* 
385+ 			 * The following two fields can be unionized, as the 
386+ 			 * ext_idx field is used for extern symbols, and the 
387+ 			 * sym_size is used for jump tables, which are never 
388+ 			 * extern 
389+ 			 */ 
390+ 			union  {
391+ 				int  ext_idx ;
392+ 				int  sym_size ;
393+ 			};
383394		};
384395	};
385396};
@@ -421,6 +432,11 @@ struct bpf_sec_def {
421432	libbpf_prog_attach_fn_t  prog_attach_fn ;
422433};
423434
435+ struct  bpf_light_subprog  {
436+ 	__u32  sec_insn_off ;
437+ 	__u32  sub_insn_off ;
438+ };
439+ 
424440/* 
425441 * bpf_prog should be a better name but it has been used in 
426442 * linux/filter.h. 
@@ -494,6 +510,9 @@ struct bpf_program {
494510	__u32  line_info_cnt ;
495511	__u32  prog_flags ;
496512	__u8   hash [SHA256_DIGEST_LENGTH ];
513+ 
514+ 	struct  bpf_light_subprog  * subprogs ;
515+ 	__u32  subprog_cnt ;
497516};
498517
499518struct  bpf_struct_ops  {
@@ -667,6 +686,7 @@ struct elf_state {
667686	int  symbols_shndx ;
668687	bool  has_st_ops ;
669688	int  arena_data_shndx ;
689+ 	int  jumptables_data_shndx ;
670690};
671691
672692struct  usdt_manager ;
@@ -738,6 +758,16 @@ struct bpf_object {
738758	void  * arena_data ;
739759	size_t  arena_data_sz ;
740760
761+ 	void  * jumptables_data ;
762+ 	size_t  jumptables_data_sz ;
763+ 
764+ 	struct  {
765+ 		struct  bpf_program  * prog ;
766+ 		int  sym_off ;
767+ 		int  fd ;
768+ 	} * jumptable_maps ;
769+ 	size_t  jumptable_map_cnt ;
770+ 
741771	struct  kern_feature_cache  * feat_cache ;
742772	char  * token_path ;
743773	int  token_fd ;
@@ -764,6 +794,7 @@ void bpf_program__unload(struct bpf_program *prog)
764794
765795	zfree (& prog -> func_info );
766796	zfree (& prog -> line_info );
797+ 	zfree (& prog -> subprogs );
767798}
768799
769800static  void  bpf_program__exit (struct  bpf_program  * prog )
@@ -3942,6 +3973,13 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
39423973			} else  if  (strcmp (name , ARENA_SEC ) ==  0 ) {
39433974				obj -> efile .arena_data  =  data ;
39443975				obj -> efile .arena_data_shndx  =  idx ;
3976+ 			} else  if  (strcmp (name , JUMPTABLES_SEC ) ==  0 ) {
3977+ 				obj -> jumptables_data  =  malloc (data -> d_size );
3978+ 				if  (!obj -> jumptables_data )
3979+ 					return  - ENOMEM ;
3980+ 				memcpy (obj -> jumptables_data , data -> d_buf , data -> d_size );
3981+ 				obj -> jumptables_data_sz  =  data -> d_size ;
3982+ 				obj -> efile .jumptables_data_shndx  =  idx ;
39453983			} else  {
39463984				pr_info ("elf: skipping unrecognized data section(%d) %s\n" ,
39473985					idx , name );
@@ -4634,6 +4672,16 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
46344672		return  0 ;
46354673	}
46364674
4675+ 	/* jump table data relocation */ 
4676+ 	if  (shdr_idx  ==  obj -> efile .jumptables_data_shndx ) {
4677+ 		reloc_desc -> type  =  RELO_INSN_ARRAY ;
4678+ 		reloc_desc -> insn_idx  =  insn_idx ;
4679+ 		reloc_desc -> map_idx  =  -1 ;
4680+ 		reloc_desc -> sym_off  =  sym -> st_value ;
4681+ 		reloc_desc -> sym_size  =  sym -> st_size ;
4682+ 		return  0 ;
4683+ 	}
4684+ 
46374685	/* generic map reference relocation */ 
46384686	if  (type  ==  LIBBPF_MAP_UNSPEC ) {
46394687		if  (!bpf_object__shndx_is_maps (obj , shdr_idx )) {
@@ -6144,6 +6192,157 @@ static void poison_kfunc_call(struct bpf_program *prog, int relo_idx,
61446192	insn -> imm  =  POISON_CALL_KFUNC_BASE  +  ext_idx ;
61456193}
61466194
6195+ static  int  find_jt_map (struct  bpf_object  * obj , struct  bpf_program  * prog , int  sym_off )
6196+ {
6197+ 	size_t  i ;
6198+ 
6199+ 	for  (i  =  0 ; i  <  obj -> jumptable_map_cnt ; i ++ ) {
6200+ 		/* 
6201+ 		 * This might happen that same offset is used for two different 
6202+ 		 * programs (as jump tables can be the same). However, for 
6203+ 		 * different programs different maps should be created. 
6204+ 		 */ 
6205+ 		if  (obj -> jumptable_maps [i ].sym_off  ==  sym_off  && 
6206+ 		    obj -> jumptable_maps [i ].prog  ==  prog )
6207+ 			return  obj -> jumptable_maps [i ].fd ;
6208+ 	}
6209+ 
6210+ 	return  - ENOENT ;
6211+ }
6212+ 
6213+ static  int  add_jt_map (struct  bpf_object  * obj , struct  bpf_program  * prog , int  sym_off , int  map_fd )
6214+ {
6215+ 	size_t  new_cnt  =  obj -> jumptable_map_cnt  +  1 ;
6216+ 	size_t  size  =  sizeof (obj -> jumptable_maps [0 ]);
6217+ 	void  * tmp ;
6218+ 
6219+ 	tmp  =  libbpf_reallocarray (obj -> jumptable_maps , new_cnt , size );
6220+ 	if  (!tmp )
6221+ 		return  - ENOMEM ;
6222+ 
6223+ 	obj -> jumptable_maps  =  tmp ;
6224+ 	obj -> jumptable_maps [new_cnt  -  1 ].prog  =  prog ;
6225+ 	obj -> jumptable_maps [new_cnt  -  1 ].sym_off  =  sym_off ;
6226+ 	obj -> jumptable_maps [new_cnt  -  1 ].fd  =  map_fd ;
6227+ 	obj -> jumptable_map_cnt  =  new_cnt ;
6228+ 
6229+ 	return  0 ;
6230+ }
6231+ 
6232+ static  int  find_subprog_idx (struct  bpf_program  * prog , int  insn_idx )
6233+ {
6234+ 	int  i ;
6235+ 
6236+ 	for  (i  =  prog -> subprog_cnt  -  1 ; i  >= 0 ; i -- ) {
6237+ 		if  (insn_idx  >= prog -> subprogs [i ].sub_insn_off )
6238+ 			return  i ;
6239+ 	}
6240+ 
6241+ 	return  -1 ;
6242+ }
6243+ 
6244+ static  int  create_jt_map (struct  bpf_object  * obj , struct  bpf_program  * prog , struct  reloc_desc  * relo )
6245+ {
6246+ 	const  __u32  jt_entry_size  =  8 ;
6247+ 	int  sym_off  =  relo -> sym_off ;
6248+ 	int  jt_size  =  relo -> sym_size ;
6249+ 	__u32  max_entries  =  jt_size  / jt_entry_size ;
6250+ 	__u32  value_size  =  sizeof (struct  bpf_insn_array_value );
6251+ 	struct  bpf_insn_array_value  val  =  {};
6252+ 	int  subprog_idx ;
6253+ 	int  map_fd , err ;
6254+ 	__u64  insn_off ;
6255+ 	__u64  * jt ;
6256+ 	__u32  i ;
6257+ 
6258+ 	map_fd  =  find_jt_map (obj , prog , sym_off );
6259+ 	if  (map_fd  >= 0 )
6260+ 		return  map_fd ;
6261+ 
6262+ 	if  (sym_off  % jt_entry_size ) {
6263+ 		pr_warn ("jumptable start %d should be multiple of %u\n" ,
6264+ 			sym_off , jt_entry_size );
6265+ 		return  - EINVAL ;
6266+ 	}
6267+ 
6268+ 	if  (jt_size  % jt_entry_size ) {
6269+ 		pr_warn ("jumptable size %d should be multiple of %u\n" ,
6270+ 			jt_size , jt_entry_size );
6271+ 		return  - EINVAL ;
6272+ 	}
6273+ 
6274+ 	map_fd  =  bpf_map_create (BPF_MAP_TYPE_INSN_ARRAY , ".jumptables" ,
6275+ 				4 , value_size , max_entries , NULL );
6276+ 	if  (map_fd  <  0 )
6277+ 		return  map_fd ;
6278+ 
6279+ 	if  (!obj -> jumptables_data ) {
6280+ 		pr_warn ("map '.jumptables': ELF file is missing jump table data\n" );
6281+ 		err  =  - EINVAL ;
6282+ 		goto err_close ;
6283+ 	}
6284+ 	if  (sym_off  +  jt_size  >  obj -> jumptables_data_sz ) {
6285+ 		pr_warn ("jumptables_data size is %zd, trying to access %d\n" ,
6286+ 			obj -> jumptables_data_sz , sym_off  +  jt_size );
6287+ 		err  =  - EINVAL ;
6288+ 		goto err_close ;
6289+ 	}
6290+ 
6291+ 	subprog_idx  =  -1 ; /* main program */ 
6292+ 	if  (relo -> insn_idx  <  0  ||  relo -> insn_idx  >= prog -> insns_cnt ) {
6293+ 		pr_warn ("invalid instruction index %d\n" , relo -> insn_idx );
6294+ 		err  =  - EINVAL ;
6295+ 		goto err_close ;
6296+ 	}
6297+ 	if  (prog -> subprogs )
6298+ 		subprog_idx  =  find_subprog_idx (prog , relo -> insn_idx );
6299+ 
6300+ 	jt  =  (__u64  * )(obj -> jumptables_data  +  sym_off );
6301+ 	for  (i  =  0 ; i  <  max_entries ; i ++ ) {
6302+ 		/* 
6303+ 		 * The offset should be made to be relative to the beginning of 
6304+ 		 * the main function, not the subfunction. 
6305+ 		 */ 
6306+ 		insn_off  =  jt [i ]/sizeof (struct  bpf_insn );
6307+ 		if  (subprog_idx  >= 0 ) {
6308+ 			insn_off  -=  prog -> subprogs [subprog_idx ].sec_insn_off ;
6309+ 			insn_off  +=  prog -> subprogs [subprog_idx ].sub_insn_off ;
6310+ 		} else  {
6311+ 			insn_off  -=  prog -> sec_insn_off ;
6312+ 		}
6313+ 
6314+ 		/* 
6315+ 		 * LLVM-generated jump tables contain u64 records, however 
6316+ 		 * should contain values that fit in u32. 
6317+ 		 */ 
6318+ 		if  (insn_off  >  UINT32_MAX ) {
6319+ 			pr_warn ("invalid jump table value 0x%llx at offset %d\n" ,
6320+ 				jt [i ], sym_off  +  i );
6321+ 			err  =  - EINVAL ;
6322+ 			goto err_close ;
6323+ 		}
6324+ 
6325+ 		val .orig_off  =  insn_off ;
6326+ 		err  =  bpf_map_update_elem (map_fd , & i , & val , 0 );
6327+ 		if  (err )
6328+ 			goto err_close ;
6329+ 	}
6330+ 
6331+ 	err  =  bpf_map_freeze (map_fd );
6332+ 	if  (err )
6333+ 		goto err_close ;
6334+ 
6335+ 	err  =  add_jt_map (obj , prog , sym_off , map_fd );
6336+ 	if  (err )
6337+ 		goto err_close ;
6338+ 
6339+ 	return  map_fd ;
6340+ 
6341+ err_close :
6342+ 	close (map_fd );
6343+ 	return  err ;
6344+ }
6345+ 
61476346/* Relocate data references within program code: 
61486347 *  - map references; 
61496348 *  - global variable references; 
@@ -6235,6 +6434,20 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
62356434		case  RELO_CORE :
62366435			/* will be handled by bpf_program_record_relos() */ 
62376436			break ;
6437+ 		case  RELO_INSN_ARRAY : {
6438+ 			int  map_fd ;
6439+ 
6440+ 			map_fd  =  create_jt_map (obj , prog , relo );
6441+ 			if  (map_fd  <  0 ) {
6442+ 				pr_warn ("prog '%s': relo #%d: can't create jump table: sym_off %u\n" ,
6443+ 						prog -> name , i , relo -> sym_off );
6444+ 				return  map_fd ;
6445+ 			}
6446+ 			insn [0 ].src_reg  =  BPF_PSEUDO_MAP_VALUE ;
6447+ 			insn -> imm  =  map_fd ;
6448+ 			insn -> off  =  0 ;
6449+ 		}
6450+ 			break ;
62386451		default :
62396452			pr_warn ("prog '%s': relo #%d: bad relo type %d\n" ,
62406453				prog -> name , i , relo -> type );
@@ -6432,6 +6645,24 @@ static int append_subprog_relos(struct bpf_program *main_prog, struct bpf_progra
64326645	return  0 ;
64336646}
64346647
6648+ static  int  save_subprog_offsets (struct  bpf_program  * main_prog , struct  bpf_program  * subprog )
6649+ {
6650+ 	size_t  size  =  sizeof (main_prog -> subprogs [0 ]);
6651+ 	int  new_cnt  =  main_prog -> subprog_cnt  +  1 ;
6652+ 	void  * tmp ;
6653+ 
6654+ 	tmp  =  libbpf_reallocarray (main_prog -> subprogs , new_cnt , size );
6655+ 	if  (!tmp )
6656+ 		return  - ENOMEM ;
6657+ 
6658+ 	main_prog -> subprogs  =  tmp ;
6659+ 	main_prog -> subprogs [new_cnt  -  1 ].sec_insn_off  =  subprog -> sec_insn_off ;
6660+ 	main_prog -> subprogs [new_cnt  -  1 ].sub_insn_off  =  subprog -> sub_insn_off ;
6661+ 	main_prog -> subprog_cnt  =  new_cnt ;
6662+ 
6663+ 	return  0 ;
6664+ }
6665+ 
64356666static  int 
64366667bpf_object__append_subprog_code (struct  bpf_object  * obj , struct  bpf_program  * main_prog ,
64376668				struct  bpf_program  * subprog )
@@ -6461,6 +6692,15 @@ bpf_object__append_subprog_code(struct bpf_object *obj, struct bpf_program *main
64616692	err  =  append_subprog_relos (main_prog , subprog );
64626693	if  (err )
64636694		return  err ;
6695+ 
6696+ 	/* Save subprogram offsets */ 
6697+ 	err  =  save_subprog_offsets (main_prog , subprog );
6698+ 	if  (err ) {
6699+ 		pr_warn ("prog '%s': failed to add subprog offsets: %s\n" ,
6700+ 			main_prog -> name , errstr (err ));
6701+ 		return  err ;
6702+ 	}
6703+ 
64646704	return  0 ;
64656705}
64666706
@@ -9228,6 +9468,13 @@ void bpf_object__close(struct bpf_object *obj)
92289468
92299469	zfree (& obj -> arena_data );
92309470
9471+ 	zfree (& obj -> jumptables_data );
9472+ 	obj -> jumptables_data_sz  =  0 ;
9473+ 
9474+ 	for  (i  =  0 ; i  <  obj -> jumptable_map_cnt ; i ++ )
9475+ 		close (obj -> jumptable_maps [i ].fd );
9476+ 	zfree (& obj -> jumptable_maps );
9477+ 
92319478	free (obj );
92329479}
92339480
0 commit comments