906 lines · 25.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * *  Copyright (C) 2017 Zihao Yu */ #include <linux/elf.h>#include <linux/err.h>#include <linux/errno.h>#include <linux/hashtable.h>#include <linux/kernel.h>#include <linux/log2.h>#include <linux/moduleloader.h>#include <linux/sizes.h>#include <linux/pgtable.h>#include <asm/alternative.h>#include <asm/sections.h> struct used_bucket {	struct list_head head;	struct hlist_head *bucket;}; struct relocation_head {	struct hlist_node node;	struct list_head rel_entry;	void *location;}; struct relocation_entry {	struct list_head head;	Elf_Addr value;	unsigned int type;}; struct relocation_handlers {	int (*reloc_handler)(struct module *me, void *location, Elf_Addr v);	int (*accumulate_handler)(struct module *me, void *location,				  long buffer);}; /* * The auipc+jalr instruction pair can reach any PC-relative offset * in the range [-2^31 - 2^11, 2^31 - 2^11) */static bool riscv_insn_valid_32bit_offset(ptrdiff_t val){#ifdef CONFIG_32BIT	return true;#else	return (-(1L << 31) - (1L << 11)) <= val && val < ((1L << 31) - (1L << 11));#endif} static int riscv_insn_rmw(void *location, u32 keep, u32 set){	__le16 *parcel = location;	u32 insn = (u32)le16_to_cpu(parcel[0]) | (u32)le16_to_cpu(parcel[1]) << 16; 	insn &= keep;	insn |= set; 	parcel[0] = cpu_to_le16(insn);	parcel[1] = cpu_to_le16(insn >> 16);	return 0;} static int riscv_insn_rvc_rmw(void *location, u16 keep, u16 set){	__le16 *parcel = location;	u16 insn = le16_to_cpu(*parcel); 	insn &= keep;	insn |= set; 	*parcel = cpu_to_le16(insn);	return 0;} static int apply_r_riscv_32_rela(struct module *me, void *location, Elf_Addr v){	if (v != (u32)v) {		pr_err("%s: value %016llx out of range for 32-bit field\n",		       me->name, (long long)v);		return -EINVAL;	}	*(u32 *)location = v;	return 0;} static int apply_r_riscv_64_rela(struct module *me, void *location, Elf_Addr v){	*(u64 *)location = v;	return 0;} static int apply_r_riscv_branch_rela(struct module *me, void *location,				     Elf_Addr v){	ptrdiff_t offset = (void *)v - location;	u32 imm12 = (offset & 0x1000) << (31 - 12);	u32 imm11 = (offset & 0x800) >> (11 - 7);	u32 imm10_5 = (offset & 0x7e0) << (30 - 10);	u32 imm4_1 = (offset & 0x1e) << (11 - 4); 	return riscv_insn_rmw(location, 0x1fff07f, imm12 | imm11 | imm10_5 | imm4_1);} static int apply_r_riscv_jal_rela(struct module *me, void *location,				  Elf_Addr v){	ptrdiff_t offset = (void *)v - location;	u32 imm20 = (offset & 0x100000) << (31 - 20);	u32 imm19_12 = (offset & 0xff000);	u32 imm11 = (offset & 0x800) << (20 - 11);	u32 imm10_1 = (offset & 0x7fe) << (30 - 10); 	return riscv_insn_rmw(location, 0xfff, imm20 | imm19_12 | imm11 | imm10_1);} static int apply_r_riscv_rvc_branch_rela(struct module *me, void *location,					 Elf_Addr v){	ptrdiff_t offset = (void *)v - location;	u16 imm8 = (offset & 0x100) << (12 - 8);	u16 imm7_6 = (offset & 0xc0) >> (6 - 5);	u16 imm5 = (offset & 0x20) >> (5 - 2);	u16 imm4_3 = (offset & 0x18) << (12 - 5);	u16 imm2_1 = (offset & 0x6) << (12 - 10); 	return riscv_insn_rvc_rmw(location, 0xe383,			imm8 | imm7_6 | imm5 | imm4_3 | imm2_1);} static int apply_r_riscv_rvc_jump_rela(struct module *me, void *location,				       Elf_Addr v){	ptrdiff_t offset = (void *)v - location;	u16 imm11 = (offset & 0x800) << (12 - 11);	u16 imm10 = (offset & 0x400) >> (10 - 8);	u16 imm9_8 = (offset & 0x300) << (12 - 11);	u16 imm7 = (offset & 0x80) >> (7 - 6);	u16 imm6 = (offset & 0x40) << (12 - 11);	u16 imm5 = (offset & 0x20) >> (5 - 2);	u16 imm4 = (offset & 0x10) << (12 - 5);	u16 imm3_1 = (offset & 0xe) << (12 - 10); 	return riscv_insn_rvc_rmw(location, 0xe003,			imm11 | imm10 | imm9_8 | imm7 | imm6 | imm5 | imm4 | imm3_1);} static int apply_r_riscv_pcrel_hi20_rela(struct module *me, void *location,					 Elf_Addr v){	ptrdiff_t offset = (void *)v - location; 	if (!riscv_insn_valid_32bit_offset(offset)) {		pr_err(		  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",		  me->name, (long long)v, location);		return -EINVAL;	} 	return riscv_insn_rmw(location, 0xfff, (offset + 0x800) & 0xfffff000);} static int apply_r_riscv_pcrel_lo12_i_rela(struct module *me, void *location,					   Elf_Addr v){	/*	 * v is the lo12 value to fill. It is calculated before calling this	 * handler.	 */	return riscv_insn_rmw(location, 0xfffff, (v & 0xfff) << 20);} static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, void *location,					   Elf_Addr v){	/*	 * v is the lo12 value to fill. It is calculated before calling this	 * handler.	 */	u32 imm11_5 = (v & 0xfe0) << (31 - 11);	u32 imm4_0 = (v & 0x1f) << (11 - 4); 	return riscv_insn_rmw(location, 0x1fff07f, imm11_5 | imm4_0);} static int apply_r_riscv_hi20_rela(struct module *me, void *location,				   Elf_Addr v){	if (IS_ENABLED(CONFIG_CMODEL_MEDLOW)) {		pr_err(		  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",		  me->name, (long long)v, location);		return -EINVAL;	} 	return riscv_insn_rmw(location, 0xfff, ((s32)v + 0x800) & 0xfffff000);} static int apply_r_riscv_lo12_i_rela(struct module *me, void *location,				     Elf_Addr v){	/* Skip medlow checking because of filtering by HI20 already */	s32 hi20 = ((s32)v + 0x800) & 0xfffff000;	s32 lo12 = ((s32)v - hi20); 	return riscv_insn_rmw(location, 0xfffff, (lo12 & 0xfff) << 20);} static int apply_r_riscv_lo12_s_rela(struct module *me, void *location,				     Elf_Addr v){	/* Skip medlow checking because of filtering by HI20 already */	s32 hi20 = ((s32)v + 0x800) & 0xfffff000;	s32 lo12 = ((s32)v - hi20);	u32 imm11_5 = (lo12 & 0xfe0) << (31 - 11);	u32 imm4_0 = (lo12 & 0x1f) << (11 - 4); 	return riscv_insn_rmw(location, 0x1fff07f, imm11_5 | imm4_0);} static int apply_r_riscv_got_hi20_rela(struct module *me, void *location,				       Elf_Addr v){	ptrdiff_t offset = (void *)v - location; 	/* Always emit the got entry */	if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {		offset = (void *)module_emit_got_entry(me, v) - location;	} else {		pr_err(		  "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n",		  me->name, (long long)v, location);		return -EINVAL;	} 	return riscv_insn_rmw(location, 0xfff, (offset + 0x800) & 0xfffff000);} static int apply_r_riscv_call_plt_rela(struct module *me, void *location,				       Elf_Addr v){	ptrdiff_t offset = (void *)v - location;	u32 hi20, lo12; 	if (!riscv_insn_valid_32bit_offset(offset)) {		/* Only emit the plt entry if offset over 32-bit range */		if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {			offset = (void *)module_emit_plt_entry(me, v) - location;		} else {			pr_err(			  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",			  me->name, (long long)v, location);			return -EINVAL;		}	} 	hi20 = (offset + 0x800) & 0xfffff000;	lo12 = (offset - hi20) & 0xfff;	riscv_insn_rmw(location, 0xfff, hi20);	return riscv_insn_rmw(location + 4, 0xfffff, lo12 << 20);} static int apply_r_riscv_call_rela(struct module *me, void *location,				   Elf_Addr v){	ptrdiff_t offset = (void *)v - location;	u32 hi20, lo12; 	if (!riscv_insn_valid_32bit_offset(offset)) {		pr_err(		  "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",		  me->name, (long long)v, location);		return -EINVAL;	} 	hi20 = (offset + 0x800) & 0xfffff000;	lo12 = (offset - hi20) & 0xfff;	riscv_insn_rmw(location, 0xfff, hi20);	return riscv_insn_rmw(location + 4, 0xfffff, lo12 << 20);} static int apply_r_riscv_relax_rela(struct module *me, void *location,				    Elf_Addr v){	return 0;} static int apply_r_riscv_align_rela(struct module *me, void *location,				    Elf_Addr v){	pr_err(	  "%s: The unexpected relocation type 'R_RISCV_ALIGN' from PC = %p\n",	  me->name, location);	return -EINVAL;} static int apply_r_riscv_add8_rela(struct module *me, void *location, Elf_Addr v){	*(u8 *)location += (u8)v;	return 0;} static int apply_r_riscv_add16_rela(struct module *me, void *location,				    Elf_Addr v){	*(u16 *)location += (u16)v;	return 0;} static int apply_r_riscv_add32_rela(struct module *me, void *location,				    Elf_Addr v){	*(u32 *)location += (u32)v;	return 0;} static int apply_r_riscv_add64_rela(struct module *me, void *location,				    Elf_Addr v){	*(u64 *)location += (u64)v;	return 0;} static int apply_r_riscv_sub8_rela(struct module *me, void *location, Elf_Addr v){	*(u8 *)location -= (u8)v;	return 0;} static int apply_r_riscv_sub16_rela(struct module *me, void *location,				    Elf_Addr v){	*(u16 *)location -= (u16)v;	return 0;} static int apply_r_riscv_sub32_rela(struct module *me, void *location,				    Elf_Addr v){	*(u32 *)location -= (u32)v;	return 0;} static int apply_r_riscv_sub64_rela(struct module *me, void *location,				    Elf_Addr v){	*(u64 *)location -= (u64)v;	return 0;} static int dynamic_linking_not_supported(struct module *me, void *location,					 Elf_Addr v){	pr_err("%s: Dynamic linking not supported in kernel modules PC = %p\n",	       me->name, location);	return -EINVAL;} static int tls_not_supported(struct module *me, void *location, Elf_Addr v){	pr_err("%s: Thread local storage not supported in kernel modules PC = %p\n",	       me->name, location);	return -EINVAL;} static int apply_r_riscv_sub6_rela(struct module *me, void *location, Elf_Addr v){	u8 *byte = location;	u8 value = v; 	*byte = (*byte - (value & 0x3f)) & 0x3f;	return 0;} static int apply_r_riscv_set6_rela(struct module *me, void *location, Elf_Addr v){	u8 *byte = location;	u8 value = v; 	*byte = (*byte & 0xc0) | (value & 0x3f);	return 0;} static int apply_r_riscv_set8_rela(struct module *me, void *location, Elf_Addr v){	*(u8 *)location = (u8)v;	return 0;} static int apply_r_riscv_set16_rela(struct module *me, void *location,				    Elf_Addr v){	*(u16 *)location = (u16)v;	return 0;} static int apply_r_riscv_set32_rela(struct module *me, void *location,				    Elf_Addr v){	*(u32 *)location = (u32)v;	return 0;} static int apply_r_riscv_32_pcrel_rela(struct module *me, void *location,				       Elf_Addr v){	*(u32 *)location = v - (uintptr_t)location;	return 0;} static int apply_r_riscv_plt32_rela(struct module *me, void *location,				    Elf_Addr v){	ptrdiff_t offset = (void *)v - location; 	if (!riscv_insn_valid_32bit_offset(offset)) {		/* Only emit the plt entry if offset over 32-bit range */		if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {			offset = (void *)module_emit_plt_entry(me, v) - location;		} else {			pr_err("%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",			       me->name, (long long)v, location);			return -EINVAL;		}	} 	*(u32 *)location = (u32)offset;	return 0;} static int apply_r_riscv_set_uleb128(struct module *me, void *location, Elf_Addr v){	*(long *)location = v;	return 0;} static int apply_r_riscv_sub_uleb128(struct module *me, void *location, Elf_Addr v){	*(long *)location -= v;	return 0;} static int apply_6_bit_accumulation(struct module *me, void *location, long buffer){	u8 *byte = location;	u8 value = buffer; 	if (buffer > 0x3f) {		pr_err("%s: value %ld out of range for 6-bit relocation.\n",		       me->name, buffer);		return -EINVAL;	} 	*byte = (*byte & 0xc0) | (value & 0x3f);	return 0;} static int apply_8_bit_accumulation(struct module *me, void *location, long buffer){	if (buffer > U8_MAX) {		pr_err("%s: value %ld out of range for 8-bit relocation.\n",		       me->name, buffer);		return -EINVAL;	}	*(u8 *)location = (u8)buffer;	return 0;} static int apply_16_bit_accumulation(struct module *me, void *location, long buffer){	if (buffer > U16_MAX) {		pr_err("%s: value %ld out of range for 16-bit relocation.\n",		       me->name, buffer);		return -EINVAL;	}	*(u16 *)location = (u16)buffer;	return 0;} static int apply_32_bit_accumulation(struct module *me, void *location, long buffer){	if (buffer > U32_MAX) {		pr_err("%s: value %ld out of range for 32-bit relocation.\n",		       me->name, buffer);		return -EINVAL;	}	*(u32 *)location = (u32)buffer;	return 0;} static int apply_64_bit_accumulation(struct module *me, void *location, long buffer){	*(u64 *)location = (u64)buffer;	return 0;} 

Lines 1–500 of 906.