📄 module.c
字号:
/* Kernel dynamically loadable module help for PARISC. * * The best reference for this stuff is probably the Processor- * Specific ELF Supplement for PA-RISC: * http://ftp.parisc-linux.org/docs/elf-pa-hp.pdf * * Linux/PA-RISC Project (http://www.parisc-linux.org/) * Copyright (C) 2003 Randolph Chung <tausq at debian . org> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/moduleloader.h>#include <linux/elf.h>#include <linux/vmalloc.h>#include <linux/fs.h>#include <linux/string.h>#include <linux/kernel.h>#if 0#define DEBUGP printk#else#define DEBUGP(fmt...)#endif#define CHECK_RELOC(val, bits) \ if ( ( !((val) & (1<<((bits)-1))) && ((val)>>(bits)) != 0 ) || \ ( ((val) & (1<<((bits)-1))) && ((val)>>(bits)) != (((__typeof__(val))(~0))>>((bits)+2)))) { \ printk(KERN_ERR "module %s relocation of symbol %s is out of range (0x%lx in %d bits)\n", \ me->name, strtab + sym->st_name, (unsigned long)val, bits); \ return -ENOEXEC; \ }/* Maximum number of GOT entries. We use a long displacement ldd from * the bottom of the table, which has a maximum signed displacement of * 0x3fff; however, since we're only going forward, this becomes * 0x1fff, and thus, since each GOT entry is 8 bytes long we can have * at most 1023 entries */#define MAX_GOTS 1023/* three functions to determine where in the module core * or init pieces the location is */static inline int is_init(struct module *me, void *loc){ return (loc >= me->module_init && loc <= (me->module_init + me->init_size));}static inline int is_core(struct module *me, void *loc){ return (loc >= me->module_core && loc <= (me->module_core + me->core_size));}static inline int is_local(struct module *me, void *loc){ return is_init(me, loc) || is_core(me, loc);}#ifndef __LP64__struct got_entry { Elf32_Addr addr;};#define Elf_Fdesc Elf32_Fdescstruct stub_entry { Elf32_Word insns[2]; /* each stub entry has two insns */};#elsestruct got_entry { Elf64_Addr addr;};#define Elf_Fdesc Elf64_Fdescstruct stub_entry { Elf64_Word insns[4]; /* each stub entry has four insns */};#endif/* Field selection types defined by hppa */#define rnd(x) (((x)+0x1000)&~0x1fff)/* fsel: full 32 bits */#define fsel(v,a) ((v)+(a))/* lsel: select left 21 bits */#define lsel(v,a) (((v)+(a))>>11)/* rsel: select right 11 bits */#define rsel(v,a) (((v)+(a))&0x7ff)/* lrsel with rounding of addend to nearest 8k */#define lrsel(v,a) (((v)+rnd(a))>>11)/* rrsel with rounding of addend to nearest 8k */#define rrsel(v,a) ((((v)+rnd(a))&0x7ff)+((a)-rnd(a)))#define mask(x,sz) ((x) & ~((1<<(sz))-1))/* The reassemble_* functions prepare an immediate value for insertion into an opcode. pa-risc uses all sorts of weird bitfields in the instruction to hold the value. */static inline int reassemble_14(int as14){ return (((as14 & 0x1fff) << 1) | ((as14 & 0x2000) >> 13));}static inline int reassemble_17(int as17){ return (((as17 & 0x10000) >> 16) | ((as17 & 0x0f800) << 5) | ((as17 & 0x00400) >> 8) | ((as17 & 0x003ff) << 3));}static inline int reassemble_21(int as21){ return (((as21 & 0x100000) >> 20) | ((as21 & 0x0ffe00) >> 8) | ((as21 & 0x000180) << 7) | ((as21 & 0x00007c) << 14) | ((as21 & 0x000003) << 12));}static inline int reassemble_22(int as22){ return (((as22 & 0x200000) >> 21) | ((as22 & 0x1f0000) << 5) | ((as22 & 0x00f800) << 5) | ((as22 & 0x000400) >> 8) | ((as22 & 0x0003ff) << 3));}void *module_alloc(unsigned long size){ if (size == 0) return NULL; return vmalloc(size);}#ifndef __LP64__static inline unsigned long count_gots(const Elf_Rela *rela, unsigned long n){ return 0;}static inline unsigned long count_fdescs(const Elf_Rela *rela, unsigned long n){ return 0;}static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n){ unsigned long cnt = 0; for (; n > 0; n--, rela++) { switch (ELF32_R_TYPE(rela->r_info)) { case R_PARISC_PCREL17F: case R_PARISC_PCREL22F: cnt++; } } return cnt;}#elsestatic inline unsigned long count_gots(const Elf_Rela *rela, unsigned long n){ unsigned long cnt = 0; for (; n > 0; n--, rela++) { switch (ELF64_R_TYPE(rela->r_info)) { case R_PARISC_LTOFF21L: case R_PARISC_LTOFF14R: case R_PARISC_PCREL22F: cnt++; } } return cnt;}static inline unsigned long count_fdescs(const Elf_Rela *rela, unsigned long n){ unsigned long cnt = 0; for (; n > 0; n--, rela++) { switch (ELF64_R_TYPE(rela->r_info)) { case R_PARISC_FPTR64: cnt++; } } return cnt;}static inline unsigned long count_stubs(const Elf_Rela *rela, unsigned long n){ unsigned long cnt = 0; for (; n > 0; n--, rela++) { switch (ELF64_R_TYPE(rela->r_info)) { case R_PARISC_PCREL22F: cnt++; } } return cnt;}#endif/* Free memory returned from module_alloc */void module_free(struct module *mod, void *module_region){ vfree(module_region); /* FIXME: If module_region == mod->init_region, trim exception table entries. */}#define CONST int module_frob_arch_sections(CONST Elf_Ehdr *hdr, CONST Elf_Shdr *sechdrs, CONST char *secstrings, struct module *me){ unsigned long gots = 0, fdescs = 0, stubs = 0, init_stubs = 0; unsigned int i; for (i = 1; i < hdr->e_shnum; i++) { const Elf_Rela *rels = (void *)hdr + sechdrs[i].sh_offset; unsigned long nrels = sechdrs[i].sh_size / sizeof(*rels); if (sechdrs[i].sh_type != SHT_RELA) continue; /* some of these are not relevant for 32-bit/64-bit * we leave them here to make the code common. the * compiler will do its thing and optimize out the * stuff we don't need */ gots += count_gots(rels, nrels); fdescs += count_fdescs(rels, nrels); if(strncmp(secstrings + sechdrs[i].sh_name, ".rela.init", 10) == 0) init_stubs += count_stubs(rels, nrels); else stubs += count_stubs(rels, nrels); } /* align things a bit */ me->core_size = ALIGN(me->core_size, 16); me->arch.got_offset = me->core_size; me->core_size += gots * sizeof(struct got_entry); me->core_size = ALIGN(me->core_size, 16); me->arch.fdesc_offset = me->core_size; me->core_size += fdescs * sizeof(Elf_Fdesc); me->core_size = ALIGN(me->core_size, 16); me->arch.stub_offset = me->core_size; me->core_size += stubs * sizeof(struct stub_entry); me->init_size = ALIGN(me->init_size, 16); me->arch.init_stub_offset = me->init_size; me->init_size += init_stubs * sizeof(struct stub_entry); me->arch.got_max = gots; me->arch.fdesc_max = fdescs; me->arch.stub_max = stubs; me->arch.init_stub_max = init_stubs; return 0;}#ifdef __LP64__static Elf64_Word get_got(struct module *me, unsigned long value, long addend){ unsigned int i; struct got_entry *got; value += addend; BUG_ON(value == 0); got = me->module_core + me->arch.got_offset; for (i = 0; got[i].addr; i++) if (got[i].addr == value) goto out; BUG_ON(++me->arch.got_count > me->arch.got_max); got[i].addr = value; out: DEBUGP("GOT ENTRY %d[%x] val %lx\n", i, i*sizeof(struct got_entry), value); return i * sizeof(struct got_entry);}#endif /* __LP64__ */#ifdef __LP64__static Elf_Addr get_fdesc(struct module *me, unsigned long value){ Elf_Fdesc *fdesc = me->module_core + me->arch.fdesc_offset; if (!value) { printk(KERN_ERR "%s: zero OPD requested!\n", me->name); return 0; } /* Look for existing fdesc entry. */ while (fdesc->addr) { if (fdesc->addr == value) return (Elf_Addr)fdesc; fdesc++; } BUG_ON(++me->arch.fdesc_count > me->arch.fdesc_max); /* Create new one */ fdesc->addr = value; fdesc->gp = (Elf_Addr)me->module_core + me->arch.got_offset; return (Elf_Addr)fdesc;}#endif /* __LP64__ */static Elf_Addr get_stub(struct module *me, unsigned long value, long addend, int millicode, int init_section){ unsigned long i; struct stub_entry *stub; if(init_section) { i = me->arch.init_stub_count++; BUG_ON(me->arch.init_stub_count > me->arch.init_stub_max); stub = me->module_init + me->arch.init_stub_offset + i * sizeof(struct stub_entry); } else { i = me->arch.stub_count++; BUG_ON(me->arch.stub_count > me->arch.stub_max); stub = me->module_core + me->arch.stub_offset + i * sizeof(struct stub_entry); }#ifndef __LP64__/* for 32-bit the stub looks like this: * ldil L'XXX,%r1 * be,n R'XXX(%sr4,%r1) */ //value = *(unsigned long *)((value + addend) & ~3); /* why? */ stub->insns[0] = 0x20200000; /* ldil L'XXX,%r1 */ stub->insns[1] = 0xe0202002; /* be,n R'XXX(%sr4,%r1) */ stub->insns[0] |= reassemble_21(lrsel(value, addend)); stub->insns[1] |= reassemble_17(rrsel(value, addend) / 4);#else/* for 64-bit we have two kinds of stubs: * for normal function calls: * ldd 0(%dp),%dp * ldd 10(%dp), %r1 * bve (%r1) * ldd 18(%dp), %dp * * for millicode: * ldil 0, %r1
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -