2023-06-20 07:57:54

by Youling Tang

[permalink] [raw]
Subject: [RFC PATCH v1 10/23] objtool: LoongArch: Implement decoder

Implement arch_decode_instruction() for LoongArch.

Add the decoding of the following part of the instruction,

Instructions that affect the SP:
- Add instruction:
addi.d
- Load-Store instructions:
st.d/ld.d
stptr.d/ldptr.d

Instructions that affect control flow:
- Branch and Jump instructions:
beq/bne/blt/bge/bltu/bgeu/beqz/bnez/b
jirl
- Call instructions:
bl
- Return instructions:
jr ra

Miscellaneous instructions:
- Break instructionw:
break
- Nop instruction:
nop
- System instruction:
ertn

Co-developed-by: Jinyang He <[email protected]>
Signed-off-by: Jinyang He <[email protected]>
Signed-off-by: Youling Tang <[email protected]>
---
tools/arch/loongarch/include/asm/inst.h | 1 +
tools/include/linux/bitops.h | 10 ++
tools/objtool/arch/loongarch/decode.c | 136 ++++++++++++++++++++++++
3 files changed, 147 insertions(+)

diff --git a/tools/arch/loongarch/include/asm/inst.h b/tools/arch/loongarch/include/asm/inst.h
index f0533fbc1e63..23d041cd76bf 100644
--- a/tools/arch/loongarch/include/asm/inst.h
+++ b/tools/arch/loongarch/include/asm/inst.h
@@ -56,6 +56,7 @@ enum reg2_op {
revbd_op = 0x0f,
revh2w_op = 0x10,
revhd_op = 0x11,
+ ertn_op = 0x1920e,
};

enum reg2i5_op {
diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h
index f18683b95ea6..d81b52c070f5 100644
--- a/tools/include/linux/bitops.h
+++ b/tools/include/linux/bitops.h
@@ -87,4 +87,14 @@ static inline __u32 rol32(__u32 word, unsigned int shift)
return (word << shift) | (word >> ((-shift) & 31));
}

+/**
+ * sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit
+ * @value: value to sign extend
+ * @index: 0 based bit index (0<=index<64) to sign bit
+ */
+static __always_inline __s64 sign_extend64(__u64 value, int index)
+{
+ __u8 shift = 63 - index;
+ return (__s64)(value << shift) >> shift;
+}
#endif
diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c
index 3f795f57e914..fc24efd6dba2 100644
--- a/tools/objtool/arch/loongarch/decode.c
+++ b/tools/objtool/arch/loongarch/decode.c
@@ -3,6 +3,7 @@
#include <stdio.h>
#include <stdlib.h>

+#include <linux/bitops.h>
#include <asm/inst.h>

#include <objtool/check.h>
@@ -13,6 +14,8 @@
#include <objtool/endianness.h>
#include <arch/cfi_regs.h>

+#define to_cfi_reg(reg) (reg)
+
int arch_ftrace_match(char *name)
{
return !strcmp(name, "_mcount");
@@ -74,11 +77,18 @@ const char *arch_ret_insn(int len)
return (const char *)&ret;
}

+#define ADD_OP(op) \
+ if (!(op = calloc(1, sizeof(*op)))) \
+ return -1; \
+ else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
+
int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
unsigned long offset, unsigned int maxlen,
struct instruction *insn)
{
+ struct stack_op **ops_list = &insn->stack_ops;
const struct elf *elf = file->elf;
+ struct stack_op *op = NULL;
union loongarch_instruction inst;

if (!is_loongarch(elf))
@@ -97,6 +107,132 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
if (inst.word == 0)
insn->type = INSN_NOP;

+ switch (inst.reg2i12_format.opcode) {
+ case addid_op:
+ if ((inst.reg2i12_format.rj == CFI_SP) || (inst.reg2i12_format.rd == CFI_SP)) {
+ /* addi.d reg1,reg2,imm */
+ insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
+ ADD_OP(op) {
+ op->src.type = OP_SRC_ADD;
+ op->src.reg = to_cfi_reg(inst.reg2i12_format.rj);
+ op->src.offset = insn->immediate;
+ op->dest.type = OP_DEST_REG;
+ op->dest.reg = to_cfi_reg(inst.reg2i12_format.rd);
+ }
+ }
+ break;
+ case std_op:
+ if (inst.reg2i12_format.rj == CFI_SP) {
+ /* st.d reg,sp,imm */
+ insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
+ ADD_OP(op) {
+ op->src.type = OP_SRC_REG;
+ op->src.reg = to_cfi_reg(inst.reg2i12_format.rd);
+ op->dest.type = OP_DEST_REG_INDIRECT;
+ op->dest.reg = CFI_SP;
+ op->dest.offset = insn->immediate;
+ }
+ }
+ break;
+ case ldd_op:
+ if (inst.reg2i12_format.rj == CFI_SP) {
+ /* ld.d reg,sp,imm */
+ insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
+ ADD_OP(op) {
+ op->src.type = OP_SRC_REG_INDIRECT;
+ op->src.reg = CFI_SP;
+ op->src.offset = insn->immediate;
+ op->dest.type = OP_DEST_REG;
+ op->dest.reg = to_cfi_reg(inst.reg2i12_format.rd);
+ }
+ }
+ break;
+ case andi_op:
+ if (inst.reg2i12_format.immediate == 0 &&
+ inst.reg2i12_format.rj == 0 &&
+ inst.reg2i12_format.rd == 0)
+ /* nop */
+ insn->type = INSN_NOP;
+ break;
+ default:
+ switch (inst.reg2i16_format.opcode) {
+ case jirl_op:
+ if (inst.reg2i16_format.rj == CFI_RA &&
+ inst.reg2i16_format.rd == 0) {
+ /* jr ra */
+ insn->type = INSN_RETURN;
+ } else if (inst.reg2i16_format.rd == CFI_RA) {
+ /* jalr reg */
+ insn->type = INSN_CALL_DYNAMIC;
+ } else if (inst.reg2i16_format.rd == 0) {
+ /* jr reg */
+ insn->type = INSN_JUMP_DYNAMIC;
+ } else if (!inst.reg2i16_format.immediate) {
+ /* jirl */
+ insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
+ insn->type = INSN_JUMP_UNCONDITIONAL;
+ }
+ break;
+ case beq_op:
+ case bne_op:
+ case blt_op:
+ case bge_op:
+ case bltu_op:
+ case bgeu_op:
+ insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
+ insn->type = INSN_JUMP_CONDITIONAL;
+ break;
+ case beqz_op:
+ case bnez_op:
+ insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 |
+ inst.reg1i21_format.immediate_l, 20);
+ insn->type = INSN_JUMP_CONDITIONAL;
+ break;
+ case bl_op:
+ insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
+ inst.reg0i26_format.immediate_l, 25);
+ insn->type = INSN_CALL;
+ break;
+ case b_op:
+ insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
+ inst.reg0i26_format.immediate_l, 25);
+ insn->type = INSN_JUMP_UNCONDITIONAL;
+ break;
+ default:
+ if (inst.reg2i14_format.opcode == stptrd_op &&
+ inst.reg2i14_format.rj == CFI_SP) {
+ /* stptr.d reg,sp,imm */
+ insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
+ ADD_OP(op) {
+ op->src.type = OP_SRC_REG;
+ op->src.reg = to_cfi_reg(inst.reg2i14_format.rd);
+ op->dest.type = OP_DEST_REG_INDIRECT;
+ op->dest.reg = CFI_SP;
+ op->dest.offset = insn->immediate;
+ }
+ } else if (inst.reg2i14_format.opcode == ldptrd_op &&
+ inst.reg2i14_format.rj == CFI_SP) {
+ /* ldptr.d reg,sp,imm */
+ insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
+ ADD_OP(op) {
+ op->src.type = OP_SRC_REG_INDIRECT;
+ op->src.reg = CFI_SP;
+ op->src.offset = insn->immediate;
+ op->dest.type = OP_DEST_REG;
+ op->dest.reg = to_cfi_reg(inst.reg2i14_format.rd);
+ }
+ } else if (inst.reg0i15_format.opcode == break_op) {
+ /* break */
+ insn->type = INSN_BUG;
+ } else if (inst.reg2_format.opcode == ertn_op) {
+ /* ertn */
+ insn->type = INSN_RETURN;
+ }
+ break;
+ }
+ break;
+ }
+
return 0;
}

--
2.39.2



2023-06-20 08:02:08

by Youling Tang

[permalink] [raw]
Subject: [RFC PATCH v1 11/23] objtool: Add annotate_reachable() for objtools

x86 removed annotate_reachable and replaced it with ASM_REACHABLE
which is not suitable for LoongArch micro.

Re-add annotation_reachable() for LoongArch.

Signed-off-by: Chen Zhongjin <[email protected]>
Signed-off-by: Youling Tang <[email protected]>
---
include/linux/compiler.h | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 947a60b801db..6b33de8bf7c3 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -117,6 +117,14 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
*/
#define __stringify_label(n) #n

+#define __annotate_reachable(c) ({ \
+ asm volatile(__stringify_label(c) ":\n\t" \
+ ".pushsection .discard.reachable\n\t" \
+ ".long " __stringify_label(c) "b - .\n\t" \
+ ".popsection\n\t"); \
+})
+#define annotate_reachable() __annotate_reachable(__COUNTER__)
+
#define __annotate_unreachable(c) ({ \
asm volatile(__stringify_label(c) ":\n\t" \
".pushsection .discard.unreachable\n\t" \
@@ -129,6 +137,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
#define __annotate_jump_table __section(".rodata..c_jump_table")

#else /* !CONFIG_OBJTOOL */
+#define annotate_reachable()
#define annotate_unreachable()
#define __annotate_jump_table
#endif /* CONFIG_OBJTOOL */
--
2.39.2


2023-06-20 08:11:48

by Youling Tang

[permalink] [raw]
Subject: [RFC PATCH v1 14/23] objtool: Add orc_print_dump() package

There is no functional change, only operations such as orc_print_{sp,fp}
are encapsulated into orc_print_dump(). It is convenient to add LoongArch
support later (because it needs to add orc_print_ra()).

Signed-off-by: Youling Tang <[email protected]>
---
tools/objtool/arch/x86/orc.c | 23 ++++++++++++++++++-----
tools/objtool/include/objtool/orc.h | 5 +----
tools/objtool/orc_dump.c | 12 +-----------
3 files changed, 20 insertions(+), 20 deletions(-)

diff --git a/tools/objtool/arch/x86/orc.c b/tools/objtool/arch/x86/orc.c
index a0c00e136089..cf546b274a79 100644
--- a/tools/objtool/arch/x86/orc.c
+++ b/tools/objtool/arch/x86/orc.c
@@ -114,7 +114,7 @@ static const char *reg_name(unsigned int reg)
}
}

-const char *orc_type_name(unsigned int type)
+static const char *orc_type_name(unsigned int type)
{
switch (type) {
case UNWIND_HINT_TYPE_CALL:
@@ -128,7 +128,7 @@ const char *orc_type_name(unsigned int type)
}
}

-void orc_print_reg(unsigned int reg, int offset)
+static void orc_print_reg(unsigned int reg, int offset)
{
if (reg == ORC_REG_BP_INDIRECT)
printf("(bp%+d)", offset);
@@ -140,12 +140,25 @@ void orc_print_reg(unsigned int reg, int offset)
printf("%s%+d", reg_name(reg), offset);
}

-void orc_print_sp(void)
+static void orc_print_sp(void)
{
printf(" sp:");
}
-
-void orc_print_fp(void)
+static void orc_print_fp(void)
{
printf(" bp:");
}
+
+void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i)
+{
+ orc_print_sp();
+
+ orc_print_reg(orc[i].sp_reg, bswap_if_needed(dummy_elf, orc[i].sp_offset));
+
+ orc_print_fp();
+
+ orc_print_reg(orc[i].bp_reg, bswap_if_needed(dummy_elf, orc[i].bp_offset));
+
+ printf(" type:%s signal:%d end:%d\n",
+ orc_type_name(orc[i].type), orc[i].signal, orc[i].end);
+}
diff --git a/tools/objtool/include/objtool/orc.h b/tools/objtool/include/objtool/orc.h
index bf141134c56f..53a037bdfc35 100644
--- a/tools/objtool/include/objtool/orc.h
+++ b/tools/objtool/include/objtool/orc.h
@@ -10,9 +10,6 @@

int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
struct instruction *insn);
-const char *orc_type_name(unsigned int type);
-void orc_print_reg(unsigned int reg, int offset);
-void orc_print_sp(void);
-void orc_print_fp(void);
+void orc_print_dump(struct elf *dummy_elf, struct orc_entry *orc, int i);

#endif /* _OBJTOOL_ORC_H */
diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c
index 82bdd33dbc39..c274c0577427 100644
--- a/tools/objtool/orc_dump.c
+++ b/tools/objtool/orc_dump.c
@@ -151,17 +151,7 @@ int orc_dump(const char *_objname)
printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i]));
}

-
- orc_print_sp();
-
- orc_print_reg(orc[i].sp_reg, bswap_if_needed(&dummy_elf, orc[i].sp_offset));
-
- orc_print_fp();
-
- orc_print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset));
-
- printf(" type:%s signal:%d end:%d\n",
- orc_type_name(orc[i].type), orc[i].signal, orc[i].end);
+ orc_print_dump(&dummy_elf, orc, i);
}

elf_end(elf);
--
2.39.2


2023-06-20 08:28:20

by Youling Tang

[permalink] [raw]
Subject: [RFC PATCH v1 12/23] LoongArch: bug: Add reachable annotation to warning macros

WARN* and BUG* both use `break 0x1` opcodes and the distinction is
provided by the contents of the bug table. This table is not accessible
to objtool, so add an annotation to WARN* macros to let objtool know
that break handler will return an resume the execution of the instructions
following the WARN's break.

Signed-off-by: Youling Tang <[email protected]>
---
arch/loongarch/include/asm/bug.h | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/loongarch/include/asm/bug.h b/arch/loongarch/include/asm/bug.h
index d4ca3ba25418..08388876ade4 100644
--- a/arch/loongarch/include/asm/bug.h
+++ b/arch/loongarch/include/asm/bug.h
@@ -44,6 +44,7 @@
do { \
instrumentation_begin(); \
__BUG_FLAGS(BUGFLAG_WARNING|(flags)); \
+ annotate_reachable(); \
instrumentation_end(); \
} while (0)

--
2.39.2


2023-06-20 08:37:27

by Youling Tang

[permalink] [raw]
Subject: [RFC PATCH v1 13/23] objtool: Add next member in struct reloc

In LoongArch, there may be multiple relocation information in one location,
so the next member is added to handle this situation.

The following warning appears when the next member is not added,
warning: objtool: unexpected relocation symbol type in .rela.discard.unreachable

Relocation section '.rela.discard.unreachable' at offset 0x1a58 contains 4 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000000 0000000200000032 R_LARCH_ADD32 0000000000000000 .text + 354
0000000000000000 0000000900000037 R_LARCH_SUB32 0000000000000000 L0^A + 0

Co-developed-by: Jinyang He <[email protected]>
Signed-off-by: Jinyang He <[email protected]>
Signed-off-by: Youling Tang <[email protected]>
---
tools/objtool/elf.c | 11 ++++++++++-
tools/objtool/include/objtool/elf.h | 1 +
2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
index 6806ce01d933..d345300d269b 100644
--- a/tools/objtool/elf.c
+++ b/tools/objtool/elf.c
@@ -895,7 +895,7 @@ static int read_relocs(struct elf *elf)
{
unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0;
struct section *sec;
- struct reloc *reloc;
+ struct reloc *reloc, *next_reloc;
unsigned int symndx;
struct symbol *sym;
int i;
@@ -915,6 +915,7 @@ static int read_relocs(struct elf *elf)
return -1;
}

+ next_reloc = NULL;
sec->base->reloc = sec;

nr_reloc = 0;
@@ -946,6 +947,14 @@ static int read_relocs(struct elf *elf)
return -1;
}

+ if (next_reloc && reloc->offset == next_reloc->offset) {
+ next_reloc->next = reloc;
+ next_reloc = reloc;
+ continue;
+ }
+
+ next_reloc = reloc;
+
list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list);
list_add_tail(&reloc->list, &sec->reloc_list);
elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
index ad0024da262b..7877298fe401 100644
--- a/tools/objtool/include/objtool/elf.h
+++ b/tools/objtool/include/objtool/elf.h
@@ -68,6 +68,7 @@ struct symbol {
struct reloc {
struct list_head list;
struct hlist_node hash;
+ struct reloc *next;
union {
GElf_Rela rela;
GElf_Rel rel;
--
2.39.2


2023-06-20 09:05:46

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [RFC PATCH v1 13/23] objtool: Add next member in struct reloc

On Tue, Jun 20, 2023 at 03:50:09PM +0800, Youling Tang wrote:
> In LoongArch, there may be multiple relocation information in one location,
> so the next member is added to handle this situation.

So Josh did a shrink on struct reloc because there are too many of them;
ideally we find another way to link them for the case where it is
needed.

>
> The following warning appears when the next member is not added,
> warning: objtool: unexpected relocation symbol type in .rela.discard.unreachable
>
> Relocation section '.rela.discard.unreachable' at offset 0x1a58 contains 4 entries:
> Offset Info Type Symbol's Value Symbol's Name + Addend
> 0000000000000000 0000000200000032 R_LARCH_ADD32 0000000000000000 .text + 354
> 0000000000000000 0000000900000037 R_LARCH_SUB32 0000000000000000 L0^A + 0
>
> Co-developed-by: Jinyang He <[email protected]>
> Signed-off-by: Jinyang He <[email protected]>
> Signed-off-by: Youling Tang <[email protected]>
> ---
> tools/objtool/elf.c | 11 ++++++++++-
> tools/objtool/include/objtool/elf.h | 1 +
> 2 files changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c
> index 6806ce01d933..d345300d269b 100644
> --- a/tools/objtool/elf.c
> +++ b/tools/objtool/elf.c
> @@ -895,7 +895,7 @@ static int read_relocs(struct elf *elf)
> {
> unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0;
> struct section *sec;
> - struct reloc *reloc;
> + struct reloc *reloc, *next_reloc;
> unsigned int symndx;
> struct symbol *sym;
> int i;
> @@ -915,6 +915,7 @@ static int read_relocs(struct elf *elf)
> return -1;
> }
>
> + next_reloc = NULL;
> sec->base->reloc = sec;
>
> nr_reloc = 0;
> @@ -946,6 +947,14 @@ static int read_relocs(struct elf *elf)
> return -1;
> }
>
> + if (next_reloc && reloc->offset == next_reloc->offset) {
> + next_reloc->next = reloc;
> + next_reloc = reloc;
> + continue;
> + }
> +
> + next_reloc = reloc;

This seems to rely on 'linked' reloc being adjecent in the ELF tables;
is this required by the LoongArch ELF spec? If not, you really should
not rely on it.

> +
> list_add_tail(&reloc->sym_reloc_entry, &sym->reloc_list);
> list_add_tail(&reloc->list, &sec->reloc_list);
> elf_hash_add(reloc, &reloc->hash, reloc_hash(reloc));
> diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h
> index ad0024da262b..7877298fe401 100644
> --- a/tools/objtool/include/objtool/elf.h
> +++ b/tools/objtool/include/objtool/elf.h
> @@ -68,6 +68,7 @@ struct symbol {
> struct reloc {
> struct list_head list;
> struct hlist_node hash;
> + struct reloc *next;
> union {
> GElf_Rela rela;
> GElf_Rel rel;
> --
> 2.39.2
>