Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  check.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
 */


#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/check.h>
#include <objtool/special.h>
#include <objtool/warn.h>
#include <objtool/endianness.h>

#include <linux/objtool_types.h>
#include <linux/hashtable.h>
#include <linux/kernel.h>
#include <linux/static_call_types.h>
#include <linux/string.h>

struct alternative {
 struct alternative *next;
 struct instruction *insn;
};

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 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;
}

static 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, __sec)    \
   sec_for_each_insn(file, __sec, insn)

#define func_for_each_insn(file, func, insn)    \
 for (insn = find_insn(file, func->sec, func->offset);  \
      insn;       \
      insn = next_insn_same_func(file, 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 symbol *insn_call_dest(struct instruction *insn)
{
 if (insn->type == INSN_JUMP_DYNAMIC ||
     insn->type == INSN_CALL_DYNAMIC)
  return NULL;

 return insn->_call_dest;
}

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 string ends with another.
 */

static bool str_ends_with(const char *s, const char *sub)
{
 const int slen = strlen(s);
 const int sublen = strlen(sub);

 if (sublen > slen)
  return 0;

 return !memcmp(s + slen - sublen, sub, sublen);
}

/*
 * 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, "_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, "_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 (func->bind == STB_GLOBAL || func->bind == STB_WEAK) {
  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 (func->bind == STB_WEAK)
  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;
 int ret;

 for_each_sec(file, sec) {
  struct instruction *insns = NULL;
  u8 prev_len = 0;
  u8 idx = 0;

  if (!(sec->sh.sh_flags & SHF_EXECINSTR))
   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->sh.sh_size; offset += insn->len) {
   if (!insns || idx == INSN_CHUNK_MAX) {
    insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE);
    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;

   ret = arch_decode_instruction(file, sec, offset,
            sec->sh.sh_size - offset,
            insn);
   if (ret)
    return ret;

   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 (func->type != STT_NOTYPE && func->type != STT_FUNC)
    continue;

   if (func->offset == sec->sh.sh_size) {
    /* Heuristic: likely an "end" symbol */
    if (func->type == STT_NOTYPE)
     continue;
    ERROR("%s(): STT_FUNC at end of section", func->name);
    return -1;
   }

   if (func->embedded_insn || func->alias != 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 (func->type == STT_FUNC &&
        insn->type == INSN_ENDBR &&
        list_empty(&insn->call_node)) {
     if (insn->offset == func->offset) {
      list_add_tail(&insn->call_node, &file->endbr_list);
      file->nr_endbr++;
     } else {
      file->nr_endbr_int++;
     }
    }
   }
  }
 }

 if (opts.stats)
  printf("nr_insns: %lu\n", nr_insns);

 return 0;
}

/*
 * Read the pv_ops[] .data table to find the static initialized values.
 */

static int add_pv_ops(struct objtool_file *file, const char *symname)
{
 struct symbol *sym, *func;
 unsigned long off, end;
 struct reloc *reloc;
 int idx;

 sym = find_symbol_by_name(file->elf, symname);
 if (!sym)
  return 0;

 off = sym->offset;
 end = off + sym->len;
 for (;;) {
  reloc = find_reloc_by_dest_range(file->elf, sym->sec, off, end - off);
  if (!reloc)
   break;

  idx = (reloc_offset(reloc) - sym->offset) / sizeof(unsigned long);

  func = reloc->sym;
  if (func->type == STT_SECTION)
   func = find_symbol_by_offset(reloc->sym->sec,
           reloc_addend(reloc));
  if (!func) {
   ERROR_FUNC(reloc->sym->sec, reloc_addend(reloc),
       "can't find func at %s[%d]", symname, idx);
   return -1;
  }

  if (objtool_pv_add(file, idx, func))
   return -1;

  off = reloc_offset(reloc) + 1;
  if (off > end)
   break;
 }

 return 0;
}

/*
 * Allocate and initialize file->pv_ops[].
 */

static int init_pv_ops(struct objtool_file *file)
{
 static const char *pv_ops_tables[] = {
  "pv_ops",
  "xen_cpu_ops",
  "xen_irq_ops",
  "xen_mmu_ops",
  NULL,
 };
 const char *pv_ops;
 struct symbol *sym;
 int idx, nr, ret;

 if (!opts.noinstr)
  return 0;

 file->pv_ops = NULL;

 sym = find_symbol_by_name(file->elf, "pv_ops");
 if (!sym)
  return 0;

 nr = sym->len / sizeof(unsigned long);
 file->pv_ops = calloc(sizeof(struct pv_state), nr);
 if (!file->pv_ops) {
  ERROR_GLIBC("calloc");
  return -1;
 }

 for (idx = 0; idx < nr; idx++)
  INIT_LIST_HEAD(&file->pv_ops[idx].targets);

 for (idx = 0; (pv_ops = pv_ops_tables[idx]); idx++) {
  ret = add_pv_ops(file, pv_ops);
  if (ret)
   return ret;
 }

 return 0;
}

static int create_static_call_sections(struct objtool_file *file)
{
 struct static_call_site *site;
 struct section *sec;
 struct instruction *insn;
 struct symbol *key_sym;
 char *key_name, *tmp;
 int idx;

 sec = find_section_by_name(file->elf, ".static_call_sites");
 if (sec) {
  INIT_LIST_HEAD(&file->static_call_list);
  WARN("file already has .static_call_sites section, skipping");
  return 0;
 }

 if (list_empty(&file->static_call_list))
  return 0;

 idx = 0;
 list_for_each_entry(insn, &file->static_call_list, call_node)
  idx++;

 sec = elf_create_section_pair(file->elf, ".static_call_sites",
          sizeof(*site), idx, idx * 2);
 if (!sec)
  return -1;

 /* Allow modules to modify the low bits of static_call_site::key */
 sec->sh.sh_flags |= SHF_WRITE;

 idx = 0;
 list_for_each_entry(insn, &file->static_call_list, call_node) {

  /* populate reloc for 'addr' */
  if (!elf_init_reloc_text_sym(file->elf, sec,
          idx * sizeof(*site), idx * 2,
          insn->sec, insn->offset))
   return -1;

  /* find key symbol */
  key_name = strdup(insn_call_dest(insn)->name);
  if (!key_name) {
   ERROR_GLIBC("strdup");
   return -1;
  }
  if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR,
       STATIC_CALL_TRAMP_PREFIX_LEN)) {
   ERROR("static_call: trampoline name malformed: %s", key_name);
   return -1;
  }
  tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN;
  memcpy(tmp, STATIC_CALL_KEY_PREFIX_STR, STATIC_CALL_KEY_PREFIX_LEN);

  key_sym = find_symbol_by_name(file->elf, tmp);
  if (!key_sym) {
   if (!opts.module) {
    ERROR("static_call: can't find static_call_key symbol: %s", tmp);
    return -1;
   }

   /*
 * For modules(), the key might not be exported, which
 * means the module can make static calls but isn't
 * allowed to change them.
 *
 * In that case we temporarily set the key to be the
 * trampoline address.  This is fixed up in
 * static_call_add_module().
 */

   key_sym = insn_call_dest(insn);
  }

  /* populate reloc for 'key' */
  if (!elf_init_reloc_data_sym(file->elf, sec,
          idx * sizeof(*site) + 4,
          (idx * 2) + 1, key_sym,
          is_sibling_call(insn) * STATIC_CALL_SITE_TAIL))
   return -1;

  idx++;
 }

 return 0;
}

static int create_retpoline_sites_sections(struct objtool_file *file)
{
 struct instruction *insn;
 struct section *sec;
 int idx;

 sec = find_section_by_name(file->elf, ".retpoline_sites");
 if (sec) {
  WARN("file already has .retpoline_sites, skipping");
  return 0;
 }

 idx = 0;
 list_for_each_entry(insn, &file->retpoline_call_list, call_node)
  idx++;

 if (!idx)
  return 0;

 sec = elf_create_section_pair(file->elf, ".retpoline_sites",
          sizeof(int), idx, idx);
 if (!sec)
  return -1;

 idx = 0;
 list_for_each_entry(insn, &file->retpoline_call_list, call_node) {

  if (!elf_init_reloc_text_sym(file->elf, sec,
          idx * sizeof(int), idx,
          insn->sec, insn->offset))
   return -1;

  idx++;
 }

 return 0;
}

static int create_return_sites_sections(struct objtool_file *file)
{
 struct instruction *insn;
 struct section *sec;
 int idx;

 sec = find_section_by_name(file->elf, ".return_sites");
 if (sec) {
  WARN("file already has .return_sites, skipping");
  return 0;
 }

 idx = 0;
 list_for_each_entry(insn, &file->return_thunk_list, call_node)
  idx++;

 if (!idx)
  return 0;

 sec = elf_create_section_pair(file->elf, ".return_sites",
          sizeof(int), idx, idx);
 if (!sec)
  return -1;

 idx = 0;
 list_for_each_entry(insn, &file->return_thunk_list, call_node) {

  if (!elf_init_reloc_text_sym(file->elf, sec,
          idx * sizeof(int), idx,
          insn->sec, insn->offset))
   return -1;

  idx++;
 }

 return 0;
}

static int create_ibt_endbr_seal_sections(struct objtool_file *file)
{
 struct instruction *insn;
 struct section *sec;
 int idx;

 sec = find_section_by_name(file->elf, ".ibt_endbr_seal");
 if (sec) {
  WARN("file already has .ibt_endbr_seal, skipping");
  return 0;
 }

 idx = 0;
 list_for_each_entry(insn, &file->endbr_list, call_node)
  idx++;

 if (opts.stats) {
  printf("ibt: ENDBR at function start: %d\n", file->nr_endbr);
  printf("ibt: ENDBR inside functions: %d\n", file->nr_endbr_int);
  printf("ibt: superfluous ENDBR: %d\n", idx);
 }

 if (!idx)
  return 0;

 sec = elf_create_section_pair(file->elf, ".ibt_endbr_seal",
          sizeof(int), idx, idx);
 if (!sec)
  return -1;

 idx = 0;
 list_for_each_entry(insn, &file->endbr_list, call_node) {

  int *site = (int *)sec->data->d_buf + idx;
  struct symbol *sym = insn->sym;
  *site = 0;

  if (opts.module && sym && sym->type == STT_FUNC &&
      insn->offset == sym->offset &&
      (!strcmp(sym->name, "init_module") ||
       !strcmp(sym->name, "cleanup_module"))) {
   ERROR("%s(): Magic init_module() function name is deprecated, use module_init(fn) instead",
         sym->name);
   return -1;
  }

  if (!elf_init_reloc_text_sym(file->elf, sec,
          idx * sizeof(int), idx,
          insn->sec, insn->offset))
   return -1;

  idx++;
 }

 return 0;
}

static int create_cfi_sections(struct objtool_file *file)
{
 struct section *sec;
 struct symbol *sym;
 int idx;

 sec = find_section_by_name(file->elf, ".cfi_sites");
 if (sec) {
  INIT_LIST_HEAD(&file->call_list);
  WARN("file already has .cfi_sites section, skipping");
  return 0;
 }

 idx = 0;
 for_each_sym(file, sym) {
  if (sym->type != STT_FUNC)
   continue;

  if (strncmp(sym->name, "__cfi_", 6))
   continue;

  idx++;
 }

 sec = elf_create_section_pair(file->elf, ".cfi_sites",
          sizeof(unsigned int), idx, idx);
 if (!sec)
  return -1;

 idx = 0;
 for_each_sym(file, sym) {
  if (sym->type != STT_FUNC)
   continue;

  if (strncmp(sym->name, "__cfi_", 6))
   continue;

  if (!elf_init_reloc_text_sym(file->elf, sec,
          idx * sizeof(unsigned int), idx,
          sym->sec, sym->offset))
   return -1;

  idx++;
 }

 return 0;
}

static int create_mcount_loc_sections(struct objtool_file *file)
{
 size_t addr_size = elf_addr_size(file->elf);
 struct instruction *insn;
 struct section *sec;
 int idx;

 sec = find_section_by_name(file->elf, "__mcount_loc");
 if (sec) {
  INIT_LIST_HEAD(&file->mcount_loc_list);
  WARN("file already has __mcount_loc section, skipping");
  return 0;
 }

 if (list_empty(&file->mcount_loc_list))
  return 0;

 idx = 0;
 list_for_each_entry(insn, &file->mcount_loc_list, call_node)
  idx++;

 sec = elf_create_section_pair(file->elf, "__mcount_loc", addr_size,
          idx, idx);
 if (!sec)
  return -1;

 sec->sh.sh_addralign = addr_size;

 idx = 0;
 list_for_each_entry(insn, &file->mcount_loc_list, call_node) {

  struct reloc *reloc;

  reloc = elf_init_reloc_text_sym(file->elf, sec, idx * addr_size, idx,
            insn->sec, insn->offset);
  if (!reloc)
   return -1;

  set_reloc_type(file->elf, reloc, addr_size == 8 ? R_ABS64 : R_ABS32);

  idx++;
 }

 return 0;
}

static int create_direct_call_sections(struct objtool_file *file)
{
 struct instruction *insn;
 struct section *sec;
 int idx;

 sec = find_section_by_name(file->elf, ".call_sites");
 if (sec) {
  INIT_LIST_HEAD(&file->call_list);
  WARN("file already has .call_sites section, skipping");
  return 0;
 }

 if (list_empty(&file->call_list))
  return 0;

 idx = 0;
 list_for_each_entry(insn, &file->call_list, call_node)
  idx++;

 sec = elf_create_section_pair(file->elf, ".call_sites",
          sizeof(unsigned int), idx, idx);
 if (!sec)
  return -1;

 idx = 0;
 list_for_each_entry(insn, &file->call_list, call_node) {

  if (!elf_init_reloc_text_sym(file->elf, sec,
          idx * sizeof(unsigned int), idx,
          insn->sec, insn->offset))
   return -1;

  idx++;
 }

 return 0;
}

/*
 * Warnings shouldn't be reported for ignored functions.
 */

static int add_ignores(struct objtool_file *file)
{
 struct section *rsec;
 struct symbol *func;
 struct reloc *reloc;

 rsec = find_section_by_name(file->elf, ".rela.discard.func_stack_frame_non_standard");
 if (!rsec)
  return 0;

 for_each_reloc(rsec, reloc) {
  switch (reloc->sym->type) {
  case STT_FUNC:
   func = reloc->sym;
   break;

  case STT_SECTION:
   func = find_func_by_offset(reloc->sym->sec, reloc_addend(reloc));
   if (!func)
    continue;
   break;

  default:
   ERROR("unexpected relocation symbol type in %s: %d",
         rsec->name, reloc->sym->type);
   return -1;
  }

  func->ignore = true;
  if (func->cfunc)
   func->cfunc->ignore = true;
 }

 return 0;
}

/*
 * This is a whitelist of functions that is allowed to be called with AC set.
 * The list is meant to be minimal and only contains compiler instrumentation
 * ABI and a few functions used to implement *_{to,from}_user() functions.
 *
 * These functions must not directly change AC, but may PUSHF/POPF.
 */

static const char *uaccess_safe_builtin[] = {
 /* KASAN */
 "kasan_report",
 "kasan_check_range",
 /* KASAN out-of-line */
 "__asan_loadN_noabort",
 "__asan_load1_noabort",
 "__asan_load2_noabort",
 "__asan_load4_noabort",
 "__asan_load8_noabort",
 "__asan_load16_noabort",
 "__asan_storeN_noabort",
 "__asan_store1_noabort",
 "__asan_store2_noabort",
 "__asan_store4_noabort",
 "__asan_store8_noabort",
 "__asan_store16_noabort",
 "__kasan_check_read",
 "__kasan_check_write",
 /* KASAN in-line */
 "__asan_report_load_n_noabort",
 "__asan_report_load1_noabort",
 "__asan_report_load2_noabort",
 "__asan_report_load4_noabort",
 "__asan_report_load8_noabort",
 "__asan_report_load16_noabort",
 "__asan_report_store_n_noabort",
 "__asan_report_store1_noabort",
 "__asan_report_store2_noabort",
 "__asan_report_store4_noabort",
 "__asan_report_store8_noabort",
 "__asan_report_store16_noabort",
 /* KCSAN */
 "__kcsan_check_access",
 "__kcsan_mb",
 "__kcsan_wmb",
 "__kcsan_rmb",
 "__kcsan_release",
 "kcsan_found_watchpoint",
 "kcsan_setup_watchpoint",
 "kcsan_check_scoped_accesses",
 "kcsan_disable_current",
 "kcsan_enable_current_nowarn",
 /* KCSAN/TSAN */
 "__tsan_func_entry",
 "__tsan_func_exit",
 "__tsan_read_range",
 "__tsan_write_range",
 "__tsan_read1",
 "__tsan_read2",
 "__tsan_read4",
 "__tsan_read8",
 "__tsan_read16",
 "__tsan_write1",
 "__tsan_write2",
 "__tsan_write4",
 "__tsan_write8",
 "__tsan_write16",
 "__tsan_read_write1",
 "__tsan_read_write2",
 "__tsan_read_write4",
 "__tsan_read_write8",
 "__tsan_read_write16",
 "__tsan_volatile_read1",
 "__tsan_volatile_read2",
 "__tsan_volatile_read4",
 "__tsan_volatile_read8",
 "__tsan_volatile_read16",
 "__tsan_volatile_write1",
 "__tsan_volatile_write2",
 "__tsan_volatile_write4",
 "__tsan_volatile_write8",
 "__tsan_volatile_write16",
 "__tsan_atomic8_load",
 "__tsan_atomic16_load",
 "__tsan_atomic32_load",
 "__tsan_atomic64_load",
 "__tsan_atomic8_store",
 "__tsan_atomic16_store",
 "__tsan_atomic32_store",
 "__tsan_atomic64_store",
 "__tsan_atomic8_exchange",
 "__tsan_atomic16_exchange",
 "__tsan_atomic32_exchange",
 "__tsan_atomic64_exchange",
 "__tsan_atomic8_fetch_add",
 "__tsan_atomic16_fetch_add",
 "__tsan_atomic32_fetch_add",
 "__tsan_atomic64_fetch_add",
 "__tsan_atomic8_fetch_sub",
 "__tsan_atomic16_fetch_sub",
 "__tsan_atomic32_fetch_sub",
 "__tsan_atomic64_fetch_sub",
 "__tsan_atomic8_fetch_and",
 "__tsan_atomic16_fetch_and",
 "__tsan_atomic32_fetch_and",
 "__tsan_atomic64_fetch_and",
 "__tsan_atomic8_fetch_or",
 "__tsan_atomic16_fetch_or",
 "__tsan_atomic32_fetch_or",
 "__tsan_atomic64_fetch_or",
 "__tsan_atomic8_fetch_xor",
 "__tsan_atomic16_fetch_xor",
 "__tsan_atomic32_fetch_xor",
 "__tsan_atomic64_fetch_xor",
 "__tsan_atomic8_fetch_nand",
 "__tsan_atomic16_fetch_nand",
 "__tsan_atomic32_fetch_nand",
 "__tsan_atomic64_fetch_nand",
 "__tsan_atomic8_compare_exchange_strong",
 "__tsan_atomic16_compare_exchange_strong",
 "__tsan_atomic32_compare_exchange_strong",
 "__tsan_atomic64_compare_exchange_strong",
 "__tsan_atomic8_compare_exchange_weak",
 "__tsan_atomic16_compare_exchange_weak",
 "__tsan_atomic32_compare_exchange_weak",
 "__tsan_atomic64_compare_exchange_weak",
 "__tsan_atomic8_compare_exchange_val",
 "__tsan_atomic16_compare_exchange_val",
 "__tsan_atomic32_compare_exchange_val",
 "__tsan_atomic64_compare_exchange_val",
 "__tsan_atomic_thread_fence",
 "__tsan_atomic_signal_fence",
 "__tsan_unaligned_read16",
 "__tsan_unaligned_write16",
 /* KCOV */
 "write_comp_data",
 "check_kcov_mode",
 "__sanitizer_cov_trace_pc",
 "__sanitizer_cov_trace_const_cmp1",
 "__sanitizer_cov_trace_const_cmp2",
 "__sanitizer_cov_trace_const_cmp4",
 "__sanitizer_cov_trace_const_cmp8",
 "__sanitizer_cov_trace_cmp1",
 "__sanitizer_cov_trace_cmp2",
 "__sanitizer_cov_trace_cmp4",
 "__sanitizer_cov_trace_cmp8",
 "__sanitizer_cov_trace_switch",
 /* KMSAN */
 "kmsan_copy_to_user",
 "kmsan_disable_current",
 "kmsan_enable_current",
 "kmsan_report",
 "kmsan_unpoison_entry_regs",
 "kmsan_unpoison_memory",
 "__msan_chain_origin",
 "__msan_get_context_state",
 "__msan_instrument_asm_store",
 "__msan_metadata_ptr_for_load_1",
 "__msan_metadata_ptr_for_load_2",
 "__msan_metadata_ptr_for_load_4",
 "__msan_metadata_ptr_for_load_8",
 "__msan_metadata_ptr_for_load_n",
 "__msan_metadata_ptr_for_store_1",
 "__msan_metadata_ptr_for_store_2",
 "__msan_metadata_ptr_for_store_4",
 "__msan_metadata_ptr_for_store_8",
 "__msan_metadata_ptr_for_store_n",
 "__msan_poison_alloca",
 "__msan_warning",
 /* UBSAN */
 "ubsan_type_mismatch_common",
 "__ubsan_handle_type_mismatch",
 "__ubsan_handle_type_mismatch_v1",
 "__ubsan_handle_shift_out_of_bounds",
 "__ubsan_handle_load_invalid_value",
 /* KSTACK_ERASE */
 "__sanitizer_cov_stack_depth",
 /* TRACE_BRANCH_PROFILING */
 "ftrace_likely_update",
 /* STACKPROTECTOR */
 "__stack_chk_fail",
 /* misc */
 "csum_partial_copy_generic",
 "copy_mc_fragile",
 "copy_mc_fragile_handle_tail",
 "copy_mc_enhanced_fast_string",
 "rep_stos_alternative",
 "rep_movs_alternative",
 "__copy_user_nocache",
 NULL
};

static void add_uaccess_safe(struct objtool_file *file)
{
 struct symbol *func;
 const char **name;

 if (!opts.uaccess)
  return;

 for (name = uaccess_safe_builtin; *name; name++) {
  func = find_symbol_by_name(file->elf, *name);
  if (!func)
   continue;

  func->uaccess_safe = true;
 }
}

/*
 * Symbols that replace INSN_CALL_DYNAMIC, every (tail) call to such a symbol
 * will be added to the .retpoline_sites section.
 */

__weak bool arch_is_retpoline(struct symbol *sym)
{
 return false;
}

/*
 * Symbols that replace INSN_RETURN, every (tail) call to such a symbol
 * will be added to the .return_sites section.
 */

__weak bool arch_is_rethunk(struct symbol *sym)
{
 return false;
}

/*
 * Symbols that are embedded inside other instructions, because sometimes crazy
 * code exists. These are mostly ignored for validation purposes.
 */

__weak bool arch_is_embedded_insn(struct symbol *sym)
{
 return false;
}

static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
{
 struct reloc *reloc;

 if (insn->no_reloc)
  return NULL;

 if (!file)
  return NULL;

 reloc = find_reloc_by_dest_range(file->elf, insn->sec,
      insn->offset, insn->len);
 if (!reloc) {
  insn->no_reloc = 1;
  return NULL;
 }

 return reloc;
}

static void remove_insn_ops(struct instruction *insn)
{
 struct stack_op *op, *next;

 for (op = insn->stack_ops; op; op = next) {
  next = op->next;
  free(op);
 }
 insn->stack_ops = NULL;
}

static int annotate_call_site(struct objtool_file *file,
          struct instruction *insn, bool sibling)
{
 struct reloc *reloc = insn_reloc(file, insn);
 struct symbol *sym = insn_call_dest(insn);

 if (!sym)
  sym = reloc->sym;

 if (sym->static_call_tramp) {
  list_add_tail(&insn->call_node, &file->static_call_list);
  return 0;
 }

 if (sym->retpoline_thunk) {
  list_add_tail(&insn->call_node, &file->retpoline_call_list);
  return 0;
 }

 /*
 * Many compilers cannot disable KCOV or sanitizer calls with a function
 * attribute so they need a little help, NOP out any such calls from
 * noinstr text.
 */

 if (opts.hack_noinstr && insn->sec->noinstr && sym->profiling_func) {
  if (reloc)
   set_reloc_type(file->elf, reloc, R_NONE);

  if (elf_write_insn(file->elf, insn->sec,
       insn->offset, insn->len,
       sibling ? arch_ret_insn(insn->len)
        : arch_nop_insn(insn->len))) {
   return -1;
  }

  insn->type = sibling ? INSN_RETURN : INSN_NOP;

  if (sibling) {
   /*
 * We've replaced the tail-call JMP insn by two new
 * insn: RET; INT3, except we only have a single struct
 * insn here. Mark it retpoline_safe to avoid the SLS
 * warning, instead of adding another insn.
 */

   insn->retpoline_safe = true;
  }

  return 0;
 }

 if (opts.mcount && sym->fentry) {
  if (sibling)
   WARN_INSN(insn, "tail call to __fentry__ !?!?");
  if (opts.mnop) {
   if (reloc)
    set_reloc_type(file->elf, reloc, R_NONE);

   if (elf_write_insn(file->elf, insn->sec,
        insn->offset, insn->len,
        arch_nop_insn(insn->len))) {
    return -1;
   }

   insn->type = INSN_NOP;
  }

  list_add_tail(&insn->call_node, &file->mcount_loc_list);
  return 0;
 }

 if (insn->type == INSN_CALL && !insn->sec->init &&
     !insn->_call_dest->embedded_insn)
  list_add_tail(&insn->call_node, &file->call_list);

 if (!sibling && dead_end_function(file, sym))
  insn->dead_end = true;

 return 0;
}

static int add_call_dest(struct objtool_file *file, struct instruction *insn,
     struct symbol *dest, bool sibling)
{
 insn->_call_dest = dest;
 if (!dest)
  return 0;

 /*
 * Whatever stack impact regular CALLs have, should be undone
 * by the RETURN of the called function.
 *
 * Annotated intra-function calls retain the stack_ops but
 * are converted to JUMP, see read_intra_function_calls().
 */

 remove_insn_ops(insn);

 return annotate_call_site(file, insn, sibling);
}

static int add_retpoline_call(struct objtool_file *file, struct instruction *insn)
{
 /*
 * Retpoline calls/jumps are really dynamic calls/jumps in disguise,
 * so convert them accordingly.
 */

 switch (insn->type) {
 case INSN_CALL:
  insn->type = INSN_CALL_DYNAMIC;
  break;
 case INSN_JUMP_UNCONDITIONAL:
  insn->type = INSN_JUMP_DYNAMIC;
  break;
 case INSN_JUMP_CONDITIONAL:
  insn->type = INSN_JUMP_DYNAMIC_CONDITIONAL;
  break;
 default:
  return 0;
 }

 insn->retpoline_safe = true;

 /*
 * Whatever stack impact regular CALLs have, should be undone
 * by the RETURN of the called function.
 *
 * Annotated intra-function calls retain the stack_ops but
 * are converted to JUMP, see read_intra_function_calls().
 */

 remove_insn_ops(insn);

 return annotate_call_site(file, insn, false);
}

static void add_return_call(struct objtool_file *file, struct instruction *insn, bool add)
{
 /*
 * Return thunk tail calls are really just returns in disguise,
 * so convert them accordingly.
 */

 insn->type = INSN_RETURN;
 insn->retpoline_safe = true;

 if (add)
  list_add_tail(&insn->call_node, &file->return_thunk_list);
}

static bool is_first_func_insn(struct objtool_file *file,
          struct instruction *insn, struct symbol *sym)
{
 if (insn->offset == sym->offset)
  return true;

 /* Allow direct CALL/JMP past ENDBR */
 if (opts.ibt) {
  struct instruction *prev = prev_insn_same_sym(file, insn);

  if (prev && prev->type == INSN_ENDBR &&
      insn->offset == sym->offset + prev->len)
   return true;
 }

 return false;
}

/*
 * A sibling call is a tail-call to another symbol -- to differentiate from a
 * recursive tail-call which is to the same symbol.
 */

static bool jump_is_sibling_call(struct objtool_file *file,
     struct instruction *from, struct instruction *to)
{
 struct symbol *fs = from->sym;
 struct symbol *ts = to->sym;

 /* Not a sibling call if from/to a symbol hole */
 if (!fs || !ts)
  return false;

 /* Not a sibling call if not targeting the start of a symbol. */
 if (!is_first_func_insn(file, to, ts))
  return false;

 /* Disallow sibling calls into STT_NOTYPE */
 if (ts->type == STT_NOTYPE)
  return false;

 /* Must not be self to be a sibling */
 return fs->pfunc != ts->pfunc;
}

/*
 * Find the destination instructions for all jumps.
 */

static int add_jump_destinations(struct objtool_file *file)
{
 struct instruction *insn, *jump_dest;
 struct reloc *reloc;
 struct section *dest_sec;
 unsigned long dest_off;
 int ret;

 for_each_insn(file, insn) {
  struct symbol *func = insn_func(insn);

  if (insn->jump_dest) {
   /*
 * handle_group_alt() may have previously set
 * 'jump_dest' for some alternatives.
 */

   continue;
  }
  if (!is_static_jump(insn))
   continue;

  reloc = insn_reloc(file, insn);
  if (!reloc) {
   dest_sec = insn->sec;
   dest_off = arch_jump_destination(insn);
  } else if (reloc->sym->type == STT_SECTION) {
   dest_sec = reloc->sym->sec;
   dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
  } else if (reloc->sym->retpoline_thunk) {
   ret = add_retpoline_call(file, insn);
   if (ret)
    return ret;
   continue;
  } else if (reloc->sym->return_thunk) {
   add_return_call(file, insn, true);
   continue;
  } else if (func) {
   /*
 * External sibling call or internal sibling call with
 * STT_FUNC reloc.
 */

   ret = add_call_dest(file, insn, reloc->sym, true);
   if (ret)
    return ret;
   continue;
  } else if (reloc->sym->sec->idx) {
   dest_sec = reloc->sym->sec;
   dest_off = reloc->sym->sym.st_value +
       arch_dest_reloc_offset(reloc_addend(reloc));
  } else {
   /* non-func asm code jumping to another file */
   continue;
  }

  jump_dest = find_insn(file, dest_sec, dest_off);
  if (!jump_dest) {
   struct symbol *sym = find_symbol_by_offset(dest_sec, dest_off);

   /*
 * This is a special case for retbleed_untrain_ret().
 * It jumps to __x86_return_thunk(), but objtool
 * can't find the thunk's starting RET
 * instruction, because the RET is also in the
 * middle of another instruction.  Objtool only
 * knows about the outer instruction.
 */

   if (sym && sym->embedded_insn) {
    add_return_call(file, insn, false);
    continue;
   }

   /*
 * GCOV/KCOV dead code can jump to the end of the
 * function/section.
 */

   if (file->ignore_unreachables && func &&
       dest_sec == insn->sec &&
       dest_off == func->offset + func->len)
    continue;

   ERROR_INSN(insn, "can't find jump dest instruction at %s+0x%lx",
       dest_sec->name, dest_off);
   return -1;
  }

  /*
 * An intra-TU jump in retpoline.o might not have a relocation
 * for its jump dest, in which case the above
 * add_{retpoline,return}_call() didn't happen.
 */

  if (jump_dest->sym && jump_dest->offset == jump_dest->sym->offset) {
   if (jump_dest->sym->retpoline_thunk) {
    ret = add_retpoline_call(file, insn);
    if (ret)
     return ret;
    continue;
   }
   if (jump_dest->sym->return_thunk) {
    add_return_call(file, insn, true);
    continue;
   }
  }

  /*
 * Cross-function jump.
 */

  if (func && insn_func(jump_dest) && func != insn_func(jump_dest)) {

   /*
 * For GCC 8+, create parent/child links for any cold
 * subfunctions.  This is _mostly_ redundant with a
 * similar initialization in read_symbols().
 *
 * If a function has aliases, we want the *first* such
 * function in the symbol table to be the subfunction's
 * parent.  In that case we overwrite the
 * initialization done in read_symbols().
 *
 * However this code can't completely replace the
 * read_symbols() code because this doesn't detect the
 * case where the parent function's only reference to a
 * subfunction is through a jump table.
 */

   if (!strstr(func->name, ".cold") &&
       strstr(insn_func(jump_dest)->name, ".cold")) {
    func->cfunc = insn_func(jump_dest);
    insn_func(jump_dest)->pfunc = func;
   }
  }

  if (jump_is_sibling_call(file, insn, jump_dest)) {
   /*
 * Internal sibling call without reloc or with
 * STT_SECTION reloc.
 */

   ret = add_call_dest(file, insn, insn_func(jump_dest), true);
   if (ret)
    return ret;
   continue;
  }

  insn->jump_dest = jump_dest;
 }

 return 0;
}

static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
{
 struct symbol *call_dest;

 call_dest = find_func_by_offset(sec, offset);
 if (!call_dest)
  call_dest = find_symbol_by_offset(sec, offset);

 return call_dest;
}

/*
 * Find the destination instructions for all calls.
 */

static int add_call_destinations(struct objtool_file *file)
{
 struct instruction *insn;
 unsigned long dest_off;
 struct symbol *dest;
 struct reloc *reloc;
 int ret;

 for_each_insn(file, insn) {
  struct symbol *func = insn_func(insn);
  if (insn->type != INSN_CALL)
   continue;

  reloc = insn_reloc(file, insn);
  if (!reloc) {
   dest_off = arch_jump_destination(insn);
   dest = find_call_destination(insn->sec, dest_off);

   ret = add_call_dest(file, insn, dest, false);
   if (ret)
    return ret;

   if (func && func->ignore)
    continue;

   if (!insn_call_dest(insn)) {
    ERROR_INSN(insn, "unannotated intra-function call");
    return -1;
   }

   if (func && insn_call_dest(insn)->type != STT_FUNC) {
    ERROR_INSN(insn, "unsupported call to non-function");
    return -1;
   }

  } else if (reloc->sym->type == STT_SECTION) {
   dest_off = arch_dest_reloc_offset(reloc_addend(reloc));
   dest = find_call_destination(reloc->sym->sec, dest_off);
   if (!dest) {
    ERROR_INSN(insn, "can't find call dest symbol at %s+0x%lx",
        reloc->sym->sec->name, dest_off);
    return -1;
   }

   ret = add_call_dest(file, insn, dest, false);
   if (ret)
    return ret;

  } else if (reloc->sym->retpoline_thunk) {
   ret = add_retpoline_call(file, insn);
   if (ret)
    return ret;

  } else {
   ret = add_call_dest(file, insn, reloc->sym, false);
   if (ret)
    return ret;
  }
 }

 return 0;
}

/*
 * The .alternatives section requires some extra special care over and above
 * other special sections because alternatives are patched in place.
 */

static int handle_group_alt(struct objtool_file *file,
       struct special_alt *special_alt,
       struct instruction *orig_insn,
       struct instruction **new_insn)
{
 struct instruction *last_new_insn = NULL, *insn, *nop = NULL;
 struct alt_group *orig_alt_group, *new_alt_group;
 unsigned long dest_off;

 orig_alt_group = orig_insn->alt_group;
 if (!orig_alt_group) {
  struct instruction *last_orig_insn = NULL;

  orig_alt_group = calloc(1, sizeof(*orig_alt_group));
  if (!orig_alt_group) {
   ERROR_GLIBC("calloc");
   return -1;
  }
  orig_alt_group->cfi = calloc(special_alt->orig_len,
          sizeof(struct cfi_state *));
  if (!orig_alt_group->cfi) {
   ERROR_GLIBC("calloc");
   return -1;
  }

  insn = orig_insn;
  sec_for_each_insn_from(file, insn) {
   if (insn->offset >= special_alt->orig_off + special_alt->orig_len)
    break;

   insn->alt_group = orig_alt_group;
   last_orig_insn = insn;
  }
  orig_alt_group->orig_group = NULL;
  orig_alt_group->first_insn = orig_insn;
  orig_alt_group->last_insn = last_orig_insn;
  orig_alt_group->nop = NULL;
  orig_alt_group->ignore = orig_insn->ignore_alts;
 } else {
  if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len -
      orig_alt_group->first_insn->offset != special_alt->orig_len) {
   ERROR_INSN(orig_insn, "weirdly overlapping alternative! %ld != %d",
       orig_alt_group->last_insn->offset +
       orig_alt_group->last_insn->len -
       orig_alt_group->first_insn->offset,
       special_alt->orig_len);
   return -1;
  }
 }

 new_alt_group = calloc(1, sizeof(*new_alt_group));
 if (!new_alt_group) {
  ERROR_GLIBC("calloc");
  return -1;
 }

 if (special_alt->new_len < special_alt->orig_len) {
  /*
 * Insert a fake nop at the end to make the replacement
 * alt_group the same size as the original.  This is needed to
 * allow propagate_alt_cfi() to do its magic.  When the last
 * instruction affects the stack, the instruction after it (the
 * nop) will propagate the new state to the shared CFI array.
 */

  nop = calloc(1, sizeof(*nop));
  if (!nop) {
   ERROR_GLIBC("calloc");
   return -1;
  }
  memset(nop, 0, sizeof(*nop));

  nop->sec = special_alt->new_sec;
  nop->offset = special_alt->new_off + special_alt->new_len;
  nop->len = special_alt->orig_len - special_alt->new_len;
  nop->type = INSN_NOP;
  nop->sym = orig_insn->sym;
  nop->alt_group = new_alt_group;
 }

 if (!special_alt->new_len) {
  *new_insn = nop;
  goto end;
 }

 insn = *new_insn;
 sec_for_each_insn_from(file, insn) {
  struct reloc *alt_reloc;

  if (insn->offset >= special_alt->new_off + special_alt->new_len)
   break;

  last_new_insn = insn;

  insn->sym = orig_insn->sym;
  insn->alt_group = new_alt_group;

  /*
 * Since alternative replacement code is copy/pasted by the
 * kernel after applying relocations, generally such code can't
 * have relative-address relocation references to outside the
 * .altinstr_replacement section, unless the arch's
 * alternatives code can adjust the relative offsets
 * accordingly.
 */

  alt_reloc = insn_reloc(file, insn);
  if (alt_reloc && arch_pc_relative_reloc(alt_reloc) &&
      !arch_support_alt_relocation(special_alt, insn, alt_reloc)) {

   ERROR_INSN(insn, "unsupported relocation in alternatives section");
   return -1;
  }

  if (!is_static_jump(insn))
   continue;

  if (!insn->immediate)
   continue;

  dest_off = arch_jump_destination(insn);
  if (dest_off == special_alt->new_off + special_alt->new_len) {
   insn->jump_dest = next_insn_same_sec(file, orig_alt_group->last_insn);
   if (!insn->jump_dest) {
    ERROR_INSN(insn, "can't find alternative jump destination");
    return -1;
   }
  }
 }

 if (!last_new_insn) {
  ERROR_FUNC(special_alt->new_sec, special_alt->new_off,
      "can't find last new alternative instruction");
  return -1;
 }

end:
 new_alt_group->orig_group = orig_alt_group;
 new_alt_group->first_insn = *new_insn;
 new_alt_group->last_insn = last_new_insn;
 new_alt_group->nop = nop;
 new_alt_group->ignore = (*new_insn)->ignore_alts;
 new_alt_group->cfi = orig_alt_group->cfi;
 return 0;
}

/*
 * A jump table entry can either convert a nop to a jump or a jump to a nop.
 * If the original instruction is a jump, make the alt entry an effective nop
 * by just skipping the original instruction.
 */

static int handle_jump_alt(struct objtool_file *file,
      struct special_alt *special_alt,
      struct instruction *orig_insn,
      struct instruction **new_insn)
{
 if (orig_insn->type != INSN_JUMP_UNCONDITIONAL &&
     orig_insn->type != INSN_NOP) {

  ERROR_INSN(orig_insn, "unsupported instruction at jump label");
  return -1;
 }

 if (opts.hack_jump_label && special_alt->key_addend & 2) {
  struct reloc *reloc = insn_reloc(file, orig_insn);

  if (reloc)
   set_reloc_type(file->elf, reloc, R_NONE);

  if (elf_write_insn(file->elf, orig_insn->sec,
       orig_insn->offset, orig_insn->len,
       arch_nop_insn(orig_insn->len))) {
   return -1;
  }

  orig_insn->type = INSN_NOP;
 }

 if (orig_insn->type == INSN_NOP) {
  if (orig_insn->len == 2)
   file->jl_nop_short++;
  else
   file->jl_nop_long++;

  return 0;
 }

 if (orig_insn->len == 2)
  file->jl_short++;
 else
  file->jl_long++;

 *new_insn = next_insn_same_sec(file, orig_insn);
 return 0;
}

/*
 * Read all the special sections which have alternate instructions which can be
 * patched in or redirected to at runtime.  Each instruction having alternate
 * instruction(s) has them added to its insn->alts list, which will be
 * traversed in validate_branch().
 */

static int add_special_section_alts(struct objtool_file *file)
{
 struct list_head special_alts;
 struct instruction *orig_insn, *new_insn;
 struct special_alt *special_alt, *tmp;
 struct alternative *alt;
 int ret;

 if (special_get_alts(file->elf, &special_alts))
  return -1;

 list_for_each_entry_safe(special_alt, tmp, &special_alts, list) {

  orig_insn = find_insn(file, special_alt->orig_sec,
          special_alt->orig_off);
  if (!orig_insn) {
   ERROR_FUNC(special_alt->orig_sec, special_alt->orig_off,
       "special: can't find orig instruction");
   return -1;
  }

  new_insn = NULL;
  if (!special_alt->group || special_alt->new_len) {
   new_insn = find_insn(file, special_alt->new_sec,
          special_alt->new_off);
   if (!new_insn) {
    ERROR_FUNC(special_alt->new_sec, special_alt->new_off,
        "special: can't find new instruction");
    return -1;
   }
  }

  if (special_alt->group) {
   if (!special_alt->orig_len) {
    ERROR_INSN(orig_insn, "empty alternative entry");
    continue;
   }

   ret = handle_group_alt(file, special_alt, orig_insn,
            &new_insn);
   if (ret)
    return ret;

  } else if (special_alt->jump_or_nop) {
   ret = handle_jump_alt(file, special_alt, orig_insn,
           &new_insn);
   if (ret)
    return ret;
  }

  alt = calloc(1, sizeof(*alt));
  if (!alt) {
   ERROR_GLIBC("calloc");
   return -1;
  }

  alt->insn = new_insn;
  alt->next = orig_insn->alts;
  orig_insn->alts = alt;

  list_del(&special_alt->list);
  free(special_alt);
 }

 if (opts.stats) {
  printf("jl\\\tNOP\tJMP\n");
  printf("short:\t%ld\t%ld\n", file->jl_nop_short, file->jl_short);
  printf("long:\t%ld\t%ld\n", file->jl_nop_long, file->jl_long);
 }

 return 0;
}

__weak unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table)
{
 return reloc->sym->offset + reloc_addend(reloc);
}

static int add_jump_table(struct objtool_file *file, struct instruction *insn)
{
 unsigned long table_size = insn_jump_table_size(insn);
 struct symbol *pfunc = insn_func(insn)->pfunc;
 struct reloc *table = insn_jump_table(insn);
 struct instruction *dest_insn;
 unsigned int prev_offset = 0;
 struct reloc *reloc = table;
 struct alternative *alt;
 unsigned long sym_offset;

 /*
 * Each @reloc is a switch table relocation which points to the target
 * instruction.
 */

 for_each_reloc_from(table->sec, reloc) {

  /* Check for the end of the table: */
  if (table_size && reloc_offset(reloc) - reloc_offset(table) >= table_size)
   break;
  if (reloc != table && is_jump_table(reloc))
   break;

  /* Make sure the table entries are consecutive: */
  if (prev_offset && reloc_offset(reloc) != prev_offset + arch_reloc_size(reloc))
   break;

  sym_offset = arch_jump_table_sym_offset(reloc, table);

  /* Detect function pointers from contiguous objects: */
  if (reloc->sym->sec == pfunc->sec && sym_offset == pfunc->offset)
   break;

  /*
 * Clang sometimes leaves dangling unused jump table entries
 * which point to the end of the function.  Ignore them.
 */

  if (reloc->sym->sec == pfunc->sec &&
      sym_offset == pfunc->offset + pfunc->len)
   goto next;

  dest_insn = find_insn(file, reloc->sym->sec, sym_offset);
  if (!dest_insn)
   break;

  /* Make sure the destination is in the same function: */
  if (!insn_func(dest_insn) || insn_func(dest_insn)->pfunc != pfunc)
   break;

  alt = calloc(1, sizeof(*alt));
  if (!alt) {
   ERROR_GLIBC("calloc");
   return -1;
  }

  alt->insn = dest_insn;
  alt->next = insn->alts;
  insn->alts = alt;
next:
  prev_offset = reloc_offset(reloc);
 }

 if (!prev_offset) {
  ERROR_INSN(insn, "can't find switch jump table");
  return -1;
 }

 return 0;
}

/*
 * find_jump_table() - Given a dynamic jump, find the switch jump table
 * associated with it.
 */

static void find_jump_table(struct objtool_file *file, struct symbol *func,
       struct instruction *insn)
{
 struct reloc *table_reloc;
 struct instruction *dest_insn, *orig_insn = insn;
 unsigned long table_size;
 unsigned long sym_offset;

 /*
 * Backward search using the @first_jump_src links, these help avoid
 * much of the 'in between' code. Which avoids us getting confused by
 * it.
 */

 for (;
      insn && insn_func(insn) && insn_func(insn)->pfunc == func;
      insn = insn->first_jump_src ?: prev_insn_same_sym(file, insn)) {

  if (insn != orig_insn && insn->type == INSN_JUMP_DYNAMIC)
   break;

  /* allow small jumps within the range */
  if (insn->type == INSN_JUMP_UNCONDITIONAL &&
      insn->jump_dest &&
      (insn->jump_dest->offset <= insn->offset ||
       insn->jump_dest->offset > orig_insn->offset))
   break;

  table_reloc = arch_find_switch_table(file, insn, &table_size);
  if (!table_reloc)
   continue;

  sym_offset = table_reloc->sym->offset + reloc_addend(table_reloc);

  dest_insn = find_insn(file, table_reloc->sym->sec, sym_offset);
  if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func)
   continue;

  set_jump_table(table_reloc);
  orig_insn->_jump_table = table_reloc;
  orig_insn->_jump_table_size = table_size;

  break;
 }
}

/*
 * First pass: Mark the head of each jump table so that in the next pass,
 * we know when a given jump table ends and the next one starts.
 */

static void mark_func_jump_tables(struct objtool_file *file,
        struct symbol *func)
{
 struct instruction *insn, *last = NULL;

 func_for_each_insn(file, func, insn) {
  if (!last)
   last = insn;

  /*
 * Store back-pointers for unconditional forward jumps such
 * that find_jump_table() can back-track using those and
 * avoid some potentially confusing code.
 */

  if (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest &&
      insn->offset > last->offset &&
      insn->jump_dest->offset > insn->offset &&
      !insn->jump_dest->first_jump_src) {

   insn->jump_dest->first_jump_src = insn;
   last = insn->jump_dest;
  }

  if (insn->type != INSN_JUMP_DYNAMIC)
   continue;

  find_jump_table(file, func, insn);
 }
}

static int add_func_jump_tables(struct objtool_file *file,
      struct symbol *func)
{
 struct instruction *insn;
 int ret;

 func_for_each_insn(file, func, insn) {
  if (!insn_jump_table(insn))
   continue;

  ret = add_jump_table(file, insn);
  if (ret)
   return ret;
 }

 return 0;
}

/*
 * For some switch statements, gcc generates a jump table in the .rodata
 * section which contains a list of addresses within the function to jump to.
 * This finds these jump tables and adds them to the insn->alts lists.
 */

static int add_jump_table_alts(struct objtool_file *file)
{
 struct symbol *func;
 int ret;

 if (!file->rodata)
  return 0;

 for_each_sym(file, func) {
  if (func->type != STT_FUNC)
   continue;

  mark_func_jump_tables(file, func);
  ret = add_func_jump_tables(file, func);
  if (ret)
   return ret;
 }

 return 0;
}

static void set_func_state(struct cfi_state *state)
{
 state->cfa = initial_func_cfi.cfa;
 memcpy(&state->regs, &initial_func_cfi.regs,
        CFI_NUM_REGS * sizeof(struct cfi_reg));
 state->stack_size = initial_func_cfi.cfa.offset;
 state->type = UNWIND_HINT_TYPE_CALL;
}

static int read_unwind_hints(struct objtool_file *file)
{
 struct cfi_state cfi = init_cfi;
 struct section *sec;
 struct unwind_hint *hint;
 struct instruction *insn;
 struct reloc *reloc;
 unsigned long offset;
 int i;

 sec = find_section_by_name(file->elf, ".discard.unwind_hints");
 if (!sec)
  return 0;

 if (!sec->rsec) {
  ERROR("missing .rela.discard.unwind_hints section");
  return -1;
 }

 if (sec->sh.sh_size % sizeof(struct unwind_hint)) {
  ERROR("struct unwind_hint size mismatch");
  return -1;
 }

 file->hints = true;

 for (i = 0; i < sec->sh.sh_size / sizeof(struct unwind_hint); i++) {
  hint = (struct unwind_hint *)sec->data->d_buf + i;

  reloc = find_reloc_by_dest(file->elf, sec, i * sizeof(*hint));
  if (!reloc) {
   ERROR("can't find reloc for unwind_hints[%d]", i);
   return -1;
  }

  if (reloc->sym->type == STT_SECTION) {
   offset = reloc_addend(reloc);
  } else if (reloc->sym->local_label) {
   offset = reloc->sym->offset;
  } else {
   ERROR("unexpected relocation symbol type in %s", sec->rsec->name);
   return -1;
  }

  insn = find_insn(file, reloc->sym->sec, offset);
  if (!insn) {
   ERROR("can't find insn for unwind_hints[%d]", i);
   return -1;
  }

  insn->hint = true;

  if (hint->type == UNWIND_HINT_TYPE_UNDEFINED) {
   insn->cfi = &force_undefined_cfi;
   continue;
  }

  if (hint->type == UNWIND_HINT_TYPE_SAVE) {
   insn->hint = false;
   insn->save = true;
   continue;
  }

  if (hint->type == UNWIND_HINT_TYPE_RESTORE) {
   insn->restore = true;
   continue;
  }

  if (hint->type == UNWIND_HINT_TYPE_REGS_PARTIAL) {
   struct symbol *sym = find_symbol_by_offset(insn->sec, insn->offset);

   if (sym && sym->bind == STB_GLOBAL) {
    if (opts.ibt && insn->type != INSN_ENDBR && !insn->noendbr) {
     ERROR_INSN(insn, "UNWIND_HINT_IRET_REGS without ENDBR");
     return -1;
    }
   }
  }

  if (hint->type == UNWIND_HINT_TYPE_FUNC) {
   insn->cfi = &func_cfi;
   continue;
  }

  if (insn->cfi)
   cfi = *(insn->cfi);

  if (arch_decode_hint_reg(hint->sp_reg, &cfi.cfa.base)) {
   ERROR_INSN(insn, "unsupported unwind_hint sp base reg %d", hint->sp_reg);
   return -1;
  }

  cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset);
  cfi.type = hint->type;
  cfi.signal = hint->signal;

  insn->cfi = cfi_hash_find_or_add(&cfi);
 }

 return 0;
}

static int read_annotate(struct objtool_file *file,
    int (*func)(struct objtool_file *file, int type, struct instruction *insn))
{
 struct section *sec;
 struct instruction *insn;
 struct reloc *reloc;
 uint64_t offset;
 int type, ret;

 sec = find_section_by_name(file->elf, ".discard.annotate_insn");
 if (!sec)
  return 0;

 if (!sec->rsec)
  return 0;

 if (sec->sh.sh_entsize != 8) {
  static bool warned = false;
  if (!warned && opts.verbose) {
   WARN("%s: dodgy linker, sh_entsize != 8", sec->name);
   warned = true;
  }
  sec->sh.sh_entsize = 8;
 }

 for_each_reloc(sec->rsec, reloc) {
  type = *(u32 *)(sec->data->d_buf + (reloc_idx(reloc) * sec->sh.sh_entsize) + 4);
  type = bswap_if_needed(file->elf, type);

  offset = reloc->sym->offset + reloc_addend(reloc);
  insn = find_insn(file, reloc->sym->sec, offset);

  if (!insn) {
   ERROR("bad .discard.annotate_insn entry: %d of type %d", reloc_idx(reloc), type);
   return -1;
  }

  ret = func(file, type, insn);
  if (ret < 0)
   return ret;
 }

 return 0;
}

static int __annotate_early(struct objtool_file *file, int type, struct instruction *insn)
{
 switch (type) {

 /* Must be before add_special_section_alts() */
 case ANNOTYPE_IGNORE_ALTS:
  insn->ignore_alts = true;
  break;

 /*
 * Must be before read_unwind_hints() since that needs insn->noendbr.
 */

 case ANNOTYPE_NOENDBR:
  insn->noendbr = 1;
  break;

 default:
  break;
 }

 return 0;
}

static int __annotate_ifc(struct objtool_file *file, int type, struct instruction *insn)
{
 unsigned long dest_off;

 if (type != ANNOTYPE_INTRA_FUNCTION_CALL)
  return 0;

 if (insn->type != INSN_CALL) {
  ERROR_INSN(insn, "intra_function_call not a direct call");
  return -1;
 }

 /*
 * Treat intra-function CALLs as JMPs, but with a stack_op.
 * See add_call_destinations(), which strips stack_ops from
 * normal CALLs.
 */

 insn->type = INSN_JUMP_UNCONDITIONAL;

 dest_off = arch_jump_destination(insn);
 insn->jump_dest = find_insn(file, insn->sec, dest_off);
 if (!insn->jump_dest) {
  ERROR_INSN(insn, "can't find call dest at %s+0x%lx",
      insn->sec->name, dest_off);
  return -1;
 }

 return 0;
}

static int __annotate_late(struct objtool_file *file, int type, struct instruction *insn)
{
 switch (type) {
 case ANNOTYPE_NOENDBR:
  /* early */
  break;

 case ANNOTYPE_RETPOLINE_SAFE:
  if (insn->type != INSN_JUMP_DYNAMIC &&
      insn->type != INSN_CALL_DYNAMIC &&
      insn->type != INSN_RETURN &&
      insn->type != INSN_NOP) {
   ERROR_INSN(insn, "retpoline_safe hint not an indirect jump/call/ret/nop");
   return -1;
  }

  insn->retpoline_safe = true;
  break;

 case ANNOTYPE_INSTR_BEGIN:
  insn->instr++;
  break;

 case ANNOTYPE_INSTR_END:
  insn->instr--;
  break;

 case ANNOTYPE_UNRET_BEGIN:
  insn->unret = 1;
  break;

 case ANNOTYPE_IGNORE_ALTS:
  /* early */
  break;

 case ANNOTYPE_INTRA_FUNCTION_CALL:
  /* ifc */
  break;

 case ANNOTYPE_REACHABLE:
  insn->dead_end = false;
  break;

 default:
  ERROR_INSN(insn, "Unknown annotation type: %d", type);
  return -1;
 }

 return 0;
}

/*
 * Return true if name matches an instrumentation function, where calls to that
 * function from noinstr code can safely be removed, but compilers won't do so.
 */

static bool is_profiling_func(const char *name)
{
 /*
 * Many compilers cannot disable KCOV with a function attribute.
 */

 if (!strncmp(name, "__sanitizer_cov_", 16))
  return true;

 /*
 * Some compilers currently do not remove __tsan_func_entry/exit nor
 * __tsan_atomic_signal_fence (used for barrier instrumentation) with
 * the __no_sanitize_thread attribute, remove them. Once the kernel's
 * minimum Clang version is 14.0, this can be removed.
 */

 if (!strncmp(name, "__tsan_func_", 12) ||
     !strcmp(name, "__tsan_atomic_signal_fence"))
  return true;

 return false;
}

static int classify_symbols(struct objtool_file *file)
{
 struct symbol *func;

 for_each_sym(file, func) {
  if (func->type == STT_NOTYPE && strstarts(func->name, ".L"))
   func->local_label = true;

  if (func->bind != STB_GLOBAL)
   continue;

  if (!strncmp(func->name, STATIC_CALL_TRAMP_PREFIX_STR,
        strlen(STATIC_CALL_TRAMP_PREFIX_STR)))
   func->static_call_tramp = true;

  if (arch_is_retpoline(func))
   func->retpoline_thunk = true;

  if (arch_is_rethunk(func))
   func->return_thunk = true;

  if (arch_is_embedded_insn(func))
   func->embedded_insn = true;

  if (arch_ftrace_match(func->name))
   func->fentry = true;

  if (is_profiling_func(func->name))
   func->profiling_func = true;
 }

 return 0;
}

static void mark_rodata(struct objtool_file *file)
{
 struct section *sec;
 bool found = false;

 /*
 * Search for the following rodata sections, each of which can
 * potentially contain jump tables:
 *
 * - .rodata: can contain GCC switch tables
 * - .rodata.<func>: same, if -fdata-sections is being used
 * - .data.rel.ro.c_jump_table: contains C annotated jump tables
 *
 * .rodata.str1.* sections are ignored; they don't contain jump tables.
 */

 for_each_sec(file, sec) {
  if ((!strncmp(sec->name, ".rodata", 7) &&
       !strstr(sec->name, ".str1.")) ||
      !strncmp(sec->name, ".data.rel.ro", 12)) {
   sec->rodata = true;
   found = true;
  }
 }

 file->rodata = found;
}

static int decode_sections(struct objtool_file *file)
{
 int ret;

 mark_rodata(file);

 ret = init_pv_ops(file);
 if (ret)
  return ret;

 /*
 * Must be before add_{jump_call}_destination.
 */

 ret = classify_symbols(file);
 if (ret)
  return ret;

 ret = decode_instructions(file);
 if (ret)
  return ret;

 ret = add_ignores(file);
 if (ret)
  return ret;

 add_uaccess_safe(file);

 ret = read_annotate(file, __annotate_early);
 if (ret)
  return ret;

 /*
 * Must be before add_jump_destinations(), which depends on 'func'
 * being set for alternatives, to enable proper sibling call detection.
 */

 if (opts.stackval || opts.orc || opts.uaccess || opts.noinstr) {
  ret = add_special_section_alts(file);
  if (ret)
   return ret;
 }

 ret = add_jump_destinations(file);
 if (ret)
  return ret;

 /*
 * Must be before add_call_destination(); it changes INSN_CALL to
 * INSN_JUMP.
 */

 ret = read_annotate(file, __annotate_ifc);
 if (ret)
  return ret;

 ret = add_call_destinations(file);
 if (ret)
  return ret;

 ret = add_jump_table_alts(file);
 if (ret)
  return ret;

 ret = read_unwind_hints(file);
 if (ret)
  return ret;

 /*
 * Must be after add_call_destinations() such that it can override
 * dead_end_function() marks.
 */

 ret = read_annotate(file, __annotate_late);
 if (ret)
  return ret;

 return 0;
}

static bool is_special_call(struct instruction *insn)
{
 if (insn->type == INSN_CALL) {
  struct symbol *dest = insn_call_dest(insn);

  if (!dest)
   return false;

  if (dest->fentry || dest->embedded_insn)
   return true;
 }

 return false;
}

static bool has_modified_stack_frame(struct instruction *insn, struct insn_state *state)
{
 struct cfi_state *cfi = &state->cfi;
 int i;

 if (cfi->cfa.base != initial_func_cfi.cfa.base || cfi->drap)
  return true;

 if (cfi->cfa.offset != initial_func_cfi.cfa.offset)
  return true;

 if (cfi->stack_size != initial_func_cfi.cfa.offset)
  return true;

 for (i = 0; i < CFI_NUM_REGS; i++) {
  if (cfi->regs[i].base != initial_func_cfi.regs[i].base ||
      cfi->regs[i].offset != initial_func_cfi.regs[i].offset)
   return true;
 }

 return false;
}

static bool check_reg_frame_pos(const struct cfi_reg *reg,
    int expected_offset)
{
 return reg->base == CFI_CFA &&
        reg->offset == expected_offset;
}

static bool has_valid_stack_frame(struct insn_state *state)
{
 struct cfi_state *cfi = &state->cfi;

 if (cfi->cfa.base == CFI_BP &&
     check_reg_frame_pos(&cfi->regs[CFI_BP], -cfi->cfa.offset) &&
     check_reg_frame_pos(&cfi->regs[CFI_RA], -cfi->cfa.offset + 8))
  return true;

 if (cfi->drap && cfi->regs[CFI_BP].base == CFI_BP)
  return true;

 return false;
}

static int update_cfi_state_regs(struct instruction *insn,
      struct cfi_state *cfi,
      struct stack_op *op)
{
 struct cfi_reg *cfa = &cfi->cfa;

 if (cfa->base != CFI_SP && cfa->base != CFI_SP_INDIRECT)
  return 0;

 /* push */
 if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF)
  cfa->offset += 8;

 /* pop */
 if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF)
  cfa->offset -= 8;

 /* add immediate to sp */
 if (op->dest.type == OP_DEST_REG && op->src.type == OP_SRC_ADD &&
     op->dest.reg == CFI_SP && op->src.reg == CFI_SP)
  cfa->offset -= op->src.offset;

 return 0;
}

static void save_reg(struct cfi_state *cfi, unsigned char reg, int base, int offset)
{
 if (arch_callee_saved_reg(reg) &&
     cfi->regs[reg].base == CFI_UNDEFINED) {
  cfi->regs[reg].base = base;
  cfi->regs[reg].offset = offset;
 }
}

static void restore_reg(struct cfi_state *cfi, unsigned char reg)
{
 cfi->regs[reg].base = initial_func_cfi.regs[reg].base;
 cfi->regs[reg].offset = initial_func_cfi.regs[reg].offset;
}

/*
 * A note about DRAP stack alignment:
 *
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=98 G=97

¤ Dauer der Verarbeitung: 0.27 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge