Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> */
#include <stdlib.h>#include <string.h>
#include <linux/objtool_types.h>#include <asm/orc_types.h>
#include <objtool/check.h>#include <objtool/orc.h>#include <objtool/warn.h>
struct orc_list_entry { struct list_head list; struct orc_entry orc; struct section *insn_sec; unsigned long insn_off;};
static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc, struct section *sec, unsigned long offset){ struct orc_list_entry *entry = malloc(sizeof(*entry));
if (!entry) { WARN("malloc failed"); return -1; }
entry->orc = *orc; entry->insn_sec = sec; entry->insn_off = offset;
list_add_tail(&entry->list, orc_list); return 0;}
static unsigned long alt_group_len(struct alt_group *alt_group){ return alt_group->last_insn->offset + alt_group->last_insn->len - alt_group->first_insn->offset;}
int orc_create(struct objtool_file *file){ struct section *sec, *orc_sec; unsigned int nr = 0, idx = 0; struct orc_list_entry *entry; struct list_head orc_list;
struct orc_entry null = { .type = ORC_TYPE_UNDEFINED };
/* Build a deduplicated list of ORC entries: */ INIT_LIST_HEAD(&orc_list); for_each_sec(file->elf, sec) { struct orc_entry orc, prev_orc = {0}; struct instruction *insn; bool empty = true;
if (!sec->text) continue;
sec_for_each_insn(file, sec, insn) { struct alt_group *alt_group = insn->alt_group; int i;
if (!alt_group) { if (init_orc_entry(&orc, insn->cfi, insn)) return -1; if (!memcmp(&prev_orc, &orc, sizeof(orc))) continue; if (orc_list_add(&orc_list, &orc, sec, insn->offset)) return -1; nr++; prev_orc = orc; empty = false; continue; }
/* * Alternatives can have different stack layout * possibilities (but they shouldn't conflict). * Instead of traversing the instructions, use the * alt_group's flattened byte-offset-addressed CFI * array. */ for (i = 0; i < alt_group_len(alt_group); i++) { struct cfi_state *cfi = alt_group->cfi[i]; if (!cfi) continue; /* errors are reported on the original insn */ if (init_orc_entry(&orc, cfi, insn)) return -1; if (!memcmp(&prev_orc, &orc, sizeof(orc))) continue; if (orc_list_add(&orc_list, &orc, insn->sec, insn->offset + i)) return -1; nr++; prev_orc = orc; empty = false; }
/* Skip to the end of the alt_group */ insn = alt_group->last_insn; }
/* Add a section terminator */ if (!empty) { orc_list_add(&orc_list, &null, sec, sec->sh.sh_size); nr++; } } if (!nr) return 0;
/* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */ sec = find_section_by_name(file->elf, ".orc_unwind"); if (sec) { WARN("file already has .orc_unwind section, skipping"); return -1; } orc_sec = elf_create_section(file->elf, ".orc_unwind", nr * sizeof(struct orc_entry), sizeof(struct orc_entry), SHT_PROGBITS, 1, SHF_ALLOC); if (!orc_sec) return -1;
sec = elf_create_section_pair(file->elf, ".orc_unwind_ip", sizeof(int), nr, nr); if (!sec) return -1;
/* Write ORC entries to sections: */ list_for_each_entry(entry, &orc_list, list) { if (write_orc_entry(file->elf, orc_sec, sec, idx++, entry->insn_sec, entry->insn_off, &entry->orc)) return -1; }
return 0;}