xref: /openbmc/linux/tools/objtool/elf.c (revision d0c5c4cc)
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>
187786032eSVasily Gorbik #include <objtool/builtin.h>
19442f04c3SJosh Poimboeuf 
207786032eSVasily Gorbik #include <objtool/elf.h>
217786032eSVasily Gorbik #include <objtool/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 
462d24dd57SPeter Zijlstra static bool symbol_to_offset(struct rb_node *a, const struct rb_node *b)
472a362eccSPeter Zijlstra {
482a362eccSPeter Zijlstra 	struct symbol *sa = rb_entry(a, struct symbol, node);
492a362eccSPeter Zijlstra 	struct symbol *sb = rb_entry(b, struct symbol, node);
502a362eccSPeter Zijlstra 
512a362eccSPeter Zijlstra 	if (sa->offset < sb->offset)
522d24dd57SPeter Zijlstra 		return true;
532a362eccSPeter Zijlstra 	if (sa->offset > sb->offset)
542d24dd57SPeter Zijlstra 		return false;
552a362eccSPeter Zijlstra 
562a362eccSPeter Zijlstra 	if (sa->len < sb->len)
572d24dd57SPeter Zijlstra 		return true;
582a362eccSPeter Zijlstra 	if (sa->len > sb->len)
592d24dd57SPeter Zijlstra 		return false;
602a362eccSPeter Zijlstra 
612a362eccSPeter Zijlstra 	sa->alias = sb;
622a362eccSPeter Zijlstra 
632d24dd57SPeter Zijlstra 	return false;
642a362eccSPeter Zijlstra }
652a362eccSPeter Zijlstra 
662a362eccSPeter Zijlstra static int symbol_by_offset(const void *key, const struct rb_node *node)
672a362eccSPeter Zijlstra {
682a362eccSPeter Zijlstra 	const struct symbol *s = rb_entry(node, struct symbol, node);
692a362eccSPeter Zijlstra 	const unsigned long *o = key;
702a362eccSPeter Zijlstra 
712a362eccSPeter Zijlstra 	if (*o < s->offset)
722a362eccSPeter Zijlstra 		return -1;
735377cae9SJulien Thierry 	if (*o >= s->offset + s->len)
742a362eccSPeter Zijlstra 		return 1;
752a362eccSPeter Zijlstra 
762a362eccSPeter Zijlstra 	return 0;
772a362eccSPeter Zijlstra }
782a362eccSPeter Zijlstra 
79894e48caSIngo Molnar struct section *find_section_by_name(const struct elf *elf, const char *name)
80442f04c3SJosh Poimboeuf {
81442f04c3SJosh Poimboeuf 	struct section *sec;
82442f04c3SJosh Poimboeuf 
8334f7c96dSPeter Zijlstra 	elf_hash_for_each_possible(elf->section_name_hash, sec, name_hash, str_hash(name))
84442f04c3SJosh Poimboeuf 		if (!strcmp(sec->name, name))
85442f04c3SJosh Poimboeuf 			return sec;
86442f04c3SJosh Poimboeuf 
87442f04c3SJosh Poimboeuf 	return NULL;
88442f04c3SJosh Poimboeuf }
89442f04c3SJosh Poimboeuf 
90442f04c3SJosh Poimboeuf static struct section *find_section_by_index(struct elf *elf,
91442f04c3SJosh Poimboeuf 					     unsigned int idx)
92442f04c3SJosh Poimboeuf {
93442f04c3SJosh Poimboeuf 	struct section *sec;
94442f04c3SJosh Poimboeuf 
9534f7c96dSPeter Zijlstra 	elf_hash_for_each_possible(elf->section_hash, sec, hash, idx)
96442f04c3SJosh Poimboeuf 		if (sec->idx == idx)
97442f04c3SJosh Poimboeuf 			return sec;
98442f04c3SJosh Poimboeuf 
99442f04c3SJosh Poimboeuf 	return NULL;
100442f04c3SJosh Poimboeuf }
101442f04c3SJosh Poimboeuf 
102442f04c3SJosh Poimboeuf static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
103442f04c3SJosh Poimboeuf {
104442f04c3SJosh Poimboeuf 	struct symbol *sym;
105442f04c3SJosh Poimboeuf 
10634f7c96dSPeter Zijlstra 	elf_hash_for_each_possible(elf->symbol_hash, sym, hash, idx)
107442f04c3SJosh Poimboeuf 		if (sym->idx == idx)
108442f04c3SJosh Poimboeuf 			return sym;
109442f04c3SJosh Poimboeuf 
110442f04c3SJosh Poimboeuf 	return NULL;
111442f04c3SJosh Poimboeuf }
112442f04c3SJosh Poimboeuf 
113442f04c3SJosh Poimboeuf struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)
114442f04c3SJosh Poimboeuf {
1152a362eccSPeter Zijlstra 	struct rb_node *node;
116442f04c3SJosh Poimboeuf 
1172d24dd57SPeter Zijlstra 	rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) {
1182a362eccSPeter Zijlstra 		struct symbol *s = rb_entry(node, struct symbol, node);
1192a362eccSPeter Zijlstra 
1202a362eccSPeter Zijlstra 		if (s->offset == offset && s->type != STT_SECTION)
1212a362eccSPeter Zijlstra 			return s;
1222a362eccSPeter Zijlstra 	}
1237acfe531SJosh Poimboeuf 
1247acfe531SJosh Poimboeuf 	return NULL;
1257acfe531SJosh Poimboeuf }
1267acfe531SJosh Poimboeuf 
1277acfe531SJosh Poimboeuf struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)
1287acfe531SJosh Poimboeuf {
1292a362eccSPeter Zijlstra 	struct rb_node *node;
1307acfe531SJosh Poimboeuf 
1312d24dd57SPeter Zijlstra 	rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) {
1322a362eccSPeter Zijlstra 		struct symbol *s = rb_entry(node, struct symbol, node);
1332a362eccSPeter Zijlstra 
1342a362eccSPeter Zijlstra 		if (s->offset == offset && s->type == STT_FUNC)
1352a362eccSPeter Zijlstra 			return s;
1362a362eccSPeter Zijlstra 	}
1372a362eccSPeter Zijlstra 
1382a362eccSPeter Zijlstra 	return NULL;
1392a362eccSPeter Zijlstra }
1402a362eccSPeter Zijlstra 
141b490f453SMiroslav Benes struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset)
1422a362eccSPeter Zijlstra {
1432a362eccSPeter Zijlstra 	struct rb_node *node;
1442a362eccSPeter Zijlstra 
1452d24dd57SPeter Zijlstra 	rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) {
1462a362eccSPeter Zijlstra 		struct symbol *s = rb_entry(node, struct symbol, node);
1472a362eccSPeter Zijlstra 
1482a362eccSPeter Zijlstra 		if (s->type != STT_SECTION)
1492a362eccSPeter Zijlstra 			return s;
1502a362eccSPeter Zijlstra 	}
1512a362eccSPeter Zijlstra 
1522a362eccSPeter Zijlstra 	return NULL;
1532a362eccSPeter Zijlstra }
1542a362eccSPeter Zijlstra 
15553d20720SPeter Zijlstra struct symbol *find_func_containing(struct section *sec, unsigned long offset)
1562a362eccSPeter Zijlstra {
1572a362eccSPeter Zijlstra 	struct rb_node *node;
1582a362eccSPeter Zijlstra 
1592d24dd57SPeter Zijlstra 	rb_for_each(node, &offset, &sec->symbol_tree, symbol_by_offset) {
1602a362eccSPeter Zijlstra 		struct symbol *s = rb_entry(node, struct symbol, node);
1612a362eccSPeter Zijlstra 
1622a362eccSPeter Zijlstra 		if (s->type == STT_FUNC)
1632a362eccSPeter Zijlstra 			return s;
1642a362eccSPeter Zijlstra 	}
165442f04c3SJosh Poimboeuf 
166442f04c3SJosh Poimboeuf 	return NULL;
167442f04c3SJosh Poimboeuf }
168442f04c3SJosh Poimboeuf 
169894e48caSIngo Molnar struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)
17013810435SJosh Poimboeuf {
17113810435SJosh Poimboeuf 	struct symbol *sym;
17213810435SJosh Poimboeuf 
17334f7c96dSPeter Zijlstra 	elf_hash_for_each_possible(elf->symbol_name_hash, sym, name_hash, str_hash(name))
17413810435SJosh Poimboeuf 		if (!strcmp(sym->name, name))
17513810435SJosh Poimboeuf 			return sym;
17613810435SJosh Poimboeuf 
17713810435SJosh Poimboeuf 	return NULL;
17813810435SJosh Poimboeuf }
17913810435SJosh Poimboeuf 
180f1974222SMatt Helsley struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
1818b5fa6bcSPeter Zijlstra 				     unsigned long offset, unsigned int len)
182442f04c3SJosh Poimboeuf {
183f1974222SMatt Helsley 	struct reloc *reloc, *r = NULL;
184042ba73fSJosh Poimboeuf 	unsigned long o;
185442f04c3SJosh Poimboeuf 
186f1974222SMatt Helsley 	if (!sec->reloc)
187442f04c3SJosh Poimboeuf 		return NULL;
188442f04c3SJosh Poimboeuf 
189f1974222SMatt Helsley 	sec = sec->reloc;
1908b5fa6bcSPeter Zijlstra 
19174b873e4SPeter Zijlstra 	for_offset_range(o, offset, offset + len) {
192f1974222SMatt Helsley 		elf_hash_for_each_possible(elf->reloc_hash, reloc, hash,
1938b5fa6bcSPeter Zijlstra 				       sec_offset_hash(sec, o)) {
194f1974222SMatt Helsley 			if (reloc->sec != sec)
19574b873e4SPeter Zijlstra 				continue;
19674b873e4SPeter Zijlstra 
197f1974222SMatt Helsley 			if (reloc->offset >= offset && reloc->offset < offset + len) {
198f1974222SMatt Helsley 				if (!r || reloc->offset < r->offset)
199f1974222SMatt Helsley 					r = reloc;
2008b5fa6bcSPeter Zijlstra 			}
2018b5fa6bcSPeter Zijlstra 		}
20274b873e4SPeter Zijlstra 		if (r)
20374b873e4SPeter Zijlstra 			return r;
20474b873e4SPeter Zijlstra 	}
205442f04c3SJosh Poimboeuf 
206442f04c3SJosh Poimboeuf 	return NULL;
207442f04c3SJosh Poimboeuf }
208442f04c3SJosh Poimboeuf 
209f1974222SMatt Helsley struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset)
210442f04c3SJosh Poimboeuf {
211f1974222SMatt Helsley 	return find_reloc_by_dest_range(elf, sec, offset, 1);
212442f04c3SJosh Poimboeuf }
213442f04c3SJosh Poimboeuf 
214442f04c3SJosh Poimboeuf static int read_sections(struct elf *elf)
215442f04c3SJosh Poimboeuf {
216442f04c3SJosh Poimboeuf 	Elf_Scn *s = NULL;
217442f04c3SJosh Poimboeuf 	struct section *sec;
218442f04c3SJosh Poimboeuf 	size_t shstrndx, sections_nr;
219442f04c3SJosh Poimboeuf 	int i;
220442f04c3SJosh Poimboeuf 
221442f04c3SJosh Poimboeuf 	if (elf_getshdrnum(elf->elf, &sections_nr)) {
222baa41469SJosh Poimboeuf 		WARN_ELF("elf_getshdrnum");
223442f04c3SJosh Poimboeuf 		return -1;
224442f04c3SJosh Poimboeuf 	}
225442f04c3SJosh Poimboeuf 
226442f04c3SJosh Poimboeuf 	if (elf_getshdrstrndx(elf->elf, &shstrndx)) {
227baa41469SJosh Poimboeuf 		WARN_ELF("elf_getshdrstrndx");
228442f04c3SJosh Poimboeuf 		return -1;
229442f04c3SJosh Poimboeuf 	}
230442f04c3SJosh Poimboeuf 
231442f04c3SJosh Poimboeuf 	for (i = 0; i < sections_nr; i++) {
232442f04c3SJosh Poimboeuf 		sec = malloc(sizeof(*sec));
233442f04c3SJosh Poimboeuf 		if (!sec) {
234442f04c3SJosh Poimboeuf 			perror("malloc");
235442f04c3SJosh Poimboeuf 			return -1;
236442f04c3SJosh Poimboeuf 		}
237442f04c3SJosh Poimboeuf 		memset(sec, 0, sizeof(*sec));
238442f04c3SJosh Poimboeuf 
239a196e171SJosh Poimboeuf 		INIT_LIST_HEAD(&sec->symbol_list);
240f1974222SMatt Helsley 		INIT_LIST_HEAD(&sec->reloc_list);
241442f04c3SJosh Poimboeuf 
242442f04c3SJosh Poimboeuf 		s = elf_getscn(elf->elf, i);
243442f04c3SJosh Poimboeuf 		if (!s) {
244baa41469SJosh Poimboeuf 			WARN_ELF("elf_getscn");
245442f04c3SJosh Poimboeuf 			return -1;
246442f04c3SJosh Poimboeuf 		}
247442f04c3SJosh Poimboeuf 
248442f04c3SJosh Poimboeuf 		sec->idx = elf_ndxscn(s);
249442f04c3SJosh Poimboeuf 
250442f04c3SJosh Poimboeuf 		if (!gelf_getshdr(s, &sec->sh)) {
251baa41469SJosh Poimboeuf 			WARN_ELF("gelf_getshdr");
252442f04c3SJosh Poimboeuf 			return -1;
253442f04c3SJosh Poimboeuf 		}
254442f04c3SJosh Poimboeuf 
255442f04c3SJosh Poimboeuf 		sec->name = elf_strptr(elf->elf, shstrndx, sec->sh.sh_name);
256442f04c3SJosh Poimboeuf 		if (!sec->name) {
257baa41469SJosh Poimboeuf 			WARN_ELF("elf_strptr");
258442f04c3SJosh Poimboeuf 			return -1;
259442f04c3SJosh Poimboeuf 		}
260442f04c3SJosh Poimboeuf 
261df968c93SPetr Vandrovec 		if (sec->sh.sh_size != 0) {
262baa41469SJosh Poimboeuf 			sec->data = elf_getdata(s, NULL);
263baa41469SJosh Poimboeuf 			if (!sec->data) {
264baa41469SJosh Poimboeuf 				WARN_ELF("elf_getdata");
265442f04c3SJosh Poimboeuf 				return -1;
266442f04c3SJosh Poimboeuf 			}
267baa41469SJosh Poimboeuf 			if (sec->data->d_off != 0 ||
268baa41469SJosh Poimboeuf 			    sec->data->d_size != sec->sh.sh_size) {
269df968c93SPetr Vandrovec 				WARN("unexpected data attributes for %s",
270df968c93SPetr Vandrovec 				     sec->name);
271442f04c3SJosh Poimboeuf 				return -1;
272442f04c3SJosh Poimboeuf 			}
273df968c93SPetr Vandrovec 		}
274df968c93SPetr Vandrovec 		sec->len = sec->sh.sh_size;
27553038996SPeter Zijlstra 
27653038996SPeter Zijlstra 		list_add_tail(&sec->list, &elf->sections);
27734f7c96dSPeter Zijlstra 		elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
27834f7c96dSPeter Zijlstra 		elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
279442f04c3SJosh Poimboeuf 	}
280442f04c3SJosh Poimboeuf 
2811e11f3fdSPeter Zijlstra 	if (stats)
2821e11f3fdSPeter Zijlstra 		printf("nr_sections: %lu\n", (unsigned long)sections_nr);
2831e11f3fdSPeter Zijlstra 
284442f04c3SJosh Poimboeuf 	/* sanity check, one more call to elf_nextscn() should return NULL */
285442f04c3SJosh Poimboeuf 	if (elf_nextscn(elf->elf, s)) {
286442f04c3SJosh Poimboeuf 		WARN("section entry mismatch");
287442f04c3SJosh Poimboeuf 		return -1;
288442f04c3SJosh Poimboeuf 	}
289442f04c3SJosh Poimboeuf 
290442f04c3SJosh Poimboeuf 	return 0;
291442f04c3SJosh Poimboeuf }
292442f04c3SJosh Poimboeuf 
293442f04c3SJosh Poimboeuf static int read_symbols(struct elf *elf)
294442f04c3SJosh Poimboeuf {
29528fe1d7bSSami Tolvanen 	struct section *symtab, *symtab_shndx, *sec;
2962a362eccSPeter Zijlstra 	struct symbol *sym, *pfunc;
2972a362eccSPeter Zijlstra 	struct list_head *entry;
2982a362eccSPeter Zijlstra 	struct rb_node *pnode;
299442f04c3SJosh Poimboeuf 	int symbols_nr, i;
30013810435SJosh Poimboeuf 	char *coldstr;
30128fe1d7bSSami Tolvanen 	Elf_Data *shndx_data = NULL;
30228fe1d7bSSami Tolvanen 	Elf32_Word shndx;
303442f04c3SJosh Poimboeuf 
304442f04c3SJosh Poimboeuf 	symtab = find_section_by_name(elf, ".symtab");
305442f04c3SJosh Poimboeuf 	if (!symtab) {
3061d489151SJosh Poimboeuf 		/*
3071d489151SJosh Poimboeuf 		 * A missing symbol table is actually possible if it's an empty
3081d489151SJosh Poimboeuf 		 * .o file.  This can happen for thunk_64.o.
3091d489151SJosh Poimboeuf 		 */
3101d489151SJosh Poimboeuf 		return 0;
311442f04c3SJosh Poimboeuf 	}
312442f04c3SJosh Poimboeuf 
31328fe1d7bSSami Tolvanen 	symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
31428fe1d7bSSami Tolvanen 	if (symtab_shndx)
31528fe1d7bSSami Tolvanen 		shndx_data = symtab_shndx->data;
31628fe1d7bSSami Tolvanen 
317442f04c3SJosh Poimboeuf 	symbols_nr = symtab->sh.sh_size / symtab->sh.sh_entsize;
318442f04c3SJosh Poimboeuf 
319442f04c3SJosh Poimboeuf 	for (i = 0; i < symbols_nr; i++) {
320442f04c3SJosh Poimboeuf 		sym = malloc(sizeof(*sym));
321442f04c3SJosh Poimboeuf 		if (!sym) {
322442f04c3SJosh Poimboeuf 			perror("malloc");
323442f04c3SJosh Poimboeuf 			return -1;
324442f04c3SJosh Poimboeuf 		}
325442f04c3SJosh Poimboeuf 		memset(sym, 0, sizeof(*sym));
3262a362eccSPeter Zijlstra 		sym->alias = sym;
327442f04c3SJosh Poimboeuf 
328442f04c3SJosh Poimboeuf 		sym->idx = i;
329442f04c3SJosh Poimboeuf 
33028fe1d7bSSami Tolvanen 		if (!gelf_getsymshndx(symtab->data, shndx_data, i, &sym->sym,
33128fe1d7bSSami Tolvanen 				      &shndx)) {
33228fe1d7bSSami Tolvanen 			WARN_ELF("gelf_getsymshndx");
333442f04c3SJosh Poimboeuf 			goto err;
334442f04c3SJosh Poimboeuf 		}
335442f04c3SJosh Poimboeuf 
336442f04c3SJosh Poimboeuf 		sym->name = elf_strptr(elf->elf, symtab->sh.sh_link,
337442f04c3SJosh Poimboeuf 				       sym->sym.st_name);
338442f04c3SJosh Poimboeuf 		if (!sym->name) {
339baa41469SJosh Poimboeuf 			WARN_ELF("elf_strptr");
340442f04c3SJosh Poimboeuf 			goto err;
341442f04c3SJosh Poimboeuf 		}
342442f04c3SJosh Poimboeuf 
343442f04c3SJosh Poimboeuf 		sym->type = GELF_ST_TYPE(sym->sym.st_info);
344442f04c3SJosh Poimboeuf 		sym->bind = GELF_ST_BIND(sym->sym.st_info);
345442f04c3SJosh Poimboeuf 
34628fe1d7bSSami Tolvanen 		if ((sym->sym.st_shndx > SHN_UNDEF &&
34728fe1d7bSSami Tolvanen 		     sym->sym.st_shndx < SHN_LORESERVE) ||
34828fe1d7bSSami Tolvanen 		    (shndx_data && sym->sym.st_shndx == SHN_XINDEX)) {
34928fe1d7bSSami Tolvanen 			if (sym->sym.st_shndx != SHN_XINDEX)
35028fe1d7bSSami Tolvanen 				shndx = sym->sym.st_shndx;
35128fe1d7bSSami Tolvanen 
35228fe1d7bSSami Tolvanen 			sym->sec = find_section_by_index(elf, shndx);
353442f04c3SJosh Poimboeuf 			if (!sym->sec) {
354442f04c3SJosh Poimboeuf 				WARN("couldn't find section for symbol %s",
355442f04c3SJosh Poimboeuf 				     sym->name);
356442f04c3SJosh Poimboeuf 				goto err;
357442f04c3SJosh Poimboeuf 			}
358442f04c3SJosh Poimboeuf 			if (sym->type == STT_SECTION) {
359442f04c3SJosh Poimboeuf 				sym->name = sym->sec->name;
360442f04c3SJosh Poimboeuf 				sym->sec->sym = sym;
361442f04c3SJosh Poimboeuf 			}
362442f04c3SJosh Poimboeuf 		} else
363442f04c3SJosh Poimboeuf 			sym->sec = find_section_by_index(elf, 0);
364442f04c3SJosh Poimboeuf 
365442f04c3SJosh Poimboeuf 		sym->offset = sym->sym.st_value;
366442f04c3SJosh Poimboeuf 		sym->len = sym->sym.st_size;
367442f04c3SJosh Poimboeuf 
3682d24dd57SPeter Zijlstra 		rb_add(&sym->node, &sym->sec->symbol_tree, symbol_to_offset);
3692a362eccSPeter Zijlstra 		pnode = rb_prev(&sym->node);
3702a362eccSPeter Zijlstra 		if (pnode)
3712a362eccSPeter Zijlstra 			entry = &rb_entry(pnode, struct symbol, node)->list;
3722a362eccSPeter Zijlstra 		else
373a196e171SJosh Poimboeuf 			entry = &sym->sec->symbol_list;
374442f04c3SJosh Poimboeuf 		list_add(&sym->list, entry);
37534f7c96dSPeter Zijlstra 		elf_hash_add(elf->symbol_hash, &sym->hash, sym->idx);
37634f7c96dSPeter Zijlstra 		elf_hash_add(elf->symbol_name_hash, &sym->name_hash, str_hash(sym->name));
377a2e38dffSJosh Poimboeuf 
378a2e38dffSJosh Poimboeuf 		/*
379a2e38dffSJosh Poimboeuf 		 * Don't store empty STT_NOTYPE symbols in the rbtree.  They
380a2e38dffSJosh Poimboeuf 		 * can exist within a function, confusing the sorting.
381a2e38dffSJosh Poimboeuf 		 */
382a2e38dffSJosh Poimboeuf 		if (!sym->len)
383a2e38dffSJosh Poimboeuf 			rb_erase(&sym->node, &sym->sec->symbol_tree);
384442f04c3SJosh Poimboeuf 	}
385442f04c3SJosh Poimboeuf 
3861e11f3fdSPeter Zijlstra 	if (stats)
3871e11f3fdSPeter Zijlstra 		printf("nr_symbols: %lu\n", (unsigned long)symbols_nr);
3881e11f3fdSPeter Zijlstra 
38913810435SJosh Poimboeuf 	/* Create parent/child links for any cold subfunctions */
39013810435SJosh Poimboeuf 	list_for_each_entry(sec, &elf->sections, list) {
39113810435SJosh Poimboeuf 		list_for_each_entry(sym, &sec->symbol_list, list) {
39222566c16SArtem Savkov 			char pname[MAX_NAME_LEN + 1];
39322566c16SArtem Savkov 			size_t pnamelen;
39413810435SJosh Poimboeuf 			if (sym->type != STT_FUNC)
39513810435SJosh Poimboeuf 				continue;
396e000acc1SKristen Carlson Accardi 
397e000acc1SKristen Carlson Accardi 			if (sym->pfunc == NULL)
398e000acc1SKristen Carlson Accardi 				sym->pfunc = sym;
399e000acc1SKristen Carlson Accardi 
400e000acc1SKristen Carlson Accardi 			if (sym->cfunc == NULL)
401e000acc1SKristen Carlson Accardi 				sym->cfunc = sym;
402e000acc1SKristen Carlson Accardi 
403bcb6fb5dSJosh Poimboeuf 			coldstr = strstr(sym->name, ".cold");
40408b393d0SJosh Poimboeuf 			if (!coldstr)
40508b393d0SJosh Poimboeuf 				continue;
40608b393d0SJosh Poimboeuf 
40722566c16SArtem Savkov 			pnamelen = coldstr - sym->name;
40822566c16SArtem Savkov 			if (pnamelen > MAX_NAME_LEN) {
40922566c16SArtem Savkov 				WARN("%s(): parent function name exceeds maximum length of %d characters",
41022566c16SArtem Savkov 				     sym->name, MAX_NAME_LEN);
41122566c16SArtem Savkov 				return -1;
41222566c16SArtem Savkov 			}
41322566c16SArtem Savkov 
41422566c16SArtem Savkov 			strncpy(pname, sym->name, pnamelen);
41522566c16SArtem Savkov 			pname[pnamelen] = '\0';
41622566c16SArtem Savkov 			pfunc = find_symbol_by_name(elf, pname);
41713810435SJosh Poimboeuf 
41813810435SJosh Poimboeuf 			if (!pfunc) {
41913810435SJosh Poimboeuf 				WARN("%s(): can't find parent function",
42013810435SJosh Poimboeuf 				     sym->name);
4210b9301fbSArtem Savkov 				return -1;
42213810435SJosh Poimboeuf 			}
42313810435SJosh Poimboeuf 
42413810435SJosh Poimboeuf 			sym->pfunc = pfunc;
42513810435SJosh Poimboeuf 			pfunc->cfunc = sym;
42608b393d0SJosh Poimboeuf 
42708b393d0SJosh Poimboeuf 			/*
42808b393d0SJosh Poimboeuf 			 * Unfortunately, -fnoreorder-functions puts the child
42908b393d0SJosh Poimboeuf 			 * inside the parent.  Remove the overlap so we can
43008b393d0SJosh Poimboeuf 			 * have sane assumptions.
43108b393d0SJosh Poimboeuf 			 *
43208b393d0SJosh Poimboeuf 			 * Note that pfunc->len now no longer matches
43308b393d0SJosh Poimboeuf 			 * pfunc->sym.st_size.
43408b393d0SJosh Poimboeuf 			 */
43508b393d0SJosh Poimboeuf 			if (sym->sec == pfunc->sec &&
43608b393d0SJosh Poimboeuf 			    sym->offset >= pfunc->offset &&
43708b393d0SJosh Poimboeuf 			    sym->offset + sym->len == pfunc->offset + pfunc->len) {
43808b393d0SJosh Poimboeuf 				pfunc->len -= sym->len;
43913810435SJosh Poimboeuf 			}
44013810435SJosh Poimboeuf 		}
44113810435SJosh Poimboeuf 	}
44213810435SJosh Poimboeuf 
443442f04c3SJosh Poimboeuf 	return 0;
444442f04c3SJosh Poimboeuf 
445442f04c3SJosh Poimboeuf err:
446442f04c3SJosh Poimboeuf 	free(sym);
447442f04c3SJosh Poimboeuf 	return -1;
448442f04c3SJosh Poimboeuf }
449442f04c3SJosh Poimboeuf 
450*d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf,
451*d0c5c4ccSPeter Zijlstra 						struct section *base,
452*d0c5c4ccSPeter Zijlstra 						int reltype);
453*d0c5c4ccSPeter Zijlstra 
454ef47cc01SPeter Zijlstra int elf_add_reloc(struct elf *elf, struct section *sec, unsigned long offset,
455ef47cc01SPeter Zijlstra 		  unsigned int type, struct symbol *sym, int addend)
45634f7c96dSPeter Zijlstra {
457ef47cc01SPeter Zijlstra 	struct reloc *reloc;
45834f7c96dSPeter Zijlstra 
459*d0c5c4ccSPeter Zijlstra 	if (!sec->reloc && !elf_create_reloc_section(elf, sec, SHT_RELA))
460*d0c5c4ccSPeter Zijlstra 		return -1;
461*d0c5c4ccSPeter Zijlstra 
462ef47cc01SPeter Zijlstra 	reloc = malloc(sizeof(*reloc));
463ef47cc01SPeter Zijlstra 	if (!reloc) {
464ef47cc01SPeter Zijlstra 		perror("malloc");
465ef47cc01SPeter Zijlstra 		return -1;
466ef47cc01SPeter Zijlstra 	}
467ef47cc01SPeter Zijlstra 	memset(reloc, 0, sizeof(*reloc));
468ef47cc01SPeter Zijlstra 
469ef47cc01SPeter Zijlstra 	reloc->sec = sec->reloc;
470ef47cc01SPeter Zijlstra 	reloc->offset = offset;
471ef47cc01SPeter Zijlstra 	reloc->type = type;
472ef47cc01SPeter Zijlstra 	reloc->sym = sym;
473ef47cc01SPeter Zijlstra 	reloc->addend = addend;
474ef47cc01SPeter Zijlstra 
475ef47cc01SPeter Zijlstra 	list_add_tail(&reloc->list, &sec->reloc->reloc_list);
476f1974222SMatt Helsley 	elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc));
4773a647607SPeter Zijlstra 
478ef47cc01SPeter Zijlstra 	sec->reloc->changed = true;
479ef47cc01SPeter Zijlstra 
480ef47cc01SPeter Zijlstra 	return 0;
481ef47cc01SPeter Zijlstra }
482ef47cc01SPeter Zijlstra 
483ef47cc01SPeter Zijlstra int elf_add_reloc_to_insn(struct elf *elf, struct section *sec,
484ef47cc01SPeter Zijlstra 			  unsigned long offset, unsigned int type,
485ef47cc01SPeter Zijlstra 			  struct section *insn_sec, unsigned long insn_off)
486ef47cc01SPeter Zijlstra {
487ef47cc01SPeter Zijlstra 	struct symbol *sym;
488ef47cc01SPeter Zijlstra 	int addend;
489ef47cc01SPeter Zijlstra 
490ef47cc01SPeter Zijlstra 	if (insn_sec->sym) {
491ef47cc01SPeter Zijlstra 		sym = insn_sec->sym;
492ef47cc01SPeter Zijlstra 		addend = insn_off;
493ef47cc01SPeter Zijlstra 
494ef47cc01SPeter Zijlstra 	} else {
495ef47cc01SPeter Zijlstra 		/*
496ef47cc01SPeter Zijlstra 		 * The Clang assembler strips section symbols, so we have to
497ef47cc01SPeter Zijlstra 		 * reference the function symbol instead:
498ef47cc01SPeter Zijlstra 		 */
499ef47cc01SPeter Zijlstra 		sym = find_symbol_containing(insn_sec, insn_off);
500ef47cc01SPeter Zijlstra 		if (!sym) {
501ef47cc01SPeter Zijlstra 			/*
502ef47cc01SPeter Zijlstra 			 * Hack alert.  This happens when we need to reference
503ef47cc01SPeter Zijlstra 			 * the NOP pad insn immediately after the function.
504ef47cc01SPeter Zijlstra 			 */
505ef47cc01SPeter Zijlstra 			sym = find_symbol_containing(insn_sec, insn_off - 1);
506ef47cc01SPeter Zijlstra 		}
507ef47cc01SPeter Zijlstra 
508ef47cc01SPeter Zijlstra 		if (!sym) {
509ef47cc01SPeter Zijlstra 			WARN("can't find symbol containing %s+0x%lx", insn_sec->name, insn_off);
510ef47cc01SPeter Zijlstra 			return -1;
511ef47cc01SPeter Zijlstra 		}
512ef47cc01SPeter Zijlstra 
513ef47cc01SPeter Zijlstra 		addend = insn_off - sym->offset;
514ef47cc01SPeter Zijlstra 	}
515ef47cc01SPeter Zijlstra 
516ef47cc01SPeter Zijlstra 	return elf_add_reloc(elf, sec, offset, type, sym, addend);
51734f7c96dSPeter Zijlstra }
51834f7c96dSPeter Zijlstra 
519fb414783SMatt Helsley static int read_rel_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)
520fb414783SMatt Helsley {
521fb414783SMatt Helsley 	if (!gelf_getrel(sec->data, i, &reloc->rel)) {
522fb414783SMatt Helsley 		WARN_ELF("gelf_getrel");
523fb414783SMatt Helsley 		return -1;
524fb414783SMatt Helsley 	}
525fb414783SMatt Helsley 	reloc->type = GELF_R_TYPE(reloc->rel.r_info);
526fb414783SMatt Helsley 	reloc->addend = 0;
527fb414783SMatt Helsley 	reloc->offset = reloc->rel.r_offset;
528fb414783SMatt Helsley 	*symndx = GELF_R_SYM(reloc->rel.r_info);
529fb414783SMatt Helsley 	return 0;
530fb414783SMatt Helsley }
531fb414783SMatt Helsley 
532fb414783SMatt Helsley static int read_rela_reloc(struct section *sec, int i, struct reloc *reloc, unsigned int *symndx)
533fb414783SMatt Helsley {
534fb414783SMatt Helsley 	if (!gelf_getrela(sec->data, i, &reloc->rela)) {
535fb414783SMatt Helsley 		WARN_ELF("gelf_getrela");
536fb414783SMatt Helsley 		return -1;
537fb414783SMatt Helsley 	}
538fb414783SMatt Helsley 	reloc->type = GELF_R_TYPE(reloc->rela.r_info);
539fb414783SMatt Helsley 	reloc->addend = reloc->rela.r_addend;
540fb414783SMatt Helsley 	reloc->offset = reloc->rela.r_offset;
541fb414783SMatt Helsley 	*symndx = GELF_R_SYM(reloc->rela.r_info);
542fb414783SMatt Helsley 	return 0;
543fb414783SMatt Helsley }
544fb414783SMatt Helsley 
545f1974222SMatt Helsley static int read_relocs(struct elf *elf)
546442f04c3SJosh Poimboeuf {
547442f04c3SJosh Poimboeuf 	struct section *sec;
548f1974222SMatt Helsley 	struct reloc *reloc;
549442f04c3SJosh Poimboeuf 	int i;
550442f04c3SJosh Poimboeuf 	unsigned int symndx;
551f1974222SMatt Helsley 	unsigned long nr_reloc, max_reloc = 0, tot_reloc = 0;
552442f04c3SJosh Poimboeuf 
553442f04c3SJosh Poimboeuf 	list_for_each_entry(sec, &elf->sections, list) {
554fb414783SMatt Helsley 		if ((sec->sh.sh_type != SHT_RELA) &&
555fb414783SMatt Helsley 		    (sec->sh.sh_type != SHT_REL))
556442f04c3SJosh Poimboeuf 			continue;
557442f04c3SJosh Poimboeuf 
5581e968bf5SSami Tolvanen 		sec->base = find_section_by_index(elf, sec->sh.sh_info);
559442f04c3SJosh Poimboeuf 		if (!sec->base) {
560f1974222SMatt Helsley 			WARN("can't find base section for reloc section %s",
561442f04c3SJosh Poimboeuf 			     sec->name);
562442f04c3SJosh Poimboeuf 			return -1;
563442f04c3SJosh Poimboeuf 		}
564442f04c3SJosh Poimboeuf 
565f1974222SMatt Helsley 		sec->base->reloc = sec;
566442f04c3SJosh Poimboeuf 
567f1974222SMatt Helsley 		nr_reloc = 0;
568442f04c3SJosh Poimboeuf 		for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) {
569f1974222SMatt Helsley 			reloc = malloc(sizeof(*reloc));
570f1974222SMatt Helsley 			if (!reloc) {
571442f04c3SJosh Poimboeuf 				perror("malloc");
572442f04c3SJosh Poimboeuf 				return -1;
573442f04c3SJosh Poimboeuf 			}
574f1974222SMatt Helsley 			memset(reloc, 0, sizeof(*reloc));
575fb414783SMatt Helsley 			switch (sec->sh.sh_type) {
576fb414783SMatt Helsley 			case SHT_REL:
577fb414783SMatt Helsley 				if (read_rel_reloc(sec, i, reloc, &symndx))
578442f04c3SJosh Poimboeuf 					return -1;
579fb414783SMatt Helsley 				break;
580fb414783SMatt Helsley 			case SHT_RELA:
581fb414783SMatt Helsley 				if (read_rela_reloc(sec, i, reloc, &symndx))
582fb414783SMatt Helsley 					return -1;
583fb414783SMatt Helsley 				break;
584fb414783SMatt Helsley 			default: return -1;
585442f04c3SJosh Poimboeuf 			}
586442f04c3SJosh Poimboeuf 
587f1974222SMatt Helsley 			reloc->sec = sec;
588d832c005SPeter Zijlstra 			reloc->idx = i;
589d832c005SPeter Zijlstra 			reloc->sym = find_symbol_by_index(elf, symndx);
590f1974222SMatt Helsley 			if (!reloc->sym) {
591f1974222SMatt Helsley 				WARN("can't find reloc entry symbol %d for %s",
592442f04c3SJosh Poimboeuf 				     symndx, sec->name);
593442f04c3SJosh Poimboeuf 				return -1;
594442f04c3SJosh Poimboeuf 			}
595042ba73fSJosh Poimboeuf 
5963a647607SPeter Zijlstra 			list_add_tail(&reloc->list, &sec->reloc_list);
5973a647607SPeter Zijlstra 			elf_hash_add(elf->reloc_hash, &reloc->hash, reloc_hash(reloc));
5983a647607SPeter Zijlstra 
599f1974222SMatt Helsley 			nr_reloc++;
600442f04c3SJosh Poimboeuf 		}
601f1974222SMatt Helsley 		max_reloc = max(max_reloc, nr_reloc);
602f1974222SMatt Helsley 		tot_reloc += nr_reloc;
6031e11f3fdSPeter Zijlstra 	}
6041e11f3fdSPeter Zijlstra 
6051e11f3fdSPeter Zijlstra 	if (stats) {
606f1974222SMatt Helsley 		printf("max_reloc: %lu\n", max_reloc);
607f1974222SMatt Helsley 		printf("tot_reloc: %lu\n", tot_reloc);
608442f04c3SJosh Poimboeuf 	}
609442f04c3SJosh Poimboeuf 
610442f04c3SJosh Poimboeuf 	return 0;
611442f04c3SJosh Poimboeuf }
612442f04c3SJosh Poimboeuf 
613bc359ff2SIngo Molnar struct elf *elf_open_read(const char *name, int flags)
614442f04c3SJosh Poimboeuf {
615442f04c3SJosh Poimboeuf 	struct elf *elf;
616627fce14SJosh Poimboeuf 	Elf_Cmd cmd;
617442f04c3SJosh Poimboeuf 
618442f04c3SJosh Poimboeuf 	elf_version(EV_CURRENT);
619442f04c3SJosh Poimboeuf 
620442f04c3SJosh Poimboeuf 	elf = malloc(sizeof(*elf));
621442f04c3SJosh Poimboeuf 	if (!elf) {
622442f04c3SJosh Poimboeuf 		perror("malloc");
623442f04c3SJosh Poimboeuf 		return NULL;
624442f04c3SJosh Poimboeuf 	}
62534f7c96dSPeter Zijlstra 	memset(elf, 0, offsetof(struct elf, sections));
626442f04c3SJosh Poimboeuf 
627442f04c3SJosh Poimboeuf 	INIT_LIST_HEAD(&elf->sections);
628442f04c3SJosh Poimboeuf 
62934f7c96dSPeter Zijlstra 	elf_hash_init(elf->symbol_hash);
63034f7c96dSPeter Zijlstra 	elf_hash_init(elf->symbol_name_hash);
63134f7c96dSPeter Zijlstra 	elf_hash_init(elf->section_hash);
63234f7c96dSPeter Zijlstra 	elf_hash_init(elf->section_name_hash);
633f1974222SMatt Helsley 	elf_hash_init(elf->reloc_hash);
63434f7c96dSPeter Zijlstra 
635627fce14SJosh Poimboeuf 	elf->fd = open(name, flags);
636442f04c3SJosh Poimboeuf 	if (elf->fd == -1) {
637385d11b1SJosh Poimboeuf 		fprintf(stderr, "objtool: Can't open '%s': %s\n",
638385d11b1SJosh Poimboeuf 			name, strerror(errno));
639442f04c3SJosh Poimboeuf 		goto err;
640442f04c3SJosh Poimboeuf 	}
641442f04c3SJosh Poimboeuf 
642627fce14SJosh Poimboeuf 	if ((flags & O_ACCMODE) == O_RDONLY)
643627fce14SJosh Poimboeuf 		cmd = ELF_C_READ_MMAP;
644627fce14SJosh Poimboeuf 	else if ((flags & O_ACCMODE) == O_RDWR)
645627fce14SJosh Poimboeuf 		cmd = ELF_C_RDWR;
646627fce14SJosh Poimboeuf 	else /* O_WRONLY */
647627fce14SJosh Poimboeuf 		cmd = ELF_C_WRITE;
648627fce14SJosh Poimboeuf 
649627fce14SJosh Poimboeuf 	elf->elf = elf_begin(elf->fd, cmd, NULL);
650442f04c3SJosh Poimboeuf 	if (!elf->elf) {
651baa41469SJosh Poimboeuf 		WARN_ELF("elf_begin");
652442f04c3SJosh Poimboeuf 		goto err;
653442f04c3SJosh Poimboeuf 	}
654442f04c3SJosh Poimboeuf 
655442f04c3SJosh Poimboeuf 	if (!gelf_getehdr(elf->elf, &elf->ehdr)) {
656baa41469SJosh Poimboeuf 		WARN_ELF("gelf_getehdr");
657442f04c3SJosh Poimboeuf 		goto err;
658442f04c3SJosh Poimboeuf 	}
659442f04c3SJosh Poimboeuf 
660442f04c3SJosh Poimboeuf 	if (read_sections(elf))
661442f04c3SJosh Poimboeuf 		goto err;
662442f04c3SJosh Poimboeuf 
663442f04c3SJosh Poimboeuf 	if (read_symbols(elf))
664442f04c3SJosh Poimboeuf 		goto err;
665442f04c3SJosh Poimboeuf 
666f1974222SMatt Helsley 	if (read_relocs(elf))
667442f04c3SJosh Poimboeuf 		goto err;
668442f04c3SJosh Poimboeuf 
669442f04c3SJosh Poimboeuf 	return elf;
670442f04c3SJosh Poimboeuf 
671442f04c3SJosh Poimboeuf err:
672442f04c3SJosh Poimboeuf 	elf_close(elf);
673442f04c3SJosh Poimboeuf 	return NULL;
674442f04c3SJosh Poimboeuf }
675442f04c3SJosh Poimboeuf 
676627fce14SJosh Poimboeuf struct section *elf_create_section(struct elf *elf, const char *name,
6771e7e4788SJosh Poimboeuf 				   unsigned int sh_flags, size_t entsize, int nr)
678627fce14SJosh Poimboeuf {
679627fce14SJosh Poimboeuf 	struct section *sec, *shstrtab;
680627fce14SJosh Poimboeuf 	size_t size = entsize * nr;
6813c3ea503SMichael Forney 	Elf_Scn *s;
682627fce14SJosh Poimboeuf 	Elf_Data *data;
683627fce14SJosh Poimboeuf 
684627fce14SJosh Poimboeuf 	sec = malloc(sizeof(*sec));
685627fce14SJosh Poimboeuf 	if (!sec) {
686627fce14SJosh Poimboeuf 		perror("malloc");
687627fce14SJosh Poimboeuf 		return NULL;
688627fce14SJosh Poimboeuf 	}
689627fce14SJosh Poimboeuf 	memset(sec, 0, sizeof(*sec));
690627fce14SJosh Poimboeuf 
691627fce14SJosh Poimboeuf 	INIT_LIST_HEAD(&sec->symbol_list);
692f1974222SMatt Helsley 	INIT_LIST_HEAD(&sec->reloc_list);
693627fce14SJosh Poimboeuf 
694627fce14SJosh Poimboeuf 	s = elf_newscn(elf->elf);
695627fce14SJosh Poimboeuf 	if (!s) {
696627fce14SJosh Poimboeuf 		WARN_ELF("elf_newscn");
697627fce14SJosh Poimboeuf 		return NULL;
698627fce14SJosh Poimboeuf 	}
699627fce14SJosh Poimboeuf 
700627fce14SJosh Poimboeuf 	sec->name = strdup(name);
701627fce14SJosh Poimboeuf 	if (!sec->name) {
702627fce14SJosh Poimboeuf 		perror("strdup");
703627fce14SJosh Poimboeuf 		return NULL;
704627fce14SJosh Poimboeuf 	}
705627fce14SJosh Poimboeuf 
706627fce14SJosh Poimboeuf 	sec->idx = elf_ndxscn(s);
707627fce14SJosh Poimboeuf 	sec->len = size;
708627fce14SJosh Poimboeuf 	sec->changed = true;
709627fce14SJosh Poimboeuf 
710627fce14SJosh Poimboeuf 	sec->data = elf_newdata(s);
711627fce14SJosh Poimboeuf 	if (!sec->data) {
712627fce14SJosh Poimboeuf 		WARN_ELF("elf_newdata");
713627fce14SJosh Poimboeuf 		return NULL;
714627fce14SJosh Poimboeuf 	}
715627fce14SJosh Poimboeuf 
716627fce14SJosh Poimboeuf 	sec->data->d_size = size;
717627fce14SJosh Poimboeuf 	sec->data->d_align = 1;
718627fce14SJosh Poimboeuf 
719627fce14SJosh Poimboeuf 	if (size) {
720627fce14SJosh Poimboeuf 		sec->data->d_buf = malloc(size);
721627fce14SJosh Poimboeuf 		if (!sec->data->d_buf) {
722627fce14SJosh Poimboeuf 			perror("malloc");
723627fce14SJosh Poimboeuf 			return NULL;
724627fce14SJosh Poimboeuf 		}
725627fce14SJosh Poimboeuf 		memset(sec->data->d_buf, 0, size);
726627fce14SJosh Poimboeuf 	}
727627fce14SJosh Poimboeuf 
728627fce14SJosh Poimboeuf 	if (!gelf_getshdr(s, &sec->sh)) {
729627fce14SJosh Poimboeuf 		WARN_ELF("gelf_getshdr");
730627fce14SJosh Poimboeuf 		return NULL;
731627fce14SJosh Poimboeuf 	}
732627fce14SJosh Poimboeuf 
733627fce14SJosh Poimboeuf 	sec->sh.sh_size = size;
734627fce14SJosh Poimboeuf 	sec->sh.sh_entsize = entsize;
735627fce14SJosh Poimboeuf 	sec->sh.sh_type = SHT_PROGBITS;
736627fce14SJosh Poimboeuf 	sec->sh.sh_addralign = 1;
7371e7e4788SJosh Poimboeuf 	sec->sh.sh_flags = SHF_ALLOC | sh_flags;
738627fce14SJosh Poimboeuf 
739627fce14SJosh Poimboeuf 
7406d77d3b4SSimon Ser 	/* Add section name to .shstrtab (or .strtab for Clang) */
741627fce14SJosh Poimboeuf 	shstrtab = find_section_by_name(elf, ".shstrtab");
7426d77d3b4SSimon Ser 	if (!shstrtab)
7436d77d3b4SSimon Ser 		shstrtab = find_section_by_name(elf, ".strtab");
744627fce14SJosh Poimboeuf 	if (!shstrtab) {
7456d77d3b4SSimon Ser 		WARN("can't find .shstrtab or .strtab section");
746627fce14SJosh Poimboeuf 		return NULL;
747627fce14SJosh Poimboeuf 	}
748627fce14SJosh Poimboeuf 
749627fce14SJosh Poimboeuf 	s = elf_getscn(elf->elf, shstrtab->idx);
750627fce14SJosh Poimboeuf 	if (!s) {
751627fce14SJosh Poimboeuf 		WARN_ELF("elf_getscn");
752627fce14SJosh Poimboeuf 		return NULL;
753627fce14SJosh Poimboeuf 	}
754627fce14SJosh Poimboeuf 
755627fce14SJosh Poimboeuf 	data = elf_newdata(s);
756627fce14SJosh Poimboeuf 	if (!data) {
757627fce14SJosh Poimboeuf 		WARN_ELF("elf_newdata");
758627fce14SJosh Poimboeuf 		return NULL;
759627fce14SJosh Poimboeuf 	}
760627fce14SJosh Poimboeuf 
761627fce14SJosh Poimboeuf 	data->d_buf = sec->name;
762627fce14SJosh Poimboeuf 	data->d_size = strlen(name) + 1;
763627fce14SJosh Poimboeuf 	data->d_align = 1;
764627fce14SJosh Poimboeuf 
765627fce14SJosh Poimboeuf 	sec->sh.sh_name = shstrtab->len;
766627fce14SJosh Poimboeuf 
767627fce14SJosh Poimboeuf 	shstrtab->len += strlen(name) + 1;
768627fce14SJosh Poimboeuf 	shstrtab->changed = true;
769627fce14SJosh Poimboeuf 
77053038996SPeter Zijlstra 	list_add_tail(&sec->list, &elf->sections);
77134f7c96dSPeter Zijlstra 	elf_hash_add(elf->section_hash, &sec->hash, sec->idx);
77234f7c96dSPeter Zijlstra 	elf_hash_add(elf->section_name_hash, &sec->name_hash, str_hash(sec->name));
77353038996SPeter Zijlstra 
7742b10be23SPeter Zijlstra 	elf->changed = true;
7752b10be23SPeter Zijlstra 
776627fce14SJosh Poimboeuf 	return sec;
777627fce14SJosh Poimboeuf }
778627fce14SJosh Poimboeuf 
779fb414783SMatt Helsley static struct section *elf_create_rel_reloc_section(struct elf *elf, struct section *base)
780fb414783SMatt Helsley {
781fb414783SMatt Helsley 	char *relocname;
782fb414783SMatt Helsley 	struct section *sec;
783fb414783SMatt Helsley 
784fb414783SMatt Helsley 	relocname = malloc(strlen(base->name) + strlen(".rel") + 1);
785fb414783SMatt Helsley 	if (!relocname) {
786fb414783SMatt Helsley 		perror("malloc");
787fb414783SMatt Helsley 		return NULL;
788fb414783SMatt Helsley 	}
789fb414783SMatt Helsley 	strcpy(relocname, ".rel");
790fb414783SMatt Helsley 	strcat(relocname, base->name);
791fb414783SMatt Helsley 
7921e7e4788SJosh Poimboeuf 	sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rel), 0);
793fb414783SMatt Helsley 	free(relocname);
794fb414783SMatt Helsley 	if (!sec)
795fb414783SMatt Helsley 		return NULL;
796fb414783SMatt Helsley 
797fb414783SMatt Helsley 	base->reloc = sec;
798fb414783SMatt Helsley 	sec->base = base;
799fb414783SMatt Helsley 
800fb414783SMatt Helsley 	sec->sh.sh_type = SHT_REL;
801fb414783SMatt Helsley 	sec->sh.sh_addralign = 8;
802fb414783SMatt Helsley 	sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
803fb414783SMatt Helsley 	sec->sh.sh_info = base->idx;
804fb414783SMatt Helsley 	sec->sh.sh_flags = SHF_INFO_LINK;
805fb414783SMatt Helsley 
806fb414783SMatt Helsley 	return sec;
807fb414783SMatt Helsley }
808fb414783SMatt Helsley 
809fb414783SMatt Helsley static struct section *elf_create_rela_reloc_section(struct elf *elf, struct section *base)
810627fce14SJosh Poimboeuf {
811f1974222SMatt Helsley 	char *relocname;
812627fce14SJosh Poimboeuf 	struct section *sec;
813627fce14SJosh Poimboeuf 
814f1974222SMatt Helsley 	relocname = malloc(strlen(base->name) + strlen(".rela") + 1);
815f1974222SMatt Helsley 	if (!relocname) {
816627fce14SJosh Poimboeuf 		perror("malloc");
817627fce14SJosh Poimboeuf 		return NULL;
818627fce14SJosh Poimboeuf 	}
819f1974222SMatt Helsley 	strcpy(relocname, ".rela");
820f1974222SMatt Helsley 	strcat(relocname, base->name);
821627fce14SJosh Poimboeuf 
8221e7e4788SJosh Poimboeuf 	sec = elf_create_section(elf, relocname, 0, sizeof(GElf_Rela), 0);
823f1974222SMatt Helsley 	free(relocname);
824627fce14SJosh Poimboeuf 	if (!sec)
825627fce14SJosh Poimboeuf 		return NULL;
826627fce14SJosh Poimboeuf 
827f1974222SMatt Helsley 	base->reloc = sec;
828627fce14SJosh Poimboeuf 	sec->base = base;
829627fce14SJosh Poimboeuf 
830627fce14SJosh Poimboeuf 	sec->sh.sh_type = SHT_RELA;
831627fce14SJosh Poimboeuf 	sec->sh.sh_addralign = 8;
832627fce14SJosh Poimboeuf 	sec->sh.sh_link = find_section_by_name(elf, ".symtab")->idx;
833627fce14SJosh Poimboeuf 	sec->sh.sh_info = base->idx;
834627fce14SJosh Poimboeuf 	sec->sh.sh_flags = SHF_INFO_LINK;
835627fce14SJosh Poimboeuf 
836627fce14SJosh Poimboeuf 	return sec;
837627fce14SJosh Poimboeuf }
838627fce14SJosh Poimboeuf 
839*d0c5c4ccSPeter Zijlstra static struct section *elf_create_reloc_section(struct elf *elf,
840fb414783SMatt Helsley 					 struct section *base,
841fb414783SMatt Helsley 					 int reltype)
842fb414783SMatt Helsley {
843fb414783SMatt Helsley 	switch (reltype) {
844fb414783SMatt Helsley 	case SHT_REL:  return elf_create_rel_reloc_section(elf, base);
845fb414783SMatt Helsley 	case SHT_RELA: return elf_create_rela_reloc_section(elf, base);
846fb414783SMatt Helsley 	default:       return NULL;
847fb414783SMatt Helsley 	}
848fb414783SMatt Helsley }
849fb414783SMatt Helsley 
850fb414783SMatt Helsley static int elf_rebuild_rel_reloc_section(struct section *sec, int nr)
851627fce14SJosh Poimboeuf {
852f1974222SMatt Helsley 	struct reloc *reloc;
853fb414783SMatt Helsley 	int idx = 0, size;
854a1a664ecSMartin Schwidefsky 	void *buf;
855fb414783SMatt Helsley 
856fb414783SMatt Helsley 	/* Allocate a buffer for relocations */
857a1a664ecSMartin Schwidefsky 	size = nr * sizeof(GElf_Rel);
858a1a664ecSMartin Schwidefsky 	buf = malloc(size);
859a1a664ecSMartin Schwidefsky 	if (!buf) {
860fb414783SMatt Helsley 		perror("malloc");
861fb414783SMatt Helsley 		return -1;
862fb414783SMatt Helsley 	}
863fb414783SMatt Helsley 
864a1a664ecSMartin Schwidefsky 	sec->data->d_buf = buf;
865fb414783SMatt Helsley 	sec->data->d_size = size;
866a1a664ecSMartin Schwidefsky 	sec->data->d_type = ELF_T_REL;
867fb414783SMatt Helsley 
868fb414783SMatt Helsley 	sec->sh.sh_size = size;
869fb414783SMatt Helsley 
870fb414783SMatt Helsley 	idx = 0;
871fb414783SMatt Helsley 	list_for_each_entry(reloc, &sec->reloc_list, list) {
872a1a664ecSMartin Schwidefsky 		reloc->rel.r_offset = reloc->offset;
873a1a664ecSMartin Schwidefsky 		reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
874a1a664ecSMartin Schwidefsky 		gelf_update_rel(sec->data, idx, &reloc->rel);
875fb414783SMatt Helsley 		idx++;
876fb414783SMatt Helsley 	}
877fb414783SMatt Helsley 
878fb414783SMatt Helsley 	return 0;
879fb414783SMatt Helsley }
880fb414783SMatt Helsley 
881fb414783SMatt Helsley static int elf_rebuild_rela_reloc_section(struct section *sec, int nr)
882fb414783SMatt Helsley {
883fb414783SMatt Helsley 	struct reloc *reloc;
884fb414783SMatt Helsley 	int idx = 0, size;
885a1a664ecSMartin Schwidefsky 	void *buf;
886627fce14SJosh Poimboeuf 
887fb414783SMatt Helsley 	/* Allocate a buffer for relocations with addends */
888a1a664ecSMartin Schwidefsky 	size = nr * sizeof(GElf_Rela);
889a1a664ecSMartin Schwidefsky 	buf = malloc(size);
890a1a664ecSMartin Schwidefsky 	if (!buf) {
891627fce14SJosh Poimboeuf 		perror("malloc");
892627fce14SJosh Poimboeuf 		return -1;
893627fce14SJosh Poimboeuf 	}
894627fce14SJosh Poimboeuf 
895a1a664ecSMartin Schwidefsky 	sec->data->d_buf = buf;
896627fce14SJosh Poimboeuf 	sec->data->d_size = size;
897a1a664ecSMartin Schwidefsky 	sec->data->d_type = ELF_T_RELA;
898627fce14SJosh Poimboeuf 
899627fce14SJosh Poimboeuf 	sec->sh.sh_size = size;
900627fce14SJosh Poimboeuf 
901627fce14SJosh Poimboeuf 	idx = 0;
902f1974222SMatt Helsley 	list_for_each_entry(reloc, &sec->reloc_list, list) {
903a1a664ecSMartin Schwidefsky 		reloc->rela.r_offset = reloc->offset;
904a1a664ecSMartin Schwidefsky 		reloc->rela.r_addend = reloc->addend;
905a1a664ecSMartin Schwidefsky 		reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
906a1a664ecSMartin Schwidefsky 		gelf_update_rela(sec->data, idx, &reloc->rela);
907627fce14SJosh Poimboeuf 		idx++;
908627fce14SJosh Poimboeuf 	}
909627fce14SJosh Poimboeuf 
910627fce14SJosh Poimboeuf 	return 0;
911627fce14SJosh Poimboeuf }
912627fce14SJosh Poimboeuf 
9133a647607SPeter Zijlstra static int elf_rebuild_reloc_section(struct elf *elf, struct section *sec)
914fb414783SMatt Helsley {
915fb414783SMatt Helsley 	struct reloc *reloc;
916fb414783SMatt Helsley 	int nr;
917fb414783SMatt Helsley 
918fb414783SMatt Helsley 	nr = 0;
919fb414783SMatt Helsley 	list_for_each_entry(reloc, &sec->reloc_list, list)
920fb414783SMatt Helsley 		nr++;
921fb414783SMatt Helsley 
922fb414783SMatt Helsley 	switch (sec->sh.sh_type) {
923fb414783SMatt Helsley 	case SHT_REL:  return elf_rebuild_rel_reloc_section(sec, nr);
924fb414783SMatt Helsley 	case SHT_RELA: return elf_rebuild_rela_reloc_section(sec, nr);
925fb414783SMatt Helsley 	default:       return -1;
926fb414783SMatt Helsley 	}
927fb414783SMatt Helsley }
928fb414783SMatt Helsley 
929fdabdd0bSPeter Zijlstra int elf_write_insn(struct elf *elf, struct section *sec,
930fdabdd0bSPeter Zijlstra 		   unsigned long offset, unsigned int len,
931fdabdd0bSPeter Zijlstra 		   const char *insn)
932fdabdd0bSPeter Zijlstra {
933fdabdd0bSPeter Zijlstra 	Elf_Data *data = sec->data;
934fdabdd0bSPeter Zijlstra 
935fdabdd0bSPeter Zijlstra 	if (data->d_type != ELF_T_BYTE || data->d_off) {
936fdabdd0bSPeter Zijlstra 		WARN("write to unexpected data for section: %s", sec->name);
937fdabdd0bSPeter Zijlstra 		return -1;
938fdabdd0bSPeter Zijlstra 	}
939fdabdd0bSPeter Zijlstra 
940fdabdd0bSPeter Zijlstra 	memcpy(data->d_buf + offset, insn, len);
941fdabdd0bSPeter Zijlstra 	elf_flagdata(data, ELF_C_SET, ELF_F_DIRTY);
942fdabdd0bSPeter Zijlstra 
943fdabdd0bSPeter Zijlstra 	elf->changed = true;
944fdabdd0bSPeter Zijlstra 
945fdabdd0bSPeter Zijlstra 	return 0;
946fdabdd0bSPeter Zijlstra }
947fdabdd0bSPeter Zijlstra 
948d832c005SPeter Zijlstra int elf_write_reloc(struct elf *elf, struct reloc *reloc)
949fdabdd0bSPeter Zijlstra {
950d832c005SPeter Zijlstra 	struct section *sec = reloc->sec;
951fdabdd0bSPeter Zijlstra 
952d832c005SPeter Zijlstra 	if (sec->sh.sh_type == SHT_REL) {
953d832c005SPeter Zijlstra 		reloc->rel.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
954d832c005SPeter Zijlstra 		reloc->rel.r_offset = reloc->offset;
955fdabdd0bSPeter Zijlstra 
956d832c005SPeter Zijlstra 		if (!gelf_update_rel(sec->data, reloc->idx, &reloc->rel)) {
957d832c005SPeter Zijlstra 			WARN_ELF("gelf_update_rel");
958d832c005SPeter Zijlstra 			return -1;
959d832c005SPeter Zijlstra 		}
960d832c005SPeter Zijlstra 	} else {
961d832c005SPeter Zijlstra 		reloc->rela.r_info = GELF_R_INFO(reloc->sym->idx, reloc->type);
962d832c005SPeter Zijlstra 		reloc->rela.r_addend = reloc->addend;
963d832c005SPeter Zijlstra 		reloc->rela.r_offset = reloc->offset;
964d832c005SPeter Zijlstra 
965d832c005SPeter Zijlstra 		if (!gelf_update_rela(sec->data, reloc->idx, &reloc->rela)) {
966fdabdd0bSPeter Zijlstra 			WARN_ELF("gelf_update_rela");
967fdabdd0bSPeter Zijlstra 			return -1;
968fdabdd0bSPeter Zijlstra 		}
969d832c005SPeter Zijlstra 	}
970fdabdd0bSPeter Zijlstra 
971fdabdd0bSPeter Zijlstra 	elf->changed = true;
972fdabdd0bSPeter Zijlstra 
973fdabdd0bSPeter Zijlstra 	return 0;
974fdabdd0bSPeter Zijlstra }
975fdabdd0bSPeter Zijlstra 
9762b10be23SPeter Zijlstra int elf_write(struct elf *elf)
977627fce14SJosh Poimboeuf {
978627fce14SJosh Poimboeuf 	struct section *sec;
979627fce14SJosh Poimboeuf 	Elf_Scn *s;
980627fce14SJosh Poimboeuf 
9813a647607SPeter Zijlstra 	/* Update changed relocation sections and section headers: */
982627fce14SJosh Poimboeuf 	list_for_each_entry(sec, &elf->sections, list) {
983627fce14SJosh Poimboeuf 		if (sec->changed) {
9843a647607SPeter Zijlstra 			if (sec->base &&
9853a647607SPeter Zijlstra 			    elf_rebuild_reloc_section(elf, sec)) {
9863a647607SPeter Zijlstra 				WARN("elf_rebuild_reloc_section");
9873a647607SPeter Zijlstra 				return -1;
9883a647607SPeter Zijlstra 			}
9893a647607SPeter Zijlstra 
990627fce14SJosh Poimboeuf 			s = elf_getscn(elf->elf, sec->idx);
991627fce14SJosh Poimboeuf 			if (!s) {
992627fce14SJosh Poimboeuf 				WARN_ELF("elf_getscn");
993627fce14SJosh Poimboeuf 				return -1;
994627fce14SJosh Poimboeuf 			}
995627fce14SJosh Poimboeuf 			if (!gelf_update_shdr(s, &sec->sh)) {
996627fce14SJosh Poimboeuf 				WARN_ELF("gelf_update_shdr");
997627fce14SJosh Poimboeuf 				return -1;
998627fce14SJosh Poimboeuf 			}
9992b10be23SPeter Zijlstra 
10002b10be23SPeter Zijlstra 			sec->changed = false;
10013a647607SPeter Zijlstra 			elf->changed = true;
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