@@ -20,3 +20,18 @@ void arch_force_alt_path(unsigned short feature,
struct special_alt *alt)
{
}
+
+int arch_add_jump_table(struct objtool_file *file, struct instruction *insn,
+ struct rela *table, struct rela *next_table)
+{
+ return 0;
+}
+
+struct rela *arch_find_switch_table(struct objtool_file *file,
+ struct rela *text_rela,
+ struct section *rodata_sec,
+ unsigned long table_offset)
+{
+ file->ignore_unreachables = true;
+ return NULL;
+}
@@ -159,7 +159,7 @@ static int is_arm64(struct elf *elf)
int arch_decode_instruction(struct elf *elf, struct section *sec,
unsigned long offset, unsigned int maxlen,
- unsigned int *len, unsigned char *type,
+ unsigned int *len, enum insn_type *type,
unsigned long *immediate, struct stack_op *op)
{
int arm64 = 0;
@@ -184,7 +184,7 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
insn = *(u32 *)(sec->data->d_buf + offset);
//dispatch according to encoding classes
- return aarch64_insn_class_decode_table[(insn >> 25) & 0xf](insn, type,
+ return aarch64_insn_class_decode_table[(insn >> 25) & 0xf](insn, (unsigned char *)type,
immediate, op);
}
@@ -26,3 +26,82 @@ void arch_force_alt_path(unsigned short feature,
alt->skip_alt = true;
}
}
+
+int arch_add_jump_table(struct objtool_file *file, struct instruction *insn,
+ struct rela *table, struct rela *next_table)
+{
+ struct rela *rela = table;
+ struct instruction *dest_insn;
+ struct alternative *alt;
+ struct symbol *pfunc = insn->func->pfunc;
+ unsigned int prev_offset = 0;
+
+ /*
+ * Each @rela is a switch table relocation which points to the target
+ * instruction.
+ */
+ list_for_each_entry_from(rela, &table->sec->rela_list, list) {
+
+ /* Check for the end of the table: */
+ if (rela != table && rela->jump_table_start)
+ break;
+
+ /* Make sure the table entries are consecutive: */
+ if (prev_offset && rela->offset != prev_offset + 8)
+ break;
+
+ /* Detect function pointers from contiguous objects: */
+ if (rela->sym->sec == pfunc->sec &&
+ rela->addend == pfunc->offset)
+ break;
+
+ dest_insn = find_insn(file, rela->sym->sec, rela->addend);
+ if (!dest_insn)
+ break;
+
+ /* Make sure the destination is in the same function: */
+ if (!dest_insn->func || dest_insn->func->pfunc != pfunc)
+ break;
+
+ alt = malloc(sizeof(*alt));
+ if (!alt) {
+ WARN("malloc failed");
+ return -1;
+ }
+
+ alt->insn = dest_insn;
+ list_add_tail(&alt->list, &insn->alts);
+ prev_offset = rela->offset;
+ }
+
+ if (!prev_offset) {
+ WARN_FUNC("can't find switch jump table",
+ insn->sec, insn->offset);
+ return -1;
+ }
+
+ return 0;
+}
+
+struct rela *arch_find_switch_table(struct objtool_file *file,
+ struct rela *text_rela,
+ struct section *rodata_sec,
+ unsigned long table_offset)
+{
+ struct rela *rodata_rela;
+
+ rodata_rela = find_rela_by_dest(rodata_sec, table_offset);
+ if (rodata_rela) {
+ /*
+ * Use of RIP-relative switch jumps is quite rare, and
+ * indicates a rare GCC quirk/bug which can leave dead
+ * code behind.
+ */
+ if (text_rela->type == R_X86_64_PC32)
+ file->ignore_unreachables = true;
+
+ return rodata_rela;
+ }
+
+ return NULL;
+}
@@ -20,12 +20,6 @@
#define C_JUMP_TABLE_SECTION ".rodata..c_jump_table"
-struct alternative {
- struct list_head list;
- struct instruction *insn;
- bool skip_orig;
-};
-
const char *objname;
struct cfi_state initial_func_cfi;
@@ -901,62 +895,6 @@ static int add_special_section_alts(struct objtool_file *file)
return ret;
}
-static int add_jump_table(struct objtool_file *file, struct instruction *insn,
- struct rela *table)
-{
- struct rela *rela = table;
- struct instruction *dest_insn;
- struct alternative *alt;
- struct symbol *pfunc = insn->func->pfunc;
- unsigned int prev_offset = 0;
-
- /*
- * Each @rela is a switch table relocation which points to the target
- * instruction.
- */
- list_for_each_entry_from(rela, &table->sec->rela_list, list) {
-
- /* Check for the end of the table: */
- if (rela != table && rela->jump_table_start)
- break;
-
- /* Make sure the table entries are consecutive: */
- if (prev_offset && rela->offset != prev_offset + 8)
- break;
-
- /* Detect function pointers from contiguous objects: */
- if (rela->sym->sec == pfunc->sec &&
- rela->addend == pfunc->offset)
- break;
-
- dest_insn = find_insn(file, rela->sym->sec, rela->addend);
- if (!dest_insn)
- break;
-
- /* Make sure the destination is in the same function: */
- if (!dest_insn->func || dest_insn->func->pfunc != pfunc)
- break;
-
- alt = malloc(sizeof(*alt));
- if (!alt) {
- WARN("malloc failed");
- return -1;
- }
-
- alt->insn = dest_insn;
- list_add_tail(&alt->list, &insn->alts);
- prev_offset = rela->offset;
- }
-
- if (!prev_offset) {
- WARN_FUNC("can't find switch jump table",
- insn->sec, insn->offset);
- return -1;
- }
-
- return 0;
-}
-
/*
* find_jump_table() - Given a dynamic jump, find the switch jump table in
* .rodata associated with it.
@@ -1058,38 +996,9 @@ static struct rela *find_jump_table(struct objtool_file *file,
continue;
/* Each table entry has a rela associated with it. */
- table_rela = find_rela_by_dest(table_sec, table_offset);
+ table_rela = arch_find_switch_table(file, text_rela, table_sec, table_offset);
if (!table_rela)
continue;
- /*
- * If we are on arm64 architecture, we now that we
- * are in presence of a switch table thanks to
- * the `br <Xn>` insn. but we can't retrieve it yet.
- * So we just ignore unreachable for this file.
- */
- if (!arch_support_switch_table()) {
- file->ignore_unreachables = true;
- return NULL;
- }
-
- rodata_rela = find_rela_by_dest(rodata_sec, table_offset);
- if (rodata_rela) {
- /*
- * Use of RIP-relative switch jumps is quite rare, and
- * indicates a rare GCC quirk/bug which can leave dead
- * code behind.
- */
- if (text_rela->type == R_X86_64_PC32)
- file->ignore_unreachables = true;
-
- /*
- * Use of RIP-relative switch jumps is quite rare, and
- * indicates a rare GCC quirk/bug which can leave dead code
- * behind.
- */
- if (text_rela->type == R_X86_64_PC32)
- file->ignore_unreachables = true;
-
return table_rela;
}
@@ -1145,7 +1054,7 @@ static int add_func_jump_tables(struct objtool_file *file,
if (!insn->jump_table)
continue;
- ret = add_jump_table(file, insn, insn->jump_table);
+ ret = arch_add_jump_table(file, insn, insn->jump_table, NULL);
if (ret)
return ret;
}
@@ -13,6 +13,7 @@
#include "orc.h"
#include "arch_special.h"
#include <linux/hashtable.h>
+;
struct insn_state {
struct cfi_reg cfa;
@@ -48,6 +49,12 @@ struct instruction {
struct orc_entry orc;
};
+struct alternative {
+ struct list_head list;
+ struct instruction *insn;
+ bool skip_orig;
+};
+
struct objtool_file {
struct elf *elf;
struct list_head insn_list;
@@ -7,7 +7,10 @@
#define _SPECIAL_H
#include <stdbool.h>
+#include <stdlib.h>
+#include "check.h"
#include "elf.h"
+#include "warn.h"
struct special_alt {
struct list_head list;
@@ -30,5 +33,10 @@ int special_get_alts(struct elf *elf, struct list_head *alts);
void arch_force_alt_path(unsigned short feature,
bool uaccess,
struct special_alt *alt);
-
+int arch_add_jump_table(struct objtool_file *file, struct instruction *insn,
+ struct rela *table, struct rela *next_table);
+struct rela *arch_find_switch_table(struct objtool_file *file,
+ struct rela *text_rela,
+ struct section *rodata_sec,
+ unsigned long table_offset);
#endif /* _SPECIAL_H */
The way to identify switch-tables and retrieves all the data necessary to handle the different execution branches is not the same on all architecture. In order to be able to add other architecture support, this patch defines arch-dependent functions to process jump-tables. Signed-off-by: Raphael Gault <raphael.gault@arm.com> --- tools/objtool/arch/arm64/arch_special.c | 15 ++++ tools/objtool/arch/arm64/decode.c | 4 +- tools/objtool/arch/x86/arch_special.c | 79 ++++++++++++++++++++ tools/objtool/check.c | 95 +------------------------ tools/objtool/check.h | 7 ++ tools/objtool/special.h | 10 ++- 6 files changed, 114 insertions(+), 96 deletions(-)