0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <stdlib.h>
0012 #include <string.h>
0013
0014 #include <arch/special.h>
0015 #include <objtool/builtin.h>
0016 #include <objtool/special.h>
0017 #include <objtool/warn.h>
0018 #include <objtool/endianness.h>
0019
0020 struct special_entry {
0021 const char *sec;
0022 bool group, jump_or_nop;
0023 unsigned char size, orig, new;
0024 unsigned char orig_len, new_len;
0025 unsigned char feature;
0026 unsigned char key;
0027 };
0028
0029 struct special_entry entries[] = {
0030 {
0031 .sec = ".altinstructions",
0032 .group = true,
0033 .size = ALT_ENTRY_SIZE,
0034 .orig = ALT_ORIG_OFFSET,
0035 .orig_len = ALT_ORIG_LEN_OFFSET,
0036 .new = ALT_NEW_OFFSET,
0037 .new_len = ALT_NEW_LEN_OFFSET,
0038 .feature = ALT_FEATURE_OFFSET,
0039 },
0040 {
0041 .sec = "__jump_table",
0042 .jump_or_nop = true,
0043 .size = JUMP_ENTRY_SIZE,
0044 .orig = JUMP_ORIG_OFFSET,
0045 .new = JUMP_NEW_OFFSET,
0046 .key = JUMP_KEY_OFFSET,
0047 },
0048 {
0049 .sec = "__ex_table",
0050 .size = EX_ENTRY_SIZE,
0051 .orig = EX_ORIG_OFFSET,
0052 .new = EX_NEW_OFFSET,
0053 },
0054 {},
0055 };
0056
0057 void __weak arch_handle_alternative(unsigned short feature, struct special_alt *alt)
0058 {
0059 }
0060
0061 static void reloc_to_sec_off(struct reloc *reloc, struct section **sec,
0062 unsigned long *off)
0063 {
0064 *sec = reloc->sym->sec;
0065 *off = reloc->sym->offset + reloc->addend;
0066 }
0067
0068 static int get_alt_entry(struct elf *elf, struct special_entry *entry,
0069 struct section *sec, int idx,
0070 struct special_alt *alt)
0071 {
0072 struct reloc *orig_reloc, *new_reloc;
0073 unsigned long offset;
0074
0075 offset = idx * entry->size;
0076
0077 alt->group = entry->group;
0078 alt->jump_or_nop = entry->jump_or_nop;
0079
0080 if (alt->group) {
0081 alt->orig_len = *(unsigned char *)(sec->data->d_buf + offset +
0082 entry->orig_len);
0083 alt->new_len = *(unsigned char *)(sec->data->d_buf + offset +
0084 entry->new_len);
0085 }
0086
0087 if (entry->feature) {
0088 unsigned short feature;
0089
0090 feature = bswap_if_needed(*(unsigned short *)(sec->data->d_buf +
0091 offset +
0092 entry->feature));
0093 arch_handle_alternative(feature, alt);
0094 }
0095
0096 orig_reloc = find_reloc_by_dest(elf, sec, offset + entry->orig);
0097 if (!orig_reloc) {
0098 WARN_FUNC("can't find orig reloc", sec, offset + entry->orig);
0099 return -1;
0100 }
0101
0102 reloc_to_sec_off(orig_reloc, &alt->orig_sec, &alt->orig_off);
0103
0104 if (!entry->group || alt->new_len) {
0105 new_reloc = find_reloc_by_dest(elf, sec, offset + entry->new);
0106 if (!new_reloc) {
0107 WARN_FUNC("can't find new reloc",
0108 sec, offset + entry->new);
0109 return -1;
0110 }
0111
0112 reloc_to_sec_off(new_reloc, &alt->new_sec, &alt->new_off);
0113
0114
0115 if (alt->new_off >= 0x7ffffff0)
0116 alt->new_off -= 0x7ffffff0;
0117 }
0118
0119 if (entry->key) {
0120 struct reloc *key_reloc;
0121
0122 key_reloc = find_reloc_by_dest(elf, sec, offset + entry->key);
0123 if (!key_reloc) {
0124 WARN_FUNC("can't find key reloc",
0125 sec, offset + entry->key);
0126 return -1;
0127 }
0128 alt->key_addend = key_reloc->addend;
0129 }
0130
0131 return 0;
0132 }
0133
0134
0135
0136
0137
0138
0139 int special_get_alts(struct elf *elf, struct list_head *alts)
0140 {
0141 struct special_entry *entry;
0142 struct section *sec;
0143 unsigned int nr_entries;
0144 struct special_alt *alt;
0145 int idx, ret;
0146
0147 INIT_LIST_HEAD(alts);
0148
0149 for (entry = entries; entry->sec; entry++) {
0150 sec = find_section_by_name(elf, entry->sec);
0151 if (!sec)
0152 continue;
0153
0154 if (sec->sh.sh_size % entry->size != 0) {
0155 WARN("%s size not a multiple of %d",
0156 sec->name, entry->size);
0157 return -1;
0158 }
0159
0160 nr_entries = sec->sh.sh_size / entry->size;
0161
0162 for (idx = 0; idx < nr_entries; idx++) {
0163 alt = malloc(sizeof(*alt));
0164 if (!alt) {
0165 WARN("malloc failed");
0166 return -1;
0167 }
0168 memset(alt, 0, sizeof(*alt));
0169
0170 ret = get_alt_entry(elf, entry, sec, idx, alt);
0171 if (ret > 0)
0172 continue;
0173 if (ret < 0)
0174 return ret;
0175
0176 list_add_tail(&alt->list, alts);
0177 }
0178 }
0179
0180 return 0;
0181 }