Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LoongArch support for kpatch #1424

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion kpatch-build/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ PLUGIN_CFLAGS := $(filter-out -std=gnu11 -Wconversion, $(CFLAGS))
PLUGIN_CFLAGS += -shared -I$(GCC_PLUGINS_DIR)/include \
-Igcc-plugins -fPIC -fno-rtti -O2 -Wall
endif
ifeq ($(filter $(ARCH),s390x x86_64 ppc64le),)
ifeq ($(filter $(ARCH),s390x x86_64 ppc64le loongarch64),)
$(error Unsupported architecture ${ARCH}, check https://github.com/dynup/kpatch/#supported-architectures)
endif

Expand Down
70 changes: 62 additions & 8 deletions kpatch-build/create-diff-object.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ static bool is_gcc6_localentry_bundled_sym(struct kpatch_elf *kelf,
return false;
case S390:
return false;
case LOONGARCH64:
return false;
default:
ERROR("unsupported arch");
}
Expand Down Expand Up @@ -615,9 +617,11 @@ static void kpatch_compare_correlated_section(struct section *sec)
!is_text_section(sec1)))
DIFF_FATAL("%s section header details differ from %s", sec1->name, sec2->name);

/* Short circuit for mcount sections, we rebuild regardless */
/* Short circuit for mcount/patchable sections, we rebuild regardless */
if (!strcmp(sec->name, ".rela__mcount_loc") ||
!strcmp(sec->name, "__mcount_loc")) {
!strcmp(sec->name, "__mcount_loc") ||
!strcmp(sec->name, ".rela__patchable_function_entries") ||
!strcmp(sec->name, "__patchable_function_entries")) {
sec->status = SAME;
goto out;
}
Expand Down Expand Up @@ -689,6 +693,11 @@ static bool insn_is_load_immediate(struct kpatch_elf *kelf, void *addr)

break;

case LOONGARCH64:
/* to be done */

break;

case S390:
/* arg2: lghi %r3, imm */
if (insn[0] == 0xa7 && insn[1] == 0x39)
Expand Down Expand Up @@ -2415,22 +2424,22 @@ static bool static_call_sites_group_filter(struct lookup_table *lookup,
static struct special_section special_sections[] = {
{
.name = "__bug_table",
.arch = X86_64 | PPC64 | S390,
.arch = X86_64 | PPC64 | S390 | LOONGARCH64,
.group_size = bug_table_group_size,
},
{
.name = ".fixup",
.arch = X86_64 | PPC64 | S390,
.arch = X86_64 | PPC64 | S390 | LOONGARCH64,
.group_size = fixup_group_size,
},
{
.name = "__ex_table", /* must come after .fixup */
.arch = X86_64 | PPC64 | S390,
.arch = X86_64 | PPC64 | S390 | LOONGARCH64,
.group_size = ex_table_group_size,
},
{
.name = "__jump_table",
.arch = X86_64 | PPC64 | S390,
.arch = X86_64 | PPC64 | S390 | LOONGARCH64,
.group_size = jump_table_group_size,
.group_filter = jump_table_group_filter,
},
Expand All @@ -2451,7 +2460,7 @@ static struct special_section special_sections[] = {
},
{
.name = ".altinstructions",
.arch = X86_64 | S390,
.arch = X86_64 | S390 | LOONGARCH64,
.group_size = altinstructions_group_size,
},
{
Expand Down Expand Up @@ -3699,7 +3708,22 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
nr++;

/* create text/rela section pair */
sec = create_section_pair(kelf, "__mcount_loc", sizeof(void*), nr);
if (kelf->arch == LOONGARCH64) {
sec = create_section_pair(kelf, "__patchable_function_entries", sizeof(void *), nr);
/*
* With -fpatchable-function-entry, LoongArch GCC sets WAL flags
* for section __patchable_function_entries in object file
* (certainly include patch-hook.o) by default.
* Kpatch sets A flags for output.o in create_section_pair().
* So here assign sh_flags = WAL to fix error:
* "ld: __patchable_function_entries has both ordered and
* unordered sections" when link patch-hook.o and output.o.
*/
sec->sh.sh_flags = SHF_WRITE | SHF_ALLOC | SHF_LINK_ORDER;
sec->sh.sh_link = 1;
} else
sec = create_section_pair(kelf, "__mcount_loc", sizeof(void *), nr);

relasec = sec->rela;

/* populate sections */
Expand Down Expand Up @@ -3779,6 +3803,20 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
insn_offset = sym->sym.st_value;
break;
}
case LOONGARCH64: {
#define LOONGARCH_NOP 0x03400000
bool found = false;
unsigned int *insn = sym->sec->data->d_buf + sym->sym.st_value;

if (*insn == LOONGARCH_NOP && *(insn + 1) == LOONGARCH_NOP)
found = true;

if (!found)
ERROR("%s: unexpected instruction at the start of the function", sym->name);

insn_offset = 0;
break;
}
default:
ERROR("unsupported arch");
}
Expand Down Expand Up @@ -3984,6 +4022,22 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf)
insn[4] == 0x00 && insn[5] == 0x00)
sym->has_func_profiling = 1;
break;
case LOONGARCH64:
struct section *sec;

sec = find_section_by_name(&kelf->sections,
"__patchable_function_entries");
if (sec) {
list_for_each_entry(rela, &sec->rela->relas, list) {
if (rela->sym->sec == sym->sec &&
(rela->sym->sym.st_value +
rela->addend) == sym->sym.st_value) {
sym->has_func_profiling = 1;
break;
}
}
}
break;
default:
ERROR("unsupported arch");
}
Expand Down
3 changes: 3 additions & 0 deletions kpatch-build/kpatch-build
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,9 @@ find_special_section_data() {
"s390x")
check[a]=true # alt_instr
;;
"loongarch64")
check[a]=true # alt_instr
;;
esac

# Kernel CONFIG_ features
Expand Down
34 changes: 34 additions & 0 deletions kpatch-build/kpatch-elf.c
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ unsigned int absolute_rela_type(struct kpatch_elf *kelf)
return R_X86_64_64;
case S390:
return R_390_64;
case LOONGARCH64:
return R_LARCH_64;
default:
ERROR("unsupported arch");
}
Expand Down Expand Up @@ -206,6 +208,7 @@ long rela_target_offset(struct kpatch_elf *kelf, struct section *relasec,

switch(kelf->arch) {
case PPC64:
case LOONGARCH64:
add_off = 0;
break;
case X86_64:
Expand Down Expand Up @@ -261,6 +264,7 @@ unsigned int insn_length(struct kpatch_elf *kelf, void *addr)
return decoded_insn.length;

case PPC64:
case LOONGARCH64:
return 4;

case S390:
Expand Down Expand Up @@ -332,6 +336,21 @@ static void kpatch_create_rela_list(struct kpatch_elf *kelf,
rela->sym->name, rela->addend);
}

if (kelf->arch == LOONGARCH64) {
/*
* LoongArch GCC creates local labels such as .LBB7266,
* replace them with section symbols.
*/
if (rela->sym->sec && (rela->sym->type == STT_NOTYPE) &&
(rela->sym->bind == STB_LOCAL)) {
log_debug("local label: %s -> ", rela->sym->name);

rela->addend += rela->sym->sym.st_value;
rela->sym = rela->sym->sec->secsym;
log_debug("section symbol: %s\n", rela->sym->name);
}
}

if (skip)
continue;
log_debug("offset %d, type %d, %s %s %ld", rela->offset,
Expand Down Expand Up @@ -593,6 +612,9 @@ struct kpatch_elf *kpatch_elf_open(const char *name)
case EM_S390:
kelf->arch = S390;
break;
case EM_LOONGARCH:
kelf->arch = LOONGARCH64;
break;
default:
ERROR("Unsupported target architecture");
}
Expand All @@ -608,6 +630,18 @@ struct kpatch_elf *kpatch_elf_open(const char *name)
kpatch_create_rela_list(kelf, relasec);
}

if (kelf->arch == LOONGARCH64) {
struct symbol *sym, *tmp;

/* Delete local labels created by LoongArch GCC */
list_for_each_entry_safe(sym, tmp, &kelf->symbols, list) {
if (sym->sec && !is_rela_section(sym->sec) &&
(sym->type == STT_NOTYPE) &&
(sym->bind == STB_LOCAL))
list_del(&sym->list);
}
}

return kelf;
}

Expand Down
6 changes: 6 additions & 0 deletions kpatch-build/kpatch-elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
#define SHF_RELA_LIVEPATCH 0x00100000
#define SHN_LIVEPATCH 0xff20

#ifndef __loongarch__
#define EM_LOONGARCH 258 /* LoongArch */
#define R_LARCH_64 2
#endif

/*******************
* Data structures
* ****************/
Expand Down Expand Up @@ -115,6 +120,7 @@ enum architecture {
PPC64 = 0x1 << 0,
X86_64 = 0x1 << 1,
S390 = 0x1 << 2,
LOONGARCH64 = 0x1 << 3,
};

struct kpatch_elf {
Expand Down
Loading