int apply_relocate_add()

in kernel/module.c [663:827]


int apply_relocate_add(Elf_Shdr *sechdrs,
		       const char *strtab,
		       unsigned int symindex,
		       unsigned int relsec,
		       struct module *me)
{
	int i;
	Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
	Elf64_Sym *sym;
	Elf64_Word *loc;
	Elf64_Xword *loc64;
	Elf64_Addr val;
	Elf64_Sxword addend;
	Elf64_Addr dot;
	Elf_Addr loc0;
	unsigned int targetsec = sechdrs[relsec].sh_info;

	pr_debug("Applying relocate section %u to %u\n", relsec,
	       targetsec);
	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
		/* This is where to make the change */
		loc = (void *)sechdrs[targetsec].sh_addr
		      + rel[i].r_offset;
		/* This is the start of the target section */
		loc0 = sechdrs[targetsec].sh_addr;
		/* This is the symbol it is referring to */
		sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
			+ ELF64_R_SYM(rel[i].r_info);
		if (!sym->st_value) {
			printk(KERN_WARNING "%s: Unknown symbol %s\n",
			       me->name, strtab + sym->st_name);
			return -ENOENT;
		}
		//dot = (sechdrs[relsec].sh_addr + rel->r_offset) & ~0x03;
		dot = (Elf64_Addr)loc & ~0x03;
		loc64 = (Elf64_Xword *)loc;

		val = sym->st_value;
		addend = rel[i].r_addend;

#if 0
#define r(t) ELF64_R_TYPE(rel[i].r_info)==t ? #t :
		printk("Symbol %s loc %p val 0x%Lx addend 0x%Lx: %s\n",
			strtab + sym->st_name,
			loc, val, addend,
			r(R_PARISC_LTOFF14R)
			r(R_PARISC_LTOFF21L)
			r(R_PARISC_PCREL22F)
			r(R_PARISC_DIR64)
			r(R_PARISC_SEGREL32)
			r(R_PARISC_FPTR64)
			"UNKNOWN");
#undef r
#endif

		switch (ELF64_R_TYPE(rel[i].r_info)) {
		case R_PARISC_LTOFF21L:
			/* LT-relative; left 21 bits */
			val = get_got(me, val, addend);
			pr_debug("LTOFF21L Symbol %s loc %p val %llx\n",
			       strtab + sym->st_name,
			       loc, val);
			val = lrsel(val, 0);
			*loc = mask(*loc, 21) | reassemble_21(val);
			break;
		case R_PARISC_LTOFF14R:
			/* L(ltoff(val+addend)) */
			/* LT-relative; right 14 bits */
			val = get_got(me, val, addend);
			val = rrsel(val, 0);
			pr_debug("LTOFF14R Symbol %s loc %p val %llx\n",
			       strtab + sym->st_name,
			       loc, val);
			*loc = mask(*loc, 14) | reassemble_14(val);
			break;
		case R_PARISC_PCREL22F:
			/* PC-relative; 22 bits */
			pr_debug("PCREL22F Symbol %s loc %p val %llx\n",
			       strtab + sym->st_name,
			       loc, val);
			val += addend;
			/* can we reach it locally? */
			if (in_local(me, (void *)val)) {
				/* this is the case where the symbol is local
				 * to the module, but in a different section,
				 * so stub the jump in case it's more than 22
				 * bits away */
				val = (val - dot - 8)/4;
				if (!RELOC_REACHABLE(val, 22)) {
					/* direct distance too far, create
					 * stub entry instead */
					val = get_stub(me, sym->st_value,
						addend, ELF_STUB_DIRECT,
						loc0, targetsec);
				} else {
					/* Ok, we can reach it directly. */
					val = sym->st_value;
					val += addend;
				}
			} else {
				val = sym->st_value;
				if (strncmp(strtab + sym->st_name, "$$", 2)
				    == 0)
					val = get_stub(me, val, addend, ELF_STUB_MILLI,
						       loc0, targetsec);
				else
					val = get_stub(me, val, addend, ELF_STUB_GOT,
						       loc0, targetsec);
			}
			pr_debug("STUB FOR %s loc %px, val %llx+%llx at %llx\n",
			       strtab + sym->st_name, loc, sym->st_value,
			       addend, val);
			val = (val - dot - 8)/4;
			CHECK_RELOC(val, 22);
			*loc = (*loc & ~0x3ff1ffd) | reassemble_22(val);
			break;
		case R_PARISC_PCREL32:
			/* 32-bit PC relative address */
			*loc = val - dot - 8 + addend;
			break;
		case R_PARISC_PCREL64:
			/* 64-bit PC relative address */
			*loc64 = val - dot - 8 + addend;
			break;
		case R_PARISC_DIR64:
			/* 64-bit effective address */
			*loc64 = val + addend;
			break;
		case R_PARISC_SEGREL32:
			/* 32-bit segment relative address */
			/* See note about special handling of SEGREL32 at
			 * the beginning of this file.
			 */
			*loc = fsel(val, addend);
			break;
		case R_PARISC_SECREL32:
			/* 32-bit section relative address. */
			*loc = fsel(val, addend);
			break;
		case R_PARISC_FPTR64:
			/* 64-bit function address */
			if(in_local(me, (void *)(val + addend))) {
				*loc64 = get_fdesc(me, val+addend);
				pr_debug("FDESC for %s at %llx points to %llx\n",
				       strtab + sym->st_name, *loc64,
				       ((Elf_Fdesc *)*loc64)->addr);
			} else {
				/* if the symbol is not local to this
				 * module then val+addend is a pointer
				 * to the function descriptor */
				pr_debug("Non local FPTR64 Symbol %s loc %p val %llx\n",
				       strtab + sym->st_name,
				       loc, val);
				*loc64 = val + addend;
			}
			break;

		default:
			printk(KERN_ERR "module %s: Unknown relocation: %Lu\n",
			       me->name, ELF64_R_TYPE(rel[i].r_info));
			return -ENOEXEC;
		}
	}
	return 0;
}