| 1 | |
| 2 | |
| 3 | * Copyright (C) 2017 Josh Poimboeuf <[email protected]>
|
| 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 |
|
| 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 |
|
| 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 |
|
| 110 | insn = alt_group->last_insn;
|
| 111 | }
|
| 112 |
|
| 113 |
|
| 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 |
|
| 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 |
|
| 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 | }
|