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; }
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; }
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; }
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; }
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; }
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; }
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); }