@@ -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