From e8fb11dfa1b376124d0a1a349c8800799f25606a Mon Sep 17 00:00:00 2001 From: Zhenhua WANG Date: Mon, 27 Feb 2017 10:14:45 +0800 Subject: [PATCH] NativeBridge: add "linked namespace" semantic corresponding to linker For dynamic linking perspective, semantics of NativeBridge needs to align with dynamic linker. This patch adds "linked namespace" semantic which shares some libraries from one namespace to another. Test: make test-art-host-run-test-115-native-bridge Change-Id: I71ce1dde19d61363d5eb9731fd4795a8c315b3a0 --- include/nativebridge/native_bridge.h | 106 ++++++++++++------ libnativebridge/native_bridge.cc | 19 +++- libnativebridge/tests/Android.mk | 2 +- libnativebridge/tests/DummyNativeBridge3.cpp | 50 +++++---- ...iveBridge3InitAnonymousNamespace_test.cpp} | 24 ++-- libnativeloader/native_loader.cpp | 9 +- 6 files changed, 137 insertions(+), 73 deletions(-) rename libnativebridge/tests/{NativeBridge3InitNamespace_test.cpp => NativeBridge3InitAnonymousNamespace_test.cpp} (59%) diff --git a/include/nativebridge/native_bridge.h b/include/nativebridge/native_bridge.h index 45266dedf..929b8aec4 100644 --- a/include/nativebridge/native_bridge.h +++ b/include/nativebridge/native_bridge.h @@ -116,14 +116,25 @@ struct native_bridge_namespace_t; // Use NativeBridgeIsSupported() instead in non-namespace scenario. bool NativeBridgeIsPathSupported(const char* path); -// Initializes public and anonymous namespace at native bridge side. +// Initializes anonymous namespace. +// NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker. +// +// The anonymous namespace is used in the case when a NativeBridge implementation +// cannot identify the caller of dlopen/dlsym which happens for the code not loaded +// by dynamic linker; for example calls from the mono-compiled code. // // 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); +bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames, + const char* anon_ns_library_path); -// Create a namespace and pass the key of related namespaces to native bridge. +// Create new namespace in which native libraries will be loaded. +// NativeBridge's peer of android_create_namespace() of dynamic linker. +// +// The libraries in the namespace are searched by folowing order: +// 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH) +// 2. In directories specified by DT_RUNPATH of the "needed by" binary. +// 3. deault_library_path (This of this as namespace-local default library path) // // Starting with v3, NativeBridge has two scenarios: with/without namespace. // Should not use in non-namespace scenario. @@ -134,7 +145,17 @@ native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name, const char* permitted_when_isolated_path, native_bridge_namespace_t* parent_ns); +// Creates a link which shares some libraries from one namespace to another. +// NativeBridge's peer of android_link_namespaces() of dynamic linker. +// +// Starting with v3, NativeBridge has two scenarios: with/without namespace. +// Should not use in non-namespace scenario. +bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to, + const char* shared_libs_sonames); + // Load a shared library with namespace key that is supported by the native bridge. +// NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace +// extension. // // Starting with v3, NativeBridge has two scenarios: with/without namespace. // Use NativeBridgeLoadLibrary() instead in non-namespace scenario. @@ -152,7 +173,7 @@ struct NativeBridgeCallbacks { // Parameters: // runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks. // Returns: - // true iff initialization was successful. + // true if initialization was successful. bool (*initialize)(const NativeBridgeRuntimeCallbacks* runtime_cbs, const char* private_dir, const char* instruction_set); @@ -194,10 +215,10 @@ struct NativeBridgeCallbacks { // instruction set. // // Parameters: - // instruction_set [IN] the instruction set of the app + // instruction_set [IN] the instruction set of the app // Returns: - // NULL if not supported by native bridge. - // Otherwise, return all environment values to be set after fork. + // NULL if not supported by native bridge. + // Otherwise, return all environment values to be set after fork. const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set); // Added callbacks in version 2. @@ -206,9 +227,9 @@ struct NativeBridgeCallbacks { // forwards- or backwards-compatible, and libnativebridge will then stop using it. // // Parameters: - // bridge_version [IN] the version of libnativebridge. + // bridge_version [IN] the version of libnativebridge. // Returns: - // true iff the native bridge supports the given version of libnativebridge. + // true if the native bridge supports the given version of libnativebridge. bool (*isCompatibleWith)(uint32_t bridge_version); // A callback to retrieve a native bridge's signal handler for the specified signal. The runtime @@ -217,12 +238,12 @@ struct NativeBridgeCallbacks { // that will potentially lead to cycles. // // Parameters: - // signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is + // signal [IN] the signal for which the handler is asked for. Currently, only SIGSEGV is // supported by the runtime. // Returns: - // NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the - // runtime. - // Otherwise, a pointer to the signal handler. + // NULL if the native bridge doesn't use a handler or doesn't want it to be managed by the + // runtime. + // Otherwise, a pointer to the signal handler. NativeBridgeSignalHandlerFn (*getSignalHandler)(int signal); // Added callbacks in version 3. @@ -231,7 +252,7 @@ struct NativeBridgeCallbacks { // to zero then the dynamic library is unloaded. // // Parameters: - // handle [IN] the handler of a dynamic library. + // handle [IN] the handler of a dynamic library. // // Returns: // 0 on success, and nonzero on error. @@ -257,33 +278,36 @@ struct NativeBridgeCallbacks { // 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. + // Initializes anonymous namespace at native bridge side. + // NativeBridge's peer of android_init_anonymous_namespace() of dynamic linker. + // + // The anonymous namespace is used in the case when a NativeBridge implementation + // cannot identify the caller of dlopen/dlsym which happens for the code not loaded + // by dynamic linker; for example calls from the mono-compiled code. // // Parameters: - // public_ns_sonames [IN] the name of "public" libraries. - // anon_ns_library_path [IN] the library search path of (anonymous) namespace. + // 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. + // 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); + bool (*initAnonymousNamespace)(const char* public_ns_sonames, const char* anon_ns_library_path); - - // Create a namespace and pass the key of releated namespaces to native bridge. + // Create new namespace in which native libraries will be loaded. + // NativeBridge's peer of android_create_namespace() of dynamic linker. // // 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. + // 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. + // 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. @@ -294,7 +318,25 @@ struct NativeBridgeCallbacks { const char* permitted_when_isolated_path, native_bridge_namespace_t* parent_ns); + // Creates a link which shares some libraries from one namespace to another. + // NativeBridge's peer of android_link_namespaces() of dynamic linker. + // + // Parameters: + // from [IN] the namespace where libraries are accessed. + // to [IN] the namespace where libraries are loaded. + // shared_libs_sonames [IN] the libraries to be shared. + // + // Returns: + // Whether successed or not. + // + // Starting with v3, NativeBridge has two scenarios: with/without namespace. + // Should not use in non-namespace scenario. + bool (*linkNamespaces)(native_bridge_namespace_t* from, native_bridge_namespace_t* to, + const char* shared_libs_sonames); + // Load a shared library within a namespace. + // NativeBridge's peer of android_dlopen_ext() of dynamic linker, only supports namespace + // extension. // // Parameters: // libpath [IN] path to the shared library diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc index 83f35b1e8..050373ae5 100644 --- a/libnativebridge/native_bridge.cc +++ b/libnativebridge/native_bridge.cc @@ -573,11 +573,11 @@ bool NativeBridgeIsPathSupported(const char* path) { return false; } -bool NativeBridgeInitNamespace(const char* public_ns_sonames, - const char* anon_ns_library_path) { +bool NativeBridgeInitAnonymousNamespace(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); + return callbacks->initAnonymousNamespace(public_ns_sonames, anon_ns_library_path); } else { ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION); } @@ -608,6 +608,19 @@ native_bridge_namespace_t* NativeBridgeCreateNamespace(const char* name, return nullptr; } +bool NativeBridgeLinkNamespaces(native_bridge_namespace_t* from, native_bridge_namespace_t* to, + const char* shared_libs_sonames) { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + return callbacks->linkNamespaces(from, to, shared_libs_sonames); + } else { + ALOGE("not compatible with version %d, cannot init namespace", NAMESPACE_VERSION); + } + } + + return false; +} + void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, native_bridge_namespace_t* ns) { if (NativeBridgeInitialized()) { if (isCompatibleWith(NAMESPACE_VERSION)) { diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk index 4c3e86298..c9468f033 100644 --- a/libnativebridge/tests/Android.mk +++ b/libnativebridge/tests/Android.mk @@ -24,7 +24,7 @@ test_src_files := \ NativeBridge3UnloadLibrary_test.cpp \ NativeBridge3GetError_test.cpp \ NativeBridge3IsPathSupported_test.cpp \ - NativeBridge3InitNamespace_test.cpp \ + NativeBridge3InitAnonymousNamespace_test.cpp \ NativeBridge3CreateNamespace_test.cpp \ NativeBridge3LoadLibraryExt_test.cpp diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp index 13fce8599..4ef1c8273 100644 --- a/libnativebridge/tests/DummyNativeBridge3.cpp +++ b/libnativebridge/tests/DummyNativeBridge3.cpp @@ -76,8 +76,8 @@ 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 */) { +extern "C" bool native_bridge3_initAnonymousNamespace(const char* /* public_ns_sonames */, + const char* /* anon_ns_library_path */) { return true; } @@ -91,30 +91,34 @@ native_bridge3_createNamespace(const char* /* name */, return nullptr; } +extern "C" bool native_bridge3_linkNamespaces(android::native_bridge_namespace_t* /* from */, + android::native_bridge_namespace_t* /* to */, + const char* /* shared_libs_soname */) { + return true; +} + 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 -}; - +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, + .initAnonymousNamespace = &native_bridge3_initAnonymousNamespace, + .createNamespace = &native_bridge3_createNamespace, + .linkNamespaces = &native_bridge3_linkNamespaces, + .loadLibraryExt = &native_bridge3_loadLibraryExt}; diff --git a/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp similarity index 59% rename from libnativebridge/tests/NativeBridge3InitNamespace_test.cpp rename to libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp index ae0fd2b02..b0d6b09d1 100644 --- a/libnativebridge/tests/NativeBridge3InitNamespace_test.cpp +++ b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp @@ -20,20 +20,20 @@ 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()); +TEST_F(NativeBridgeTest, V3_InitAnonymousNamespace) { + // 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)); + ASSERT_EQ(3U, NativeBridgeGetVersion()); + ASSERT_EQ(true, NativeBridgeInitAnonymousNamespace(nullptr, nullptr)); - // Clean-up code_cache - ASSERT_EQ(0, rmdir(kCodeCache)); + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); } } // namespace android diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp index 74f2f1d31..f3391d1be 100644 --- a/libnativeloader/native_loader.cpp +++ b/libnativeloader/native_loader.cpp @@ -183,6 +183,11 @@ class LibraryNamespaces { return false; } + if (!NativeBridgeLinkNamespaces(ns, nullptr, public_libraries_.c_str())) { + *error_msg = NativeBridgeGetError(); + return false; + } + native_loader_ns = NativeLoaderNamespace(ns); } @@ -324,8 +329,8 @@ class LibraryNamespaces { // and now initialize native bridge namespaces if necessary. if (NativeBridgeInitialized()) { - initialized_ = NativeBridgeInitNamespace(public_libraries_.c_str(), - is_native_bridge ? library_path : nullptr); + initialized_ = NativeBridgeInitAnonymousNamespace(public_libraries_.c_str(), + is_native_bridge ? library_path : nullptr); if (!initialized_) { *error_msg = NativeBridgeGetError(); }