1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
11 #include <linux/list.h>
12 #include <linux/hashtable.h>
13 #include <linux/rbtree.h>
14 #include <linux/jhash.h>
17 #ifdef LIBELF_USE_DEPRECATED
18 # define elf_getshdrnum elf_getshnum
19 # define elf_getshdrstrndx elf_getshstrndx
23 * Fallback for systems without this "read, mmaping if possible" cmd.
25 #ifndef ELF_C_READ_MMAP
26 #define ELF_C_READ_MMAP ELF_C_READ
29 struct elf_hash_node {
30 struct elf_hash_node *next;
34 struct list_head list;
35 struct elf_hash_node hash;
36 struct elf_hash_node name_hash;
38 struct rb_root_cached symbol_tree;
39 struct list_head symbol_list;
40 struct section *base, *rsec;
45 bool _changed, text, rodata, noinstr, init, truncate;
50 struct list_head list;
52 struct elf_hash_node hash;
53 struct elf_hash_node name_hash;
57 unsigned int idx, len;
59 unsigned long __subtree_last;
60 struct symbol *pfunc, *cfunc, *alias;
61 unsigned char bind, type;
63 u8 static_call_tramp : 1;
64 u8 retpoline_thunk : 1;
67 u8 profiling_func : 1;
70 struct list_head pv_target;
75 struct elf_hash_node hash;
78 struct reloc *sym_next_reloc;
87 unsigned int num_files;
88 struct list_head sections;
89 unsigned long num_relocs;
94 int section_name_bits;
97 struct elf_hash_node **symbol_hash;
98 struct elf_hash_node **symbol_name_hash;
99 struct elf_hash_node **section_hash;
100 struct elf_hash_node **section_name_hash;
101 struct elf_hash_node **reloc_hash;
103 struct section *section_data;
104 struct symbol *symbol_data;
107 struct elf *elf_open_read(const char *name, int flags);
109 struct section *elf_create_section(struct elf *elf, const char *name,
110 size_t entsize, unsigned int nr);
111 struct section *elf_create_section_pair(struct elf *elf, const char *name,
112 size_t entsize, unsigned int nr,
113 unsigned int reloc_nr);
115 struct symbol *elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, long size);
117 struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,
118 unsigned long offset,
119 unsigned int reloc_idx,
120 struct section *insn_sec,
121 unsigned long insn_off);
123 struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
124 unsigned long offset,
125 unsigned int reloc_idx,
129 int elf_write_insn(struct elf *elf, struct section *sec,
130 unsigned long offset, unsigned int len,
132 int elf_write(struct elf *elf);
133 void elf_close(struct elf *elf);
135 struct section *find_section_by_name(const struct elf *elf, const char *name);
136 struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
137 struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
138 struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
139 struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
140 int find_symbol_hole_containing(const struct section *sec, unsigned long offset);
141 struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
142 struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
143 unsigned long offset, unsigned int len);
144 struct symbol *find_func_containing(struct section *sec, unsigned long offset);
147 * Try to see if it's a whole archive (vmlinux.o or module).
149 * Note this will miss the case where a module only has one source file.
151 static inline bool has_multiple_files(struct elf *elf)
153 return elf->num_files > 1;
156 static inline size_t elf_addr_size(struct elf *elf)
158 return elf->ehdr.e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
161 static inline size_t elf_rela_size(struct elf *elf)
163 return elf_addr_size(elf) == 4 ? sizeof(Elf32_Rela) : sizeof(Elf64_Rela);
166 static inline unsigned int elf_data_rela_type(struct elf *elf)
168 return elf_addr_size(elf) == 4 ? R_DATA32 : R_DATA64;
171 static inline unsigned int elf_text_rela_type(struct elf *elf)
173 return elf_addr_size(elf) == 4 ? R_TEXT32 : R_TEXT64;
176 static inline bool is_reloc_sec(struct section *sec)
178 return sec->sh.sh_type == SHT_RELA || sec->sh.sh_type == SHT_REL;
181 static inline bool sec_changed(struct section *sec)
183 return sec->_changed;
186 static inline void mark_sec_changed(struct elf *elf, struct section *sec,
189 sec->_changed = changed;
190 elf->changed |= changed;
193 static inline unsigned int sec_num_entries(struct section *sec)
195 return sec->sh.sh_size / sec->sh.sh_entsize;
198 static inline unsigned int reloc_idx(struct reloc *reloc)
200 return reloc - reloc->sec->relocs;
203 static inline void *reloc_rel(struct reloc *reloc)
205 struct section *rsec = reloc->sec;
207 return rsec->data->d_buf + (reloc_idx(reloc) * rsec->sh.sh_entsize);
210 static inline bool is_32bit_reloc(struct reloc *reloc)
214 * Elf32_Rela: 12 bytes
215 * Elf64_Rel: 16 bytes
216 * Elf64_Rela: 24 bytes
218 return reloc->sec->sh.sh_entsize < 16;
221 #define __get_reloc_field(reloc, field) \
223 is_32bit_reloc(reloc) ? \
224 ((Elf32_Rela *)reloc_rel(reloc))->field : \
225 ((Elf64_Rela *)reloc_rel(reloc))->field; \
228 #define __set_reloc_field(reloc, field, val) \
230 if (is_32bit_reloc(reloc)) \
231 ((Elf32_Rela *)reloc_rel(reloc))->field = val; \
233 ((Elf64_Rela *)reloc_rel(reloc))->field = val; \
236 static inline u64 reloc_offset(struct reloc *reloc)
238 return __get_reloc_field(reloc, r_offset);
241 static inline void set_reloc_offset(struct elf *elf, struct reloc *reloc, u64 offset)
243 __set_reloc_field(reloc, r_offset, offset);
244 mark_sec_changed(elf, reloc->sec, true);
247 static inline s64 reloc_addend(struct reloc *reloc)
249 return __get_reloc_field(reloc, r_addend);
252 static inline void set_reloc_addend(struct elf *elf, struct reloc *reloc, s64 addend)
254 __set_reloc_field(reloc, r_addend, addend);
255 mark_sec_changed(elf, reloc->sec, true);
259 static inline unsigned int reloc_sym(struct reloc *reloc)
261 u64 info = __get_reloc_field(reloc, r_info);
263 return is_32bit_reloc(reloc) ?
268 static inline unsigned int reloc_type(struct reloc *reloc)
270 u64 info = __get_reloc_field(reloc, r_info);
272 return is_32bit_reloc(reloc) ?
277 static inline void set_reloc_sym(struct elf *elf, struct reloc *reloc, unsigned int sym)
279 u64 info = is_32bit_reloc(reloc) ?
280 ELF32_R_INFO(sym, reloc_type(reloc)) :
281 ELF64_R_INFO(sym, reloc_type(reloc));
283 __set_reloc_field(reloc, r_info, info);
285 mark_sec_changed(elf, reloc->sec, true);
287 static inline void set_reloc_type(struct elf *elf, struct reloc *reloc, unsigned int type)
289 u64 info = is_32bit_reloc(reloc) ?
290 ELF32_R_INFO(reloc_sym(reloc), type) :
291 ELF64_R_INFO(reloc_sym(reloc), type);
293 __set_reloc_field(reloc, r_info, info);
295 mark_sec_changed(elf, reloc->sec, true);
298 #define for_each_sec(file, sec) \
299 list_for_each_entry(sec, &file->elf->sections, list)
301 #define sec_for_each_sym(sec, sym) \
302 list_for_each_entry(sym, &sec->symbol_list, list)
304 #define for_each_sym(file, sym) \
305 for (struct section *__sec, *__fake = (struct section *)1; \
306 __fake; __fake = NULL) \
307 for_each_sec(file, __sec) \
308 sec_for_each_sym(__sec, sym)
310 #define for_each_reloc(rsec, reloc) \
311 for (int __i = 0, __fake = 1; __fake; __fake = 0) \
312 for (reloc = rsec->relocs; \
313 __i < sec_num_entries(rsec); \
316 #define for_each_reloc_from(rsec, reloc) \
317 for (int __i = reloc_idx(reloc); \
318 __i < sec_num_entries(rsec); \
321 #define OFFSET_STRIDE_BITS 4
322 #define OFFSET_STRIDE (1UL << OFFSET_STRIDE_BITS)
323 #define OFFSET_STRIDE_MASK (~(OFFSET_STRIDE - 1))
325 #define for_offset_range(_offset, _start, _end) \
326 for (_offset = ((_start) & OFFSET_STRIDE_MASK); \
327 _offset >= ((_start) & OFFSET_STRIDE_MASK) && \
328 _offset <= ((_end) & OFFSET_STRIDE_MASK); \
329 _offset += OFFSET_STRIDE)
331 static inline u32 sec_offset_hash(struct section *sec, unsigned long offset)
333 u32 ol, oh, idx = sec->idx;
335 offset &= OFFSET_STRIDE_MASK;
338 oh = (offset >> 16) >> 16;
340 __jhash_mix(ol, oh, idx);
345 static inline u32 reloc_hash(struct reloc *reloc)
347 return sec_offset_hash(reloc->sec, reloc_offset(reloc));
350 #endif /* _OBJTOOL_ELF_H */