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