1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "gtest/gtest.h"
18
19 #include <sys/mman.h>
20
21 #include <cstdint>
22 #include <cstring>
23
24 namespace {
25
26 // Flush cache and i-cache
ClearInsnCache(void * start,void * end)27 extern "C" void ClearInsnCache(void* start, void* end) {
28 // Only use __builtin___clear_cache with Clang or with GCC >= 4.3.0
29 __builtin___clear_cache(static_cast<char*>(start), static_cast<char*>(end));
30 }
31
32 } // namespace
33
34 extern "C" char PatchCodeInCurrentThreadHelper_begin;
35 extern "C" char PatchCodeInCurrentThreadHelper_end;
36 // By default android .text section including this snippet is not executable. We
37 // ensure it is position independent, so that we can copy it to a writable page,
38 // where it'll actually work. The only position dependent address of
39 // ClearInsnCache callback must be provided in r0.
40 asm(R"(
41 .globl PatchCodeInCurrentThreadHelper_begin
42 PatchCodeInCurrentThreadHelper_begin:
43 // Save link register and ClearInsnCache callback.
44 str x30, [sp, -16]!
45 mov x3, x0
46
47 // Facilitate caching of the result setting code.
48 mov x1, #1000
49 PatchCodeInCurrentThreadHelper_warmup_loop:
50 bl PatchCodeInCurrentThreadHelper_assign_result
51 subs x1, x1, #1
52 bne PatchCodeInCurrentThreadHelper_warmup_loop
53
54 // Overwrite bad-clobber with nop.
55 ldr w1, PatchCodeInCurrentThreadHelper_nop
56 adr x0, PatchCodeInCurrentThreadHelper_bad_clobber
57 str w1, [x0]
58 // Call ClearInsnCache. x0 is pointing at the overwritten instruction.
59 add x1, x0, 4
60 blr x3
61
62 // Final result assignment.
63 bl PatchCodeInCurrentThreadHelper_assign_result
64
65 ldr x30, [sp], 16
66 ret
67
68 PatchCodeInCurrentThreadHelper_assign_result:
69 mov x0, 42
70 PatchCodeInCurrentThreadHelper_bad_clobber:
71 mov x0, 21
72 ret
73
74 PatchCodeInCurrentThreadHelper_nop:
75 nop
76
77 .globl PatchCodeInCurrentThreadHelper_end
78 PatchCodeInCurrentThreadHelper_end:
79 )");
80
TEST(RuntimeCodePatching,PatchCodeInCurrentThread)81 TEST(RuntimeCodePatching, PatchCodeInCurrentThread) {
82 uint32_t* code = reinterpret_cast<uint32_t*>(
83 mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
84 memcpy(code,
85 &PatchCodeInCurrentThreadHelper_begin,
86 &PatchCodeInCurrentThreadHelper_end - &PatchCodeInCurrentThreadHelper_begin);
87 // ATTENTION: flush insn cache! Otherwise just mmaped page might remain cached with wrong prot!
88 ClearInsnCache(code, code + 1024);
89
90 auto Func = reinterpret_cast<uint64_t (*)(void*)>(code);
91 uint64_t result = Func(reinterpret_cast<void*>(ClearInsnCache));
92 EXPECT_EQ(result, 42U);
93
94 munmap(code, 4096);
95 }
96