int apply_relocate_add()

in kernel/module.c [518:660]


int apply_relocate_add(Elf_Shdr *sechdrs,
		       const char *strtab,
		       unsigned int symindex,
		       unsigned int relsec,
		       struct module *me)
{
	int i;
	Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr;
	Elf32_Sym *sym;
	Elf32_Word *loc;
	Elf32_Addr val;
	Elf32_Sword addend;
	Elf32_Addr dot;
	Elf_Addr loc0;
	unsigned int targetsec = sechdrs[relsec].sh_info;
	//unsigned long dp = (unsigned long)$global$;
	register unsigned long dp asm ("r27");

	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 = (Elf32_Sym *)sechdrs[symindex].sh_addr
			+ ELF32_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 =  (Elf32_Addr)loc & ~0x03;

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

#if 0
#define r(t) ELF32_R_TYPE(rel[i].r_info)==t ? #t :
		pr_debug("Symbol %s loc 0x%x val 0x%x addend 0x%x: %s\n",
			strtab + sym->st_name,
			(uint32_t)loc, val, addend,
			r(R_PARISC_PLABEL32)
			r(R_PARISC_DIR32)
			r(R_PARISC_DIR21L)
			r(R_PARISC_DIR14R)
			r(R_PARISC_SEGREL32)
			r(R_PARISC_DPREL21L)
			r(R_PARISC_DPREL14R)
			r(R_PARISC_PCREL17F)
			r(R_PARISC_PCREL22F)
			"UNKNOWN");
#undef r
#endif

		switch (ELF32_R_TYPE(rel[i].r_info)) {
		case R_PARISC_PLABEL32:
			/* 32-bit function address */
			/* no function descriptors... */
			*loc = fsel(val, addend);
			break;
		case R_PARISC_DIR32:
			/* direct 32-bit ref */
			*loc = fsel(val, addend);
			break;
		case R_PARISC_DIR21L:
			/* left 21 bits of effective address */
			val = lrsel(val, addend);
			*loc = mask(*loc, 21) | reassemble_21(val);
			break;
		case R_PARISC_DIR14R:
			/* right 14 bits of effective address */
			val = rrsel(val, addend);
			*loc = mask(*loc, 14) | reassemble_14(val);
			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_DPREL21L:
			/* left 21 bit of relative address */
			val = lrsel(val - dp, addend);
			*loc = mask(*loc, 21) | reassemble_21(val);
			break;
		case R_PARISC_DPREL14R:
			/* right 14 bit of relative address */
			val = rrsel(val - dp, addend);
			*loc = mask(*loc, 14) | reassemble_14(val);
			break;
		case R_PARISC_PCREL17F:
			/* 17-bit PC relative address */
			/* calculate direct call offset */
			val += addend;
			val = (val - dot - 8)/4;
			if (!RELOC_REACHABLE(val, 17)) {
				/* direct distance too far, create
				 * stub entry instead */
				val = get_stub(me, sym->st_value, addend,
					ELF_STUB_DIRECT, loc0, targetsec);
				val = (val - dot - 8)/4;
				CHECK_RELOC(val, 17);
			}
			*loc = (*loc & ~0x1f1ffd) | reassemble_17(val);
			break;
		case R_PARISC_PCREL22F:
			/* 22-bit PC relative address; only defined for pa20 */
			/* calculate direct call offset */
			val += addend;
			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);
				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;

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

	return 0;
}