mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-03 18:20:25 +02:00 
			
		
		
		
	LoongArch: Add ELF and module support
Add ELF-related definition and module relocation code for basic LoongArch support. Cc: Jessica Yu <jeyu@kernel.org> Reviewed-by: WANG Xuerui <git@xen0n.name> Reviewed-by: Luis Chamberlain <mcgrof@kernel.org> Reviewed-by: Jiaxun Yang <jiaxun.yang@flygoat.com> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
This commit is contained in:
		
							parent
							
								
									b74baf4ad0
								
							
						
					
					
						commit
						fcdfe9d22b
					
				
					 12 changed files with 1043 additions and 0 deletions
				
			
		
							
								
								
									
										24
									
								
								arch/loongarch/include/asm/cpufeature.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								arch/loongarch/include/asm/cpufeature.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * CPU feature definitions for module loading, used by
 | 
				
			||||||
 | 
					 * module_cpu_feature_match(), see uapi/asm/hwcap.h for LoongArch CPU features.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __ASM_CPUFEATURE_H
 | 
				
			||||||
 | 
					#define __ASM_CPUFEATURE_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <uapi/asm/hwcap.h>
 | 
				
			||||||
 | 
					#include <asm/elf.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define cpu_feature(x)		ilog2(HWCAP_ ## x)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool cpu_have_feature(unsigned int num)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return elf_hwcap & (1UL << num);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* __ASM_CPUFEATURE_H */
 | 
				
			||||||
							
								
								
									
										301
									
								
								arch/loongarch/include/asm/elf.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								arch/loongarch/include/asm/elf.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,301 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifndef _ASM_ELF_H
 | 
				
			||||||
 | 
					#define _ASM_ELF_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/auxvec.h>
 | 
				
			||||||
 | 
					#include <linux/fs.h>
 | 
				
			||||||
 | 
					#include <uapi/linux/elf.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asm/current.h>
 | 
				
			||||||
 | 
					#include <asm/vdso.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The ABI of a file. */
 | 
				
			||||||
 | 
					#define EF_LOONGARCH_ABI_LP64_SOFT_FLOAT	0x1
 | 
				
			||||||
 | 
					#define EF_LOONGARCH_ABI_LP64_SINGLE_FLOAT	0x2
 | 
				
			||||||
 | 
					#define EF_LOONGARCH_ABI_LP64_DOUBLE_FLOAT	0x3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EF_LOONGARCH_ABI_ILP32_SOFT_FLOAT	0x5
 | 
				
			||||||
 | 
					#define EF_LOONGARCH_ABI_ILP32_SINGLE_FLOAT	0x6
 | 
				
			||||||
 | 
					#define EF_LOONGARCH_ABI_ILP32_DOUBLE_FLOAT	0x7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* LoongArch relocation types used by the dynamic linker */
 | 
				
			||||||
 | 
					#define R_LARCH_NONE				0
 | 
				
			||||||
 | 
					#define R_LARCH_32				1
 | 
				
			||||||
 | 
					#define R_LARCH_64				2
 | 
				
			||||||
 | 
					#define R_LARCH_RELATIVE			3
 | 
				
			||||||
 | 
					#define R_LARCH_COPY				4
 | 
				
			||||||
 | 
					#define R_LARCH_JUMP_SLOT			5
 | 
				
			||||||
 | 
					#define R_LARCH_TLS_DTPMOD32			6
 | 
				
			||||||
 | 
					#define R_LARCH_TLS_DTPMOD64			7
 | 
				
			||||||
 | 
					#define R_LARCH_TLS_DTPREL32			8
 | 
				
			||||||
 | 
					#define R_LARCH_TLS_DTPREL64			9
 | 
				
			||||||
 | 
					#define R_LARCH_TLS_TPREL32			10
 | 
				
			||||||
 | 
					#define R_LARCH_TLS_TPREL64			11
 | 
				
			||||||
 | 
					#define R_LARCH_IRELATIVE			12
 | 
				
			||||||
 | 
					#define R_LARCH_MARK_LA				20
 | 
				
			||||||
 | 
					#define R_LARCH_MARK_PCREL			21
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_PUSH_PCREL			22
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_PUSH_ABSOLUTE		23
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_PUSH_DUP			24
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_PUSH_GPREL			25
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_PUSH_TLS_TPREL		26
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_PUSH_TLS_GOT		27
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_PUSH_TLS_GD			28
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_PUSH_PLT_PCREL		29
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_ASSERT			30
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_NOT				31
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_SUB				32
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_SL				33
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_SR				34
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_ADD				35
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_AND				36
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_IF_ELSE			37
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_POP_32_S_10_5		38
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_POP_32_U_10_12		39
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_POP_32_S_10_12		40
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_POP_32_S_10_16		41
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_POP_32_S_10_16_S2		42
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_POP_32_S_5_20		43
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_POP_32_S_0_5_10_16_S2	44
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_POP_32_S_0_10_10_16_S2	45
 | 
				
			||||||
 | 
					#define R_LARCH_SOP_POP_32_U			46
 | 
				
			||||||
 | 
					#define R_LARCH_ADD8				47
 | 
				
			||||||
 | 
					#define R_LARCH_ADD16				48
 | 
				
			||||||
 | 
					#define R_LARCH_ADD24				49
 | 
				
			||||||
 | 
					#define R_LARCH_ADD32				50
 | 
				
			||||||
 | 
					#define R_LARCH_ADD64				51
 | 
				
			||||||
 | 
					#define R_LARCH_SUB8				52
 | 
				
			||||||
 | 
					#define R_LARCH_SUB16				53
 | 
				
			||||||
 | 
					#define R_LARCH_SUB24				54
 | 
				
			||||||
 | 
					#define R_LARCH_SUB32				55
 | 
				
			||||||
 | 
					#define R_LARCH_SUB64				56
 | 
				
			||||||
 | 
					#define R_LARCH_GNU_VTINHERIT			57
 | 
				
			||||||
 | 
					#define R_LARCH_GNU_VTENTRY			58
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef ELF_ARCH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ELF register definitions */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * General purpose have the following registers:
 | 
				
			||||||
 | 
					 *	Register	Number
 | 
				
			||||||
 | 
					 *	GPRs		32
 | 
				
			||||||
 | 
					 *	ORIG_A0		1
 | 
				
			||||||
 | 
					 *	ERA		1
 | 
				
			||||||
 | 
					 *	BADVADDR	1
 | 
				
			||||||
 | 
					 *	CRMD		1
 | 
				
			||||||
 | 
					 *	PRMD		1
 | 
				
			||||||
 | 
					 *	EUEN		1
 | 
				
			||||||
 | 
					 *	ECFG		1
 | 
				
			||||||
 | 
					 *	ESTAT		1
 | 
				
			||||||
 | 
					 *	Reserved	5
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define ELF_NGREG	45
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Floating point have the following registers:
 | 
				
			||||||
 | 
					 *	Register	Number
 | 
				
			||||||
 | 
					 *	FPR		32
 | 
				
			||||||
 | 
					 *	FCC		1
 | 
				
			||||||
 | 
					 *	FCSR		1
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define ELF_NFPREG	34
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef unsigned long elf_greg_t;
 | 
				
			||||||
 | 
					typedef elf_greg_t elf_gregset_t[ELF_NGREG];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef double elf_fpreg_t;
 | 
				
			||||||
 | 
					typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void loongarch_dump_regs64(u64 *uregs, const struct pt_regs *regs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_32BIT
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This is used to ensure we don't load something for the wrong architecture.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define elf_check_arch elf32_check_arch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * These are used to set parameters in the core dumps.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define ELF_CLASS	ELFCLASS32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ELF_CORE_COPY_REGS(dest, regs) \
 | 
				
			||||||
 | 
						loongarch_dump_regs32((u32 *)&(dest), (regs));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* CONFIG_32BIT */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_64BIT
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This is used to ensure we don't load something for the wrong architecture.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define elf_check_arch elf64_check_arch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * These are used to set parameters in the core dumps.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define ELF_CLASS	ELFCLASS64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ELF_CORE_COPY_REGS(dest, regs) \
 | 
				
			||||||
 | 
						loongarch_dump_regs64((u64 *)&(dest), (regs));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* CONFIG_64BIT */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * These are used to set parameters in the core dumps.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define ELF_DATA	ELFDATA2LSB
 | 
				
			||||||
 | 
					#define ELF_ARCH	EM_LOONGARCH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* !defined(ELF_ARCH) */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define loongarch_elf_check_machine(x) ((x)->e_machine == EM_LOONGARCH)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define vmcore_elf32_check_arch loongarch_elf_check_machine
 | 
				
			||||||
 | 
					#define vmcore_elf64_check_arch loongarch_elf_check_machine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Return non-zero if HDR identifies an 32bit ELF binary.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define elf32_check_arch(hdr)						\
 | 
				
			||||||
 | 
					({									\
 | 
				
			||||||
 | 
						int __res = 1;							\
 | 
				
			||||||
 | 
						struct elfhdr *__h = (hdr);					\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
						if (!loongarch_elf_check_machine(__h))				\
 | 
				
			||||||
 | 
							__res = 0;						\
 | 
				
			||||||
 | 
						if (__h->e_ident[EI_CLASS] != ELFCLASS32)			\
 | 
				
			||||||
 | 
							__res = 0;						\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
						__res;								\
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Return non-zero if HDR identifies an 64bit ELF binary.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define elf64_check_arch(hdr)						\
 | 
				
			||||||
 | 
					({									\
 | 
				
			||||||
 | 
						int __res = 1;							\
 | 
				
			||||||
 | 
						struct elfhdr *__h = (hdr);					\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
						if (!loongarch_elf_check_machine(__h))				\
 | 
				
			||||||
 | 
							__res = 0;						\
 | 
				
			||||||
 | 
						if (__h->e_ident[EI_CLASS] != ELFCLASS64)			\
 | 
				
			||||||
 | 
							__res = 0;						\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
						__res;								\
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_32BIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SET_PERSONALITY2(ex, state)					\
 | 
				
			||||||
 | 
					do {									\
 | 
				
			||||||
 | 
						current->thread.vdso = &vdso_info;				\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
						loongarch_set_personality_fcsr(state);				\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
						if (personality(current->personality) != PER_LINUX)		\
 | 
				
			||||||
 | 
							set_personality(PER_LINUX);				\
 | 
				
			||||||
 | 
					} while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* CONFIG_32BIT */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_64BIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SET_PERSONALITY2(ex, state)					\
 | 
				
			||||||
 | 
					do {									\
 | 
				
			||||||
 | 
						unsigned int p;							\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
						clear_thread_flag(TIF_32BIT_REGS);				\
 | 
				
			||||||
 | 
						clear_thread_flag(TIF_32BIT_ADDR);				\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
						current->thread.vdso = &vdso_info;				\
 | 
				
			||||||
 | 
						loongarch_set_personality_fcsr(state);				\
 | 
				
			||||||
 | 
														\
 | 
				
			||||||
 | 
						p = personality(current->personality);				\
 | 
				
			||||||
 | 
						if (p != PER_LINUX32 && p != PER_LINUX)				\
 | 
				
			||||||
 | 
							set_personality(PER_LINUX);				\
 | 
				
			||||||
 | 
					} while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* CONFIG_64BIT */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CORE_DUMP_USE_REGSET
 | 
				
			||||||
 | 
					#define ELF_EXEC_PAGESIZE	PAGE_SIZE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This yields a mask that user programs can use to figure out what
 | 
				
			||||||
 | 
					 * instruction set this cpu supports. This could be done in userspace,
 | 
				
			||||||
 | 
					 * but it's not easy, and we've already done it here.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ELF_HWCAP	(elf_hwcap)
 | 
				
			||||||
 | 
					extern unsigned int elf_hwcap;
 | 
				
			||||||
 | 
					#include <asm/hwcap.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This yields a string that ld.so will use to load implementation
 | 
				
			||||||
 | 
					 * specific libraries for optimization.	 This is more specific in
 | 
				
			||||||
 | 
					 * intent than poking at uname or /proc/cpuinfo.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ELF_PLATFORM  __elf_platform
 | 
				
			||||||
 | 
					extern const char *__elf_platform;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ELF_PLAT_INIT(_r, load_addr)	do { \
 | 
				
			||||||
 | 
						_r->regs[1] = _r->regs[2] = _r->regs[3] = _r->regs[4] = 0;	\
 | 
				
			||||||
 | 
						_r->regs[5] = _r->regs[6] = _r->regs[7] = _r->regs[8] = 0;	\
 | 
				
			||||||
 | 
						_r->regs[9] = _r->regs[10] = _r->regs[11] = _r->regs[12] = 0;	\
 | 
				
			||||||
 | 
						_r->regs[13] = _r->regs[14] = _r->regs[15] = _r->regs[16] = 0;	\
 | 
				
			||||||
 | 
						_r->regs[17] = _r->regs[18] = _r->regs[19] = _r->regs[20] = 0;	\
 | 
				
			||||||
 | 
						_r->regs[21] = _r->regs[22] = _r->regs[23] = _r->regs[24] = 0;	\
 | 
				
			||||||
 | 
						_r->regs[25] = _r->regs[26] = _r->regs[27] = _r->regs[28] = 0;	\
 | 
				
			||||||
 | 
						_r->regs[29] = _r->regs[30] = _r->regs[31] = 0;			\
 | 
				
			||||||
 | 
					} while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * This is the location that an ET_DYN program is loaded if exec'ed. Typical
 | 
				
			||||||
 | 
					 * use of this is to invoke "./ld.so someprog" to test out a new version of
 | 
				
			||||||
 | 
					 * the loader. We need to make sure that it is out of the way of the program
 | 
				
			||||||
 | 
					 * that it will "exec", and that there is sufficient room for the brk.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ELF_ET_DYN_BASE		(TASK_SIZE / 3 * 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
 | 
				
			||||||
 | 
					#define ARCH_DLINFO							\
 | 
				
			||||||
 | 
					do {									\
 | 
				
			||||||
 | 
						NEW_AUX_ENT(AT_SYSINFO_EHDR,					\
 | 
				
			||||||
 | 
							    (unsigned long)current->mm->context.vdso);		\
 | 
				
			||||||
 | 
					} while (0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
 | 
				
			||||||
 | 
					struct linux_binprm;
 | 
				
			||||||
 | 
					extern int arch_setup_additional_pages(struct linux_binprm *bprm,
 | 
				
			||||||
 | 
									       int uses_interp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct arch_elf_state {
 | 
				
			||||||
 | 
						int fp_abi;
 | 
				
			||||||
 | 
						int interp_fp_abi;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOONGARCH_ABI_FP_ANY	(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define INIT_ARCH_ELF_STATE {			\
 | 
				
			||||||
 | 
						.fp_abi = LOONGARCH_ABI_FP_ANY,		\
 | 
				
			||||||
 | 
						.interp_fp_abi = LOONGARCH_ABI_FP_ANY,	\
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define elf_read_implies_exec(ex, exec_stk) (exec_stk == EXSTACK_DEFAULT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int arch_elf_pt_proc(void *ehdr, void *phdr, struct file *elf,
 | 
				
			||||||
 | 
								    bool is_interp, struct arch_elf_state *state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern int arch_check_elf(void *ehdr, bool has_interpreter, void *interp_ehdr,
 | 
				
			||||||
 | 
								  struct arch_elf_state *state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern void loongarch_set_personality_fcsr(struct arch_elf_state *state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _ASM_ELF_H */
 | 
				
			||||||
							
								
								
									
										10
									
								
								arch/loongarch/include/asm/exec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								arch/loongarch/include/asm/exec.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifndef _ASM_EXEC_H
 | 
				
			||||||
 | 
					#define _ASM_EXEC_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern unsigned long arch_align_stack(unsigned long sp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _ASM_EXEC_H */
 | 
				
			||||||
							
								
								
									
										80
									
								
								arch/loongarch/include/asm/module.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								arch/loongarch/include/asm/module.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,80 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifndef _ASM_MODULE_H
 | 
				
			||||||
 | 
					#define _ASM_MODULE_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asm/inst.h>
 | 
				
			||||||
 | 
					#include <asm-generic/module.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RELA_STACK_DEPTH 16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct mod_section {
 | 
				
			||||||
 | 
						Elf_Shdr *shdr;
 | 
				
			||||||
 | 
						int num_entries;
 | 
				
			||||||
 | 
						int max_entries;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct mod_arch_specific {
 | 
				
			||||||
 | 
						struct mod_section plt;
 | 
				
			||||||
 | 
						struct mod_section plt_idx;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct plt_entry {
 | 
				
			||||||
 | 
						u32 inst_lu12iw;
 | 
				
			||||||
 | 
						u32 inst_lu32id;
 | 
				
			||||||
 | 
						u32 inst_lu52id;
 | 
				
			||||||
 | 
						u32 inst_jirl;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct plt_idx_entry {
 | 
				
			||||||
 | 
						unsigned long symbol_addr;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Elf_Addr module_emit_plt_entry(struct module *mod, unsigned long val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct plt_entry emit_plt_entry(unsigned long val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						u32 lu12iw, lu32id, lu52id, jirl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lu12iw = (lu12iw_op << 25 | (((val >> 12) & 0xfffff) << 5) | LOONGARCH_GPR_T1);
 | 
				
			||||||
 | 
						lu32id = larch_insn_gen_lu32id(LOONGARCH_GPR_T1, ADDR_IMM(val, LU32ID));
 | 
				
			||||||
 | 
						lu52id = larch_insn_gen_lu52id(LOONGARCH_GPR_T1, LOONGARCH_GPR_T1, ADDR_IMM(val, LU52ID));
 | 
				
			||||||
 | 
						jirl = larch_insn_gen_jirl(0, LOONGARCH_GPR_T1, 0, (val & 0xfff));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (struct plt_entry) { lu12iw, lu32id, lu52id, jirl };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct plt_idx_entry emit_plt_idx_entry(unsigned long val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (struct plt_idx_entry) { val };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int get_plt_idx(unsigned long val, const struct mod_section *sec)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						struct plt_idx_entry *plt_idx = (struct plt_idx_entry *)sec->shdr->sh_addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < sec->num_entries; i++) {
 | 
				
			||||||
 | 
							if (plt_idx[i].symbol_addr == val)
 | 
				
			||||||
 | 
								return i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct plt_entry *get_plt_entry(unsigned long val,
 | 
				
			||||||
 | 
									      const struct mod_section *sec_plt,
 | 
				
			||||||
 | 
									      const struct mod_section *sec_plt_idx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int plt_idx = get_plt_idx(val, sec_plt_idx);
 | 
				
			||||||
 | 
						struct plt_entry *plt = (struct plt_entry *)sec_plt->shdr->sh_addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (plt_idx < 0)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return plt + plt_idx;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _ASM_MODULE_H */
 | 
				
			||||||
							
								
								
									
										7
									
								
								arch/loongarch/include/asm/module.lds.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								arch/loongarch/include/asm/module.lds.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					/* Copyright (C) 2020-2022 Loongson Technology Corporation Limited */
 | 
				
			||||||
 | 
					SECTIONS {
 | 
				
			||||||
 | 
						. = ALIGN(4);
 | 
				
			||||||
 | 
						.plt : { BYTE(0) }
 | 
				
			||||||
 | 
						.plt.idx : { BYTE(0) }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								arch/loongarch/include/asm/vermagic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								arch/loongarch/include/asm/vermagic.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#ifndef _ASM_VERMAGIC_H
 | 
				
			||||||
 | 
					#define _ASM_VERMAGIC_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MODULE_PROC_FAMILY "LOONGARCH "
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_32BIT
 | 
				
			||||||
 | 
					#define MODULE_KERNEL_TYPE "32BIT "
 | 
				
			||||||
 | 
					#elif defined CONFIG_64BIT
 | 
				
			||||||
 | 
					#define MODULE_KERNEL_TYPE "64BIT "
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MODULE_ARCH_VERMAGIC \
 | 
				
			||||||
 | 
						MODULE_PROC_FAMILY MODULE_KERNEL_TYPE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _ASM_VERMAGIC_H */
 | 
				
			||||||
							
								
								
									
										17
									
								
								arch/loongarch/include/uapi/asm/auxvec.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								arch/loongarch/include/uapi/asm/auxvec.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Author: Hanlu Li <lihanlu@loongson.cn>
 | 
				
			||||||
 | 
					 *         Huacai Chen <chenhuacai@loongson.cn>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef __ASM_AUXVEC_H
 | 
				
			||||||
 | 
					#define __ASM_AUXVEC_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Location of VDSO image. */
 | 
				
			||||||
 | 
					#define AT_SYSINFO_EHDR		33
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AT_VECTOR_SIZE_ARCH 1 /* entries in ARCH_DLINFO */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* __ASM_AUXVEC_H */
 | 
				
			||||||
							
								
								
									
										20
									
								
								arch/loongarch/include/uapi/asm/hwcap.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								arch/loongarch/include/uapi/asm/hwcap.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
 | 
				
			||||||
 | 
					#ifndef _UAPI_ASM_HWCAP_H
 | 
				
			||||||
 | 
					#define _UAPI_ASM_HWCAP_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* HWCAP flags */
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_CPUCFG		(1 << 0)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_LAM		(1 << 1)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_UAL		(1 << 2)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_FPU		(1 << 3)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_LSX		(1 << 4)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_LASX		(1 << 5)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_CRC32		(1 << 6)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_COMPLEX		(1 << 7)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_CRYPTO		(1 << 8)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_LVZ		(1 << 9)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_LBT_X86		(1 << 10)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_LBT_ARM		(1 << 11)
 | 
				
			||||||
 | 
					#define HWCAP_LOONGARCH_LBT_MIPS	(1 << 12)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _UAPI_ASM_HWCAP_H */
 | 
				
			||||||
							
								
								
									
										30
									
								
								arch/loongarch/kernel/elf.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								arch/loongarch/kernel/elf.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,30 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Author: Huacai Chen <chenhuacai@loongson.cn>
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/binfmts.h>
 | 
				
			||||||
 | 
					#include <linux/elf.h>
 | 
				
			||||||
 | 
					#include <linux/export.h>
 | 
				
			||||||
 | 
					#include <linux/sched.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <asm/cpu-features.h>
 | 
				
			||||||
 | 
					#include <asm/cpu-info.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
 | 
				
			||||||
 | 
							     bool is_interp, struct arch_elf_state *state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int arch_check_elf(void *_ehdr, bool has_interpreter, void *_interp_ehdr,
 | 
				
			||||||
 | 
							   struct arch_elf_state *state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void loongarch_set_personality_fcsr(struct arch_elf_state *state)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						current->thread.fpu.fcsr = boot_cpu_data.fpu_csr0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										40
									
								
								arch/loongarch/kernel/inst.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								arch/loongarch/kernel/inst.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <asm/inst.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						union loongarch_instruction insn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						insn.reg1i20_format.opcode = lu32id_op;
 | 
				
			||||||
 | 
						insn.reg1i20_format.rd = rd;
 | 
				
			||||||
 | 
						insn.reg1i20_format.immediate = imm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return insn.word;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						union loongarch_instruction insn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						insn.reg2i12_format.opcode = lu52id_op;
 | 
				
			||||||
 | 
						insn.reg2i12_format.rd = rd;
 | 
				
			||||||
 | 
						insn.reg2i12_format.rj = rj;
 | 
				
			||||||
 | 
						insn.reg2i12_format.immediate = imm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return insn.word;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						union loongarch_instruction insn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						insn.reg2i16_format.opcode = jirl_op;
 | 
				
			||||||
 | 
						insn.reg2i16_format.rd = rd;
 | 
				
			||||||
 | 
						insn.reg2i16_format.rj = rj;
 | 
				
			||||||
 | 
						insn.reg2i16_format.immediate = (dest - pc) >> 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return insn.word;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										121
									
								
								arch/loongarch/kernel/module-sections.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								arch/loongarch/kernel/module-sections.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,121 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/elf.h>
 | 
				
			||||||
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Elf_Addr module_emit_plt_entry(struct module *mod, unsigned long val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int nr;
 | 
				
			||||||
 | 
						struct mod_section *plt_sec = &mod->arch.plt;
 | 
				
			||||||
 | 
						struct mod_section *plt_idx_sec = &mod->arch.plt_idx;
 | 
				
			||||||
 | 
						struct plt_entry *plt = get_plt_entry(val, plt_sec, plt_idx_sec);
 | 
				
			||||||
 | 
						struct plt_idx_entry *plt_idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (plt)
 | 
				
			||||||
 | 
							return (Elf_Addr)plt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						nr = plt_sec->num_entries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* There is no duplicate entry, create a new one */
 | 
				
			||||||
 | 
						plt = (struct plt_entry *)plt_sec->shdr->sh_addr;
 | 
				
			||||||
 | 
						plt[nr] = emit_plt_entry(val);
 | 
				
			||||||
 | 
						plt_idx = (struct plt_idx_entry *)plt_idx_sec->shdr->sh_addr;
 | 
				
			||||||
 | 
						plt_idx[nr] = emit_plt_idx_entry(val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						plt_sec->num_entries++;
 | 
				
			||||||
 | 
						plt_idx_sec->num_entries++;
 | 
				
			||||||
 | 
						BUG_ON(plt_sec->num_entries > plt_sec->max_entries);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return (Elf_Addr)&plt[nr];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int is_rela_equal(const Elf_Rela *x, const Elf_Rela *y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return x->r_info == y->r_info && x->r_addend == y->r_addend;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool duplicate_rela(const Elf_Rela *rela, int idx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < idx; i++) {
 | 
				
			||||||
 | 
							if (is_rela_equal(&rela[i], &rela[idx]))
 | 
				
			||||||
 | 
								return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int i, type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < num; i++) {
 | 
				
			||||||
 | 
							type = ELF_R_TYPE(relas[i].r_info);
 | 
				
			||||||
 | 
							if (type == R_LARCH_SOP_PUSH_PLT_PCREL) {
 | 
				
			||||||
 | 
								if (!duplicate_rela(relas, i))
 | 
				
			||||||
 | 
									(*plts)++;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
 | 
				
			||||||
 | 
								      char *secstrings, struct module *mod)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int i, num_plts = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Find the empty .plt sections.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						for (i = 0; i < ehdr->e_shnum; i++) {
 | 
				
			||||||
 | 
							if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt"))
 | 
				
			||||||
 | 
								mod->arch.plt.shdr = sechdrs + i;
 | 
				
			||||||
 | 
							else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx"))
 | 
				
			||||||
 | 
								mod->arch.plt_idx.shdr = sechdrs + i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!mod->arch.plt.shdr) {
 | 
				
			||||||
 | 
							pr_err("%s: module PLT section(s) missing\n", mod->name);
 | 
				
			||||||
 | 
							return -ENOEXEC;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (!mod->arch.plt_idx.shdr) {
 | 
				
			||||||
 | 
							pr_err("%s: module PLT.IDX section(s) missing\n", mod->name);
 | 
				
			||||||
 | 
							return -ENOEXEC;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Calculate the maxinum number of entries */
 | 
				
			||||||
 | 
						for (i = 0; i < ehdr->e_shnum; i++) {
 | 
				
			||||||
 | 
							int num_rela = sechdrs[i].sh_size / sizeof(Elf_Rela);
 | 
				
			||||||
 | 
							Elf_Rela *relas = (void *)ehdr + sechdrs[i].sh_offset;
 | 
				
			||||||
 | 
							Elf_Shdr *dst_sec = sechdrs + sechdrs[i].sh_info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (sechdrs[i].sh_type != SHT_RELA)
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* ignore relocations that operate on non-exec sections */
 | 
				
			||||||
 | 
							if (!(dst_sec->sh_flags & SHF_EXECINSTR))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							count_max_entries(relas, num_rela, &num_plts);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mod->arch.plt.shdr->sh_type = SHT_NOBITS;
 | 
				
			||||||
 | 
						mod->arch.plt.shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
 | 
				
			||||||
 | 
						mod->arch.plt.shdr->sh_addralign = L1_CACHE_BYTES;
 | 
				
			||||||
 | 
						mod->arch.plt.shdr->sh_size = (num_plts + 1) * sizeof(struct plt_entry);
 | 
				
			||||||
 | 
						mod->arch.plt.num_entries = 0;
 | 
				
			||||||
 | 
						mod->arch.plt.max_entries = num_plts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mod->arch.plt_idx.shdr->sh_type = SHT_NOBITS;
 | 
				
			||||||
 | 
						mod->arch.plt_idx.shdr->sh_flags = SHF_ALLOC;
 | 
				
			||||||
 | 
						mod->arch.plt_idx.shdr->sh_addralign = L1_CACHE_BYTES;
 | 
				
			||||||
 | 
						mod->arch.plt_idx.shdr->sh_size = (num_plts + 1) * sizeof(struct plt_idx_entry);
 | 
				
			||||||
 | 
						mod->arch.plt_idx.num_entries = 0;
 | 
				
			||||||
 | 
						mod->arch.plt_idx.max_entries = num_plts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										374
									
								
								arch/loongarch/kernel/module.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								arch/loongarch/kernel/module.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,374 @@
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: GPL-2.0+
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Author: Hanlu Li <lihanlu@loongson.cn>
 | 
				
			||||||
 | 
					 *         Huacai Chen <chenhuacai@loongson.cn>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define pr_fmt(fmt) "kmod: " fmt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/moduleloader.h>
 | 
				
			||||||
 | 
					#include <linux/elf.h>
 | 
				
			||||||
 | 
					#include <linux/mm.h>
 | 
				
			||||||
 | 
					#include <linux/vmalloc.h>
 | 
				
			||||||
 | 
					#include <linux/slab.h>
 | 
				
			||||||
 | 
					#include <linux/fs.h>
 | 
				
			||||||
 | 
					#include <linux/string.h>
 | 
				
			||||||
 | 
					#include <linux/kernel.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool signed_imm_check(long val, unsigned int bit)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return -(1L << (bit - 1)) <= val && val < (1L << (bit - 1));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline bool unsigned_imm_check(unsigned long val, unsigned int bit)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return val < (1UL << bit);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (*rela_stack_top >= RELA_STACK_DEPTH)
 | 
				
			||||||
 | 
							return -ENOEXEC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rela_stack[(*rela_stack_top)++] = stack_value;
 | 
				
			||||||
 | 
						pr_debug("%s stack_value = 0x%llx\n", __func__, stack_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int rela_stack_pop(s64 *stack_value, s64 *rela_stack, size_t *rela_stack_top)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (*rela_stack_top == 0)
 | 
				
			||||||
 | 
							return -ENOEXEC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*stack_value = rela_stack[--(*rela_stack_top)];
 | 
				
			||||||
 | 
						pr_debug("%s stack_value = 0x%llx\n", __func__, *stack_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_none(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_error(struct module *me, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						pr_err("%s: Unsupport relocation type %u, please add its support.\n", me->name, type);
 | 
				
			||||||
 | 
						return -EINVAL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_32(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						*location = v;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_64(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						*(Elf_Addr *)location = v;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_sop_push_pcrel(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return rela_stack_push(v - (u64)location, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_sop_push_absolute(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return rela_stack_push(v, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_sop_push_dup(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
						s64 opr1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						err = rela_stack_push(opr1, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						err = rela_stack_push(opr1, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_sop_push_plt_pcrel(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ptrdiff_t offset = (void *)v - (void *)location;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (offset >= SZ_128M)
 | 
				
			||||||
 | 
							v = module_emit_plt_entry(mod, v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (offset < -SZ_128M)
 | 
				
			||||||
 | 
							v = module_emit_plt_entry(mod, v);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return apply_r_larch_sop_push_pcrel(mod, location, v, rela_stack, rela_stack_top, type);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_sop(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
						s64 opr1, opr2, opr3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (type == R_LARCH_SOP_IF_ELSE) {
 | 
				
			||||||
 | 
							err = rela_stack_pop(&opr3, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = rela_stack_pop(&opr2, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
						err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case R_LARCH_SOP_AND:
 | 
				
			||||||
 | 
							err = rela_stack_push(opr1 & opr2, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_ADD:
 | 
				
			||||||
 | 
							err = rela_stack_push(opr1 + opr2, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_SUB:
 | 
				
			||||||
 | 
							err = rela_stack_push(opr1 - opr2, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_SL:
 | 
				
			||||||
 | 
							err = rela_stack_push(opr1 << opr2, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_SR:
 | 
				
			||||||
 | 
							err = rela_stack_push(opr1 >> opr2, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_IF_ELSE:
 | 
				
			||||||
 | 
							err = rela_stack_push(opr1 ? opr2 : opr3, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return err;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_sop_imm_field(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int err = 0;
 | 
				
			||||||
 | 
						s64 opr1;
 | 
				
			||||||
 | 
						union loongarch_instruction *insn = (union loongarch_instruction *)location;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case R_LARCH_SOP_POP_32_U_10_12:
 | 
				
			||||||
 | 
							if (!unsigned_imm_check(opr1, 12))
 | 
				
			||||||
 | 
								goto overflow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* (*(uint32_t *) PC) [21 ... 10] = opr [11 ... 0] */
 | 
				
			||||||
 | 
							insn->reg2i12_format.immediate = opr1 & 0xfff;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_POP_32_S_10_12:
 | 
				
			||||||
 | 
							if (!signed_imm_check(opr1, 12))
 | 
				
			||||||
 | 
								goto overflow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							insn->reg2i12_format.immediate = opr1 & 0xfff;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_POP_32_S_10_16:
 | 
				
			||||||
 | 
							if (!signed_imm_check(opr1, 16))
 | 
				
			||||||
 | 
								goto overflow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							insn->reg2i16_format.immediate = opr1 & 0xffff;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_POP_32_S_10_16_S2:
 | 
				
			||||||
 | 
							if (opr1 % 4)
 | 
				
			||||||
 | 
								goto unaligned;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!signed_imm_check(opr1, 18))
 | 
				
			||||||
 | 
								goto overflow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							insn->reg2i16_format.immediate = (opr1 >> 2) & 0xffff;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_POP_32_S_5_20:
 | 
				
			||||||
 | 
							if (!signed_imm_check(opr1, 20))
 | 
				
			||||||
 | 
								goto overflow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							insn->reg1i20_format.immediate = (opr1) & 0xfffff;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_POP_32_S_0_5_10_16_S2:
 | 
				
			||||||
 | 
							if (opr1 % 4)
 | 
				
			||||||
 | 
								goto unaligned;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!signed_imm_check(opr1, 23))
 | 
				
			||||||
 | 
								goto overflow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							opr1 >>= 2;
 | 
				
			||||||
 | 
							insn->reg1i21_format.immediate_l = opr1 & 0xffff;
 | 
				
			||||||
 | 
							insn->reg1i21_format.immediate_h = (opr1 >> 16) & 0x1f;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_POP_32_S_0_10_10_16_S2:
 | 
				
			||||||
 | 
							if (opr1 % 4)
 | 
				
			||||||
 | 
								goto unaligned;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!signed_imm_check(opr1, 28))
 | 
				
			||||||
 | 
								goto overflow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							opr1 >>= 2;
 | 
				
			||||||
 | 
							insn->reg0i26_format.immediate_l = opr1 & 0xffff;
 | 
				
			||||||
 | 
							insn->reg0i26_format.immediate_h = (opr1 >> 16) & 0x3ff;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_SOP_POP_32_U:
 | 
				
			||||||
 | 
							if (!unsigned_imm_check(opr1, 32))
 | 
				
			||||||
 | 
								goto overflow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* (*(uint32_t *) PC) = opr */
 | 
				
			||||||
 | 
							*location = (u32)opr1;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					overflow:
 | 
				
			||||||
 | 
						pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s (%u) relocation\n",
 | 
				
			||||||
 | 
							mod->name, opr1, __func__, type);
 | 
				
			||||||
 | 
						return -ENOEXEC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unaligned:
 | 
				
			||||||
 | 
						pr_err("module %s: opr1 = 0x%llx unaligned! dangerous %s (%u) relocation\n",
 | 
				
			||||||
 | 
							mod->name, opr1, __func__, type);
 | 
				
			||||||
 | 
						return -ENOEXEC;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int apply_r_larch_add_sub(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case R_LARCH_ADD32:
 | 
				
			||||||
 | 
							*(s32 *)location += v;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_ADD64:
 | 
				
			||||||
 | 
							*(s64 *)location += v;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_SUB32:
 | 
				
			||||||
 | 
							*(s32 *)location -= v;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						case R_LARCH_SUB64:
 | 
				
			||||||
 | 
							*(s64 *)location -= v;
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							pr_err("%s: Unsupport relocation type %u\n", mod->name, type);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * reloc_handlers_rela() - Apply a particular relocation to a module
 | 
				
			||||||
 | 
					 * @mod: the module to apply the reloc to
 | 
				
			||||||
 | 
					 * @location: the address at which the reloc is to be applied
 | 
				
			||||||
 | 
					 * @v: the value of the reloc, with addend for RELA-style
 | 
				
			||||||
 | 
					 * @rela_stack: the stack used for store relocation info, LOCAL to THIS module
 | 
				
			||||||
 | 
					 * @rela_stac_top: where the stack operation(pop/push) applies to
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Return: 0 upon success, else -ERRNO
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef int (*reloc_rela_handler)(struct module *mod, u32 *location, Elf_Addr v,
 | 
				
			||||||
 | 
								s64 *rela_stack, size_t *rela_stack_top, unsigned int type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* The handlers for known reloc types */
 | 
				
			||||||
 | 
					static reloc_rela_handler reloc_rela_handlers[] = {
 | 
				
			||||||
 | 
						[R_LARCH_NONE ... R_LARCH_SUB64]		     = apply_r_larch_error,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						[R_LARCH_NONE]					     = apply_r_larch_none,
 | 
				
			||||||
 | 
						[R_LARCH_32]					     = apply_r_larch_32,
 | 
				
			||||||
 | 
						[R_LARCH_64]					     = apply_r_larch_64,
 | 
				
			||||||
 | 
						[R_LARCH_MARK_LA]				     = apply_r_larch_none,
 | 
				
			||||||
 | 
						[R_LARCH_MARK_PCREL]				     = apply_r_larch_none,
 | 
				
			||||||
 | 
						[R_LARCH_SOP_PUSH_PCREL]			     = apply_r_larch_sop_push_pcrel,
 | 
				
			||||||
 | 
						[R_LARCH_SOP_PUSH_ABSOLUTE]			     = apply_r_larch_sop_push_absolute,
 | 
				
			||||||
 | 
						[R_LARCH_SOP_PUSH_DUP]				     = apply_r_larch_sop_push_dup,
 | 
				
			||||||
 | 
						[R_LARCH_SOP_PUSH_PLT_PCREL]			     = apply_r_larch_sop_push_plt_pcrel,
 | 
				
			||||||
 | 
						[R_LARCH_SOP_SUB ... R_LARCH_SOP_IF_ELSE] 	     = apply_r_larch_sop,
 | 
				
			||||||
 | 
						[R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field,
 | 
				
			||||||
 | 
						[R_LARCH_ADD32 ... R_LARCH_SUB64]		     = apply_r_larch_add_sub,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
 | 
				
			||||||
 | 
							       unsigned int symindex, unsigned int relsec,
 | 
				
			||||||
 | 
							       struct module *mod)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i, err;
 | 
				
			||||||
 | 
						unsigned int type;
 | 
				
			||||||
 | 
						s64 rela_stack[RELA_STACK_DEPTH];
 | 
				
			||||||
 | 
						size_t rela_stack_top = 0;
 | 
				
			||||||
 | 
						reloc_rela_handler handler;
 | 
				
			||||||
 | 
						void *location;
 | 
				
			||||||
 | 
						Elf_Addr v;
 | 
				
			||||||
 | 
						Elf_Sym *sym;
 | 
				
			||||||
 | 
						Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pr_debug("%s: Applying relocate section %u to %u\n", __func__, relsec,
 | 
				
			||||||
 | 
						       sechdrs[relsec].sh_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rela_stack_top = 0;
 | 
				
			||||||
 | 
						for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
 | 
				
			||||||
 | 
							/* This is where to make the change */
 | 
				
			||||||
 | 
							location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset;
 | 
				
			||||||
 | 
							/* This is the symbol it is referring to */
 | 
				
			||||||
 | 
							sym = (Elf_Sym *)sechdrs[symindex].sh_addr + ELF_R_SYM(rel[i].r_info);
 | 
				
			||||||
 | 
							if (IS_ERR_VALUE(sym->st_value)) {
 | 
				
			||||||
 | 
								/* Ignore unresolved weak symbol */
 | 
				
			||||||
 | 
								if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
								pr_warn("%s: Unknown symbol %s\n", mod->name, strtab + sym->st_name);
 | 
				
			||||||
 | 
								return -ENOENT;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							type = ELF_R_TYPE(rel[i].r_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (type < ARRAY_SIZE(reloc_rela_handlers))
 | 
				
			||||||
 | 
								handler = reloc_rela_handlers[type];
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								handler = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!handler) {
 | 
				
			||||||
 | 
								pr_err("%s: Unknown relocation type %u\n", mod->name, type);
 | 
				
			||||||
 | 
								return -EINVAL;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pr_debug("type %d st_value %llx r_addend %llx loc %llx\n",
 | 
				
			||||||
 | 
							       (int)ELF_R_TYPE(rel[i].r_info),
 | 
				
			||||||
 | 
							       sym->st_value, rel[i].r_addend, (u64)location);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							v = sym->st_value + rel[i].r_addend;
 | 
				
			||||||
 | 
							err = handler(mod, location, v, rela_stack, &rela_stack_top, type);
 | 
				
			||||||
 | 
							if (err)
 | 
				
			||||||
 | 
								return err;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void *module_alloc(unsigned long size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
 | 
				
			||||||
 | 
								GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in a new issue