@@ -78,47 +78,76 @@ static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
78
78
}
79
79
80
80
/*
81
- * Turn on the call to ftrace_caller() in instrumented function
81
+ * Find the address the callsite must branch to in order to reach '*addr'.
82
+ *
83
+ * Due to the limited range of 'BL' instructions, modules may be placed too far
84
+ * away to branch directly and must use a PLT.
85
+ *
86
+ * Returns true when '*addr' contains a reachable target address, or has been
87
+ * modified to contain a PLT address. Returns false otherwise.
82
88
*/
83
- int ftrace_make_call (struct dyn_ftrace * rec , unsigned long addr )
89
+ static bool ftrace_find_callable_addr (struct dyn_ftrace * rec ,
90
+ struct module * mod ,
91
+ unsigned long * addr )
84
92
{
85
93
unsigned long pc = rec -> ip ;
86
- u32 old , new ;
87
- long offset = ( long ) pc - ( long ) addr ;
94
+ long offset = ( long ) * addr - ( long ) pc ;
95
+ struct plt_entry * plt ;
88
96
89
- if (offset < - SZ_128M || offset >= SZ_128M ) {
90
- struct module * mod ;
91
- struct plt_entry * plt ;
97
+ /*
98
+ * When the target is within range of the 'BL' instruction, use 'addr'
99
+ * as-is and branch to that directly.
100
+ */
101
+ if (offset >= - SZ_128M && offset < SZ_128M )
102
+ return true;
92
103
93
- if (!IS_ENABLED (CONFIG_ARM64_MODULE_PLTS ))
94
- return - EINVAL ;
104
+ /*
105
+ * When the target is outside of the range of a 'BL' instruction, we
106
+ * must use a PLT to reach it. We can only place PLTs for modules, and
107
+ * only when module PLT support is built-in.
108
+ */
109
+ if (!IS_ENABLED (CONFIG_ARM64_MODULE_PLTS ))
110
+ return false;
95
111
96
- /*
97
- * On kernels that support module PLTs, the offset between the
98
- * branch instruction and its target may legally exceed the
99
- * range of an ordinary relative 'bl' opcode. In this case, we
100
- * need to branch via a trampoline in the module.
101
- *
102
- * NOTE: __module_text_address() must be called with preemption
103
- * disabled, but we can rely on ftrace_lock to ensure that 'mod'
104
- * retains its validity throughout the remainder of this code.
105
- */
112
+ /*
113
+ * 'mod' is only set at module load time, but if we end up
114
+ * dealing with an out-of-range condition, we can assume it
115
+ * is due to a module being loaded far away from the kernel.
116
+ *
117
+ * NOTE: __module_text_address() must be called with preemption
118
+ * disabled, but we can rely on ftrace_lock to ensure that 'mod'
119
+ * retains its validity throughout the remainder of this code.
120
+ */
121
+ if (! mod ) {
106
122
preempt_disable ();
107
123
mod = __module_text_address (pc );
108
124
preempt_enable ();
125
+ }
109
126
110
- if (WARN_ON (!mod ))
111
- return - EINVAL ;
127
+ if (WARN_ON (!mod ))
128
+ return false ;
112
129
113
- plt = get_ftrace_plt (mod , addr );
114
- if (!plt ) {
115
- pr_err ("ftrace: no module PLT for %ps\n" , (void * )addr );
116
- return - EINVAL ;
117
- }
118
-
119
- addr = (unsigned long )plt ;
130
+ plt = get_ftrace_plt (mod , * addr );
131
+ if (!plt ) {
132
+ pr_err ("ftrace: no module PLT for %ps\n" , (void * )* addr );
133
+ return false;
120
134
}
121
135
136
+ * addr = (unsigned long )plt ;
137
+ return true;
138
+ }
139
+
140
+ /*
141
+ * Turn on the call to ftrace_caller() in instrumented function
142
+ */
143
+ int ftrace_make_call (struct dyn_ftrace * rec , unsigned long addr )
144
+ {
145
+ unsigned long pc = rec -> ip ;
146
+ u32 old , new ;
147
+
148
+ if (!ftrace_find_callable_addr (rec , NULL , & addr ))
149
+ return - EINVAL ;
150
+
122
151
old = aarch64_insn_gen_nop ();
123
152
new = aarch64_insn_gen_branch_imm (pc , addr , AARCH64_INSN_BRANCH_LINK );
124
153
@@ -132,6 +161,11 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
132
161
unsigned long pc = rec -> ip ;
133
162
u32 old , new ;
134
163
164
+ if (!ftrace_find_callable_addr (rec , NULL , & old_addr ))
165
+ return - EINVAL ;
166
+ if (!ftrace_find_callable_addr (rec , NULL , & addr ))
167
+ return - EINVAL ;
168
+
135
169
old = aarch64_insn_gen_branch_imm (pc , old_addr ,
136
170
AARCH64_INSN_BRANCH_LINK );
137
171
new = aarch64_insn_gen_branch_imm (pc , addr , AARCH64_INSN_BRANCH_LINK );
@@ -181,54 +215,15 @@ int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
181
215
unsigned long addr )
182
216
{
183
217
unsigned long pc = rec -> ip ;
184
- bool validate = true;
185
218
u32 old = 0 , new ;
186
- long offset = (long )pc - (long )addr ;
187
219
188
- if (offset < - SZ_128M || offset >= SZ_128M ) {
189
- u32 replaced ;
190
-
191
- if (!IS_ENABLED (CONFIG_ARM64_MODULE_PLTS ))
192
- return - EINVAL ;
193
-
194
- /*
195
- * 'mod' is only set at module load time, but if we end up
196
- * dealing with an out-of-range condition, we can assume it
197
- * is due to a module being loaded far away from the kernel.
198
- */
199
- if (!mod ) {
200
- preempt_disable ();
201
- mod = __module_text_address (pc );
202
- preempt_enable ();
203
-
204
- if (WARN_ON (!mod ))
205
- return - EINVAL ;
206
- }
207
-
208
- /*
209
- * The instruction we are about to patch may be a branch and
210
- * link instruction that was redirected via a PLT entry. In
211
- * this case, the normal validation will fail, but we can at
212
- * least check that we are dealing with a branch and link
213
- * instruction that points into the right module.
214
- */
215
- if (aarch64_insn_read ((void * )pc , & replaced ))
216
- return - EFAULT ;
217
-
218
- if (!aarch64_insn_is_bl (replaced ) ||
219
- !within_module (pc + aarch64_get_branch_offset (replaced ),
220
- mod ))
221
- return - EINVAL ;
222
-
223
- validate = false;
224
- } else {
225
- old = aarch64_insn_gen_branch_imm (pc , addr ,
226
- AARCH64_INSN_BRANCH_LINK );
227
- }
220
+ if (!ftrace_find_callable_addr (rec , mod , & addr ))
221
+ return - EINVAL ;
228
222
223
+ old = aarch64_insn_gen_branch_imm (pc , addr , AARCH64_INSN_BRANCH_LINK );
229
224
new = aarch64_insn_gen_nop ();
230
225
231
- return ftrace_modify_code (pc , old , new , validate );
226
+ return ftrace_modify_code (pc , old , new , true );
232
227
}
233
228
234
229
void arch_ftrace_update_code (int command )
0 commit comments