diff options
| -rw-r--r-- | include/nativebridge/native_bridge.h | 139 | ||||
| -rw-r--r-- | libnativebridge/native_bridge.cc | 117 | ||||
| -rw-r--r-- | libnativebridge/tests/Android.mk | 8 | ||||
| -rw-r--r-- | libnativebridge/tests/Android.nativebridge-dummy.mk | 38 | ||||
| -rw-r--r-- | libnativebridge/tests/DummyNativeBridge3.cpp | 120 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp | 40 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridge3GetError_test.cpp | 39 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridge3InitNamespace_test.cpp | 39 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp | 39 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp | 39 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp | 39 | ||||
| -rw-r--r-- | libnativebridge/tests/NativeBridgeTest.h | 2 | ||||
| -rw-r--r-- | libnativeloader/Android.bp | 1 | ||||
| -rw-r--r-- | libnativeloader/include/nativeloader/native_loader.h | 7 | ||||
| -rw-r--r-- | libnativeloader/native_loader.cpp | 246 |
15 files changed, 845 insertions, 68 deletions
diff --git a/include/nativebridge/native_bridge.h b/include/nativebridge/native_bridge.h index 18300bc87..26556f048 100644 --- a/include/nativebridge/native_bridge.h +++ b/include/nativebridge/native_bridge.h @@ -62,12 +62,19 @@ bool NativeBridgeAvailable(); bool NativeBridgeInitialized(); // Load a shared library that is supported by the native bridge. +// +// Starting with v3, NativeBridge has two scenarios: with/without namespace. +// Use NativeBridgeLoadLibraryExt() instead in namespace scenario. void* NativeBridgeLoadLibrary(const char* libpath, int flag); // Get a native bridge trampoline for specified native method. void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len); -// True if native library is valid and is for an ABI that is supported by native bridge. +// True if native library paths are valid and is for an ABI that is supported by native bridge. +// The *libpath* must point to a library. +// +// Starting with v3, NativeBridge has two scenarios: with/without namespace. +// Use NativeBridgeIsPathSupported() instead in namespace scenario. bool NativeBridgeIsSupported(const char* libpath); // Returns the version number of the native bridge. This information is available after a @@ -91,6 +98,48 @@ bool NativeBridgeError(); // This functionality is exposed mainly for testing. bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename); +// Decrements the reference count on the dynamic library handler. If the reference count drops +// to zero then the dynamic library is unloaded. +int NativeBridgeUnloadLibrary(void* handle); + +// Get last error message of native bridge when fail to load library or search symbol. +// This is reflection of dlerror() for native bridge. +char* NativeBridgeGetError(); + +struct native_bridge_namespace_t; + +// True if native library paths are valid and is for an ABI that is supported by native bridge. +// Different from NativeBridgeIsSupported(), the *path* here must be a directory containing +// libraries of an ABI. +// +// Starting with v3, NativeBridge has two scenarios: with/without namespace. +// Use NativeBridgeIsSupported() instead in non-namespace scenario. +bool NativeBridgeIsPathSupported(const char* path); + +// Initializes public and anonymous namespace at native bridge side. +// +// Starting with v3, NativeBridge has two scenarios: with/without namespace. +// Should not use in non-namespace scenario. +bool NativeBridgeInitNamespace(const char* public_ns_sonames, + const char* anon_ns_library_path); + +// Create a namespace and pass the key of related namespaces to native bridge. +// +// Starting with v3, NativeBridge has two scenarios: with/without namespace. +// Should not use in non-namespace scenario. +native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + native_bridge_namespace_t* parent_ns); + +// Load a shared library with namespace key that is supported by the native bridge. +// +// Starting with v3, NativeBridge has two scenarios: with/without namespace. +// Use NativeBridgeLoadLibrary() instead in non-namespace scenario. +void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns); + // Native bridge interfaces to runtime. struct NativeBridgeCallbacks { // Version number of the interface. @@ -114,6 +163,9 @@ struct NativeBridgeCallbacks { // flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h // Returns: // The opaque handle of the shared library if sucessful, otherwise NULL + // + // Starting with v3, NativeBridge has two scenarios: with/without namespace. + // Use loadLibraryExt instead in namespace scenario. void* (*loadLibrary)(const char* libpath, int flag); // Get a native bridge trampoline for specified native method. The trampoline has same @@ -133,6 +185,9 @@ struct NativeBridgeCallbacks { // libpath [IN] path to the shared library // Returns: // TRUE if library is supported by native bridge, FALSE otherwise + // + // Starting with v3, NativeBridge has two scenarios: with/without namespace. + // Use isPathSupported instead in namespace scenario. bool (*isSupported)(const char* libpath); // Provide environment values required by the app running with native bridge according to the @@ -169,6 +224,88 @@ struct NativeBridgeCallbacks { // runtime. // Otherwise, a pointer to the signal handler. NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal); + + // Added callbacks in version 3. + + // Decrements the reference count on the dynamic library handler. If the reference count drops + // to zero then the dynamic library is unloaded. + // + // Parameters: + // handle [IN] the handler of a dynamic library. + // + // Returns: + // 0 on success, and nonzero on error. + int (*unloadLibrary)(void* handle); + + // Dump the last failure message of native bridge when fail to load library or search symbol. + // + // Parameters: + // + // Returns: + // A string describing the most recent error that occurred when load library + // or lookup symbol via native bridge. + char* (*getError)(); + + // Check whether library paths are supported by native bridge. + // + // Parameters: + // library_path [IN] search paths for native libraries (directories separated by ':') + // Returns: + // TRUE if libraries within search paths are supported by native bridge, FALSE otherwise + // + // Starting with v3, NativeBridge has two scenarios: with/without namespace. + // Use isSupported instead in non-namespace scenario. + bool (*isPathSupported)(const char* library_path); + + // Initializes anonymous namespace at native bridge side and pass the key of + // two namespaces(default and anonymous) owned by dynamic linker to native bridge. + // + // Parameters: + // public_ns_sonames [IN] the name of "public" libraries. + // anon_ns_library_path [IN] the library search path of (anonymous) namespace. + // Returns: + // true if the pass is ok. + // Otherwise, false. + // + // Starting with v3, NativeBridge has two scenarios: with/without namespace. + // Should not use in non-namespace scenario. + bool (*initNamespace)(const char* public_ns_sonames, + const char* anon_ns_library_path); + + + // Create a namespace and pass the key of releated namespaces to native bridge. + // + // Parameters: + // name [IN] the name of the namespace. + // ld_library_path [IN] the first set of library search paths of the namespace. + // default_library_path [IN] the second set of library search path of the namespace. + // type [IN] the attribute of the namespace. + // permitted_when_isolated_path [IN] the permitted path for isolated namespace(if it is). + // parent_ns [IN] the pointer of the parent namespace to be inherited from. + // Returns: + // native_bridge_namespace_t* for created namespace or nullptr in the case of error. + // + // Starting with v3, NativeBridge has two scenarios: with/without namespace. + // Should not use in non-namespace scenario. + native_bridge_namespace_t* (*createNamespace)(const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + native_bridge_namespace_t* parent_ns); + + // Load a shared library within a namespace. + // + // Parameters: + // libpath [IN] path to the shared library + // flag [IN] the stardard RTLD_XXX defined in bionic dlfcn.h + // ns [IN] the pointer of the namespace in which the library should be loaded. + // Returns: + // The opaque handle of the shared library if sucessful, otherwise NULL + // + // Starting with v3, NativeBridge has two scenarios: with/without namespace. + // Use loadLibrary instead in non-namespace scenario. + void* (*loadLibraryExt)(const char* libpath, int flag, native_bridge_namespace_t* ns); }; // Runtime interfaces to native bridge. diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc index 32b2d27db..43e6c0a5b 100644 --- a/libnativebridge/native_bridge.cc +++ b/libnativebridge/native_bridge.cc @@ -80,6 +80,19 @@ static const char* GetNativeBridgeStateString(NativeBridgeState state) { // Current state of the native bridge. static NativeBridgeState state = NativeBridgeState::kNotSetup; +// The version of NativeBridge implementation. +// Different Nativebridge interface needs the service of different version of +// Nativebridge implementation. +// Used by isCompatibleWith() which is introduced in v2. +enum NativeBridgeImplementationVersion { + // first version, not used. + DEFAULT_VERSION = 1, + // The version which signal semantic is introduced. + SIGNAL_VERSION = 2, + // The version which namespace semantic is introduced. + NAMESPACE_VERSION = 3, +}; + // Whether we had an error at some point. static bool had_error = false; @@ -100,8 +113,6 @@ static char* app_code_cache_dir = nullptr; // and hard code the directory name again here. static constexpr const char* kCodeCacheDir = "code_cache"; -static constexpr uint32_t kLibNativeBridgeVersion = 2; - // Characters allowed in a native bridge filename. The first character must // be in [a-zA-Z] (expected 'l' for "libx"). The rest must be in [a-zA-Z0-9._-]. static bool CharacterAllowed(char c, bool first) { @@ -152,19 +163,18 @@ bool NativeBridgeNameAcceptable(const char* nb_library_filename) { } } -static bool VersionCheck(const NativeBridgeCallbacks* cb) { +// The policy of invoking Nativebridge changed in v3 with/without namespace. +// Suggest Nativebridge implementation not maintain backward-compatible. +static bool isCompatibleWith(const uint32_t version) { // Libnativebridge is now designed to be forward-compatible. So only "0" is an unsupported // version. - if (cb == nullptr || cb->version == 0) { + if (callbacks == nullptr || callbacks->version == 0 || version == 0) { return false; } // If this is a v2+ bridge, it may not be forwards- or backwards-compatible. Check. - if (cb->version >= 2) { - if (!callbacks->isCompatibleWith(kLibNativeBridgeVersion)) { - // TODO: Scan which version is supported, and fall back to handle it. - return false; - } + if (callbacks->version >= SIGNAL_VERSION) { + return callbacks->isCompatibleWith(version); } return true; @@ -205,7 +215,7 @@ bool LoadNativeBridge(const char* nb_library_filename, callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle, kNativeBridgeInterfaceSymbol)); if (callbacks != nullptr) { - if (VersionCheck(callbacks)) { + if (isCompatibleWith(NAMESPACE_VERSION)) { // Store the handle for later. native_bridge_handle = handle; } else { @@ -520,8 +530,91 @@ uint32_t NativeBridgeGetVersion() { } NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) { - if (NativeBridgeInitialized() && callbacks->version >= 2) { - return callbacks->getSignalHandler(signal); + if (NativeBridgeInitialized()) { + if (isCompatibleWith(SIGNAL_VERSION)) { + return callbacks->getSignalHandler(signal); + } else { + ALOGE("not compatible with version %d, cannot get signal handler", SIGNAL_VERSION); + } + } + return nullptr; +} + +int NativeBridgeUnloadLibrary(void* handle) { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + return callbacks->unloadLibrary(handle); + } else { + ALOGE("not compatible with version %d, cannot unload library", NAMESPACE_VERSION); + } + } + return -1; +} + +char* NativeBridgeGetError() { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + return callbacks->getError(); + } else { + ALOGE("not compatible with version %d, cannot get message", NAMESPACE_VERSION); + } + } + return nullptr; +} + +bool NativeBridgeIsPathSupported(const char* path) { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + return callbacks->isPathSupported(path); + } else { + ALOGE("not compatible with version %d, cannot check via library path", NAMESPACE_VERSION); + } + } + return false; +} + +bool NativeBridgeInitNamespace(const char* public_ns_sonames, + const char* anon_ns_library_path) { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + return callbacks->initNamespace(public_ns_sonames, anon_ns_library_path); + } else { + ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION); + } + } + + return false; +} + +native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + native_bridge_namespace_t* parent_ns) { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + return callbacks->createNamespace(name, + ld_library_path, + default_library_path, + type, + permitted_when_isolated_path, + parent_ns); + } else { + ALOGE("not compatible with version %d, cannot create namespace %s", NAMESPACE_VERSION, name); + } + } + + return nullptr; +} + +void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + return callbacks->loadLibraryExt(libpath, flag, ns); + } else { + ALOGE("not compatible with version %d, cannot load library in namespace", NAMESPACE_VERSION); + } } return nullptr; } diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk index 5ad1569ed..4c3e86298 100644 --- a/libnativebridge/tests/Android.mk +++ b/libnativebridge/tests/Android.mk @@ -20,7 +20,13 @@ test_src_files := \ PreInitializeNativeBridgeFail2_test.cpp \ ReSetupNativeBridge_test.cpp \ UnavailableNativeBridge_test.cpp \ - ValidNameNativeBridge_test.cpp + ValidNameNativeBridge_test.cpp \ + NativeBridge3UnloadLibrary_test.cpp \ + NativeBridge3GetError_test.cpp \ + NativeBridge3IsPathSupported_test.cpp \ + NativeBridge3InitNamespace_test.cpp \ + NativeBridge3CreateNamespace_test.cpp \ + NativeBridge3LoadLibraryExt_test.cpp shared_libraries := \ diff --git a/libnativebridge/tests/Android.nativebridge-dummy.mk b/libnativebridge/tests/Android.nativebridge-dummy.mk index e556f805f..2d78be0a1 100644 --- a/libnativebridge/tests/Android.nativebridge-dummy.mk +++ b/libnativebridge/tests/Android.nativebridge-dummy.mk @@ -68,3 +68,41 @@ LOCAL_LDFLAGS := -ldl LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) + + +# v3. + +NATIVE_BRIDGE3_COMMON_SRC_FILES := \ + DummyNativeBridge3.cpp + +# Shared library for target +# ======================================================== +include $(CLEAR_VARS) + +LOCAL_MODULE:= libnativebridge3-dummy + +LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES) +LOCAL_CLANG := true +LOCAL_CFLAGS += -Werror -Wall +LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected +LOCAL_LDFLAGS := -ldl +LOCAL_MULTILIB := both + +include $(BUILD_SHARED_LIBRARY) + +# Shared library for host +# ======================================================== +include $(CLEAR_VARS) + +LOCAL_MODULE:= libnativebridge3-dummy + +LOCAL_SRC_FILES:= $(NATIVE_BRIDGE3_COMMON_SRC_FILES) +LOCAL_CLANG := true +LOCAL_CFLAGS += -Werror -Wall +LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected +LOCAL_LDFLAGS := -ldl +LOCAL_MULTILIB := both + +include $(BUILD_HOST_SHARED_LIBRARY) + + diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp new file mode 100644 index 000000000..c538fa071 --- /dev/null +++ b/libnativebridge/tests/DummyNativeBridge3.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// A dummy implementation of the native-bridge interface. + +#include "nativebridge/native_bridge.h" + +#include <signal.h> + +// NativeBridgeCallbacks implementations +extern "C" bool native_bridge3_initialize( + const android::NativeBridgeRuntimeCallbacks* /* art_cbs */, + const char* /* app_code_cache_dir */, + const char* /* isa */) { + return true; +} + +extern "C" void* native_bridge3_loadLibrary(const char* /* libpath */, int /* flag */) { + return nullptr; +} + +extern "C" void* native_bridge3_getTrampoline(void* /* handle */, const char* /* name */, + const char* /* shorty */, uint32_t /* len */) { + return nullptr; +} + +extern "C" bool native_bridge3_isSupported(const char* /* libpath */) { + return false; +} + +extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge3_getAppEnv( + const char* /* abi */) { + return nullptr; +} + +extern "C" bool native_bridge3_isCompatibleWith(uint32_t version) { + // For testing, allow 1-3, but disallow 4+. + return version <= 3; +} + +static bool native_bridge3_dummy_signal_handler(int, siginfo_t*, void*) { + // TODO: Implement something here. We'd either have to have a death test with a log here, or + // we'd have to be able to resume after the faulting instruction... + return true; +} + +extern "C" android::NativeBridgeSignalHandlerFn native_bridge3_getSignalHandler(int signal) { + if (signal == SIGSEGV) { + return &native_bridge3_dummy_signal_handler; + } + return nullptr; +} + +extern "C" int native_bridge3_unloadLibrary(void* /* handle */) { + return 0; +} + +extern "C" char* native_bridge3_getError() { + return nullptr; +} + +extern "C" bool native_bridge3_isPathSupported(const char* /* path */) { + return true; +} + +extern "C" bool native_bridge3_initNamespace(const char* /* public_ns_sonames */, + const char* /* anon_ns_library_path */) { + return true; +} + +extern "C" android::native_bridge_namespace_t* +native_bridge3_createNamespace(const char* /* name */, + const char* /* ld_library_path */, + const char* /* default_library_path */, + uint64_t /* type */, + const char* /* permitted_when_isolated_path */, + android::native_bridge_namespace_t* /* parent_ns */) { + return nullptr; +} + +extern "C" void* native_bridge3_loadLibraryExt(const char* /* libpath */, + int /* flag */, + android::native_bridge_namespace_t* /* ns */) { + return nullptr; +} + + +android::NativeBridgeCallbacks NativeBridgeItf { + // v1 + .version = 3, + .initialize = &native_bridge3_initialize, + .loadLibrary = &native_bridge3_loadLibrary, + .getTrampoline = &native_bridge3_getTrampoline, + .isSupported = &native_bridge3_isSupported, + .getAppEnv = &native_bridge3_getAppEnv, + // v2 + .isCompatibleWith = &native_bridge3_isCompatibleWith, + .getSignalHandler = &native_bridge3_getSignalHandler, + // v3 + .unloadLibrary = &native_bridge3_unloadLibrary, + .getError = &native_bridge3_getError, + .isPathSupported = &native_bridge3_isPathSupported, + .initNamespace = &native_bridge3_initNamespace, + .createNamespace = &native_bridge3_createNamespace, + .loadLibraryExt = &native_bridge3_loadLibraryExt +}; + diff --git a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp new file mode 100644 index 000000000..668d9428a --- /dev/null +++ b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NativeBridgeTest.h" + +namespace android { + +constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so"; + +TEST_F(NativeBridgeTest, V3_CreateNamespace) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + ASSERT_EQ(3U, NativeBridgeGetVersion()); + ASSERT_EQ(nullptr, NativeBridgeCreateNamespace(nullptr, nullptr, nullptr, + 0, nullptr, nullptr)); + + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridge3GetError_test.cpp b/libnativebridge/tests/NativeBridge3GetError_test.cpp new file mode 100644 index 000000000..0b9f5821f --- /dev/null +++ b/libnativebridge/tests/NativeBridge3GetError_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NativeBridgeTest.h" + +namespace android { + +constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so"; + +TEST_F(NativeBridgeTest, V3_GetError) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + ASSERT_EQ(3U, NativeBridgeGetVersion()); + ASSERT_EQ(nullptr, NativeBridgeGetError()); + + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp new file mode 100644 index 000000000..ae0fd2b02 --- /dev/null +++ b/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NativeBridgeTest.h" + +namespace android { + +constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so"; + +TEST_F(NativeBridgeTest, V3_InitNamespace) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + ASSERT_EQ(3U, NativeBridgeGetVersion()); + ASSERT_EQ(true, NativeBridgeInitNamespace(nullptr, nullptr)); + + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp new file mode 100644 index 000000000..325e40b40 --- /dev/null +++ b/libnativebridge/tests/NativeBridge3IsPathSupported_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NativeBridgeTest.h" + +namespace android { + +constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so"; + +TEST_F(NativeBridgeTest, V3_IsPathSupported) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + ASSERT_EQ(3U, NativeBridgeGetVersion()); + ASSERT_EQ(true, NativeBridgeIsPathSupported(nullptr)); + + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp new file mode 100644 index 000000000..4caeb4481 --- /dev/null +++ b/libnativebridge/tests/NativeBridge3LoadLibraryExt_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NativeBridgeTest.h" + +namespace android { + +constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so"; + +TEST_F(NativeBridgeTest, V3_LoadLibraryExt) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + ASSERT_EQ(3U, NativeBridgeGetVersion()); + ASSERT_EQ(nullptr, NativeBridgeLoadLibraryExt(nullptr, 0, nullptr)); + + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp new file mode 100644 index 000000000..93a979c0a --- /dev/null +++ b/libnativebridge/tests/NativeBridge3UnloadLibrary_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "NativeBridgeTest.h" + +namespace android { + +constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so"; + +TEST_F(NativeBridgeTest, V3_UnloadLibrary) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary3, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + ASSERT_EQ(3U, NativeBridgeGetVersion()); + ASSERT_EQ(0, NativeBridgeUnloadLibrary(nullptr)); + + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h index d48942009..0f99816c9 100644 --- a/libnativebridge/tests/NativeBridgeTest.h +++ b/libnativebridge/tests/NativeBridgeTest.h @@ -25,6 +25,8 @@ constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so"; constexpr const char* kCodeCache = "./code_cache"; constexpr const char* kCodeCacheStatFail = "./code_cache/temp"; +constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so"; +constexpr const char* kNativeBridgeLibrary3 = "libnativebridge3-dummy.so"; namespace android { diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp index 30531bcbd..9d3389958 100644 --- a/libnativeloader/Android.bp +++ b/libnativeloader/Android.bp @@ -8,6 +8,7 @@ cc_library { "libnativehelper", "liblog", "libcutils", + "libnativebridge", ], static_libs: ["libbase"], target: { diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h index 2a6aaec50..99ae3a759 100644 --- a/libnativeloader/include/nativeloader/native_loader.h +++ b/libnativeloader/include/nativeloader/native_loader.h @@ -19,6 +19,7 @@ #include "jni.h" #include <stdint.h> +#include <string> #if defined(__ANDROID__) #include <android/dlext.h> #endif @@ -41,10 +42,12 @@ void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader, - jstring library_path); + jstring library_path, + bool* needs_native_bridge, + std::string* error_msg); __attribute__((visibility("default"))) -bool CloseNativeLibrary(void* handle); +bool CloseNativeLibrary(void* handle, const bool needs_native_bridge); #if defined(__ANDROID__) // Look up linker namespace by class_loader. Returns nullptr if diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp index 3a6e54d82..fb95cb63d 100644 --- a/libnativeloader/native_loader.cpp +++ b/libnativeloader/native_loader.cpp @@ -24,6 +24,7 @@ #include "android/log.h" #include "cutils/properties.h" #endif +#include "nativebridge/native_bridge.h" #include <algorithm> #include <vector> @@ -34,11 +35,53 @@ #include <android-base/macros.h> #include <android-base/strings.h> +#define CHECK(predicate) LOG_ALWAYS_FATAL_IF(!(predicate),\ + "%s:%d: %s CHECK '" #predicate "' failed.",\ + __FILE__, __LINE__, __FUNCTION__) + namespace android { #if defined(__ANDROID__) -static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot = "/etc/public.libraries.txt"; -static constexpr const char* kPublicNativeLibrariesVendorConfig = "/vendor/etc/public.libraries.txt"; +class NativeLoaderNamespace { + public: + NativeLoaderNamespace() + : android_ns_(nullptr), native_bridge_ns_(nullptr) { } + + explicit NativeLoaderNamespace(android_namespace_t* ns) + : android_ns_(ns), native_bridge_ns_(nullptr) { } + + explicit NativeLoaderNamespace(native_bridge_namespace_t* ns) + : android_ns_(nullptr), native_bridge_ns_(ns) { } + + NativeLoaderNamespace(NativeLoaderNamespace&& that) = default; + NativeLoaderNamespace(const NativeLoaderNamespace& that) = default; + + NativeLoaderNamespace& operator=(const NativeLoaderNamespace& that) = default; + + android_namespace_t* get_android_ns() const { + CHECK(native_bridge_ns_ == nullptr); + return android_ns_; + } + + native_bridge_namespace_t* get_native_bridge_ns() const { + CHECK(android_ns_ == nullptr); + return native_bridge_ns_; + } + + bool is_android_namespace() const { + return native_bridge_ns_ == nullptr; + } + + private: + // Only one of them can be not null + android_namespace_t* android_ns_; + native_bridge_namespace_t* native_bridge_ns_; +}; + +static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot = + "/etc/public.libraries.txt"; +static constexpr const char* kPublicNativeLibrariesVendorConfig = + "/vendor/etc/public.libraries.txt"; // (http://b/27588281) This is a workaround for apps using custom classloaders and calling // System.load() with an absolute path which is outside of the classloader library search path. @@ -55,11 +98,13 @@ class LibraryNamespaces { public: LibraryNamespaces() : initialized_(false) { } - android_namespace_t* Create(JNIEnv* env, - jobject class_loader, - bool is_shared, - jstring java_library_path, - jstring java_permitted_path) { + bool Create(JNIEnv* env, + jobject class_loader, + bool is_shared, + jstring java_library_path, + jstring java_permitted_path, + NativeLoaderNamespace* ns, + std::string* error_msg) { std::string library_path; // empty string by default. if (java_library_path != nullptr) { @@ -82,13 +127,13 @@ class LibraryNamespaces { } } - if (!initialized_ && !InitPublicNamespace(library_path.c_str())) { - return nullptr; + if (!initialized_ && !InitPublicNamespace(library_path.c_str(), error_msg)) { + return false; } - android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader); + bool found = FindNamespaceByClassLoader(env, class_loader, nullptr); - LOG_ALWAYS_FATAL_IF(ns != nullptr, + LOG_ALWAYS_FATAL_IF(found, "There is already a namespace associated with this classloader"); uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED; @@ -96,28 +141,66 @@ class LibraryNamespaces { namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED; } - android_namespace_t* parent_ns = FindParentNamespaceByClassLoader(env, class_loader); + NativeLoaderNamespace parent_ns; + bool found_parent_namespace = FindParentNamespaceByClassLoader(env, class_loader, &parent_ns); - ns = android_create_namespace("classloader-namespace", - nullptr, - library_path.c_str(), - namespace_type, - permitted_path.c_str(), - parent_ns); + bool is_native_bridge = false; + + if (found_parent_namespace) { + is_native_bridge = !parent_ns.is_android_namespace(); + } else if (!library_path.empty()) { + is_native_bridge = NativeBridgeIsPathSupported(library_path.c_str()); + } + + NativeLoaderNamespace native_loader_ns; + if (!is_native_bridge) { + android_namespace_t* ns = android_create_namespace("classloader-namespace", + nullptr, + library_path.c_str(), + namespace_type, + permitted_path.c_str(), + parent_ns.get_android_ns()); + if (ns == nullptr) { + *error_msg = dlerror(); + return false; + } + + native_loader_ns = NativeLoaderNamespace(ns); + } else { + native_bridge_namespace_t* ns = NativeBridgeCreateNamespace("classloader-namespace", + nullptr, + library_path.c_str(), + namespace_type, + permitted_path.c_str(), + parent_ns.get_native_bridge_ns()); + if (ns == nullptr) { + *error_msg = NativeBridgeGetError(); + return false; + } - if (ns != nullptr) { - namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns)); + native_loader_ns = NativeLoaderNamespace(ns); } - return ns; + namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), native_loader_ns)); + + *ns = native_loader_ns; + return true; } - android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) { + bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) { auto it = std::find_if(namespaces_.begin(), namespaces_.end(), - [&](const std::pair<jweak, android_namespace_t*>& value) { + [&](const std::pair<jweak, NativeLoaderNamespace>& value) { return env->IsSameObject(value.first, class_loader); }); - return it != namespaces_.end() ? it->second : nullptr; + if (it != namespaces_.end()) { + if (ns != nullptr) { + *ns = it->second; + } + + return true; + } + + return false; } void Initialize() { @@ -217,12 +300,25 @@ class LibraryNamespaces { return true; } - bool InitPublicNamespace(const char* library_path) { + bool InitPublicNamespace(const char* library_path, std::string* error_msg) { + // Ask native bride if this apps library path should be handled by it + bool is_native_bridge = NativeBridgeIsPathSupported(library_path); + // (http://b/25844435) - Some apps call dlopen from generated code (mono jited // code is one example) unknown to linker in which case linker uses anonymous // namespace. The second argument specifies the search path for the anonymous // namespace which is the library_path of the classloader. - initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path); + if (!is_native_bridge) { + initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path); + if (!initialized_) { + *error_msg = dlerror(); + } + } else { + initialized_ = NativeBridgeInitNamespace(public_libraries_.c_str(), library_path); + if (!initialized_) { + *error_msg = NativeBridgeGetError(); + } + } return initialized_; } @@ -236,22 +332,24 @@ class LibraryNamespaces { return env->CallObjectMethod(class_loader, get_parent); } - android_namespace_t* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader) { + bool FindParentNamespaceByClassLoader(JNIEnv* env, + jobject class_loader, + NativeLoaderNamespace* ns) { jobject parent_class_loader = GetParentClassLoader(env, class_loader); while (parent_class_loader != nullptr) { - android_namespace_t* ns = FindNamespaceByClassLoader(env, parent_class_loader); - if (ns != nullptr) { - return ns; + if (FindNamespaceByClassLoader(env, parent_class_loader, ns)) { + return true; } parent_class_loader = GetParentClassLoader(env, parent_class_loader); } - return nullptr; + + return false; } bool initialized_; - std::vector<std::pair<jweak, android_namespace_t*>> namespaces_; + std::vector<std::pair<jweak, NativeLoaderNamespace>> namespaces_; std::string public_libraries_; @@ -285,13 +383,18 @@ jstring CreateClassLoaderNamespace(JNIEnv* env, #if defined(__ANDROID__) UNUSED(target_sdk_version); std::lock_guard<std::mutex> guard(g_namespaces_mutex); - android_namespace_t* ns = g_namespaces->Create(env, - class_loader, - is_shared, - library_path, - permitted_path); - if (ns == nullptr) { - return env->NewStringUTF(dlerror()); + + std::string error_msg; + NativeLoaderNamespace ns; + bool success = g_namespaces->Create(env, + class_loader, + is_shared, + library_path, + permitted_path, + &ns, + &error_msg); + if (!success) { + return env->NewStringUTF(error_msg.c_str()); } #else UNUSED(env, target_sdk_version, class_loader, is_shared, @@ -304,44 +407,83 @@ void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader, - jstring library_path) { + jstring library_path, + bool* needs_native_bridge, + std::string* error_msg) { #if defined(__ANDROID__) UNUSED(target_sdk_version); if (class_loader == nullptr) { + *needs_native_bridge = false; return dlopen(path, RTLD_NOW); } std::lock_guard<std::mutex> guard(g_namespaces_mutex); - android_namespace_t* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader); + NativeLoaderNamespace ns; - if (ns == nullptr) { + if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) { // This is the case where the classloader was not created by ApplicationLoaders // In this case we create an isolated not-shared namespace for it. - ns = g_namespaces->Create(env, class_loader, false, library_path, nullptr); - if (ns == nullptr) { + if (!g_namespaces->Create(env, class_loader, false, library_path, nullptr, &ns, error_msg)) { return nullptr; } } - android_dlextinfo extinfo; - extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; - extinfo.library_namespace = ns; + if (ns.is_android_namespace()) { + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns.get_android_ns(); - return android_dlopen_ext(path, RTLD_NOW, &extinfo); + void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo); + if (handle == nullptr) { + *error_msg = dlerror(); + } + *needs_native_bridge = false; + return handle; + } else { + void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns()); + if (handle == nullptr) { + *error_msg = NativeBridgeGetError(); + } + *needs_native_bridge = true; + return handle; + } #else UNUSED(env, target_sdk_version, class_loader, library_path); - return dlopen(path, RTLD_NOW); + *needs_native_bridge = false; + void* handle = dlopen(path, RTLD_NOW); + if (handle == nullptr) { + if (NativeBridgeIsSupported(path)) { + *needs_native_bridge = true; + handle = NativeBridgeLoadLibrary(path, RTLD_NOW); + if (handle == nullptr) { + *error_msg = NativeBridgeGetError(); + } + } else { + *needs_native_bridge = false; + *error_msg = dlerror(); + } + } + return handle; #endif } -bool CloseNativeLibrary(void* handle) { - return dlclose(handle) == 0; +bool CloseNativeLibrary(void* handle, const bool needs_native_bridge) { + return needs_native_bridge ? NativeBridgeUnloadLibrary(handle) : + dlclose(handle); } #if defined(__ANDROID__) android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) { std::lock_guard<std::mutex> guard(g_namespaces_mutex); - return g_namespaces->FindNamespaceByClassLoader(env, class_loader); + // native_bridge_namespaces are not supported for callers of this function. + // At the moment this is libwebviewchromium_loader and vulkan. + NativeLoaderNamespace ns; + if (g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) { + CHECK(ns.is_android_namespace()); + return ns.get_android_ns(); + } + + return nullptr; } #endif |
