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 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ #include <linux/elf.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleloader.h> #include <linux/ftrace.h> Elf_Addr module_emit_got_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val) { struct mod_section *got_sec = &mod->arch.got; int i = got_sec->num_entries; struct got_entry *got = get_got_entry(val, sechdrs, got_sec); if (got) return (Elf_Addr)got; /* There is no GOT entry for val yet, create a new one. */ got = (struct got_entry *)sechdrs[got_sec->shndx].sh_addr; got[i] = emit_got_entry(val); got_sec->num_entries++; if (got_sec->num_entries > got_sec->max_entries) { /* * This may happen when the module contains a GOT_HI20 without * a paired GOT_LO12. Such a module is broken, reject it. */ pr_err("%s: module contains bad GOT relocation\n", mod->name); return 0; } return (Elf_Addr)&got[i]; } Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val) { int nr; struct mod_section *plt_sec = &mod->arch.plt; struct mod_section *plt_idx_sec = &mod->arch.plt_idx; struct plt_entry *plt = get_plt_entry(val, sechdrs, plt_sec, plt_idx_sec); struct plt_idx_entry *plt_idx; if (plt) return (Elf_Addr)plt; nr = plt_sec->num_entries; /* There is no duplicate entry, create a new one */ plt = (struct plt_entry *)sechdrs[plt_sec->shndx].sh_addr; plt[nr] = emit_plt_entry(val); plt_idx = (struct plt_idx_entry *)sechdrs[plt_idx_sec->shndx].sh_addr; plt_idx[nr] = emit_plt_idx_entry(val); plt_sec->num_entries++; plt_idx_sec->num_entries++; BUG_ON(plt_sec->num_entries > plt_sec->max_entries); return (Elf_Addr)&plt[nr]; } 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 i, type; for (i = 0; i < num; i++) { type = ELF_R_TYPE(relas[i].r_info); switch (type) { case R_LARCH_SOP_PUSH_PLT_PCREL: case R_LARCH_B26: if (!duplicate_rela(relas, i)) (*plts)++; break; case R_LARCH_GOT_PC_HI20: if (!duplicate_rela(relas, i)) (*gots)++; break; default: break; /* Do nothing. */ } } } int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings, struct module *mod) { unsigned int i, num_plts = 0, num_gots = 0; Elf_Shdr *got_sec, *plt_sec, *plt_idx_sec, *tramp = NULL; /* * Find the empty .plt sections. */ for (i = 0; i < ehdr->e_shnum; i++) { if (!strcmp(secstrings + sechdrs[i].sh_name, ".got")) mod->arch.got.shndx = i; else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt")) mod->arch.plt.shndx = i; else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx")) mod->arch.plt_idx.shndx = i; else if (!strcmp(secstrings + sechdrs[i].sh_name, ".ftrace_trampoline")) tramp = sechdrs + i; } if (!mod->arch.got.shndx) { pr_err("%s: module GOT section(s) missing\n", mod->name); return -ENOEXEC; } if (!mod->arch.plt.shndx) { pr_err("%s: module PLT section(s) missing\n", mod->name); return -ENOEXEC; } if (!mod->arch.plt_idx.shndx) { pr_err("%s: module PLT.IDX section(s) missing\n", mod->name); return -ENOEXEC; } /* Calculate the maxinum number of entries */ for (i = 0; i < ehdr->e_shnum; i++) { int num_rela = sechdrs[i].sh_size / sizeof(Elf_Rela); Elf_Rela *relas = (void *)ehdr + sechdrs[i].sh_offset; 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); } got_sec = sechdrs + mod->arch.got.shndx; got_sec->sh_type = SHT_NOBITS; got_sec->sh_flags = SHF_ALLOC; got_sec->sh_addralign = L1_CACHE_BYTES; got_sec->sh_size = (num_gots + 1) * sizeof(struct got_entry); mod->arch.got.num_entries = 0; mod->arch.got.max_entries = num_gots; plt_sec = sechdrs + mod->arch.plt.shndx; plt_sec->sh_type = SHT_NOBITS; plt_sec->sh_flags = SHF_EXECINSTR | SHF_ALLOC; plt_sec->sh_addralign = L1_CACHE_BYTES; plt_sec->sh_size = (num_plts + 1) * sizeof(struct plt_entry); mod->arch.plt.num_entries = 0; mod->arch.plt.max_entries = num_plts; plt_idx_sec = sechdrs + mod->arch.plt_idx.shndx; plt_idx_sec->sh_type = SHT_NOBITS; plt_idx_sec->sh_flags = SHF_ALLOC; plt_idx_sec->sh_addralign = L1_CACHE_BYTES; plt_idx_sec->sh_size = (num_plts + 1) * sizeof(struct plt_idx_entry); mod->arch.plt_idx.num_entries = 0; mod->arch.plt_idx.max_entries = num_plts; if (tramp) { tramp->sh_type = SHT_NOBITS; tramp->sh_flags = SHF_EXECINSTR | SHF_ALLOC; tramp->sh_addralign = __alignof__(struct plt_entry); tramp->sh_size = NR_FTRACE_PLTS * sizeof(struct plt_entry); } return 0; } |