On powerpc, switch tables are relative, than means the address of the table is added to the value of the entry in order to get the pointed address: (r10 is the table address, r4 the index in the table)
lis r10,0 <== Load r10 with upper part of .rodata address R_PPC_ADDR16_HA .rodata addi r10,r10,0 <== Add lower part of .rodata address R_PPC_ADDR16_LO .rodata lwzx r8,r10,r4 <== Read table entry at r10 + r4 into r8 add r10,r8,r10 <== Add table address to read value mtctr r10 <== Save calculated address in CTR bctr <== Branch to address in CTR But for c_jump_tables it is not the case, they contain the pointed address directly: lis r28,0 <== Load r28 with upper .rodata..c_jump_table R_PPC_ADDR16_HA .rodata..c_jump_table addi r28,r28,0 <== Add lower part of .rodata..c_jump_table R_PPC_ADDR16_LO .rodata..c_jump_table lwzx r10,r28,r10 <== Read table entry at r10 + r28 into r10 mtctr r10 <== Save read value in CTR bctr <== Branch to address in CTR Add support to objtool for relative tables, with a flag in order to tell table by table if that table uses relative or absolute addressing. And use correct size for 'long' instead of hard coding a size of '8'. Signed-off-by: Christophe Leroy <christophe.le...@csgroup.eu> --- tools/objtool/arch/powerpc/special.c | 2 +- tools/objtool/arch/x86/special.c | 3 ++- tools/objtool/check.c | 21 ++++++++++++++------- tools/objtool/include/objtool/elf.h | 1 + tools/objtool/include/objtool/special.h | 2 +- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/tools/objtool/arch/powerpc/special.c b/tools/objtool/arch/powerpc/special.c index d33868147196..979b555b16ea 100644 --- a/tools/objtool/arch/powerpc/special.c +++ b/tools/objtool/arch/powerpc/special.c @@ -13,7 +13,7 @@ bool arch_support_alt_relocation(struct special_alt *special_alt, } struct reloc *arch_find_switch_table(struct objtool_file *file, - struct instruction *insn) + struct instruction *insn, bool *is_rel) { exit(-1); } diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c index 7c97b7391279..e7e1775f313a 100644 --- a/tools/objtool/arch/x86/special.c +++ b/tools/objtool/arch/x86/special.c @@ -92,7 +92,7 @@ bool arch_support_alt_relocation(struct special_alt *special_alt, * NOTE: RETPOLINE made it harder still to decode dynamic jumps. */ struct reloc *arch_find_switch_table(struct objtool_file *file, - struct instruction *insn) + struct instruction *insn, bool *is_rel) { struct reloc *text_reloc, *rodata_reloc; struct section *table_sec; @@ -141,5 +141,6 @@ struct reloc *arch_find_switch_table(struct objtool_file *file, if (text_reloc->type == R_X86_64_PC32) file->ignore_unreachables = true; + *is_rel = false; return rodata_reloc; } diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 8977cdf93f54..b810be087d7c 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2053,7 +2053,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, struct instruction *dest_insn; struct alternative *alt; struct symbol *pfunc = insn_func(insn)->pfunc; - unsigned int prev_offset = 0; + unsigned int offset, prev_offset = 0; /* * Each @reloc is a switch table relocation which points to the target @@ -2066,7 +2066,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, break; /* Make sure the table entries are consecutive: */ - if (prev_offset && reloc->offset != prev_offset + 8) + if (prev_offset && reloc->offset != prev_offset + elf_class_addrsize(file->elf)) break; /* Detect function pointers from contiguous objects: */ @@ -2074,7 +2074,12 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, reloc->addend == pfunc->offset) break; - dest_insn = find_insn(file, reloc->sym->sec, reloc->addend); + if (table->jump_table_is_rel) + offset = reloc->addend + table->offset - reloc->offset; + else + offset = reloc->addend; + + dest_insn = find_insn(file, reloc->sym->sec, offset); if (!dest_insn) break; @@ -2107,8 +2112,8 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, * associated with it. */ static struct reloc *find_jump_table(struct objtool_file *file, - struct symbol *func, - struct instruction *insn) + struct symbol *func, + struct instruction *insn, bool *is_rel) { struct reloc *table_reloc; struct instruction *dest_insn, *orig_insn = insn; @@ -2132,7 +2137,7 @@ static struct reloc *find_jump_table(struct objtool_file *file, insn->jump_dest->offset > orig_insn->offset)) break; - table_reloc = arch_find_switch_table(file, insn); + table_reloc = arch_find_switch_table(file, insn, is_rel); if (!table_reloc) continue; dest_insn = find_insn(file, table_reloc->sym->sec, table_reloc->addend); @@ -2154,6 +2159,7 @@ static void mark_func_jump_tables(struct objtool_file *file, { struct instruction *insn, *last = NULL; struct reloc *reloc; + bool is_rel; func_for_each_insn(file, func, insn) { if (!last) @@ -2176,9 +2182,10 @@ static void mark_func_jump_tables(struct objtool_file *file, if (insn->type != INSN_JUMP_DYNAMIC) continue; - reloc = find_jump_table(file, func, insn); + reloc = find_jump_table(file, func, insn, &is_rel); if (reloc) { reloc->jump_table_start = true; + reloc->jump_table_is_rel = is_rel; insn->_jump_table = reloc; } } diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index e1ca588eb69d..64aac87a4825 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -80,6 +80,7 @@ struct reloc { s64 addend; int idx; bool jump_table_start; + bool jump_table_is_rel; }; #define ELF_HASH_BITS 20 diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h index 86d4af9c5aa9..5edfc4c3e582 100644 --- a/tools/objtool/include/objtool/special.h +++ b/tools/objtool/include/objtool/special.h @@ -38,5 +38,5 @@ bool arch_support_alt_relocation(struct special_alt *special_alt, struct instruction *insn, struct reloc *reloc); struct reloc *arch_find_switch_table(struct objtool_file *file, - struct instruction *insn); + struct instruction *insn, bool *is_rel); #endif /* _SPECIAL_H */ -- 2.40.1