native_bridge.cc revision 25bacb3ab71ee7e8289c76d0aa0c6473e47340e3
1/* 2 * Copyright (C) 2014 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 "nativebridge/native_bridge.h" 18 19#include <cutils/log.h> 20#include <dlfcn.h> 21#include <stdio.h> 22 23 24namespace android { 25 26// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks. 27static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf"; 28 29enum class NativeBridgeState { 30 kNotSetup, // Initial state. 31 kOpened, // After successful dlopen. 32 kInitialized, // After successful initialization. 33 kClosed // Closed or errors. 34}; 35 36static const char* kNotSetupString = "kNotSetup"; 37static const char* kOpenedString = "kOpened"; 38static const char* kInitializedString = "kInitialized"; 39static const char* kClosedString = "kClosed"; 40 41static const char* GetNativeBridgeStateString(NativeBridgeState state) { 42 switch (state) { 43 case NativeBridgeState::kNotSetup: 44 return kNotSetupString; 45 46 case NativeBridgeState::kOpened: 47 return kOpenedString; 48 49 case NativeBridgeState::kInitialized: 50 return kInitializedString; 51 52 case NativeBridgeState::kClosed: 53 return kClosedString; 54 } 55} 56 57// Current state of the native bridge. 58static NativeBridgeState state = NativeBridgeState::kNotSetup; 59 60// Whether we had an error at some point. 61static bool had_error = false; 62 63// Handle of the loaded library. 64static void* native_bridge_handle = nullptr; 65// Pointer to the callbacks. Available as soon as LoadNativeBridge succeeds, but only initialized 66// later. 67static NativeBridgeCallbacks* callbacks = nullptr; 68// Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge. 69static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr; 70 71// Characters allowed in a native bridge filename. The first character must 72// be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-]. 73static bool CharacterAllowed(char c, bool first) { 74 if (first) { 75 return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); 76 } else { 77 return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || 78 (c == '.') || (c == '_') || (c == '-'); 79 } 80} 81 82// We only allow simple names for the library. It is supposed to be a file in 83// /system/lib or /vendor/lib. Only allow a small range of characters, that is 84// names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z]. 85bool NativeBridgeNameAcceptable(const char* nb_library_filename) { 86 const char* ptr = nb_library_filename; 87 if (*ptr == 0) { 88 // Emptry string. Allowed, means no native bridge. 89 return true; 90 } else { 91 // First character must be [a-zA-Z]. 92 if (!CharacterAllowed(*ptr, true)) { 93 // Found an invalid fist character, don't accept. 94 ALOGE("Native bridge library %s has been rejected for first character %c", nb_library_filename, *ptr); 95 return false; 96 } else { 97 // For the rest, be more liberal. 98 ptr++; 99 while (*ptr != 0) { 100 if (!CharacterAllowed(*ptr, false)) { 101 // Found an invalid character, don't accept. 102 ALOGE("Native bridge library %s has been rejected for %c", nb_library_filename, *ptr); 103 return false; 104 } 105 ptr++; 106 } 107 } 108 return true; 109 } 110} 111 112bool LoadNativeBridge(const char* nb_library_filename, 113 const NativeBridgeRuntimeCallbacks* runtime_cbs) { 114 // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not 115 // multi-threaded, so we do not need locking here. 116 117 if (state != NativeBridgeState::kNotSetup) { 118 // Setup has been called before. Ignore this call. 119 ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.", 120 GetNativeBridgeStateString(state)); 121 // Note: counts as an error, even though the bridge may be functional. 122 had_error = true; 123 return false; 124 } 125 126 if (nb_library_filename == nullptr || *nb_library_filename == 0) { 127 state = NativeBridgeState::kClosed; 128 return true; 129 } else { 130 if (!NativeBridgeNameAcceptable(nb_library_filename)) { 131 state = NativeBridgeState::kClosed; 132 had_error = true; 133 } else { 134 // Try to open the library. 135 void* handle = dlopen(nb_library_filename, RTLD_LAZY); 136 if (handle != nullptr) { 137 callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle, 138 kNativeBridgeInterfaceSymbol)); 139 if (callbacks != nullptr) { 140 // Store the handle for later. 141 native_bridge_handle = handle; 142 } else { 143 dlclose(handle); 144 } 145 } 146 147 // Two failure conditions: could not find library (dlopen failed), or could not find native 148 // bridge interface (dlsym failed). Both are an error and close the native bridge. 149 if (callbacks == nullptr) { 150 had_error = true; 151 state = NativeBridgeState::kClosed; 152 } else { 153 runtime_callbacks = runtime_cbs; 154 state = NativeBridgeState::kOpened; 155 } 156 } 157 return state == NativeBridgeState::kOpened; 158 } 159} 160 161bool InitializeNativeBridge() { 162 // We expect only one place that calls InitializeNativeBridge: Runtime::DidForkFromZygote. At that 163 // point we are not multi-threaded, so we do not need locking here. 164 165 if (state == NativeBridgeState::kOpened) { 166 // Try to initialize. 167 if (callbacks->initialize(runtime_callbacks)) { 168 state = NativeBridgeState::kInitialized; 169 } else { 170 // Unload the library. 171 dlclose(native_bridge_handle); 172 had_error = true; 173 state = NativeBridgeState::kClosed; 174 } 175 } else { 176 had_error = true; 177 state = NativeBridgeState::kClosed; 178 } 179 180 return state == NativeBridgeState::kInitialized; 181} 182 183void UnloadNativeBridge() { 184 // We expect only one place that calls UnloadNativeBridge: Runtime::DidForkFromZygote. At that 185 // point we are not multi-threaded, so we do not need locking here. 186 187 switch(state) { 188 case NativeBridgeState::kOpened: 189 case NativeBridgeState::kInitialized: 190 // Unload. 191 dlclose(native_bridge_handle); 192 break; 193 194 case NativeBridgeState::kNotSetup: 195 // Not even set up. Error. 196 had_error = true; 197 break; 198 199 case NativeBridgeState::kClosed: 200 // Ignore. 201 break; 202 } 203 204 state = NativeBridgeState::kClosed; 205} 206 207bool NativeBridgeError() { 208 return had_error; 209} 210 211bool NativeBridgeAvailable() { 212 return state == NativeBridgeState::kOpened || state == NativeBridgeState::kInitialized; 213} 214 215bool NativeBridgeInitialized() { 216 // Calls of this are supposed to happen in a state where the native bridge is stable, i.e., after 217 // Runtime::DidForkFromZygote. In that case we do not need a lock. 218 return state == NativeBridgeState::kInitialized; 219} 220 221void* NativeBridgeLoadLibrary(const char* libpath, int flag) { 222 if (NativeBridgeInitialized()) { 223 return callbacks->loadLibrary(libpath, flag); 224 } 225 return nullptr; 226} 227 228void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, 229 uint32_t len) { 230 if (NativeBridgeInitialized()) { 231 return callbacks->getTrampoline(handle, name, shorty, len); 232 } 233 return nullptr; 234} 235 236bool NativeBridgeIsSupported(const char* libpath) { 237 if (NativeBridgeInitialized()) { 238 return callbacks->isSupported(libpath); 239 } 240 return false; 241} 242 243}; // namespace android 244