blob: 8457218dcf2292959bef21cf12a71b9ac3b2ae9d [file] [log] [blame]
Thomas Gleixner1ccea772019-05-19 15:51:43 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06002/*
3 * elf.c - ELF access library
4 *
5 * Adapted from kpatch (https://github.com/dynup/kpatch):
6 * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com>
7 * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06008 */
9
10#include <sys/types.h>
11#include <sys/stat.h>
12#include <fcntl.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
Josh Poimboeuf385d11b2018-01-15 08:17:08 -060017#include <errno.h>
Vasily Gorbik77860322020-11-13 00:03:32 +010018#include <objtool/builtin.h>
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060019
Vasily Gorbik77860322020-11-13 00:03:32 +010020#include <objtool/elf.h>
21#include <objtool/warn.h>
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060022
Artem Savkov22566c12018-11-20 11:52:16 -060023#define MAX_NAME_LEN 128
24
Peter Zijlstraae358192020-03-12 09:32:10 +010025static inline u32 str_hash(const char *str)
26{
27 return jhash(str, strlen(str), 0);
28}
29
Peter Zijlstra34f7c962020-03-12 14:29:38 +010030static inline int elf_hash_bits(void)
31{
32 return vmlinux ? ELF_HASH_BITS : 16;
33}
34
35#define elf_hash_add(hashtable, node, key) \
36 hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())])
37
38static void elf_hash_init(struct hlist_head *table)
39{
40 __hash_init(table, 1U << elf_hash_bits());
41}
42
43#define elf_hash_for_each_possible(name, obj, member, key) \
44 hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member)
45
Peter Zijlstra2d24dd52020-04-29 17:03:22 +020046static bool symbol_to_offset(struct rb_node *a, const struct rb_node *b)
Peter Zijlstra2a362ec2020-03-12 09:34:42 +010047{
48 struct symbol *sa = rb_entry(a, struct symbol, node);
49 struct symbol *sb = rb_entry(b, struct symbol, node);
50
51 if (sa->offset < sb->offset)
Peter Zijlstra2d24dd52020-04-29 17:03:22 +020052 return true;
Peter Zijlstra2a362ec2020-03-12 09:34:42 +010053 if (sa->offset > sb->offset)
Peter Zijlstra2d24dd52020-04-29 17:03:22 +020054 return false;
Peter Zijlstra2a362ec2020-03-12 09:34:42 +010055
56 if (sa->len < sb->len)
Peter Zijlstra2d24dd52020-04-29 17:03:22 +020057 return true;
Peter Zijlstra2a362ec2020-03-12 09:34:42 +010058 if (sa->len > sb->len)
Peter Zijlstra2d24dd52020-04-29 17:03:22 +020059 return false;
Peter Zijlstra2a362ec2020-03-12 09:34:42 +010060
61 sa->alias = sb;
62
Peter Zijlstra2d24dd52020-04-29 17:03:22 +020063 return false;
Peter Zijlstra2a362ec2020-03-12 09:34:42 +010064}
65
66static int symbol_by_offset(const void *key, const struct rb_node *node)
67{
68 const struct symbol *s = rb_entry(node, struct symbol, node);
69 const unsigned long *o = key;
70
71 if (*o < s->offset)
72 return -1;
Julien Thierry5377cae2020-04-03 14:17:30 +010073 if (*o >= s->offset + s->len)
Peter Zijlstra2a362ec2020-03-12 09:34:42 +010074 return 1;
75
76 return 0;
77}
78
Ingo Molnar894e48c2020-04-22 12:32:03 +020079struct section *find_section_by_name(const struct elf *elf, const char *name)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060080{
81 struct section *sec;
82
Peter Zijlstra34f7c962020-03-12 14:29:38 +010083 elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060084 if (!strcmp(sec->name, name))
85 return sec;
86
87 return NULL;
88}
89
90static struct section *find_section_by_index(struct elf *elf,
91 unsigned int idx)
92{
93 struct section *sec;
94
Peter Zijlstra34f7c962020-03-12 14:29:38 +010095 elf_hash_for_each_possible(elf->section_hash, sec, hash, idx)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -060096 if (sec->idx == idx)
97 return sec;
98
99 return NULL;
100}
101
102static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
103{
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600104 struct symbol *sym;
105
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100106 elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
Peter Zijlstra65fb11a2020-03-10 18:39:45 +0100107 if (sym->idx == idx)
108 return sym;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600109
110 return NULL;
111}
112
113struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
114{
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100115 struct rb_node *node;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600116
Peter Zijlstra2d24dd52020-04-29 17:03:22 +0200117 rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) {
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100118 struct symbol *s = rb_entry(node, struct symbol, node);
119
120 if (s->offset == offset && s->type != STT_SECTION)
121 return s;
122 }
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600123
124 return NULL;
125}
126
127struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
128{
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100129 struct rb_node *node;
Josh Poimboeuf7acfe532020-02-17 21:41:54 -0600130
Peter Zijlstra2d24dd52020-04-29 17:03:22 +0200131 rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) {
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100132 struct symbol *s = rb_entry(node, struct symbol, node);
133
134 if (s->offset == offset && s->type == STT_FUNC)
135 return s;
136 }
137
138 return NULL;
139}
140
Miroslav Benesb490f452020-04-24 16:30:42 +0200141struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset)
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100142{
143 struct rb_node *node;
144
Peter Zijlstra2d24dd52020-04-29 17:03:22 +0200145 rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) {
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100146 struct symbol *s = rb_entry(node, struct symbol, node);
147
148 if (s->type != STT_SECTION)
149 return s;
150 }
151
152 return NULL;
153}
154
Peter Zijlstra53d20722020-03-16 10:36:53 +0100155struct symbol *find_func_containing(struct section *sec, unsigned long offset)
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100156{
157 struct rb_node *node;
158
Peter Zijlstra2d24dd52020-04-29 17:03:22 +0200159 rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) {
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100160 struct symbol *s = rb_entry(node, struct symbol, node);
161
162 if (s->type == STT_FUNC)
163 return s;
164 }
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600165
166 return NULL;
167}
168
Ingo Molnar894e48c2020-04-22 12:32:03 +0200169struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)
Josh Poimboeuf13810432018-05-09 22:39:15 -0500170{
Josh Poimboeuf13810432018-05-09 22:39:15 -0500171 struct symbol *sym;
172
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100173 elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
Peter Zijlstracdb3d052020-03-12 10:17:38 +0100174 if (!strcmp(sym->name, name))
175 return sym;
Josh Poimboeuf13810432018-05-09 22:39:15 -0500176
177 return NULL;
178}
179
Matt Helsleyf1974222020-05-29 14:01:13 -0700180struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100181 unsigned long offset, unsigned int len)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600182{
Matt Helsleyf1974222020-05-29 14:01:13 -0700183 struct reloc *reloc, *r = NULL;
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600184 unsigned long o;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600185
Matt Helsleyf1974222020-05-29 14:01:13 -0700186 if (!sec->reloc)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600187 return NULL;
188
Matt Helsleyf1974222020-05-29 14:01:13 -0700189 sec = sec->reloc;
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100190
Peter Zijlstra74b873e2020-03-12 11:30:50 +0100191 for_offset_range(o, offset, offset + len) {
Matt Helsleyf1974222020-05-29 14:01:13 -0700192 elf_hash_for_each_possible(elf->reloc_hash, reloc, hash,
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100193 sec_offset_hash(sec, o)) {
Matt Helsleyf1974222020-05-29 14:01:13 -0700194 if (reloc->sec != sec)
Peter Zijlstra74b873e2020-03-12 11:30:50 +0100195 continue;
196
Matt Helsleyf1974222020-05-29 14:01:13 -0700197 if (reloc->offset >= offset && reloc->offset < offset + len) {
198 if (!r || reloc->offset < r->offset)
199 r = reloc;
Peter Zijlstra74b873e2020-03-12 11:30:50 +0100200 }
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100201 }
Peter Zijlstra74b873e2020-03-12 11:30:50 +0100202 if (r)
203 return r;
Peter Zijlstra8b5fa6b2020-03-12 11:23:36 +0100204 }
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600205
206 return NULL;
207}
208
Matt Helsleyf1974222020-05-29 14:01:13 -0700209struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600210{
Matt Helsleyf1974222020-05-29 14:01:13 -0700211 return find_reloc_by_dest_range(elf, sec, offset, 1);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600212}
213
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600214static int read_sections(struct elf *elf)
215{
216 Elf_Scn *s = NULL;
217 struct section *sec;
218 size_t shstrndx, sections_nr;
219 int i;
220
221 if (elf_getshdrnum(elf->elf, &sections_nr)) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500222 WARN_ELF("elf_getshdrnum");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600223 return -1;
224 }
225
226 if (elf_getshdrstrndx(elf->elf, &shstrndx)) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500227 WARN_ELF("elf_getshdrstrndx");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600228 return -1;
229 }
230
231 for (i = 0; i < sections_nr; i++) {
232 sec = malloc(sizeof(*sec));
233 if (!sec) {
234 perror("malloc");
235 return -1;
236 }
237 memset(sec, 0, sizeof(*sec));
238
Josh Poimboeufa196e172016-03-09 00:06:57 -0600239 INIT_LIST_HEAD(&sec->symbol_list);
Matt Helsleyf1974222020-05-29 14:01:13 -0700240 INIT_LIST_HEAD(&sec->reloc_list);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600241
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600242 s = elf_getscn(elf->elf, i);
243 if (!s) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500244 WARN_ELF("elf_getscn");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600245 return -1;
246 }
247
248 sec->idx = elf_ndxscn(s);
249
250 if (!gelf_getshdr(s, &sec->sh)) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500251 WARN_ELF("gelf_getshdr");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600252 return -1;
253 }
254
255 sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name);
256 if (!sec->name) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500257 WARN_ELF("elf_strptr");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600258 return -1;
259 }
260
Petr Vandrovecdf968c92017-09-15 02:15:05 -0500261 if (sec->sh.sh_size != 0) {
262 sec->data = elf_getdata(s, NULL);
263 if (!sec->data) {
264 WARN_ELF("elf_getdata");
265 return -1;
266 }
267 if (sec->data->d_off != 0 ||
268 sec->data->d_size != sec->sh.sh_size) {
269 WARN("unexpected data attributes for %s",
270 sec->name);
271 return -1;
272 }
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600273 }
Petr Vandrovecdf968c92017-09-15 02:15:05 -0500274 sec->len = sec->sh.sh_size;
Peter Zijlstra530389962020-03-10 18:43:35 +0100275
276 list_add_tail(&sec->list, &elf->sections);
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100277 elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
278 elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600279 }
280
Peter Zijlstra1e11f3f2020-03-12 09:26:29 +0100281 if (stats)
282 printf("nr_sections: %lu\n", (unsigned long)sections_nr);
283
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600284 /* sanity check, one more call to elf_nextscn() should return NULL */
285 if (elf_nextscn(elf->elf, s)) {
286 WARN("section entry mismatch");
287 return -1;
288 }
289
290 return 0;
291}
292
Peter Zijlstra9a7827b2021-03-26 16:12:10 +0100293static void elf_add_symbol(struct elf *elf, struct symbol *sym)
294{
295 struct list_head *entry;
296 struct rb_node *pnode;
297
298 sym->type = GELF_ST_TYPE(sym->sym.st_info);
299 sym->bind = GELF_ST_BIND(sym->sym.st_info);
300
301 sym->offset = sym->sym.st_value;
302 sym->len = sym->sym.st_size;
303
304 rb_add(&sym->node, &sym->sec->symbol_tree, symbol_to_offset);
305 pnode = rb_prev(&sym->node);
306 if (pnode)
307 entry = &rb_entry(pnode, struct symbol, node)->list;
308 else
309 entry = &sym->sec->symbol_list;
310 list_add(&sym->list, entry);
311 elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx);
312 elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
313
314 /*
315 * Don't store empty STT_NOTYPE symbols in the rbtree. They
316 * can exist within a function, confusing the sorting.
317 */
318 if (!sym->len)
319 rb_erase(&sym->node, &sym->sec->symbol_tree);
320}
321
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600322static int read_symbols(struct elf *elf)
323{
Sami Tolvanen28fe1d72020-04-21 15:08:42 -0700324 struct section *symtab, *symtab_shndx, *sec;
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100325 struct symbol *sym, *pfunc;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600326 int symbols_nr, i;
Josh Poimboeuf13810432018-05-09 22:39:15 -0500327 char *coldstr;
Sami Tolvanen28fe1d72020-04-21 15:08:42 -0700328 Elf_Data *shndx_data = NULL;
329 Elf32_Word shndx;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600330
331 symtab = find_section_by_name(elf, ".symtab");
332 if (!symtab) {
Josh Poimboeuf1d489152021-01-14 16:14:01 -0600333 /*
334 * A missing symbol table is actually possible if it's an empty
335 * .o file. This can happen for thunk_64.o.
336 */
337 return 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600338 }
339
Sami Tolvanen28fe1d72020-04-21 15:08:42 -0700340 symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
341 if (symtab_shndx)
342 shndx_data = symtab_shndx->data;
343
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600344 symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
345
346 for (i = 0; i < symbols_nr; i++) {
347 sym = malloc(sizeof(*sym));
348 if (!sym) {
349 perror("malloc");
350 return -1;
351 }
352 memset(sym, 0, sizeof(*sym));
Peter Zijlstra2a362ec2020-03-12 09:34:42 +0100353 sym->alias = sym;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600354
355 sym->idx = i;
356
Sami Tolvanen28fe1d72020-04-21 15:08:42 -0700357 if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym,
358 &shndx)) {
359 WARN_ELF("gelf_getsymshndx");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600360 goto err;
361 }
362
363 sym->name = elf_strptr(elf->elf, symtab->sh.sh_link,
364 sym->sym.st_name);
365 if (!sym->name) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500366 WARN_ELF("elf_strptr");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600367 goto err;
368 }
369
Sami Tolvanen28fe1d72020-04-21 15:08:42 -0700370 if ((sym->sym.st_shndx > SHN_UNDEF &&
371 sym->sym.st_shndx < SHN_LORESERVE) ||
372 (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) {
373 if (sym->sym.st_shndx != SHN_XINDEX)
374 shndx = sym->sym.st_shndx;
375
376 sym->sec = find_section_by_index(elf, shndx);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600377 if (!sym->sec) {
378 WARN("couldn't find section for symbol %s",
379 sym->name);
380 goto err;
381 }
Peter Zijlstra9a7827b2021-03-26 16:12:10 +0100382 if (GELF_ST_TYPE(sym->sym.st_info) == STT_SECTION) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600383 sym->name = sym->sec->name;
384 sym->sec->sym = sym;
385 }
386 } else
387 sym->sec = find_section_by_index(elf, 0);
388
Peter Zijlstra9a7827b2021-03-26 16:12:10 +0100389 elf_add_symbol(elf, sym);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600390 }
391
Peter Zijlstra1e11f3f2020-03-12 09:26:29 +0100392 if (stats)
393 printf("nr_symbols: %lu\n", (unsigned long)symbols_nr);
394
Josh Poimboeuf13810432018-05-09 22:39:15 -0500395 /* Create parent/child links for any cold subfunctions */
396 list_for_each_entry(sec, &elf->sections, list) {
397 list_for_each_entry(sym, &sec->symbol_list, list) {
Artem Savkov22566c12018-11-20 11:52:16 -0600398 char pname[MAX_NAME_LEN + 1];
399 size_t pnamelen;
Josh Poimboeuf13810432018-05-09 22:39:15 -0500400 if (sym->type != STT_FUNC)
401 continue;
Kristen Carlson Accardie000acc2020-04-15 14:04:43 -0700402
403 if (sym->pfunc == NULL)
404 sym->pfunc = sym;
405
406 if (sym->cfunc == NULL)
407 sym->cfunc = sym;
408
Josh Poimboeufbcb6fb52018-10-31 21:57:30 -0500409 coldstr = strstr(sym->name, ".cold");
Josh Poimboeuf08b393d2018-06-27 17:03:45 -0500410 if (!coldstr)
411 continue;
Josh Poimboeuf13810432018-05-09 22:39:15 -0500412
Artem Savkov22566c12018-11-20 11:52:16 -0600413 pnamelen = coldstr - sym->name;
414 if (pnamelen > MAX_NAME_LEN) {
415 WARN("%s(): parent function name exceeds maximum length of %d characters",
416 sym->name, MAX_NAME_LEN);
417 return -1;
418 }
419
420 strncpy(pname, sym->name, pnamelen);
421 pname[pnamelen] = '\0';
422 pfunc = find_symbol_by_name(elf, pname);
Josh Poimboeuf13810432018-05-09 22:39:15 -0500423
Josh Poimboeuf08b393d2018-06-27 17:03:45 -0500424 if (!pfunc) {
425 WARN("%s(): can't find parent function",
426 sym->name);
Artem Savkov0b9301fb2018-11-20 11:52:15 -0600427 return -1;
Josh Poimboeuf08b393d2018-06-27 17:03:45 -0500428 }
429
430 sym->pfunc = pfunc;
431 pfunc->cfunc = sym;
432
433 /*
434 * Unfortunately, -fnoreorder-functions puts the child
435 * inside the parent. Remove the overlap so we can
436 * have sane assumptions.
437 *
438 * Note that pfunc->len now no longer matches
439 * pfunc->sym.st_size.
440 */
441 if (sym->sec == pfunc->sec &&
442 sym->offset >= pfunc->offset &&
443 sym->offset + sym->len == pfunc->offset + pfunc->len) {
444 pfunc->len -= sym->len;
Josh Poimboeuf13810432018-05-09 22:39:15 -0500445 }
446 }
447 }
448
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600449 return 0;
450
451err:
452 free(sym);
453 return -1;
454}
455
Peter Zijlstrad0c5c4c2021-03-26 16:12:08 +0100456static struct section *elf_create_reloc_section(struct elf *elf,
457 struct section *base,
458 int reltype);
459
Peter Zijlstraef47cc02021-03-26 16:12:07 +0100460int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
461 unsigned int type, struct symbol *sym, int addend)
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100462{
Peter Zijlstraef47cc02021-03-26 16:12:07 +0100463 struct reloc *reloc;
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100464
Peter Zijlstrad0c5c4c2021-03-26 16:12:08 +0100465 if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA))
466 return -1;
467
Peter Zijlstraef47cc02021-03-26 16:12:07 +0100468 reloc = malloc(sizeof(*reloc));
469 if (!reloc) {
470 perror("malloc");
471 return -1;
472 }
473 memset(reloc, 0, sizeof(*reloc));
474
475 reloc->sec = sec->reloc;
476 reloc->offset = offset;
477 reloc->type = type;
478 reloc->sym = sym;
479 reloc->addend = addend;
480
481 list_add_tail(&reloc->list, &sec->reloc->reloc_list);
Matt Helsleyf1974222020-05-29 14:01:13 -0700482 elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc));
Peter Zijlstra3a647602021-03-26 16:12:06 +0100483
Peter Zijlstraef47cc02021-03-26 16:12:07 +0100484 sec->reloc->changed = true;
485
486 return 0;
487}
488
489int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
490 unsigned long offset, unsigned int type,
491 struct section *insn_sec, unsigned long insn_off)
492{
493 struct symbol *sym;
494 int addend;
495
496 if (insn_sec->sym) {
497 sym = insn_sec->sym;
498 addend = insn_off;
499
500 } else {
501 /*
502 * The Clang assembler strips section symbols, so we have to
503 * reference the function symbol instead:
504 */
505 sym = find_symbol_containing(insn_sec, insn_off);
506 if (!sym) {
507 /*
508 * Hack alert. This happens when we need to reference
509 * the NOP pad insn immediately after the function.
510 */
511 sym = find_symbol_containing(insn_sec, insn_off - 1);
512 }
513
514 if (!sym) {
515 WARN("can't find symbol containing %s+0x%lx", insn_sec->name, insn_off);
516 return -1;
517 }
518
519 addend = insn_off - sym->offset;
520 }
521
522 return elf_add_reloc(elf, sec, offset, type, sym, addend);
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100523}
524
Matt Helsleyfb414782020-05-29 14:01:14 -0700525static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)
526{
527 if (!gelf_getrel(sec->data, i, &reloc->rel)) {
528 WARN_ELF("gelf_getrel");
529 return -1;
530 }
531 reloc->type = GELF_R_TYPE(reloc->rel.r_info);
532 reloc->addend = 0;
533 reloc->offset = reloc->rel.r_offset;
534 *symndx = GELF_R_SYM(reloc->rel.r_info);
535 return 0;
536}
537
538static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)
539{
540 if (!gelf_getrela(sec->data, i, &reloc->rela)) {
541 WARN_ELF("gelf_getrela");
542 return -1;
543 }
544 reloc->type = GELF_R_TYPE(reloc->rela.r_info);
545 reloc->addend = reloc->rela.r_addend;
546 reloc->offset = reloc->rela.r_offset;
547 *symndx = GELF_R_SYM(reloc->rela.r_info);
548 return 0;
549}
550
Matt Helsleyf1974222020-05-29 14:01:13 -0700551static int read_relocs(struct elf *elf)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600552{
553 struct section *sec;
Matt Helsleyf1974222020-05-29 14:01:13 -0700554 struct reloc *reloc;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600555 int i;
556 unsigned int symndx;
Matt Helsleyf1974222020-05-29 14:01:13 -0700557 unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600558
559 list_for_each_entry(sec, &elf->sections, list) {
Matt Helsleyfb414782020-05-29 14:01:14 -0700560 if ((sec->sh.sh_type != SHT_RELA) &&
561 (sec->sh.sh_type != SHT_REL))
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600562 continue;
563
Sami Tolvanen1e968bf2020-04-21 11:25:01 -0700564 sec->base = find_section_by_index(elf, sec->sh.sh_info);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600565 if (!sec->base) {
Matt Helsleyf1974222020-05-29 14:01:13 -0700566 WARN("can't find base section for reloc section %s",
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600567 sec->name);
568 return -1;
569 }
570
Matt Helsleyf1974222020-05-29 14:01:13 -0700571 sec->base->reloc = sec;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600572
Matt Helsleyf1974222020-05-29 14:01:13 -0700573 nr_reloc = 0;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600574 for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) {
Matt Helsleyf1974222020-05-29 14:01:13 -0700575 reloc = malloc(sizeof(*reloc));
576 if (!reloc) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600577 perror("malloc");
578 return -1;
579 }
Matt Helsleyf1974222020-05-29 14:01:13 -0700580 memset(reloc, 0, sizeof(*reloc));
Matt Helsleyfb414782020-05-29 14:01:14 -0700581 switch (sec->sh.sh_type) {
582 case SHT_REL:
583 if (read_rel_reloc(sec, i, reloc, &symndx))
584 return -1;
585 break;
586 case SHT_RELA:
587 if (read_rela_reloc(sec, i, reloc, &symndx))
588 return -1;
589 break;
590 default: return -1;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600591 }
592
Matt Helsleyf1974222020-05-29 14:01:13 -0700593 reloc->sec = sec;
Peter Zijlstrad832c002020-06-18 17:55:29 +0200594 reloc->idx = i;
595 reloc->sym = find_symbol_by_index(elf, symndx);
Matt Helsleyf1974222020-05-29 14:01:13 -0700596 if (!reloc->sym) {
597 WARN("can't find reloc entry symbol %d for %s",
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600598 symndx, sec->name);
599 return -1;
600 }
Josh Poimboeuf042ba732016-03-09 00:07:00 -0600601
Peter Zijlstra3a647602021-03-26 16:12:06 +0100602 list_add_tail(&reloc->list, &sec->reloc_list);
603 elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc));
604
Matt Helsleyf1974222020-05-29 14:01:13 -0700605 nr_reloc++;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600606 }
Matt Helsleyf1974222020-05-29 14:01:13 -0700607 max_reloc = max(max_reloc, nr_reloc);
608 tot_reloc += nr_reloc;
Peter Zijlstra1e11f3f2020-03-12 09:26:29 +0100609 }
610
611 if (stats) {
Matt Helsleyf1974222020-05-29 14:01:13 -0700612 printf("max_reloc: %lu\n", max_reloc);
613 printf("tot_reloc: %lu\n", tot_reloc);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600614 }
615
616 return 0;
617}
618
Ingo Molnarbc359ff2020-04-22 12:32:04 +0200619struct elf *elf_open_read(const char *name, int flags)
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600620{
621 struct elf *elf;
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500622 Elf_Cmd cmd;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600623
624 elf_version(EV_CURRENT);
625
626 elf = malloc(sizeof(*elf));
627 if (!elf) {
628 perror("malloc");
629 return NULL;
630 }
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100631 memset(elf, 0, offsetof(struct elf, sections));
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600632
633 INIT_LIST_HEAD(&elf->sections);
634
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100635 elf_hash_init(elf->symbol_hash);
636 elf_hash_init(elf->symbol_name_hash);
637 elf_hash_init(elf->section_hash);
638 elf_hash_init(elf->section_name_hash);
Matt Helsleyf1974222020-05-29 14:01:13 -0700639 elf_hash_init(elf->reloc_hash);
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100640
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500641 elf->fd = open(name, flags);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600642 if (elf->fd == -1) {
Josh Poimboeuf385d11b2018-01-15 08:17:08 -0600643 fprintf(stderr, "objtool: Can't open '%s': %s\n",
644 name, strerror(errno));
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600645 goto err;
646 }
647
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500648 if ((flags & O_ACCMODE) == O_RDONLY)
649 cmd = ELF_C_READ_MMAP;
650 else if ((flags & O_ACCMODE) == O_RDWR)
651 cmd = ELF_C_RDWR;
652 else /* O_WRONLY */
653 cmd = ELF_C_WRITE;
654
655 elf->elf = elf_begin(elf->fd, cmd, NULL);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600656 if (!elf->elf) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500657 WARN_ELF("elf_begin");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600658 goto err;
659 }
660
661 if (!gelf_getehdr(elf->elf, &elf->ehdr)) {
Josh Poimboeufbaa41462017-06-28 10:11:07 -0500662 WARN_ELF("gelf_getehdr");
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600663 goto err;
664 }
665
666 if (read_sections(elf))
667 goto err;
668
669 if (read_symbols(elf))
670 goto err;
671
Matt Helsleyf1974222020-05-29 14:01:13 -0700672 if (read_relocs(elf))
Josh Poimboeuf442f04c2016-02-28 22:22:41 -0600673 goto err;
674
675 return elf;
676
677err:
678 elf_close(elf);
679 return NULL;
680}
681
Peter Zijlstra417a4dc2021-03-26 16:12:09 +0100682static int elf_add_string(struct elf *elf, struct section *strtab, char *str)
683{
684 Elf_Data *data;
685 Elf_Scn *s;
686 int len;
687
688 if (!strtab)
689 strtab = find_section_by_name(elf, ".strtab");
690 if (!strtab) {
691 WARN("can't find .strtab section");
692 return -1;
693 }
694
695 s = elf_getscn(elf->elf, strtab->idx);
696 if (!s) {
697 WARN_ELF("elf_getscn");
698 return -1;
699 }
700
701 data = elf_newdata(s);
702 if (!data) {
703 WARN_ELF("elf_newdata");
704 return -1;
705 }
706
707 data->d_buf = str;
708 data->d_size = strlen(str) + 1;
709 data->d_align = 1;
710
711 len = strtab->len;
712 strtab->len += data->d_size;
713 strtab->changed = true;
714
715 return len;
716}
717
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500718struct section *elf_create_section(struct elf *elf, const char *name,
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +0200719 unsigned int sh_flags, size_t entsize, int nr)
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500720{
721 struct section *sec, *shstrtab;
722 size_t size = entsize * nr;
Michael Forney3c3ea502019-07-10 16:17:35 -0500723 Elf_Scn *s;
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500724
725 sec = malloc(sizeof(*sec));
726 if (!sec) {
727 perror("malloc");
728 return NULL;
729 }
730 memset(sec, 0, sizeof(*sec));
731
732 INIT_LIST_HEAD(&sec->symbol_list);
Matt Helsleyf1974222020-05-29 14:01:13 -0700733 INIT_LIST_HEAD(&sec->reloc_list);
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500734
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500735 s = elf_newscn(elf->elf);
736 if (!s) {
737 WARN_ELF("elf_newscn");
738 return NULL;
739 }
740
741 sec->name = strdup(name);
742 if (!sec->name) {
743 perror("strdup");
744 return NULL;
745 }
746
747 sec->idx = elf_ndxscn(s);
748 sec->len = size;
749 sec->changed = true;
750
751 sec->data = elf_newdata(s);
752 if (!sec->data) {
753 WARN_ELF("elf_newdata");
754 return NULL;
755 }
756
757 sec->data->d_size = size;
758 sec->data->d_align = 1;
759
760 if (size) {
761 sec->data->d_buf = malloc(size);
762 if (!sec->data->d_buf) {
763 perror("malloc");
764 return NULL;
765 }
766 memset(sec->data->d_buf, 0, size);
767 }
768
769 if (!gelf_getshdr(s, &sec->sh)) {
770 WARN_ELF("gelf_getshdr");
771 return NULL;
772 }
773
774 sec->sh.sh_size = size;
775 sec->sh.sh_entsize = entsize;
776 sec->sh.sh_type = SHT_PROGBITS;
777 sec->sh.sh_addralign = 1;
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +0200778 sec->sh.sh_flags = SHF_ALLOC | sh_flags;
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500779
Simon Ser6d77d3b2018-07-09 11:17:22 -0500780 /* Add section name to .shstrtab (or .strtab for Clang) */
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500781 shstrtab = find_section_by_name(elf, ".shstrtab");
Simon Ser6d77d3b2018-07-09 11:17:22 -0500782 if (!shstrtab)
783 shstrtab = find_section_by_name(elf, ".strtab");
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500784 if (!shstrtab) {
Simon Ser6d77d3b2018-07-09 11:17:22 -0500785 WARN("can't find .shstrtab or .strtab section");
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500786 return NULL;
787 }
Peter Zijlstra417a4dc2021-03-26 16:12:09 +0100788 sec->sh.sh_name = elf_add_string(elf, shstrtab, sec->name);
789 if (sec->sh.sh_name == -1)
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500790 return NULL;
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500791
Peter Zijlstra530389962020-03-10 18:43:35 +0100792 list_add_tail(&sec->list, &elf->sections);
Peter Zijlstra34f7c962020-03-12 14:29:38 +0100793 elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
794 elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
Peter Zijlstra530389962020-03-10 18:43:35 +0100795
Peter Zijlstra2b10be22020-04-17 23:15:00 +0200796 elf->changed = true;
797
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500798 return sec;
799}
800
Matt Helsleyfb414782020-05-29 14:01:14 -0700801static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base)
802{
803 char *relocname;
804 struct section *sec;
805
806 relocname = malloc(strlen(base->name) + strlen(".rel") + 1);
807 if (!relocname) {
808 perror("malloc");
809 return NULL;
810 }
811 strcpy(relocname, ".rel");
812 strcat(relocname, base->name);
813
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +0200814 sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0);
Matt Helsleyfb414782020-05-29 14:01:14 -0700815 free(relocname);
816 if (!sec)
817 return NULL;
818
819 base->reloc = sec;
820 sec->base = base;
821
822 sec->sh.sh_type = SHT_REL;
823 sec->sh.sh_addralign = 8;
824 sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
825 sec->sh.sh_info = base->idx;
826 sec->sh.sh_flags = SHF_INFO_LINK;
827
828 return sec;
829}
830
831static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base)
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500832{
Matt Helsleyf1974222020-05-29 14:01:13 -0700833 char *relocname;
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500834 struct section *sec;
835
Matt Helsleyf1974222020-05-29 14:01:13 -0700836 relocname = malloc(strlen(base->name) + strlen(".rela") + 1);
837 if (!relocname) {
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500838 perror("malloc");
839 return NULL;
840 }
Matt Helsleyf1974222020-05-29 14:01:13 -0700841 strcpy(relocname, ".rela");
842 strcat(relocname, base->name);
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500843
Josh Poimboeuf1e7e4782020-08-18 15:57:45 +0200844 sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0);
Matt Helsleyf1974222020-05-29 14:01:13 -0700845 free(relocname);
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500846 if (!sec)
847 return NULL;
848
Matt Helsleyf1974222020-05-29 14:01:13 -0700849 base->reloc = sec;
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500850 sec->base = base;
851
852 sec->sh.sh_type = SHT_RELA;
853 sec->sh.sh_addralign = 8;
854 sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
855 sec->sh.sh_info = base->idx;
856 sec->sh.sh_flags = SHF_INFO_LINK;
857
858 return sec;
859}
860
Peter Zijlstrad0c5c4c2021-03-26 16:12:08 +0100861static struct section *elf_create_reloc_section(struct elf *elf,
Matt Helsleyfb414782020-05-29 14:01:14 -0700862 struct section *base,
863 int reltype)
864{
865 switch (reltype) {
866 case SHT_REL: return elf_create_rel_reloc_section(elf, base);
867 case SHT_RELA: return elf_create_rela_reloc_section(elf, base);
868 default: return NULL;
869 }
870}
871
872static int elf_rebuild_rel_reloc_section(struct section *sec, int nr)
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500873{
Matt Helsleyf1974222020-05-29 14:01:13 -0700874 struct reloc *reloc;
Matt Helsleyfb414782020-05-29 14:01:14 -0700875 int idx = 0, size;
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100876 void *buf;
Matt Helsleyfb414782020-05-29 14:01:14 -0700877
878 /* Allocate a buffer for relocations */
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100879 size = nr * sizeof(GElf_Rel);
880 buf = malloc(size);
881 if (!buf) {
Matt Helsleyfb414782020-05-29 14:01:14 -0700882 perror("malloc");
883 return -1;
884 }
885
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100886 sec->data->d_buf = buf;
Matt Helsleyfb414782020-05-29 14:01:14 -0700887 sec->data->d_size = size;
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100888 sec->data->d_type = ELF_T_REL;
Matt Helsleyfb414782020-05-29 14:01:14 -0700889
890 sec->sh.sh_size = size;
891
892 idx = 0;
893 list_for_each_entry(reloc, &sec->reloc_list, list) {
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100894 reloc->rel.r_offset = reloc->offset;
895 reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
896 gelf_update_rel(sec->data, idx, &reloc->rel);
Matt Helsleyfb414782020-05-29 14:01:14 -0700897 idx++;
898 }
899
900 return 0;
901}
902
903static int elf_rebuild_rela_reloc_section(struct section *sec, int nr)
904{
905 struct reloc *reloc;
906 int idx = 0, size;
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100907 void *buf;
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500908
Matt Helsleyfb414782020-05-29 14:01:14 -0700909 /* Allocate a buffer for relocations with addends */
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100910 size = nr * sizeof(GElf_Rela);
911 buf = malloc(size);
912 if (!buf) {
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500913 perror("malloc");
914 return -1;
915 }
916
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100917 sec->data->d_buf = buf;
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500918 sec->data->d_size = size;
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100919 sec->data->d_type = ELF_T_RELA;
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500920
921 sec->sh.sh_size = size;
922
923 idx = 0;
Matt Helsleyf1974222020-05-29 14:01:13 -0700924 list_for_each_entry(reloc, &sec->reloc_list, list) {
Martin Schwidefskya1a664e2020-11-13 00:03:26 +0100925 reloc->rela.r_offset = reloc->offset;
926 reloc->rela.r_addend = reloc->addend;
927 reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
928 gelf_update_rela(sec->data, idx, &reloc->rela);
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500929 idx++;
930 }
931
932 return 0;
933}
934
Peter Zijlstra3a647602021-03-26 16:12:06 +0100935static int elf_rebuild_reloc_section(struct elf *elf, struct section *sec)
Matt Helsleyfb414782020-05-29 14:01:14 -0700936{
937 struct reloc *reloc;
938 int nr;
939
940 nr = 0;
941 list_for_each_entry(reloc, &sec->reloc_list, list)
942 nr++;
943
944 switch (sec->sh.sh_type) {
945 case SHT_REL: return elf_rebuild_rel_reloc_section(sec, nr);
946 case SHT_RELA: return elf_rebuild_rela_reloc_section(sec, nr);
947 default: return -1;
948 }
949}
950
Peter Zijlstrafdabdd02020-06-12 15:43:00 +0200951int elf_write_insn(struct elf *elf, struct section *sec,
952 unsigned long offset, unsigned int len,
953 const char *insn)
954{
955 Elf_Data *data = sec->data;
956
957 if (data->d_type != ELF_T_BYTE || data->d_off) {
958 WARN("write to unexpected data for section: %s", sec->name);
959 return -1;
960 }
961
962 memcpy(data->d_buf + offset, insn, len);
963 elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY);
964
965 elf->changed = true;
966
967 return 0;
968}
969
Peter Zijlstrad832c002020-06-18 17:55:29 +0200970int elf_write_reloc(struct elf *elf, struct reloc *reloc)
Peter Zijlstrafdabdd02020-06-12 15:43:00 +0200971{
Peter Zijlstrad832c002020-06-18 17:55:29 +0200972 struct section *sec = reloc->sec;
Peter Zijlstrafdabdd02020-06-12 15:43:00 +0200973
Peter Zijlstrad832c002020-06-18 17:55:29 +0200974 if (sec->sh.sh_type == SHT_REL) {
975 reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
976 reloc->rel.r_offset = reloc->offset;
Peter Zijlstrafdabdd02020-06-12 15:43:00 +0200977
Peter Zijlstrad832c002020-06-18 17:55:29 +0200978 if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) {
979 WARN_ELF("gelf_update_rel");
980 return -1;
981 }
982 } else {
983 reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
984 reloc->rela.r_addend = reloc->addend;
985 reloc->rela.r_offset = reloc->offset;
986
987 if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) {
988 WARN_ELF("gelf_update_rela");
989 return -1;
990 }
Peter Zijlstrafdabdd02020-06-12 15:43:00 +0200991 }
992
993 elf->changed = true;
994
995 return 0;
996}
997
Peter Zijlstra2b10be22020-04-17 23:15:00 +0200998int elf_write(struct elf *elf)
Josh Poimboeuf627fce12017-07-11 10:33:42 -0500999{
1000 struct section *sec;
1001 Elf_Scn *s;
1002
Peter Zijlstra3a647602021-03-26 16:12:06 +01001003 /* Update changed relocation sections and section headers: */
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001004 list_for_each_entry(sec, &elf->sections, list) {
1005 if (sec->changed) {
Peter Zijlstra3a647602021-03-26 16:12:06 +01001006 if (sec->base &&
1007 elf_rebuild_reloc_section(elf, sec)) {
1008 WARN("elf_rebuild_reloc_section");
1009 return -1;
1010 }
1011
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001012 s = elf_getscn(elf->elf, sec->idx);
1013 if (!s) {
1014 WARN_ELF("elf_getscn");
1015 return -1;
1016 }
Josh Poimboeuf97dab2a2017-09-15 02:17:11 -05001017 if (!gelf_update_shdr(s, &sec->sh)) {
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001018 WARN_ELF("gelf_update_shdr");
1019 return -1;
1020 }
Peter Zijlstra2b10be22020-04-17 23:15:00 +02001021
1022 sec->changed = false;
Peter Zijlstra3a647602021-03-26 16:12:06 +01001023 elf->changed = true;
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001024 }
1025 }
1026
Josh Poimboeuf97dab2a2017-09-15 02:17:11 -05001027 /* Make sure the new section header entries get updated properly. */
1028 elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY);
1029
1030 /* Write all changes to the file. */
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001031 if (elf_update(elf->elf, ELF_C_WRITE) < 0) {
1032 WARN_ELF("elf_update");
1033 return -1;
1034 }
1035
Peter Zijlstra2b10be22020-04-17 23:15:00 +02001036 elf->changed = false;
1037
Josh Poimboeuf627fce12017-07-11 10:33:42 -05001038 return 0;
1039}
1040
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001041void elf_close(struct elf *elf)
1042{
1043 struct section *sec, *tmpsec;
1044 struct symbol *sym, *tmpsym;
Matt Helsleyf1974222020-05-29 14:01:13 -07001045 struct reloc *reloc, *tmpreloc;
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001046
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001047 if (elf->elf)
1048 elf_end(elf->elf);
1049
1050 if (elf->fd > 0)
1051 close(elf->fd);
1052
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001053 list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) {
Josh Poimboeufa196e172016-03-09 00:06:57 -06001054 list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) {
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001055 list_del(&sym->list);
Josh Poimboeuf042ba732016-03-09 00:07:00 -06001056 hash_del(&sym->hash);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001057 free(sym);
1058 }
Matt Helsleyf1974222020-05-29 14:01:13 -07001059 list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) {
1060 list_del(&reloc->list);
1061 hash_del(&reloc->hash);
1062 free(reloc);
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001063 }
1064 list_del(&sec->list);
1065 free(sec);
1066 }
Josh Poimboeufbaa41462017-06-28 10:11:07 -05001067
Josh Poimboeuf442f04c2016-02-28 22:22:41 -06001068 free(elf);
1069}