1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2015 Josh Poimboeuf <jpoimboe@redhat.com>
7 * This file reads all the special sections which have alternate instructions
8 * which can be patched in or redirected to at runtime.
14 #include <arch/special.h>
15 #include <objtool/builtin.h>
16 #include <objtool/special.h>
17 #include <objtool/warn.h>
18 #include <objtool/endianness.h>
20 struct special_entry
{
22 bool group
, jump_or_nop
;
23 unsigned char size
, orig
, new;
24 unsigned char orig_len
, new_len
; /* group only */
25 unsigned char feature
; /* ALTERNATIVE macro CPU feature */
26 unsigned char key
; /* jump_label key */
29 static const struct special_entry entries
[] = {
31 .sec
= ".altinstructions",
33 .size
= ALT_ENTRY_SIZE
,
34 .orig
= ALT_ORIG_OFFSET
,
35 .orig_len
= ALT_ORIG_LEN_OFFSET
,
36 .new = ALT_NEW_OFFSET
,
37 .new_len
= ALT_NEW_LEN_OFFSET
,
38 .feature
= ALT_FEATURE_OFFSET
,
41 .sec
= "__jump_table",
43 .size
= JUMP_ENTRY_SIZE
,
44 .orig
= JUMP_ORIG_OFFSET
,
45 .new = JUMP_NEW_OFFSET
,
46 .key
= JUMP_KEY_OFFSET
,
50 .size
= EX_ENTRY_SIZE
,
51 .orig
= EX_ORIG_OFFSET
,
57 void __weak
arch_handle_alternative(unsigned short feature
, struct special_alt
*alt
)
61 static void reloc_to_sec_off(struct reloc
*reloc
, struct section
**sec
,
64 *sec
= reloc
->sym
->sec
;
65 *off
= reloc
->sym
->offset
+ reloc_addend(reloc
);
68 static int get_alt_entry(struct elf
*elf
, const struct special_entry
*entry
,
69 struct section
*sec
, int idx
,
70 struct special_alt
*alt
)
72 struct reloc
*orig_reloc
, *new_reloc
;
75 offset
= idx
* entry
->size
;
77 alt
->group
= entry
->group
;
78 alt
->jump_or_nop
= entry
->jump_or_nop
;
81 alt
->orig_len
= *(unsigned char *)(sec
->data
->d_buf
+ offset
+
83 alt
->new_len
= *(unsigned char *)(sec
->data
->d_buf
+ offset
+
87 orig_reloc
= find_reloc_by_dest(elf
, sec
, offset
+ entry
->orig
);
89 WARN_FUNC("can't find orig reloc", sec
, offset
+ entry
->orig
);
93 reloc_to_sec_off(orig_reloc
, &alt
->orig_sec
, &alt
->orig_off
);
96 unsigned short feature
;
98 feature
= bswap_if_needed(elf
,
99 *(unsigned short *)(sec
->data
->d_buf
+
102 arch_handle_alternative(feature
, alt
);
105 if (!entry
->group
|| alt
->new_len
) {
106 new_reloc
= find_reloc_by_dest(elf
, sec
, offset
+ entry
->new);
108 WARN_FUNC("can't find new reloc",
109 sec
, offset
+ entry
->new);
113 reloc_to_sec_off(new_reloc
, &alt
->new_sec
, &alt
->new_off
);
115 /* _ASM_EXTABLE_EX hack */
116 if (alt
->new_off
>= 0x7ffffff0)
117 alt
->new_off
-= 0x7ffffff0;
121 struct reloc
*key_reloc
;
123 key_reloc
= find_reloc_by_dest(elf
, sec
, offset
+ entry
->key
);
125 WARN_FUNC("can't find key reloc",
126 sec
, offset
+ entry
->key
);
129 alt
->key_addend
= reloc_addend(key_reloc
);
136 * Read all the special sections and create a list of special_alt structs which
137 * describe all the alternate instructions which can be patched in or
138 * redirected to at runtime.
140 int special_get_alts(struct elf
*elf
, struct list_head
*alts
)
142 const struct special_entry
*entry
;
144 unsigned int nr_entries
;
145 struct special_alt
*alt
;
148 INIT_LIST_HEAD(alts
);
150 for (entry
= entries
; entry
->sec
; entry
++) {
151 sec
= find_section_by_name(elf
, entry
->sec
);
155 if (sec
->sh
.sh_size
% entry
->size
!= 0) {
156 WARN("%s size not a multiple of %d",
157 sec
->name
, entry
->size
);
161 nr_entries
= sec
->sh
.sh_size
/ entry
->size
;
163 for (idx
= 0; idx
< nr_entries
; idx
++) {
164 alt
= malloc(sizeof(*alt
));
166 WARN("malloc failed");
169 memset(alt
, 0, sizeof(*alt
));
171 ret
= get_alt_entry(elf
, entry
, sec
, idx
, alt
);
177 list_add_tail(&alt
->list
, alts
);