Commit f3b7e73b authored by Ilya Leoshkevich's avatar Ilya Leoshkevich Committed by Heiko Carstens
Browse files

s390/module: fix loading modules with a lot of relocations



If the size of the PLT entries generated by apply_rela() exceeds
64KiB, the first ones can no longer reach __jump_r1 with brc. Fix by
using brcl. An alternative solution is to add a __jump_r1 copy after
every 64KiB, however, the space savings are quite small and do not
justify the additional complexity.

Fixes: f19fbd5e ("s390: introduce execute-trampolines for branches")
Cc: stable@vger.kernel.org
Reported-by: default avatarAndrea Righi <andrea.righi@canonical.com>
Signed-off-by: default avatarIlya Leoshkevich <iii@linux.ibm.com>
Reviewed-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Cc: Vasily Gorbik <gor@linux.ibm.com>
Cc: Christian Borntraeger <borntraeger@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
parent 3d787b39
Loading
Loading
Loading
Loading
+18 −19
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@
#define DEBUGP(fmt , ...)
#endif

#define PLT_ENTRY_SIZE 20
#define PLT_ENTRY_SIZE 22

void *module_alloc(unsigned long size)
{
@@ -341,27 +341,26 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
	case R_390_PLTOFF32:	/* 32 bit offset from GOT to PLT. */
	case R_390_PLTOFF64:	/* 16 bit offset from GOT to PLT. */
		if (info->plt_initialized == 0) {
			unsigned int insn[5];
			unsigned int *ip = me->core_layout.base +
					   me->arch.plt_offset +
					   info->plt_offset;

			insn[0] = 0x0d10e310;	/* basr 1,0  */
			insn[1] = 0x100a0004;	/* lg	1,10(1) */
			unsigned char insn[PLT_ENTRY_SIZE];
			char *plt_base;
			char *ip;

			plt_base = me->core_layout.base + me->arch.plt_offset;
			ip = plt_base + info->plt_offset;
			*(int *)insn = 0x0d10e310;	/* basr 1,0  */
			*(int *)&insn[4] = 0x100c0004;	/* lg	1,12(1) */
			if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable) {
				unsigned int *ij;
				ij = me->core_layout.base +
					me->arch.plt_offset +
					me->arch.plt_size - PLT_ENTRY_SIZE;
				insn[2] = 0xa7f40000 +	/* j __jump_r1 */
					(unsigned int)(u16)
					(((unsigned long) ij - 8 -
					  (unsigned long) ip) / 2);
				char *jump_r1;

				jump_r1 = plt_base + me->arch.plt_size -
					PLT_ENTRY_SIZE;
				/* brcl	0xf,__jump_r1 */
				*(short *)&insn[8] = 0xc0f4;
				*(int *)&insn[10] = (jump_r1 - (ip + 8)) / 2;
			} else {
				insn[2] = 0x07f10000;	/* br %r1 */
				*(int *)&insn[8] = 0x07f10000;	/* br %r1 */
			}
			insn[3] = (unsigned int) (val >> 32);
			insn[4] = (unsigned int) val;
			*(long *)&insn[14] = val;

			write(ip, insn, sizeof(insn));
			info->plt_initialized = 1;