[RFC v5 39/57] objtool: arm64: Decode load literal

From: Julien Thierry
Date: Thu Jan 09 2020 - 11:07:53 EST


Decode load literal instruction.

Aarch64 load literal is a pseudo-instruction (part of the instruction
set) that allows to load literal values may cannot be encoded within
a single instruction. The GNU assembler implementation of the pseudo
instruction generates constant data within the same section as the
pseudo-instruction itself.

That data could very well be a valid opcode (e.g. jump, return,
stack operation, etc) which confuses objtool.

Mark the "instructions" corresponding to load literal offsets as
invalid as they should never be reach by the execution flow.

Signed-off-by: Julien Thierry <jthierry@xxxxxxxxxx>
---
tools/objtool/arch.h | 3 +
tools/objtool/arch/arm64/decode.c | 88 +++++++++++++++++++
.../objtool/arch/arm64/include/insn_decode.h | 3 +
tools/objtool/arch/x86/decode.c | 5 ++
tools/objtool/check.c | 3 +
5 files changed, 102 insertions(+)

diff --git a/tools/objtool/arch.h b/tools/objtool/arch.h
index 0336efecb9d9..829d6d73aec6 100644
--- a/tools/objtool/arch.h
+++ b/tools/objtool/arch.h
@@ -68,6 +68,7 @@ struct stack_op {
struct list_head list;
};

+struct objtool_file;
struct instruction;

void arch_initial_func_cfi_state(struct cfi_state *state);
@@ -78,6 +79,8 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
unsigned long *immediate,
struct list_head *ops_list);

+int arch_post_process_file(struct objtool_file *file);
+
bool arch_callee_saved_reg(unsigned char reg);

unsigned long arch_jump_destination(struct instruction *insn);
diff --git a/tools/objtool/arch/arm64/decode.c b/tools/objtool/arch/arm64/decode.c
index bc4c62401012..aed8ba0f812e 100644
--- a/tools/objtool/arch/arm64/decode.c
+++ b/tools/objtool/arch/arm64/decode.c
@@ -18,6 +18,47 @@ static bool stack_related_reg(int reg)
return reg == CFI_SP || reg == CFI_BP;
}

+struct insn_loc {
+ struct section *sec;
+ unsigned long offset;
+ struct hlist_node hnode;
+};
+
+DEFINE_HASHTABLE(text_constants, 16);
+
+int arch_post_process_file(struct objtool_file *file)
+{
+ struct hlist_node *tmp;
+ struct insn_loc *loc;
+ unsigned int bkt;
+ int res = 0;
+
+ /*
+ * Data placed in code sections could turn out to be a valid aarch64
+ * opcode.
+ * If that is the case, change the insn type to invalid as it should
+ * never be reached by the execution flow.
+ */
+ hash_for_each_safe(text_constants, bkt, tmp, loc, hnode) {
+ struct instruction *insn;
+
+ insn = find_insn(file, loc->sec, loc->offset);
+ if (insn) {
+ insn->type = INSN_INVALID;
+ } else {
+ WARN("failed to find constant at %s+0x%lx",
+ loc->sec->name, loc->offset);
+ res = -1;
+ }
+ hash_del(&loc->hnode);
+ free(loc);
+ }
+
+ return res;
+}
+
+static struct insn_loc current_location;
+
bool arch_callee_saved_reg(unsigned char reg)
{
switch (reg) {
@@ -127,6 +168,8 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
//retrieve instruction (from sec->data->offset)
insn = *(u32 *)(sec->data->d_buf + offset);

+ current_location.sec = sec;
+ current_location.offset = offset;
//dispatch according to encoding classes
decode_fun = aarch64_insn_class_decode_table[INSN_CLASS(insn)];
if (decode_fun)
@@ -136,6 +179,9 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,

if (res)
WARN_FUNC("Unsupported instruction", sec, offset);
+
+ memset(&current_location, 0, sizeof(current_location));
+
return res;
}

@@ -845,6 +891,11 @@ static struct aarch64_insn_decoder ld_st_decoder[] = {
.value = 0b000101000000000,
.decode_func = arm_decode_ldapr_stlr_unsc_imm,
},
+ {
+ .mask = 0b001101000000000,
+ .value = 0b000100000000000,
+ .decode_func = arm_decode_ld_regs_literal,
+ },
{
.mask = 0b001101100000000,
.value = 0b001000000000000,
@@ -1350,6 +1401,43 @@ int arm_decode_ld_st_exclusive(u32 instr, enum insn_type *type,
#undef LDLAR_64
#undef LDAR_64

+int arm_decode_ld_regs_literal(u32 instr, enum insn_type *type,
+ unsigned long *immediate,
+ struct list_head *ops_list)
+{
+ unsigned char opc = 0, V = 0;
+ long pc_offset;
+ struct insn_loc *loc;
+
+ opc = (instr >> 30) & ONES(2);
+ V = EXTRACT_BIT(instr, 26);
+
+ if (((opc << 1) | V) == 0x7)
+ return arm_decode_unknown(instr, type, immediate, ops_list);
+
+ pc_offset = instr & GENMASK(23, 5);
+
+ /* Sign extend and multiply by 4 */
+ pc_offset = (pc_offset << (64 - 23));
+ pc_offset = ((pc_offset >> (64 - 23)) >> 5) << 2;
+
+ loc = malloc(sizeof(*loc));
+ loc->sec = current_location.sec;
+ loc->offset = current_location.offset + pc_offset;
+ hash_add(text_constants, &loc->hnode, loc->offset);
+
+ /* 64-bit literal */
+ if (opc & 1) {
+ loc = malloc(sizeof(*loc));
+ loc->sec = current_location.sec;
+ loc->offset = current_location.offset + pc_offset + 4;
+ hash_add(text_constants, &loc->hnode, loc->offset);
+ }
+
+ *type = INSN_OTHER;
+ return 0;
+}
+
int arm_decode_ld_st_regs_unsc_imm(u32 instr, enum insn_type *type,
unsigned long *immediate,
struct list_head *ops_list)
diff --git a/tools/objtool/arch/arm64/include/insn_decode.h b/tools/objtool/arch/arm64/include/insn_decode.h
index e6a62691b487..3ec4c69547ac 100644
--- a/tools/objtool/arch/arm64/include/insn_decode.h
+++ b/tools/objtool/arch/arm64/include/insn_decode.h
@@ -112,6 +112,9 @@ int arm_decode_ld_st_mem_tags(u32 instr, enum insn_type *type,
int arm_decode_ldapr_stlr_unsc_imm(u32 instr, enum insn_type *type,
unsigned long *immediate,
struct list_head *ops_list);
+int arm_decode_ld_regs_literal(u32 instr, enum insn_type *type,
+ unsigned long *immediate,
+ struct list_head *ops_list);
int arm_decode_ld_st_noalloc_pair_off(u32 instr, enum insn_type *type,
unsigned long *immediate,
struct list_head *ops_list);
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 650e5d021486..57a5f817a63c 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -497,6 +497,11 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
return 0;
}

+int arch_post_process_file(struct objtool_file *file)
+{
+ return 0;
+}
+
void arch_initial_func_cfi_state(struct cfi_state *state)
{
int i;
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 52a8e64e15ca..e0c6bda261c8 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -279,6 +279,9 @@ static int decode_instructions(struct objtool_file *file)
}
}

+ if (arch_post_process_file(file))
+ return -1;
+
return 0;

err:
--
2.21.0