diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp index 17983bc14..8903b72c7 100644 --- a/libnativeloader/Android.bp +++ b/libnativeloader/Android.bp @@ -23,4 +23,20 @@ cc_library { "llndk.libraries.txt", "vndksp.libraries.txt", ], + target: { + android: { + version_script: "libnativeloader.map.txt", + }, + }, + stubs: { + symbol_file: "libnativeloader.map.txt", + versions: ["1"], + }, +} + +cc_library_headers { + name: "libnativeloader-dummy-headers", + + host_supported: true, + export_include_dirs: ["include"], } diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h index 43c932978..ca026b3be 100644 --- a/libnativeloader/include/nativeloader/dlext_namespaces.h +++ b/libnativeloader/include/nativeloader/dlext_namespaces.h @@ -18,6 +18,7 @@ #define __ANDROID_DLEXT_NAMESPACES_H__ #include +#include __BEGIN_DECLS @@ -84,12 +85,9 @@ enum { * If a library or any of its dependencies are outside of the permitted_when_isolated_path * and search_path, and it is not part of the public namespace dlopen will fail. */ -extern struct android_namespace_t* android_create_namespace(const char* name, - const char* ld_library_path, - const char* default_library_path, - uint64_t type, - const char* permitted_when_isolated_path, - android_namespace_t* parent); +extern struct android_namespace_t* android_create_namespace( + const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, + const char* permitted_when_isolated_path, struct android_namespace_t* parent); /* * Creates a link between namespaces. Every link has list of sonames of @@ -107,8 +105,8 @@ extern struct android_namespace_t* android_create_namespace(const char* name, * step will not go deeper into linked namespaces for this library but * will do so for DT_NEEDED libraries. */ -extern bool android_link_namespaces(android_namespace_t* from, - android_namespace_t* to, +extern bool android_link_namespaces(struct android_namespace_t* from, + struct android_namespace_t* to, const char* shared_libs_sonames); /* @@ -124,7 +122,7 @@ extern bool android_link_namespaces(android_namespace_t* from, */ extern void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size); -extern android_namespace_t* android_get_exported_namespace(const char* name); +extern struct android_namespace_t* android_get_exported_namespace(const char* name); __END_DECLS diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h index c1d9d2a92..af53dc5a1 100644 --- a/libnativeloader/include/nativeloader/native_loader.h +++ b/libnativeloader/include/nativeloader/native_loader.h @@ -17,14 +17,21 @@ #ifndef NATIVE_LOADER_H_ #define NATIVE_LOADER_H_ -#include "jni.h" +#include #include -#include +#include "jni.h" #if defined(__ANDROID__) #include #endif +#ifdef __cplusplus namespace android { +extern "C" { +#endif // __cplusplus + +// README: the char** error message parameter being passed +// to the methods below need to be freed through calling NativeLoaderFreeErrorMessage. +// It's the caller's responsibility to call that method. __attribute__((visibility("default"))) void InitializeNativeLoader(); @@ -38,42 +45,39 @@ jstring CreateClassLoaderNamespace(JNIEnv* env, jstring library_path, jstring permitted_path); -__attribute__((visibility("default"))) -void* OpenNativeLibrary(JNIEnv* env, - int32_t target_sdk_version, - const char* path, - jobject class_loader, - jstring library_path, - bool* needs_native_bridge, - std::string* error_msg); +__attribute__((visibility("default"))) void* OpenNativeLibrary( + JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader, + jstring library_path, bool* needs_native_bridge, char** error_msg); __attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, - std::string* error_msg); + char** error_msg); + +__attribute__((visibility("default"))) void NativeLoaderFreeErrorMessage(char* msg); #if defined(__ANDROID__) // Look up linker namespace by class_loader. Returns nullptr if // there is no namespace associated with the class_loader. // TODO(b/79940628): move users to FindNativeLoaderNamespaceByClassLoader and remove this function. -__attribute__((visibility("default"))) -android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader); -// That version works with native bridge namespaces, but requires use of OpenNativeLibrary. -class NativeLoaderNamespace; -__attribute__((visibility("default"))) -NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader( +__attribute__((visibility("default"))) struct android_namespace_t* FindNamespaceByClassLoader( JNIEnv* env, jobject class_loader); +// That version works with native bridge namespaces, but requires use of OpenNativeLibrary. +struct NativeLoaderNamespace; +__attribute__((visibility("default"))) struct NativeLoaderNamespace* +FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader); // Load library. Unlinke OpenNativeLibrary above couldn't create namespace on demand, but does // not require access to JNIEnv either. -__attribute__((visibility("default"))) -void* OpenNativeLibrary(NativeLoaderNamespace* ns, - const char* path, - bool* needs_native_bridge, - std::string* error_msg); +__attribute__((visibility("default"))) void* OpenNativeLibraryInNamespace( + struct NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge, + char** error_msg); #endif __attribute__((visibility("default"))) void ResetNativeLoader(); -}; // namespace android +#ifdef __cplusplus +} // extern "C" +} // namespace android +#endif // __cplusplus #endif // NATIVE_BRIDGE_H_ diff --git a/libnativeloader/libnativeloader.map.txt b/libnativeloader/libnativeloader.map.txt new file mode 100644 index 000000000..40c30bd4a --- /dev/null +++ b/libnativeloader/libnativeloader.map.txt @@ -0,0 +1,31 @@ +# +# Copyright (C) 2019 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. +# + +# TODO(b/122710865): Prune these uses once the runtime APEX is complete. +LIBNATIVELOADER_1 { + global: + OpenNativeLibrary; + InitializeNativeLoader; + ResetNativeLoader; + CloseNativeLibrary; + OpenNativeLibraryInNamespace; + FindNamespaceByClassLoader; + FindNativeLoaderNamespaceByClassLoader; + CreateClassLoaderNamespace; + NativeLoaderFreeErrorMessage; + local: + *; +}; diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp index de7ea0885..f231afa5b 100644 --- a/libnativeloader/native_loader.cpp +++ b/libnativeloader/native_loader.cpp @@ -52,7 +52,7 @@ using namespace std::string_literals; namespace android { #if defined(__ANDROID__) -class NativeLoaderNamespace { +struct NativeLoaderNamespace { public: NativeLoaderNamespace() : android_ns_(nullptr), native_bridge_ns_(nullptr) { } @@ -151,14 +151,9 @@ class LibraryNamespaces { public: LibraryNamespaces() : initialized_(false) { } - NativeLoaderNamespace* Create(JNIEnv* env, - uint32_t target_sdk_version, - jobject class_loader, - bool is_shared, - bool is_for_vendor, - jstring java_library_path, - jstring java_permitted_path, - std::string* error_msg) { + NativeLoaderNamespace* Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader, + bool is_shared, bool is_for_vendor, jstring java_library_path, + jstring java_permitted_path, std::string* error_msg) { std::string library_path; // empty string by default. if (java_library_path != nullptr) { @@ -628,13 +623,9 @@ jstring CreateClassLoaderNamespace(JNIEnv* env, return nullptr; } -void* OpenNativeLibrary(JNIEnv* env, - int32_t target_sdk_version, - const char* path, - jobject class_loader, - jstring library_path, - bool* needs_native_bridge, - std::string* error_msg) { +void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path, + jobject class_loader, jstring library_path, bool* needs_native_bridge, + char** error_msg) { #if defined(__ANDROID__) UNUSED(target_sdk_version); if (class_loader == nullptr) { @@ -652,19 +643,16 @@ void* OpenNativeLibrary(JNIEnv* env, if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) { // This is the case where the classloader was not created by ApplicationLoaders // In this case we create an isolated not-shared namespace for it. - if ((ns = g_namespaces->Create(env, - target_sdk_version, - class_loader, - false /* is_shared */, - false /* is_for_vendor */, - library_path, - nullptr, - error_msg)) == nullptr) { + std::string create_error_msg; + if ((ns = g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */, + false /* is_for_vendor */, library_path, nullptr, + &create_error_msg)) == nullptr) { + *error_msg = strdup(create_error_msg.c_str()); return nullptr; } } - return OpenNativeLibrary(ns, path, needs_native_bridge, error_msg); + return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg); #else UNUSED(env, target_sdk_version, class_loader); @@ -705,35 +693,40 @@ void* OpenNativeLibrary(JNIEnv* env, if (handle != nullptr) { return handle; } - *error_msg = NativeBridgeGetError(); + *error_msg = strdup(NativeBridgeGetError()); } else { - *error_msg = dlerror(); + *error_msg = strdup(dlerror()); } } return nullptr; #endif } -bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, std::string* error_msg) { +bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) { bool success; if (needs_native_bridge) { success = (NativeBridgeUnloadLibrary(handle) == 0); if (!success) { - *error_msg = NativeBridgeGetError(); + *error_msg = strdup(NativeBridgeGetError()); } } else { success = (dlclose(handle) == 0); if (!success) { - *error_msg = dlerror(); + *error_msg = strdup(dlerror()); } } return success; } +void NativeLoaderFreeErrorMessage(char* msg) { + // The error messages get allocated through strdup, so we must call free on them. + free(msg); +} + #if defined(__ANDROID__) -void* OpenNativeLibrary(NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge, - std::string* error_msg) { +void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path, + bool* needs_native_bridge, char** error_msg) { if (ns->is_android_namespace()) { android_dlextinfo extinfo; extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; @@ -741,14 +734,14 @@ void* OpenNativeLibrary(NativeLoaderNamespace* ns, const char* path, bool* needs void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo); if (handle == nullptr) { - *error_msg = dlerror(); + *error_msg = strdup(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(); + *error_msg = strdup(NativeBridgeGetError()); } *needs_native_bridge = true; return handle; diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp index d528f3031..1464e39f3 100644 --- a/libnativeloader/test/Android.bp +++ b/libnativeloader/test/Android.bp @@ -69,3 +69,14 @@ cc_library { "libbase", ], } + +// Build the test for the C API. +cc_test { + name: "libnativeloader-api-tests", + host_supported: true, + test_per_src: true, + srcs: [ + "api_test.c", + ], + header_libs: ["libnativeloader-dummy-headers"], +} diff --git a/libnativeloader/test/api_test.c b/libnativeloader/test/api_test.c new file mode 100644 index 000000000..e7025fd7a --- /dev/null +++ b/libnativeloader/test/api_test.c @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2019 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. + */ + +/* The main purpose of this test is to ensure this C header compiles in C, so + * that no C++ features inadvertently leak into the C ABI. */ +#include "nativeloader/native_loader.h" + +int main(int argc, char** argv) { + (void)argc; + (void)argv; + return 0; +}