162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <stdlib.h> 762306a36Sopenharmony_ci#include <string.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/objtool_types.h> 1062306a36Sopenharmony_ci#include <asm/orc_types.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <objtool/check.h> 1362306a36Sopenharmony_ci#include <objtool/warn.h> 1462306a36Sopenharmony_ci#include <objtool/endianness.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, 1762306a36Sopenharmony_ci struct instruction *insn) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci struct cfi_reg *bp = &cfi->regs[CFI_BP]; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci memset(orc, 0, sizeof(*orc)); 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci if (!cfi) { 2462306a36Sopenharmony_ci /* 2562306a36Sopenharmony_ci * This is usually either unreachable nops/traps (which don't 2662306a36Sopenharmony_ci * trigger unreachable instruction warnings), or 2762306a36Sopenharmony_ci * STACK_FRAME_NON_STANDARD functions. 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci orc->type = ORC_TYPE_UNDEFINED; 3062306a36Sopenharmony_ci return 0; 3162306a36Sopenharmony_ci } 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci switch (cfi->type) { 3462306a36Sopenharmony_ci case UNWIND_HINT_TYPE_UNDEFINED: 3562306a36Sopenharmony_ci orc->type = ORC_TYPE_UNDEFINED; 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci case UNWIND_HINT_TYPE_END_OF_STACK: 3862306a36Sopenharmony_ci orc->type = ORC_TYPE_END_OF_STACK; 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci case UNWIND_HINT_TYPE_CALL: 4162306a36Sopenharmony_ci orc->type = ORC_TYPE_CALL; 4262306a36Sopenharmony_ci break; 4362306a36Sopenharmony_ci case UNWIND_HINT_TYPE_REGS: 4462306a36Sopenharmony_ci orc->type = ORC_TYPE_REGS; 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci case UNWIND_HINT_TYPE_REGS_PARTIAL: 4762306a36Sopenharmony_ci orc->type = ORC_TYPE_REGS_PARTIAL; 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci default: 5062306a36Sopenharmony_ci WARN_INSN(insn, "unknown unwind hint type %d", cfi->type); 5162306a36Sopenharmony_ci return -1; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci orc->signal = cfi->signal; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci switch (cfi->cfa.base) { 5762306a36Sopenharmony_ci case CFI_SP: 5862306a36Sopenharmony_ci orc->sp_reg = ORC_REG_SP; 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci case CFI_SP_INDIRECT: 6162306a36Sopenharmony_ci orc->sp_reg = ORC_REG_SP_INDIRECT; 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci case CFI_BP: 6462306a36Sopenharmony_ci orc->sp_reg = ORC_REG_BP; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci case CFI_BP_INDIRECT: 6762306a36Sopenharmony_ci orc->sp_reg = ORC_REG_BP_INDIRECT; 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci case CFI_R10: 7062306a36Sopenharmony_ci orc->sp_reg = ORC_REG_R10; 7162306a36Sopenharmony_ci break; 7262306a36Sopenharmony_ci case CFI_R13: 7362306a36Sopenharmony_ci orc->sp_reg = ORC_REG_R13; 7462306a36Sopenharmony_ci break; 7562306a36Sopenharmony_ci case CFI_DI: 7662306a36Sopenharmony_ci orc->sp_reg = ORC_REG_DI; 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci case CFI_DX: 7962306a36Sopenharmony_ci orc->sp_reg = ORC_REG_DX; 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci default: 8262306a36Sopenharmony_ci WARN_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); 8362306a36Sopenharmony_ci return -1; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci switch (bp->base) { 8762306a36Sopenharmony_ci case CFI_UNDEFINED: 8862306a36Sopenharmony_ci orc->bp_reg = ORC_REG_UNDEFINED; 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci case CFI_CFA: 9162306a36Sopenharmony_ci orc->bp_reg = ORC_REG_PREV_SP; 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case CFI_BP: 9462306a36Sopenharmony_ci orc->bp_reg = ORC_REG_BP; 9562306a36Sopenharmony_ci break; 9662306a36Sopenharmony_ci default: 9762306a36Sopenharmony_ci WARN_INSN(insn, "unknown BP base reg %d", bp->base); 9862306a36Sopenharmony_ci return -1; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci orc->sp_offset = cfi->cfa.offset; 10262306a36Sopenharmony_ci orc->bp_offset = bp->offset; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int write_orc_entry(struct elf *elf, struct section *orc_sec, 10862306a36Sopenharmony_ci struct section *ip_sec, unsigned int idx, 10962306a36Sopenharmony_ci struct section *insn_sec, unsigned long insn_off, 11062306a36Sopenharmony_ci struct orc_entry *o) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci struct orc_entry *orc; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* populate ORC data */ 11562306a36Sopenharmony_ci orc = (struct orc_entry *)orc_sec->data->d_buf + idx; 11662306a36Sopenharmony_ci memcpy(orc, o, sizeof(*orc)); 11762306a36Sopenharmony_ci orc->sp_offset = bswap_if_needed(elf, orc->sp_offset); 11862306a36Sopenharmony_ci orc->bp_offset = bswap_if_needed(elf, orc->bp_offset); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* populate reloc for ip */ 12162306a36Sopenharmony_ci if (!elf_init_reloc_text_sym(elf, ip_sec, idx * sizeof(int), idx, 12262306a36Sopenharmony_ci insn_sec, insn_off)) 12362306a36Sopenharmony_ci return -1; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistruct orc_list_entry { 12962306a36Sopenharmony_ci struct list_head list; 13062306a36Sopenharmony_ci struct orc_entry orc; 13162306a36Sopenharmony_ci struct section *insn_sec; 13262306a36Sopenharmony_ci unsigned long insn_off; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_cistatic int orc_list_add(struct list_head *orc_list, struct orc_entry *orc, 13662306a36Sopenharmony_ci struct section *sec, unsigned long offset) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci struct orc_list_entry *entry = malloc(sizeof(*entry)); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (!entry) { 14162306a36Sopenharmony_ci WARN("malloc failed"); 14262306a36Sopenharmony_ci return -1; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci entry->orc = *orc; 14662306a36Sopenharmony_ci entry->insn_sec = sec; 14762306a36Sopenharmony_ci entry->insn_off = offset; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci list_add_tail(&entry->list, orc_list); 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic unsigned long alt_group_len(struct alt_group *alt_group) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci return alt_group->last_insn->offset + 15662306a36Sopenharmony_ci alt_group->last_insn->len - 15762306a36Sopenharmony_ci alt_group->first_insn->offset; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ciint orc_create(struct objtool_file *file) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct section *sec, *orc_sec; 16362306a36Sopenharmony_ci unsigned int nr = 0, idx = 0; 16462306a36Sopenharmony_ci struct orc_list_entry *entry; 16562306a36Sopenharmony_ci struct list_head orc_list; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci struct orc_entry null = { .type = ORC_TYPE_UNDEFINED }; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Build a deduplicated list of ORC entries: */ 17062306a36Sopenharmony_ci INIT_LIST_HEAD(&orc_list); 17162306a36Sopenharmony_ci for_each_sec(file, sec) { 17262306a36Sopenharmony_ci struct orc_entry orc, prev_orc = {0}; 17362306a36Sopenharmony_ci struct instruction *insn; 17462306a36Sopenharmony_ci bool empty = true; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (!sec->text) 17762306a36Sopenharmony_ci continue; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci sec_for_each_insn(file, sec, insn) { 18062306a36Sopenharmony_ci struct alt_group *alt_group = insn->alt_group; 18162306a36Sopenharmony_ci int i; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!alt_group) { 18462306a36Sopenharmony_ci if (init_orc_entry(&orc, insn->cfi, insn)) 18562306a36Sopenharmony_ci return -1; 18662306a36Sopenharmony_ci if (!memcmp(&prev_orc, &orc, sizeof(orc))) 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci if (orc_list_add(&orc_list, &orc, sec, 18962306a36Sopenharmony_ci insn->offset)) 19062306a36Sopenharmony_ci return -1; 19162306a36Sopenharmony_ci nr++; 19262306a36Sopenharmony_ci prev_orc = orc; 19362306a36Sopenharmony_ci empty = false; 19462306a36Sopenharmony_ci continue; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * Alternatives can have different stack layout 19962306a36Sopenharmony_ci * possibilities (but they shouldn't conflict). 20062306a36Sopenharmony_ci * Instead of traversing the instructions, use the 20162306a36Sopenharmony_ci * alt_group's flattened byte-offset-addressed CFI 20262306a36Sopenharmony_ci * array. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci for (i = 0; i < alt_group_len(alt_group); i++) { 20562306a36Sopenharmony_ci struct cfi_state *cfi = alt_group->cfi[i]; 20662306a36Sopenharmony_ci if (!cfi) 20762306a36Sopenharmony_ci continue; 20862306a36Sopenharmony_ci /* errors are reported on the original insn */ 20962306a36Sopenharmony_ci if (init_orc_entry(&orc, cfi, insn)) 21062306a36Sopenharmony_ci return -1; 21162306a36Sopenharmony_ci if (!memcmp(&prev_orc, &orc, sizeof(orc))) 21262306a36Sopenharmony_ci continue; 21362306a36Sopenharmony_ci if (orc_list_add(&orc_list, &orc, insn->sec, 21462306a36Sopenharmony_ci insn->offset + i)) 21562306a36Sopenharmony_ci return -1; 21662306a36Sopenharmony_ci nr++; 21762306a36Sopenharmony_ci prev_orc = orc; 21862306a36Sopenharmony_ci empty = false; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* Skip to the end of the alt_group */ 22262306a36Sopenharmony_ci insn = alt_group->last_insn; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* Add a section terminator */ 22662306a36Sopenharmony_ci if (!empty) { 22762306a36Sopenharmony_ci orc_list_add(&orc_list, &null, sec, sec->sh.sh_size); 22862306a36Sopenharmony_ci nr++; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci if (!nr) 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */ 23562306a36Sopenharmony_ci sec = find_section_by_name(file->elf, ".orc_unwind"); 23662306a36Sopenharmony_ci if (sec) { 23762306a36Sopenharmony_ci WARN("file already has .orc_unwind section, skipping"); 23862306a36Sopenharmony_ci return -1; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci orc_sec = elf_create_section(file->elf, ".orc_unwind", 24162306a36Sopenharmony_ci sizeof(struct orc_entry), nr); 24262306a36Sopenharmony_ci if (!orc_sec) 24362306a36Sopenharmony_ci return -1; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci sec = elf_create_section_pair(file->elf, ".orc_unwind_ip", sizeof(int), nr, nr); 24662306a36Sopenharmony_ci if (!sec) 24762306a36Sopenharmony_ci return -1; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* Write ORC entries to sections: */ 25062306a36Sopenharmony_ci list_for_each_entry(entry, &orc_list, list) { 25162306a36Sopenharmony_ci if (write_orc_entry(file->elf, orc_sec, sec, idx++, 25262306a36Sopenharmony_ci entry->insn_sec, entry->insn_off, 25362306a36Sopenharmony_ci &entry->orc)) 25462306a36Sopenharmony_ci return -1; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 259