@@ -112,46 +112,34 @@ static struct reloc *find_switch_table(struct objtool_file *file,
struct instruction *insn,
unsigned long *table_size)
{
- struct reloc *text_reloc, *rodata_reloc;
- struct section *table_sec;
- unsigned long table_offset;
-
- /* look for a relocation which references .rodata */
- text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
- insn->offset, insn->len);
- if (!text_reloc || text_reloc->sym->type != STT_SECTION ||
- !text_reloc->sym->sec->rodata)
- return NULL;
-
- table_offset = reloc_addend(text_reloc);
- table_sec = text_reloc->sym->sec;
+ struct reloc *rodata_reloc;
+ struct symbol *sym = NULL;
- if (reloc_type(text_reloc) == R_X86_64_PC32)
- table_offset += 4;
+ /*
+ * Each table entry has a rela associated with it. The rela
+ * should reference text in the same function as the original
+ * instruction.
+ */
+ rodata_reloc = find_rodata_sym_reference(file, insn, &sym);
/*
- * Make sure the .rodata address isn't associated with a
- * symbol. GCC jump tables are anonymous data.
+ * Annotations, if present, are attached to the indirect jump
+ * instruction directly. In this case, a symbol annotation is
+ * expected.
+ *
+ * Otherwise, make sure the .rodata address isn't associated with
+ * a symbol. Unannotated GCC jump tables are anonymous data.
*
* Also support C jump tables which are in the same format as
* switch jump tables. For objtool to recognize them, they
* need to be placed in the C_JUMP_TABLE_SECTION section. They
* have symbols associated with them.
*/
- if (find_symbol_containing(table_sec, table_offset) &&
- strcmp(table_sec->name, C_JUMP_TABLE_SECTION))
- return NULL;
-
- /*
- * Each table entry has a rela associated with it. The rela
- * should reference text in the same function as the original
- * instruction.
- */
- rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset);
- if (!rodata_reloc)
+ if (insn->type != INSN_JUMP_DYNAMIC && sym &&
+ strcmp(sym->sec->name, C_JUMP_TABLE_SECTION))
return NULL;
- *table_size = 0;
+ *table_size = sym ? sym->len : 0;
return rodata_reloc;
}
@@ -1372,6 +1372,8 @@ __weak const char *arch_nop_fentry_call(int len)
static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
{
+ unsigned long offset = insn->offset;
+ unsigned int len = insn->len;
struct reloc *reloc;
if (insn->no_reloc)
@@ -1380,8 +1382,12 @@ static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *i
if (!file)
return NULL;
- reloc = find_reloc_by_dest_range(file->elf, insn->sec,
- insn->offset, insn->len);
+ do {
+ /* Skip any R_*_NONE relocations */
+ reloc = find_reloc_by_dest_range(file->elf, insn->sec,
+ offset++, len--);
+ } while (len && reloc && reloc_type(reloc) == R_NONE);
+
if (!reloc) {
insn->no_reloc = 1;
return NULL;
@@ -2169,10 +2175,86 @@ int add_jump_table(struct objtool_file *file, struct instruction *insn,
return 0;
}
+struct reloc *find_rodata_sym_reference(struct objtool_file *file,
+ struct instruction *insn,
+ struct symbol **table_sym)
+{
+ struct reloc *text_reloc, *rodata_reloc;
+ unsigned long addend;
+ struct symbol *sym;
+
+ /*
+ * Look for a relocation which references .rodata. We must use
+ * find_reloc_by_dest_range() directly here, as insn_reloc() filters
+ * out R_*_NONE relocations which are used for jump table annotations.
+ */
+ text_reloc = find_reloc_by_dest_range(file->elf, insn->sec,
+ insn->offset, insn->len);
+ if (!text_reloc) {
+ insn->no_reloc = 1;
+ return NULL;
+ }
+
+ sym = text_reloc->sym;
+ if (!sym->sec->rodata)
+ return NULL;
+
+ if (reloc_type(text_reloc) == elf_data_rela_type(file->elf))
+ addend = arch_dest_reloc_offset(reloc_addend(text_reloc));
+ else
+ addend = reloc_addend(text_reloc);
+
+ rodata_reloc = find_reloc_by_dest(file->elf, sym->sec,
+ sym->offset + addend);
+ if (!rodata_reloc)
+ return NULL;
+
+ /*
+ * Find the ELF symbol covering the destination of the relocation. This
+ * is trivial if the reloc refers to a STT_OBJECT directly, but it may
+ * have been emitted as section relative as well.
+ */
+ if (sym->type == STT_SECTION)
+ sym = find_symbol_containing(sym->sec, addend);
+
+ *table_sym = sym;
+ return rodata_reloc;
+}
+
+/*
+ * Generic version of jump table handling, relying strictly on annotations
+ * provided by the compiler. Overridden for x86 using heuristics that attempt
+ * to correlate indirect jump instructions with preceding .rodata references.
+ */
int __weak add_func_jump_tables(struct objtool_file *file,
struct symbol *func)
{
- return 0;
+ struct instruction *insn;
+ int ret = 0;
+
+ func_for_each_insn(file, func, insn) {
+ struct reloc *reloc;
+ struct symbol *sym;
+
+ if (insn->type != INSN_JUMP_DYNAMIC)
+ continue;
+
+ /*
+ * Look for a relocation attached to this indirect jump that
+ * references an ELF object in .rodata. This should be the jump
+ * table annotation emitted by the compiler.
+ */
+ reloc = find_rodata_sym_reference(file, insn, &sym);
+ if (reloc && sym && sym->len) {
+ insn->_jump_table = reloc;
+ insn->_jump_table_size = sym->len;
+
+ ret = add_jump_table(file, insn, NULL);
+ if (ret)
+ break;
+ }
+ }
+ return ret;
}
/*
@@ -140,4 +140,8 @@ struct instruction *next_insn_same_func(struct objtool_file *file, struct instru
insn; \
insn = next_insn_same_func(file, insn))
+struct reloc *find_rodata_sym_reference(struct objtool_file *file,
+ struct instruction *insn,
+ struct symbol **sym);
+
#endif /* _CHECK_H */