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