1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> 4 */ 5 6 #include <stdlib.h> 7 #include <string.h> 8 9 #include <linux/objtool_types.h> 10 #include <asm/orc_types.h> 11 12 #include <objtool/check.h> 13 #include <objtool/orc.h> 14 #include <objtool/warn.h> 15 16 struct orc_list_entry { 17 struct list_head list; 18 struct orc_entry orc; 19 struct section *insn_sec; 20 unsigned long insn_off; 21 }; 22 23 static int orc_list_add(struct list_head *orc_list, struct orc_entry *orc, 24 struct section *sec, unsigned long offset) 25 { 26 struct orc_list_entry *entry = malloc(sizeof(*entry)); 27 28 if (!entry) { 29 WARN("malloc failed"); 30 return -1; 31 } 32 33 entry->orc = *orc; 34 entry->insn_sec = sec; 35 entry->insn_off = offset; 36 37 list_add_tail(&entry->list, orc_list); 38 return 0; 39 } 40 41 static unsigned long alt_group_len(struct alt_group *alt_group) 42 { 43 return alt_group->last_insn->offset + 44 alt_group->last_insn->len - 45 alt_group->first_insn->offset; 46 } 47 48 int orc_create(struct objtool_file *file) 49 { 50 struct section *sec, *orc_sec; 51 unsigned int nr = 0, idx = 0; 52 struct orc_list_entry *entry; 53 struct list_head orc_list; 54 55 struct orc_entry null = { .type = ORC_TYPE_UNDEFINED }; 56 57 /* Build a deduplicated list of ORC entries: */ 58 INIT_LIST_HEAD(&orc_list); 59 for_each_sec(file->elf, sec) { 60 struct orc_entry orc, prev_orc = {0}; 61 struct instruction *insn; 62 bool empty = true; 63 64 if (!sec->text) 65 continue; 66 67 sec_for_each_insn(file, sec, insn) { 68 struct alt_group *alt_group = insn->alt_group; 69 int i; 70 71 if (!alt_group) { 72 if (init_orc_entry(&orc, insn->cfi, insn)) 73 return -1; 74 if (!memcmp(&prev_orc, &orc, sizeof(orc))) 75 continue; 76 if (orc_list_add(&orc_list, &orc, sec, 77 insn->offset)) 78 return -1; 79 nr++; 80 prev_orc = orc; 81 empty = false; 82 continue; 83 } 84 85 /* 86 * Alternatives can have different stack layout 87 * possibilities (but they shouldn't conflict). 88 * Instead of traversing the instructions, use the 89 * alt_group's flattened byte-offset-addressed CFI 90 * array. 91 */ 92 for (i = 0; i < alt_group_len(alt_group); i++) { 93 struct cfi_state *cfi = alt_group->cfi[i]; 94 if (!cfi) 95 continue; 96 /* errors are reported on the original insn */ 97 if (init_orc_entry(&orc, cfi, insn)) 98 return -1; 99 if (!memcmp(&prev_orc, &orc, sizeof(orc))) 100 continue; 101 if (orc_list_add(&orc_list, &orc, insn->sec, 102 insn->offset + i)) 103 return -1; 104 nr++; 105 prev_orc = orc; 106 empty = false; 107 } 108 109 /* Skip to the end of the alt_group */ 110 insn = alt_group->last_insn; 111 } 112 113 /* Add a section terminator */ 114 if (!empty) { 115 orc_list_add(&orc_list, &null, sec, sec->sh.sh_size); 116 nr++; 117 } 118 } 119 if (!nr) 120 return 0; 121 122 /* Create .orc_unwind, .orc_unwind_ip and .rela.orc_unwind_ip sections: */ 123 sec = find_section_by_name(file->elf, ".orc_unwind"); 124 if (sec) { 125 WARN("file already has .orc_unwind section, skipping"); 126 return -1; 127 } 128 orc_sec = elf_create_section(file->elf, ".orc_unwind", 129 nr * sizeof(struct orc_entry), 130 sizeof(struct orc_entry), 131 SHT_PROGBITS, 132 1, 133 SHF_ALLOC); 134 if (!orc_sec) 135 return -1; 136 137 sec = elf_create_section_pair(file->elf, ".orc_unwind_ip", sizeof(int), nr, nr); 138 if (!sec) 139 return -1; 140 141 /* Write ORC entries to sections: */ 142 list_for_each_entry(entry, &orc_list, list) { 143 if (write_orc_entry(file->elf, orc_sec, sec, idx++, 144 entry->insn_sec, entry->insn_off, 145 &entry->orc)) 146 return -1; 147 } 148 149 return 0; 150 } 151