xref: /openbmc/linux/tools/objtool/elf.c (revision a1a664ec)
11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2442f04c3SJosh Poimboeuf /*
3442f04c3SJosh Poimboeuf  * elf.c - ELF access library
4442f04c3SJosh Poimboeuf  *
5442f04c3SJosh Poimboeuf  * Adapted from kpatch (https://github.com/dynup/kpatch):
6442f04c3SJosh Poimboeuf  * Copyright (C) 2013-2015 Josh Poimboeuf <jpoimboe@redhat.com>
7442f04c3SJosh Poimboeuf  * Copyright (C) 2014 Seth Jennings <sjenning@redhat.com>
8442f04c3SJosh Poimboeuf  */
9442f04c3SJosh Poimboeuf 
10442f04c3SJosh Poimboeuf #include <sys/types.h>
11442f04c3SJosh Poimboeuf #include <sys/stat.h>
12442f04c3SJosh Poimboeuf #include <fcntl.h>
13442f04c3SJosh Poimboeuf #include <stdio.h>
14442f04c3SJosh Poimboeuf #include <stdlib.h>
15442f04c3SJosh Poimboeuf #include <string.h>
16442f04c3SJosh Poimboeuf #include <unistd.h>
17385d11b1SJosh Poimboeuf #include <errno.h>
181e11f3fdSPeter Zijlstra #include "builtin.h"
19442f04c3SJosh Poimboeuf 
20442f04c3SJosh Poimboeuf #include "elf.h"
21442f04c3SJosh Poimboeuf #include "warn.h"
22442f04c3SJosh Poimboeuf 
2322566c16SArtem Savkov #define MAX_NAME_LEN 128
2422566c16SArtem Savkov 
25ae358196SPeter Zijlstra static inline u32 str_hash(const char *str)
26ae358196SPeter Zijlstra {
27ae358196SPeter Zijlstra 	return jhash(str, strlen(str), 0);
28ae358196SPeter Zijlstra }
29ae358196SPeter Zijlstra 
3034f7c96dSPeter Zijlstra static inline int elf_hash_bits(void)
3134f7c96dSPeter Zijlstra {
3234f7c96dSPeter Zijlstra 	return vmlinux ? ELF_HASH_BITS : 16;
3334f7c96dSPeter Zijlstra }
3434f7c96dSPeter Zijlstra 
3534f7c96dSPeter Zijlstra #define elf_hash_add(hashtable, node, key) \
3634f7c96dSPeter Zijlstra 	hlist_add_head(node, &hashtable[hash_min(key, elf_hash_bits())])
3734f7c96dSPeter Zijlstra 
3834f7c96dSPeter Zijlstra static void elf_hash_init(struct hlist_head *table)
3934f7c96dSPeter Zijlstra {
4034f7c96dSPeter Zijlstra 	__hash_init(table, 1U << elf_hash_bits());
4134f7c96dSPeter Zijlstra }
4234f7c96dSPeter Zijlstra 
4334f7c96dSPeter Zijlstra #define elf_hash_for_each_possible(name, obj, member, key)			\
4434f7c96dSPeter Zijlstra 	hlist_for_each_entry(obj, &name[hash_min(key, elf_hash_bits())], member)
4534f7c96dSPeter Zijlstra 
462a362eccSPeter Zijlstra static void rb_add(struct rb_root *tree, struct rb_node *node,
472a362eccSPeter Zijlstra 		   int (*cmp)(struct rb_node *, const struct rb_node *))
482a362eccSPeter Zijlstra {
492a362eccSPeter Zijlstra 	struct rb_node **link = &tree->rb_node;
502a362eccSPeter Zijlstra 	struct rb_node *parent = NULL;
512a362eccSPeter Zijlstra 
522a362eccSPeter Zijlstra 	while (*link) {
532a362eccSPeter Zijlstra 		parent = *link;
542a362eccSPeter Zijlstra 		if (cmp(node, parent) < 0)
552a362eccSPeter Zijlstra 			link = &parent->rb_left;
562a362eccSPeter Zijlstra 		else
572a362eccSPeter Zijlstra 			link = &parent->rb_right;
582a362eccSPeter Zijlstra 	}
592a362eccSPeter Zijlstra 
602a362eccSPeter Zijlstra 	rb_link_node(node, parent, link);
612a362eccSPeter Zijlstra 	rb_insert_color(node, tree);
622a362eccSPeter Zijlstra }
632a362eccSPeter Zijlstra 
64b490f453SMiroslav Benes static struct rb_node *rb_find_first(const struct rb_root *tree, const void *key,
652a362eccSPeter Zijlstra 			       int (*cmp)(const void *key, const struct rb_node *))
662a362eccSPeter Zijlstra {
672a362eccSPeter Zijlstra 	struct rb_node *node = tree->rb_node;
682a362eccSPeter Zijlstra 	struct rb_node *match = NULL;
692a362eccSPeter Zijlstra 
702a362eccSPeter Zijlstra 	while (node) {
712a362eccSPeter Zijlstra 		int c = cmp(key, node);
722a362eccSPeter Zijlstra 		if (c <= 0) {
732a362eccSPeter Zijlstra 			if (!c)
742a362eccSPeter Zijlstra 				match = node;
752a362eccSPeter Zijlstra 			node = node->rb_left;
762a362eccSPeter Zijlstra 		} else if (c > 0) {
772a362eccSPeter Zijlstra 			node = node->rb_right;
782a362eccSPeter Zijlstra 		}
792a362eccSPeter Zijlstra 	}
802a362eccSPeter Zijlstra 
812a362eccSPeter Zijlstra 	return match;
822a362eccSPeter Zijlstra }
832a362eccSPeter Zijlstra 
842a362eccSPeter Zijlstra static struct rb_node *rb_next_match(struct rb_node *node, const void *key,
852a362eccSPeter Zijlstra 				    int (*cmp)(const void *key, const struct rb_node *))
862a362eccSPeter Zijlstra {
872a362eccSPeter Zijlstra 	node = rb_next(node);
882a362eccSPeter Zijlstra 	if (node && cmp(key, node))
892a362eccSPeter Zijlstra 		node = NULL;
902a362eccSPeter Zijlstra 	return node;
912a362eccSPeter Zijlstra }
922a362eccSPeter Zijlstra 
932a362eccSPeter Zijlstra #define rb_for_each(tree, node, key, cmp) \
942a362eccSPeter Zijlstra 	for ((node) = rb_find_first((tree), (key), (cmp)); \
952a362eccSPeter Zijlstra 	     (node); (node) = rb_next_match((node), (key), (cmp)))
962a362eccSPeter Zijlstra 
972a362eccSPeter Zijlstra static int symbol_to_offset(struct rb_node *a, const struct rb_node *b)
982a362eccSPeter Zijlstra {
992a362eccSPeter Zijlstra 	struct symbol *sa = rb_entry(a, struct symbol, node);
1002a362eccSPeter Zijlstra 	struct symbol *sb = rb_entry(b, struct symbol, node);
1012a362eccSPeter Zijlstra 
1022a362eccSPeter Zijlstra 	if (sa->offset < sb->offset)
1032a362eccSPeter Zijlstra 		return -1;
1042a362eccSPeter Zijlstra 	if (sa->offset > sb->offset)
1052a362eccSPeter Zijlstra 		return 1;
1062a362eccSPeter Zijlstra 
1072a362eccSPeter Zijlstra 	if (sa->len < sb->len)
1082a362eccSPeter Zijlstra 		return -1;
1092a362eccSPeter Zijlstra 	if (sa->len > sb->len)
1102a362eccSPeter Zijlstra 		return 1;
1112a362eccSPeter Zijlstra 
1122a362eccSPeter Zijlstra 	sa->alias = sb;
1132a362eccSPeter Zijlstra 
1142a362eccSPeter Zijlstra 	return 0;
1152a362eccSPeter Zijlstra }
1162a362eccSPeter Zijlstra 
1172a362eccSPeter Zijlstra static int symbol_by_offset(const void *key, const struct rb_node *node)
1182a362eccSPeter Zijlstra {
1192a362eccSPeter Zijlstra 	const struct symbol *s = rb_entry(node, struct symbol, node);
1202a362eccSPeter Zijlstra 	const unsigned long *o = key;
1212a362eccSPeter Zijlstra 
1222a362eccSPeter Zijlstra 	if (*o < s->offset)
1232a362eccSPeter Zijlstra 		return -1;
1245377cae9SJulien Thierry 	if (*o >= s->offset + s->len)
1252a362eccSPeter Zijlstra 		return 1;
1262a362eccSPeter Zijlstra 
1272a362eccSPeter Zijlstra 	return 0;
1282a362eccSPeter Zijlstra }
1292a362eccSPeter Zijlstra 
130894e48caSIngo Molnar struct section *find_section_by_name(const struct elf *elf, const char *name)
131442f04c3SJosh Poimboeuf {
132442f04c3SJosh Poimboeuf 	struct section *sec;
133442f04c3SJosh Poimboeuf 
13434f7c96dSPeter Zijlstra 	elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
135442f04c3SJosh Poimboeuf 		if (!strcmp(sec->name, name))
136442f04c3SJosh Poimboeuf 			return sec;
137442f04c3SJosh Poimboeuf 
138442f04c3SJosh Poimboeuf 	return NULL;
139442f04c3SJosh Poimboeuf }
140442f04c3SJosh Poimboeuf 
141442f04c3SJosh Poimboeuf static struct section *find_section_by_index(struct elf *elf,
142442f04c3SJosh Poimboeuf 					     unsigned int idx)
143442f04c3SJosh Poimboeuf {
144442f04c3SJosh Poimboeuf 	struct section *sec;
145442f04c3SJosh Poimboeuf 
14634f7c96dSPeter Zijlstra 	elf_hash_for_each_possible(elf->section_hash, sec, hash, idx)
147442f04c3SJosh Poimboeuf 		if (sec->idx == idx)
148442f04c3SJosh Poimboeuf 			return sec;
149442f04c3SJosh Poimboeuf 
150442f04c3SJosh Poimboeuf 	return NULL;
151442f04c3SJosh Poimboeuf }
152442f04c3SJosh Poimboeuf 
153442f04c3SJosh Poimboeuf static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
154442f04c3SJosh Poimboeuf {
155442f04c3SJosh Poimboeuf 	struct symbol *sym;
156442f04c3SJosh Poimboeuf 
15734f7c96dSPeter Zijlstra 	elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
158442f04c3SJosh Poimboeuf 		if (sym->idx == idx)
159442f04c3SJosh Poimboeuf 			return sym;
160442f04c3SJosh Poimboeuf 
161442f04c3SJosh Poimboeuf 	return NULL;
162442f04c3SJosh Poimboeuf }
163442f04c3SJosh Poimboeuf 
164442f04c3SJosh Poimboeuf struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
165442f04c3SJosh Poimboeuf {
1662a362eccSPeter Zijlstra 	struct rb_node *node;
167442f04c3SJosh Poimboeuf 
1682a362eccSPeter Zijlstra 	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
1692a362eccSPeter Zijlstra 		struct symbol *s = rb_entry(node, struct symbol, node);
1702a362eccSPeter Zijlstra 
1712a362eccSPeter Zijlstra 		if (s->offset == offset && s->type != STT_SECTION)
1722a362eccSPeter Zijlstra 			return s;
1732a362eccSPeter Zijlstra 	}
1747acfe531SJosh Poimboeuf 
1757acfe531SJosh Poimboeuf 	return NULL;
1767acfe531SJosh Poimboeuf }
1777acfe531SJosh Poimboeuf 
1787acfe531SJosh Poimboeuf struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
1797acfe531SJosh Poimboeuf {
1802a362eccSPeter Zijlstra 	struct rb_node *node;
1817acfe531SJosh Poimboeuf 
1822a362eccSPeter Zijlstra 	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
1832a362eccSPeter Zijlstra 		struct symbol *s = rb_entry(node, struct symbol, node);
1842a362eccSPeter Zijlstra 
1852a362eccSPeter Zijlstra 		if (s->offset == offset && s->type == STT_FUNC)
1862a362eccSPeter Zijlstra 			return s;
1872a362eccSPeter Zijlstra 	}
1882a362eccSPeter Zijlstra 
1892a362eccSPeter Zijlstra 	return NULL;
1902a362eccSPeter Zijlstra }
1912a362eccSPeter Zijlstra 
192b490f453SMiroslav Benes struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset)
1932a362eccSPeter Zijlstra {
1942a362eccSPeter Zijlstra 	struct rb_node *node;
1952a362eccSPeter Zijlstra 
1962a362eccSPeter Zijlstra 	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
1972a362eccSPeter Zijlstra 		struct symbol *s = rb_entry(node, struct symbol, node);
1982a362eccSPeter Zijlstra 
1992a362eccSPeter Zijlstra 		if (s->type != STT_SECTION)
2002a362eccSPeter Zijlstra 			return s;
2012a362eccSPeter Zijlstra 	}
2022a362eccSPeter Zijlstra 
2032a362eccSPeter Zijlstra 	return NULL;
2042a362eccSPeter Zijlstra }
2052a362eccSPeter Zijlstra 
20653d20720SPeter Zijlstra struct symbol *find_func_containing(struct section *sec, unsigned long offset)
2072a362eccSPeter Zijlstra {
2082a362eccSPeter Zijlstra 	struct rb_node *node;
2092a362eccSPeter Zijlstra 
2102a362eccSPeter Zijlstra 	rb_for_each(&sec->symbol_tree, node, &offset, symbol_by_offset) {
2112a362eccSPeter Zijlstra 		struct symbol *s = rb_entry(node, struct symbol, node);
2122a362eccSPeter Zijlstra 
2132a362eccSPeter Zijlstra 		if (s->type == STT_FUNC)
2142a362eccSPeter Zijlstra 			return s;
2152a362eccSPeter Zijlstra 	}
216442f04c3SJosh Poimboeuf 
217442f04c3SJosh Poimboeuf 	return NULL;
218442f04c3SJosh Poimboeuf }
219442f04c3SJosh Poimboeuf 
220894e48caSIngo Molnar struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)
22113810435SJosh Poimboeuf {
22213810435SJosh Poimboeuf 	struct symbol *sym;
22313810435SJosh Poimboeuf 
22434f7c96dSPeter Zijlstra 	elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
22513810435SJosh Poimboeuf 		if (!strcmp(sym->name, name))
22613810435SJosh Poimboeuf 			return sym;
22713810435SJosh Poimboeuf 
22813810435SJosh Poimboeuf 	return NULL;
22913810435SJosh Poimboeuf }
23013810435SJosh Poimboeuf 
231f1974222SMatt Helsley struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
2328b5fa6bcSPeter Zijlstra 				     unsigned long offset, unsigned int len)
233442f04c3SJosh Poimboeuf {
234f1974222SMatt Helsley 	struct reloc *reloc, *r = NULL;
235042ba73fSJosh Poimboeuf 	unsigned long o;
236442f04c3SJosh Poimboeuf 
237f1974222SMatt Helsley 	if (!sec->reloc)
238442f04c3SJosh Poimboeuf 		return NULL;
239442f04c3SJosh Poimboeuf 
240f1974222SMatt Helsley 	sec = sec->reloc;
2418b5fa6bcSPeter Zijlstra 
24274b873e4SPeter Zijlstra 	for_offset_range(o, offset, offset + len) {
243f1974222SMatt Helsley 		elf_hash_for_each_possible(elf->reloc_hash, reloc, hash,
2448b5fa6bcSPeter Zijlstra 				       sec_offset_hash(sec, o)) {
245f1974222SMatt Helsley 			if (reloc->sec != sec)
24674b873e4SPeter Zijlstra 				continue;
24774b873e4SPeter Zijlstra 
248f1974222SMatt Helsley 			if (reloc->offset >= offset && reloc->offset < offset + len) {
249f1974222SMatt Helsley 				if (!r || reloc->offset < r->offset)
250f1974222SMatt Helsley 					r = reloc;
2518b5fa6bcSPeter Zijlstra 			}
2528b5fa6bcSPeter Zijlstra 		}
25374b873e4SPeter Zijlstra 		if (r)
25474b873e4SPeter Zijlstra 			return r;
25574b873e4SPeter Zijlstra 	}
256442f04c3SJosh Poimboeuf 
257442f04c3SJosh Poimboeuf 	return NULL;
258442f04c3SJosh Poimboeuf }
259442f04c3SJosh Poimboeuf 
260f1974222SMatt Helsley struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset)
261442f04c3SJosh Poimboeuf {
262f1974222SMatt Helsley 	return find_reloc_by_dest_range(elf, sec, offset, 1);
263442f04c3SJosh Poimboeuf }
264442f04c3SJosh Poimboeuf 
26544f6a7c0SJosh Poimboeuf void insn_to_reloc_sym_addend(struct section *sec, unsigned long offset,
26644f6a7c0SJosh Poimboeuf 			      struct reloc *reloc)
26744f6a7c0SJosh Poimboeuf {
26844f6a7c0SJosh Poimboeuf 	if (sec->sym) {
26944f6a7c0SJosh Poimboeuf 		reloc->sym = sec->sym;
27044f6a7c0SJosh Poimboeuf 		reloc->addend = offset;
27144f6a7c0SJosh Poimboeuf 		return;
27244f6a7c0SJosh Poimboeuf 	}
27344f6a7c0SJosh Poimboeuf 
27444f6a7c0SJosh Poimboeuf 	/*
27544f6a7c0SJosh Poimboeuf 	 * The Clang assembler strips section symbols, so we have to reference
27644f6a7c0SJosh Poimboeuf 	 * the function symbol instead:
27744f6a7c0SJosh Poimboeuf 	 */
27844f6a7c0SJosh Poimboeuf 	reloc->sym = find_symbol_containing(sec, offset);
27944f6a7c0SJosh Poimboeuf 	if (!reloc->sym) {
28044f6a7c0SJosh Poimboeuf 		/*
28144f6a7c0SJosh Poimboeuf 		 * Hack alert.  This happens when we need to reference the NOP
28244f6a7c0SJosh Poimboeuf 		 * pad insn immediately after the function.
28344f6a7c0SJosh Poimboeuf 		 */
28444f6a7c0SJosh Poimboeuf 		reloc->sym = find_symbol_containing(sec, offset - 1);
28544f6a7c0SJosh Poimboeuf 	}
28644f6a7c0SJosh Poimboeuf 
28744f6a7c0SJosh Poimboeuf 	if (reloc->sym)
28844f6a7c0SJosh Poimboeuf 		reloc->addend = offset - reloc->sym->offset;
28944f6a7c0SJosh Poimboeuf }
29044f6a7c0SJosh Poimboeuf 
291442f04c3SJosh Poimboeuf static int read_sections(struct elf *elf)
292442f04c3SJosh Poimboeuf {
293442f04c3SJosh Poimboeuf 	Elf_Scn *s = NULL;
294442f04c3SJosh Poimboeuf 	struct section *sec;
295442f04c3SJosh Poimboeuf 	size_t shstrndx, sections_nr;
296442f04c3SJosh Poimboeuf 	int i;
297442f04c3SJosh Poimboeuf 
298442f04c3SJosh Poimboeuf 	if (elf_getshdrnum(elf->elf, &sections_nr)) {
299baa41469SJosh Poimboeuf 		WARN_ELF("elf_getshdrnum");
300442f04c3SJosh Poimboeuf 		return -1;
301442f04c3SJosh Poimboeuf 	}
302442f04c3SJosh Poimboeuf 
303442f04c3SJosh Poimboeuf 	if (elf_getshdrstrndx(elf->elf, &shstrndx)) {
304baa41469SJosh Poimboeuf 		WARN_ELF("elf_getshdrstrndx");
305442f04c3SJosh Poimboeuf 		return -1;
306442f04c3SJosh Poimboeuf 	}
307442f04c3SJosh Poimboeuf 
308442f04c3SJosh Poimboeuf 	for (i = 0; i < sections_nr; i++) {
309442f04c3SJosh Poimboeuf 		sec = malloc(sizeof(*sec));
310442f04c3SJosh Poimboeuf 		if (!sec) {
311442f04c3SJosh Poimboeuf 			perror("malloc");
312442f04c3SJosh Poimboeuf 			return -1;
313442f04c3SJosh Poimboeuf 		}
314442f04c3SJosh Poimboeuf 		memset(sec, 0, sizeof(*sec));
315442f04c3SJosh Poimboeuf 
316a196e171SJosh Poimboeuf 		INIT_LIST_HEAD(&sec->symbol_list);
317f1974222SMatt Helsley 		INIT_LIST_HEAD(&sec->reloc_list);
318442f04c3SJosh Poimboeuf 
319442f04c3SJosh Poimboeuf 		s = elf_getscn(elf->elf, i);
320442f04c3SJosh Poimboeuf 		if (!s) {
321baa41469SJosh Poimboeuf 			WARN_ELF("elf_getscn");
322442f04c3SJosh Poimboeuf 			return -1;
323442f04c3SJosh Poimboeuf 		}
324442f04c3SJosh Poimboeuf 
325442f04c3SJosh Poimboeuf 		sec->idx = elf_ndxscn(s);
326442f04c3SJosh Poimboeuf 
327442f04c3SJosh Poimboeuf 		if (!gelf_getshdr(s, &sec->sh)) {
328baa41469SJosh Poimboeuf 			WARN_ELF("gelf_getshdr");
329442f04c3SJosh Poimboeuf 			return -1;
330442f04c3SJosh Poimboeuf 		}
331442f04c3SJosh Poimboeuf 
332442f04c3SJosh Poimboeuf 		sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name);
333442f04c3SJosh Poimboeuf 		if (!sec->name) {
334baa41469SJosh Poimboeuf 			WARN_ELF("elf_strptr");
335442f04c3SJosh Poimboeuf 			return -1;
336442f04c3SJosh Poimboeuf 		}
337442f04c3SJosh Poimboeuf 
338df968c93SPetr Vandrovec 		if (sec->sh.sh_size != 0) {
339baa41469SJosh Poimboeuf 			sec->data = elf_getdata(s, NULL);
340baa41469SJosh Poimboeuf 			if (!sec->data) {
341baa41469SJosh Poimboeuf 				WARN_ELF("elf_getdata");
342442f04c3SJosh Poimboeuf 				return -1;
343442f04c3SJosh Poimboeuf 			}
344baa41469SJosh Poimboeuf 			if (sec->data->d_off != 0 ||
345baa41469SJosh Poimboeuf 			    sec->data->d_size != sec->sh.sh_size) {
346df968c93SPetr Vandrovec 				WARN("unexpected data attributes for %s",
347df968c93SPetr Vandrovec 				     sec->name);
348442f04c3SJosh Poimboeuf 				return -1;
349442f04c3SJosh Poimboeuf 			}
350df968c93SPetr Vandrovec 		}
351df968c93SPetr Vandrovec 		sec->len = sec->sh.sh_size;
35253038996SPeter Zijlstra 
35353038996SPeter Zijlstra 		list_add_tail(&sec->list, &elf->sections);
35434f7c96dSPeter Zijlstra 		elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
35534f7c96dSPeter Zijlstra 		elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
356442f04c3SJosh Poimboeuf 	}
357442f04c3SJosh Poimboeuf 
3581e11f3fdSPeter Zijlstra 	if (stats)
3591e11f3fdSPeter Zijlstra 		printf("nr_sections: %lu\n", (unsigned long)sections_nr);
3601e11f3fdSPeter Zijlstra 
361442f04c3SJosh Poimboeuf 	/* sanity check, one more call to elf_nextscn() should return NULL */
362442f04c3SJosh Poimboeuf 	if (elf_nextscn(elf->elf, s)) {
363442f04c3SJosh Poimboeuf 		WARN("section entry mismatch");
364442f04c3SJosh Poimboeuf 		return -1;
365442f04c3SJosh Poimboeuf 	}
366442f04c3SJosh Poimboeuf 
367442f04c3SJosh Poimboeuf 	return 0;
368442f04c3SJosh Poimboeuf }
369442f04c3SJosh Poimboeuf 
370442f04c3SJosh Poimboeuf static int read_symbols(struct elf *elf)
371442f04c3SJosh Poimboeuf {
37228fe1d7bSSami Tolvanen 	struct section *symtab, *symtab_shndx, *sec;
3732a362eccSPeter Zijlstra 	struct symbol *sym, *pfunc;
3742a362eccSPeter Zijlstra 	struct list_head *entry;
3752a362eccSPeter Zijlstra 	struct rb_node *pnode;
376442f04c3SJosh Poimboeuf 	int symbols_nr, i;
37713810435SJosh Poimboeuf 	char *coldstr;
37828fe1d7bSSami Tolvanen 	Elf_Data *shndx_data = NULL;
37928fe1d7bSSami Tolvanen 	Elf32_Word shndx;
380442f04c3SJosh Poimboeuf 
381442f04c3SJosh Poimboeuf 	symtab = find_section_by_name(elf, ".symtab");
382442f04c3SJosh Poimboeuf 	if (!symtab) {
383442f04c3SJosh Poimboeuf 		WARN("missing symbol table");
384442f04c3SJosh Poimboeuf 		return -1;
385442f04c3SJosh Poimboeuf 	}
386442f04c3SJosh Poimboeuf 
38728fe1d7bSSami Tolvanen 	symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
38828fe1d7bSSami Tolvanen 	if (symtab_shndx)
38928fe1d7bSSami Tolvanen 		shndx_data = symtab_shndx->data;
39028fe1d7bSSami Tolvanen 
391442f04c3SJosh Poimboeuf 	symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
392442f04c3SJosh Poimboeuf 
393442f04c3SJosh Poimboeuf 	for (i = 0; i < symbols_nr; i++) {
394442f04c3SJosh Poimboeuf 		sym = malloc(sizeof(*sym));
395442f04c3SJosh Poimboeuf 		if (!sym) {
396442f04c3SJosh Poimboeuf 			perror("malloc");
397442f04c3SJosh Poimboeuf 			return -1;
398442f04c3SJosh Poimboeuf 		}
399442f04c3SJosh Poimboeuf 		memset(sym, 0, sizeof(*sym));
4002a362eccSPeter Zijlstra 		sym->alias = sym;
401442f04c3SJosh Poimboeuf 
402442f04c3SJosh Poimboeuf 		sym->idx = i;
403442f04c3SJosh Poimboeuf 
40428fe1d7bSSami Tolvanen 		if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym,
40528fe1d7bSSami Tolvanen 				      &shndx)) {
40628fe1d7bSSami Tolvanen 			WARN_ELF("gelf_getsymshndx");
407442f04c3SJosh Poimboeuf 			goto err;
408442f04c3SJosh Poimboeuf 		}
409442f04c3SJosh Poimboeuf 
410442f04c3SJosh Poimboeuf 		sym->name = elf_strptr(elf->elf, symtab->sh.sh_link,
411442f04c3SJosh Poimboeuf 				       sym->sym.st_name);
412442f04c3SJosh Poimboeuf 		if (!sym->name) {
413baa41469SJosh Poimboeuf 			WARN_ELF("elf_strptr");
414442f04c3SJosh Poimboeuf 			goto err;
415442f04c3SJosh Poimboeuf 		}
416442f04c3SJosh Poimboeuf 
417442f04c3SJosh Poimboeuf 		sym->type = GELF_ST_TYPE(sym->sym.st_info);
418442f04c3SJosh Poimboeuf 		sym->bind = GELF_ST_BIND(sym->sym.st_info);
419442f04c3SJosh Poimboeuf 
42028fe1d7bSSami Tolvanen 		if ((sym->sym.st_shndx > SHN_UNDEF &&
42128fe1d7bSSami Tolvanen 		     sym->sym.st_shndx < SHN_LORESERVE) ||
42228fe1d7bSSami Tolvanen 		    (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) {
42328fe1d7bSSami Tolvanen 			if (sym->sym.st_shndx != SHN_XINDEX)
42428fe1d7bSSami Tolvanen 				shndx = sym->sym.st_shndx;
42528fe1d7bSSami Tolvanen 
42628fe1d7bSSami Tolvanen 			sym->sec = find_section_by_index(elf, shndx);
427442f04c3SJosh Poimboeuf 			if (!sym->sec) {
428442f04c3SJosh Poimboeuf 				WARN("couldn't find section for symbol %s",
429442f04c3SJosh Poimboeuf 				     sym->name);
430442f04c3SJosh Poimboeuf 				goto err;
431442f04c3SJosh Poimboeuf 			}
432442f04c3SJosh Poimboeuf 			if (sym->type == STT_SECTION) {
433442f04c3SJosh Poimboeuf 				sym->name = sym->sec->name;
434442f04c3SJosh Poimboeuf 				sym->sec->sym = sym;
435442f04c3SJosh Poimboeuf 			}
436442f04c3SJosh Poimboeuf 		} else
437442f04c3SJosh Poimboeuf 			sym->sec = find_section_by_index(elf, 0);
438442f04c3SJosh Poimboeuf 
439442f04c3SJosh Poimboeuf 		sym->offset = sym->sym.st_value;
440442f04c3SJosh Poimboeuf 		sym->len = sym->sym.st_size;
441442f04c3SJosh Poimboeuf 
4422a362eccSPeter Zijlstra 		rb_add(&sym->sec->symbol_tree, &sym->node, symbol_to_offset);
4432a362eccSPeter Zijlstra 		pnode = rb_prev(&sym->node);
4442a362eccSPeter Zijlstra 		if (pnode)
4452a362eccSPeter Zijlstra 			entry = &rb_entry(pnode, struct symbol, node)->list;
4462a362eccSPeter Zijlstra 		else
447a196e171SJosh Poimboeuf 			entry = &sym->sec->symbol_list;
448442f04c3SJosh Poimboeuf 		list_add(&sym->list, entry);
44934f7c96dSPeter Zijlstra 		elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx);
45034f7c96dSPeter Zijlstra 		elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
451442f04c3SJosh Poimboeuf 	}
452442f04c3SJosh Poimboeuf 
4531e11f3fdSPeter Zijlstra 	if (stats)
4541e11f3fdSPeter Zijlstra 		printf("nr_symbols: %lu\n", (unsigned long)symbols_nr);
4551e11f3fdSPeter Zijlstra 
45613810435SJosh Poimboeuf 	/* Create parent/child links for any cold subfunctions */
45713810435SJosh Poimboeuf 	list_for_each_entry(sec, &elf->sections, list) {
45813810435SJosh Poimboeuf 		list_for_each_entry(sym, &sec->symbol_list, list) {
45922566c16SArtem Savkov 			char pname[MAX_NAME_LEN + 1];
46022566c16SArtem Savkov 			size_t pnamelen;
46113810435SJosh Poimboeuf 			if (sym->type != STT_FUNC)
46213810435SJosh Poimboeuf 				continue;
463e000acc1SKristen Carlson Accardi 
464e000acc1SKristen Carlson Accardi 			if (sym->pfunc == NULL)
465e000acc1SKristen Carlson Accardi 				sym->pfunc = sym;
466e000acc1SKristen Carlson Accardi 
467e000acc1SKristen Carlson Accardi 			if (sym->cfunc == NULL)
468e000acc1SKristen Carlson Accardi 				sym->cfunc = sym;
469e000acc1SKristen Carlson Accardi 
470bcb6fb5dSJosh Poimboeuf 			coldstr = strstr(sym->name, ".cold");
47108b393d0SJosh Poimboeuf 			if (!coldstr)
47208b393d0SJosh Poimboeuf 				continue;
47308b393d0SJosh Poimboeuf 
47422566c16SArtem Savkov 			pnamelen = coldstr - sym->name;
47522566c16SArtem Savkov 			if (pnamelen > MAX_NAME_LEN) {
47622566c16SArtem Savkov 				WARN("%s(): parent function name exceeds maximum length of %d characters",
47722566c16SArtem Savkov 				     sym->name, MAX_NAME_LEN);
47822566c16SArtem Savkov 				return -1;
47922566c16SArtem Savkov 			}
48022566c16SArtem Savkov 
48122566c16SArtem Savkov 			strncpy(pname, sym->name, pnamelen);
48222566c16SArtem Savkov 			pname[pnamelen] = '\0';
48322566c16SArtem Savkov 			pfunc = find_symbol_by_name(elf, pname);
48413810435SJosh Poimboeuf 
48513810435SJosh Poimboeuf 			if (!pfunc) {
48613810435SJosh Poimboeuf 				WARN("%s(): can't find parent function",
48713810435SJosh Poimboeuf 				     sym->name);
4880b9301fbSArtem Savkov 				return -1;
48913810435SJosh Poimboeuf 			}
49013810435SJosh Poimboeuf 
49113810435SJosh Poimboeuf 			sym->pfunc = pfunc;
49213810435SJosh Poimboeuf 			pfunc->cfunc = sym;
49308b393d0SJosh Poimboeuf 
49408b393d0SJosh Poimboeuf 			/*
49508b393d0SJosh Poimboeuf 			 * Unfortunately, -fnoreorder-functions puts the child
49608b393d0SJosh Poimboeuf 			 * inside the parent.  Remove the overlap so we can
49708b393d0SJosh Poimboeuf 			 * have sane assumptions.
49808b393d0SJosh Poimboeuf 			 *
49908b393d0SJosh Poimboeuf 			 * Note that pfunc->len now no longer matches
50008b393d0SJosh Poimboeuf 			 * pfunc->sym.st_size.
50108b393d0SJosh Poimboeuf 			 */
50208b393d0SJosh Poimboeuf 			if (sym->sec == pfunc->sec &&
50308b393d0SJosh Poimboeuf 			    sym->offset >= pfunc->offset &&
50408b393d0SJosh Poimboeuf 			    sym->offset + sym->len == pfunc->offset + pfunc->len) {
50508b393d0SJosh Poimboeuf 				pfunc->len -= sym->len;
50613810435SJosh Poimboeuf 			}
50713810435SJosh Poimboeuf 		}
50813810435SJosh Poimboeuf 	}
50913810435SJosh Poimboeuf 
510442f04c3SJosh Poimboeuf 	return 0;
511442f04c3SJosh Poimboeuf 
512442f04c3SJosh Poimboeuf err:
513442f04c3SJosh Poimboeuf 	free(sym);
514442f04c3SJosh Poimboeuf 	return -1;
515442f04c3SJosh Poimboeuf }
516442f04c3SJosh Poimboeuf 
517f1974222SMatt Helsley void elf_add_reloc(struct elf *elf, struct reloc *reloc)
51834f7c96dSPeter Zijlstra {
519f1974222SMatt Helsley 	struct section *sec = reloc->sec;
52034f7c96dSPeter Zijlstra 
521f1974222SMatt Helsley 	list_add_tail(&reloc->list, &sec->reloc_list);
522f1974222SMatt Helsley 	elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc));
52334f7c96dSPeter Zijlstra }
52434f7c96dSPeter Zijlstra 
525fb414783SMatt Helsley static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)
526fb414783SMatt Helsley {
527fb414783SMatt Helsley 	if (!gelf_getrel(sec->data, i, &reloc->rel)) {
528fb414783SMatt Helsley 		WARN_ELF("gelf_getrel");
529fb414783SMatt Helsley 		return -1;
530fb414783SMatt Helsley 	}
531fb414783SMatt Helsley 	reloc->type = GELF_R_TYPE(reloc->rel.r_info);
532fb414783SMatt Helsley 	reloc->addend = 0;
533fb414783SMatt Helsley 	reloc->offset = reloc->rel.r_offset;
534fb414783SMatt Helsley 	*symndx = GELF_R_SYM(reloc->rel.r_info);
535fb414783SMatt Helsley 	return 0;
536fb414783SMatt Helsley }
537fb414783SMatt Helsley 
538fb414783SMatt Helsley static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)
539fb414783SMatt Helsley {
540fb414783SMatt Helsley 	if (!gelf_getrela(sec->data, i, &reloc->rela)) {
541fb414783SMatt Helsley 		WARN_ELF("gelf_getrela");
542fb414783SMatt Helsley 		return -1;
543fb414783SMatt Helsley 	}
544fb414783SMatt Helsley 	reloc->type = GELF_R_TYPE(reloc->rela.r_info);
545fb414783SMatt Helsley 	reloc->addend = reloc->rela.r_addend;
546fb414783SMatt Helsley 	reloc->offset = reloc->rela.r_offset;
547fb414783SMatt Helsley 	*symndx = GELF_R_SYM(reloc->rela.r_info);
548fb414783SMatt Helsley 	return 0;
549fb414783SMatt Helsley }
550fb414783SMatt Helsley 
551f1974222SMatt Helsley static int read_relocs(struct elf *elf)
552442f04c3SJosh Poimboeuf {
553442f04c3SJosh Poimboeuf 	struct section *sec;
554f1974222SMatt Helsley 	struct reloc *reloc;
555442f04c3SJosh Poimboeuf 	int i;
556442f04c3SJosh Poimboeuf 	unsigned int symndx;
557f1974222SMatt Helsley 	unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0;
558442f04c3SJosh Poimboeuf 
559442f04c3SJosh Poimboeuf 	list_for_each_entry(sec, &elf->sections, list) {
560fb414783SMatt Helsley 		if ((sec->sh.sh_type != SHT_RELA) &&
561fb414783SMatt Helsley 		    (sec->sh.sh_type != SHT_REL))
562442f04c3SJosh Poimboeuf 			continue;
563442f04c3SJosh Poimboeuf 
5641e968bf5SSami Tolvanen 		sec->base = find_section_by_index(elf, sec->sh.sh_info);
565442f04c3SJosh Poimboeuf 		if (!sec->base) {
566f1974222SMatt Helsley 			WARN("can't find base section for reloc section %s",
567442f04c3SJosh Poimboeuf 			     sec->name);
568442f04c3SJosh Poimboeuf 			return -1;
569442f04c3SJosh Poimboeuf 		}
570442f04c3SJosh Poimboeuf 
571f1974222SMatt Helsley 		sec->base->reloc = sec;
572442f04c3SJosh Poimboeuf 
573f1974222SMatt Helsley 		nr_reloc = 0;
574442f04c3SJosh Poimboeuf 		for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) {
575f1974222SMatt Helsley 			reloc = malloc(sizeof(*reloc));
576f1974222SMatt Helsley 			if (!reloc) {
577442f04c3SJosh Poimboeuf 				perror("malloc");
578442f04c3SJosh Poimboeuf 				return -1;
579442f04c3SJosh Poimboeuf 			}
580f1974222SMatt Helsley 			memset(reloc, 0, sizeof(*reloc));
581fb414783SMatt Helsley 			switch (sec->sh.sh_type) {
582fb414783SMatt Helsley 			case SHT_REL:
583fb414783SMatt Helsley 				if (read_rel_reloc(sec, i, reloc, &symndx))
584442f04c3SJosh Poimboeuf 					return -1;
585fb414783SMatt Helsley 				break;
586fb414783SMatt Helsley 			case SHT_RELA:
587fb414783SMatt Helsley 				if (read_rela_reloc(sec, i, reloc, &symndx))
588fb414783SMatt Helsley 					return -1;
589fb414783SMatt Helsley 				break;
590fb414783SMatt Helsley 			default: return -1;
591442f04c3SJosh Poimboeuf 			}
592442f04c3SJosh Poimboeuf 
593f1974222SMatt Helsley 			reloc->sec = sec;
594d832c005SPeter Zijlstra 			reloc->idx = i;
595d832c005SPeter Zijlstra 			reloc->sym = find_symbol_by_index(elf, symndx);
596f1974222SMatt Helsley 			if (!reloc->sym) {
597f1974222SMatt Helsley 				WARN("can't find reloc entry symbol %d for %s",
598442f04c3SJosh Poimboeuf 				     symndx, sec->name);
599442f04c3SJosh Poimboeuf 				return -1;
600442f04c3SJosh Poimboeuf 			}
601042ba73fSJosh Poimboeuf 
602f1974222SMatt Helsley 			elf_add_reloc(elf, reloc);
603f1974222SMatt Helsley 			nr_reloc++;
604442f04c3SJosh Poimboeuf 		}
605f1974222SMatt Helsley 		max_reloc = max(max_reloc, nr_reloc);
606f1974222SMatt Helsley 		tot_reloc += nr_reloc;
6071e11f3fdSPeter Zijlstra 	}
6081e11f3fdSPeter Zijlstra 
6091e11f3fdSPeter Zijlstra 	if (stats) {
610f1974222SMatt Helsley 		printf("max_reloc: %lu\n", max_reloc);
611f1974222SMatt Helsley 		printf("tot_reloc: %lu\n", tot_reloc);
612442f04c3SJosh Poimboeuf 	}
613442f04c3SJosh Poimboeuf 
614442f04c3SJosh Poimboeuf 	return 0;
615442f04c3SJosh Poimboeuf }
616442f04c3SJosh Poimboeuf 
617bc359ff2SIngo Molnar struct elf *elf_open_read(const char *name, int flags)
618442f04c3SJosh Poimboeuf {
619442f04c3SJosh Poimboeuf 	struct elf *elf;
620627fce14SJosh Poimboeuf 	Elf_Cmd cmd;
621442f04c3SJosh Poimboeuf 
622442f04c3SJosh Poimboeuf 	elf_version(EV_CURRENT);
623442f04c3SJosh Poimboeuf 
624442f04c3SJosh Poimboeuf 	elf = malloc(sizeof(*elf));
625442f04c3SJosh Poimboeuf 	if (!elf) {
626442f04c3SJosh Poimboeuf 		perror("malloc");
627442f04c3SJosh Poimboeuf 		return NULL;
628442f04c3SJosh Poimboeuf 	}
62934f7c96dSPeter Zijlstra 	memset(elf, 0, offsetof(struct elf, sections));
630442f04c3SJosh Poimboeuf 
631442f04c3SJosh Poimboeuf 	INIT_LIST_HEAD(&elf->sections);
632442f04c3SJosh Poimboeuf 
63334f7c96dSPeter Zijlstra 	elf_hash_init(elf->symbol_hash);
63434f7c96dSPeter Zijlstra 	elf_hash_init(elf->symbol_name_hash);
63534f7c96dSPeter Zijlstra 	elf_hash_init(elf->section_hash);
63634f7c96dSPeter Zijlstra 	elf_hash_init(elf->section_name_hash);
637f1974222SMatt Helsley 	elf_hash_init(elf->reloc_hash);
63834f7c96dSPeter Zijlstra 
639627fce14SJosh Poimboeuf 	elf->fd = open(name, flags);
640442f04c3SJosh Poimboeuf 	if (elf->fd == -1) {
641385d11b1SJosh Poimboeuf 		fprintf(stderr, "objtool: Can't open '%s': %s\n",
642385d11b1SJosh Poimboeuf 			name, strerror(errno));
643442f04c3SJosh Poimboeuf 		goto err;
644442f04c3SJosh Poimboeuf 	}
645442f04c3SJosh Poimboeuf 
646627fce14SJosh Poimboeuf 	if ((flags & O_ACCMODE) == O_RDONLY)
647627fce14SJosh Poimboeuf 		cmd = ELF_C_READ_MMAP;
648627fce14SJosh Poimboeuf 	else if ((flags & O_ACCMODE) == O_RDWR)
649627fce14SJosh Poimboeuf 		cmd = ELF_C_RDWR;
650627fce14SJosh Poimboeuf 	else /* O_WRONLY */
651627fce14SJosh Poimboeuf 		cmd = ELF_C_WRITE;
652627fce14SJosh Poimboeuf 
653627fce14SJosh Poimboeuf 	elf->elf = elf_begin(elf->fd, cmd, NULL);
654442f04c3SJosh Poimboeuf 	if (!elf->elf) {
655baa41469SJosh Poimboeuf 		WARN_ELF("elf_begin");
656442f04c3SJosh Poimboeuf 		goto err;
657442f04c3SJosh Poimboeuf 	}
658442f04c3SJosh Poimboeuf 
659442f04c3SJosh Poimboeuf 	if (!gelf_getehdr(elf->elf, &elf->ehdr)) {
660baa41469SJosh Poimboeuf 		WARN_ELF("gelf_getehdr");
661442f04c3SJosh Poimboeuf 		goto err;
662442f04c3SJosh Poimboeuf 	}
663442f04c3SJosh Poimboeuf 
664442f04c3SJosh Poimboeuf 	if (read_sections(elf))
665442f04c3SJosh Poimboeuf 		goto err;
666442f04c3SJosh Poimboeuf 
667442f04c3SJosh Poimboeuf 	if (read_symbols(elf))
668442f04c3SJosh Poimboeuf 		goto err;
669442f04c3SJosh Poimboeuf 
670f1974222SMatt Helsley 	if (read_relocs(elf))
671442f04c3SJosh Poimboeuf 		goto err;
672442f04c3SJosh Poimboeuf 
673442f04c3SJosh Poimboeuf 	return elf;
674442f04c3SJosh Poimboeuf 
675442f04c3SJosh Poimboeuf err:
676442f04c3SJosh Poimboeuf 	elf_close(elf);
677442f04c3SJosh Poimboeuf 	return NULL;
678442f04c3SJosh Poimboeuf }
679442f04c3SJosh Poimboeuf 
680627fce14SJosh Poimboeuf struct section *elf_create_section(struct elf *elf, const char *name,
6811e7e4788SJosh Poimboeuf 				   unsigned int sh_flags, size_t entsize, int nr)
682627fce14SJosh Poimboeuf {
683627fce14SJosh Poimboeuf 	struct section *sec, *shstrtab;
684627fce14SJosh Poimboeuf 	size_t size = entsize * nr;
6853c3ea503SMichael Forney 	Elf_Scn *s;
686627fce14SJosh Poimboeuf 	Elf_Data *data;
687627fce14SJosh Poimboeuf 
688627fce14SJosh Poimboeuf 	sec = malloc(sizeof(*sec));
689627fce14SJosh Poimboeuf 	if (!sec) {
690627fce14SJosh Poimboeuf 		perror("malloc");
691627fce14SJosh Poimboeuf 		return NULL;
692627fce14SJosh Poimboeuf 	}
693627fce14SJosh Poimboeuf 	memset(sec, 0, sizeof(*sec));
694627fce14SJosh Poimboeuf 
695627fce14SJosh Poimboeuf 	INIT_LIST_HEAD(&sec->symbol_list);
696f1974222SMatt Helsley 	INIT_LIST_HEAD(&sec->reloc_list);
697627fce14SJosh Poimboeuf 
698627fce14SJosh Poimboeuf 	s = elf_newscn(elf->elf);
699627fce14SJosh Poimboeuf 	if (!s) {
700627fce14SJosh Poimboeuf 		WARN_ELF("elf_newscn");
701627fce14SJosh Poimboeuf 		return NULL;
702627fce14SJosh Poimboeuf 	}
703627fce14SJosh Poimboeuf 
704627fce14SJosh Poimboeuf 	sec->name = strdup(name);
705627fce14SJosh Poimboeuf 	if (!sec->name) {
706627fce14SJosh Poimboeuf 		perror("strdup");
707627fce14SJosh Poimboeuf 		return NULL;
708627fce14SJosh Poimboeuf 	}
709627fce14SJosh Poimboeuf 
710627fce14SJosh Poimboeuf 	sec->idx = elf_ndxscn(s);
711627fce14SJosh Poimboeuf 	sec->len = size;
712627fce14SJosh Poimboeuf 	sec->changed = true;
713627fce14SJosh Poimboeuf 
714627fce14SJosh Poimboeuf 	sec->data = elf_newdata(s);
715627fce14SJosh Poimboeuf 	if (!sec->data) {
716627fce14SJosh Poimboeuf 		WARN_ELF("elf_newdata");
717627fce14SJosh Poimboeuf 		return NULL;
718627fce14SJosh Poimboeuf 	}
719627fce14SJosh Poimboeuf 
720627fce14SJosh Poimboeuf 	sec->data->d_size = size;
721627fce14SJosh Poimboeuf 	sec->data->d_align = 1;
722627fce14SJosh Poimboeuf 
723627fce14SJosh Poimboeuf 	if (size) {
724627fce14SJosh Poimboeuf 		sec->data->d_buf = malloc(size);
725627fce14SJosh Poimboeuf 		if (!sec->data->d_buf) {
726627fce14SJosh Poimboeuf 			perror("malloc");
727627fce14SJosh Poimboeuf 			return NULL;
728627fce14SJosh Poimboeuf 		}
729627fce14SJosh Poimboeuf 		memset(sec->data->d_buf, 0, size);
730627fce14SJosh Poimboeuf 	}
731627fce14SJosh Poimboeuf 
732627fce14SJosh Poimboeuf 	if (!gelf_getshdr(s, &sec->sh)) {
733627fce14SJosh Poimboeuf 		WARN_ELF("gelf_getshdr");
734627fce14SJosh Poimboeuf 		return NULL;
735627fce14SJosh Poimboeuf 	}
736627fce14SJosh Poimboeuf 
737627fce14SJosh Poimboeuf 	sec->sh.sh_size = size;
738627fce14SJosh Poimboeuf 	sec->sh.sh_entsize = entsize;
739627fce14SJosh Poimboeuf 	sec->sh.sh_type = SHT_PROGBITS;
740627fce14SJosh Poimboeuf 	sec->sh.sh_addralign = 1;
7411e7e4788SJosh Poimboeuf 	sec->sh.sh_flags = SHF_ALLOC | sh_flags;
742627fce14SJosh Poimboeuf 
743627fce14SJosh Poimboeuf 
7446d77d3b4SSimon Ser 	/* Add section name to .shstrtab (or .strtab for Clang) */
745627fce14SJosh Poimboeuf 	shstrtab = find_section_by_name(elf, ".shstrtab");
7466d77d3b4SSimon Ser 	if (!shstrtab)
7476d77d3b4SSimon Ser 		shstrtab = find_section_by_name(elf, ".strtab");
748627fce14SJosh Poimboeuf 	if (!shstrtab) {
7496d77d3b4SSimon Ser 		WARN("can't find .shstrtab or .strtab section");
750627fce14SJosh Poimboeuf 		return NULL;
751627fce14SJosh Poimboeuf 	}
752627fce14SJosh Poimboeuf 
753627fce14SJosh Poimboeuf 	s = elf_getscn(elf->elf, shstrtab->idx);
754627fce14SJosh Poimboeuf 	if (!s) {
755627fce14SJosh Poimboeuf 		WARN_ELF("elf_getscn");
756627fce14SJosh Poimboeuf 		return NULL;
757627fce14SJosh Poimboeuf 	}
758627fce14SJosh Poimboeuf 
759627fce14SJosh Poimboeuf 	data = elf_newdata(s);
760627fce14SJosh Poimboeuf 	if (!data) {
761627fce14SJosh Poimboeuf 		WARN_ELF("elf_newdata");
762627fce14SJosh Poimboeuf 		return NULL;
763627fce14SJosh Poimboeuf 	}
764627fce14SJosh Poimboeuf 
765627fce14SJosh Poimboeuf 	data->d_buf = sec->name;
766627fce14SJosh Poimboeuf 	data->d_size = strlen(name) + 1;
767627fce14SJosh Poimboeuf 	data->d_align = 1;
768627fce14SJosh Poimboeuf 
769627fce14SJosh Poimboeuf 	sec->sh.sh_name = shstrtab->len;
770627fce14SJosh Poimboeuf 
771627fce14SJosh Poimboeuf 	shstrtab->len += strlen(name) + 1;
772627fce14SJosh Poimboeuf 	shstrtab->changed = true;
773627fce14SJosh Poimboeuf 
77453038996SPeter Zijlstra 	list_add_tail(&sec->list, &elf->sections);
77534f7c96dSPeter Zijlstra 	elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
77634f7c96dSPeter Zijlstra 	elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
77753038996SPeter Zijlstra 
7782b10be23SPeter Zijlstra 	elf->changed = true;
7792b10be23SPeter Zijlstra 
780627fce14SJosh Poimboeuf 	return sec;
781627fce14SJosh Poimboeuf }
782627fce14SJosh Poimboeuf 
783fb414783SMatt Helsley static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base)
784fb414783SMatt Helsley {
785fb414783SMatt Helsley 	char *relocname;
786fb414783SMatt Helsley 	struct section *sec;
787fb414783SMatt Helsley 
788fb414783SMatt Helsley 	relocname = malloc(strlen(base->name) + strlen(".rel") + 1);
789fb414783SMatt Helsley 	if (!relocname) {
790fb414783SMatt Helsley 		perror("malloc");
791fb414783SMatt Helsley 		return NULL;
792fb414783SMatt Helsley 	}
793fb414783SMatt Helsley 	strcpy(relocname, ".rel");
794fb414783SMatt Helsley 	strcat(relocname, base->name);
795fb414783SMatt Helsley 
7961e7e4788SJosh Poimboeuf 	sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0);
797fb414783SMatt Helsley 	free(relocname);
798fb414783SMatt Helsley 	if (!sec)
799fb414783SMatt Helsley 		return NULL;
800fb414783SMatt Helsley 
801fb414783SMatt Helsley 	base->reloc = sec;
802fb414783SMatt Helsley 	sec->base = base;
803fb414783SMatt Helsley 
804fb414783SMatt Helsley 	sec->sh.sh_type = SHT_REL;
805fb414783SMatt Helsley 	sec->sh.sh_addralign = 8;
806fb414783SMatt Helsley 	sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
807fb414783SMatt Helsley 	sec->sh.sh_info = base->idx;
808fb414783SMatt Helsley 	sec->sh.sh_flags = SHF_INFO_LINK;
809fb414783SMatt Helsley 
810fb414783SMatt Helsley 	return sec;
811fb414783SMatt Helsley }
812fb414783SMatt Helsley 
813fb414783SMatt Helsley static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base)
814627fce14SJosh Poimboeuf {
815f1974222SMatt Helsley 	char *relocname;
816627fce14SJosh Poimboeuf 	struct section *sec;
817627fce14SJosh Poimboeuf 
818f1974222SMatt Helsley 	relocname = malloc(strlen(base->name) + strlen(".rela") + 1);
819f1974222SMatt Helsley 	if (!relocname) {
820627fce14SJosh Poimboeuf 		perror("malloc");
821627fce14SJosh Poimboeuf 		return NULL;
822627fce14SJosh Poimboeuf 	}
823f1974222SMatt Helsley 	strcpy(relocname, ".rela");
824f1974222SMatt Helsley 	strcat(relocname, base->name);
825627fce14SJosh Poimboeuf 
8261e7e4788SJosh Poimboeuf 	sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0);
827f1974222SMatt Helsley 	free(relocname);
828627fce14SJosh Poimboeuf 	if (!sec)
829627fce14SJosh Poimboeuf 		return NULL;
830627fce14SJosh Poimboeuf 
831f1974222SMatt Helsley 	base->reloc = sec;
832627fce14SJosh Poimboeuf 	sec->base = base;
833627fce14SJosh Poimboeuf 
834627fce14SJosh Poimboeuf 	sec->sh.sh_type = SHT_RELA;
835627fce14SJosh Poimboeuf 	sec->sh.sh_addralign = 8;
836627fce14SJosh Poimboeuf 	sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
837627fce14SJosh Poimboeuf 	sec->sh.sh_info = base->idx;
838627fce14SJosh Poimboeuf 	sec->sh.sh_flags = SHF_INFO_LINK;
839627fce14SJosh Poimboeuf 
840627fce14SJosh Poimboeuf 	return sec;
841627fce14SJosh Poimboeuf }
842627fce14SJosh Poimboeuf 
843fb414783SMatt Helsley struct section *elf_create_reloc_section(struct elf *elf,
844fb414783SMatt Helsley 					 struct section *base,
845fb414783SMatt Helsley 					 int reltype)
846fb414783SMatt Helsley {
847fb414783SMatt Helsley 	switch (reltype) {
848fb414783SMatt Helsley 	case SHT_REL:  return elf_create_rel_reloc_section(elf, base);
849fb414783SMatt Helsley 	case SHT_RELA: return elf_create_rela_reloc_section(elf, base);
850fb414783SMatt Helsley 	default:       return NULL;
851fb414783SMatt Helsley 	}
852fb414783SMatt Helsley }
853fb414783SMatt Helsley 
854fb414783SMatt Helsley static int elf_rebuild_rel_reloc_section(struct section *sec, int nr)
855627fce14SJosh Poimboeuf {
856f1974222SMatt Helsley 	struct reloc *reloc;
857fb414783SMatt Helsley 	int idx = 0, size;
858*a1a664ecSMartin Schwidefsky 	void *buf;
859fb414783SMatt Helsley 
860fb414783SMatt Helsley 	/* Allocate a buffer for relocations */
861*a1a664ecSMartin Schwidefsky 	size = nr * sizeof(GElf_Rel);
862*a1a664ecSMartin Schwidefsky 	buf = malloc(size);
863*a1a664ecSMartin Schwidefsky 	if (!buf) {
864fb414783SMatt Helsley 		perror("malloc");
865fb414783SMatt Helsley 		return -1;
866fb414783SMatt Helsley 	}
867fb414783SMatt Helsley 
868*a1a664ecSMartin Schwidefsky 	sec->data->d_buf = buf;
869fb414783SMatt Helsley 	sec->data->d_size = size;
870*a1a664ecSMartin Schwidefsky 	sec->data->d_type = ELF_T_REL;
871fb414783SMatt Helsley 
872fb414783SMatt Helsley 	sec->sh.sh_size = size;
873fb414783SMatt Helsley 
874fb414783SMatt Helsley 	idx = 0;
875fb414783SMatt Helsley 	list_for_each_entry(reloc, &sec->reloc_list, list) {
876*a1a664ecSMartin Schwidefsky 		reloc->rel.r_offset = reloc->offset;
877*a1a664ecSMartin Schwidefsky 		reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
878*a1a664ecSMartin Schwidefsky 		gelf_update_rel(sec->data, idx, &reloc->rel);
879fb414783SMatt Helsley 		idx++;
880fb414783SMatt Helsley 	}
881fb414783SMatt Helsley 
882fb414783SMatt Helsley 	return 0;
883fb414783SMatt Helsley }
884fb414783SMatt Helsley 
885fb414783SMatt Helsley static int elf_rebuild_rela_reloc_section(struct section *sec, int nr)
886fb414783SMatt Helsley {
887fb414783SMatt Helsley 	struct reloc *reloc;
888fb414783SMatt Helsley 	int idx = 0, size;
889*a1a664ecSMartin Schwidefsky 	void *buf;
890627fce14SJosh Poimboeuf 
891fb414783SMatt Helsley 	/* Allocate a buffer for relocations with addends */
892*a1a664ecSMartin Schwidefsky 	size = nr * sizeof(GElf_Rela);
893*a1a664ecSMartin Schwidefsky 	buf = malloc(size);
894*a1a664ecSMartin Schwidefsky 	if (!buf) {
895627fce14SJosh Poimboeuf 		perror("malloc");
896627fce14SJosh Poimboeuf 		return -1;
897627fce14SJosh Poimboeuf 	}
898627fce14SJosh Poimboeuf 
899*a1a664ecSMartin Schwidefsky 	sec->data->d_buf = buf;
900627fce14SJosh Poimboeuf 	sec->data->d_size = size;
901*a1a664ecSMartin Schwidefsky 	sec->data->d_type = ELF_T_RELA;
902627fce14SJosh Poimboeuf 
903627fce14SJosh Poimboeuf 	sec->sh.sh_size = size;
904627fce14SJosh Poimboeuf 
905627fce14SJosh Poimboeuf 	idx = 0;
906f1974222SMatt Helsley 	list_for_each_entry(reloc, &sec->reloc_list, list) {
907*a1a664ecSMartin Schwidefsky 		reloc->rela.r_offset = reloc->offset;
908*a1a664ecSMartin Schwidefsky 		reloc->rela.r_addend = reloc->addend;
909*a1a664ecSMartin Schwidefsky 		reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
910*a1a664ecSMartin Schwidefsky 		gelf_update_rela(sec->data, idx, &reloc->rela);
911627fce14SJosh Poimboeuf 		idx++;
912627fce14SJosh Poimboeuf 	}
913627fce14SJosh Poimboeuf 
914627fce14SJosh Poimboeuf 	return 0;
915627fce14SJosh Poimboeuf }
916627fce14SJosh Poimboeuf 
917d832c005SPeter Zijlstra int elf_rebuild_reloc_section(struct elf *elf, struct section *sec)
918fb414783SMatt Helsley {
919fb414783SMatt Helsley 	struct reloc *reloc;
920fb414783SMatt Helsley 	int nr;
921fb414783SMatt Helsley 
922d832c005SPeter Zijlstra 	sec->changed = true;
923d832c005SPeter Zijlstra 	elf->changed = true;
924d832c005SPeter Zijlstra 
925fb414783SMatt Helsley 	nr = 0;
926fb414783SMatt Helsley 	list_for_each_entry(reloc, &sec->reloc_list, list)
927fb414783SMatt Helsley 		nr++;
928fb414783SMatt Helsley 
929fb414783SMatt Helsley 	switch (sec->sh.sh_type) {
930fb414783SMatt Helsley 	case SHT_REL:  return elf_rebuild_rel_reloc_section(sec, nr);
931fb414783SMatt Helsley 	case SHT_RELA: return elf_rebuild_rela_reloc_section(sec, nr);
932fb414783SMatt Helsley 	default:       return -1;
933fb414783SMatt Helsley 	}
934fb414783SMatt Helsley }
935fb414783SMatt Helsley 
936fdabdd0bSPeter Zijlstra int elf_write_insn(struct elf *elf, struct section *sec,
937fdabdd0bSPeter Zijlstra 		   unsigned long offset, unsigned int len,
938fdabdd0bSPeter Zijlstra 		   const char *insn)
939fdabdd0bSPeter Zijlstra {
940fdabdd0bSPeter Zijlstra 	Elf_Data *data = sec->data;
941fdabdd0bSPeter Zijlstra 
942fdabdd0bSPeter Zijlstra 	if (data->d_type != ELF_T_BYTE || data->d_off) {
943fdabdd0bSPeter Zijlstra 		WARN("write to unexpected data for section: %s", sec->name);
944fdabdd0bSPeter Zijlstra 		return -1;
945fdabdd0bSPeter Zijlstra 	}
946fdabdd0bSPeter Zijlstra 
947fdabdd0bSPeter Zijlstra 	memcpy(data->d_buf + offset, insn, len);
948fdabdd0bSPeter Zijlstra 	elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY);
949fdabdd0bSPeter Zijlstra 
950fdabdd0bSPeter Zijlstra 	elf->changed = true;
951fdabdd0bSPeter Zijlstra 
952fdabdd0bSPeter Zijlstra 	return 0;
953fdabdd0bSPeter Zijlstra }
954fdabdd0bSPeter Zijlstra 
955d832c005SPeter Zijlstra int elf_write_reloc(struct elf *elf, struct reloc *reloc)
956fdabdd0bSPeter Zijlstra {
957d832c005SPeter Zijlstra 	struct section *sec = reloc->sec;
958fdabdd0bSPeter Zijlstra 
959d832c005SPeter Zijlstra 	if (sec->sh.sh_type == SHT_REL) {
960d832c005SPeter Zijlstra 		reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
961d832c005SPeter Zijlstra 		reloc->rel.r_offset = reloc->offset;
962fdabdd0bSPeter Zijlstra 
963d832c005SPeter Zijlstra 		if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) {
964d832c005SPeter Zijlstra 			WARN_ELF("gelf_update_rel");
965d832c005SPeter Zijlstra 			return -1;
966d832c005SPeter Zijlstra 		}
967d832c005SPeter Zijlstra 	} else {
968d832c005SPeter Zijlstra 		reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
969d832c005SPeter Zijlstra 		reloc->rela.r_addend = reloc->addend;
970d832c005SPeter Zijlstra 		reloc->rela.r_offset = reloc->offset;
971d832c005SPeter Zijlstra 
972d832c005SPeter Zijlstra 		if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) {
973fdabdd0bSPeter Zijlstra 			WARN_ELF("gelf_update_rela");
974fdabdd0bSPeter Zijlstra 			return -1;
975fdabdd0bSPeter Zijlstra 		}
976d832c005SPeter Zijlstra 	}
977fdabdd0bSPeter Zijlstra 
978fdabdd0bSPeter Zijlstra 	elf->changed = true;
979fdabdd0bSPeter Zijlstra 
980fdabdd0bSPeter Zijlstra 	return 0;
981fdabdd0bSPeter Zijlstra }
982fdabdd0bSPeter Zijlstra 
9832b10be23SPeter Zijlstra int elf_write(struct elf *elf)
984627fce14SJosh Poimboeuf {
985627fce14SJosh Poimboeuf 	struct section *sec;
986627fce14SJosh Poimboeuf 	Elf_Scn *s;
987627fce14SJosh Poimboeuf 
98897dab2aeSJosh Poimboeuf 	/* Update section headers for changed sections: */
989627fce14SJosh Poimboeuf 	list_for_each_entry(sec, &elf->sections, list) {
990627fce14SJosh Poimboeuf 		if (sec->changed) {
991627fce14SJosh Poimboeuf 			s = elf_getscn(elf->elf, sec->idx);
992627fce14SJosh Poimboeuf 			if (!s) {
993627fce14SJosh Poimboeuf 				WARN_ELF("elf_getscn");
994627fce14SJosh Poimboeuf 				return -1;
995627fce14SJosh Poimboeuf 			}
996627fce14SJosh Poimboeuf 			if (!gelf_update_shdr(s, &sec->sh)) {
997627fce14SJosh Poimboeuf 				WARN_ELF("gelf_update_shdr");
998627fce14SJosh Poimboeuf 				return -1;
999627fce14SJosh Poimboeuf 			}
10002b10be23SPeter Zijlstra 
10012b10be23SPeter Zijlstra 			sec->changed = false;
1002627fce14SJosh Poimboeuf 		}
1003627fce14SJosh Poimboeuf 	}
1004627fce14SJosh Poimboeuf 
100597dab2aeSJosh Poimboeuf 	/* Make sure the new section header entries get updated properly. */
100697dab2aeSJosh Poimboeuf 	elf_flagelf(elf->elf, ELF_C_SET, ELF_F_DIRTY);
100797dab2aeSJosh Poimboeuf 
100897dab2aeSJosh Poimboeuf 	/* Write all changes to the file. */
1009627fce14SJosh Poimboeuf 	if (elf_update(elf->elf, ELF_C_WRITE) < 0) {
1010627fce14SJosh Poimboeuf 		WARN_ELF("elf_update");
1011627fce14SJosh Poimboeuf 		return -1;
1012627fce14SJosh Poimboeuf 	}
1013627fce14SJosh Poimboeuf 
10142b10be23SPeter Zijlstra 	elf->changed = false;
10152b10be23SPeter Zijlstra 
1016627fce14SJosh Poimboeuf 	return 0;
1017627fce14SJosh Poimboeuf }
1018627fce14SJosh Poimboeuf 
1019442f04c3SJosh Poimboeuf void elf_close(struct elf *elf)
1020442f04c3SJosh Poimboeuf {
1021442f04c3SJosh Poimboeuf 	struct section *sec, *tmpsec;
1022442f04c3SJosh Poimboeuf 	struct symbol *sym, *tmpsym;
1023f1974222SMatt Helsley 	struct reloc *reloc, *tmpreloc;
1024442f04c3SJosh Poimboeuf 
1025baa41469SJosh Poimboeuf 	if (elf->elf)
1026baa41469SJosh Poimboeuf 		elf_end(elf->elf);
1027baa41469SJosh Poimboeuf 
1028baa41469SJosh Poimboeuf 	if (elf->fd > 0)
1029baa41469SJosh Poimboeuf 		close(elf->fd);
1030baa41469SJosh Poimboeuf 
1031442f04c3SJosh Poimboeuf 	list_for_each_entry_safe(sec, tmpsec, &elf->sections, list) {
1032a196e171SJosh Poimboeuf 		list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) {
1033442f04c3SJosh Poimboeuf 			list_del(&sym->list);
1034042ba73fSJosh Poimboeuf 			hash_del(&sym->hash);
1035442f04c3SJosh Poimboeuf 			free(sym);
1036442f04c3SJosh Poimboeuf 		}
1037f1974222SMatt Helsley 		list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) {
1038f1974222SMatt Helsley 			list_del(&reloc->list);
1039f1974222SMatt Helsley 			hash_del(&reloc->hash);
1040f1974222SMatt Helsley 			free(reloc);
1041442f04c3SJosh Poimboeuf 		}
1042442f04c3SJosh Poimboeuf 		list_del(&sec->list);
1043442f04c3SJosh Poimboeuf 		free(sec);
1044442f04c3SJosh Poimboeuf 	}
1045baa41469SJosh Poimboeuf 
1046442f04c3SJosh Poimboeuf 	free(elf);
1047442f04c3SJosh Poimboeuf }
1048