Skip to content

Commit 1eb2f02

Browse files
georgejguogeorge
authored andcommitted
kpatch/LoongArch: Convert PC-relative LA instructions to GOT version for livepatch
Introduces a post-processing function `kpatch_modify_la_rela_sections()` which: 1. Scans all rela sections within executable segments (`SHF_EXECINSTR`). 2. Identifies `la` instructions that use the PC-relative sequence (`pcalau12i + addi.d`) via the `is_la_pcrel()` helper. 3. Modifies the instruction sequence in-place to use the GOT-based sequence (`pcalau12i + ld.d`) via `la_pcrel_to_la_got()`. 4. Updates the corresponding relocation types from their original PC-relative types to the appropriate GOT types (`R_LARCH_GOT_PC_HI20` and `R_LARCH_GOT_PC_LO12`). Verification: The modification is confirmed by disassembling the patched code. The original sequence: pcalau12i $a1, 40 addi.d $a1, $a1, 56 // PC-relative calculation Was successfully transformed into the GOT-based sequence: pcalau12i $a1, 40 ld.d $a1, $a1, 56 // Load from GOT entry Signed-off-by: george <[email protected]>
1 parent 2e45b11 commit 1eb2f02

File tree

1 file changed

+73
-0
lines changed

1 file changed

+73
-0
lines changed

kpatch-build/create-diff-object.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,6 +3520,77 @@ static bool need_klp_reloc(struct kpatch_elf *kelf, struct lookup_table *table,
35203520
return false;
35213521
}
35223522

3523+
static bool is_la_pcrel(void *ptr)
3524+
{
3525+
unsigned int *insn = ptr;
3526+
int rd;
3527+
3528+
/* pcalau12i rd, si20: 0 0 0 1 1 0 1 si20 rd*/
3529+
if ((*insn & 0xfe000000) != 0x1a000000)
3530+
return false;
3531+
3532+
rd = *insn & 0x1f;
3533+
3534+
/* addi.d rd, rd, si12: 0 0 0 0 0 0 1 0 1 1 si12 rd rd */
3535+
if ((*(insn + 3) & 0xffc003ff) != (0x02c00000U | (rd << 5) | rd)) {
3536+
return false;
3537+
}
3538+
3539+
return true;
3540+
}
3541+
3542+
static void la_pcrel_to_la_got(void *ptr)
3543+
{
3544+
unsigned int *insn = ptr;
3545+
int rd;
3546+
3547+
rd = *insn & 0x1f;
3548+
3549+
/* ld.d rd, rd, si12: 0 0 1 0 1 0 0 0 1 1 si12 rd rd */
3550+
*(insn + 3) = 0x28c00000 | (rd << 5) | rd;
3551+
}
3552+
3553+
static void kpatch_modify_la_rela_sections(struct kpatch_elf *kelf)
3554+
{
3555+
struct rela *rela, *cur;
3556+
struct section *sec;
3557+
unsigned int offset;
3558+
void *ndata_buf;
3559+
3560+
list_for_each_entry(sec, &kelf->sections, list) {
3561+
if (!is_rela_section(sec) || !(sec->base->sh.sh_flags & SHF_EXECINSTR))
3562+
continue;
3563+
ndata_buf = NULL;
3564+
list_for_each_entry(rela, &sec->relas, list) {
3565+
if (rela->sym->sec)
3566+
continue;
3567+
3568+
if (!is_la_pcrel(sec->base->data->d_buf + rela->offset))
3569+
continue;
3570+
3571+
if (ndata_buf == NULL) {
3572+
ndata_buf = malloc(sec->base->data->d_size + 4);
3573+
ndata_buf = ndata_buf + 4 - ((unsigned long)ndata_buf % 4);
3574+
memcpy(ndata_buf, sec->base->data->d_buf, sec->base->data->d_size);
3575+
sec->base->data->d_buf = ndata_buf;
3576+
}
3577+
la_pcrel_to_la_got(sec->base->data->d_buf + rela->offset);
3578+
3579+
log_debug("change rela type = %d to %d\n", rela->type, R_LARCH_GOT_PC_HI20);
3580+
rela->type = R_LARCH_GOT_PC_HI20;
3581+
3582+
offset = rela->offset;
3583+
list_for_each_entry(cur, &(rela->list), list) {
3584+
if (cur->offset == offset + 12) {
3585+
log_debug("change rela type = %d to %d\n", cur->type, R_LARCH_GOT_PC_LO12);
3586+
cur->type = R_LARCH_GOT_PC_LO12;
3587+
break;
3588+
}
3589+
}
3590+
}
3591+
}
3592+
}
3593+
35233594
/*
35243595
* kpatch_create_intermediate_sections()
35253596
*
@@ -4374,6 +4445,8 @@ int main(int argc, char *argv[])
43744445
/* create strings, patches, and klp relocation sections */
43754446
kpatch_create_strings_elements(kelf_out);
43764447
kpatch_create_patches_sections(kelf_out, lookup, parent_name);
4448+
if (kelf_out->arch == LOONGARCH64)
4449+
kpatch_modify_la_rela_sections(kelf_out);
43774450
kpatch_create_intermediate_sections(kelf_out, lookup, parent_name, patch_name);
43784451
kpatch_create_kpatch_arch_section(kelf_out, parent_name);
43794452
kpatch_create_callbacks_objname_rela(kelf_out, parent_name);

0 commit comments

Comments
 (0)