diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 50a3d95117598..f5348d6647ea3 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -135,7 +135,7 @@ config LOONGARCH select HAVE_KVM select HAVE_MOD_ARCH_SPECIFIC select HAVE_NMI - select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS && AS_HAS_THIN_ADD_SUB && !CC_IS_CLANG + select HAVE_OBJTOOL if AS_HAS_EXPLICIT_RELOCS && AS_HAS_THIN_ADD_SUB select HAVE_PCI select HAVE_PERF_EVENTS select HAVE_PERF_REGS @@ -262,7 +262,7 @@ config AS_HAS_FCSR_CLASS def_bool $(as-instr,movfcsr2gr \$t0$(comma)\$fcsr0) config AS_HAS_THIN_ADD_SUB - def_bool $(cc-option,-Wa$(comma)-mthin-add-sub) + def_bool $(cc-option,-Wa$(comma)-mthin-add-sub) || AS_IS_LLVM config AS_HAS_LSX_EXTENSION def_bool $(as-instr,vld \$vr0$(comma)\$a0$(comma)0) @@ -276,6 +276,9 @@ config AS_HAS_LBT_EXTENSION config AS_HAS_LVZ_EXTENSION def_bool $(as-instr,hvcl 0) +config CC_HAS_ANNOTATE_TABLEJUMP + def_bool $(cc-option,-mannotate-tablejump) + menu "Kernel type and options" source "kernel/Kconfig.hz" diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index e05c10b044560..b9514ef3b6507 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -94,7 +94,11 @@ KBUILD_AFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma) KBUILD_CFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub) ifdef CONFIG_OBJTOOL -KBUILD_CFLAGS += -fno-jump-tables +ifdef CONFIG_CC_HAS_ANNOTATE_TABLEJUMP +KBUILD_CFLAGS += $(call cc-option,-mannotate-tablejump) +else +KBUILD_CFLAGS += -fno-jump-tables # keep compatibility with older compilers +endif endif ifeq ($(CONFIG_RELOCATABLE),y) diff --git a/arch/loongarch/configs/deepin_loongarch_desktop_defconfig b/arch/loongarch/configs/deepin_loongarch_desktop_defconfig index 721a099a1a86b..c7f4aab5e44d7 100644 --- a/arch/loongarch/configs/deepin_loongarch_desktop_defconfig +++ b/arch/loongarch/configs/deepin_loongarch_desktop_defconfig @@ -6010,3 +6010,4 @@ CONFIG_RV_MON_WIP=y CONFIG_RV_MON_WWNR=y # CONFIG_STRICT_DEVMEM is not set # CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_UNWINDER_ORC=y diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 75f0caefacc5f..a9943d2c1ea42 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -2203,3 +2203,4 @@ CONFIG_RCU_CPU_STALL_TIMEOUT=60 # CONFIG_RCU_TRACE is not set # CONFIG_STRICT_DEVMEM is not set # CONFIG_RUNTIME_TESTING_MENU is not set +CONFIG_UNWINDER_ORC=y diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S index 69a85f2479fba..6ab640101457c 100644 --- a/arch/loongarch/kernel/fpu.S +++ b/arch/loongarch/kernel/fpu.S @@ -530,6 +530,10 @@ SYM_FUNC_END(_restore_lasx_context) #ifdef CONFIG_CPU_HAS_LBT STACK_FRAME_NON_STANDARD _restore_fp +#ifdef CONFIG_CPU_HAS_LSX STACK_FRAME_NON_STANDARD _restore_lsx +#endif +#ifdef CONFIG_CPU_HAS_LASX STACK_FRAME_NON_STANDARD _restore_lasx #endif +#endif diff --git a/include/linux/compiler.h b/include/linux/compiler.h index 0517d344baa05..db140f106f3df 100644 --- a/include/linux/compiler.h +++ b/include/linux/compiler.h @@ -133,7 +133,7 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, #define annotate_unreachable() __annotate_unreachable(__COUNTER__) /* Annotate a C jump table to allow objtool to follow the code flow */ -#define __annotate_jump_table __section(".rodata..c_jump_table") +#define __annotate_jump_table __section(".rodata..c_jump_table,\"a\",@progbits #") #else /* !CONFIG_OBJTOOL */ #define annotate_reachable() diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index aee479d2191c2..69b66994f2a15 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -122,7 +122,7 @@ static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst, switch (inst.reg2i12_format.opcode) { case addid_op: if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) { - /* addi.d sp,sp,si12 or addi.d fp,sp,si12 */ + /* addi.d sp,sp,si12 or addi.d fp,sp,si12 or addi.d sp,fp,si12 */ insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); ADD_OP(op) { op->src.type = OP_SRC_ADD; @@ -132,6 +132,15 @@ static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst, op->dest.reg = inst.reg2i12_format.rd; } } + if ((inst.reg2i12_format.rd == CFI_SP) && (inst.reg2i12_format.rj == CFI_FP)) { + /* addi.d sp,fp,si12 */ + struct symbol *func = find_func_containing(insn->sec, insn->offset); + + if (!func) + return false; + + func->frame_pointer = true; + } break; case ldd_op: if (inst.reg2i12_format.rj == CFI_SP) { diff --git a/tools/objtool/arch/loongarch/special.c b/tools/objtool/arch/loongarch/special.c index 9bba1e9318e0b..c5f4217df3a7a 100644 --- a/tools/objtool/arch/loongarch/special.c +++ b/tools/objtool/arch/loongarch/special.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include #include +#include bool arch_support_alt_relocation(struct special_alt *special_alt, struct instruction *insn, @@ -8,8 +10,160 @@ bool arch_support_alt_relocation(struct special_alt *special_alt, return false; } +struct table_info { + struct list_head jump_info; + unsigned long insn_offset; + unsigned long rodata_offset; +}; + +static void get_rodata_table_size_by_table_annotate(struct objtool_file *file, + struct instruction *insn) +{ + struct section *rsec; + struct reloc *reloc; + struct list_head table_list; + struct table_info *orig_table; + struct table_info *next_table; + unsigned long tmp_insn_offset; + unsigned long tmp_rodata_offset; + + rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); + if (!rsec) + return; + + INIT_LIST_HEAD(&table_list); + + for_each_reloc(rsec, reloc) { + if (reloc->sym->type != STT_SECTION) + return; + + orig_table = malloc(sizeof(struct table_info)); + if (!orig_table) { + WARN("malloc failed"); + return; + } + + orig_table->insn_offset = reloc_addend(reloc); + reloc++; + orig_table->rodata_offset = reloc_addend(reloc); + + list_add_tail(&orig_table->jump_info, &table_list); + + if (reloc_idx(reloc) + 1 == sec_num_entries(rsec)) + break; + } + + list_for_each_entry(orig_table, &table_list, jump_info) { + next_table = list_next_entry(orig_table, jump_info); + list_for_each_entry_from(next_table, &table_list, jump_info) { + if (next_table->rodata_offset < orig_table->rodata_offset) { + tmp_insn_offset = next_table->insn_offset; + tmp_rodata_offset = next_table->rodata_offset; + next_table->insn_offset = orig_table->insn_offset; + next_table->rodata_offset = orig_table->rodata_offset; + orig_table->insn_offset = tmp_insn_offset; + orig_table->rodata_offset = tmp_rodata_offset; + } + } + } + + list_for_each_entry(orig_table, &table_list, jump_info) { + if (insn->offset == orig_table->insn_offset) { + next_table = list_next_entry(orig_table, jump_info); + if (&next_table->jump_info == &table_list) + break; + + while (next_table->rodata_offset == orig_table->rodata_offset) + next_table = list_next_entry(next_table, jump_info); + + insn->table_size = next_table->rodata_offset - orig_table->rodata_offset; + } + } +} + +static struct reloc *find_reloc_by_table_annotate(struct objtool_file *file, + struct instruction *insn) +{ + struct section *rsec; + struct reloc *reloc; + unsigned long offset; + + rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); + if (!rsec) + return NULL; + + for_each_reloc(rsec, reloc) { + if (reloc->sym->sec->rodata) + continue; + + if (strcmp(insn->sec->name, reloc->sym->sec->name)) + continue; + + if (reloc->sym->type == STT_SECTION) + offset = reloc_addend(reloc); + else + offset = reloc->sym->offset; + + if (insn->offset == offset) { + get_rodata_table_size_by_table_annotate(file, insn); + reloc++; + return reloc; + } + } + + return NULL; +} + +static struct reloc *find_reloc_of_rodata_c_jump_table(struct section *sec, + unsigned long offset) +{ + struct section *rsec; + struct reloc *reloc; + + rsec = sec->rsec; + if (!rsec) + return NULL; + + for_each_reloc(rsec, reloc) { + if (reloc_offset(reloc) > offset) + break; + + if (!strncmp(reloc->sym->sec->name, ".rodata..c_jump_table", 21)) + return reloc; + } + + return NULL; +} + struct reloc *arch_find_switch_table(struct objtool_file *file, struct instruction *insn) { - return NULL; + struct reloc *annotate_reloc; + struct reloc *rodata_reloc; + struct section *table_sec; + unsigned long table_offset; + + annotate_reloc = find_reloc_by_table_annotate(file, insn); + if (!annotate_reloc) { + annotate_reloc = find_reloc_of_rodata_c_jump_table(insn->sec, insn->offset); + if (!annotate_reloc) + return NULL; + } + + table_sec = annotate_reloc->sym->sec; + if (annotate_reloc->sym->type == STT_SECTION) + table_offset = reloc_addend(annotate_reloc); + else + table_offset = annotate_reloc->sym->offset; + + /* + * 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) + return NULL; + + return rodata_reloc; } diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 02090727e9b17..da74e73cb339e 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -22,6 +22,14 @@ #include #include +#ifndef EM_LOONGARCH +#define EM_LOONGARCH 258 +#endif + +#ifndef R_LARCH_32_PCREL +#define R_LARCH_32_PCREL 99 +#endif + struct alternative { struct alternative *next; struct instruction *insn; @@ -661,6 +669,18 @@ static int add_dead_ends(struct objtool_file *file) } insn->dead_end = false; + + /* Handle the special cases compiled with Clang on LoongArch */ + if (file->elf->ehdr.e_machine == EM_LOONGARCH && + reloc->sym->type == STT_SECTION) { + while (insn && insn_func(insn)) { + insn = prev_insn_same_sym(file, insn); + if (insn && insn->dead_end) { + insn->dead_end = false; + break; + } + } + } } return 0; @@ -2011,6 +2031,9 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, unsigned int prev_offset = 0; struct reloc *reloc = table; struct alternative *alt; + unsigned long offset; + unsigned long rodata_entry_size; + unsigned long rodata_table_size = insn->table_size; /* * Each @reloc is a switch table relocation which points to the target @@ -2022,19 +2045,51 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, if (reloc != table && reloc == next_table) break; + /* Handle the special cases compiled with Clang on LoongArch */ + if (file->elf->ehdr.e_machine == EM_LOONGARCH && + reloc->sym->type == STT_SECTION && reloc != table && + reloc_offset(reloc) == reloc_offset(table) + rodata_table_size) + break; + + /* Handle the special cases compiled with Clang on LoongArch */ + if (file->elf->ehdr.e_machine == EM_LOONGARCH && + reloc_type(reloc) == R_LARCH_32_PCREL) + rodata_entry_size = 4; + else + rodata_entry_size = 8; + /* Make sure the table entries are consecutive: */ - if (prev_offset && reloc_offset(reloc) != prev_offset + 8) + if (prev_offset && reloc_offset(reloc) != prev_offset + rodata_entry_size) break; + if (reloc->sym->type == STT_SECTION) { + /* Addend field in the relocation entry associated with the symbol */ + offset = reloc_addend(reloc); + /* Handle the special cases compiled with Clang on LoongArch */ + if (file->elf->ehdr.e_machine == EM_LOONGARCH && + reloc_type(reloc) == R_LARCH_32_PCREL) + offset = reloc->sym->offset + reloc_addend(reloc) - + (reloc_offset(reloc) - reloc_offset(table)); + } else { + /* The address of the symbol in the relocation entry */ + offset = reloc->sym->offset; + } + /* Detect function pointers from contiguous objects: */ - if (reloc->sym->sec == pfunc->sec && - reloc_addend(reloc) == pfunc->offset) + if (reloc->sym->sec == pfunc->sec && offset == pfunc->offset) break; - dest_insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); + dest_insn = find_insn(file, reloc->sym->sec, offset); if (!dest_insn) break; + /* Handle the special cases compiled with Clang on LoongArch */ + if (file->elf->ehdr.e_machine == EM_LOONGARCH && reloc->sym->type == STT_SECTION && + (!insn_func(dest_insn) || insn_func(dest_insn)->pfunc != pfunc)) { + prev_offset = reloc_offset(reloc); + continue; + } + /* Make sure the destination is in the same function: */ if (!insn_func(dest_insn) || insn_func(dest_insn)->pfunc != pfunc) break; @@ -2069,6 +2124,7 @@ static struct reloc *find_jump_table(struct objtool_file *file, { struct reloc *table_reloc; struct instruction *dest_insn, *orig_insn = insn; + unsigned long offset; /* * Backward search using the @first_jump_src links, these help avoid @@ -2092,7 +2148,16 @@ static struct reloc *find_jump_table(struct objtool_file *file, table_reloc = arch_find_switch_table(file, insn); if (!table_reloc) continue; - dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc)); + + if (table_reloc->sym->type == STT_SECTION) { + /* Addend field in the relocation entry associated with the symbol */ + offset = reloc_addend(table_reloc); + } else { + /* The address of the symbol in the relocation entry */ + offset = table_reloc->sym->offset; + } + + dest_insn = find_insn(file, table_reloc->sym->sec, offset); if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func) continue; @@ -2975,10 +3040,27 @@ static int update_cfi_state(struct instruction *insn, break; } - if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) { + if (op->dest.reg == CFI_BP && op->src.reg == CFI_SP && + insn->sym->frame_pointer) { + /* addi.d fp,sp,imm on LoongArch */ + if (cfa->base == CFI_SP && cfa->offset == op->src.offset) { + cfa->base = CFI_BP; + cfa->offset = 0; + } + break; + } - /* lea disp(%rbp), %rsp */ - cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset); + if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) { + /* addi.d sp,fp,imm on LoongArch */ + if (cfa->base == CFI_BP && cfa->offset == 0) { + if (insn->sym->frame_pointer) { + cfa->base = CFI_SP; + cfa->offset = -op->src.offset; + } + } else { + /* lea disp(%rbp), %rsp */ + cfi->stack_size = -(op->src.offset + regs[CFI_BP].offset); + } break; } diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index daa46f1f0965a..11aafcc7b0c7e 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -46,6 +46,7 @@ struct instruction { struct section *sec; unsigned long offset; unsigned long immediate; + unsigned long table_size; u8 len; u8 prev_len; diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index 2b8a69de4db87..d7e815c2fd156 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -68,6 +68,7 @@ struct symbol { u8 warned : 1; u8 embedded_insn : 1; u8 local_label : 1; + u8 frame_pointer : 1; struct list_head pv_target; struct reloc *relocs; };