Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | /* SPDX-License-Identifier: GPL-2.0 * * Copyright (C) 2014-2017 Linaro Ltd. <ard.biesheuvel@linaro.org> * * Copyright (C) 2018 Andes Technology Corporation <zong@andestech.com> */ #include <linux/elf.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleloader.h> unsigned long module_emit_got_entry(struct module *mod, unsigned long val) { struct mod_section *got_sec = &mod->arch.got; int i = got_sec->num_entries; struct got_entry *got = get_got_entry(val, got_sec); if (got) return (unsigned long)got; /* There is no duplicate entry, create a new one */ got = (struct got_entry *)got_sec->shdr->sh_addr; got[i] = emit_got_entry(val); got_sec->num_entries++; BUG_ON(got_sec->num_entries > got_sec->max_entries); return (unsigned long)&got[i]; } unsigned long module_emit_plt_entry(struct module *mod, unsigned long val) { struct mod_section *got_plt_sec = &mod->arch.got_plt; struct got_entry *got_plt; struct mod_section *plt_sec = &mod->arch.plt; struct plt_entry *plt = get_plt_entry(val, plt_sec, got_plt_sec); int i = plt_sec->num_entries; if (plt) return (unsigned long)plt; /* There is no duplicate entry, create a new one */ got_plt = (struct got_entry *)got_plt_sec->shdr->sh_addr; got_plt[i] = emit_got_entry(val); plt = (struct plt_entry *)plt_sec->shdr->sh_addr; plt[i] = emit_plt_entry(val, (unsigned long)&plt[i], (unsigned long)&got_plt[i]); plt_sec->num_entries++; got_plt_sec->num_entries++; BUG_ON(plt_sec->num_entries > plt_sec->max_entries); return (unsigned long)&plt[i]; } static int is_rela_equal(const Elf_Rela *x, const Elf_Rela *y) { return x->r_info == y->r_info && x->r_addend == y->r_addend; } static bool duplicate_rela(const Elf_Rela *rela, int idx) { int i; for (i = 0; i < idx; i++) { if (is_rela_equal(&rela[i], &rela[idx])) return true; } return false; } static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts, unsigned int *gots) { unsigned int type, i; for (i = 0; i < num; i++) { type = ELF_RISCV_R_TYPE(relas[i].r_info); if (type == R_RISCV_CALL_PLT) { if (!duplicate_rela(relas, i)) (*plts)++; } else if (type == R_RISCV_GOT_HI20) { if (!duplicate_rela(relas, i)) (*gots)++; } } } int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings, struct module *mod) { unsigned int num_plts = 0; unsigned int num_gots = 0; int i; /* * Find the empty .got and .plt sections. */ for (i = 0; i < ehdr->e_shnum; i++) { if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt")) mod->arch.plt.shdr = sechdrs + i; else if (!strcmp(secstrings + sechdrs[i].sh_name, ".got")) mod->arch.got.shdr = sechdrs + i; else if (!strcmp(secstrings + sechdrs[i].sh_name, ".got.plt")) mod->arch.got_plt.shdr = sechdrs + i; } if (!mod->arch.plt.shdr) { pr_err("%s: module PLT section(s) missing\n", mod->name); return -ENOEXEC; } if (!mod->arch.got.shdr) { pr_err("%s: module GOT section(s) missing\n", mod->name); return -ENOEXEC; } if (!mod->arch.got_plt.shdr) { pr_err("%s: module GOT.PLT section(s) missing\n", mod->name); return -ENOEXEC; } /* Calculate the maxinum number of entries */ for (i = 0; i < ehdr->e_shnum; i++) { Elf_Rela *relas = (void *)ehdr + sechdrs[i].sh_offset; int num_rela = sechdrs[i].sh_size / sizeof(Elf_Rela); Elf_Shdr *dst_sec = sechdrs + sechdrs[i].sh_info; if (sechdrs[i].sh_type != SHT_RELA) continue; /* ignore relocations that operate on non-exec sections */ if (!(dst_sec->sh_flags & SHF_EXECINSTR)) continue; count_max_entries(relas, num_rela, &num_plts, &num_gots); } mod->arch.plt.shdr->sh_type = SHT_NOBITS; mod->arch.plt.shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC; mod->arch.plt.shdr->sh_addralign = L1_CACHE_BYTES; mod->arch.plt.shdr->sh_size = (num_plts + 1) * sizeof(struct plt_entry); mod->arch.plt.num_entries = 0; mod->arch.plt.max_entries = num_plts; mod->arch.got.shdr->sh_type = SHT_NOBITS; mod->arch.got.shdr->sh_flags = SHF_ALLOC; mod->arch.got.shdr->sh_addralign = L1_CACHE_BYTES; mod->arch.got.shdr->sh_size = (num_gots + 1) * sizeof(struct got_entry); mod->arch.got.num_entries = 0; mod->arch.got.max_entries = num_gots; mod->arch.got_plt.shdr->sh_type = SHT_NOBITS; mod->arch.got_plt.shdr->sh_flags = SHF_ALLOC; mod->arch.got_plt.shdr->sh_addralign = L1_CACHE_BYTES; mod->arch.got_plt.shdr->sh_size = (num_plts + 1) * sizeof(struct got_entry); mod->arch.got_plt.num_entries = 0; mod->arch.got_plt.max_entries = num_plts; return 0; } |