xref: /openbmc/linux/arch/ia64/kernel/module.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * IA-64-specific support for kernel module loader.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Copyright (C) 2003 Hewlett-Packard Co
61da177e4SLinus Torvalds  *	David Mosberger-Tang <davidm@hpl.hp.com>
71da177e4SLinus Torvalds  *
81da177e4SLinus Torvalds  * Loosely based on patch by Rusty Russell.
91da177e4SLinus Torvalds  */
101da177e4SLinus Torvalds 
111da177e4SLinus Torvalds /* relocs tested so far:
121da177e4SLinus Torvalds 
131da177e4SLinus Torvalds    DIR64LSB
141da177e4SLinus Torvalds    FPTR64LSB
151da177e4SLinus Torvalds    GPREL22
161da177e4SLinus Torvalds    LDXMOV
171da177e4SLinus Torvalds    LDXMOV
181da177e4SLinus Torvalds    LTOFF22
191da177e4SLinus Torvalds    LTOFF22X
201da177e4SLinus Torvalds    LTOFF22X
211da177e4SLinus Torvalds    LTOFF_FPTR22
221da177e4SLinus Torvalds    PCREL21B	(for br.call only; br.cond is not supported out of modules!)
231da177e4SLinus Torvalds    PCREL60B	(for brl.cond only; brl.call is not supported for modules!)
241da177e4SLinus Torvalds    PCREL64LSB
251da177e4SLinus Torvalds    SECREL32LSB
261da177e4SLinus Torvalds    SEGREL64LSB
271da177e4SLinus Torvalds  */
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds 
301da177e4SLinus Torvalds #include <linux/kernel.h>
311da177e4SLinus Torvalds #include <linux/sched.h>
321da177e4SLinus Torvalds #include <linux/elf.h>
331da177e4SLinus Torvalds #include <linux/moduleloader.h>
341da177e4SLinus Torvalds #include <linux/string.h>
351da177e4SLinus Torvalds #include <linux/vmalloc.h>
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds #include <asm/patch.h>
381da177e4SLinus Torvalds #include <asm/unaligned.h>
398e307888SSergey Senozhatsky #include <asm/sections.h>
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds #define ARCH_MODULE_DEBUG 0
421da177e4SLinus Torvalds 
431da177e4SLinus Torvalds #if ARCH_MODULE_DEBUG
441da177e4SLinus Torvalds # define DEBUGP printk
451da177e4SLinus Torvalds # define inline
461da177e4SLinus Torvalds #else
471da177e4SLinus Torvalds # define DEBUGP(fmt , a...)
481da177e4SLinus Torvalds #endif
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds #ifdef CONFIG_ITANIUM
511da177e4SLinus Torvalds # define USE_BRL	0
521da177e4SLinus Torvalds #else
531da177e4SLinus Torvalds # define USE_BRL	1
541da177e4SLinus Torvalds #endif
551da177e4SLinus Torvalds 
561da177e4SLinus Torvalds #define MAX_LTOFF	((uint64_t) (1 << 22))	/* max. allowable linkage-table offset */
571da177e4SLinus Torvalds 
581da177e4SLinus Torvalds /* Define some relocation helper macros/types: */
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds #define FORMAT_SHIFT	0
611da177e4SLinus Torvalds #define FORMAT_BITS	3
621da177e4SLinus Torvalds #define FORMAT_MASK	((1 << FORMAT_BITS) - 1)
631da177e4SLinus Torvalds #define VALUE_SHIFT	3
641da177e4SLinus Torvalds #define VALUE_BITS	5
651da177e4SLinus Torvalds #define VALUE_MASK	((1 << VALUE_BITS) - 1)
661da177e4SLinus Torvalds 
671da177e4SLinus Torvalds enum reloc_target_format {
681da177e4SLinus Torvalds 	/* direct encoded formats: */
691da177e4SLinus Torvalds 	RF_NONE = 0,
701da177e4SLinus Torvalds 	RF_INSN14 = 1,
711da177e4SLinus Torvalds 	RF_INSN22 = 2,
721da177e4SLinus Torvalds 	RF_INSN64 = 3,
731da177e4SLinus Torvalds 	RF_32MSB = 4,
741da177e4SLinus Torvalds 	RF_32LSB = 5,
751da177e4SLinus Torvalds 	RF_64MSB = 6,
761da177e4SLinus Torvalds 	RF_64LSB = 7,
771da177e4SLinus Torvalds 
781da177e4SLinus Torvalds 	/* formats that cannot be directly decoded: */
791da177e4SLinus Torvalds 	RF_INSN60,
801da177e4SLinus Torvalds 	RF_INSN21B,	/* imm21 form 1 */
811da177e4SLinus Torvalds 	RF_INSN21M,	/* imm21 form 2 */
821da177e4SLinus Torvalds 	RF_INSN21F	/* imm21 form 3 */
831da177e4SLinus Torvalds };
841da177e4SLinus Torvalds 
851da177e4SLinus Torvalds enum reloc_value_formula {
861da177e4SLinus Torvalds 	RV_DIRECT = 4,		/* S + A */
871da177e4SLinus Torvalds 	RV_GPREL = 5,		/* @gprel(S + A) */
881da177e4SLinus Torvalds 	RV_LTREL = 6,		/* @ltoff(S + A) */
891da177e4SLinus Torvalds 	RV_PLTREL = 7,		/* @pltoff(S + A) */
901da177e4SLinus Torvalds 	RV_FPTR = 8,		/* @fptr(S + A) */
911da177e4SLinus Torvalds 	RV_PCREL = 9,		/* S + A - P */
921da177e4SLinus Torvalds 	RV_LTREL_FPTR = 10,	/* @ltoff(@fptr(S + A)) */
931da177e4SLinus Torvalds 	RV_SEGREL = 11,		/* @segrel(S + A) */
941da177e4SLinus Torvalds 	RV_SECREL = 12,		/* @secrel(S + A) */
951da177e4SLinus Torvalds 	RV_BDREL = 13,		/* BD + A */
961da177e4SLinus Torvalds 	RV_LTV = 14,		/* S + A (like RV_DIRECT, except frozen at static link-time) */
971da177e4SLinus Torvalds 	RV_PCREL2 = 15,		/* S + A - P */
981da177e4SLinus Torvalds 	RV_SPECIAL = 16,	/* various (see below) */
991da177e4SLinus Torvalds 	RV_RSVD17 = 17,
1001da177e4SLinus Torvalds 	RV_TPREL = 18,		/* @tprel(S + A) */
1011da177e4SLinus Torvalds 	RV_LTREL_TPREL = 19,	/* @ltoff(@tprel(S + A)) */
1021da177e4SLinus Torvalds 	RV_DTPMOD = 20,		/* @dtpmod(S + A) */
1031da177e4SLinus Torvalds 	RV_LTREL_DTPMOD = 21,	/* @ltoff(@dtpmod(S + A)) */
1041da177e4SLinus Torvalds 	RV_DTPREL = 22,		/* @dtprel(S + A) */
1051da177e4SLinus Torvalds 	RV_LTREL_DTPREL = 23,	/* @ltoff(@dtprel(S + A)) */
1061da177e4SLinus Torvalds 	RV_RSVD24 = 24,
1071da177e4SLinus Torvalds 	RV_RSVD25 = 25,
1081da177e4SLinus Torvalds 	RV_RSVD26 = 26,
1091da177e4SLinus Torvalds 	RV_RSVD27 = 27
1101da177e4SLinus Torvalds 	/* 28-31 reserved for implementation-specific purposes.  */
1111da177e4SLinus Torvalds };
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds #define N(reloc)	[R_IA64_##reloc] = #reloc
1141da177e4SLinus Torvalds 
1151da177e4SLinus Torvalds static const char *reloc_name[256] = {
1161da177e4SLinus Torvalds 	N(NONE),		N(IMM14),		N(IMM22),		N(IMM64),
1171da177e4SLinus Torvalds 	N(DIR32MSB),		N(DIR32LSB),		N(DIR64MSB),		N(DIR64LSB),
1181da177e4SLinus Torvalds 	N(GPREL22),		N(GPREL64I),		N(GPREL32MSB),		N(GPREL32LSB),
1191da177e4SLinus Torvalds 	N(GPREL64MSB),		N(GPREL64LSB),		N(LTOFF22),		N(LTOFF64I),
1201da177e4SLinus Torvalds 	N(PLTOFF22),		N(PLTOFF64I),		N(PLTOFF64MSB),		N(PLTOFF64LSB),
1211da177e4SLinus Torvalds 	N(FPTR64I),		N(FPTR32MSB),		N(FPTR32LSB),		N(FPTR64MSB),
1221da177e4SLinus Torvalds 	N(FPTR64LSB),		N(PCREL60B),		N(PCREL21B),		N(PCREL21M),
1231da177e4SLinus Torvalds 	N(PCREL21F),		N(PCREL32MSB),		N(PCREL32LSB),		N(PCREL64MSB),
1241da177e4SLinus Torvalds 	N(PCREL64LSB),		N(LTOFF_FPTR22),	N(LTOFF_FPTR64I),	N(LTOFF_FPTR32MSB),
1251da177e4SLinus Torvalds 	N(LTOFF_FPTR32LSB),	N(LTOFF_FPTR64MSB),	N(LTOFF_FPTR64LSB),	N(SEGREL32MSB),
1261da177e4SLinus Torvalds 	N(SEGREL32LSB),		N(SEGREL64MSB),		N(SEGREL64LSB),		N(SECREL32MSB),
1271da177e4SLinus Torvalds 	N(SECREL32LSB),		N(SECREL64MSB),		N(SECREL64LSB),		N(REL32MSB),
1281da177e4SLinus Torvalds 	N(REL32LSB),		N(REL64MSB),		N(REL64LSB),		N(LTV32MSB),
1291da177e4SLinus Torvalds 	N(LTV32LSB),		N(LTV64MSB),		N(LTV64LSB),		N(PCREL21BI),
1301da177e4SLinus Torvalds 	N(PCREL22),		N(PCREL64I),		N(IPLTMSB),		N(IPLTLSB),
1311da177e4SLinus Torvalds 	N(COPY),		N(LTOFF22X),		N(LDXMOV),		N(TPREL14),
1321da177e4SLinus Torvalds 	N(TPREL22),		N(TPREL64I),		N(TPREL64MSB),		N(TPREL64LSB),
1331da177e4SLinus Torvalds 	N(LTOFF_TPREL22),	N(DTPMOD64MSB),		N(DTPMOD64LSB),		N(LTOFF_DTPMOD22),
1341da177e4SLinus Torvalds 	N(DTPREL14),		N(DTPREL22),		N(DTPREL64I),		N(DTPREL32MSB),
1351da177e4SLinus Torvalds 	N(DTPREL32LSB),		N(DTPREL64MSB),		N(DTPREL64LSB),		N(LTOFF_DTPREL22)
1361da177e4SLinus Torvalds };
1371da177e4SLinus Torvalds 
1381da177e4SLinus Torvalds #undef N
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds /* Opaque struct for insns, to protect against derefs. */
1411da177e4SLinus Torvalds struct insn;
1421da177e4SLinus Torvalds 
1431da177e4SLinus Torvalds static inline uint64_t
bundle(const struct insn * insn)1441da177e4SLinus Torvalds bundle (const struct insn *insn)
1451da177e4SLinus Torvalds {
1461da177e4SLinus Torvalds 	return (uint64_t) insn & ~0xfUL;
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds 
1491da177e4SLinus Torvalds static inline int
slot(const struct insn * insn)1501da177e4SLinus Torvalds slot (const struct insn *insn)
1511da177e4SLinus Torvalds {
1521da177e4SLinus Torvalds 	return (uint64_t) insn & 0x3;
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds static int
apply_imm64(struct module * mod,struct insn * insn,uint64_t val)1561da177e4SLinus Torvalds apply_imm64 (struct module *mod, struct insn *insn, uint64_t val)
1571da177e4SLinus Torvalds {
158a25fb850SSergei Trofimovich 	if (slot(insn) != 1 && slot(insn) != 2) {
1591da177e4SLinus Torvalds 		printk(KERN_ERR "%s: invalid slot number %d for IMM64\n",
1601da177e4SLinus Torvalds 		       mod->name, slot(insn));
1611da177e4SLinus Torvalds 		return 0;
1621da177e4SLinus Torvalds 	}
1631da177e4SLinus Torvalds 	ia64_patch_imm64((u64) insn, val);
1641da177e4SLinus Torvalds 	return 1;
1651da177e4SLinus Torvalds }
1661da177e4SLinus Torvalds 
1671da177e4SLinus Torvalds static int
apply_imm60(struct module * mod,struct insn * insn,uint64_t val)1681da177e4SLinus Torvalds apply_imm60 (struct module *mod, struct insn *insn, uint64_t val)
1691da177e4SLinus Torvalds {
170a25fb850SSergei Trofimovich 	if (slot(insn) != 1 && slot(insn) != 2) {
1711da177e4SLinus Torvalds 		printk(KERN_ERR "%s: invalid slot number %d for IMM60\n",
1721da177e4SLinus Torvalds 		       mod->name, slot(insn));
1731da177e4SLinus Torvalds 		return 0;
1741da177e4SLinus Torvalds 	}
1751da177e4SLinus Torvalds 	if (val + ((uint64_t) 1 << 59) >= (1UL << 60)) {
176e088a4adSMatthew Wilcox 		printk(KERN_ERR "%s: value %ld out of IMM60 range\n",
177e088a4adSMatthew Wilcox 			mod->name, (long) val);
1781da177e4SLinus Torvalds 		return 0;
1791da177e4SLinus Torvalds 	}
1801da177e4SLinus Torvalds 	ia64_patch_imm60((u64) insn, val);
1811da177e4SLinus Torvalds 	return 1;
1821da177e4SLinus Torvalds }
1831da177e4SLinus Torvalds 
1841da177e4SLinus Torvalds static int
apply_imm22(struct module * mod,struct insn * insn,uint64_t val)1851da177e4SLinus Torvalds apply_imm22 (struct module *mod, struct insn *insn, uint64_t val)
1861da177e4SLinus Torvalds {
1871da177e4SLinus Torvalds 	if (val + (1 << 21) >= (1 << 22)) {
188e088a4adSMatthew Wilcox 		printk(KERN_ERR "%s: value %li out of IMM22 range\n",
189e088a4adSMatthew Wilcox 			mod->name, (long)val);
1901da177e4SLinus Torvalds 		return 0;
1911da177e4SLinus Torvalds 	}
1921da177e4SLinus Torvalds 	ia64_patch((u64) insn, 0x01fffcfe000UL, (  ((val & 0x200000UL) << 15) /* bit 21 -> 36 */
1931da177e4SLinus Torvalds 					         | ((val & 0x1f0000UL) <<  6) /* bit 16 -> 22 */
1941da177e4SLinus Torvalds 					         | ((val & 0x00ff80UL) << 20) /* bit  7 -> 27 */
1951da177e4SLinus Torvalds 					         | ((val & 0x00007fUL) << 13) /* bit  0 -> 13 */));
1961da177e4SLinus Torvalds 	return 1;
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds 
1991da177e4SLinus Torvalds static int
apply_imm21b(struct module * mod,struct insn * insn,uint64_t val)2001da177e4SLinus Torvalds apply_imm21b (struct module *mod, struct insn *insn, uint64_t val)
2011da177e4SLinus Torvalds {
2021da177e4SLinus Torvalds 	if (val + (1 << 20) >= (1 << 21)) {
203e088a4adSMatthew Wilcox 		printk(KERN_ERR "%s: value %li out of IMM21b range\n",
204e088a4adSMatthew Wilcox 			mod->name, (long)val);
2051da177e4SLinus Torvalds 		return 0;
2061da177e4SLinus Torvalds 	}
2071da177e4SLinus Torvalds 	ia64_patch((u64) insn, 0x11ffffe000UL, (  ((val & 0x100000UL) << 16) /* bit 20 -> 36 */
2081da177e4SLinus Torvalds 					        | ((val & 0x0fffffUL) << 13) /* bit  0 -> 13 */));
2091da177e4SLinus Torvalds 	return 1;
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds 
2121da177e4SLinus Torvalds #if USE_BRL
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds struct plt_entry {
2151da177e4SLinus Torvalds 	/* Three instruction bundles in PLT. */
2161da177e4SLinus Torvalds  	unsigned char bundle[2][16];
2171da177e4SLinus Torvalds };
2181da177e4SLinus Torvalds 
2191da177e4SLinus Torvalds static const struct plt_entry ia64_plt_template = {
2201da177e4SLinus Torvalds 	{
2211da177e4SLinus Torvalds 		{
2221da177e4SLinus Torvalds 			0x04, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */
2231da177e4SLinus Torvalds 			0x00, 0x00, 0x00, 0x00, 0x00, 0x20, /*	     movl gp=TARGET_GP */
2241da177e4SLinus Torvalds 			0x00, 0x00, 0x00, 0x60
2251da177e4SLinus Torvalds 		},
2261da177e4SLinus Torvalds 		{
2271da177e4SLinus Torvalds 			0x05, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */
2281da177e4SLinus Torvalds 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*	     brl.many gp=TARGET_GP */
2291da177e4SLinus Torvalds 			0x08, 0x00, 0x00, 0xc0
2301da177e4SLinus Torvalds 		}
2311da177e4SLinus Torvalds 	}
2321da177e4SLinus Torvalds };
2331da177e4SLinus Torvalds 
2341da177e4SLinus Torvalds static int
patch_plt(struct module * mod,struct plt_entry * plt,long target_ip,unsigned long target_gp)2351da177e4SLinus Torvalds patch_plt (struct module *mod, struct plt_entry *plt, long target_ip, unsigned long target_gp)
2361da177e4SLinus Torvalds {
2371da177e4SLinus Torvalds 	if (apply_imm64(mod, (struct insn *) (plt->bundle[0] + 2), target_gp)
2381da177e4SLinus Torvalds 	    && apply_imm60(mod, (struct insn *) (plt->bundle[1] + 2),
2391da177e4SLinus Torvalds 			   (target_ip - (int64_t) plt->bundle[1]) / 16))
2401da177e4SLinus Torvalds 		return 1;
2411da177e4SLinus Torvalds 	return 0;
2421da177e4SLinus Torvalds }
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds unsigned long
plt_target(struct plt_entry * plt)2451da177e4SLinus Torvalds plt_target (struct plt_entry *plt)
2461da177e4SLinus Torvalds {
2471da177e4SLinus Torvalds 	uint64_t b0, b1, *b = (uint64_t *) plt->bundle[1];
2481da177e4SLinus Torvalds 	long off;
2491da177e4SLinus Torvalds 
2501da177e4SLinus Torvalds 	b0 = b[0]; b1 = b[1];
2511da177e4SLinus Torvalds 	off = (  ((b1 & 0x00fffff000000000UL) >> 36)		/* imm20b -> bit 0 */
2521da177e4SLinus Torvalds 	       | ((b0 >> 48) << 20) | ((b1 & 0x7fffffUL) << 36)	/* imm39 -> bit 20 */
2531da177e4SLinus Torvalds 	       | ((b1 & 0x0800000000000000UL) << 0));		/* i -> bit 59 */
2541da177e4SLinus Torvalds 	return (long) plt->bundle[1] + 16*off;
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds #else /* !USE_BRL */
2581da177e4SLinus Torvalds 
2591da177e4SLinus Torvalds struct plt_entry {
2601da177e4SLinus Torvalds 	/* Three instruction bundles in PLT. */
2611da177e4SLinus Torvalds  	unsigned char bundle[3][16];
2621da177e4SLinus Torvalds };
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds static const struct plt_entry ia64_plt_template = {
2651da177e4SLinus Torvalds 	{
2661da177e4SLinus Torvalds 		{
2671da177e4SLinus Torvalds 			0x05, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */
2681da177e4SLinus Torvalds 			0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*	     movl r16=TARGET_IP */
2691da177e4SLinus Torvalds 			0x02, 0x00, 0x00, 0x60
2701da177e4SLinus Torvalds 		},
2711da177e4SLinus Torvalds 		{
2721da177e4SLinus Torvalds 			0x04, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MLX] nop.m 0 */
2731da177e4SLinus Torvalds 			0x00, 0x00, 0x00, 0x00, 0x00, 0x20, /*	     movl gp=TARGET_GP */
2741da177e4SLinus Torvalds 			0x00, 0x00, 0x00, 0x60
2751da177e4SLinus Torvalds 		},
2761da177e4SLinus Torvalds 		{
2771da177e4SLinus Torvalds 			0x11, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MIB] nop.m 0 */
2781da177e4SLinus Torvalds 			0x60, 0x80, 0x04, 0x80, 0x03, 0x00, /*	     mov b6=r16 */
2791da177e4SLinus Torvalds 			0x60, 0x00, 0x80, 0x00		    /*	     br.few b6 */
2801da177e4SLinus Torvalds 		}
2811da177e4SLinus Torvalds 	}
2821da177e4SLinus Torvalds };
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds static int
patch_plt(struct module * mod,struct plt_entry * plt,long target_ip,unsigned long target_gp)2851da177e4SLinus Torvalds patch_plt (struct module *mod, struct plt_entry *plt, long target_ip, unsigned long target_gp)
2861da177e4SLinus Torvalds {
2871da177e4SLinus Torvalds 	if (apply_imm64(mod, (struct insn *) (plt->bundle[0] + 2), target_ip)
2881da177e4SLinus Torvalds 	    && apply_imm64(mod, (struct insn *) (plt->bundle[1] + 2), target_gp))
2891da177e4SLinus Torvalds 		return 1;
2901da177e4SLinus Torvalds 	return 0;
2911da177e4SLinus Torvalds }
2921da177e4SLinus Torvalds 
2931da177e4SLinus Torvalds unsigned long
plt_target(struct plt_entry * plt)2941da177e4SLinus Torvalds plt_target (struct plt_entry *plt)
2951da177e4SLinus Torvalds {
2961da177e4SLinus Torvalds 	uint64_t b0, b1, *b = (uint64_t *) plt->bundle[0];
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds 	b0 = b[0]; b1 = b[1];
2991da177e4SLinus Torvalds 	return (  ((b1 & 0x000007f000000000) >> 36)		/* imm7b -> bit 0 */
3001da177e4SLinus Torvalds 		| ((b1 & 0x07fc000000000000) >> 43)		/* imm9d -> bit 7 */
3011da177e4SLinus Torvalds 		| ((b1 & 0x0003e00000000000) >> 29)		/* imm5c -> bit 16 */
3021da177e4SLinus Torvalds 		| ((b1 & 0x0000100000000000) >> 23)		/* ic -> bit 21 */
3031da177e4SLinus Torvalds 		| ((b0 >> 46) << 22) | ((b1 & 0x7fffff) << 40)	/* imm41 -> bit 22 */
3041da177e4SLinus Torvalds 		| ((b1 & 0x0800000000000000) <<  4));		/* i -> bit 63 */
3051da177e4SLinus Torvalds }
3061da177e4SLinus Torvalds 
3071da177e4SLinus Torvalds #endif /* !USE_BRL */
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds void
module_arch_freeing_init(struct module * mod)310d453cdedSRusty Russell module_arch_freeing_init (struct module *mod)
3111da177e4SLinus Torvalds {
312d453cdedSRusty Russell 	if (mod->arch.init_unw_table) {
3131da177e4SLinus Torvalds 		unw_remove_unwind_table(mod->arch.init_unw_table);
3141da177e4SLinus Torvalds 		mod->arch.init_unw_table = NULL;
3151da177e4SLinus Torvalds 	}
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds /* Have we already seen one of these relocations? */
3191da177e4SLinus Torvalds /* FIXME: we could look in other sections, too --RR */
3201da177e4SLinus Torvalds static int
duplicate_reloc(const Elf64_Rela * rela,unsigned int num)3211da177e4SLinus Torvalds duplicate_reloc (const Elf64_Rela *rela, unsigned int num)
3221da177e4SLinus Torvalds {
3231da177e4SLinus Torvalds 	unsigned int i;
3241da177e4SLinus Torvalds 
3251da177e4SLinus Torvalds 	for (i = 0; i < num; i++) {
3261da177e4SLinus Torvalds 		if (rela[i].r_info == rela[num].r_info && rela[i].r_addend == rela[num].r_addend)
3271da177e4SLinus Torvalds 			return 1;
3281da177e4SLinus Torvalds 	}
3291da177e4SLinus Torvalds 	return 0;
3301da177e4SLinus Torvalds }
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds /* Count how many GOT entries we may need */
3331da177e4SLinus Torvalds static unsigned int
count_gots(const Elf64_Rela * rela,unsigned int num)3341da177e4SLinus Torvalds count_gots (const Elf64_Rela *rela, unsigned int num)
3351da177e4SLinus Torvalds {
3361da177e4SLinus Torvalds 	unsigned int i, ret = 0;
3371da177e4SLinus Torvalds 
3381da177e4SLinus Torvalds 	/* Sure, this is order(n^2), but it's usually short, and not
3391da177e4SLinus Torvalds            time critical */
3401da177e4SLinus Torvalds 	for (i = 0; i < num; i++) {
3411da177e4SLinus Torvalds 		switch (ELF64_R_TYPE(rela[i].r_info)) {
3421da177e4SLinus Torvalds 		      case R_IA64_LTOFF22:
3431da177e4SLinus Torvalds 		      case R_IA64_LTOFF22X:
3441da177e4SLinus Torvalds 		      case R_IA64_LTOFF64I:
3451da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR22:
3461da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR64I:
3471da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR32MSB:
3481da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR32LSB:
3491da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR64MSB:
3501da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR64LSB:
3511da177e4SLinus Torvalds 			if (!duplicate_reloc(rela, i))
3521da177e4SLinus Torvalds 				ret++;
3531da177e4SLinus Torvalds 			break;
3541da177e4SLinus Torvalds 		}
3551da177e4SLinus Torvalds 	}
3561da177e4SLinus Torvalds 	return ret;
3571da177e4SLinus Torvalds }
3581da177e4SLinus Torvalds 
3591da177e4SLinus Torvalds /* Count how many PLT entries we may need */
3601da177e4SLinus Torvalds static unsigned int
count_plts(const Elf64_Rela * rela,unsigned int num)3611da177e4SLinus Torvalds count_plts (const Elf64_Rela *rela, unsigned int num)
3621da177e4SLinus Torvalds {
3631da177e4SLinus Torvalds 	unsigned int i, ret = 0;
3641da177e4SLinus Torvalds 
3651da177e4SLinus Torvalds 	/* Sure, this is order(n^2), but it's usually short, and not
3661da177e4SLinus Torvalds            time critical */
3671da177e4SLinus Torvalds 	for (i = 0; i < num; i++) {
3681da177e4SLinus Torvalds 		switch (ELF64_R_TYPE(rela[i].r_info)) {
3691da177e4SLinus Torvalds 		      case R_IA64_PCREL21B:
3701da177e4SLinus Torvalds 		      case R_IA64_PLTOFF22:
3711da177e4SLinus Torvalds 		      case R_IA64_PLTOFF64I:
3721da177e4SLinus Torvalds 		      case R_IA64_PLTOFF64MSB:
3731da177e4SLinus Torvalds 		      case R_IA64_PLTOFF64LSB:
3741da177e4SLinus Torvalds 		      case R_IA64_IPLTMSB:
3751da177e4SLinus Torvalds 		      case R_IA64_IPLTLSB:
3761da177e4SLinus Torvalds 			if (!duplicate_reloc(rela, i))
3771da177e4SLinus Torvalds 				ret++;
3781da177e4SLinus Torvalds 			break;
3791da177e4SLinus Torvalds 		}
3801da177e4SLinus Torvalds 	}
3811da177e4SLinus Torvalds 	return ret;
3821da177e4SLinus Torvalds }
3831da177e4SLinus Torvalds 
3841da177e4SLinus Torvalds /* We need to create an function-descriptors for any internal function
3851da177e4SLinus Torvalds    which is referenced. */
3861da177e4SLinus Torvalds static unsigned int
count_fdescs(const Elf64_Rela * rela,unsigned int num)3871da177e4SLinus Torvalds count_fdescs (const Elf64_Rela *rela, unsigned int num)
3881da177e4SLinus Torvalds {
3891da177e4SLinus Torvalds 	unsigned int i, ret = 0;
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 	/* Sure, this is order(n^2), but it's usually short, and not time critical.  */
3921da177e4SLinus Torvalds 	for (i = 0; i < num; i++) {
3931da177e4SLinus Torvalds 		switch (ELF64_R_TYPE(rela[i].r_info)) {
3941da177e4SLinus Torvalds 		      case R_IA64_FPTR64I:
3951da177e4SLinus Torvalds 		      case R_IA64_FPTR32LSB:
3961da177e4SLinus Torvalds 		      case R_IA64_FPTR32MSB:
3971da177e4SLinus Torvalds 		      case R_IA64_FPTR64LSB:
3981da177e4SLinus Torvalds 		      case R_IA64_FPTR64MSB:
3991da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR22:
4001da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR32LSB:
4011da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR32MSB:
4021da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR64I:
4031da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR64LSB:
4041da177e4SLinus Torvalds 		      case R_IA64_LTOFF_FPTR64MSB:
4051da177e4SLinus Torvalds 		      case R_IA64_IPLTMSB:
4061da177e4SLinus Torvalds 		      case R_IA64_IPLTLSB:
4071da177e4SLinus Torvalds 			/*
4081da177e4SLinus Torvalds 			 * Jumps to static functions sometimes go straight to their
4091da177e4SLinus Torvalds 			 * offset.  Of course, that may not be possible if the jump is
4101da177e4SLinus Torvalds 			 * from init -> core or vice. versa, so we need to generate an
4111da177e4SLinus Torvalds 			 * FDESC (and PLT etc) for that.
4121da177e4SLinus Torvalds 			 */
4131da177e4SLinus Torvalds 		      case R_IA64_PCREL21B:
4141da177e4SLinus Torvalds 			if (!duplicate_reloc(rela, i))
4151da177e4SLinus Torvalds 				ret++;
4161da177e4SLinus Torvalds 			break;
4171da177e4SLinus Torvalds 		}
4181da177e4SLinus Torvalds 	}
4191da177e4SLinus Torvalds 	return ret;
4201da177e4SLinus Torvalds }
4211da177e4SLinus Torvalds 
4221da177e4SLinus Torvalds int
module_frob_arch_sections(Elf_Ehdr * ehdr,Elf_Shdr * sechdrs,char * secstrings,struct module * mod)4231da177e4SLinus Torvalds module_frob_arch_sections (Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings,
4241da177e4SLinus Torvalds 			   struct module *mod)
4251da177e4SLinus Torvalds {
4261da177e4SLinus Torvalds 	unsigned long core_plts = 0, init_plts = 0, gots = 0, fdescs = 0;
4271da177e4SLinus Torvalds 	Elf64_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum;
4281da177e4SLinus Torvalds 
4291da177e4SLinus Torvalds 	/*
4301da177e4SLinus Torvalds 	 * To store the PLTs and function-descriptors, we expand the .text section for
4311da177e4SLinus Torvalds 	 * core module-code and the .init.text section for initialization code.
4321da177e4SLinus Torvalds 	 */
4331da177e4SLinus Torvalds 	for (s = sechdrs; s < sechdrs_end; ++s)
4341da177e4SLinus Torvalds 		if (strcmp(".core.plt", secstrings + s->sh_name) == 0)
4351da177e4SLinus Torvalds 			mod->arch.core_plt = s;
4361da177e4SLinus Torvalds 		else if (strcmp(".init.plt", secstrings + s->sh_name) == 0)
4371da177e4SLinus Torvalds 			mod->arch.init_plt = s;
4381da177e4SLinus Torvalds 		else if (strcmp(".got", secstrings + s->sh_name) == 0)
4391da177e4SLinus Torvalds 			mod->arch.got = s;
4401da177e4SLinus Torvalds 		else if (strcmp(".opd", secstrings + s->sh_name) == 0)
4411da177e4SLinus Torvalds 			mod->arch.opd = s;
4421da177e4SLinus Torvalds 		else if (strcmp(".IA_64.unwind", secstrings + s->sh_name) == 0)
4431da177e4SLinus Torvalds 			mod->arch.unwind = s;
4441da177e4SLinus Torvalds 
4451da177e4SLinus Torvalds 	if (!mod->arch.core_plt || !mod->arch.init_plt || !mod->arch.got || !mod->arch.opd) {
4461da177e4SLinus Torvalds 		printk(KERN_ERR "%s: sections missing\n", mod->name);
4471da177e4SLinus Torvalds 		return -ENOEXEC;
4481da177e4SLinus Torvalds 	}
4491da177e4SLinus Torvalds 
4501da177e4SLinus Torvalds 	/* GOT and PLTs can occur in any relocated section... */
4511da177e4SLinus Torvalds 	for (s = sechdrs + 1; s < sechdrs_end; ++s) {
4521da177e4SLinus Torvalds 		const Elf64_Rela *rels = (void *)ehdr + s->sh_offset;
4531da177e4SLinus Torvalds 		unsigned long numrels = s->sh_size/sizeof(Elf64_Rela);
4541da177e4SLinus Torvalds 
4551da177e4SLinus Torvalds 		if (s->sh_type != SHT_RELA)
4561da177e4SLinus Torvalds 			continue;
4571da177e4SLinus Torvalds 
4581da177e4SLinus Torvalds 		gots += count_gots(rels, numrels);
4591da177e4SLinus Torvalds 		fdescs += count_fdescs(rels, numrels);
4601da177e4SLinus Torvalds 		if (strstr(secstrings + s->sh_name, ".init"))
4611da177e4SLinus Torvalds 			init_plts += count_plts(rels, numrels);
4621da177e4SLinus Torvalds 		else
4631da177e4SLinus Torvalds 			core_plts += count_plts(rels, numrels);
4641da177e4SLinus Torvalds 	}
4651da177e4SLinus Torvalds 
4661da177e4SLinus Torvalds 	mod->arch.core_plt->sh_type = SHT_NOBITS;
4671da177e4SLinus Torvalds 	mod->arch.core_plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
4681da177e4SLinus Torvalds 	mod->arch.core_plt->sh_addralign = 16;
4691da177e4SLinus Torvalds 	mod->arch.core_plt->sh_size = core_plts * sizeof(struct plt_entry);
4701da177e4SLinus Torvalds 	mod->arch.init_plt->sh_type = SHT_NOBITS;
4711da177e4SLinus Torvalds 	mod->arch.init_plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
4721da177e4SLinus Torvalds 	mod->arch.init_plt->sh_addralign = 16;
4731da177e4SLinus Torvalds 	mod->arch.init_plt->sh_size = init_plts * sizeof(struct plt_entry);
4741da177e4SLinus Torvalds 	mod->arch.got->sh_type = SHT_NOBITS;
4751da177e4SLinus Torvalds 	mod->arch.got->sh_flags = ARCH_SHF_SMALL | SHF_ALLOC;
4761da177e4SLinus Torvalds 	mod->arch.got->sh_addralign = 8;
4771da177e4SLinus Torvalds 	mod->arch.got->sh_size = gots * sizeof(struct got_entry);
4781da177e4SLinus Torvalds 	mod->arch.opd->sh_type = SHT_NOBITS;
4791da177e4SLinus Torvalds 	mod->arch.opd->sh_flags = SHF_ALLOC;
4801da177e4SLinus Torvalds 	mod->arch.opd->sh_addralign = 8;
4811da177e4SLinus Torvalds 	mod->arch.opd->sh_size = fdescs * sizeof(struct fdesc);
4821da177e4SLinus Torvalds 	DEBUGP("%s: core.plt=%lx, init.plt=%lx, got=%lx, fdesc=%lx\n",
483d4ed8084SHarvey Harrison 	       __func__, mod->arch.core_plt->sh_size, mod->arch.init_plt->sh_size,
4841da177e4SLinus Torvalds 	       mod->arch.got->sh_size, mod->arch.opd->sh_size);
4851da177e4SLinus Torvalds 	return 0;
4861da177e4SLinus Torvalds }
4871da177e4SLinus Torvalds 
488*ac3b4328SSong Liu static inline bool
in_init(const struct module * mod,uint64_t addr)4891da177e4SLinus Torvalds in_init (const struct module *mod, uint64_t addr)
4901da177e4SLinus Torvalds {
491*ac3b4328SSong Liu 	return within_module_init(addr, mod);
4921da177e4SLinus Torvalds }
4931da177e4SLinus Torvalds 
494*ac3b4328SSong Liu static inline bool
in_core(const struct module * mod,uint64_t addr)4951da177e4SLinus Torvalds in_core (const struct module *mod, uint64_t addr)
4961da177e4SLinus Torvalds {
497*ac3b4328SSong Liu 	return within_module_core(addr, mod);
4981da177e4SLinus Torvalds }
4991da177e4SLinus Torvalds 
500*ac3b4328SSong Liu static inline bool
is_internal(const struct module * mod,uint64_t value)5011da177e4SLinus Torvalds is_internal (const struct module *mod, uint64_t value)
5021da177e4SLinus Torvalds {
5031da177e4SLinus Torvalds 	return in_init(mod, value) || in_core(mod, value);
5041da177e4SLinus Torvalds }
5051da177e4SLinus Torvalds 
5061da177e4SLinus Torvalds /*
5071da177e4SLinus Torvalds  * Get gp-relative offset for the linkage-table entry of VALUE.
5081da177e4SLinus Torvalds  */
5091da177e4SLinus Torvalds static uint64_t
get_ltoff(struct module * mod,uint64_t value,int * okp)5101da177e4SLinus Torvalds get_ltoff (struct module *mod, uint64_t value, int *okp)
5111da177e4SLinus Torvalds {
5121da177e4SLinus Torvalds 	struct got_entry *got, *e;
5131da177e4SLinus Torvalds 
5141da177e4SLinus Torvalds 	if (!*okp)
5151da177e4SLinus Torvalds 		return 0;
5161da177e4SLinus Torvalds 
5171da177e4SLinus Torvalds 	got = (void *) mod->arch.got->sh_addr;
5181da177e4SLinus Torvalds 	for (e = got; e < got + mod->arch.next_got_entry; ++e)
5191da177e4SLinus Torvalds 		if (e->val == value)
5201da177e4SLinus Torvalds 			goto found;
5211da177e4SLinus Torvalds 
5221da177e4SLinus Torvalds 	/* Not enough GOT entries? */
52380a03e29SStoyan Gaydarov 	BUG_ON(e >= (struct got_entry *) (mod->arch.got->sh_addr + mod->arch.got->sh_size));
5241da177e4SLinus Torvalds 
5251da177e4SLinus Torvalds 	e->val = value;
5261da177e4SLinus Torvalds 	++mod->arch.next_got_entry;
5271da177e4SLinus Torvalds   found:
5281da177e4SLinus Torvalds 	return (uint64_t) e - mod->arch.gp;
5291da177e4SLinus Torvalds }
5301da177e4SLinus Torvalds 
5311da177e4SLinus Torvalds static inline int
gp_addressable(struct module * mod,uint64_t value)5321da177e4SLinus Torvalds gp_addressable (struct module *mod, uint64_t value)
5331da177e4SLinus Torvalds {
5341da177e4SLinus Torvalds 	return value - mod->arch.gp + MAX_LTOFF/2 < MAX_LTOFF;
5351da177e4SLinus Torvalds }
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds /* Get PC-relative PLT entry for this value.  Returns 0 on failure. */
5381da177e4SLinus Torvalds static uint64_t
get_plt(struct module * mod,const struct insn * insn,uint64_t value,int * okp)5391da177e4SLinus Torvalds get_plt (struct module *mod, const struct insn *insn, uint64_t value, int *okp)
5401da177e4SLinus Torvalds {
5411da177e4SLinus Torvalds 	struct plt_entry *plt, *plt_end;
5421da177e4SLinus Torvalds 	uint64_t target_ip, target_gp;
5431da177e4SLinus Torvalds 
5441da177e4SLinus Torvalds 	if (!*okp)
5451da177e4SLinus Torvalds 		return 0;
5461da177e4SLinus Torvalds 
5471da177e4SLinus Torvalds 	if (in_init(mod, (uint64_t) insn)) {
5481da177e4SLinus Torvalds 		plt = (void *) mod->arch.init_plt->sh_addr;
5491da177e4SLinus Torvalds 		plt_end = (void *) plt + mod->arch.init_plt->sh_size;
5501da177e4SLinus Torvalds 	} else {
5511da177e4SLinus Torvalds 		plt = (void *) mod->arch.core_plt->sh_addr;
5521da177e4SLinus Torvalds 		plt_end = (void *) plt + mod->arch.core_plt->sh_size;
5531da177e4SLinus Torvalds 	}
5541da177e4SLinus Torvalds 
5551da177e4SLinus Torvalds 	/* "value" is a pointer to a function-descriptor; fetch the target ip/gp from it: */
5561da177e4SLinus Torvalds 	target_ip = ((uint64_t *) value)[0];
5571da177e4SLinus Torvalds 	target_gp = ((uint64_t *) value)[1];
5581da177e4SLinus Torvalds 
5591da177e4SLinus Torvalds 	/* Look for existing PLT entry. */
5601da177e4SLinus Torvalds 	while (plt->bundle[0][0]) {
5611da177e4SLinus Torvalds 		if (plt_target(plt) == target_ip)
5621da177e4SLinus Torvalds 			goto found;
5631da177e4SLinus Torvalds 		if (++plt >= plt_end)
5641da177e4SLinus Torvalds 			BUG();
5651da177e4SLinus Torvalds 	}
5661da177e4SLinus Torvalds 	*plt = ia64_plt_template;
5671da177e4SLinus Torvalds 	if (!patch_plt(mod, plt, target_ip, target_gp)) {
5681da177e4SLinus Torvalds 		*okp = 0;
5691da177e4SLinus Torvalds 		return 0;
5701da177e4SLinus Torvalds 	}
5711da177e4SLinus Torvalds #if ARCH_MODULE_DEBUG
5721da177e4SLinus Torvalds 	if (plt_target(plt) != target_ip) {
5731da177e4SLinus Torvalds 		printk("%s: mistargeted PLT: wanted %lx, got %lx\n",
574d4ed8084SHarvey Harrison 		       __func__, target_ip, plt_target(plt));
5751da177e4SLinus Torvalds 		*okp = 0;
5761da177e4SLinus Torvalds 		return 0;
5771da177e4SLinus Torvalds 	}
5781da177e4SLinus Torvalds #endif
5791da177e4SLinus Torvalds   found:
5801da177e4SLinus Torvalds 	return (uint64_t) plt;
5811da177e4SLinus Torvalds }
5821da177e4SLinus Torvalds 
5831da177e4SLinus Torvalds /* Get function descriptor for VALUE. */
5841da177e4SLinus Torvalds static uint64_t
get_fdesc(struct module * mod,uint64_t value,int * okp)5851da177e4SLinus Torvalds get_fdesc (struct module *mod, uint64_t value, int *okp)
5861da177e4SLinus Torvalds {
5871da177e4SLinus Torvalds 	struct fdesc *fdesc = (void *) mod->arch.opd->sh_addr;
5881da177e4SLinus Torvalds 
5891da177e4SLinus Torvalds 	if (!*okp)
5901da177e4SLinus Torvalds 		return 0;
5911da177e4SLinus Torvalds 
5921da177e4SLinus Torvalds 	if (!value) {
5931da177e4SLinus Torvalds 		printk(KERN_ERR "%s: fdesc for zero requested!\n", mod->name);
5941da177e4SLinus Torvalds 		return 0;
5951da177e4SLinus Torvalds 	}
5961da177e4SLinus Torvalds 
5971da177e4SLinus Torvalds 	if (!is_internal(mod, value))
5981da177e4SLinus Torvalds 		/*
5991da177e4SLinus Torvalds 		 * If it's not a module-local entry-point, "value" already points to a
6001da177e4SLinus Torvalds 		 * function-descriptor.
6011da177e4SLinus Torvalds 		 */
6021da177e4SLinus Torvalds 		return value;
6031da177e4SLinus Torvalds 
6041da177e4SLinus Torvalds 	/* Look for existing function descriptor. */
60541a88b45SChristophe Leroy 	while (fdesc->addr) {
60641a88b45SChristophe Leroy 		if (fdesc->addr == value)
6071da177e4SLinus Torvalds 			return (uint64_t)fdesc;
6081da177e4SLinus Torvalds 		if ((uint64_t) ++fdesc >= mod->arch.opd->sh_addr + mod->arch.opd->sh_size)
6091da177e4SLinus Torvalds 			BUG();
6101da177e4SLinus Torvalds 	}
6111da177e4SLinus Torvalds 
6121da177e4SLinus Torvalds 	/* Create new one */
61341a88b45SChristophe Leroy 	fdesc->addr = value;
6141da177e4SLinus Torvalds 	fdesc->gp = mod->arch.gp;
6151da177e4SLinus Torvalds 	return (uint64_t) fdesc;
6161da177e4SLinus Torvalds }
6171da177e4SLinus Torvalds 
6181da177e4SLinus Torvalds static inline int
do_reloc(struct module * mod,uint8_t r_type,Elf64_Sym * sym,uint64_t addend,Elf64_Shdr * sec,void * location)6191da177e4SLinus Torvalds do_reloc (struct module *mod, uint8_t r_type, Elf64_Sym *sym, uint64_t addend,
6201da177e4SLinus Torvalds 	  Elf64_Shdr *sec, void *location)
6211da177e4SLinus Torvalds {
6221da177e4SLinus Torvalds 	enum reloc_target_format format = (r_type >> FORMAT_SHIFT) & FORMAT_MASK;
6231da177e4SLinus Torvalds 	enum reloc_value_formula formula = (r_type >> VALUE_SHIFT) & VALUE_MASK;
6241da177e4SLinus Torvalds 	uint64_t val;
6251da177e4SLinus Torvalds 	int ok = 1;
6261da177e4SLinus Torvalds 
6271da177e4SLinus Torvalds 	val = sym->st_value + addend;
6281da177e4SLinus Torvalds 
6291da177e4SLinus Torvalds 	switch (formula) {
6301da177e4SLinus Torvalds 	      case RV_SEGREL:	/* segment base is arbitrarily chosen to be 0 for kernel modules */
6311da177e4SLinus Torvalds 	      case RV_DIRECT:
6321da177e4SLinus Torvalds 		break;
6331da177e4SLinus Torvalds 
6341da177e4SLinus Torvalds 	      case RV_GPREL:	  val -= mod->arch.gp; break;
6351da177e4SLinus Torvalds 	      case RV_LTREL:	  val = get_ltoff(mod, val, &ok); break;
6361da177e4SLinus Torvalds 	      case RV_PLTREL:	  val = get_plt(mod, location, val, &ok); break;
6371da177e4SLinus Torvalds 	      case RV_FPTR:	  val = get_fdesc(mod, val, &ok); break;
6381da177e4SLinus Torvalds 	      case RV_SECREL:	  val -= sec->sh_addr; break;
6391da177e4SLinus Torvalds 	      case RV_LTREL_FPTR: val = get_ltoff(mod, get_fdesc(mod, val, &ok), &ok); break;
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	      case RV_PCREL:
6421da177e4SLinus Torvalds 		switch (r_type) {
6431da177e4SLinus Torvalds 		      case R_IA64_PCREL21B:
6441da177e4SLinus Torvalds 			if ((in_init(mod, val) && in_core(mod, (uint64_t)location)) ||
6451da177e4SLinus Torvalds 			    (in_core(mod, val) && in_init(mod, (uint64_t)location))) {
6461da177e4SLinus Torvalds 				/*
6471da177e4SLinus Torvalds 				 * Init section may have been allocated far away from core,
6481da177e4SLinus Torvalds 				 * if the branch won't reach, then allocate a plt for it.
6491da177e4SLinus Torvalds 				 */
6501da177e4SLinus Torvalds 				uint64_t delta = ((int64_t)val - (int64_t)location) / 16;
6511da177e4SLinus Torvalds 				if (delta + (1 << 20) >= (1 << 21)) {
6521da177e4SLinus Torvalds 					val = get_fdesc(mod, val, &ok);
6531da177e4SLinus Torvalds 					val = get_plt(mod, location, val, &ok);
6541da177e4SLinus Torvalds 				}
6551da177e4SLinus Torvalds 			} else if (!is_internal(mod, val))
6561da177e4SLinus Torvalds 				val = get_plt(mod, location, val, &ok);
657df561f66SGustavo A. R. Silva 			fallthrough;
6581da177e4SLinus Torvalds 		      default:
6591da177e4SLinus Torvalds 			val -= bundle(location);
6601da177e4SLinus Torvalds 			break;
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds 		      case R_IA64_PCREL32MSB:
6631da177e4SLinus Torvalds 		      case R_IA64_PCREL32LSB:
6641da177e4SLinus Torvalds 		      case R_IA64_PCREL64MSB:
6651da177e4SLinus Torvalds 		      case R_IA64_PCREL64LSB:
6661da177e4SLinus Torvalds 			val -= (uint64_t) location;
6671da177e4SLinus Torvalds 			break;
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 		}
6701da177e4SLinus Torvalds 		switch (r_type) {
6711da177e4SLinus Torvalds 		      case R_IA64_PCREL60B: format = RF_INSN60; break;
6721da177e4SLinus Torvalds 		      case R_IA64_PCREL21B: format = RF_INSN21B; break;
6731da177e4SLinus Torvalds 		      case R_IA64_PCREL21M: format = RF_INSN21M; break;
6741da177e4SLinus Torvalds 		      case R_IA64_PCREL21F: format = RF_INSN21F; break;
6751da177e4SLinus Torvalds 		      default: break;
6761da177e4SLinus Torvalds 		}
6771da177e4SLinus Torvalds 		break;
6781da177e4SLinus Torvalds 
6791da177e4SLinus Torvalds 	      case RV_BDREL:
680*ac3b4328SSong Liu 		val -= (uint64_t) (in_init(mod, val) ? mod->mem[MOD_INIT_TEXT].base :
681*ac3b4328SSong Liu 				   mod->mem[MOD_TEXT].base);
6821da177e4SLinus Torvalds 		break;
6831da177e4SLinus Torvalds 
6841da177e4SLinus Torvalds 	      case RV_LTV:
6851da177e4SLinus Torvalds 		/* can link-time value relocs happen here?  */
6861da177e4SLinus Torvalds 		BUG();
6871da177e4SLinus Torvalds 		break;
6881da177e4SLinus Torvalds 
6891da177e4SLinus Torvalds 	      case RV_PCREL2:
6901da177e4SLinus Torvalds 		if (r_type == R_IA64_PCREL21BI) {
6911da177e4SLinus Torvalds 			if (!is_internal(mod, val)) {
692e088a4adSMatthew Wilcox 				printk(KERN_ERR "%s: %s reloc against "
693e088a4adSMatthew Wilcox 					"non-local symbol (%lx)\n", __func__,
694e088a4adSMatthew Wilcox 					reloc_name[r_type], (unsigned long)val);
6951da177e4SLinus Torvalds 				return -ENOEXEC;
6961da177e4SLinus Torvalds 			}
6971da177e4SLinus Torvalds 			format = RF_INSN21B;
6981da177e4SLinus Torvalds 		}
6991da177e4SLinus Torvalds 		val -= bundle(location);
7001da177e4SLinus Torvalds 		break;
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds 	      case RV_SPECIAL:
7031da177e4SLinus Torvalds 		switch (r_type) {
7041da177e4SLinus Torvalds 		      case R_IA64_IPLTMSB:
7051da177e4SLinus Torvalds 		      case R_IA64_IPLTLSB:
7061da177e4SLinus Torvalds 			val = get_fdesc(mod, get_plt(mod, location, val, &ok), &ok);
7071da177e4SLinus Torvalds 			format = RF_64LSB;
7081da177e4SLinus Torvalds 			if (r_type == R_IA64_IPLTMSB)
7091da177e4SLinus Torvalds 				format = RF_64MSB;
7101da177e4SLinus Torvalds 			break;
7111da177e4SLinus Torvalds 
7121da177e4SLinus Torvalds 		      case R_IA64_SUB:
7131da177e4SLinus Torvalds 			val = addend - sym->st_value;
7141da177e4SLinus Torvalds 			format = RF_INSN64;
7151da177e4SLinus Torvalds 			break;
7161da177e4SLinus Torvalds 
7171da177e4SLinus Torvalds 		      case R_IA64_LTOFF22X:
7181da177e4SLinus Torvalds 			if (gp_addressable(mod, val))
7191da177e4SLinus Torvalds 				val -= mod->arch.gp;
7201da177e4SLinus Torvalds 			else
7211da177e4SLinus Torvalds 				val = get_ltoff(mod, val, &ok);
7221da177e4SLinus Torvalds 			format = RF_INSN22;
7231da177e4SLinus Torvalds 			break;
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds 		      case R_IA64_LDXMOV:
7261da177e4SLinus Torvalds 			if (gp_addressable(mod, val)) {
7271da177e4SLinus Torvalds 				/* turn "ld8" into "mov": */
728d4ed8084SHarvey Harrison 				DEBUGP("%s: patching ld8 at %p to mov\n", __func__, location);
7291da177e4SLinus Torvalds 				ia64_patch((u64) location, 0x1fff80fe000UL, 0x10000000000UL);
7301da177e4SLinus Torvalds 			}
7311da177e4SLinus Torvalds 			return 0;
7321da177e4SLinus Torvalds 
7331da177e4SLinus Torvalds 		      default:
7341da177e4SLinus Torvalds 			if (reloc_name[r_type])
7351da177e4SLinus Torvalds 				printk(KERN_ERR "%s: special reloc %s not supported",
7361da177e4SLinus Torvalds 				       mod->name, reloc_name[r_type]);
7371da177e4SLinus Torvalds 			else
7381da177e4SLinus Torvalds 				printk(KERN_ERR "%s: unknown special reloc %x\n",
7391da177e4SLinus Torvalds 				       mod->name, r_type);
7401da177e4SLinus Torvalds 			return -ENOEXEC;
7411da177e4SLinus Torvalds 		}
7421da177e4SLinus Torvalds 		break;
7431da177e4SLinus Torvalds 
7441da177e4SLinus Torvalds 	      case RV_TPREL:
7451da177e4SLinus Torvalds 	      case RV_LTREL_TPREL:
7461da177e4SLinus Torvalds 	      case RV_DTPMOD:
7471da177e4SLinus Torvalds 	      case RV_LTREL_DTPMOD:
7481da177e4SLinus Torvalds 	      case RV_DTPREL:
7491da177e4SLinus Torvalds 	      case RV_LTREL_DTPREL:
7501da177e4SLinus Torvalds 		printk(KERN_ERR "%s: %s reloc not supported\n",
7511da177e4SLinus Torvalds 		       mod->name, reloc_name[r_type] ? reloc_name[r_type] : "?");
7521da177e4SLinus Torvalds 		return -ENOEXEC;
7531da177e4SLinus Torvalds 
7541da177e4SLinus Torvalds 	      default:
7551da177e4SLinus Torvalds 		printk(KERN_ERR "%s: unknown reloc %x\n", mod->name, r_type);
7561da177e4SLinus Torvalds 		return -ENOEXEC;
7571da177e4SLinus Torvalds 	}
7581da177e4SLinus Torvalds 
7591da177e4SLinus Torvalds 	if (!ok)
7601da177e4SLinus Torvalds 		return -ENOEXEC;
7611da177e4SLinus Torvalds 
762d4ed8084SHarvey Harrison 	DEBUGP("%s: [%p]<-%016lx = %s(%lx)\n", __func__, location, val,
7631da177e4SLinus Torvalds 	       reloc_name[r_type] ? reloc_name[r_type] : "?", sym->st_value + addend);
7641da177e4SLinus Torvalds 
7651da177e4SLinus Torvalds 	switch (format) {
7661da177e4SLinus Torvalds 	      case RF_INSN21B:	ok = apply_imm21b(mod, location, (int64_t) val / 16); break;
7671da177e4SLinus Torvalds 	      case RF_INSN22:	ok = apply_imm22(mod, location, val); break;
7681da177e4SLinus Torvalds 	      case RF_INSN64:	ok = apply_imm64(mod, location, val); break;
7691da177e4SLinus Torvalds 	      case RF_INSN60:	ok = apply_imm60(mod, location, (int64_t) val / 16); break;
7701da177e4SLinus Torvalds 	      case RF_32LSB:	put_unaligned(val, (uint32_t *) location); break;
7711da177e4SLinus Torvalds 	      case RF_64LSB:	put_unaligned(val, (uint64_t *) location); break;
7721da177e4SLinus Torvalds 	      case RF_32MSB:	/* ia64 Linux is little-endian... */
7731da177e4SLinus Torvalds 	      case RF_64MSB:	/* ia64 Linux is little-endian... */
7741da177e4SLinus Torvalds 	      case RF_INSN14:	/* must be within-module, i.e., resolved by "ld -r" */
7751da177e4SLinus Torvalds 	      case RF_INSN21M:	/* must be within-module, i.e., resolved by "ld -r" */
7761da177e4SLinus Torvalds 	      case RF_INSN21F:	/* must be within-module, i.e., resolved by "ld -r" */
7771da177e4SLinus Torvalds 		printk(KERN_ERR "%s: format %u needed by %s reloc is not supported\n",
7781da177e4SLinus Torvalds 		       mod->name, format, reloc_name[r_type] ? reloc_name[r_type] : "?");
7791da177e4SLinus Torvalds 		return -ENOEXEC;
7801da177e4SLinus Torvalds 
7811da177e4SLinus Torvalds 	      default:
7821da177e4SLinus Torvalds 		printk(KERN_ERR "%s: relocation %s resulted in unknown format %u\n",
7831da177e4SLinus Torvalds 		       mod->name, reloc_name[r_type] ? reloc_name[r_type] : "?", format);
7841da177e4SLinus Torvalds 		return -ENOEXEC;
7851da177e4SLinus Torvalds 	}
7861da177e4SLinus Torvalds 	return ok ? 0 : -ENOEXEC;
7871da177e4SLinus Torvalds }
7881da177e4SLinus Torvalds 
7891da177e4SLinus Torvalds int
apply_relocate_add(Elf64_Shdr * sechdrs,const char * strtab,unsigned int symindex,unsigned int relsec,struct module * mod)7901da177e4SLinus Torvalds apply_relocate_add (Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex,
7911da177e4SLinus Torvalds 		    unsigned int relsec, struct module *mod)
7921da177e4SLinus Torvalds {
7931da177e4SLinus Torvalds 	unsigned int i, n = sechdrs[relsec].sh_size / sizeof(Elf64_Rela);
7941da177e4SLinus Torvalds 	Elf64_Rela *rela = (void *) sechdrs[relsec].sh_addr;
7951da177e4SLinus Torvalds 	Elf64_Shdr *target_sec;
7961da177e4SLinus Torvalds 	int ret;
7971da177e4SLinus Torvalds 
798d4ed8084SHarvey Harrison 	DEBUGP("%s: applying section %u (%u relocs) to %u\n", __func__,
7991da177e4SLinus Torvalds 	       relsec, n, sechdrs[relsec].sh_info);
8001da177e4SLinus Torvalds 
8011da177e4SLinus Torvalds 	target_sec = sechdrs + sechdrs[relsec].sh_info;
8021da177e4SLinus Torvalds 
8031da177e4SLinus Torvalds 	if (target_sec->sh_entsize == ~0UL)
8041da177e4SLinus Torvalds 		/*
8051da177e4SLinus Torvalds 		 * If target section wasn't allocated, we don't need to relocate it.
8061da177e4SLinus Torvalds 		 * Happens, e.g., for debug sections.
8071da177e4SLinus Torvalds 		 */
8081da177e4SLinus Torvalds 		return 0;
8091da177e4SLinus Torvalds 
8101da177e4SLinus Torvalds 	if (!mod->arch.gp) {
8111da177e4SLinus Torvalds 		/*
8121da177e4SLinus Torvalds 		 * XXX Should have an arch-hook for running this after final section
8131da177e4SLinus Torvalds 		 *     addresses have been selected...
8141da177e4SLinus Torvalds 		 */
815866ba633SKeith Owens 		uint64_t gp;
816*ac3b4328SSong Liu 		struct module_memory *mod_mem;
817*ac3b4328SSong Liu 
818*ac3b4328SSong Liu 		mod_mem = &mod->mem[MOD_DATA];
819*ac3b4328SSong Liu 		if (mod_mem->size > MAX_LTOFF)
8201da177e4SLinus Torvalds 			/*
8211da177e4SLinus Torvalds 			 * This takes advantage of fact that SHF_ARCH_SMALL gets allocated
8221da177e4SLinus Torvalds 			 * at the end of the module.
8231da177e4SLinus Torvalds 			 */
824*ac3b4328SSong Liu 			gp = mod_mem->size - MAX_LTOFF / 2;
825866ba633SKeith Owens 		else
826*ac3b4328SSong Liu 			gp = mod_mem->size / 2;
827*ac3b4328SSong Liu 		gp = (uint64_t) mod_mem->base + ((gp + 7) & -8);
8281da177e4SLinus Torvalds 		mod->arch.gp = gp;
829d4ed8084SHarvey Harrison 		DEBUGP("%s: placing gp at 0x%lx\n", __func__, gp);
8301da177e4SLinus Torvalds 	}
8311da177e4SLinus Torvalds 
8321da177e4SLinus Torvalds 	for (i = 0; i < n; i++) {
8331da177e4SLinus Torvalds 		ret = do_reloc(mod, ELF64_R_TYPE(rela[i].r_info),
8341da177e4SLinus Torvalds 			       ((Elf64_Sym *) sechdrs[symindex].sh_addr
8351da177e4SLinus Torvalds 				+ ELF64_R_SYM(rela[i].r_info)),
8361da177e4SLinus Torvalds 			       rela[i].r_addend, target_sec,
8371da177e4SLinus Torvalds 			       (void *) target_sec->sh_addr + rela[i].r_offset);
8381da177e4SLinus Torvalds 		if (ret < 0)
8391da177e4SLinus Torvalds 			return ret;
8401da177e4SLinus Torvalds 	}
8411da177e4SLinus Torvalds 	return 0;
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds /*
8451da177e4SLinus Torvalds  * Modules contain a single unwind table which covers both the core and the init text
8461da177e4SLinus Torvalds  * sections but since the two are not contiguous, we need to split this table up such that
84772fdbdceSSimon Arlott  * we can register (and unregister) each "segment" separately.  Fortunately, this sounds
8481da177e4SLinus Torvalds  * more complicated than it really is.
8491da177e4SLinus Torvalds  */
8501da177e4SLinus Torvalds static void
register_unwind_table(struct module * mod)8511da177e4SLinus Torvalds register_unwind_table (struct module *mod)
8521da177e4SLinus Torvalds {
8531da177e4SLinus Torvalds 	struct unw_table_entry *start = (void *) mod->arch.unwind->sh_addr;
8541da177e4SLinus Torvalds 	struct unw_table_entry *end = start + mod->arch.unwind->sh_size / sizeof (*start);
855f2fed022SYang Guang 	struct unw_table_entry *e1, *e2, *core, *init;
8561da177e4SLinus Torvalds 	unsigned long num_init = 0, num_core = 0;
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds 	/* First, count how many init and core unwind-table entries there are.  */
8591da177e4SLinus Torvalds 	for (e1 = start; e1 < end; ++e1)
8601da177e4SLinus Torvalds 		if (in_init(mod, e1->start_offset))
8611da177e4SLinus Torvalds 			++num_init;
8621da177e4SLinus Torvalds 		else
8631da177e4SLinus Torvalds 			++num_core;
8641da177e4SLinus Torvalds 	/*
8651da177e4SLinus Torvalds 	 * Second, sort the table such that all unwind-table entries for the init and core
8661da177e4SLinus Torvalds 	 * text sections are nicely separated.  We do this with a stupid bubble sort
8671da177e4SLinus Torvalds 	 * (unwind tables don't get ridiculously huge).
8681da177e4SLinus Torvalds 	 */
8691da177e4SLinus Torvalds 	for (e1 = start; e1 < end; ++e1) {
8701da177e4SLinus Torvalds 		for (e2 = e1 + 1; e2 < end; ++e2) {
8711da177e4SLinus Torvalds 			if (e2->start_offset < e1->start_offset) {
872f2fed022SYang Guang 				swap(*e1, *e2);
8731da177e4SLinus Torvalds 			}
8741da177e4SLinus Torvalds 		}
8751da177e4SLinus Torvalds 	}
8761da177e4SLinus Torvalds 	/*
8771da177e4SLinus Torvalds 	 * Third, locate the init and core segments in the unwind table:
8781da177e4SLinus Torvalds 	 */
8791da177e4SLinus Torvalds 	if (in_init(mod, start->start_offset)) {
8801da177e4SLinus Torvalds 		init = start;
8811da177e4SLinus Torvalds 		core = start + num_init;
8821da177e4SLinus Torvalds 	} else {
8831da177e4SLinus Torvalds 		core = start;
8841da177e4SLinus Torvalds 		init = start + num_core;
8851da177e4SLinus Torvalds 	}
8861da177e4SLinus Torvalds 
887d4ed8084SHarvey Harrison 	DEBUGP("%s: name=%s, gp=%lx, num_init=%lu, num_core=%lu\n", __func__,
8881da177e4SLinus Torvalds 	       mod->name, mod->arch.gp, num_init, num_core);
8891da177e4SLinus Torvalds 
8901da177e4SLinus Torvalds 	/*
8911da177e4SLinus Torvalds 	 * Fourth, register both tables (if not empty).
8921da177e4SLinus Torvalds 	 */
8931da177e4SLinus Torvalds 	if (num_core > 0) {
8941da177e4SLinus Torvalds 		mod->arch.core_unw_table = unw_add_unwind_table(mod->name, 0, mod->arch.gp,
8951da177e4SLinus Torvalds 								core, core + num_core);
896d4ed8084SHarvey Harrison 		DEBUGP("%s:  core: handle=%p [%p-%p)\n", __func__,
8971da177e4SLinus Torvalds 		       mod->arch.core_unw_table, core, core + num_core);
8981da177e4SLinus Torvalds 	}
8991da177e4SLinus Torvalds 	if (num_init > 0) {
9001da177e4SLinus Torvalds 		mod->arch.init_unw_table = unw_add_unwind_table(mod->name, 0, mod->arch.gp,
9011da177e4SLinus Torvalds 								init, init + num_init);
902d4ed8084SHarvey Harrison 		DEBUGP("%s:  init: handle=%p [%p-%p)\n", __func__,
9031da177e4SLinus Torvalds 		       mod->arch.init_unw_table, init, init + num_init);
9041da177e4SLinus Torvalds 	}
9051da177e4SLinus Torvalds }
9061da177e4SLinus Torvalds 
9071da177e4SLinus Torvalds int
module_finalize(const Elf_Ehdr * hdr,const Elf_Shdr * sechdrs,struct module * mod)9081da177e4SLinus Torvalds module_finalize (const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod)
9091da177e4SLinus Torvalds {
91099e729bdSSergei Trofimovich 	struct mod_arch_specific *mas = &mod->arch;
91199e729bdSSergei Trofimovich 
912d4ed8084SHarvey Harrison 	DEBUGP("%s: init: entry=%p\n", __func__, mod->init);
91399e729bdSSergei Trofimovich 	if (mas->unwind)
9141da177e4SLinus Torvalds 		register_unwind_table(mod);
91599e729bdSSergei Trofimovich 
91699e729bdSSergei Trofimovich 	/*
91799e729bdSSergei Trofimovich 	 * ".opd" was already relocated to the final destination. Store
91899e729bdSSergei Trofimovich 	 * it's address for use in symbolizer.
91999e729bdSSergei Trofimovich 	 */
92099e729bdSSergei Trofimovich 	mas->opd_addr = (void *)mas->opd->sh_addr;
92199e729bdSSergei Trofimovich 	mas->opd_size = mas->opd->sh_size;
92299e729bdSSergei Trofimovich 
92399e729bdSSergei Trofimovich 	/*
92499e729bdSSergei Trofimovich 	 * Module relocation was already done at this point. Section
92599e729bdSSergei Trofimovich 	 * headers are about to be deleted. Wipe out load-time context.
92699e729bdSSergei Trofimovich 	 */
92799e729bdSSergei Trofimovich 	mas->core_plt = NULL;
92899e729bdSSergei Trofimovich 	mas->init_plt = NULL;
92999e729bdSSergei Trofimovich 	mas->got = NULL;
93099e729bdSSergei Trofimovich 	mas->opd = NULL;
93199e729bdSSergei Trofimovich 	mas->unwind = NULL;
93299e729bdSSergei Trofimovich 	mas->gp = 0;
93399e729bdSSergei Trofimovich 	mas->next_got_entry = 0;
93499e729bdSSergei Trofimovich 
9351da177e4SLinus Torvalds 	return 0;
9361da177e4SLinus Torvalds }
9371da177e4SLinus Torvalds 
9381da177e4SLinus Torvalds void
module_arch_cleanup(struct module * mod)9391da177e4SLinus Torvalds module_arch_cleanup (struct module *mod)
9401da177e4SLinus Torvalds {
941c5e5c48cSchenzefeng 	if (mod->arch.init_unw_table) {
9421da177e4SLinus Torvalds 		unw_remove_unwind_table(mod->arch.init_unw_table);
943c5e5c48cSchenzefeng 		mod->arch.init_unw_table = NULL;
944c5e5c48cSchenzefeng 	}
945c5e5c48cSchenzefeng 	if (mod->arch.core_unw_table) {
9461da177e4SLinus Torvalds 		unw_remove_unwind_table(mod->arch.core_unw_table);
947c5e5c48cSchenzefeng 		mod->arch.core_unw_table = NULL;
948c5e5c48cSchenzefeng 	}
9491da177e4SLinus Torvalds }
9508e307888SSergey Senozhatsky 
dereference_module_function_descriptor(struct module * mod,void * ptr)9518e307888SSergey Senozhatsky void *dereference_module_function_descriptor(struct module *mod, void *ptr)
9528e307888SSergey Senozhatsky {
95399e729bdSSergei Trofimovich 	struct mod_arch_specific *mas = &mod->arch;
9548e307888SSergey Senozhatsky 
95599e729bdSSergei Trofimovich 	if (ptr < mas->opd_addr || ptr >= mas->opd_addr + mas->opd_size)
9568e307888SSergey Senozhatsky 		return ptr;
9578e307888SSergey Senozhatsky 
9588e307888SSergey Senozhatsky 	return dereference_function_descriptor(ptr);
9598e307888SSergey Senozhatsky }
960