/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved. * Copyright (C) 2013 Imagination Technologies Ltd. * * VPE support module for loading a MIPS SP program into VPE1. The SP * environment is rather simple since there are no TLBs. It needs * to be relocatable (or partially linked). Initialize your stack in * the startup-code. The loader looks for the symbol __start and sets * up the execution to resume from there. To load and run, simply do * a cat SP 'binary' to the /dev/vpe1 device. */#include <linux/kernel.h>#include <linux/device.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/list.h>#include <linux/vmalloc.h>#include <linux/elf.h>#include <linux/seq_file.h>#include <linux/string.h>#include <linux/syscalls.h>#include <linux/moduleloader.h>#include <linux/interrupt.h>#include <linux/poll.h>#include <linux/memblock.h>#include <asm/mipsregs.h>#include <asm/mipsmtregs.h>#include <asm/cacheflush.h>#include <linux/atomic.h>#include <asm/mips_mt.h>#include <asm/processor.h>#include <asm/vpe.h> #ifndef ARCH_SHF_SMALL#define ARCH_SHF_SMALL 0#endif /* If this is set, the section belongs in the init part of the module */#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) struct vpe_control vpecontrol = { .vpe_list_lock = __SPIN_LOCK_UNLOCKED(vpe_list_lock), .vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list), .tc_list_lock = __SPIN_LOCK_UNLOCKED(tc_list_lock), .tc_list = LIST_HEAD_INIT(vpecontrol.tc_list)}; /* get the vpe associated with this minor */struct vpe *get_vpe(int minor){ struct vpe *res, *v; if (!cpu_has_mipsmt) return NULL; res = NULL; spin_lock(&vpecontrol.vpe_list_lock); list_for_each_entry(v, &vpecontrol.vpe_list, list) { if (v->minor == VPE_MODULE_MINOR) { res = v; break; } } spin_unlock(&vpecontrol.vpe_list_lock); return res;} /* get the vpe associated with this minor */struct tc *get_tc(int index){ struct tc *res, *t; res = NULL; spin_lock(&vpecontrol.tc_list_lock); list_for_each_entry(t, &vpecontrol.tc_list, list) { if (t->index == index) { res = t; break; } } spin_unlock(&vpecontrol.tc_list_lock); return res;} /* allocate a vpe and associate it with this minor (or index) */struct vpe *alloc_vpe(int minor){ struct vpe *v; v = kzalloc_obj(struct vpe); if (v == NULL) goto out; INIT_LIST_HEAD(&v->tc); spin_lock(&vpecontrol.vpe_list_lock); list_add_tail(&v->list, &vpecontrol.vpe_list); spin_unlock(&vpecontrol.vpe_list_lock); INIT_LIST_HEAD(&v->notify); v->minor = VPE_MODULE_MINOR; out: return v;} /* allocate a tc. At startup only tc0 is running, all other can be halted. */struct tc *alloc_tc(int index){ struct tc *tc; tc = kzalloc_obj(struct tc); if (tc == NULL) goto out; INIT_LIST_HEAD(&tc->tc); tc->index = index; spin_lock(&vpecontrol.tc_list_lock); list_add_tail(&tc->list, &vpecontrol.tc_list); spin_unlock(&vpecontrol.tc_list_lock); out: return tc;} /* clean up and free everything */void release_vpe(struct vpe *v){ list_del(&v->list); if (v->load_addr) release_progmem(v->load_addr); kfree(v);} /* Find some VPE program space */void *alloc_progmem(unsigned long len){ void *addr; #ifdef CONFIG_MIPS_VPE_LOADER_TOM /* * This means you must tell Linux to use less memory than you * physically have, for example by passing a mem= boot argument. */ addr = pfn_to_kaddr(max_low_pfn); memset(addr, 0, len);#else /* simple grab some mem for now */ addr = kzalloc(len, GFP_KERNEL);#endif return addr;} void release_progmem(void *ptr){#ifndef CONFIG_MIPS_VPE_LOADER_TOM kfree(ptr);#endif} /* Update size with this section: return offset. */static long get_offset(unsigned long *size, Elf_Shdr *sechdr){ long ret; ret = ALIGN(*size, sechdr->sh_addralign ? : 1); *size = ret + sechdr->sh_size; return ret;} /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld might -- code, read-only data, read-write data, small data. Tally sizes, and place the offsets into sh_entsize fields: high bit means it belongs in init. */static void layout_sections(struct module *mod, const Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings){ static unsigned long const masks[][2] = { /* NOTE: all executable code must be the first section * in this array; otherwise modify the text_size * finder in the two loops below */ {SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL}, {SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL}, {SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL}, {ARCH_SHF_SMALL | SHF_ALLOC, 0} }; unsigned int m, i; for (i = 0; i < hdr->e_shnum; i++) sechdrs[i].sh_entsize = ~0UL; for (m = 0; m < ARRAY_SIZE(masks); ++m) { for (i = 0; i < hdr->e_shnum; ++i) { Elf_Shdr *s = &sechdrs[i]; struct module_memory *mod_mem; mod_mem = &mod->mem[MOD_TEXT]; if ((s->sh_flags & masks[m][0]) != masks[m][0] || (s->sh_flags & masks[m][1]) || s->sh_entsize != ~0UL) continue; s->sh_entsize = get_offset((unsigned long *)&mod_mem->size, s); } }} /* from module-elf32.c, but subverted a little */ struct mips_hi16 { struct mips_hi16 *next; Elf32_Addr *addr; Elf32_Addr value;}; static struct mips_hi16 *mips_hi16_list;static unsigned int gp_offs, gp_addr; static int apply_r_mips_none(struct module *me, uint32_t *location, Elf32_Addr v){ return 0;} static int apply_r_mips_gprel16(struct module *me, uint32_t *location, Elf32_Addr v){ int rel; if (!(*location & 0xffff)) { rel = (int)v - gp_addr; } else { /* .sbss + gp(relative) + offset */ /* kludge! */ rel = (int)(short)((int)v + gp_offs + (int)(short)(*location & 0xffff) - gp_addr); } if ((rel > 32768) || (rel < -32768)) { pr_debug("VPE loader: apply_r_mips_gprel16: relative address 0x%x out of range of gp register\n", rel); return -ENOEXEC; } *location = (*location & 0xffff0000) | (rel & 0xffff); return 0;} static int apply_r_mips_pc16(struct module *me, uint32_t *location, Elf32_Addr v){ int rel; rel = (((unsigned int)v - (unsigned int)location)); rel >>= 2; /* because the offset is in _instructions_ not bytes. */ rel -= 1; /* and one instruction less due to the branch delay slot. */ if ((rel > 32768) || (rel < -32768)) { pr_debug("VPE loader: apply_r_mips_pc16: relative address out of range 0x%x\n", rel); return -ENOEXEC; } *location = (*location & 0xffff0000) | (rel & 0xffff); return 0;} static int apply_r_mips_32(struct module *me, uint32_t *location, Elf32_Addr v){ *location += v; return 0;} static int apply_r_mips_26(struct module *me, uint32_t *location, Elf32_Addr v){ if (v % 4) { pr_debug("VPE loader: apply_r_mips_26: unaligned relocation\n"); return -ENOEXEC; } /* * Not desperately convinced this is a good check of an overflow condition * anyway. But it gets in the way of handling undefined weak symbols which * we want to set to zero. * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { * printk(KERN_ERR * "module %s: relocation overflow\n", * me->name); * return -ENOEXEC; * } */ *location = (*location & ~0x03ffffff) | ((*location + (v >> 2)) & 0x03ffffff); return 0;} static int apply_r_mips_hi16(struct module *me, uint32_t *location, Elf32_Addr v){ struct mips_hi16 *n; /* * We cannot relocate this one now because we don't know the value of * the carry we need to add. Save the information, and let LO16 do the * actual relocation. */ n = kmalloc_obj(*n); if (!n) return -ENOMEM; n->addr = location; n->value = v; n->next = mips_hi16_list; mips_hi16_list = n; return 0;} static int apply_r_mips_lo16(struct module *me, uint32_t *location, Elf32_Addr v){ unsigned long insnlo = *location; Elf32_Addr val, vallo; struct mips_hi16 *l, *next; /* Sign extend the addend we extract from the lo insn. */ vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; if (mips_hi16_list != NULL) { l = mips_hi16_list; while (l != NULL) { unsigned long insn; /* * The value for the HI16 had best be the same. */ if (v != l->value) { pr_debug("VPE loader: apply_r_mips_lo16/hi16: inconsistent value information\n"); goto out_free; } /* * Do the HI16 relocation. Note that we actually don't * need to know anything about the LO16 itself, except * where to find the low 16 bits of the addend needed * by the LO16. */ insn = *l->addr; val = ((insn & 0xffff) << 16) + vallo; val += v; /* * Account for the sign extension that will happen in * the low bits. */ val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff; insn = (insn & ~0xffff) | val; *l->addr = insn; next = l->next; kfree(l); l = next; } mips_hi16_list = NULL; } /* * Ok, we're done with the HI16 relocs. Now deal with the LO16. */ val = v + vallo; insnlo = (insnlo & ~0xffff) | (val & 0xffff); *location = insnlo; return 0; out_free: while (l != NULL) { next = l->next; kfree(l); l = next; } mips_hi16_list = NULL; return -ENOEXEC;} static int (*reloc_handlers[]) (struct module *me, uint32_t *location, Elf32_Addr v) = { [R_MIPS_NONE] = apply_r_mips_none, [R_MIPS_32] = apply_r_mips_32, [R_MIPS_26] = apply_r_mips_26, [R_MIPS_HI16] = apply_r_mips_hi16, [R_MIPS_LO16] = apply_r_mips_lo16, [R_MIPS_GPREL16] = apply_r_mips_gprel16, [R_MIPS_PC16] = apply_r_mips_pc16}; static char *rstrs[] = { [R_MIPS_NONE] = "MIPS_NONE", [R_MIPS_32] = "MIPS_32", [R_MIPS_26] = "MIPS_26", [R_MIPS_HI16] = "MIPS_HI16", [R_MIPS_LO16] = "MIPS_LO16", [R_MIPS_GPREL16] = "MIPS_GPREL16", [R_MIPS_PC16] = "MIPS_PC16"}; static int apply_relocations(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me){ Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr; Elf32_Sym *sym; uint32_t *location; unsigned int i; Elf32_Addr v; int res; for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { Elf32_Word r_info = rel[i].r_info; /* This is where to make the change */ location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset; /* This is the symbol it is referring to */ sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + ELF32_R_SYM(r_info); if (!sym->st_value) { pr_debug("%s: undefined weak symbol %s\n", me->name, strtab + sym->st_name); /* just print the warning, dont barf */ } v = sym->st_value; res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v); if (res) { char *r = rstrs[ELF32_R_TYPE(r_info)]; pr_warn("VPE loader: .text+0x%x relocation type %s for symbol \"%s\" failed\n", rel[i].r_offset, r ? r : "UNKNOWN", strtab + sym->st_name); return res; } } return 0;} static inline void save_gp_address(unsigned int secbase, unsigned int rel){ gp_addr = secbase + rel; gp_offs = gp_addr - (secbase & 0xffff0000);}/* end module-elf32.c */ /* Change all symbols so that sh_value encodes the pointer directly. */static void simplify_symbols(Elf_Shdr *sechdrs, unsigned int symindex, const char *strtab, const char *secstrings, unsigned int nsecs, struct module *mod){ Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; unsigned long secbase, bssbase = 0; unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym); int size; /* find the .bss section for COMMON symbols */ for (i = 0; i < nsecs; i++) { if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) { bssbase = sechdrs[i].sh_addr; break; } } for (i = 1; i < n; i++) { switch (sym[i].st_shndx) { case SHN_COMMON: /* Allocate space for the symbol in the .bss section. st_value is currently size.Lines 1–500 of 900. Load more
/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 2004, 2005 MIPS Technologies, Inc. All rights reserved. * Copyright (C) 2013 Imagination Technologies Ltd. * * VPE support module for loading a MIPS SP program into VPE1. The SP * environment is rather simple since there are no TLBs. It needs * to be relocatable (or partially linked). Initialize your stack in * the startup-code. The loader looks for the symbol __start and sets * up the execution to resume from there. To load and run, simply do * a cat SP 'binary' to the /dev/vpe1 device. */ #include <linux/kernel.h> #include <linux/device.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/list.h> #include <linux/vmalloc.h> #include <linux/elf.h> #include <linux/seq_file.h> #include <linux/string.h> #include <linux/syscalls.h> #include <linux/moduleloader.h> #include <linux/interrupt.h> #include <linux/poll.h> #include <linux/memblock.h> #include <asm/mipsregs.h> #include <asm/mipsmtregs.h> #include <asm/cacheflush.h> #include <linux/atomic.h> #include <asm/mips_mt.h> #include <asm/processor.h> #include <asm/vpe.h> #ifndef ARCH_SHF_SMALL #define ARCH_SHF_SMALL 0 #endif /* If this is set, the section belongs in the init part of the module */ #define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1)) struct vpe_control vpecontrol = { .vpe_list_lock = __SPIN_LOCK_UNLOCKED(vpe_list_lock), .vpe_list = LIST_HEAD_INIT(vpecontrol.vpe_list), .tc_list_lock = __SPIN_LOCK_UNLOCKED(tc_list_lock), .tc_list = LIST_HEAD_INIT(vpecontrol.tc_list) }; /* get the vpe associated with this minor */ struct vpe *get_vpe(int minor) { struct vpe *res, *v; if (!cpu_has_mipsmt) return NULL; res = NULL; spin_lock(&vpecontrol.vpe_list_lock); list_for_each_entry(v, &vpecontrol.vpe_list, list) { if (v->minor == VPE_MODULE_MINOR) { res = v; break; } } spin_unlock(&vpecontrol.vpe_list_lock); return res; } /* get the vpe associated with this minor */ struct tc *get_tc(int index) { struct tc *res, *t; res = NULL; spin_lock(&vpecontrol.tc_list_lock); list_for_each_entry(t, &vpecontrol.tc_list, list) { if (t->index == index) { res = t; break; } } spin_unlock(&vpecontrol.tc_list_lock); return res; } /* allocate a vpe and associate it with this minor (or index) */ struct vpe *alloc_vpe(int minor) { struct vpe *v; v = kzalloc_obj(struct vpe); if (v == NULL) goto out; INIT_LIST_HEAD(&v->tc); spin_lock(&vpecontrol.vpe_list_lock); list_add_tail(&v->list, &vpecontrol.vpe_list); spin_unlock(&vpecontrol.vpe_list_lock); INIT_LIST_HEAD(&v->notify); v->minor = VPE_MODULE_MINOR; out: return v; } /* allocate a tc. At startup only tc0 is running, all other can be halted. */ struct tc *alloc_tc(int index) { struct tc *tc; tc = kzalloc_obj(struct tc); if (tc == NULL) goto out; INIT_LIST_HEAD(&tc->tc); tc->index = index; spin_lock(&vpecontrol.tc_list_lock); list_add_tail(&tc->list, &vpecontrol.tc_list); spin_unlock(&vpecontrol.tc_list_lock); out: return tc; } /* clean up and free everything */ void release_vpe(struct vpe *v) { list_del(&v->list); if (v->load_addr) release_progmem(v->load_addr); kfree(v); } /* Find some VPE program space */ void *alloc_progmem(unsigned long len) { void *addr; #ifdef CONFIG_MIPS_VPE_LOADER_TOM /* * This means you must tell Linux to use less memory than you * physically have, for example by passing a mem= boot argument. */ addr = pfn_to_kaddr(max_low_pfn); memset(addr, 0, len); #else /* simple grab some mem for now */ addr = kzalloc(len, GFP_KERNEL); #endif return addr; } void release_progmem(void *ptr) { #ifndef CONFIG_MIPS_VPE_LOADER_TOM kfree(ptr); #endif } /* Update size with this section: return offset. */ static long get_offset(unsigned long *size, Elf_Shdr *sechdr) { long ret; ret = ALIGN(*size, sechdr->sh_addralign ? : 1); *size = ret + sechdr->sh_size; return ret; } /* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld might -- code, read-only data, read-write data, small data. Tally sizes, and place the offsets into sh_entsize fields: high bit means it belongs in init. */ static void layout_sections(struct module *mod, const Elf_Ehdr *hdr, Elf_Shdr *sechdrs, const char *secstrings) { static unsigned long const masks[][2] = { /* NOTE: all executable code must be the first section * in this array; otherwise modify the text_size * finder in the two loops below */ {SHF_EXECINSTR | SHF_ALLOC, ARCH_SHF_SMALL}, {SHF_ALLOC, SHF_WRITE | ARCH_SHF_SMALL}, {SHF_WRITE | SHF_ALLOC, ARCH_SHF_SMALL}, {ARCH_SHF_SMALL | SHF_ALLOC, 0} }; unsigned int m, i; for (i = 0; i < hdr->e_shnum; i++) sechdrs[i].sh_entsize = ~0UL; for (m = 0; m < ARRAY_SIZE(masks); ++m) { for (i = 0; i < hdr->e_shnum; ++i) { Elf_Shdr *s = &sechdrs[i]; struct module_memory *mod_mem; mod_mem = &mod->mem[MOD_TEXT]; if ((s->sh_flags & masks[m][0]) != masks[m][0] || (s->sh_flags & masks[m][1]) || s->sh_entsize != ~0UL) continue; s->sh_entsize = get_offset((unsigned long *)&mod_mem->size, s); } } } /* from module-elf32.c, but subverted a little */ struct mips_hi16 { struct mips_hi16 *next; Elf32_Addr *addr; Elf32_Addr value; }; static struct mips_hi16 *mips_hi16_list; static unsigned int gp_offs, gp_addr; static int apply_r_mips_none(struct module *me, uint32_t *location, Elf32_Addr v) { return 0; } static int apply_r_mips_gprel16(struct module *me, uint32_t *location, Elf32_Addr v) { int rel; if (!(*location & 0xffff)) { rel = (int)v - gp_addr; } else { /* .sbss + gp(relative) + offset */ /* kludge! */ rel = (int)(short)((int)v + gp_offs + (int)(short)(*location & 0xffff) - gp_addr); } if ((rel > 32768) || (rel < -32768)) { pr_debug("VPE loader: apply_r_mips_gprel16: relative address 0x%x out of range of gp register\n", rel); return -ENOEXEC; } *location = (*location & 0xffff0000) | (rel & 0xffff); return 0; } static int apply_r_mips_pc16(struct module *me, uint32_t *location, Elf32_Addr v) { int rel; rel = (((unsigned int)v - (unsigned int)location)); rel >>= 2; /* because the offset is in _instructions_ not bytes. */ rel -= 1; /* and one instruction less due to the branch delay slot. */ if ((rel > 32768) || (rel < -32768)) { pr_debug("VPE loader: apply_r_mips_pc16: relative address out of range 0x%x\n", rel); return -ENOEXEC; } *location = (*location & 0xffff0000) | (rel & 0xffff); return 0; } static int apply_r_mips_32(struct module *me, uint32_t *location, Elf32_Addr v) { *location += v; return 0; } static int apply_r_mips_26(struct module *me, uint32_t *location, Elf32_Addr v) { if (v % 4) { pr_debug("VPE loader: apply_r_mips_26: unaligned relocation\n"); return -ENOEXEC; } /* * Not desperately convinced this is a good check of an overflow condition * anyway. But it gets in the way of handling undefined weak symbols which * we want to set to zero. * if ((v & 0xf0000000) != (((unsigned long)location + 4) & 0xf0000000)) { * printk(KERN_ERR * "module %s: relocation overflow\n", * me->name); * return -ENOEXEC; * } */ *location = (*location & ~0x03ffffff) | ((*location + (v >> 2)) & 0x03ffffff); return 0; } static int apply_r_mips_hi16(struct module *me, uint32_t *location, Elf32_Addr v) { struct mips_hi16 *n; /* * We cannot relocate this one now because we don't know the value of * the carry we need to add. Save the information, and let LO16 do the * actual relocation. */ n = kmalloc_obj(*n); if (!n) return -ENOMEM; n->addr = location; n->value = v; n->next = mips_hi16_list; mips_hi16_list = n; return 0; } static int apply_r_mips_lo16(struct module *me, uint32_t *location, Elf32_Addr v) { unsigned long insnlo = *location; Elf32_Addr val, vallo; struct mips_hi16 *l, *next; /* Sign extend the addend we extract from the lo insn. */ vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; if (mips_hi16_list != NULL) { l = mips_hi16_list; while (l != NULL) { unsigned long insn; /* * The value for the HI16 had best be the same. */ if (v != l->value) { pr_debug("VPE loader: apply_r_mips_lo16/hi16: inconsistent value information\n"); goto out_free; } /* * Do the HI16 relocation. Note that we actually don't * need to know anything about the LO16 itself, except * where to find the low 16 bits of the addend needed * by the LO16. */ insn = *l->addr; val = ((insn & 0xffff) << 16) + vallo; val += v; /* * Account for the sign extension that will happen in * the low bits. */ val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff; insn = (insn & ~0xffff) | val; *l->addr = insn; next = l->next; kfree(l); l = next; } mips_hi16_list = NULL; } /* * Ok, we're done with the HI16 relocs. Now deal with the LO16. */ val = v + vallo; insnlo = (insnlo & ~0xffff) | (val & 0xffff); *location = insnlo; return 0; out_free: while (l != NULL) { next = l->next; kfree(l); l = next; } mips_hi16_list = NULL; return -ENOEXEC; } static int (*reloc_handlers[]) (struct module *me, uint32_t *location, Elf32_Addr v) = { [R_MIPS_NONE] = apply_r_mips_none, [R_MIPS_32] = apply_r_mips_32, [R_MIPS_26] = apply_r_mips_26, [R_MIPS_HI16] = apply_r_mips_hi16, [R_MIPS_LO16] = apply_r_mips_lo16, [R_MIPS_GPREL16] = apply_r_mips_gprel16, [R_MIPS_PC16] = apply_r_mips_pc16 }; static char *rstrs[] = { [R_MIPS_NONE] = "MIPS_NONE", [R_MIPS_32] = "MIPS_32", [R_MIPS_26] = "MIPS_26", [R_MIPS_HI16] = "MIPS_HI16", [R_MIPS_LO16] = "MIPS_LO16", [R_MIPS_GPREL16] = "MIPS_GPREL16", [R_MIPS_PC16] = "MIPS_PC16" }; static int apply_relocations(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { Elf32_Rel *rel = (void *) sechdrs[relsec].sh_addr; Elf32_Sym *sym; uint32_t *location; unsigned int i; Elf32_Addr v; int res; for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { Elf32_Word r_info = rel[i].r_info; /* This is where to make the change */ location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset; /* This is the symbol it is referring to */ sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + ELF32_R_SYM(r_info); if (!sym->st_value) { pr_debug("%s: undefined weak symbol %s\n", me->name, strtab + sym->st_name); /* just print the warning, dont barf */ } v = sym->st_value; res = reloc_handlers[ELF32_R_TYPE(r_info)](me, location, v); if (res) { char *r = rstrs[ELF32_R_TYPE(r_info)]; pr_warn("VPE loader: .text+0x%x relocation type %s for symbol \"%s\" failed\n", rel[i].r_offset, r ? r : "UNKNOWN", strtab + sym->st_name); return res; } } return 0; } static inline void save_gp_address(unsigned int secbase, unsigned int rel) { gp_addr = secbase + rel; gp_offs = gp_addr - (secbase & 0xffff0000); } /* end module-elf32.c */ /* Change all symbols so that sh_value encodes the pointer directly. */ static void simplify_symbols(Elf_Shdr *sechdrs, unsigned int symindex, const char *strtab, const char *secstrings, unsigned int nsecs, struct module *mod) { Elf_Sym *sym = (void *)sechdrs[symindex].sh_addr; unsigned long secbase, bssbase = 0; unsigned int i, n = sechdrs[symindex].sh_size / sizeof(Elf_Sym); int size; /* find the .bss section for COMMON symbols */ for (i = 0; i < nsecs; i++) { if (strncmp(secstrings + sechdrs[i].sh_name, ".bss", 4) == 0) { bssbase = sechdrs[i].sh_addr; break; } } for (i = 1; i < n; i++) { switch (sym[i].st_shndx) { case SHN_COMMON: /* Allocate space for the symbol in the .bss section. st_value is currently size.