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

objtool: Allow arch code to discover jump table size

In preparation for adding support for annotated jump tables, where
ELF relocations and symbols are used to describe the locations of jump
tables in the executable, refactor the jump table discovery logic so the
table size can be returned from arch_find_switch_table().

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20241011170847.334429-12-ardb+git@google.com

authored by

Ard Biesheuvel and committed by
Peter Zijlstra
c3cb6c15 e7e0eb53

+33 -16
+2 -1
tools/objtool/arch/loongarch/special.c
··· 9 9 } 10 10 11 11 struct reloc *arch_find_switch_table(struct objtool_file *file, 12 - struct instruction *insn) 12 + struct instruction *insn, 13 + unsigned long *table_size) 13 14 { 14 15 return NULL; 15 16 }
+2 -1
tools/objtool/arch/powerpc/special.c
··· 13 13 } 14 14 15 15 struct reloc *arch_find_switch_table(struct objtool_file *file, 16 - struct instruction *insn) 16 + struct instruction *insn, 17 + unsigned long *table_size) 17 18 { 18 19 exit(-1); 19 20 }
+3 -1
tools/objtool/arch/x86/special.c
··· 109 109 * NOTE: MITIGATION_RETPOLINE made it harder still to decode dynamic jumps. 110 110 */ 111 111 struct reloc *arch_find_switch_table(struct objtool_file *file, 112 - struct instruction *insn) 112 + struct instruction *insn, 113 + unsigned long *table_size) 113 114 { 114 115 struct reloc *text_reloc, *rodata_reloc; 115 116 struct section *table_sec; ··· 159 158 if (reloc_type(text_reloc) == R_X86_64_PC32) 160 159 file->ignore_unreachables = true; 161 160 161 + *table_size = 0; 162 162 return rodata_reloc; 163 163 }
+20 -11
tools/objtool/check.c
··· 150 150 return NULL; 151 151 } 152 152 153 + static inline unsigned long insn_jump_table_size(struct instruction *insn) 154 + { 155 + if (insn->type == INSN_JUMP_DYNAMIC || 156 + insn->type == INSN_CALL_DYNAMIC) 157 + return insn->_jump_table_size; 158 + 159 + return 0; 160 + } 161 + 153 162 static bool is_jump_table_jump(struct instruction *insn) 154 163 { 155 164 struct alt_group *alt_group = insn->alt_group; ··· 1946 1937 static int add_jump_table(struct objtool_file *file, struct instruction *insn, 1947 1938 struct reloc *next_table) 1948 1939 { 1940 + unsigned long table_size = insn_jump_table_size(insn); 1949 1941 struct symbol *pfunc = insn_func(insn)->pfunc; 1950 1942 struct reloc *table = insn_jump_table(insn); 1951 1943 struct instruction *dest_insn; ··· 1961 1951 for_each_reloc_from(table->sec, reloc) { 1962 1952 1963 1953 /* Check for the end of the table: */ 1954 + if (table_size && reloc_offset(reloc) - reloc_offset(table) >= table_size) 1955 + break; 1964 1956 if (reloc != table && reloc == next_table) 1965 1957 break; 1966 1958 ··· 2007 1995 * find_jump_table() - Given a dynamic jump, find the switch jump table 2008 1996 * associated with it. 2009 1997 */ 2010 - static struct reloc *find_jump_table(struct objtool_file *file, 2011 - struct symbol *func, 2012 - struct instruction *insn) 1998 + static void find_jump_table(struct objtool_file *file, struct symbol *func, 1999 + struct instruction *insn) 2013 2000 { 2014 2001 struct reloc *table_reloc; 2015 2002 struct instruction *dest_insn, *orig_insn = insn; 2003 + unsigned long table_size; 2016 2004 2017 2005 /* 2018 2006 * Backward search using the @first_jump_src links, these help avoid ··· 2033 2021 insn->jump_dest->offset > orig_insn->offset)) 2034 2022 break; 2035 2023 2036 - table_reloc = arch_find_switch_table(file, insn); 2024 + table_reloc = arch_find_switch_table(file, insn, &table_size); 2037 2025 if (!table_reloc) 2038 2026 continue; 2039 2027 dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc)); 2040 2028 if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func) 2041 2029 continue; 2042 2030 2043 - return table_reloc; 2031 + orig_insn->_jump_table = table_reloc; 2032 + orig_insn->_jump_table_size = table_size; 2033 + break; 2044 2034 } 2045 - 2046 - return NULL; 2047 2035 } 2048 2036 2049 2037 /* ··· 2054 2042 struct symbol *func) 2055 2043 { 2056 2044 struct instruction *insn, *last = NULL; 2057 - struct reloc *reloc; 2058 2045 2059 2046 func_for_each_insn(file, func, insn) { 2060 2047 if (!last) ··· 2076 2065 if (insn->type != INSN_JUMP_DYNAMIC) 2077 2066 continue; 2078 2067 2079 - reloc = find_jump_table(file, func, insn); 2080 - if (reloc) 2081 - insn->_jump_table = reloc; 2068 + find_jump_table(file, func, insn); 2082 2069 } 2083 2070 } 2084 2071
+4 -1
tools/objtool/include/objtool/check.h
··· 71 71 struct instruction *first_jump_src; 72 72 union { 73 73 struct symbol *_call_dest; 74 - struct reloc *_jump_table; 74 + struct { 75 + struct reloc *_jump_table; 76 + unsigned long _jump_table_size; 77 + }; 75 78 }; 76 79 struct alternative *alts; 77 80 struct symbol *sym;
+2 -1
tools/objtool/include/objtool/special.h
··· 38 38 struct instruction *insn, 39 39 struct reloc *reloc); 40 40 struct reloc *arch_find_switch_table(struct objtool_file *file, 41 - struct instruction *insn); 41 + struct instruction *insn, 42 + unsigned long *table_size); 42 43 #endif /* _SPECIAL_H */