blob: faacf0c899762d747bbc450a46c9abe200e04e3f [file] [log] [blame]
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001/*
2 * elf.c - ELF access library
3 *
4 * Adapted from kpatch (https://github.com/dynup/kpatch):
5 * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com>
6 * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
Josh Poimboeuf13ccac52018-01-15 08:17:08 -060029#include <errno.h>
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060030
31#include "elf.h"
32#include "warn.h"
33
Arnaldo Carvalho de Melo774bec32016-07-13 15:28:51 -030034/*
35 * Fallback for systems without this "read, mmaping if possible" cmd.
36 */
37#ifndef ELF_C_READ_MMAP
38#define ELF_C_READ_MMAP ELF_C_READ
39#endif
40
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060041struct section *find_section_by_name(struct elf *elf, const char *name)
42{
43 struct section *sec;
44
45 list_for_each_entry(sec, &elf->sections, list)
46 if (!strcmp(sec->name, name))
47 return sec;
48
49 return NULL;
50}
51
52static struct section *find_section_by_index(struct elf *elf,
53 unsigned int idx)
54{
55 struct section *sec;
56
57 list_for_each_entry(sec, &elf->sections, list)
58 if (sec->idx == idx)
59 return sec;
60
61 return NULL;
62}
63
64static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
65{
66 struct section *sec;
67 struct symbol *sym;
68
69 list_for_each_entry(sec, &elf->sections, list)
Josh Poimboeuf042ba732016-03-09 00:07:00 -060070 hash_for_each_possible(sec->symbol_hash, sym, hash, idx)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060071 if (sym->idx == idx)
72 return sym;
73
74 return NULL;
75}
76
77struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
78{
79 struct symbol *sym;
80
Josh Poimboeufa196e172016-03-09 00:06:57 -060081 list_for_each_entry(sym, &sec->symbol_list, list)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060082 if (sym->type != STT_SECTION &&
83 sym->offset == offset)
84 return sym;
85
86 return NULL;
87}
88
Josh Poimboeuf3e51ccb2017-03-02 16:57:23 -060089struct symbol *find_symbol_containing(struct section *sec, unsigned long offset)
90{
91 struct symbol *sym;
92
93 list_for_each_entry(sym, &sec->symbol_list, list)
94 if (sym->type != STT_SECTION &&
95 offset >= sym->offset && offset < sym->offset + sym->len)
96 return sym;
97
98 return NULL;
99}
100
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600101struct rela *find_rela_by_dest_range(struct section *sec, unsigned long offset,
102 unsigned int len)
103{
104 struct rela *rela;
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600105 unsigned long o;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600106
107 if (!sec->rela)
108 return NULL;
109
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600110 for (o = offset; o < offset + len; o++)
111 hash_for_each_possible(sec->rela->rela_hash, rela, hash, o)
112 if (rela->offset == o)
113 return rela;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600114
115 return NULL;
116}
117
118struct rela *find_rela_by_dest(struct section *sec, unsigned long offset)
119{
120 return find_rela_by_dest_range(sec, offset, 1);
121}
122
123struct symbol *find_containing_func(struct section *sec, unsigned long offset)
124{
125 struct symbol *func;
126
Josh Poimboeufa196e172016-03-09 00:06:57 -0600127 list_for_each_entry(func, &sec->symbol_list, list)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600128 if (func->type == STT_FUNC && offset >= func->offset &&
129 offset < func->offset + func->len)
130 return func;
131
132 return NULL;
133}
134
135static int read_sections(struct elf *elf)
136{
137 Elf_Scn *s = NULL;
138 struct section *sec;
139 size_t shstrndx, sections_nr;
140 int i;
141
142 if (elf_getshdrnum(elf->elf, &sections_nr)) {
143 perror("elf_getshdrnum");
144 return -1;
145 }
146
147 if (elf_getshdrstrndx(elf->elf, &shstrndx)) {
148 perror("elf_getshdrstrndx");
149 return -1;
150 }
151
152 for (i = 0; i < sections_nr; i++) {
153 sec = malloc(sizeof(*sec));
154 if (!sec) {
155 perror("malloc");
156 return -1;
157 }
158 memset(sec, 0, sizeof(*sec));
159
Josh Poimboeufa196e172016-03-09 00:06:57 -0600160 INIT_LIST_HEAD(&sec->symbol_list);
161 INIT_LIST_HEAD(&sec->rela_list);
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600162 hash_init(sec->rela_hash);
163 hash_init(sec->symbol_hash);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600164
165 list_add_tail(&sec->list, &elf->sections);
166
167 s = elf_getscn(elf->elf, i);
168 if (!s) {
169 perror("elf_getscn");
170 return -1;
171 }
172
173 sec->idx = elf_ndxscn(s);
174
175 if (!gelf_getshdr(s, &sec->sh)) {
176 perror("gelf_getshdr");
177 return -1;
178 }
179
180 sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name);
181 if (!sec->name) {
182 perror("elf_strptr");
183 return -1;
184 }
185
186 sec->elf_data = elf_getdata(s, NULL);
187 if (!sec->elf_data) {
188 perror("elf_getdata");
189 return -1;
190 }
191
192 if (sec->elf_data->d_off != 0 ||
193 sec->elf_data->d_size != sec->sh.sh_size) {
194 WARN("unexpected data attributes for %s", sec->name);
195 return -1;
196 }
197
198 sec->data = (unsigned long)sec->elf_data->d_buf;
199 sec->len = sec->elf_data->d_size;
200 }
201
202 /* sanity check, one more call to elf_nextscn() should return NULL */
203 if (elf_nextscn(elf->elf, s)) {
204 WARN("section entry mismatch");
205 return -1;
206 }
207
208 return 0;
209}
210
211static int read_symbols(struct elf *elf)
212{
213 struct section *symtab;
214 struct symbol *sym;
215 struct list_head *entry, *tmp;
216 int symbols_nr, i;
217
218 symtab = find_section_by_name(elf, ".symtab");
219 if (!symtab) {
220 WARN("missing symbol table");
221 return -1;
222 }
223
224 symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
225
226 for (i = 0; i < symbols_nr; i++) {
227 sym = malloc(sizeof(*sym));
228 if (!sym) {
229 perror("malloc");
230 return -1;
231 }
232 memset(sym, 0, sizeof(*sym));
233
234 sym->idx = i;
235
236 if (!gelf_getsym(symtab->elf_data, i, &sym->sym)) {
237 perror("gelf_getsym");
238 goto err;
239 }
240
241 sym->name = elf_strptr(elf->elf, symtab->sh.sh_link,
242 sym->sym.st_name);
243 if (!sym->name) {
244 perror("elf_strptr");
245 goto err;
246 }
247
248 sym->type = GELF_ST_TYPE(sym->sym.st_info);
249 sym->bind = GELF_ST_BIND(sym->sym.st_info);
250
251 if (sym->sym.st_shndx > SHN_UNDEF &&
252 sym->sym.st_shndx < SHN_LORESERVE) {
253 sym->sec = find_section_by_index(elf,
254 sym->sym.st_shndx);
255 if (!sym->sec) {
256 WARN("couldn't find section for symbol %s",
257 sym->name);
258 goto err;
259 }
260 if (sym->type == STT_SECTION) {
261 sym->name = sym->sec->name;
262 sym->sec->sym = sym;
263 }
264 } else
265 sym->sec = find_section_by_index(elf, 0);
266
267 sym->offset = sym->sym.st_value;
268 sym->len = sym->sym.st_size;
269
270 /* sorted insert into a per-section list */
Josh Poimboeufa196e172016-03-09 00:06:57 -0600271 entry = &sym->sec->symbol_list;
272 list_for_each_prev(tmp, &sym->sec->symbol_list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600273 struct symbol *s;
274
275 s = list_entry(tmp, struct symbol, list);
276
277 if (sym->offset > s->offset) {
278 entry = tmp;
279 break;
280 }
281
282 if (sym->offset == s->offset && sym->len >= s->len) {
283 entry = tmp;
284 break;
285 }
286 }
287 list_add(&sym->list, entry);
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600288 hash_add(sym->sec->symbol_hash, &sym->hash, sym->idx);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600289 }
290
291 return 0;
292
293err:
294 free(sym);
295 return -1;
296}
297
298static int read_relas(struct elf *elf)
299{
300 struct section *sec;
301 struct rela *rela;
302 int i;
303 unsigned int symndx;
304
305 list_for_each_entry(sec, &elf->sections, list) {
306 if (sec->sh.sh_type != SHT_RELA)
307 continue;
308
309 sec->base = find_section_by_name(elf, sec->name + 5);
310 if (!sec->base) {
311 WARN("can't find base section for rela section %s",
312 sec->name);
313 return -1;
314 }
315
316 sec->base->rela = sec;
317
318 for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) {
319 rela = malloc(sizeof(*rela));
320 if (!rela) {
321 perror("malloc");
322 return -1;
323 }
324 memset(rela, 0, sizeof(*rela));
325
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600326 if (!gelf_getrela(sec->elf_data, i, &rela->rela)) {
327 perror("gelf_getrela");
328 return -1;
329 }
330
331 rela->type = GELF_R_TYPE(rela->rela.r_info);
332 rela->addend = rela->rela.r_addend;
333 rela->offset = rela->rela.r_offset;
334 symndx = GELF_R_SYM(rela->rela.r_info);
335 rela->sym = find_symbol_by_index(elf, symndx);
336 if (!rela->sym) {
337 WARN("can't find rela entry symbol %d for %s",
338 symndx, sec->name);
339 return -1;
340 }
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600341
342 list_add_tail(&rela->list, &sec->rela_list);
343 hash_add(sec->rela_hash, &rela->hash, rela->offset);
344
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600345 }
346 }
347
348 return 0;
349}
350
351struct elf *elf_open(const char *name)
352{
353 struct elf *elf;
354
355 elf_version(EV_CURRENT);
356
357 elf = malloc(sizeof(*elf));
358 if (!elf) {
359 perror("malloc");
360 return NULL;
361 }
362 memset(elf, 0, sizeof(*elf));
363
364 INIT_LIST_HEAD(&elf->sections);
365
366 elf->name = strdup(name);
367 if (!elf->name) {
368 perror("strdup");
369 goto err;
370 }
371
372 elf->fd = open(name, O_RDONLY);
373 if (elf->fd == -1) {
Josh Poimboeuf13ccac52018-01-15 08:17:08 -0600374 fprintf(stderr, "objtool: Can't open '%s': %s\n",
375 name, strerror(errno));
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600376 goto err;
377 }
378
379 elf->elf = elf_begin(elf->fd, ELF_C_READ_MMAP, NULL);
380 if (!elf->elf) {
381 perror("elf_begin");
382 goto err;
383 }
384
385 if (!gelf_getehdr(elf->elf, &elf->ehdr)) {
386 perror("gelf_getehdr");
387 goto err;
388 }
389
390 if (read_sections(elf))
391 goto err;
392
393 if (read_symbols(elf))
394 goto err;
395
396 if (read_relas(elf))
397 goto err;
398
399 return elf;
400
401err:
402 elf_close(elf);
403 return NULL;
404}
405
406void elf_close(struct elf *elf)
407{
408 struct section *sec, *tmpsec;
409 struct symbol *sym, *tmpsym;
410 struct rela *rela, *tmprela;
411
412 list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) {
Josh Poimboeufa196e172016-03-09 00:06:57 -0600413 list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600414 list_del(&sym->list);
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600415 hash_del(&sym->hash);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600416 free(sym);
417 }
Josh Poimboeufa196e172016-03-09 00:06:57 -0600418 list_for_each_entry_safe(rela, tmprela, &sec->rela_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600419 list_del(&rela->list);
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600420 hash_del(&rela->hash);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600421 free(rela);
422 }
423 list_del(&sec->list);
424 free(sec);
425 }
426 if (elf->name)
427 free(elf->name);
428 if (elf->fd > 0)
429 close(elf->fd);
430 if (elf->elf)
431 elf_end(elf->elf);
432 free(elf);
433}