aboutsummaryrefslogtreecommitdiffstats
path: root/tools/objtool/include/objtool/elf.h
blob: 9f71e988eca45fd6bc81734b9a52d76b895e9517 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
 */

#ifndef _OBJTOOL_ELF_H
#define _OBJTOOL_ELF_H

#include <stdio.h>
#include <gelf.h>
#include <linux/list.h>
#include <linux/hashtable.h>
#include <linux/rbtree.h>
#include <linux/jhash.h>
#include <arch/elf.h>

#ifdef LIBELF_USE_DEPRECATED
# define elf_getshdrnum    elf_getshnum
# define elf_getshdrstrndx elf_getshstrndx
#endif

/*
 * Fallback for systems without this "read, mmaping if possible" cmd.
 */
#ifndef ELF_C_READ_MMAP
#define ELF_C_READ_MMAP ELF_C_READ
#endif

struct elf_hash_node {
	struct elf_hash_node *next;
};

struct section {
	struct list_head list;
	struct elf_hash_node hash;
	struct elf_hash_node name_hash;
	GElf_Shdr sh;
	struct rb_root_cached symbol_tree;
	struct list_head symbol_list;
	struct section *base, *rsec;
	struct symbol *sym;
	Elf_Data *data;
	char *name;
	int idx;
	bool _changed, text, rodata, noinstr, init, truncate;
	struct reloc *relocs;
};

struct symbol {
	struct list_head list;
	struct rb_node node;
	struct elf_hash_node hash;
	struct elf_hash_node name_hash;
	GElf_Sym sym;
	struct section *sec;
	char *name;
	unsigned int idx, len;
	unsigned long offset;
	unsigned long __subtree_last;
	struct symbol *pfunc, *cfunc, *alias;
	unsigned char bind, type;
	u8 uaccess_safe      : 1;
	u8 static_call_tramp : 1;
	u8 retpoline_thunk   : 1;
	u8 return_thunk      : 1;
	u8 fentry            : 1;
	u8 profiling_func    : 1;
	u8 warned	     : 1;
	u8 embedded_insn     : 1;
	struct list_head pv_target;
	struct reloc *relocs;
};

struct reloc {
	struct elf_hash_node hash;
	struct section *sec;
	struct symbol *sym;
	struct reloc *sym_next_reloc;
};

struct elf {
	Elf *elf;
	GElf_Ehdr ehdr;
	int fd;
	bool changed;
	char *name;
	unsigned int num_files;
	struct list_head sections;
	unsigned long num_relocs;

	int symbol_bits;
	int symbol_name_bits;
	int section_bits;
	int section_name_bits;
	int reloc_bits;

	struct elf_hash_node **symbol_hash;
	struct elf_hash_node **symbol_name_hash;
	struct elf_hash_node **section_hash;
	struct elf_hash_node **section_name_hash;
	struct elf_hash_node **reloc_hash;

	struct section *section_data;
	struct symbol *symbol_data;
};

struct elf *elf_open_read(const char *name, int flags);

struct section *elf_create_section(struct elf *elf, const char *name,
				   size_t entsize, unsigned int nr);
struct section *elf_create_section_pair(struct elf *elf, const char *name,
					size_t entsize, unsigned int nr,
					unsigned int reloc_nr);

struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size);

struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
				      unsigned long offset,
				      unsigned int reloc_idx,
				      struct section *insn_sec,
				      unsigned long insn_off);

struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
				      unsigned long offset,
				      unsigned int reloc_idx,
				      struct symbol *sym,
				      s64 addend);

int elf_write_insn(struct elf *elf, struct section *sec,
		   unsigned long offset, unsigned int len,
		   const char *insn);
int elf_write(struct elf *elf);
void elf_close(struct elf *elf);

struct section *find_section_by_name(const struct elf *elf, const char *name);
struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
int find_symbol_hole_containing(const struct section *sec, unsigned long offset);
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
				     unsigned long offset, unsigned int len);
struct symbol *find_func_containing(struct section *sec, unsigned long offset);

/*
 * Try to see if it's a whole archive (vmlinux.o or module).
 *
 * Note this will miss the case where a module only has one source file.
 */
static inline bool has_multiple_files(struct elf *elf)
{
	return elf->num_files > 1;
}

static inline size_t elf_addr_size(struct elf *elf)
{
	return elf->ehdr.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
}

static inline size_t elf_rela_size(struct elf *elf)
{
	return elf_addr_size(elf) == 4 ? sizeof(Elf32_Rela) : sizeof(Elf64_Rela);
}

static inline unsigned int elf_data_rela_type(struct elf *elf)
{
	return elf_addr_size(elf) == 4 ? R_DATA32 : R_DATA64;
}

static inline unsigned int elf_text_rela_type(struct elf *elf)
{
	return elf_addr_size(elf) == 4 ? R_TEXT32 : R_TEXT64;
}

static inline bool is_reloc_sec(struct section *sec)
{
	return sec->sh.sh_type == SHT_RELA || sec->sh.sh_type == SHT_REL;
}

static inline bool sec_changed(struct section *sec)
{
	return sec->_changed;
}

static inline void mark_sec_changed(struct elf *elf, struct section *sec,
				    bool changed)
{
	sec->_changed = changed;
	elf->changed |= changed;
}

static inline unsigned int sec_num_entries(struct section *sec)
{
	return sec->sh.sh_size / sec->sh.sh_entsize;
}

static inline unsigned int reloc_idx(struct reloc *reloc)
{
	return reloc - reloc->sec->relocs;
}

static inline void *reloc_rel(struct reloc *reloc)
{
	struct section *rsec = reloc->sec;

	return rsec->data->d_buf + (reloc_idx(reloc) * rsec->sh.sh_entsize);
}

static inline bool is_32bit_reloc(struct reloc *reloc)
{
	/*
	 * Elf32_Rel:   8 bytes
	 * Elf32_Rela: 12 bytes
	 * Elf64_Rel:  16 bytes
	 * Elf64_Rela: 24 bytes
	 */
	return reloc->sec->sh.sh_entsize < 16;
}

#define __get_reloc_field(reloc, field)					\
({									\
	is_32bit_reloc(reloc) ?						\
		((Elf32_Rela *)reloc_rel(reloc))->field :		\
		((Elf64_Rela *)reloc_rel(reloc))->field;		\
})

#define __set_reloc_field(reloc, field, val)				\
({									\
	if (is_32bit_reloc(reloc))					\
		((Elf32_Rela *)reloc_rel(reloc))->field = val;		\
	else								\
		((Elf64_Rela *)reloc_rel(reloc))->field = val;		\
})

static inline u64 reloc_offset(struct reloc *reloc)
{
	return __get_reloc_field(reloc, r_offset);
}

static inline void set_reloc_offset(struct elf *elf, struct reloc *reloc, u64 offset)
{
	__set_reloc_field(reloc, r_offset, offset);
	mark_sec_changed(elf, reloc->sec, true);
}

static inline s64 reloc_addend(struct reloc *reloc)
{
	return __get_reloc_field(reloc, r_addend);
}

static inline void set_reloc_addend(struct elf *elf, struct reloc *reloc, s64 addend)
{
	__set_reloc_field(reloc, r_addend, addend);
	mark_sec_changed(elf, reloc->sec, true);
}


static inline unsigned int reloc_sym(struct reloc *reloc)
{
	u64 info = __get_reloc_field(reloc, r_info);

	return is_32bit_reloc(reloc) ?
		ELF32_R_SYM(info) :
		ELF64_R_SYM(info);
}

static inline unsigned int reloc_type(struct reloc *reloc)
{
	u64 info = __get_reloc_field(reloc, r_info);

	return is_32bit_reloc(reloc) ?
		ELF32_R_TYPE(info) :
		ELF64_R_TYPE(info);
}

static inline void set_reloc_sym(struct elf *elf, struct reloc *reloc, unsigned int sym)
{
	u64 info = is_32bit_reloc(reloc) ?
		ELF32_R_INFO(sym, reloc_type(reloc)) :
		ELF64_R_INFO(sym, reloc_type(reloc));

	__set_reloc_field(reloc, r_info, info);

	mark_sec_changed(elf, reloc->sec, true);
}
static inline void set_reloc_type(struct elf *elf, struct reloc *reloc, unsigned int type)
{
	u64 info = is_32bit_reloc(reloc) ?
		ELF32_R_INFO(reloc_sym(reloc), type) :
		ELF64_R_INFO(reloc_sym(reloc), type);

	__set_reloc_field(reloc, r_info, info);

	mark_sec_changed(elf, reloc->sec, true);
}

#define for_each_sec(file, sec)						\
	list_for_each_entry(sec, &file->elf->sections, list)

#define sec_for_each_sym(sec, sym)					\
	list_for_each_entry(sym, &sec->symbol_list, list)

#define for_each_sym(file, sym)						\
	for (struct section *__sec, *__fake = (struct section *)1;	\
	     __fake; __fake = NULL)					\
		for_each_sec(file, __sec)				\
			sec_for_each_sym(__sec, sym)

#define for_each_reloc(rsec, reloc)					\
	for (int __i = 0, __fake = 1; __fake; __fake = 0)		\
		for (reloc = rsec->relocs;				\
		     __i < sec_num_entries(rsec);			\
		     __i++, reloc++)

#define for_each_reloc_from(rsec, reloc)				\
	for (int __i = reloc_idx(reloc);				\
	     __i < sec_num_entries(rsec);				\
	     __i++, reloc++)

#define OFFSET_STRIDE_BITS	4
#define OFFSET_STRIDE		(1UL << OFFSET_STRIDE_BITS)
#define OFFSET_STRIDE_MASK	(~(OFFSET_STRIDE - 1))

#define for_offset_range(_offset, _start, _end)			\
	for (_offset = ((_start) & OFFSET_STRIDE_MASK);		\
	     _offset >= ((_start) & OFFSET_STRIDE_MASK) &&	\
	     _offset <= ((_end) & OFFSET_STRIDE_MASK);		\
	     _offset += OFFSET_STRIDE)

static inline u32 sec_offset_hash(struct section *sec, unsigned long offset)
{
	u32 ol, oh, idx = sec->idx;

	offset &= OFFSET_STRIDE_MASK;

	ol = offset;
	oh = (offset >> 16) >> 16;

	__jhash_mix(ol, oh, idx);

	return ol;
}

static inline u32 reloc_hash(struct reloc *reloc)
{
	return sec_offset_hash(reloc->sec, reloc_offset(reloc));
}

#endif /* _OBJTOOL_ELF_H */