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.
17 #include "arch_special.h"
19 struct special_entry
{
21 bool group
, jump_or_nop
;
22 unsigned char size
, orig
, new;
23 unsigned char orig_len
, new_len
; /* group only */
24 unsigned char feature
; /* ALTERNATIVE macro CPU feature */
27 struct special_entry entries
[] = {
29 .sec
= ".altinstructions",
31 .size
= ALT_ENTRY_SIZE
,
32 .orig
= ALT_ORIG_OFFSET
,
33 .orig_len
= ALT_ORIG_LEN_OFFSET
,
34 .new = ALT_NEW_OFFSET
,
35 .new_len
= ALT_NEW_LEN_OFFSET
,
36 .feature
= ALT_FEATURE_OFFSET
,
39 .sec
= "__jump_table",
41 .size
= JUMP_ENTRY_SIZE
,
42 .orig
= JUMP_ORIG_OFFSET
,
43 .new = JUMP_NEW_OFFSET
,
47 .size
= EX_ENTRY_SIZE
,
48 .orig
= EX_ORIG_OFFSET
,
54 void __weak
arch_handle_alternative(unsigned short feature
, struct special_alt
*alt
)
58 static int get_alt_entry(struct elf
*elf
, struct special_entry
*entry
,
59 struct section
*sec
, int idx
,
60 struct special_alt
*alt
)
62 struct reloc
*orig_reloc
, *new_reloc
;
65 offset
= idx
* entry
->size
;
67 alt
->group
= entry
->group
;
68 alt
->jump_or_nop
= entry
->jump_or_nop
;
71 alt
->orig_len
= *(unsigned char *)(sec
->data
->d_buf
+ offset
+
73 alt
->new_len
= *(unsigned char *)(sec
->data
->d_buf
+ offset
+
78 unsigned short feature
;
80 feature
= *(unsigned short *)(sec
->data
->d_buf
+ offset
+
82 arch_handle_alternative(feature
, alt
);
85 orig_reloc
= find_reloc_by_dest(elf
, sec
, offset
+ entry
->orig
);
87 WARN_FUNC("can't find orig reloc", sec
, offset
+ entry
->orig
);
90 if (orig_reloc
->sym
->type
!= STT_SECTION
) {
91 WARN_FUNC("don't know how to handle non-section reloc symbol %s",
92 sec
, offset
+ entry
->orig
, orig_reloc
->sym
->name
);
96 alt
->orig_sec
= orig_reloc
->sym
->sec
;
97 alt
->orig_off
= orig_reloc
->addend
;
99 if (!entry
->group
|| alt
->new_len
) {
100 new_reloc
= find_reloc_by_dest(elf
, sec
, offset
+ entry
->new);
102 WARN_FUNC("can't find new reloc",
103 sec
, offset
+ entry
->new);
107 alt
->new_sec
= new_reloc
->sym
->sec
;
108 alt
->new_off
= (unsigned int)new_reloc
->addend
;
110 /* _ASM_EXTABLE_EX hack */
111 if (alt
->new_off
>= 0x7ffffff0)
112 alt
->new_off
-= 0x7ffffff0;
119 * Read all the special sections and create a list of special_alt structs which
120 * describe all the alternate instructions which can be patched in or
121 * redirected to at runtime.
123 int special_get_alts(struct elf
*elf
, struct list_head
*alts
)
125 struct special_entry
*entry
;
127 unsigned int nr_entries
;
128 struct special_alt
*alt
;
131 INIT_LIST_HEAD(alts
);
133 for (entry
= entries
; entry
->sec
; entry
++) {
134 sec
= find_section_by_name(elf
, entry
->sec
);
138 if (sec
->len
% entry
->size
!= 0) {
139 WARN("%s size not a multiple of %d",
140 sec
->name
, entry
->size
);
144 nr_entries
= sec
->len
/ entry
->size
;
146 for (idx
= 0; idx
< nr_entries
; idx
++) {
147 alt
= malloc(sizeof(*alt
));
149 WARN("malloc failed");
152 memset(alt
, 0, sizeof(*alt
));
154 ret
= get_alt_entry(elf
, entry
, sec
, idx
, alt
);
158 list_add_tail(&alt
->list
, alts
);