@@ -418,8 +418,16 @@ static int decode_instructions(struct objtool_file *file)
418418 return -1 ;
419419 }
420420
421- sym_for_each_insn (file , func , insn )
421+ sym_for_each_insn (file , func , insn ) {
422422 insn -> func = func ;
423+ if (insn -> type == INSN_ENDBR ) {
424+ if (insn -> offset == insn -> func -> offset ) {
425+ file -> nr_endbr ++ ;
426+ } else {
427+ file -> nr_endbr_int ++ ;
428+ }
429+ }
430+ }
423431 }
424432 }
425433
@@ -1171,6 +1179,19 @@ static void add_retpoline_call(struct objtool_file *file, struct instruction *in
11711179
11721180 annotate_call_site (file , insn , false);
11731181}
1182+
1183+ static bool same_function (struct instruction * insn1 , struct instruction * insn2 )
1184+ {
1185+ return insn1 -> func -> pfunc == insn2 -> func -> pfunc ;
1186+ }
1187+
1188+ static bool is_first_func_insn (struct instruction * insn )
1189+ {
1190+ return insn -> offset == insn -> func -> offset ||
1191+ (insn -> type == INSN_ENDBR &&
1192+ insn -> offset == insn -> func -> offset + insn -> len );
1193+ }
1194+
11741195/*
11751196 * Find the destination instructions for all jumps.
11761197 */
@@ -1251,8 +1272,8 @@ static int add_jump_destinations(struct objtool_file *file)
12511272 insn -> func -> cfunc = insn -> jump_dest -> func ;
12521273 insn -> jump_dest -> func -> pfunc = insn -> func ;
12531274
1254- } else if (insn -> jump_dest -> func -> pfunc != insn -> func -> pfunc &&
1255- insn -> jump_dest -> offset == insn -> jump_dest -> func -> offset ) {
1275+ } else if (! same_function ( insn , insn -> jump_dest ) &&
1276+ is_first_func_insn ( insn -> jump_dest ) ) {
12561277 /* internal sibling call (without reloc) */
12571278 add_call_dest (file , insn , insn -> jump_dest -> func , true);
12581279 }
@@ -1842,6 +1863,16 @@ static int read_unwind_hints(struct objtool_file *file)
18421863
18431864 insn -> hint = true;
18441865
1866+ if (ibt && hint -> type == UNWIND_HINT_TYPE_REGS_PARTIAL ) {
1867+ struct symbol * sym = find_symbol_by_offset (insn -> sec , insn -> offset );
1868+
1869+ if (sym && sym -> bind == STB_GLOBAL &&
1870+ insn -> type != INSN_ENDBR && !insn -> noendbr ) {
1871+ WARN_FUNC ("UNWIND_HINT_IRET_REGS without ENDBR" ,
1872+ insn -> sec , insn -> offset );
1873+ }
1874+ }
1875+
18451876 if (hint -> type == UNWIND_HINT_TYPE_FUNC ) {
18461877 insn -> cfi = & func_cfi ;
18471878 continue ;
@@ -1883,6 +1914,9 @@ static int read_noendbr_hints(struct objtool_file *file)
18831914 return -1 ;
18841915 }
18851916
1917+ if (insn -> type == INSN_ENDBR )
1918+ WARN_FUNC ("ANNOTATE_NOENDBR on ENDBR" , insn -> sec , insn -> offset );
1919+
18861920 insn -> noendbr = 1 ;
18871921 }
18881922
@@ -2122,6 +2156,9 @@ static int decode_sections(struct objtool_file *file)
21222156 if (ret )
21232157 return ret ;
21242158
2159+ /*
2160+ * Must be before read_unwind_hints() since that needs insn->noendbr.
2161+ */
21252162 ret = read_noendbr_hints (file );
21262163 if (ret )
21272164 return ret ;
@@ -3063,6 +3100,111 @@ static struct instruction *next_insn_to_validate(struct objtool_file *file,
30633100 return next_insn_same_sec (file , insn );
30643101}
30653102
3103+ static struct instruction *
3104+ validate_ibt_reloc (struct objtool_file * file , struct reloc * reloc )
3105+ {
3106+ struct instruction * dest ;
3107+ struct section * sec ;
3108+ unsigned long off ;
3109+
3110+ sec = reloc -> sym -> sec ;
3111+ off = reloc -> sym -> offset ;
3112+
3113+ if ((reloc -> sec -> base -> sh .sh_flags & SHF_EXECINSTR ) &&
3114+ (reloc -> type == R_X86_64_PC32 || reloc -> type == R_X86_64_PLT32 ))
3115+ off += arch_dest_reloc_offset (reloc -> addend );
3116+ else
3117+ off += reloc -> addend ;
3118+
3119+ dest = find_insn (file , sec , off );
3120+ if (!dest )
3121+ return NULL ;
3122+
3123+ if (dest -> type == INSN_ENDBR )
3124+ return NULL ;
3125+
3126+ if (reloc -> sym -> static_call_tramp )
3127+ return NULL ;
3128+
3129+ return dest ;
3130+ }
3131+
3132+ static void warn_noendbr (const char * msg , struct section * sec , unsigned long offset ,
3133+ struct instruction * dest )
3134+ {
3135+ WARN_FUNC ("%srelocation to !ENDBR: %s+0x%lx" , sec , offset , msg ,
3136+ dest -> func ? dest -> func -> name : dest -> sec -> name ,
3137+ dest -> func ? dest -> offset - dest -> func -> offset : dest -> offset );
3138+ }
3139+
3140+ static void validate_ibt_dest (struct objtool_file * file , struct instruction * insn ,
3141+ struct instruction * dest )
3142+ {
3143+ if (dest -> func && dest -> func == insn -> func ) {
3144+ /*
3145+ * Anything from->to self is either _THIS_IP_ or IRET-to-self.
3146+ *
3147+ * There is no sane way to annotate _THIS_IP_ since the compiler treats the
3148+ * relocation as a constant and is happy to fold in offsets, skewing any
3149+ * annotation we do, leading to vast amounts of false-positives.
3150+ *
3151+ * There's also compiler generated _THIS_IP_ through KCOV and
3152+ * such which we have no hope of annotating.
3153+ *
3154+ * As such, blanket accept self-references without issue.
3155+ */
3156+ return ;
3157+ }
3158+
3159+ if (dest -> noendbr )
3160+ return ;
3161+
3162+ warn_noendbr ("" , insn -> sec , insn -> offset , dest );
3163+ }
3164+
3165+ static void validate_ibt_insn (struct objtool_file * file , struct instruction * insn )
3166+ {
3167+ struct instruction * dest ;
3168+ struct reloc * reloc ;
3169+
3170+ switch (insn -> type ) {
3171+ case INSN_CALL :
3172+ case INSN_CALL_DYNAMIC :
3173+ case INSN_JUMP_CONDITIONAL :
3174+ case INSN_JUMP_UNCONDITIONAL :
3175+ case INSN_JUMP_DYNAMIC :
3176+ case INSN_JUMP_DYNAMIC_CONDITIONAL :
3177+ case INSN_RETURN :
3178+ /*
3179+ * We're looking for code references setting up indirect code
3180+ * flow. As such, ignore direct code flow and the actual
3181+ * dynamic branches.
3182+ */
3183+ return ;
3184+
3185+ case INSN_NOP :
3186+ /*
3187+ * handle_group_alt() will create INSN_NOP instruction that
3188+ * don't belong to any section, ignore all NOP since they won't
3189+ * carry a (useful) relocation anyway.
3190+ */
3191+ return ;
3192+
3193+ default :
3194+ break ;
3195+ }
3196+
3197+ for (reloc = insn_reloc (file , insn );
3198+ reloc ;
3199+ reloc = find_reloc_by_dest_range (file -> elf , insn -> sec ,
3200+ reloc -> offset + 1 ,
3201+ (insn -> offset + insn -> len ) - (reloc -> offset + 1 ))) {
3202+ dest = validate_ibt_reloc (file , reloc );
3203+ if (dest )
3204+ validate_ibt_dest (file , insn , dest );
3205+ }
3206+ }
3207+
30663208/*
30673209 * Follow the branch starting at the given instruction, and recursively follow
30683210 * any other branches (jumps). Meanwhile, track the frame pointer state at
@@ -3272,6 +3414,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
32723414 break ;
32733415 }
32743416
3417+ if (ibt )
3418+ validate_ibt_insn (file , insn );
3419+
32753420 if (insn -> dead_end )
32763421 return 0 ;
32773422
@@ -3557,6 +3702,53 @@ static int validate_functions(struct objtool_file *file)
35573702 return warnings ;
35583703}
35593704
3705+ static int validate_ibt (struct objtool_file * file )
3706+ {
3707+ struct section * sec ;
3708+ struct reloc * reloc ;
3709+
3710+ for_each_sec (file , sec ) {
3711+ bool is_data ;
3712+
3713+ /* already done in validate_branch() */
3714+ if (sec -> sh .sh_flags & SHF_EXECINSTR )
3715+ continue ;
3716+
3717+ if (!sec -> reloc )
3718+ continue ;
3719+
3720+ if (!strncmp (sec -> name , ".orc" , 4 ))
3721+ continue ;
3722+
3723+ if (!strncmp (sec -> name , ".discard" , 8 ))
3724+ continue ;
3725+
3726+ if (!strncmp (sec -> name , ".debug" , 6 ))
3727+ continue ;
3728+
3729+ if (!strcmp (sec -> name , "_error_injection_whitelist" ))
3730+ continue ;
3731+
3732+ if (!strcmp (sec -> name , "_kprobe_blacklist" ))
3733+ continue ;
3734+
3735+ is_data = strstr (sec -> name , ".data" ) || strstr (sec -> name , ".rodata" );
3736+
3737+ list_for_each_entry (reloc , & sec -> reloc -> reloc_list , list ) {
3738+ struct instruction * dest ;
3739+
3740+ dest = validate_ibt_reloc (file , reloc );
3741+ if (is_data && dest && !dest -> noendbr ) {
3742+ warn_noendbr ("data " , reloc -> sym -> sec ,
3743+ reloc -> sym -> offset + reloc -> addend ,
3744+ dest );
3745+ }
3746+ }
3747+ }
3748+
3749+ return 0 ;
3750+ }
3751+
35603752static int validate_reachable_instructions (struct objtool_file * file )
35613753{
35623754 struct instruction * insn ;
@@ -3584,6 +3776,11 @@ int check(struct objtool_file *file)
35843776 return 1 ;
35853777 }
35863778
3779+ if (ibt && !lto ) {
3780+ fprintf (stderr , "--ibt requires: --lto\n" );
3781+ return 1 ;
3782+ }
3783+
35873784 arch_initial_func_cfi_state (& initial_func_cfi );
35883785 init_cfi_state (& init_cfi );
35893786 init_cfi_state (& func_cfi );
@@ -3630,6 +3827,13 @@ int check(struct objtool_file *file)
36303827 goto out ;
36313828 warnings += ret ;
36323829
3830+ if (ibt ) {
3831+ ret = validate_ibt (file );
3832+ if (ret < 0 )
3833+ goto out ;
3834+ warnings += ret ;
3835+ }
3836+
36333837 if (!warnings ) {
36343838 ret = validate_reachable_instructions (file );
36353839 if (ret < 0 )
0 commit comments