static uint64_t Elf64_r_type(Elf64_Xword r_info) { return ELF64_R_TYPE(r_info); }
int apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { unsigned int i; int ovf; bool overflow_check; Elf64_Sym *sym; void *loc; u64 val; Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { /* loc corresponds to P in the AArch64 ELF document. */ loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset; /* sym is the ELF symbol we're referring to. */ sym = (Elf64_Sym *)sechdrs[symindex].sh_addr + ELF64_R_SYM(rel[i].r_info); /* val corresponds to (S + A) in the AArch64 ELF document. */ val = sym->st_value + rel[i].r_addend; /* Check for overflow by default. */ overflow_check = true; /* Perform the static relocation. */ switch (ELF64_R_TYPE(rel[i].r_info)) { /* Null relocations. */ case R_ARM_NONE: case R_AARCH64_NONE: ovf = 0; break; /* Data relocations. */ case R_AARCH64_ABS64: overflow_check = false; ovf = reloc_data(RELOC_OP_ABS, loc, val, 64); break; case R_AARCH64_ABS32: ovf = reloc_data(RELOC_OP_ABS, loc, val, 32); break; case R_AARCH64_ABS16: ovf = reloc_data(RELOC_OP_ABS, loc, val, 16); break; case R_AARCH64_PREL64: overflow_check = false; ovf = reloc_data(RELOC_OP_PREL, loc, val, 64); break; case R_AARCH64_PREL32: ovf = reloc_data(RELOC_OP_PREL, loc, val, 32); break; case R_AARCH64_PREL16: ovf = reloc_data(RELOC_OP_PREL, loc, val, 16); break; /* MOVW instruction relocations. */ case R_AARCH64_MOVW_UABS_G0_NC: overflow_check = false; case R_AARCH64_MOVW_UABS_G0: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, INSN_IMM_16); break; case R_AARCH64_MOVW_UABS_G1_NC: overflow_check = false; case R_AARCH64_MOVW_UABS_G1: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, INSN_IMM_16); break; case R_AARCH64_MOVW_UABS_G2_NC: overflow_check = false; case R_AARCH64_MOVW_UABS_G2: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, INSN_IMM_16); break; case R_AARCH64_MOVW_UABS_G3: /* We're using the top bits so we can't overflow. */ overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48, INSN_IMM_16); break; case R_AARCH64_MOVW_SABS_G0: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0, INSN_IMM_MOVNZ); break; case R_AARCH64_MOVW_SABS_G1: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16, INSN_IMM_MOVNZ); break; case R_AARCH64_MOVW_SABS_G2: ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32, INSN_IMM_MOVNZ); break; case R_AARCH64_MOVW_PREL_G0_NC: overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, INSN_IMM_MOVK); break; case R_AARCH64_MOVW_PREL_G0: ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0, INSN_IMM_MOVNZ); break; case R_AARCH64_MOVW_PREL_G1_NC: overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, INSN_IMM_MOVK); break; case R_AARCH64_MOVW_PREL_G1: ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16, INSN_IMM_MOVNZ); break; case R_AARCH64_MOVW_PREL_G2_NC: overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, INSN_IMM_MOVK); break; case R_AARCH64_MOVW_PREL_G2: ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32, INSN_IMM_MOVNZ); break; case R_AARCH64_MOVW_PREL_G3: /* We're using the top bits so we can't overflow. */ overflow_check = false; ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48, INSN_IMM_MOVNZ); break; /* Immediate instruction relocations. */ case R_AARCH64_LD_PREL_LO19: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, INSN_IMM_19); break; case R_AARCH64_ADR_PREL_LO21: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21, INSN_IMM_ADR); break; #ifndef CONFIG_ARM64_ERRATUM_843419 case R_AARCH64_ADR_PREL_PG_HI21_NC: overflow_check = false; case R_AARCH64_ADR_PREL_PG_HI21: ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21, INSN_IMM_ADR); break; #endif case R_AARCH64_ADD_ABS_LO12_NC: case R_AARCH64_LDST8_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12, INSN_IMM_12); break; case R_AARCH64_LDST16_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11, INSN_IMM_12); break; case R_AARCH64_LDST32_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10, INSN_IMM_12); break; case R_AARCH64_LDST64_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9, INSN_IMM_12); break; case R_AARCH64_LDST128_ABS_LO12_NC: overflow_check = false; ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8, INSN_IMM_12); break; case R_AARCH64_TSTBR14: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14, INSN_IMM_14); break; case R_AARCH64_CONDBR19: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19, INSN_IMM_19); break; case R_AARCH64_JUMP26: case R_AARCH64_CALL26: ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26, INSN_IMM_26); break; default: pr_err("module %s: unsupported RELA relocation: %llu\n", me->name, ELF64_R_TYPE(rel[i].r_info)); return -ENOEXEC; } if (overflow_check && ovf == -ERANGE) goto overflow; } return 0; overflow: pr_err("module %s: overflow in relocation type %d val %Lx\n", me->name, (int)ELF64_R_TYPE(rel[i].r_info), val); return -ENOEXEC; }
int gelf_update_rela(Elf_Data *d, int ndx, GElf_Rela *dr) { int ec; Elf *e; Elf_Scn *scn; Elf32_Rela *rela32; Elf64_Rela *rela64; size_t msz; uint32_t sh_type; if (d == NULL || ndx < 0 || dr == NULL || (scn = d->d_scn) == NULL || (e = scn->s_elf) == NULL) { LIBELF_SET_ERROR(ARGUMENT, 0); return (0); } ec = e->e_class; assert(ec == ELFCLASS32 || ec == ELFCLASS64); if (ec == ELFCLASS32) sh_type = scn->s_shdr.s_shdr32.sh_type; else sh_type = scn->s_shdr.s_shdr64.sh_type; if (_libelf_xlate_shtype(sh_type) != ELF_T_RELA) { LIBELF_SET_ERROR(ARGUMENT, 0); return (0); } msz = _libelf_msize(ELF_T_RELA, ec, e->e_version); assert(msz > 0); if (msz * ndx >= d->d_size) { LIBELF_SET_ERROR(ARGUMENT, 0); return (0); } if (ec == ELFCLASS32) { rela32 = (Elf32_Rela *) d->d_buf + ndx; LIBELF_COPY_U32(rela32, dr, r_offset); if (ELF64_R_SYM(dr->r_info) > ELF32_R_SYM(~0UL) || ELF64_R_TYPE(dr->r_info) > ELF32_R_TYPE(~0U)) { LIBELF_SET_ERROR(RANGE, 0); return (0); } rela32->r_info = ELF32_R_INFO(ELF64_R_SYM(dr->r_info), ELF64_R_TYPE(dr->r_info)); LIBELF_COPY_S32(rela32, dr, r_addend); } else { rela64 = (Elf64_Rela *) d->d_buf + ndx; *rela64 = *dr; } return (1); }
int apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { unsigned int i; Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; Elf64_Sym *sym; void *loc; u64 val; #ifdef CONFIG_PAX_KERNEXEC unsigned long cr0; #endif DEBUGP("Applying relocate section %u to %u\n", relsec, sechdrs[relsec].sh_info); for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { /* This is where to make the change */ loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset; /* This is the symbol it is referring to. Note that all undefined symbols have been resolved. */ sym = (Elf64_Sym *)sechdrs[symindex].sh_addr + ELF64_R_SYM(rel[i].r_info); DEBUGP("type %d st_value %Lx r_addend %Lx loc %Lx\n", (int)ELF64_R_TYPE(rel[i].r_info), sym->st_value, rel[i].r_addend, (u64)loc); val = sym->st_value + rel[i].r_addend; switch (ELF64_R_TYPE(rel[i].r_info)) { case R_X86_64_NONE: break; case R_X86_64_64: #ifdef CONFIG_PAX_KERNEXEC pax_open_kernel(cr0); #endif *(u64 *)loc = val; #ifdef CONFIG_PAX_KERNEXEC pax_close_kernel(cr0); #endif break; case R_X86_64_32: #ifdef CONFIG_PAX_KERNEXEC pax_open_kernel(cr0); #endif *(u32 *)loc = val; #ifdef CONFIG_PAX_KERNEXEC pax_close_kernel(cr0); #endif if (val != *(u32 *)loc) goto overflow; break; case R_X86_64_32S: #ifdef CONFIG_PAX_KERNEXEC pax_open_kernel(cr0); #endif *(s32 *)loc = val; #ifdef CONFIG_PAX_KERNEXEC pax_close_kernel(cr0); #endif if ((s64)val != *(s32 *)loc) goto overflow; break; case R_X86_64_PC32: val -= (u64)loc; #ifdef CONFIG_PAX_KERNEXEC pax_open_kernel(cr0); #endif *(u32 *)loc = val; #ifdef CONFIG_PAX_KERNEXEC pax_close_kernel(cr0); #endif #if 0 if ((s64)val != *(s32 *)loc) goto overflow; #endif break; default: printk(KERN_ERR "module %s: Unknown rela relocation: %Lu\n", me->name, ELF64_R_TYPE(rel[i].r_info)); return -ENOEXEC; } } return 0; overflow: printk(KERN_ERR "overflow in relocation type %d val %Lx\n", (int)ELF64_R_TYPE(rel[i].r_info), val); printk(KERN_ERR "`%s' likely not compiled with -mcmodel=kernel\n", me->name); return -ENOEXEC; }
/* Add the value, subtract its position */ *location += sym->st_value - (uint32_t)location; break; default: pr_err("%s: Unknown relocation: %u\n", me->name, ELF32_R_TYPE(rel[i].r_info)); return -ENOEXEC; } } return 0; } #else /*X86_64*/ int apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { unsigned int i; Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr; Elf64_Sym *sym; void *loc; u64 val; bool rhel70 = check_module_rhelversion(me, "7.0"); bool warned = false; DEBUGP("Applying relocate section %u to %u\n", relsec, sechdrs[relsec].sh_info); for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { Elf64_Sym kstack_sym; bool apply_kstack_fixup = false; const char *symname; /* This is where to make the change */ loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset; /* This is the symbol it is referring to. Note that all undefined symbols have been resolved. */ sym = (Elf64_Sym *)sechdrs[symindex].sh_addr + ELF64_R_SYM(rel[i].r_info); symname = strtab + sym->st_name; DEBUGP("symname %s type %d st_value %Lx r_addend %Lx loc %Lx\n", symname, (int)ELF64_R_TYPE(rel[i].r_info), sym->st_value, rel[i].r_addend, (u64)loc); if (rhel70 && !strcmp(symname, "kernel_stack")) { if (!warned) printk(KERN_INFO "%s: applying kernel_stack fix up\n", me->name); apply_kstack_fixup = true; warned = true; } /* kernel_stack is referenced to access current_thread_info in * a variety of places... if we're loading a module which * expects an 8K stack, fix up the symbol reference to look * at a second copy. Nobody should be using this symbol for * any other purpose. */ if (apply_kstack_fixup) { const struct kernel_symbol *ksym2; ksym2 = find_symbol("__kernel_stack_70__", NULL, NULL, true, true); if (!IS_ERR(ksym2)) { kstack_sym.st_value = ksym2->value; sym = &kstack_sym; } else return PTR_ERR(ksym2) ?: -ENOEXEC; } val = sym->st_value + rel[i].r_addend; switch (ELF64_R_TYPE(rel[i].r_info)) { case R_X86_64_NONE: break; case R_X86_64_64: *(u64 *)loc = val; break; case R_X86_64_32: *(u32 *)loc = val; if (val != *(u32 *)loc) goto overflow; break; case R_X86_64_32S: *(s32 *)loc = val; if ((s64)val != *(s32 *)loc) goto overflow; break; case R_X86_64_PC32: val -= (u64)loc; *(u32 *)loc = val; #if 0 if ((s64)val != *(s32 *)loc) goto overflow; #endif break; default: pr_err("%s: Unknown rela relocation: %llu\n", me->name, ELF64_R_TYPE(rel[i].r_info)); return -ENOEXEC; } } return 0; overflow: pr_err("overflow in relocation type %d val %Lx\n", (int)ELF64_R_TYPE(rel[i].r_info), val); pr_err("`%s' likely not compiled with -mcmodel=kernel\n", me->name); return -ENOEXEC; }
static void relocate(void * r) { ElfRelocateFunc * func; if (!relocs->file->elf64) { if (relocs->type == SHT_REL) { Elf32_Rel bf = *(Elf32_Rel *)r; if (relocs->file->byte_swap) { SWAP(bf.r_offset); SWAP(bf.r_info); } sym_index = ELF32_R_SYM(bf.r_info); reloc_type = ELF32_R_TYPE(bf.r_info); reloc_addend = 0; } else { Elf32_Rela bf = *(Elf32_Rela *)r; if (relocs->file->byte_swap) { SWAP(bf.r_offset); SWAP(bf.r_info); SWAP(bf.r_addend); } sym_index = ELF32_R_SYM(bf.r_info); reloc_type = ELF32_R_TYPE(bf.r_info); reloc_addend = bf.r_addend; } if (sym_index != STN_UNDEF) { Elf32_Sym bf = ((Elf32_Sym *)symbols->data)[sym_index]; if (symbols->file->byte_swap) { SWAP(bf.st_name); SWAP(bf.st_value); SWAP(bf.st_size); SWAP(bf.st_info); SWAP(bf.st_other); SWAP(bf.st_shndx); } switch (bf.st_shndx) { case SHN_ABS: sym_value = bf.st_value; break; case SHN_COMMON: str_exception(ERR_INV_FORMAT, "Common relocation record unsupported"); break; case SHN_UNDEF: str_exception(ERR_INV_FORMAT, "Invalid relocation record"); break; default: if (bf.st_shndx >= symbols->file->section_cnt) str_exception(ERR_INV_FORMAT, "Invalid relocation record"); if (symbols->file->type != ET_EXEC) { sym_value = (symbols->file->sections + bf.st_shndx)->addr + bf.st_value; } else { sym_value = bf.st_value; } *destination_section = symbols->file->sections + bf.st_shndx; break; } } } else { if (relocs->type == SHT_REL) { Elf64_Rel bf = *(Elf64_Rel *)r; if (relocs->file->byte_swap) { SWAP(bf.r_offset); SWAP(bf.r_info); } sym_index = ELF64_R_SYM(bf.r_info); reloc_type = ELF64_R_TYPE(bf.r_info); reloc_addend = 0; } else { Elf64_Rela bf = *(Elf64_Rela *)r; if (relocs->file->byte_swap) { SWAP(bf.r_offset); SWAP(bf.r_info); SWAP(bf.r_addend); } sym_index = ELF64_R_SYM(bf.r_info); reloc_type = ELF64_R_TYPE(bf.r_info); reloc_addend = bf.r_addend; } if (sym_index != STN_UNDEF) { Elf64_Sym bf = ((Elf64_Sym *)symbols->data)[sym_index]; if (symbols->file->byte_swap) { SWAP(bf.st_name); SWAP(bf.st_value); SWAP(bf.st_size); SWAP(bf.st_info); SWAP(bf.st_other); SWAP(bf.st_shndx); } switch (bf.st_shndx) { case SHN_ABS: sym_value = bf.st_value; break; case SHN_COMMON: str_exception(ERR_INV_FORMAT, "Common relocation record unsupported"); break; case SHN_UNDEF: str_exception(ERR_INV_FORMAT, "Invalid relocation record"); break; default: if (bf.st_shndx >= symbols->file->section_cnt) str_exception(ERR_INV_FORMAT, "Invalid relocation record"); if (symbols->file->type != ET_EXEC) { sym_value = (symbols->file->sections + bf.st_shndx)->addr + bf.st_value; } else { sym_value = bf.st_value; } *destination_section = symbols->file->sections + bf.st_shndx; break; } } } /* For executable file we don't need to apply the relocation, * all we need is destination_section */ if (section->file->type != ET_REL) return; func = elf_relocate_funcs; while (func->machine != section->file->machine) { if (func->func == NULL) str_exception(ERR_INV_FORMAT, "Unsupported ELF machine code"); func++; } func->func(); }