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.
18 #define EX_ENTRY_SIZE 12
19 #define EX_ORIG_OFFSET 0
20 #define EX_NEW_OFFSET 4
22 #define JUMP_ENTRY_SIZE 16
23 #define JUMP_ORIG_OFFSET 0
24 #define JUMP_NEW_OFFSET 4
26 #define ALT_ENTRY_SIZE 13
27 #define ALT_ORIG_OFFSET 0
28 #define ALT_NEW_OFFSET 4
29 #define ALT_FEATURE_OFFSET 8
30 #define ALT_ORIG_LEN_OFFSET 10
31 #define ALT_NEW_LEN_OFFSET 11
33 #define X86_FEATURE_POPCNT (4*32+23)
34 #define X86_FEATURE_SMAP (9*32+20)
36 struct special_entry
{
38 bool group
, jump_or_nop
;
39 unsigned char size
, orig
, new;
40 unsigned char orig_len
, new_len
; /* group only */
41 unsigned char feature
; /* ALTERNATIVE macro CPU feature */
44 struct special_entry entries
[] = {
46 .sec
= ".altinstructions",
48 .size
= ALT_ENTRY_SIZE
,
49 .orig
= ALT_ORIG_OFFSET
,
50 .orig_len
= ALT_ORIG_LEN_OFFSET
,
51 .new = ALT_NEW_OFFSET
,
52 .new_len
= ALT_NEW_LEN_OFFSET
,
53 .feature
= ALT_FEATURE_OFFSET
,
56 .sec
= "__jump_table",
58 .size
= JUMP_ENTRY_SIZE
,
59 .orig
= JUMP_ORIG_OFFSET
,
60 .new = JUMP_NEW_OFFSET
,
64 .size
= EX_ENTRY_SIZE
,
65 .orig
= EX_ORIG_OFFSET
,
71 static int get_alt_entry(struct elf
*elf
, struct special_entry
*entry
,
72 struct section
*sec
, int idx
,
73 struct special_alt
*alt
)
75 struct rela
*orig_rela
, *new_rela
;
78 offset
= idx
* entry
->size
;
80 alt
->group
= entry
->group
;
81 alt
->jump_or_nop
= entry
->jump_or_nop
;
84 alt
->orig_len
= *(unsigned char *)(sec
->data
->d_buf
+ offset
+
86 alt
->new_len
= *(unsigned char *)(sec
->data
->d_buf
+ offset
+
91 unsigned short feature
;
93 feature
= *(unsigned short *)(sec
->data
->d_buf
+ offset
+
97 * It has been requested that we don't validate the !POPCNT
98 * feature path which is a "very very small percentage of
101 if (feature
== X86_FEATURE_POPCNT
)
102 alt
->skip_orig
= true;
105 * If UACCESS validation is enabled; force that alternative;
106 * otherwise force it the other way.
108 * What we want to avoid is having both the original and the
109 * alternative code flow at the same time, in that case we can
110 * find paths that see the STAC but take the NOP instead of
111 * CLAC and the other way around.
113 if (feature
== X86_FEATURE_SMAP
) {
115 alt
->skip_orig
= true;
117 alt
->skip_alt
= true;
121 orig_rela
= find_rela_by_dest(sec
, offset
+ entry
->orig
);
123 WARN_FUNC("can't find orig rela", sec
, offset
+ entry
->orig
);
126 if (orig_rela
->sym
->type
!= STT_SECTION
) {
127 WARN_FUNC("don't know how to handle non-section rela symbol %s",
128 sec
, offset
+ entry
->orig
, orig_rela
->sym
->name
);
132 alt
->orig_sec
= orig_rela
->sym
->sec
;
133 alt
->orig_off
= orig_rela
->addend
;
135 if (!entry
->group
|| alt
->new_len
) {
136 new_rela
= find_rela_by_dest(sec
, offset
+ entry
->new);
138 WARN_FUNC("can't find new rela",
139 sec
, offset
+ entry
->new);
143 alt
->new_sec
= new_rela
->sym
->sec
;
144 alt
->new_off
= (unsigned int)new_rela
->addend
;
146 /* _ASM_EXTABLE_EX hack */
147 if (alt
->new_off
>= 0x7ffffff0)
148 alt
->new_off
-= 0x7ffffff0;
155 * Read all the special sections and create a list of special_alt structs which
156 * describe all the alternate instructions which can be patched in or
157 * redirected to at runtime.
159 int special_get_alts(struct elf
*elf
, struct list_head
*alts
)
161 struct special_entry
*entry
;
163 unsigned int nr_entries
;
164 struct special_alt
*alt
;
167 INIT_LIST_HEAD(alts
);
169 for (entry
= entries
; entry
->sec
; entry
++) {
170 sec
= find_section_by_name(elf
, entry
->sec
);
174 if (sec
->len
% entry
->size
!= 0) {
175 WARN("%s size not a multiple of %d",
176 sec
->name
, entry
->size
);
180 nr_entries
= sec
->len
/ entry
->size
;
182 for (idx
= 0; idx
< nr_entries
; idx
++) {
183 alt
= malloc(sizeof(*alt
));
185 WARN("malloc failed");
188 memset(alt
, 0, sizeof(*alt
));
190 ret
= get_alt_entry(elf
, entry
, sec
, idx
, alt
);
194 list_add_tail(&alt
->list
, alts
);