Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

objtool: Refactor sibling call detection logic

Simplify the sibling call detection logic a bit.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Nick Desaulniers <ndesaulniers@google.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/8357dbef9e7f5512e76bf83a76c81722fc09eb5e.1563413318.git.jpoimboe@redhat.com

authored by

Josh Poimboeuf and committed by
Thomas Gleixner
0c1ddd33 c9bab22b

+33 -32
+33 -32
tools/objtool/check.c
··· 97 97 for (insn = next_insn_same_sec(file, insn); insn; \ 98 98 insn = next_insn_same_sec(file, insn)) 99 99 100 + static bool is_sibling_call(struct instruction *insn) 101 + { 102 + /* An indirect jump is either a sibling call or a jump to a table. */ 103 + if (insn->type == INSN_JUMP_DYNAMIC) 104 + return list_empty(&insn->alts); 105 + 106 + if (insn->type != INSN_JUMP_CONDITIONAL && 107 + insn->type != INSN_JUMP_UNCONDITIONAL) 108 + return false; 109 + 110 + /* add_jump_destinations() sets insn->call_dest for sibling calls. */ 111 + return !!insn->call_dest; 112 + } 113 + 100 114 /* 101 115 * This checks to see if the given function is a "noreturn" function. 102 116 * ··· 181 167 * of the sibling call returns. 182 168 */ 183 169 func_for_each_insn_all(file, func, insn) { 184 - if (insn->type == INSN_JUMP_UNCONDITIONAL) { 170 + if (is_sibling_call(insn)) { 185 171 struct instruction *dest = insn->jump_dest; 186 172 187 173 if (!dest) 188 174 /* sibling call to another file */ 189 175 return false; 190 176 191 - if (dest->func && dest->func->pfunc != insn->func->pfunc) { 192 - 193 - /* local sibling call */ 194 - if (recursion == 5) { 195 - /* 196 - * Infinite recursion: two functions 197 - * have sibling calls to each other. 198 - * This is a very rare case. It means 199 - * they aren't dead ends. 200 - */ 201 - return false; 202 - } 203 - 204 - return __dead_end_function(file, dest->func, 205 - recursion + 1); 177 + /* local sibling call */ 178 + if (recursion == 5) { 179 + /* 180 + * Infinite recursion: two functions have 181 + * sibling calls to each other. This is a very 182 + * rare case. It means they aren't dead ends. 183 + */ 184 + return false; 206 185 } 207 - } 208 186 209 - if (insn->type == INSN_JUMP_DYNAMIC && list_empty(&insn->alts)) 210 - /* sibling call */ 211 - return false; 187 + return __dead_end_function(file, dest->func, recursion+1); 188 + } 212 189 } 213 190 214 191 return true; ··· 586 581 insn->retpoline_safe = true; 587 582 continue; 588 583 } else { 589 - /* sibling call */ 584 + /* external sibling call */ 590 585 insn->call_dest = rela->sym; 591 - insn->jump_dest = NULL; 592 586 continue; 593 587 } 594 588 ··· 637 633 } else if (insn->jump_dest->func->pfunc != insn->func->pfunc && 638 634 insn->jump_dest->offset == insn->jump_dest->func->offset) { 639 635 640 - /* sibling class */ 636 + /* internal sibling call */ 641 637 insn->call_dest = insn->jump_dest->func; 642 - insn->jump_dest = NULL; 643 638 } 644 639 } 645 640 } ··· 1892 1889 return false; 1893 1890 } 1894 1891 1895 - static inline const char *insn_dest_name(struct instruction *insn) 1892 + static inline const char *call_dest_name(struct instruction *insn) 1896 1893 { 1897 1894 if (insn->call_dest) 1898 1895 return insn->call_dest->name; ··· 1904 1901 { 1905 1902 if (state->uaccess && !func_uaccess_safe(insn->call_dest)) { 1906 1903 WARN_FUNC("call to %s() with UACCESS enabled", 1907 - insn->sec, insn->offset, insn_dest_name(insn)); 1904 + insn->sec, insn->offset, call_dest_name(insn)); 1908 1905 return 1; 1909 1906 } 1910 1907 1911 1908 if (state->df) { 1912 1909 WARN_FUNC("call to %s() with DF set", 1913 - insn->sec, insn->offset, insn_dest_name(insn)); 1910 + insn->sec, insn->offset, call_dest_name(insn)); 1914 1911 return 1; 1915 1912 } 1916 1913 ··· 2091 2088 2092 2089 case INSN_JUMP_CONDITIONAL: 2093 2090 case INSN_JUMP_UNCONDITIONAL: 2094 - if (func && !insn->jump_dest) { 2091 + if (func && is_sibling_call(insn)) { 2095 2092 ret = validate_sibling_call(insn, &state); 2096 2093 if (ret) 2097 2094 return ret; 2098 2095 2099 - } else if (insn->jump_dest && 2100 - (!func || !insn->jump_dest->func || 2101 - insn->jump_dest->func->pfunc == func)) { 2096 + } else if (insn->jump_dest) { 2102 2097 ret = validate_branch(file, func, 2103 2098 insn->jump_dest, state); 2104 2099 if (ret) { ··· 2112 2111 break; 2113 2112 2114 2113 case INSN_JUMP_DYNAMIC: 2115 - if (func && list_empty(&insn->alts)) { 2114 + if (func && is_sibling_call(insn)) { 2116 2115 ret = validate_sibling_call(insn, &state); 2117 2116 if (ret) 2118 2117 return ret;