]>
Commit | Line | Data |
---|---|---|
fd045f6c | 1 | /* |
24af6c4e | 2 | * Copyright (C) 2014-2017 Linaro Ltd. <ard.biesheuvel@linaro.org> |
fd045f6c AB |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/elf.h> | |
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/sort.h> | |
13 | ||
24af6c4e AB |
14 | static bool in_init(const struct module *mod, void *loc) |
15 | { | |
16 | return (u64)loc - (u64)mod->init_layout.base < mod->init_layout.size; | |
17 | } | |
18 | ||
19 | u64 module_emit_plt_entry(struct module *mod, void *loc, const Elf64_Rela *rela, | |
fd045f6c AB |
20 | Elf64_Sym *sym) |
21 | { | |
24af6c4e AB |
22 | struct mod_plt_sec *pltsec = !in_init(mod, loc) ? &mod->arch.core : |
23 | &mod->arch.init; | |
24 | struct plt_entry *plt = (struct plt_entry *)pltsec->plt->sh_addr; | |
25 | int i = pltsec->plt_num_entries; | |
fd045f6c AB |
26 | u64 val = sym->st_value + rela->r_addend; |
27 | ||
7e8b9c1d | 28 | plt[i] = get_plt_entry(val); |
fd045f6c | 29 | |
24af6c4e AB |
30 | /* |
31 | * Check if the entry we just created is a duplicate. Given that the | |
32 | * relocations are sorted, this will be the last entry we allocated. | |
33 | * (if one exists). | |
34 | */ | |
7e8b9c1d | 35 | if (i > 0 && plt_entries_equal(plt + i, plt + i - 1)) |
24af6c4e AB |
36 | return (u64)&plt[i - 1]; |
37 | ||
38 | pltsec->plt_num_entries++; | |
39 | BUG_ON(pltsec->plt_num_entries > pltsec->plt_max_entries); | |
fd045f6c AB |
40 | |
41 | return (u64)&plt[i]; | |
42 | } | |
43 | ||
44 | #define cmp_3way(a,b) ((a) < (b) ? -1 : (a) > (b)) | |
45 | ||
46 | static int cmp_rela(const void *a, const void *b) | |
47 | { | |
48 | const Elf64_Rela *x = a, *y = b; | |
49 | int i; | |
50 | ||
51 | /* sort by type, symbol index and addend */ | |
52 | i = cmp_3way(ELF64_R_TYPE(x->r_info), ELF64_R_TYPE(y->r_info)); | |
53 | if (i == 0) | |
54 | i = cmp_3way(ELF64_R_SYM(x->r_info), ELF64_R_SYM(y->r_info)); | |
55 | if (i == 0) | |
56 | i = cmp_3way(x->r_addend, y->r_addend); | |
57 | return i; | |
58 | } | |
59 | ||
60 | static bool duplicate_rel(const Elf64_Rela *rela, int num) | |
61 | { | |
62 | /* | |
63 | * Entries are sorted by type, symbol index and addend. That means | |
64 | * that, if a duplicate entry exists, it must be in the preceding | |
65 | * slot. | |
66 | */ | |
67 | return num > 0 && cmp_rela(rela + num, rela + num - 1) == 0; | |
68 | } | |
69 | ||
24af6c4e AB |
70 | static unsigned int count_plts(Elf64_Sym *syms, Elf64_Rela *rela, int num, |
71 | Elf64_Word dstidx) | |
fd045f6c AB |
72 | { |
73 | unsigned int ret = 0; | |
74 | Elf64_Sym *s; | |
75 | int i; | |
76 | ||
77 | for (i = 0; i < num; i++) { | |
78 | switch (ELF64_R_TYPE(rela[i].r_info)) { | |
79 | case R_AARCH64_JUMP26: | |
80 | case R_AARCH64_CALL26: | |
81 | /* | |
82 | * We only have to consider branch targets that resolve | |
24af6c4e AB |
83 | * to symbols that are defined in a different section. |
84 | * This is not simply a heuristic, it is a fundamental | |
85 | * limitation, since there is no guaranteed way to emit | |
86 | * PLT entries sufficiently close to the branch if the | |
87 | * section size exceeds the range of a branch | |
88 | * instruction. So ignore relocations against defined | |
89 | * symbols if they live in the same section as the | |
90 | * relocation target. | |
fd045f6c AB |
91 | */ |
92 | s = syms + ELF64_R_SYM(rela[i].r_info); | |
24af6c4e | 93 | if (s->st_shndx == dstidx) |
fd045f6c AB |
94 | break; |
95 | ||
96 | /* | |
97 | * Jump relocations with non-zero addends against | |
98 | * undefined symbols are supported by the ELF spec, but | |
99 | * do not occur in practice (e.g., 'jump n bytes past | |
100 | * the entry point of undefined function symbol f'). | |
101 | * So we need to support them, but there is no need to | |
102 | * take them into consideration when trying to optimize | |
103 | * this code. So let's only check for duplicates when | |
104 | * the addend is zero: this allows us to record the PLT | |
105 | * entry address in the symbol table itself, rather than | |
106 | * having to search the list for duplicates each time we | |
107 | * emit one. | |
108 | */ | |
109 | if (rela[i].r_addend != 0 || !duplicate_rel(rela, i)) | |
110 | ret++; | |
111 | break; | |
112 | } | |
113 | } | |
114 | return ret; | |
115 | } | |
116 | ||
117 | int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, | |
118 | char *secstrings, struct module *mod) | |
119 | { | |
24af6c4e AB |
120 | unsigned long core_plts = 0; |
121 | unsigned long init_plts = 0; | |
fd045f6c | 122 | Elf64_Sym *syms = NULL; |
be0f272b | 123 | Elf_Shdr *tramp = NULL; |
fd045f6c AB |
124 | int i; |
125 | ||
126 | /* | |
127 | * Find the empty .plt section so we can expand it to store the PLT | |
128 | * entries. Record the symtab address as well. | |
129 | */ | |
130 | for (i = 0; i < ehdr->e_shnum; i++) { | |
24af6c4e AB |
131 | if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt")) |
132 | mod->arch.core.plt = sechdrs + i; | |
133 | else if (!strcmp(secstrings + sechdrs[i].sh_name, ".init.plt")) | |
134 | mod->arch.init.plt = sechdrs + i; | |
be0f272b AB |
135 | else if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE) && |
136 | !strcmp(secstrings + sechdrs[i].sh_name, | |
137 | ".text.ftrace_trampoline")) | |
138 | tramp = sechdrs + i; | |
fd045f6c AB |
139 | else if (sechdrs[i].sh_type == SHT_SYMTAB) |
140 | syms = (Elf64_Sym *)sechdrs[i].sh_addr; | |
141 | } | |
142 | ||
24af6c4e AB |
143 | if (!mod->arch.core.plt || !mod->arch.init.plt) { |
144 | pr_err("%s: module PLT section(s) missing\n", mod->name); | |
fd045f6c AB |
145 | return -ENOEXEC; |
146 | } | |
147 | if (!syms) { | |
148 | pr_err("%s: module symtab section missing\n", mod->name); | |
149 | return -ENOEXEC; | |
150 | } | |
151 | ||
152 | for (i = 0; i < ehdr->e_shnum; i++) { | |
153 | Elf64_Rela *rels = (void *)ehdr + sechdrs[i].sh_offset; | |
154 | int numrels = sechdrs[i].sh_size / sizeof(Elf64_Rela); | |
155 | Elf64_Shdr *dstsec = sechdrs + sechdrs[i].sh_info; | |
156 | ||
157 | if (sechdrs[i].sh_type != SHT_RELA) | |
158 | continue; | |
159 | ||
160 | /* ignore relocations that operate on non-exec sections */ | |
161 | if (!(dstsec->sh_flags & SHF_EXECINSTR)) | |
162 | continue; | |
163 | ||
164 | /* sort by type, symbol index and addend */ | |
165 | sort(rels, numrels, sizeof(Elf64_Rela), cmp_rela, NULL); | |
166 | ||
24af6c4e AB |
167 | if (strncmp(secstrings + dstsec->sh_name, ".init", 5) != 0) |
168 | core_plts += count_plts(syms, rels, numrels, | |
169 | sechdrs[i].sh_info); | |
170 | else | |
171 | init_plts += count_plts(syms, rels, numrels, | |
172 | sechdrs[i].sh_info); | |
fd045f6c AB |
173 | } |
174 | ||
24af6c4e AB |
175 | mod->arch.core.plt->sh_type = SHT_NOBITS; |
176 | mod->arch.core.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; | |
177 | mod->arch.core.plt->sh_addralign = L1_CACHE_BYTES; | |
178 | mod->arch.core.plt->sh_size = (core_plts + 1) * sizeof(struct plt_entry); | |
179 | mod->arch.core.plt_num_entries = 0; | |
180 | mod->arch.core.plt_max_entries = core_plts; | |
181 | ||
182 | mod->arch.init.plt->sh_type = SHT_NOBITS; | |
183 | mod->arch.init.plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC; | |
184 | mod->arch.init.plt->sh_addralign = L1_CACHE_BYTES; | |
185 | mod->arch.init.plt->sh_size = (init_plts + 1) * sizeof(struct plt_entry); | |
186 | mod->arch.init.plt_num_entries = 0; | |
187 | mod->arch.init.plt_max_entries = init_plts; | |
188 | ||
be0f272b AB |
189 | if (tramp) { |
190 | tramp->sh_type = SHT_NOBITS; | |
191 | tramp->sh_flags = SHF_EXECINSTR | SHF_ALLOC; | |
192 | tramp->sh_addralign = __alignof__(struct plt_entry); | |
193 | tramp->sh_size = sizeof(struct plt_entry); | |
194 | } | |
195 | ||
fd045f6c AB |
196 | return 0; |
197 | } |