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