Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions arch/loongarch/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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"
Expand Down
6 changes: 5 additions & 1 deletion arch/loongarch/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions arch/loongarch/configs/deepin_loongarch_desktop_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions arch/loongarch/configs/loongson3_defconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
4 changes: 4 additions & 0 deletions arch/loongarch/kernel/fpu.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion include/linux/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
11 changes: 10 additions & 1 deletion tools/objtool/arch/loongarch/decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
156 changes: 155 additions & 1 deletion tools/objtool/arch/loongarch/special.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string.h>
#include <objtool/special.h>
#include <objtool/warn.h>

bool arch_support_alt_relocation(struct special_alt *special_alt,
struct instruction *insn,
Expand All @@ -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,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider extracting the nested sorting logic into a dedicated helper function.

Consider extracting the nested sorting logic into its own routine to eliminate the duplicated pointer arithmetic and deeply nested loops. For example, if you have a list sort helper available (or can add one), you could write a dedicated comparator and sort routine:

static int table_info_cmp(void *priv, struct list_head *a, struct list_head *b)
{
    struct table_info *ta = list_entry(a, struct table_info, jump_info);
    struct table_info *tb = list_entry(b, struct table_info, jump_info);
    return (ta->rodata_offset < tb->rodata_offset) ? -1 : (ta->rodata_offset > tb->rodata_offset);
}

static void sort_table_list(struct list_head *head)
{
    /* Assuming list_sort is available; otherwise, implement your own sort using the comparator */
    list_sort(NULL, head, table_info_cmp);
}

Then update the function to delegate the sorting:

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 *table;

	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;

		table = malloc(sizeof(struct table_info));
		if (!table) {
			WARN("malloc failed");
			return;
		}

		table->insn_offset = reloc_addend(reloc);
		reloc++;
		table->rodata_offset = reloc_addend(reloc);

		list_add_tail(&table->jump_info, &table_list);

		if (reloc_idx(reloc) + 1 == sec_num_entries(rsec))
			break;
	}

	/* Sort the list to simplify subsequent operations */
	sort_table_list(&table_list);

	list_for_each_entry(table, &table_list, jump_info) {
		if (insn->offset == table->insn_offset) {
			struct table_info *next_table = list_next_entry(table, jump_info);
			while (&next_table->jump_info != &table_list &&
			       next_table->rodata_offset == table->rodata_offset)
				next_table = list_next_entry(next_table, jump_info);

			insn->table_size = next_table->rodata_offset - table->rodata_offset;
		}
	}
}

This separation increases clarity, isolates pointer arithmetic, and makes the control flow easier to follow while keeping the functionality intact.

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));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Potential memory leak with allocated table_info objects.

The allocated table_info structures are added into the local list but never freed after computing table_size. Consider freeing these allocations before the function returns.

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;
}
Loading
Loading