Ejemplo n.º 1
0
static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val,
			  int lsb, int len, enum aarch64_insn_imm_type imm_type)
{
	u64 imm, imm_mask;
	s64 sval;
	u32 insn = le32_to_cpu(*(u32 *)place);

	/* Calculate the relocation value. */
	sval = do_reloc(op, place, val);
	sval >>= lsb;

	/* Extract the value bits and shift them to bit 0. */
	imm_mask = (BIT(lsb + len) - 1) >> lsb;
	imm = sval & imm_mask;

	/* Update the instruction's immediate field. */
	insn = aarch64_insn_encode_immediate(imm_type, insn, imm);
	*(u32 *)place = cpu_to_le32(insn);

	/*
	 * Extract the upper value bits (including the sign bit) and
	 * shift them to bit 0.
	 */
	sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1);

	/*
	 * Overflow has occurred if the upper bits are not all equal to
	 * the sign bit of the value.
	 */
	if ((u64)(sval + 1) >= 2)
		return -ERANGE;

	return 0;
}
Ejemplo n.º 2
0
static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,
			   int lsb, enum aarch64_imm_type imm_type)
{
	u64 imm, limit = 0;
	s64 sval;
	u32 insn = *(u32 *)place;

	sval = do_reloc(op, place, val);
	sval >>= lsb;
	imm = sval & 0xffff;

	/* Update the instruction with the new encoding. */
	*(u32 *)place = encode_insn_immediate(imm_type, insn, imm);

	/* Shift out the immediate field. */
	sval >>= 16;

	/*
	 * For unsigned immediates, the overflow check is straightforward.
	 * For signed immediates, the sign bit is actually the bit past the
	 * most significant bit of the field.
	 * The INSN_IMM_16 immediate type is unsigned.
	 */
	if (imm_type != INSN_IMM_16) {
		sval++;
		limit++;
	}

	/* Check the upper bits depending on the sign of the immediate. */
	if ((u64)sval > limit)
		return -ERANGE;

	return 0;
}
Ejemplo n.º 3
0
static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,
			   int lsb, enum aarch64_insn_imm_type imm_type)
{
	u64 imm, limit = 0;
	s64 sval;
	u32 insn = le32_to_cpu(*(u32 *)place);

	sval = do_reloc(op, place, val);
	sval >>= lsb;
	imm = sval & 0xffff;

	if (imm_type == AARCH64_INSN_IMM_MOVNZ) {
		/*
		 * For signed MOVW relocations, we have to manipulate the
		 * instruction encoding depending on whether or not the
		 * immediate is less than zero.
		 */
		insn &= ~(3 << 29);
		if ((s64)imm >= 0) {
			/* >=0: Set the instruction to MOVZ (opcode 10b). */
			insn |= 2 << 29;
		} else {
			/*
			 * <0: Set the instruction to MOVN (opcode 00b).
			 *     Since we've masked the opcode already, we
			 *     don't need to do anything other than
			 *     inverting the new immediate field.
			 */
			imm = ~imm;
		}
		imm_type = AARCH64_INSN_IMM_MOVK;
	}

	/* Update the instruction with the new encoding. */
	insn = aarch64_insn_encode_immediate(imm_type, insn, imm);
	*(u32 *)place = cpu_to_le32(insn);

	/* Shift out the immediate field. */
	sval >>= 16;

	/*
	 * For unsigned immediates, the overflow check is straightforward.
	 * For signed immediates, the sign bit is actually the bit past the
	 * most significant bit of the field.
	 * The AARCH64_INSN_IMM_16 immediate type is unsigned.
	 */
	if (imm_type != AARCH64_INSN_IMM_16) {
		sval++;
		limit++;
	}

	/* Check the upper bits depending on the sign of the immediate. */
	if ((u64)sval > limit)
		return -ERANGE;

	return 0;
}
Ejemplo n.º 4
0
static int reloc_insn_movw(enum aarch64_reloc_op op, __le32 *place, u64 val,
			   int lsb, enum aarch64_insn_movw_imm_type imm_type)
{
	u64 imm;
	s64 sval;
	u32 insn = le32_to_cpu(*place);

	sval = do_reloc(op, place, val);
	imm = sval >> lsb;

	if (imm_type == AARCH64_INSN_IMM_MOVNZ) {
		/*
		 * For signed MOVW relocations, we have to manipulate the
		 * instruction encoding depending on whether or not the
		 * immediate is less than zero.
		 */
		insn &= ~(3 << 29);
		if (sval >= 0) {
			/* >=0: Set the instruction to MOVZ (opcode 10b). */
			insn |= 2 << 29;
		} else {
			/*
			 * <0: Set the instruction to MOVN (opcode 00b).
			 *     Since we've masked the opcode already, we
			 *     don't need to do anything other than
			 *     inverting the new immediate field.
			 */
			imm = ~imm;
		}
	}

	/* Update the instruction with the new encoding. */
	insn = aarch64_insn_encode_immediate(AARCH64_INSN_IMM_16, insn, imm);
	*place = cpu_to_le32(insn);

	if (imm > U16_MAX)
		return -ERANGE;

	return 0;
}
Ejemplo n.º 5
0
static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)
{
	u64 imm_mask = (1 << len) - 1;
	s64 sval = do_reloc(op, place, val);

	switch (len) {
	case 16:
		*(s16 *)place = sval;
		break;
	case 32:
		*(s32 *)place = sval;
		break;
	case 64:
		*(s64 *)place = sval;
		break;
	default:
		pr_err("Invalid length (%d) for data relocation\n", len);
		return 0;
	}

	/*
	 * Extract the upper value bits (including the sign bit) and
	 * shift them to bit 0.
	 */
	sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1);

	/*
	 * Overflow has occurred if the value is not representable in
	 * len bits (i.e the bottom len bits are not sign-extended and
	 * the top bits are not all zero).
	 */
	if ((u64)(sval + 1) > 2)
		return -ERANGE;

	return 0;
}
Ejemplo n.º 6
0
static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)
{
	s64 sval = do_reloc(op, place, val);

	switch (len) {
	case 16:
		*(s16 *)place = sval;
		if (sval < S16_MIN || sval > U16_MAX)
			return -ERANGE;
		break;
	case 32:
		*(s32 *)place = sval;
		if (sval < S32_MIN || sval > U32_MAX)
			return -ERANGE;
		break;
	case 64:
		*(s64 *)place = sval;
		break;
	default:
		pr_err("Invalid length (%d) for data relocation\n", len);
		return 0;
	}
	return 0;
}
Ejemplo n.º 7
0
int
/* ARGSUSED2 */
do_relocate(struct module *mp, char *reltbl, Word relshtype, int nreloc,
	int relocsize, Addr baseaddr)
{
	unsigned long stndx;
	unsigned long off;	/* can't be register for tnf_reloc_resolve() */
	register unsigned long reladdr, rend;
	register unsigned int rtype;
	long value;
	Sym *symref;
	int err = 0;
	tnf_probe_control_t *probelist = NULL;
	tnf_tag_data_t *taglist = NULL;
	int symnum;

	reladdr = (unsigned long)reltbl;
	rend = reladdr + nreloc * relocsize;

#ifdef	KOBJ_DEBUG
	if (kobj_debug & D_RELOCATIONS) {
		_kobj_printf(ops, "krtld:\ttype\t\t\toffset      symbol\n");
		_kobj_printf(ops, "krtld:\t\t\t\t\t   value\n");
	}
#endif

	symnum = -1;
	/* loop through relocations */
	while (reladdr < rend) {
		symnum++;
		rtype = ELF32_R_TYPE(((Rel *)reladdr)->r_info);
		off = ((Rel *)reladdr)->r_offset;
		stndx = ELF32_R_SYM(((Rel *)reladdr)->r_info);
		if (stndx >= mp->nsyms) {
			_kobj_printf(ops, "do_relocate: bad strndx %d\n",
			    symnum);
			return (-1);
		}
		if ((rtype > R_386_NUM) || IS_TLS_INS(rtype)) {
			_kobj_printf(ops, "krtld: invalid relocation type %d",
			    rtype);
			_kobj_printf(ops, " at 0x%llx:", off);
			_kobj_printf(ops, " file=%s\n", mp->filename);
			err = 1;
			continue;
		}


		reladdr += relocsize;


		if (rtype == R_386_NONE)
			continue;

#ifdef	KOBJ_DEBUG
		if (kobj_debug & D_RELOCATIONS) {
			Sym *	symp;
			symp = (Sym *)
				(mp->symtbl+(stndx * mp->symhdr->sh_entsize));
			_kobj_printf(ops, "krtld:\t%s",
				conv_reloc_386_type(rtype));
			_kobj_printf(ops, "\t0x%8x", off);
			_kobj_printf(ops, "  %s\n",
			    (const char *)mp->strings + symp->st_name);
		}
#endif

		if (!(mp->flags & KOBJ_EXEC))
			off += baseaddr;

		/*
		 * if R_386_RELATIVE, simply add base addr
		 * to reloc location
		 */

		if (rtype == R_386_RELATIVE) {
			value = baseaddr;
		} else {
			/*
			 * get symbol table entry - if symbol is local
			 * value is base address of this object
			 */
			symref = (Sym *)
				(mp->symtbl+(stndx * mp->symhdr->sh_entsize));

			if (ELF32_ST_BIND(symref->st_info) == STB_LOCAL) {
				/* *** this is different for .o and .so */
				value = symref->st_value;
			} else {
				/*
				 * It's global. Allow weak references.  If
				 * the symbol is undefined, give TNF (the
				 * kernel probes facility) a chance to see
				 * if it's a probe site, and fix it up if so.
				 */
				if (symref->st_shndx == SHN_UNDEF &&
				    sdt_reloc_resolve(mp, mp->strings +
				    symref->st_name, (uint8_t *)off) == 0)
					continue;

				if (symref->st_shndx == SHN_UNDEF &&
				    tnf_reloc_resolve(mp->strings +
					symref->st_name, &symref->st_value,
					off,
					&probelist,
					&taglist) != 0) {
					if (ELF32_ST_BIND(symref->st_info)
					    != STB_WEAK) {
						_kobj_printf(ops,
						    "not found: %s\n",
						    mp->strings +
						    symref->st_name);
						err = 1;
					}
					continue;
				} else { /* symbol found  - relocate */
					/*
					 * calculate location of definition
					 * - symbol value plus base address of
					 * containing shared object
					 */
					value = symref->st_value;

				} /* end else symbol found */
			} /* end global or weak */
		} /* end not R_386_RELATIVE */

		/*
		 * calculate final value -
		 * if PC-relative, subtract ref addr
		 */
		if (IS_PC_RELATIVE(rtype))
			value -= off;

#ifdef	KOBJ_DEBUG
		if (kobj_debug & D_RELOCATIONS) {
			_kobj_printf(ops, "krtld:\t\t\t\t0x%8x", off);
			_kobj_printf(ops, " 0x%8x\n", value);
		}
#endif

		if (do_reloc(rtype, (unsigned char *)off, (Word *)&value,
		    (const char *)mp->strings + symref->st_name,
		    mp->filename, 0) == 0)
			err = 1;

	} /* end of while loop */
	if (err)
		return (-1);

	if (tnf_splice_probes(mp->flags & KOBJ_PRIM, probelist, taglist))
		mp->flags |= KOBJ_TNF_PROBE;

	return (0);
}