#ifdef CONFIG_ARM_HAS_GROUP_RELOCS /* * This implements the partitioning algorithm for group relocations as * documented in the ARM AArch32 ELF psABI (IHI 0044). * * A single PC-relative symbol reference is divided in up to 3 add or subtract * operations, where the final one could be incorporated into a load/store * instruction with immediate offset. E.g., * * ADD Rd, PC, #... or ADD Rd, PC, #... * ADD Rd, Rd, #... ADD Rd, Rd, #... * LDR Rd, [Rd, #...] ADD Rd, Rd, #... * * The latter has a guaranteed range of only 16 MiB (3x8 == 24 bits), so it is * of limited use in the kernel. However, the ADD/ADD/LDR combo has a range of * -/+ 256 MiB, (2x8 + 12 == 28 bits), which means it has sufficient range for * any in-kernel symbol reference (unless module PLTs are being used). * * The main advantage of this approach over the typical pattern using a literal * load is that literal loads may miss in the D-cache, and generally lead to * lower cache efficiency for variables that are referenced often from many * different places in the code.
*/ static u32 get_group_rem(u32 group, u32 *offset)
{
u32 val = *offset;
u32 shift; do {
shift = val ? (31 - __fls(val)) & ~1 : 32;
*offset = val; if (!val) break;
val &= 0xffffff >> shift;
} while (group--); return shift;
} #endif
/* * Route through a PLT entry if 'offset' exceeds the * supported range. Note that 'offset + loc + 8' * contains the absolute jump target, i.e., * @sym + addend, corrected for the +8 PC bias.
*/ if (IS_ENABLED(CONFIG_ARM_MODULE_PLTS) &&
(offset <= (s32)0xfe000000 ||
offset >= (s32)0x02000000))
offset = get_module_plt(module, loc,
offset + loc + 8)
- loc - 8;
if (offset <= (s32)0xfe000000 ||
offset >= (s32)0x02000000) {
pr_err("%s: section %u reloc %u sym '%s': relocation %u out of range (%#lx -> %#x)\n",
module->name, relindex, i, symname,
ELF32_R_TYPE(rel->r_info), loc,
sym->st_value); return -ENOEXEC;
}
case R_ARM_V4BX: /* Preserve Rm and the condition code. Alter * other bits to re-code instruction as * MOV PC,Rm.
*/
*(u32 *)loc &= __opcode_to_mem_arm(0xf000000f);
*(u32 *)loc |= __opcode_to_mem_arm(0x01a0f000); break;
case R_ARM_LDR_PC_G2:
tmp = __mem_to_opcode_arm(*(u32 *)loc);
offset = tmp & 0xfff; if (~tmp & BIT(23)) // U bit cleared?
offset = -offset;
offset += sym->st_value - loc; if (offset < 0) {
offset = -offset;
tmp &= ~BIT(23); // clear U bit
} else {
tmp |= BIT(23); // set U bit
}
get_group_rem(2, &offset);
if (offset > 0xfff) {
pr_err("%s: section %u reloc %u sym '%s': relocation %u out of range (%#lx -> %#x)\n",
module->name, relindex, i, symname,
ELF32_R_TYPE(rel->r_info), loc,
sym->st_value); return -ENOEXEC;
}
*(u32 *)loc = __opcode_to_mem_arm((tmp & ~0xfff) | offset); break; #endif #ifdef CONFIG_THUMB2_KERNEL case R_ARM_THM_CALL: case R_ARM_THM_JUMP24: /* * For function symbols, only Thumb addresses are * allowed (no interworking). * * For non-function symbols, the destination * has no specific ARM/Thumb disposition, so * the branch is resolved under the assumption * that interworking is not required.
*/ if (ELF32_ST_TYPE(sym->st_info) == STT_FUNC &&
!(sym->st_value & 1)) {
pr_err("%s: section %u reloc %u sym '%s': unsupported interworking call (Thumb -> ARM)\n",
module->name, relindex, i, symname); return -ENOEXEC;
}
case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL:
upper = __mem_to_opcode_thumb16(*(u16 *)loc);
lower = __mem_to_opcode_thumb16(*(u16 *)(loc + 2));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.