@@ -192,6 +192,7 @@ static const char * const map_type_name[] = {
192192 [BPF_MAP_TYPE_USER_RINGBUF ] = "user_ringbuf" ,
193193 [BPF_MAP_TYPE_CGRP_STORAGE ] = "cgrp_storage" ,
194194 [BPF_MAP_TYPE_ARENA ] = "arena" ,
195+ [BPF_MAP_TYPE_INSN_ARRAY ] = "insn_array" ,
195196};
196197
197198static const char * const prog_type_name [] = {
@@ -373,6 +374,7 @@ enum reloc_type {
373374 RELO_EXTERN_CALL ,
374375 RELO_SUBPROG_ADDR ,
375376 RELO_CORE ,
377+ RELO_INSN_ARRAY ,
376378};
377379
378380struct reloc_desc {
@@ -383,7 +385,10 @@ struct reloc_desc {
383385 struct {
384386 int map_idx ;
385387 int sym_off ;
386- int ext_idx ;
388+ union {
389+ int ext_idx ;
390+ int sym_size ;
391+ };
387392 };
388393 };
389394};
@@ -425,6 +430,11 @@ struct bpf_sec_def {
425430 libbpf_prog_attach_fn_t prog_attach_fn ;
426431};
427432
433+ struct bpf_light_subprog {
434+ __u32 sec_insn_off ;
435+ __u32 sub_insn_off ;
436+ };
437+
428438/*
429439 * bpf_prog should be a better name but it has been used in
430440 * linux/filter.h.
@@ -498,6 +508,9 @@ struct bpf_program {
498508 __u32 line_info_cnt ;
499509 __u32 prog_flags ;
500510 __u8 hash [SHA256_DIGEST_LENGTH ];
511+
512+ struct bpf_light_subprog * subprogs ;
513+ __u32 subprog_cnt ;
501514};
502515
503516struct bpf_struct_ops {
@@ -527,6 +540,7 @@ struct bpf_struct_ops {
527540#define STRUCT_OPS_SEC ".struct_ops"
528541#define STRUCT_OPS_LINK_SEC ".struct_ops.link"
529542#define ARENA_SEC ".addr_space.1"
543+ #define JUMPTABLES_SEC ".jumptables"
530544
531545enum libbpf_map_type {
532546 LIBBPF_MAP_UNSPEC ,
@@ -671,6 +685,7 @@ struct elf_state {
671685 int symbols_shndx ;
672686 bool has_st_ops ;
673687 int arena_data_shndx ;
688+ int jumptables_data_shndx ;
674689};
675690
676691struct usdt_manager ;
@@ -742,6 +757,16 @@ struct bpf_object {
742757 void * arena_data ;
743758 size_t arena_data_sz ;
744759
760+ void * jumptables_data ;
761+ size_t jumptables_data_sz ;
762+
763+ struct {
764+ struct bpf_program * prog ;
765+ int off ;
766+ int fd ;
767+ } * jumptable_maps ;
768+ size_t jumptable_map_cnt ;
769+
745770 struct kern_feature_cache * feat_cache ;
746771 char * token_path ;
747772 int token_fd ;
@@ -768,6 +793,7 @@ void bpf_program__unload(struct bpf_program *prog)
768793
769794 zfree (& prog -> func_info );
770795 zfree (& prog -> line_info );
796+ zfree (& prog -> subprogs );
771797}
772798
773799static void bpf_program__exit (struct bpf_program * prog )
@@ -3946,6 +3972,13 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
39463972 } else if (strcmp (name , ARENA_SEC ) == 0 ) {
39473973 obj -> efile .arena_data = data ;
39483974 obj -> efile .arena_data_shndx = idx ;
3975+ } else if (strcmp (name , JUMPTABLES_SEC ) == 0 ) {
3976+ obj -> jumptables_data = malloc (data -> d_size );
3977+ if (!obj -> jumptables_data )
3978+ return - ENOMEM ;
3979+ memcpy (obj -> jumptables_data , data -> d_buf , data -> d_size );
3980+ obj -> jumptables_data_sz = data -> d_size ;
3981+ obj -> efile .jumptables_data_shndx = idx ;
39493982 } else {
39503983 pr_info ("elf: skipping unrecognized data section(%d) %s\n" ,
39513984 idx , name );
@@ -4638,6 +4671,16 @@ static int bpf_program__record_reloc(struct bpf_program *prog,
46384671 return 0 ;
46394672 }
46404673
4674+ /* jump table data relocation */
4675+ if (shdr_idx == obj -> efile .jumptables_data_shndx ) {
4676+ reloc_desc -> type = RELO_INSN_ARRAY ;
4677+ reloc_desc -> insn_idx = insn_idx ;
4678+ reloc_desc -> map_idx = -1 ;
4679+ reloc_desc -> sym_off = sym -> st_value ;
4680+ reloc_desc -> sym_size = sym -> st_size ;
4681+ return 0 ;
4682+ }
4683+
46414684 /* generic map reference relocation */
46424685 if (type == LIBBPF_MAP_UNSPEC ) {
46434686 if (!bpf_object__shndx_is_maps (obj , shdr_idx )) {
@@ -6148,6 +6191,131 @@ static void poison_kfunc_call(struct bpf_program *prog, int relo_idx,
61486191 insn -> imm = POISON_CALL_KFUNC_BASE + ext_idx ;
61496192}
61506193
6194+ static int find_jt_map (struct bpf_object * obj , struct bpf_program * prog , int off )
6195+ {
6196+ size_t i ;
6197+
6198+ for (i = 0 ; i < obj -> jumptable_map_cnt ; i ++ ) {
6199+ /*
6200+ * This might happen that same offset is used for two different
6201+ * programs (as jump tables can be the same). However, for
6202+ * different programs different maps should be created.
6203+ */
6204+ if (obj -> jumptable_maps [i ].off == off &&
6205+ obj -> jumptable_maps [i ].prog == prog )
6206+ return obj -> jumptable_maps [i ].fd ;
6207+ }
6208+
6209+ return - ENOENT ;
6210+ }
6211+
6212+ static int add_jt_map (struct bpf_object * obj , struct bpf_program * prog , int off , int map_fd )
6213+ {
6214+ size_t new_cnt = obj -> jumptable_map_cnt + 1 ;
6215+ size_t size = sizeof (obj -> jumptable_maps [0 ]);
6216+ void * tmp ;
6217+
6218+ tmp = libbpf_reallocarray (obj -> jumptable_maps , new_cnt , size );
6219+ if (!tmp )
6220+ return - ENOMEM ;
6221+
6222+ obj -> jumptable_maps = tmp ;
6223+ obj -> jumptable_maps [new_cnt - 1 ].prog = prog ;
6224+ obj -> jumptable_maps [new_cnt - 1 ].off = off ;
6225+ obj -> jumptable_maps [new_cnt - 1 ].fd = map_fd ;
6226+ obj -> jumptable_map_cnt = new_cnt ;
6227+
6228+ return 0 ;
6229+ }
6230+
6231+ static int create_jt_map (struct bpf_object * obj , struct bpf_program * prog ,
6232+ int off , int size , int adjust_off )
6233+ {
6234+ const __u32 value_size = sizeof (struct bpf_insn_array_value );
6235+ const __u32 max_entries = size / value_size ;
6236+ struct bpf_insn_array_value val = {};
6237+ int map_fd , err ;
6238+ __u64 xlated_off ;
6239+ __u64 * jt ;
6240+ __u32 i ;
6241+
6242+ map_fd = find_jt_map (obj , prog , off );
6243+ if (map_fd >= 0 )
6244+ return map_fd ;
6245+
6246+ map_fd = bpf_map_create (BPF_MAP_TYPE_INSN_ARRAY , ".jumptables" ,
6247+ 4 , value_size , max_entries , NULL );
6248+ if (map_fd < 0 )
6249+ return map_fd ;
6250+
6251+ if (!obj -> jumptables_data ) {
6252+ pr_warn ("map '.jumptables': ELF file is missing jump table data\n" );
6253+ err = - EINVAL ;
6254+ goto err_close ;
6255+ }
6256+ if (off + size > obj -> jumptables_data_sz ) {
6257+ pr_warn ("jumptables_data size is %zd, trying to access %d\n" ,
6258+ obj -> jumptables_data_sz , off + size );
6259+ err = - EINVAL ;
6260+ goto err_close ;
6261+ }
6262+
6263+ jt = (__u64 * )(obj -> jumptables_data + off );
6264+ for (i = 0 ; i < max_entries ; i ++ ) {
6265+ /*
6266+ * LLVM-generated jump tables contain u64 records, however
6267+ * should contain values that fit in u32.
6268+ * The adjust_off provided by the caller adjusts the offset to
6269+ * be relative to the beginning of the main function
6270+ */
6271+ xlated_off = jt [i ]/sizeof (struct bpf_insn ) + adjust_off ;
6272+ if (xlated_off > UINT32_MAX ) {
6273+ pr_warn ("invalid jump table value %llx at offset %d (adjust_off %d)\n" ,
6274+ jt [i ], off + i , adjust_off );
6275+ err = - EINVAL ;
6276+ goto err_close ;
6277+ }
6278+
6279+ val .xlated_off = xlated_off ;
6280+ err = bpf_map_update_elem (map_fd , & i , & val , 0 );
6281+ if (err )
6282+ goto err_close ;
6283+ }
6284+
6285+ err = bpf_map_freeze (map_fd );
6286+ if (err )
6287+ goto err_close ;
6288+
6289+ err = add_jt_map (obj , prog , off , map_fd );
6290+ if (err )
6291+ goto err_close ;
6292+
6293+ return map_fd ;
6294+
6295+ err_close :
6296+ close (map_fd );
6297+ return err ;
6298+ }
6299+
6300+ /*
6301+ * In LLVM the .jumptables section contains jump tables entries relative to the
6302+ * section start. The BPF kernel-side code expects jump table offsets relative
6303+ * to the beginning of the program (passed in bpf(BPF_PROG_LOAD)). This helper
6304+ * computes a delta to be added when creating a map.
6305+ */
6306+ static int jt_adjust_off (struct bpf_program * prog , int insn_idx )
6307+ {
6308+ int i ;
6309+
6310+ for (i = prog -> subprog_cnt - 1 ; i >= 0 ; i -- ) {
6311+ if (insn_idx >= prog -> subprogs [i ].sub_insn_off )
6312+ return prog -> subprogs [i ].sub_insn_off - prog -> subprogs [i ].sec_insn_off ;
6313+ }
6314+
6315+ return - prog -> sec_insn_off ;
6316+ }
6317+
6318+
61516319/* Relocate data references within program code:
61526320 * - map references;
61536321 * - global variable references;
@@ -6239,6 +6407,21 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
62396407 case RELO_CORE :
62406408 /* will be handled by bpf_program_record_relos() */
62416409 break ;
6410+ case RELO_INSN_ARRAY : {
6411+ int map_fd ;
6412+
6413+ map_fd = create_jt_map (obj , prog , relo -> sym_off , relo -> sym_size ,
6414+ jt_adjust_off (prog , relo -> insn_idx ));
6415+ if (map_fd < 0 ) {
6416+ pr_warn ("prog '%s': relo #%d: can't create jump table: sym_off %u\n" ,
6417+ prog -> name , i , relo -> sym_off );
6418+ return map_fd ;
6419+ }
6420+ insn [0 ].src_reg = BPF_PSEUDO_MAP_VALUE ;
6421+ insn -> imm = map_fd ;
6422+ insn -> off = 0 ;
6423+ }
6424+ break ;
62426425 default :
62436426 pr_warn ("prog '%s': relo #%d: bad relo type %d\n" ,
62446427 prog -> name , i , relo -> type );
@@ -6436,6 +6619,24 @@ static int append_subprog_relos(struct bpf_program *main_prog, struct bpf_progra
64366619 return 0 ;
64376620}
64386621
6622+ static int save_subprog_offsets (struct bpf_program * main_prog , struct bpf_program * subprog )
6623+ {
6624+ size_t size = sizeof (main_prog -> subprogs [0 ]);
6625+ int new_cnt = main_prog -> subprog_cnt + 1 ;
6626+ void * tmp ;
6627+
6628+ tmp = libbpf_reallocarray (main_prog -> subprogs , new_cnt , size );
6629+ if (!tmp )
6630+ return - ENOMEM ;
6631+
6632+ main_prog -> subprogs = tmp ;
6633+ main_prog -> subprogs [new_cnt - 1 ].sec_insn_off = subprog -> sec_insn_off ;
6634+ main_prog -> subprogs [new_cnt - 1 ].sub_insn_off = subprog -> sub_insn_off ;
6635+ main_prog -> subprog_cnt = new_cnt ;
6636+
6637+ return 0 ;
6638+ }
6639+
64396640static int
64406641bpf_object__append_subprog_code (struct bpf_object * obj , struct bpf_program * main_prog ,
64416642 struct bpf_program * subprog )
@@ -6465,6 +6666,15 @@ bpf_object__append_subprog_code(struct bpf_object *obj, struct bpf_program *main
64656666 err = append_subprog_relos (main_prog , subprog );
64666667 if (err )
64676668 return err ;
6669+
6670+ /* Save subprogram offsets */
6671+ err = save_subprog_offsets (main_prog , subprog );
6672+ if (err ) {
6673+ pr_warn ("prog '%s': failed to add subprog offsets: %s\n" ,
6674+ main_prog -> name , errstr (err ));
6675+ return err ;
6676+ }
6677+
64686678 return 0 ;
64696679}
64706680
@@ -9232,6 +9442,15 @@ void bpf_object__close(struct bpf_object *obj)
92329442
92339443 zfree (& obj -> arena_data );
92349444
9445+ zfree (& obj -> jumptables_data );
9446+ obj -> jumptables_data_sz = 0 ;
9447+
9448+ if (obj -> jumptable_maps && obj -> jumptable_map_cnt ) {
9449+ for (i = 0 ; i < obj -> jumptable_map_cnt ; i ++ )
9450+ close (obj -> jumptable_maps [i ].fd );
9451+ }
9452+ zfree (& obj -> jumptable_maps );
9453+
92359454 free (obj );
92369455}
92379456
0 commit comments