// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> */ #define _GNU_SOURCE /* memmem() */#include <fnmatch.h>#include <string.h>#include <stdlib.h>#include <inttypes.h>#include <sys/mman.h> #include <objtool/builtin.h>#include <objtool/cfi.h>#include <objtool/arch.h>#include <objtool/disas.h>#include <objtool/check.h>#include <objtool/special.h>#include <objtool/trace.h>#include <objtool/warn.h>#include <objtool/util.h> #include <linux/objtool_types.h>#include <linux/hashtable.h>#include <linux/kernel.h>#include <linux/static_call_types.h>#include <linux/string.h> static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache; static struct cfi_init_state initial_func_cfi;static struct cfi_state init_cfi;static struct cfi_state func_cfi;static struct cfi_state force_undefined_cfi; struct disas_context *objtool_disas_ctx; size_t sym_name_max_len; struct instruction *find_insn(struct objtool_file *file, struct section *sec, unsigned long offset){ struct instruction *insn; hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) { if (insn->sec == sec && insn->offset == offset) return insn; } return NULL;} struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn){ if (insn->idx == INSN_CHUNK_MAX) return find_insn(file, insn->sec, insn->offset + insn->len); insn++; if (!insn->len) return NULL; return insn;} struct instruction *next_insn_same_func(struct objtool_file *file, struct instruction *insn){ struct instruction *next = next_insn_same_sec(file, insn); struct symbol *func = insn_func(insn); if (!func) return NULL; if (next && insn_func(next) == func) return next; /* Check if we're already in the subfunction: */ if (func == func->cfunc) return NULL; /* Move to the subfunction: */ return find_insn(file, func->cfunc->sec, func->cfunc->offset);} static struct instruction *prev_insn_same_sec(struct objtool_file *file, struct instruction *insn){ if (insn->idx == 0) { if (insn->prev_len) return find_insn(file, insn->sec, insn->offset - insn->prev_len); return NULL; } return insn - 1;} static struct instruction *prev_insn_same_sym(struct objtool_file *file, struct instruction *insn){ struct instruction *prev = prev_insn_same_sec(file, insn); if (prev && insn_func(prev) == insn_func(insn)) return prev; return NULL;} #define for_each_insn(file, insn) \ for (struct section *__sec, *__fake = (struct section *)1; \ __fake; __fake = NULL) \ for_each_sec(file->elf, __sec) \ sec_for_each_insn(file, __sec, insn) #define sym_for_each_insn(file, sym, insn) \ for (insn = find_insn(file, sym->sec, sym->offset); \ insn && insn->offset < sym->offset + sym->len; \ insn = next_insn_same_sec(file, insn)) #define sym_for_each_insn_continue_reverse(file, sym, insn) \ for (insn = prev_insn_same_sec(file, insn); \ insn && insn->offset >= sym->offset; \ insn = prev_insn_same_sec(file, insn)) #define sec_for_each_insn_from(file, insn) \ for (; insn; insn = next_insn_same_sec(file, insn)) #define sec_for_each_insn_continue(file, insn) \ for (insn = next_insn_same_sec(file, insn); insn; \ insn = next_insn_same_sec(file, insn)) static inline struct reloc *insn_jump_table(struct instruction *insn){ if (insn->type == INSN_JUMP_DYNAMIC || insn->type == INSN_CALL_DYNAMIC) return insn->_jump_table; return NULL;} static inline unsigned long insn_jump_table_size(struct instruction *insn){ if (insn->type == INSN_JUMP_DYNAMIC || insn->type == INSN_CALL_DYNAMIC) return insn->_jump_table_size; return 0;} static bool is_jump_table_jump(struct instruction *insn){ struct alt_group *alt_group = insn->alt_group; if (insn_jump_table(insn)) return true; /* Retpoline alternative for a jump table? */ return alt_group && alt_group->orig_group && insn_jump_table(alt_group->orig_group->first_insn);} static bool is_sibling_call(struct instruction *insn){ /* * Assume only STT_FUNC calls have jump-tables. */ if (insn_func(insn)) { /* An indirect jump is either a sibling call or a jump to a table. */ if (insn->type == INSN_JUMP_DYNAMIC) return !is_jump_table_jump(insn); } /* add_jump_destinations() sets insn_call_dest(insn) for sibling calls. */ return (is_static_jump(insn) && insn_call_dest(insn));} /* * Checks if a function is a Rust "noreturn" one. */static bool is_rust_noreturn(const struct symbol *func){ /* * If it does not start with "_R", then it is not a Rust symbol. */ if (strncmp(func->name, "_R", 2)) return false; /* * These are just heuristics -- we do not control the precise symbol * name, due to the crate disambiguators (which depend on the compiler) * as well as changes to the source code itself between versions (since * these come from the Rust standard library). */ return str_ends_with(func->name, "_4core3num20from_str_radix_panic") || str_ends_with(func->name, "_4core3num22from_ascii_radix_panic") || str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail") || str_ends_with(func->name, "_4core6option13expect_failed") || str_ends_with(func->name, "_4core6option13unwrap_failed") || str_ends_with(func->name, "_4core6result13unwrap_failed") || str_ends_with(func->name, "_4core9panicking5panic") || str_ends_with(func->name, "_4core9panicking9panic_fmt") || str_ends_with(func->name, "_4core9panicking14panic_explicit") || str_ends_with(func->name, "_4core9panicking14panic_nounwind") || str_ends_with(func->name, "_4core9panicking18panic_bounds_check") || str_ends_with(func->name, "_4core9panicking18panic_nounwind_fmt") || str_ends_with(func->name, "_4core9panicking19assert_failed_inner") || str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference") || str_ends_with(func->name, "_4core9panicking32panic_null_reference_constructed") || str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference") || str_ends_with(func->name, "_7___rustc17rust_begin_unwind") || strstr(func->name, "_4core9panicking13assert_failed") || strstr(func->name, "_4core9panicking11panic_const24panic_const_") || (strstr(func->name, "_4core5slice5index") && strstr(func->name, "slice_") && str_ends_with(func->name, "_fail"));} /* * This checks to see if the given function is a "noreturn" function. * * For global functions which are outside the scope of this object file, we * have to keep a manual list of them. * * For local functions, we have to detect them manually by simply looking for * the lack of a return instruction. */static bool __dead_end_function(struct objtool_file *file, struct symbol *func, int recursion){ int i; struct instruction *insn; bool empty = true; #define NORETURN(func) __stringify(func), static const char * const global_noreturns[] = {#include "noreturns.h" };#undef NORETURN if (!func) return false; if (!is_local_sym(func)) { if (is_rust_noreturn(func)) return true; for (i = 0; i < ARRAY_SIZE(global_noreturns); i++) if (!strcmp(func->name, global_noreturns[i])) return true; } if (is_weak_sym(func)) return false; if (!func->len) return false; insn = find_insn(file, func->sec, func->offset); if (!insn || !insn_func(insn)) return false; func_for_each_insn(file, func, insn) { empty = false; if (insn->type == INSN_RETURN) return false; } if (empty) return false; /* * A function can have a sibling call instead of a return. In that * case, the function's dead-end status depends on whether the target * of the sibling call returns. */ func_for_each_insn(file, func, insn) { if (is_sibling_call(insn)) { struct instruction *dest = insn->jump_dest; if (!dest) /* sibling call to another file */ return false; /* local sibling call */ if (recursion == 5) { /* * Infinite recursion: two functions have * sibling calls to each other. This is a very * rare case. It means they aren't dead ends. */ return false; } return __dead_end_function(file, insn_func(dest), recursion+1); } } return true;} static bool dead_end_function(struct objtool_file *file, struct symbol *func){ return __dead_end_function(file, func, 0);} static void init_cfi_state(struct cfi_state *cfi){ int i; for (i = 0; i < CFI_NUM_REGS; i++) { cfi->regs[i].base = CFI_UNDEFINED; cfi->vals[i].base = CFI_UNDEFINED; } cfi->cfa.base = CFI_UNDEFINED; cfi->drap_reg = CFI_UNDEFINED; cfi->drap_offset = -1;} static void init_insn_state(struct objtool_file *file, struct insn_state *state, struct section *sec){ memset(state, 0, sizeof(*state)); init_cfi_state(&state->cfi); if (opts.noinstr && sec) state->noinstr = sec->noinstr;} static struct cfi_state *cfi_alloc(void){ struct cfi_state *cfi = calloc(1, sizeof(struct cfi_state)); if (!cfi) { ERROR_GLIBC("calloc"); exit(1); } nr_cfi++; return cfi;} static int cfi_bits;static struct hlist_head *cfi_hash; static inline bool cficmp(struct cfi_state *cfi1, struct cfi_state *cfi2){ return memcmp((void *)cfi1 + sizeof(cfi1->hash), (void *)cfi2 + sizeof(cfi2->hash), sizeof(struct cfi_state) - sizeof(struct hlist_node));} static inline u32 cfi_key(struct cfi_state *cfi){ return jhash((void *)cfi + sizeof(cfi->hash), sizeof(*cfi) - sizeof(cfi->hash), 0);} static struct cfi_state *cfi_hash_find_or_add(struct cfi_state *cfi){ struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)]; struct cfi_state *obj; hlist_for_each_entry(obj, head, hash) { if (!cficmp(cfi, obj)) { nr_cfi_cache++; return obj; } } obj = cfi_alloc(); *obj = *cfi; hlist_add_head(&obj->hash, head); return obj;} static void cfi_hash_add(struct cfi_state *cfi){ struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)]; hlist_add_head(&cfi->hash, head);} static void *cfi_hash_alloc(unsigned long size){ cfi_bits = max(10, ilog2(size)); cfi_hash = mmap(NULL, sizeof(struct hlist_head) << cfi_bits, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (cfi_hash == (void *)-1L) { ERROR_GLIBC("mmap fail cfi_hash"); cfi_hash = NULL; } else if (opts.stats) { printf("cfi_bits: %d\n", cfi_bits); } return cfi_hash;} static unsigned long nr_insns;static unsigned long nr_insns_visited; /* * Call the arch-specific instruction decoder for all the instructions and add * them to the global instruction list. */static int decode_instructions(struct objtool_file *file){ struct section *sec; struct symbol *func; unsigned long offset; struct instruction *insn; for_each_sec(file->elf, sec) { struct instruction *insns = NULL; u8 prev_len = 0; u8 idx = 0; if (!is_text_sec(sec)) continue; if (strcmp(sec->name, ".altinstr_replacement") && strcmp(sec->name, ".altinstr_aux") && strncmp(sec->name, ".discard.", 9)) sec->text = true; if (!strcmp(sec->name, ".noinstr.text") || !strcmp(sec->name, ".entry.text") || !strcmp(sec->name, ".cpuidle.text") || !strncmp(sec->name, ".text..__x86.", 13)) sec->noinstr = true; /* * .init.text code is ran before userspace and thus doesn't * strictly need retpolines, except for modules which are * loaded late, they very much do need retpoline in their * .init.text */ if (!strcmp(sec->name, ".init.text") && !opts.module) sec->init = true; for (offset = 0; offset < sec_size(sec); offset += insn->len) { if (!insns || idx == INSN_CHUNK_MAX) { insns = calloc(INSN_CHUNK_SIZE, sizeof(*insn)); if (!insns) { ERROR_GLIBC("calloc"); return -1; } idx = 0; } else { idx++; } insn = &insns[idx]; insn->idx = idx; INIT_LIST_HEAD(&insn->call_node); insn->sec = sec; insn->offset = offset; insn->prev_len = prev_len; if (arch_decode_instruction(file, sec, offset, sec_size(sec) - offset, insn)) return -1; prev_len = insn->len; /* * By default, "ud2" is a dead end unless otherwise * annotated, because GCC 7 inserts it for certain * divide-by-zero cases. */ if (insn->type == INSN_BUG) insn->dead_end = true; hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset)); nr_insns++; } sec_for_each_sym(sec, func) { if (!is_notype_sym(func) && !is_func_sym(func)) continue; if (func->offset == sec_size(sec)) { /* Heuristic: likely an "end" symbol */ if (is_notype_sym(func)) continue; ERROR("%s(): STT_FUNC at end of section", func->name); return -1; } if (func->embedded_insn || is_alias_sym(func)) continue; if (!find_insn(file, sec, func->offset)) { ERROR("%s(): can't find starting instruction", func->name); return -1; } sym_for_each_insn(file, func, insn) { insn->_sym = func; if (is_func_sym(func) &&Lines 1–500 of 4971. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com> */ #define _GNU_SOURCE /* memmem() */ #include <fnmatch.h> #include <string.h> #include <stdlib.h> #include <inttypes.h> #include <sys/mman.h> #include <objtool/builtin.h> #include <objtool/cfi.h> #include <objtool/arch.h> #include <objtool/disas.h> #include <objtool/check.h> #include <objtool/special.h> #include <objtool/trace.h> #include <objtool/warn.h> #include <objtool/util.h> #include <linux/objtool_types.h> #include <linux/hashtable.h> #include <linux/kernel.h> #include <linux/static_call_types.h> #include <linux/string.h> static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache; static struct cfi_init_state initial_func_cfi; static struct cfi_state init_cfi; static struct cfi_state func_cfi; static struct cfi_state force_undefined_cfi; struct disas_context *objtool_disas_ctx; size_t sym_name_max_len; struct instruction *find_insn(struct objtool_file *file, struct section *sec, unsigned long offset) { struct instruction *insn; hash_for_each_possible(file->insn_hash, insn, hash, sec_offset_hash(sec, offset)) { if (insn->sec == sec && insn->offset == offset) return insn; } return NULL; } struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn) { if (insn->idx == INSN_CHUNK_MAX) return find_insn(file, insn->sec, insn->offset + insn->len); insn++; if (!insn->len) return NULL; return insn; } struct instruction *next_insn_same_func(struct objtool_file *file, struct instruction *insn) { struct instruction *next = next_insn_same_sec(file, insn); struct symbol *func = insn_func(insn); if (!func) return NULL; if (next && insn_func(next) == func) return next; /* Check if we're already in the subfunction: */ if (func == func->cfunc) return NULL; /* Move to the subfunction: */ return find_insn(file, func->cfunc->sec, func->cfunc->offset); } static struct instruction *prev_insn_same_sec(struct objtool_file *file, struct instruction *insn) { if (insn->idx == 0) { if (insn->prev_len) return find_insn(file, insn->sec, insn->offset - insn->prev_len); return NULL; } return insn - 1; } static struct instruction *prev_insn_same_sym(struct objtool_file *file, struct instruction *insn) { struct instruction *prev = prev_insn_same_sec(file, insn); if (prev && insn_func(prev) == insn_func(insn)) return prev; return NULL; } #define for_each_insn(file, insn) \ for (struct section *__sec, *__fake = (struct section *)1; \ __fake; __fake = NULL) \ for_each_sec(file->elf, __sec) \ sec_for_each_insn(file, __sec, insn) #define sym_for_each_insn(file, sym, insn) \ for (insn = find_insn(file, sym->sec, sym->offset); \ insn && insn->offset < sym->offset + sym->len; \ insn = next_insn_same_sec(file, insn)) #define sym_for_each_insn_continue_reverse(file, sym, insn) \ for (insn = prev_insn_same_sec(file, insn); \ insn && insn->offset >= sym->offset; \ insn = prev_insn_same_sec(file, insn)) #define sec_for_each_insn_from(file, insn) \ for (; insn; insn = next_insn_same_sec(file, insn)) #define sec_for_each_insn_continue(file, insn) \ for (insn = next_insn_same_sec(file, insn); insn; \ insn = next_insn_same_sec(file, insn)) static inline struct reloc *insn_jump_table(struct instruction *insn) { if (insn->type == INSN_JUMP_DYNAMIC || insn->type == INSN_CALL_DYNAMIC) return insn->_jump_table; return NULL; } static inline unsigned long insn_jump_table_size(struct instruction *insn) { if (insn->type == INSN_JUMP_DYNAMIC || insn->type == INSN_CALL_DYNAMIC) return insn->_jump_table_size; return 0; } static bool is_jump_table_jump(struct instruction *insn) { struct alt_group *alt_group = insn->alt_group; if (insn_jump_table(insn)) return true; /* Retpoline alternative for a jump table? */ return alt_group && alt_group->orig_group && insn_jump_table(alt_group->orig_group->first_insn); } static bool is_sibling_call(struct instruction *insn) { /* * Assume only STT_FUNC calls have jump-tables. */ if (insn_func(insn)) { /* An indirect jump is either a sibling call or a jump to a table. */ if (insn->type == INSN_JUMP_DYNAMIC) return !is_jump_table_jump(insn); } /* add_jump_destinations() sets insn_call_dest(insn) for sibling calls. */ return (is_static_jump(insn) && insn_call_dest(insn)); } /* * Checks if a function is a Rust "noreturn" one. */ static bool is_rust_noreturn(const struct symbol *func) { /* * If it does not start with "_R", then it is not a Rust symbol. */ if (strncmp(func->name, "_R", 2)) return false; /* * These are just heuristics -- we do not control the precise symbol * name, due to the crate disambiguators (which depend on the compiler) * as well as changes to the source code itself between versions (since * these come from the Rust standard library). */ return str_ends_with(func->name, "_4core3num20from_str_radix_panic") || str_ends_with(func->name, "_4core3num22from_ascii_radix_panic") || str_ends_with(func->name, "_4core5sliceSp15copy_from_slice17len_mismatch_fail") || str_ends_with(func->name, "_4core6option13expect_failed") || str_ends_with(func->name, "_4core6option13unwrap_failed") || str_ends_with(func->name, "_4core6result13unwrap_failed") || str_ends_with(func->name, "_4core9panicking5panic") || str_ends_with(func->name, "_4core9panicking9panic_fmt") || str_ends_with(func->name, "_4core9panicking14panic_explicit") || str_ends_with(func->name, "_4core9panicking14panic_nounwind") || str_ends_with(func->name, "_4core9panicking18panic_bounds_check") || str_ends_with(func->name, "_4core9panicking18panic_nounwind_fmt") || str_ends_with(func->name, "_4core9panicking19assert_failed_inner") || str_ends_with(func->name, "_4core9panicking30panic_null_pointer_dereference") || str_ends_with(func->name, "_4core9panicking32panic_null_reference_constructed") || str_ends_with(func->name, "_4core9panicking36panic_misaligned_pointer_dereference") || str_ends_with(func->name, "_7___rustc17rust_begin_unwind") || strstr(func->name, "_4core9panicking13assert_failed") || strstr(func->name, "_4core9panicking11panic_const24panic_const_") || (strstr(func->name, "_4core5slice5index") && strstr(func->name, "slice_") && str_ends_with(func->name, "_fail")); } /* * This checks to see if the given function is a "noreturn" function. * * For global functions which are outside the scope of this object file, we * have to keep a manual list of them. * * For local functions, we have to detect them manually by simply looking for * the lack of a return instruction. */ static bool __dead_end_function(struct objtool_file *file, struct symbol *func, int recursion) { int i; struct instruction *insn; bool empty = true; #define NORETURN(func) __stringify(func), static const char * const global_noreturns[] = { #include "noreturns.h" }; #undef NORETURN if (!func) return false; if (!is_local_sym(func)) { if (is_rust_noreturn(func)) return true; for (i = 0; i < ARRAY_SIZE(global_noreturns); i++) if (!strcmp(func->name, global_noreturns[i])) return true; } if (is_weak_sym(func)) return false; if (!func->len) return false; insn = find_insn(file, func->sec, func->offset); if (!insn || !insn_func(insn)) return false; func_for_each_insn(file, func, insn) { empty = false; if (insn->type == INSN_RETURN) return false; } if (empty) return false; /* * A function can have a sibling call instead of a return. In that * case, the function's dead-end status depends on whether the target * of the sibling call returns. */ func_for_each_insn(file, func, insn) { if (is_sibling_call(insn)) { struct instruction *dest = insn->jump_dest; if (!dest) /* sibling call to another file */ return false; /* local sibling call */ if (recursion == 5) { /* * Infinite recursion: two functions have * sibling calls to each other. This is a very * rare case. It means they aren't dead ends. */ return false; } return __dead_end_function(file, insn_func(dest), recursion+1); } } return true; } static bool dead_end_function(struct objtool_file *file, struct symbol *func) { return __dead_end_function(file, func, 0); } static void init_cfi_state(struct cfi_state *cfi) { int i; for (i = 0; i < CFI_NUM_REGS; i++) { cfi->regs[i].base = CFI_UNDEFINED; cfi->vals[i].base = CFI_UNDEFINED; } cfi->cfa.base = CFI_UNDEFINED; cfi->drap_reg = CFI_UNDEFINED; cfi->drap_offset = -1; } static void init_insn_state(struct objtool_file *file, struct insn_state *state, struct section *sec) { memset(state, 0, sizeof(*state)); init_cfi_state(&state->cfi); if (opts.noinstr && sec) state->noinstr = sec->noinstr; } static struct cfi_state *cfi_alloc(void) { struct cfi_state *cfi = calloc(1, sizeof(struct cfi_state)); if (!cfi) { ERROR_GLIBC("calloc"); exit(1); } nr_cfi++; return cfi; } static int cfi_bits; static struct hlist_head *cfi_hash; static inline bool cficmp(struct cfi_state *cfi1, struct cfi_state *cfi2) { return memcmp((void *)cfi1 + sizeof(cfi1->hash), (void *)cfi2 + sizeof(cfi2->hash), sizeof(struct cfi_state) - sizeof(struct hlist_node)); } static inline u32 cfi_key(struct cfi_state *cfi) { return jhash((void *)cfi + sizeof(cfi->hash), sizeof(*cfi) - sizeof(cfi->hash), 0); } static struct cfi_state *cfi_hash_find_or_add(struct cfi_state *cfi) { struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)]; struct cfi_state *obj; hlist_for_each_entry(obj, head, hash) { if (!cficmp(cfi, obj)) { nr_cfi_cache++; return obj; } } obj = cfi_alloc(); *obj = *cfi; hlist_add_head(&obj->hash, head); return obj; } static void cfi_hash_add(struct cfi_state *cfi) { struct hlist_head *head = &cfi_hash[hash_min(cfi_key(cfi), cfi_bits)]; hlist_add_head(&cfi->hash, head); } static void *cfi_hash_alloc(unsigned long size) { cfi_bits = max(10, ilog2(size)); cfi_hash = mmap(NULL, sizeof(struct hlist_head) << cfi_bits, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (cfi_hash == (void *)-1L) { ERROR_GLIBC("mmap fail cfi_hash"); cfi_hash = NULL; } else if (opts.stats) { printf("cfi_bits: %d\n", cfi_bits); } return cfi_hash; } static unsigned long nr_insns; static unsigned long nr_insns_visited; /* * Call the arch-specific instruction decoder for all the instructions and add * them to the global instruction list. */ static int decode_instructions(struct objtool_file *file) { struct section *sec; struct symbol *func; unsigned long offset; struct instruction *insn; for_each_sec(file->elf, sec) { struct instruction *insns = NULL; u8 prev_len = 0; u8 idx = 0; if (!is_text_sec(sec)) continue; if (strcmp(sec->name, ".altinstr_replacement") && strcmp(sec->name, ".altinstr_aux") && strncmp(sec->name, ".discard.", 9)) sec->text = true; if (!strcmp(sec->name, ".noinstr.text") || !strcmp(sec->name, ".entry.text") || !strcmp(sec->name, ".cpuidle.text") || !strncmp(sec->name, ".text..__x86.", 13)) sec->noinstr = true; /* * .init.text code is ran before userspace and thus doesn't * strictly need retpolines, except for modules which are * loaded late, they very much do need retpoline in their * .init.text */ if (!strcmp(sec->name, ".init.text") && !opts.module) sec->init = true; for (offset = 0; offset < sec_size(sec); offset += insn->len) { if (!insns || idx == INSN_CHUNK_MAX) { insns = calloc(INSN_CHUNK_SIZE, sizeof(*insn)); if (!insns) { ERROR_GLIBC("calloc"); return -1; } idx = 0; } else { idx++; } insn = &insns[idx]; insn->idx = idx; INIT_LIST_HEAD(&insn->call_node); insn->sec = sec; insn->offset = offset; insn->prev_len = prev_len; if (arch_decode_instruction(file, sec, offset, sec_size(sec) - offset, insn)) return -1; prev_len = insn->len; /* * By default, "ud2" is a dead end unless otherwise * annotated, because GCC 7 inserts it for certain * divide-by-zero cases. */ if (insn->type == INSN_BUG) insn->dead_end = true; hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset)); nr_insns++; } sec_for_each_sym(sec, func) { if (!is_notype_sym(func) && !is_func_sym(func)) continue; if (func->offset == sec_size(sec)) { /* Heuristic: likely an "end" symbol */ if (is_notype_sym(func)) continue; ERROR("%s(): STT_FUNC at end of section", func->name); return -1; } if (func->embedded_insn || is_alias_sym(func)) continue; if (!find_insn(file, sec, func->offset)) { ERROR("%s(): can't find starting instruction", func->name); return -1; } sym_for_each_insn(file, func, insn) { insn->_sym = func; if (is_func_sym(func) &&