1 //
2 // Copyright (C) 2017 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 #ifndef NATIVE_BRIDGE_SUPPORT_VDSO_INTERCEPTABLE_FUNCTIONS_H_
18 #define NATIVE_BRIDGE_SUPPORT_VDSO_INTERCEPTABLE_FUNCTIONS_H_
19 
20 #include <stdint.h>
21 
22 #include "native_bridge_support/vdso/vdso.h"
23 
24 // An app may patch symbols exported from NDK libraries (e.g. b/378772009). This effectively
25 // invalidates trampolines bound to such symbols. In addition invalidation usually affects the whole
26 // cache line so that unpatched functions adjacent to the patched one may lose trampoline
27 // connection too.
28 //
29 // As a workaround to this issue each symbol below has two entries: a regular exported symbol and a
30 // hidden stub. The regular symbol simply jumps to the stub which we bind to a trampoline. This way
31 // if the regular symbol is patched the stub still remains correctly connected to the trampoline.
32 // Since the stub is hidden it's unlikely that it'll be patched on purpose.
33 //
34 // When a symbol is patched the corresponding instruction cache invalidation instruction is
35 // issued on ARM and RISC-V. It usually invalidates the whole cache line so that unpatched functions
36 // adjacent to the patched one may also lose trampoline connection. Since currently regular and stub
37 // entries are interleaved we align them on cache line size (64 bytes) so that invalidations are
38 // isolated.
39 // TODO(b/379378784): This results in somewhat larger stubs binaries (<1Mb in total for all of
40 // them). If we combine regular and stub entries in two groups, we'll only need to ensure alignment
41 // at the start/end of the regular symbols group. Note, that we should leave enough code for
42 // patching to be successful. E.g. 8 bytes may not be enough to encode arbitrary 64-bit address,
43 // but 16 bytes should always be enough.
44 //
45 // As an optimization we keep regular symbols bound to trampolines as well, so that we don't need
46 // to translate their code unless and until it's invalidated.
47 
48 #if defined(__arm__)
49 
50 #define INTERCEPTABLE_STUB_ASM_FUNCTION(name)                                                      \
51   extern "C" void                                                                                  \
52       __attribute__((target("arm"), aligned(64), naked, __visibility__("hidden"))) name##_stub() { \
53     __asm__ __volatile__(                                                                          \
54         "ldr r3, =0\n"                                                                             \
55         "bx r3");                                                                                  \
56   }                                                                                                \
57                                                                                                    \
58   extern "C" __attribute__((target("arm"), aligned(64), naked)) void name() {                      \
59     __asm__ __volatile__("b " #name "_stub");                                                      \
60   }
61 
62 #elif defined(__aarch64__)
63 
64 #define INTERCEPTABLE_STUB_ASM_FUNCTION(name)                                                   \
65   extern "C" void __attribute__((aligned(64), naked, __visibility__("hidden"))) name##_stub() { \
66     /* TODO(b/232598137): maybe replace with "udf imm16" */                                     \
67     __asm__ __volatile__(                                                                       \
68         "ldr x3, =0\n"                                                                          \
69         "blr x3\n");                                                                            \
70   }                                                                                             \
71                                                                                                 \
72   extern "C" __attribute__((aligned(64), naked)) void name() {                                  \
73     __asm__ __volatile__("b " #name "_stub");                                                   \
74   }
75 
76 #elif defined(__riscv)
77 
78 #define INTERCEPTABLE_STUB_ASM_FUNCTION(name)                                                   \
79   extern "C" void __attribute__((aligned(64), naked, __visibility__("hidden"))) name##_stub() { \
80     __asm__ __volatile__("unimp\n");                                                            \
81   }                                                                                             \
82                                                                                                 \
83   extern "C" __attribute__((aligned(64), naked)) void name() {                                  \
84     __asm__ __volatile__("j " #name "_stub");                                                   \
85   }
86 
87 #else
88 
89 #error Unknown architecture, only riscv64, arm and aarch64 are supported.
90 
91 #endif
92 
93 #define DEFINE_INTERCEPTABLE_STUB_VARIABLE(name) uintptr_t name;
94 
95 #define INIT_INTERCEPTABLE_STUB_VARIABLE(library_name, name) \
96   native_bridge_intercept_symbol(&name, library_name, #name)
97 
98 #define DEFINE_INTERCEPTABLE_STUB_FUNCTION(name) INTERCEPTABLE_STUB_ASM_FUNCTION(name)
99 
100 #define INIT_INTERCEPTABLE_STUB_FUNCTION(library_name, name)                          \
101   native_bridge_intercept_symbol(reinterpret_cast<void*>(name), library_name, #name); \
102   native_bridge_intercept_symbol(reinterpret_cast<void*>(name##_stub), library_name, #name)
103 
104 #endif  // NATIVE_BRIDGE_SUPPORT_VDSO_INTERCEPTABLE_FUNCTIONS_H_
105