diff options
author | 2019-10-09 13:29:16 +0100 | |
---|---|---|
committer | 2019-10-11 09:57:02 +0000 | |
commit | 9b16e344b246096d228dd4b41ff711884bcfcb3e (patch) | |
tree | ff93f416fcc59fb831202e80b5fb8c8ede99d500 | |
parent | e828ea0553b3106598071239e8215fca63b15c9b (diff) |
Move libnative{bridge,loader} to art/
This change moves system/core/libnative{bridge,loader} under art/.
Bug: 137364733
Test: m
Change-Id: I9be7333d00fcd3f36cd80520e50a30ea840187ad
64 files changed, 5486 insertions, 0 deletions
diff --git a/libnativebridge/.clang-format b/libnativebridge/.clang-format new file mode 120000 index 0000000000..fd0645fdf9 --- /dev/null +++ b/libnativebridge/.clang-format @@ -0,0 +1 @@ +../.clang-format-2
\ No newline at end of file diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp new file mode 100644 index 0000000000..c97845d16a --- /dev/null +++ b/libnativebridge/Android.bp @@ -0,0 +1,65 @@ +cc_defaults { + name: "libnativebridge-defaults", + cflags: [ + "-Werror", + "-Wall", + ], + cppflags: [ + "-fvisibility=protected", + ], + header_libs: ["libnativebridge-headers"], + export_header_lib_headers: ["libnativebridge-headers"], +} + +cc_library_headers { + name: "libnativebridge-headers", + + host_supported: true, + export_include_dirs: ["include"], +} + +cc_library { + name: "libnativebridge", + defaults: ["libnativebridge-defaults"], + // TODO(oth): remove after moving under art/ (b/137364733) + visibility: ["//visibility:public"], + + host_supported: true, + srcs: ["native_bridge.cc"], + header_libs: [ + "libbase_headers", + ], + shared_libs: [ + "liblog", + ], + // TODO(jiyong): remove this line after aosp/885921 lands + export_include_dirs: ["include"], + + target: { + android: { + version_script: "libnativebridge.map.txt", + }, + linux: { + version_script: "libnativebridge.map.txt", + }, + }, + + stubs: { + symbol_file: "libnativebridge.map.txt", + versions: ["1"], + }, +} + +// TODO(b/124250621): eliminate the need for this library +cc_library { + name: "libnativebridge_lazy", + defaults: ["libnativebridge-defaults"], + // TODO(oth): remove after moving under art/ (b/137364733) + visibility: ["//visibility:public"], + + host_supported: false, + srcs: ["native_bridge_lazy.cc"], + required: ["libnativebridge"], +} + +subdirs = ["tests"] diff --git a/libnativebridge/CPPLINT.cfg b/libnativebridge/CPPLINT.cfg new file mode 100644 index 0000000000..578047ba10 --- /dev/null +++ b/libnativebridge/CPPLINT.cfg @@ -0,0 +1,19 @@ +# +# 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. +# + +filter=-build/header_guard +filter=-whitespace/comments +filter=-whitespace/parens diff --git a/libnativebridge/OWNERS b/libnativebridge/OWNERS new file mode 100644 index 0000000000..daf87f47b1 --- /dev/null +++ b/libnativebridge/OWNERS @@ -0,0 +1,4 @@ +dimitry@google.com +eaeltsin@google.com +ngeoffray@google.com +oth@google.com diff --git a/libnativebridge/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h new file mode 100644 index 0000000000..e9c9500252 --- /dev/null +++ b/libnativebridge/include/nativebridge/native_bridge.h @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef NATIVE_BRIDGE_H_ +#define NATIVE_BRIDGE_H_ + +#include <signal.h> +#include <stdbool.h> +#include <stdint.h> +#include <sys/types.h> + +#include "jni.h" + +#ifdef __cplusplus +namespace android { +extern "C" { +#endif // __cplusplus + +struct NativeBridgeRuntimeCallbacks; +struct NativeBridgeRuntimeValues; + +// Function pointer type for sigaction. This is mostly the signature of a signal handler, except +// for the return type. The runtime needs to know whether the signal was handled or should be given +// to the chain. +typedef bool (*NativeBridgeSignalHandlerFn)(int, siginfo_t*, void*); + +// Open the native bridge, if any. Should be called by Runtime::Init(). A null library filename +// signals that we do not want to load a native bridge. +bool LoadNativeBridge(const char* native_bridge_library_filename, + const struct NativeBridgeRuntimeCallbacks* runtime_callbacks); + +// Quick check whether a native bridge will be needed. This is based off of the instruction set +// of the process. +bool NeedsNativeBridge(const char* instruction_set); + +// Do the early initialization part of the native bridge, if necessary. This should be done under +// high privileges. +bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set); + +// Initialize the native bridge, if any. Should be called by Runtime::DidForkFromZygote. The JNIEnv* +// will be used to modify the app environment for the bridge. +bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set); + +// Unload the native bridge, if any. Should be called by Runtime::DidForkFromZygote. +void UnloadNativeBridge(); + +// Check whether a native bridge is available (opened or initialized). Requires a prior call to +// LoadNativeBridge. +bool NativeBridgeAvailable(); + +// Check whether a native bridge is available (initialized). Requires a prior call to +// LoadNativeBridge & InitializeNativeBridge. +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 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 +// successful LoadNativeBridge() and before closing it, that is, as long as NativeBridgeAvailable() +// returns true. Returns 0 otherwise. +uint32_t NativeBridgeGetVersion(); + +// Returns a signal handler that the bridge would like to be managed. Only valid for a native +// bridge supporting the version 2 interface. Will return null if the bridge does not support +// version 2, or if it doesn't have a signal handler it wants to be known. +NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal); + +// Returns whether we have seen a native bridge error. This could happen because the library +// was not found, rejected, could not be initialized and so on. +// +// This functionality is mainly for testing. +bool NativeBridgeError(); + +// Returns whether a given string is acceptable as a native bridge library filename. +// +// 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. Returns 0 on success and non-zero on error. +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. +const 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 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 NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames, + const char* anon_ns_library_path); + +// 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. +struct 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, struct 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(struct native_bridge_namespace_t* from, + struct 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. +void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, + struct native_bridge_namespace_t* ns); + +// Returns exported namespace by the name. This is a reflection of +// android_get_exported_namespace function. Introduced in v5. +struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name); + +// Native bridge interfaces to runtime. +struct NativeBridgeCallbacks { + // Version number of the interface. + uint32_t version; + + // Initialize native bridge. Native bridge's internal implementation must ensure MT safety and + // that the native bridge is initialized only once. Thus it is OK to call this interface for an + // already initialized native bridge. + // + // Parameters: + // runtime_cbs [IN] the pointer to NativeBridgeRuntimeCallbacks. + // Returns: + // true if initialization was successful. + bool (*initialize)(const struct NativeBridgeRuntimeCallbacks* runtime_cbs, + const char* private_dir, const char* instruction_set); + + // Load a shared library that is supported by the native bridge. + // + // Parameters: + // libpath [IN] path to the shared library + // 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 + // sigature as the native method. + // + // Parameters: + // handle [IN] the handle returned from loadLibrary + // shorty [IN] short descriptor of native method + // len [IN] length of shorty + // Returns: + // address of trampoline if successful, otherwise NULL + void* (*getTrampoline)(void* handle, const char* name, const char* shorty, uint32_t len); + + // Check whether native library is valid and is for an ABI that is supported by native bridge. + // + // Parameters: + // 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 + // instruction set. + // + // Parameters: + // 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. + const struct NativeBridgeRuntimeValues* (*getAppEnv)(const char* instruction_set); + + // Added callbacks in version 2. + + // Check whether the bridge is compatible with the given version. A bridge may decide not to be + // forwards- or backwards-compatible, and libnativebridge will then stop using it. + // + // Parameters: + // bridge_version [IN] the version of libnativebridge. + // Returns: + // 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 + // will ensure that the signal handler is being called after the runtime's own handler, but before + // all chained handlers. The native bridge should not try to install the handler by itself, as + // that will potentially lead to cycles. + // + // Parameters: + // 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. + 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. + const 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. + // 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. + // 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 (*initAnonymousNamespace)(const char* public_ns_sonames, const char* anon_ns_library_path); + + // 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. + // 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. + struct 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, + struct 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)(struct native_bridge_namespace_t* from, + struct 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 + // 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, struct native_bridge_namespace_t* ns); + + // Get native bridge version of vendor namespace. + // The vendor namespace is the namespace used to load vendor public libraries. + // With O release this namespace can be different from the default namespace. + // For the devices without enable vendor namespaces this function should return null + // + // Returns: + // vendor namespace or null if it was not set up for the device + // + // Starting with v5 (Android Q) this function is no longer used. + // Use getExportedNamespace() below. + struct native_bridge_namespace_t* (*getVendorNamespace)(); + + // Get native bridge version of exported namespace. Peer of + // android_get_exported_namespace(const char*) function. + // + // Returns: + // exported namespace or null if it was not set up for the device + struct native_bridge_namespace_t* (*getExportedNamespace)(const char* name); +}; + +// Runtime interfaces to native bridge. +struct NativeBridgeRuntimeCallbacks { + // Get shorty of a Java method. The shorty is supposed to be persistent in memory. + // + // Parameters: + // env [IN] pointer to JNIenv. + // mid [IN] Java methodID. + // Returns: + // short descriptor for method. + const char* (*getMethodShorty)(JNIEnv* env, jmethodID mid); + + // Get number of native methods for specified class. + // + // Parameters: + // env [IN] pointer to JNIenv. + // clazz [IN] Java class object. + // Returns: + // number of native methods. + uint32_t (*getNativeMethodCount)(JNIEnv* env, jclass clazz); + + // Get at most 'method_count' native methods for specified class 'clazz'. Results are outputed + // via 'methods' [OUT]. The signature pointer in JNINativeMethod is reused as the method shorty. + // + // Parameters: + // env [IN] pointer to JNIenv. + // clazz [IN] Java class object. + // methods [OUT] array of method with the name, shorty, and fnPtr. + // method_count [IN] max number of elements in methods. + // Returns: + // number of method it actually wrote to methods. + uint32_t (*getNativeMethods)(JNIEnv* env, jclass clazz, JNINativeMethod* methods, + uint32_t method_count); +}; + +#ifdef __cplusplus +} // extern "C" +} // namespace android +#endif // __cplusplus + +#endif // NATIVE_BRIDGE_H_ diff --git a/libnativebridge/libnativebridge.map.txt b/libnativebridge/libnativebridge.map.txt new file mode 100644 index 0000000000..a6841a3a81 --- /dev/null +++ b/libnativebridge/libnativebridge.map.txt @@ -0,0 +1,45 @@ +# +# 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): Most of these uses come from libnativeloader, which should be bundled +# together with libnativebridge in the APEX. Once this happens, prune this list. +LIBNATIVEBRIDGE_1 { + global: + NativeBridgeIsSupported; + NativeBridgeLoadLibrary; + NativeBridgeUnloadLibrary; + NativeBridgeGetError; + NativeBridgeIsPathSupported; + NativeBridgeCreateNamespace; + NativeBridgeGetExportedNamespace; + NativeBridgeLinkNamespaces; + NativeBridgeLoadLibraryExt; + NativeBridgeInitAnonymousNamespace; + NativeBridgeInitialized; + NativeBridgeGetTrampoline; + LoadNativeBridge; + PreInitializeNativeBridge; + InitializeNativeBridge; + NativeBridgeGetVersion; + NativeBridgeGetSignalHandler; + UnloadNativeBridge; + NativeBridgeAvailable; + NeedsNativeBridge; + NativeBridgeError; + NativeBridgeNameAcceptable; + local: + *; +}; diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc new file mode 100644 index 0000000000..9adba9a50e --- /dev/null +++ b/libnativebridge/native_bridge.cc @@ -0,0 +1,646 @@ +/* + * Copyright (C) 2014 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. + */ + +#define LOG_TAG "nativebridge" + +#include "nativebridge/native_bridge.h" + +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <cstring> + +#include <android-base/macros.h> +#include <log/log.h> + +namespace android { + +#ifdef __APPLE__ +template <typename T> +void UNUSED(const T&) {} +#endif + +extern "C" { + +// Environment values required by the apps running with native bridge. +struct NativeBridgeRuntimeValues { + const char* os_arch; + const char* cpu_abi; + const char* cpu_abi2; + const char* *supported_abis; + int32_t abi_count; +}; + +// The symbol name exposed by native-bridge with the type of NativeBridgeCallbacks. +static constexpr const char* kNativeBridgeInterfaceSymbol = "NativeBridgeItf"; + +enum class NativeBridgeState { + kNotSetup, // Initial state. + kOpened, // After successful dlopen. + kPreInitialized, // After successful pre-initialization. + kInitialized, // After successful initialization. + kClosed // Closed or errors. +}; + +static constexpr const char* kNotSetupString = "kNotSetup"; +static constexpr const char* kOpenedString = "kOpened"; +static constexpr const char* kPreInitializedString = "kPreInitialized"; +static constexpr const char* kInitializedString = "kInitialized"; +static constexpr const char* kClosedString = "kClosed"; + +static const char* GetNativeBridgeStateString(NativeBridgeState state) { + switch (state) { + case NativeBridgeState::kNotSetup: + return kNotSetupString; + + case NativeBridgeState::kOpened: + return kOpenedString; + + case NativeBridgeState::kPreInitialized: + return kPreInitializedString; + + case NativeBridgeState::kInitialized: + return kInitializedString; + + case NativeBridgeState::kClosed: + return kClosedString; + } +} + +// 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, + // The version with vendor namespaces + VENDOR_NAMESPACE_VERSION = 4, + // The version with runtime namespaces + RUNTIME_NAMESPACE_VERSION = 5, +}; + +// Whether we had an error at some point. +static bool had_error = false; + +// Handle of the loaded library. +static void* native_bridge_handle = nullptr; +// Pointer to the callbacks. Available as soon as LoadNativeBridge succeeds, but only initialized +// later. +static const NativeBridgeCallbacks* callbacks = nullptr; +// Callbacks provided by the environment to the bridge. Passed to LoadNativeBridge. +static const NativeBridgeRuntimeCallbacks* runtime_callbacks = nullptr; + +// The app's code cache directory. +static char* app_code_cache_dir = nullptr; + +// Code cache directory (relative to the application private directory) +// Ideally we'd like to call into framework to retrieve this name. However that's considered an +// implementation detail and will require either hacks or consistent refactorings. We compromise +// and hard code the directory name again here. +static constexpr const char* kCodeCacheDir = "code_cache"; + +// 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) { + if (first) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + } else { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || + (c == '.') || (c == '_') || (c == '-'); + } +} + +static void ReleaseAppCodeCacheDir() { + if (app_code_cache_dir != nullptr) { + delete[] app_code_cache_dir; + app_code_cache_dir = nullptr; + } +} + +// We only allow simple names for the library. It is supposed to be a file in +// /system/lib or /vendor/lib. Only allow a small range of characters, that is +// names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z]. +bool NativeBridgeNameAcceptable(const char* nb_library_filename) { + const char* ptr = nb_library_filename; + if (*ptr == 0) { + // Emptry string. Allowed, means no native bridge. + return true; + } else { + // First character must be [a-zA-Z]. + if (!CharacterAllowed(*ptr, true)) { + // Found an invalid fist character, don't accept. + ALOGE("Native bridge library %s has been rejected for first character %c", + nb_library_filename, + *ptr); + return false; + } else { + // For the rest, be more liberal. + ptr++; + while (*ptr != 0) { + if (!CharacterAllowed(*ptr, false)) { + // Found an invalid character, don't accept. + ALOGE("Native bridge library %s has been rejected for %c", nb_library_filename, *ptr); + return false; + } + ptr++; + } + } + return true; + } +} + +// 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 (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 (callbacks->version >= SIGNAL_VERSION) { + return callbacks->isCompatibleWith(version); + } + + return true; +} + +static void CloseNativeBridge(bool with_error) { + state = NativeBridgeState::kClosed; + had_error |= with_error; + ReleaseAppCodeCacheDir(); +} + +bool LoadNativeBridge(const char* nb_library_filename, + const NativeBridgeRuntimeCallbacks* runtime_cbs) { + // We expect only one place that calls LoadNativeBridge: Runtime::Init. At that point we are not + // multi-threaded, so we do not need locking here. + + if (state != NativeBridgeState::kNotSetup) { + // Setup has been called before. Ignore this call. + if (nb_library_filename != nullptr) { // Avoids some log-spam for dalvikvm. + ALOGW("Called LoadNativeBridge for an already set up native bridge. State is %s.", + GetNativeBridgeStateString(state)); + } + // Note: counts as an error, even though the bridge may be functional. + had_error = true; + return false; + } + + if (nb_library_filename == nullptr || *nb_library_filename == 0) { + CloseNativeBridge(false); + return false; + } else { + if (!NativeBridgeNameAcceptable(nb_library_filename)) { + CloseNativeBridge(true); + } else { + // Try to open the library. + void* handle = dlopen(nb_library_filename, RTLD_LAZY); + if (handle != nullptr) { + callbacks = reinterpret_cast<NativeBridgeCallbacks*>(dlsym(handle, + kNativeBridgeInterfaceSymbol)); + if (callbacks != nullptr) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + // Store the handle for later. + native_bridge_handle = handle; + } else { + callbacks = nullptr; + dlclose(handle); + ALOGW("Unsupported native bridge interface."); + } + } else { + dlclose(handle); + } + } + + // Two failure conditions: could not find library (dlopen failed), or could not find native + // bridge interface (dlsym failed). Both are an error and close the native bridge. + if (callbacks == nullptr) { + CloseNativeBridge(true); + } else { + runtime_callbacks = runtime_cbs; + state = NativeBridgeState::kOpened; + } + } + return state == NativeBridgeState::kOpened; + } +} + +bool NeedsNativeBridge(const char* instruction_set) { + if (instruction_set == nullptr) { + ALOGE("Null instruction set in NeedsNativeBridge."); + return false; + } + return strncmp(instruction_set, ABI_STRING, strlen(ABI_STRING) + 1) != 0; +} + +bool PreInitializeNativeBridge(const char* app_data_dir_in, const char* instruction_set) { + if (state != NativeBridgeState::kOpened) { + ALOGE("Invalid state: native bridge is expected to be opened."); + CloseNativeBridge(true); + return false; + } + + if (app_data_dir_in == nullptr) { + ALOGE("Application private directory cannot be null."); + CloseNativeBridge(true); + return false; + } + + // Create the path to the application code cache directory. + // The memory will be release after Initialization or when the native bridge is closed. + const size_t len = strlen(app_data_dir_in) + strlen(kCodeCacheDir) + 2; // '\0' + '/' + app_code_cache_dir = new char[len]; + snprintf(app_code_cache_dir, len, "%s/%s", app_data_dir_in, kCodeCacheDir); + + // Bind-mount /system/lib{,64}/<isa>/cpuinfo to /proc/cpuinfo. + // Failure is not fatal and will keep the native bridge in kPreInitialized. + state = NativeBridgeState::kPreInitialized; + +#ifndef __APPLE__ + if (instruction_set == nullptr) { + return true; + } + size_t isa_len = strlen(instruction_set); + if (isa_len > 10) { + // 10 is a loose upper bound on the currently known instruction sets (a tight bound is 7 for + // x86_64 [including the trailing \0]). This is so we don't have to change here if there will + // be another instruction set in the future. + ALOGW("Instruction set %s is malformed, must be less than or equal to 10 characters.", + instruction_set); + return true; + } + + // If the file does not exist, the mount command will fail, + // so we save the extra file existence check. + char cpuinfo_path[1024]; + +#if defined(__ANDROID__) + snprintf(cpuinfo_path, sizeof(cpuinfo_path), "/system/lib" +#ifdef __LP64__ + "64" +#endif // __LP64__ + "/%s/cpuinfo", instruction_set); +#else // !__ANDROID__ + // To be able to test on the host, we hardwire a relative path. + snprintf(cpuinfo_path, sizeof(cpuinfo_path), "./cpuinfo"); +#endif + + // Bind-mount. + if (TEMP_FAILURE_RETRY(mount(cpuinfo_path, // Source. + "/proc/cpuinfo", // Target. + nullptr, // FS type. + MS_BIND, // Mount flags: bind mount. + nullptr)) == -1) { // "Data." + ALOGW("Failed to bind-mount %s as /proc/cpuinfo: %s", cpuinfo_path, strerror(errno)); + } +#else // __APPLE__ + UNUSED(instruction_set); + ALOGW("Mac OS does not support bind-mounting. Host simulation of native bridge impossible."); +#endif + + return true; +} + +static void SetCpuAbi(JNIEnv* env, jclass build_class, const char* field, const char* value) { + if (value != nullptr) { + jfieldID field_id = env->GetStaticFieldID(build_class, field, "Ljava/lang/String;"); + if (field_id == nullptr) { + env->ExceptionClear(); + ALOGW("Could not find %s field.", field); + return; + } + + jstring str = env->NewStringUTF(value); + if (str == nullptr) { + env->ExceptionClear(); + ALOGW("Could not create string %s.", value); + return; + } + + env->SetStaticObjectField(build_class, field_id, str); + } +} + +// Set up the environment for the bridged app. +static void SetupEnvironment(const NativeBridgeCallbacks* callbacks, JNIEnv* env, const char* isa) { + // Need a JNIEnv* to do anything. + if (env == nullptr) { + ALOGW("No JNIEnv* to set up app environment."); + return; + } + + // Query the bridge for environment values. + const struct NativeBridgeRuntimeValues* env_values = callbacks->getAppEnv(isa); + if (env_values == nullptr) { + return; + } + + // Keep the JNIEnv clean. + jint success = env->PushLocalFrame(16); // That should be small and large enough. + if (success < 0) { + // Out of memory, really borked. + ALOGW("Out of memory while setting up app environment."); + env->ExceptionClear(); + return; + } + + // Reset CPU_ABI & CPU_ABI2 to values required by the apps running with native bridge. + if (env_values->cpu_abi != nullptr || env_values->cpu_abi2 != nullptr || + env_values->abi_count >= 0) { + jclass bclass_id = env->FindClass("android/os/Build"); + if (bclass_id != nullptr) { + SetCpuAbi(env, bclass_id, "CPU_ABI", env_values->cpu_abi); + SetCpuAbi(env, bclass_id, "CPU_ABI2", env_values->cpu_abi2); + } else { + // For example in a host test environment. + env->ExceptionClear(); + ALOGW("Could not find Build class."); + } + } + + if (env_values->os_arch != nullptr) { + jclass sclass_id = env->FindClass("java/lang/System"); + if (sclass_id != nullptr) { + jmethodID set_prop_id = env->GetStaticMethodID(sclass_id, "setUnchangeableSystemProperty", + "(Ljava/lang/String;Ljava/lang/String;)V"); + if (set_prop_id != nullptr) { + // Init os.arch to the value reqired by the apps running with native bridge. + env->CallStaticVoidMethod(sclass_id, set_prop_id, env->NewStringUTF("os.arch"), + env->NewStringUTF(env_values->os_arch)); + } else { + env->ExceptionClear(); + ALOGW("Could not find System#setUnchangeableSystemProperty."); + } + } else { + env->ExceptionClear(); + ALOGW("Could not find System class."); + } + } + + // Make it pristine again. + env->PopLocalFrame(nullptr); +} + +bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) { + // We expect only one place that calls InitializeNativeBridge: Runtime::DidForkFromZygote. At that + // point we are not multi-threaded, so we do not need locking here. + + if (state == NativeBridgeState::kPreInitialized) { + // Check for code cache: if it doesn't exist try to create it. + struct stat st; + if (stat(app_code_cache_dir, &st) == -1) { + if (errno == ENOENT) { + if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) { + ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno)); + ReleaseAppCodeCacheDir(); + } + } else { + ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno)); + ReleaseAppCodeCacheDir(); + } + } else if (!S_ISDIR(st.st_mode)) { + ALOGW("Code cache is not a directory %s.", app_code_cache_dir); + ReleaseAppCodeCacheDir(); + } + + // If we're still PreInitialized (dind't fail the code cache checks) try to initialize. + if (state == NativeBridgeState::kPreInitialized) { + if (callbacks->initialize(runtime_callbacks, app_code_cache_dir, instruction_set)) { + SetupEnvironment(callbacks, env, instruction_set); + state = NativeBridgeState::kInitialized; + // We no longer need the code cache path, release the memory. + ReleaseAppCodeCacheDir(); + } else { + // Unload the library. + dlclose(native_bridge_handle); + CloseNativeBridge(true); + } + } + } else { + CloseNativeBridge(true); + } + + return state == NativeBridgeState::kInitialized; +} + +void UnloadNativeBridge() { + // We expect only one place that calls UnloadNativeBridge: Runtime::DidForkFromZygote. At that + // point we are not multi-threaded, so we do not need locking here. + + switch(state) { + case NativeBridgeState::kOpened: + case NativeBridgeState::kPreInitialized: + case NativeBridgeState::kInitialized: + // Unload. + dlclose(native_bridge_handle); + CloseNativeBridge(false); + break; + + case NativeBridgeState::kNotSetup: + // Not even set up. Error. + CloseNativeBridge(true); + break; + + case NativeBridgeState::kClosed: + // Ignore. + break; + } +} + +bool NativeBridgeError() { + return had_error; +} + +bool NativeBridgeAvailable() { + return state == NativeBridgeState::kOpened + || state == NativeBridgeState::kPreInitialized + || state == NativeBridgeState::kInitialized; +} + +bool NativeBridgeInitialized() { + // Calls of this are supposed to happen in a state where the native bridge is stable, i.e., after + // Runtime::DidForkFromZygote. In that case we do not need a lock. + return state == NativeBridgeState::kInitialized; +} + +void* NativeBridgeLoadLibrary(const char* libpath, int flag) { + if (NativeBridgeInitialized()) { + return callbacks->loadLibrary(libpath, flag); + } + return nullptr; +} + +void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, + uint32_t len) { + if (NativeBridgeInitialized()) { + return callbacks->getTrampoline(handle, name, shorty, len); + } + return nullptr; +} + +bool NativeBridgeIsSupported(const char* libpath) { + if (NativeBridgeInitialized()) { + return callbacks->isSupported(libpath); + } + return false; +} + +uint32_t NativeBridgeGetVersion() { + if (NativeBridgeAvailable()) { + return callbacks->version; + } + return 0; +} + +NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int 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; +} + +const char* NativeBridgeGetError() { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + return callbacks->getError(); + } else { + return "native bridge implementation is not compatible with version 3, cannot get message"; + } + } + return "native bridge is not initialized"; +} + +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 NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames, + const char* anon_ns_library_path) { + if (NativeBridgeInitialized()) { + if (isCompatibleWith(NAMESPACE_VERSION)) { + return callbacks->initAnonymousNamespace(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; +} + +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; +} + +native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) { + if (!NativeBridgeInitialized()) { + return nullptr; + } + + if (isCompatibleWith(RUNTIME_NAMESPACE_VERSION)) { + return callbacks->getExportedNamespace(name); + } + + // sphal is vendor namespace name -> use v4 callback in the case NB callbacks + // are not compatible with v5 + if (isCompatibleWith(VENDOR_NAMESPACE_VERSION) && name != nullptr && strcmp("sphal", name) == 0) { + return callbacks->getVendorNamespace(); + } + + 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; +} + +} // extern "C" + +} // namespace android diff --git a/libnativebridge/native_bridge_lazy.cc b/libnativebridge/native_bridge_lazy.cc new file mode 100644 index 0000000000..94c80848ee --- /dev/null +++ b/libnativebridge/native_bridge_lazy.cc @@ -0,0 +1,167 @@ +/* + * 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. + */ + +#include "nativebridge/native_bridge.h" +#define LOG_TAG "nativebridge" + +#include <dlfcn.h> +#include <errno.h> +#include <string.h> + +#include <log/log.h> + +namespace android { + +namespace { + +void* GetLibHandle() { + static void* handle = dlopen("libnativebridge.so", RTLD_NOW); + LOG_FATAL_IF(handle == nullptr, "Failed to load libnativebridge.so: %s", dlerror()); + return handle; +} + +template <typename FuncPtr> +FuncPtr GetFuncPtr(const char* function_name) { + auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name)); + LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror()); + return f; +} + +#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name) + +} // namespace + +bool LoadNativeBridge(const char* native_bridge_library_filename, + const struct NativeBridgeRuntimeCallbacks* runtime_callbacks) { + static auto f = GET_FUNC_PTR(LoadNativeBridge); + return f(native_bridge_library_filename, runtime_callbacks); +} + +bool NeedsNativeBridge(const char* instruction_set) { + static auto f = GET_FUNC_PTR(NeedsNativeBridge); + return f(instruction_set); +} + +bool PreInitializeNativeBridge(const char* app_data_dir, const char* instruction_set) { + static auto f = GET_FUNC_PTR(PreInitializeNativeBridge); + return f(app_data_dir, instruction_set); +} + +bool InitializeNativeBridge(JNIEnv* env, const char* instruction_set) { + static auto f = GET_FUNC_PTR(InitializeNativeBridge); + return f(env, instruction_set); +} + +void UnloadNativeBridge() { + static auto f = GET_FUNC_PTR(UnloadNativeBridge); + return f(); +} + +bool NativeBridgeAvailable() { + static auto f = GET_FUNC_PTR(NativeBridgeAvailable); + return f(); +} + +bool NativeBridgeInitialized() { + static auto f = GET_FUNC_PTR(NativeBridgeInitialized); + return f(); +} + +void* NativeBridgeLoadLibrary(const char* libpath, int flag) { + static auto f = GET_FUNC_PTR(NativeBridgeLoadLibrary); + return f(libpath, flag); +} + +void* NativeBridgeGetTrampoline(void* handle, const char* name, const char* shorty, uint32_t len) { + static auto f = GET_FUNC_PTR(NativeBridgeGetTrampoline); + return f(handle, name, shorty, len); +} + +bool NativeBridgeIsSupported(const char* libpath) { + static auto f = GET_FUNC_PTR(NativeBridgeIsSupported); + return f(libpath); +} + +uint32_t NativeBridgeGetVersion() { + static auto f = GET_FUNC_PTR(NativeBridgeGetVersion); + return f(); +} + +NativeBridgeSignalHandlerFn NativeBridgeGetSignalHandler(int signal) { + static auto f = GET_FUNC_PTR(NativeBridgeGetSignalHandler); + return f(signal); +} + +bool NativeBridgeError() { + static auto f = GET_FUNC_PTR(NativeBridgeError); + return f(); +} + +bool NativeBridgeNameAcceptable(const char* native_bridge_library_filename) { + static auto f = GET_FUNC_PTR(NativeBridgeNameAcceptable); + return f(native_bridge_library_filename); +} + +int NativeBridgeUnloadLibrary(void* handle) { + static auto f = GET_FUNC_PTR(NativeBridgeUnloadLibrary); + return f(handle); +} + +const char* NativeBridgeGetError() { + static auto f = GET_FUNC_PTR(NativeBridgeGetError); + return f(); +} + +bool NativeBridgeIsPathSupported(const char* path) { + static auto f = GET_FUNC_PTR(NativeBridgeIsPathSupported); + return f(path); +} + +bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames, + const char* anon_ns_library_path) { + static auto f = GET_FUNC_PTR(NativeBridgeInitAnonymousNamespace); + return f(public_ns_sonames, anon_ns_library_path); +} + +struct 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, struct native_bridge_namespace_t* parent_ns) { + static auto f = GET_FUNC_PTR(NativeBridgeCreateNamespace); + return f(name, ld_library_path, default_library_path, type, permitted_when_isolated_path, + parent_ns); +} + +bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from, + struct native_bridge_namespace_t* to, + const char* shared_libs_sonames) { + static auto f = GET_FUNC_PTR(NativeBridgeLinkNamespaces); + return f(from, to, shared_libs_sonames); +} + +void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, + struct native_bridge_namespace_t* ns) { + static auto f = GET_FUNC_PTR(NativeBridgeLoadLibraryExt); + return f(libpath, flag, ns); +} + +struct native_bridge_namespace_t* NativeBridgeGetVendorNamespace() { + static auto f = GET_FUNC_PTR(NativeBridgeGetVendorNamespace); + return f(); +} + +#undef GET_FUNC_PTR + +} // namespace android diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp new file mode 100644 index 0000000000..2bb8467385 --- /dev/null +++ b/libnativebridge/tests/Android.bp @@ -0,0 +1,110 @@ +// +// Copyright (C) 2017 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. +// + +cc_defaults { + name: "libnativebridge-dummy-defaults", + + host_supported: true, + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + header_libs: ["libnativebridge-headers"], + cppflags: ["-fvisibility=protected"], +} + +cc_library_shared { + name: "libnativebridge-dummy", + srcs: ["DummyNativeBridge.cpp"], + defaults: ["libnativebridge-dummy-defaults"], +} + +cc_library_shared { + name: "libnativebridge2-dummy", + srcs: ["DummyNativeBridge2.cpp"], + defaults: ["libnativebridge-dummy-defaults"], +} + +cc_library_shared { + name: "libnativebridge3-dummy", + srcs: ["DummyNativeBridge3.cpp"], + defaults: ["libnativebridge-dummy-defaults"], +} + +// Build the unit tests. +cc_defaults { + name: "libnativebridge-tests-defaults", + test_per_src: true, + + cflags: [ + "-Wall", + "-Werror", + ], + + srcs: [ + "CodeCacheCreate_test.cpp", + "CodeCacheExists_test.cpp", + "CodeCacheStatFail_test.cpp", + "CompleteFlow_test.cpp", + "InvalidCharsNativeBridge_test.cpp", + "NativeBridge2Signal_test.cpp", + "NativeBridgeVersion_test.cpp", + "NeedsNativeBridge_test.cpp", + "PreInitializeNativeBridge_test.cpp", + "PreInitializeNativeBridgeFail1_test.cpp", + "PreInitializeNativeBridgeFail2_test.cpp", + "ReSetupNativeBridge_test.cpp", + "UnavailableNativeBridge_test.cpp", + "ValidNameNativeBridge_test.cpp", + "NativeBridge3UnloadLibrary_test.cpp", + "NativeBridge3GetError_test.cpp", + "NativeBridge3IsPathSupported_test.cpp", + "NativeBridge3InitAnonymousNamespace_test.cpp", + "NativeBridge3CreateNamespace_test.cpp", + "NativeBridge3LoadLibraryExt_test.cpp", + ], + + shared_libs: [ + "liblog", + "libnativebridge-dummy", + ], + header_libs: ["libbase_headers"], +} + +cc_test { + name: "libnativebridge-tests", + defaults: ["libnativebridge-tests-defaults"], + host_supported: true, + shared_libs: ["libnativebridge"], +} + +cc_test { + name: "libnativebridge-lazy-tests", + defaults: ["libnativebridge-tests-defaults"], + shared_libs: ["libnativebridge_lazy"], +} + +// Build the test for the C API. +cc_test { + name: "libnativebridge-api-tests", + host_supported: true, + test_per_src: true, + srcs: [ + "NativeBridgeApi.c", + ], + header_libs: ["libnativebridge-headers"], +} diff --git a/libnativebridge/tests/CodeCacheCreate_test.cpp b/libnativebridge/tests/CodeCacheCreate_test.cpp new file mode 100644 index 0000000000..58270c43dd --- /dev/null +++ b/libnativebridge/tests/CodeCacheCreate_test.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 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" + +#include <errno.h> +#include <sys/stat.h> +#include <unistd.h> + +namespace android { + +// Tests that the bridge initialization creates the code_cache if it doesn't +// exists. +TEST_F(NativeBridgeTest, CodeCacheCreate) { + // Make sure that code_cache does not exists + struct stat st; + ASSERT_EQ(-1, stat(kCodeCache, &st)); + ASSERT_EQ(ENOENT, errno); + + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_FALSE(NativeBridgeError()); + + // Check that code_cache was created + ASSERT_EQ(0, stat(kCodeCache, &st)); + ASSERT_TRUE(S_ISDIR(st.st_mode)); + + // Clean up + UnloadNativeBridge(); + ASSERT_EQ(0, rmdir(kCodeCache)); + + ASSERT_FALSE(NativeBridgeError()); +} + +} // namespace android diff --git a/libnativebridge/tests/CodeCacheExists_test.cpp b/libnativebridge/tests/CodeCacheExists_test.cpp new file mode 100644 index 0000000000..8ba01586b5 --- /dev/null +++ b/libnativebridge/tests/CodeCacheExists_test.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2014 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" + +#include <errno.h> +#include <sys/stat.h> +#include <unistd.h> + +namespace android { + +// Tests that the bridge is initialized without errors if the code_cache already +// exists. +TEST_F(NativeBridgeTest, CodeCacheExists) { + // Make sure that code_cache does not exists + struct stat st; + ASSERT_EQ(-1, stat(kCodeCache, &st)); + ASSERT_EQ(ENOENT, errno); + + // Create the code_cache + ASSERT_EQ(0, mkdir(kCodeCache, S_IRWXU | S_IRWXG | S_IXOTH)); + + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_FALSE(NativeBridgeError()); + + // Check that the code cache is still there + ASSERT_EQ(0, stat(kCodeCache, &st)); + ASSERT_TRUE(S_ISDIR(st.st_mode)); + + // Clean up + UnloadNativeBridge(); + ASSERT_EQ(0, rmdir(kCodeCache)); + + ASSERT_FALSE(NativeBridgeError()); +} + +} // namespace android diff --git a/libnativebridge/tests/CodeCacheStatFail_test.cpp b/libnativebridge/tests/CodeCacheStatFail_test.cpp new file mode 100644 index 0000000000..4ea519ee96 --- /dev/null +++ b/libnativebridge/tests/CodeCacheStatFail_test.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 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" + +#include <errno.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> + +namespace android { + +// Tests that the bridge is initialized without errors if the code_cache is +// existed as a file. +TEST_F(NativeBridgeTest, CodeCacheStatFail) { + int fd = creat(kCodeCache, O_RDWR); + ASSERT_NE(-1, fd); + close(fd); + + struct stat st; + ASSERT_EQ(-1, stat(kCodeCacheStatFail, &st)); + ASSERT_EQ(ENOTDIR, errno); + + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); + ASSERT_TRUE(PreInitializeNativeBridge(kCodeCacheStatFail, "isa")); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_FALSE(NativeBridgeError()); + + // Clean up + UnloadNativeBridge(); + + ASSERT_FALSE(NativeBridgeError()); + unlink(kCodeCache); +} + +} // namespace android diff --git a/libnativebridge/tests/CompleteFlow_test.cpp b/libnativebridge/tests/CompleteFlow_test.cpp new file mode 100644 index 0000000000..b033792301 --- /dev/null +++ b/libnativebridge/tests/CompleteFlow_test.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 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" + +#include <unistd.h> + +namespace android { + +TEST_F(NativeBridgeTest, CompleteFlow) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + // Basic calls to check that nothing crashes + ASSERT_FALSE(NativeBridgeIsSupported(nullptr)); + ASSERT_EQ(nullptr, NativeBridgeLoadLibrary(nullptr, 0)); + ASSERT_EQ(nullptr, NativeBridgeGetTrampoline(nullptr, nullptr, nullptr, 0)); + + // Unload + UnloadNativeBridge(); + + ASSERT_FALSE(NativeBridgeAvailable()); + ASSERT_FALSE(NativeBridgeError()); + + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); +} + +} // namespace android diff --git a/libnativebridge/tests/DummyNativeBridge.cpp b/libnativebridge/tests/DummyNativeBridge.cpp new file mode 100644 index 0000000000..b9894f6b95 --- /dev/null +++ b/libnativebridge/tests/DummyNativeBridge.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 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" + +// NativeBridgeCallbacks implementations +extern "C" bool native_bridge_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */, + const char* /* app_code_cache_dir */, + const char* /* isa */) { + return true; +} + +extern "C" void* native_bridge_loadLibrary(const char* /* libpath */, int /* flag */) { + return nullptr; +} + +extern "C" void* native_bridge_getTrampoline(void* /* handle */, const char* /* name */, + const char* /* shorty */, uint32_t /* len */) { + return nullptr; +} + +extern "C" bool native_bridge_isSupported(const char* /* libpath */) { + return false; +} + +extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge_getAppEnv( + const char* /* abi */) { + return nullptr; +} + +android::NativeBridgeCallbacks NativeBridgeItf { + .version = 1, + .initialize = &native_bridge_initialize, + .loadLibrary = &native_bridge_loadLibrary, + .getTrampoline = &native_bridge_getTrampoline, + .isSupported = &native_bridge_isSupported, + .getAppEnv = &native_bridge_getAppEnv +}; diff --git a/libnativebridge/tests/DummyNativeBridge2.cpp b/libnativebridge/tests/DummyNativeBridge2.cpp new file mode 100644 index 0000000000..6920c74822 --- /dev/null +++ b/libnativebridge/tests/DummyNativeBridge2.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2014 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_bridge2_initialize(const android::NativeBridgeRuntimeCallbacks* /* art_cbs */, + const char* /* app_code_cache_dir */, + const char* /* isa */) { + return true; +} + +extern "C" void* native_bridge2_loadLibrary(const char* /* libpath */, int /* flag */) { + return nullptr; +} + +extern "C" void* native_bridge2_getTrampoline(void* /* handle */, const char* /* name */, + const char* /* shorty */, uint32_t /* len */) { + return nullptr; +} + +extern "C" bool native_bridge2_isSupported(const char* /* libpath */) { + return false; +} + +extern "C" const struct android::NativeBridgeRuntimeValues* native_bridge2_getAppEnv( + const char* /* abi */) { + return nullptr; +} + +extern "C" bool native_bridge2_is_compatible_compatible_with(uint32_t version) { + // For testing, allow 1 and 2, but disallow 3+. + return version <= 2; +} + +static bool native_bridge2_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_bridge2_get_signal_handler(int signal) { + if (signal == SIGSEGV) { + return &native_bridge2_dummy_signal_handler; + } + return nullptr; +} + +android::NativeBridgeCallbacks NativeBridgeItf { + .version = 2, + .initialize = &native_bridge2_initialize, + .loadLibrary = &native_bridge2_loadLibrary, + .getTrampoline = &native_bridge2_getTrampoline, + .isSupported = &native_bridge2_isSupported, + .getAppEnv = &native_bridge2_getAppEnv, + .isCompatibleWith = &native_bridge2_is_compatible_compatible_with, + .getSignalHandler = &native_bridge2_get_signal_handler +}; + diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp new file mode 100644 index 0000000000..4ef1c82733 --- /dev/null +++ b/libnativebridge/tests/DummyNativeBridge3.cpp @@ -0,0 +1,124 @@ +/* + * 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" const char* native_bridge3_getError() { + return nullptr; +} + +extern "C" bool native_bridge3_isPathSupported(const char* /* path */) { + return true; +} + +extern "C" bool native_bridge3_initAnonymousNamespace(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" 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, + .initAnonymousNamespace = &native_bridge3_initAnonymousNamespace, + .createNamespace = &native_bridge3_createNamespace, + .linkNamespaces = &native_bridge3_linkNamespaces, + .loadLibraryExt = &native_bridge3_loadLibraryExt}; diff --git a/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp new file mode 100644 index 0000000000..8f7973df09 --- /dev/null +++ b/libnativebridge/tests/InvalidCharsNativeBridge_test.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 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 { + +static const char* kTestName = "../librandom$@-bridge_not.existing.so"; + +TEST_F(NativeBridgeTest, InvalidChars) { + // Do one test actually calling setup. + EXPECT_EQ(false, NativeBridgeError()); + LoadNativeBridge(kTestName, nullptr); + // This should lead to an error for invalid characters. + EXPECT_EQ(true, NativeBridgeError()); + + // Further tests need to use NativeBridgeNameAcceptable, as the error + // state can't be changed back. + EXPECT_EQ(false, NativeBridgeNameAcceptable(".")); + EXPECT_EQ(false, NativeBridgeNameAcceptable("..")); + EXPECT_EQ(false, NativeBridgeNameAcceptable("_")); + EXPECT_EQ(false, NativeBridgeNameAcceptable("-")); + EXPECT_EQ(false, NativeBridgeNameAcceptable("lib@.so")); + EXPECT_EQ(false, NativeBridgeNameAcceptable("lib$.so")); +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridge2Signal_test.cpp b/libnativebridge/tests/NativeBridge2Signal_test.cpp new file mode 100644 index 0000000000..44e45e3622 --- /dev/null +++ b/libnativebridge/tests/NativeBridge2Signal_test.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 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" + +#include <signal.h> +#include <unistd.h> + +namespace android { + +constexpr const char* kNativeBridgeLibrary2 = "libnativebridge2-dummy.so"; + +TEST_F(NativeBridgeTest, V2_Signal) { + // Init + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary2, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(PreInitializeNativeBridge(".", "isa")); + ASSERT_TRUE(NativeBridgeAvailable()); + ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr)); + ASSERT_TRUE(NativeBridgeAvailable()); + + ASSERT_EQ(2U, NativeBridgeGetVersion()); + ASSERT_NE(nullptr, NativeBridgeGetSignalHandler(SIGSEGV)); + + // Clean-up code_cache + ASSERT_EQ(0, rmdir(kCodeCache)); +} + +} // namespace android diff --git a/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp b/libnativebridge/tests/NativeBridge3CreateNamespace_test.cpp new file mode 100644 index 0000000000..668d9428a0 --- /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 0000000000..0b9f5821f2 --- /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/NativeBridge3InitAnonymousNamespace_test.cpp b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_test.cpp new file mode 100644 index 0000000000..b0d6b09d13 --- /dev/null +++ b/libnativebridge/tests/NativeBridge3InitAnonymousNamespace_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_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, NativeBridgeInitAnonymousNamespace(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 0000000000..325e40b408 --- /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 0000000000..4caeb4481f --- /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 0000000000..93a979c0a0 --- /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/NativeBridgeApi.c b/libnativebridge/tests/NativeBridgeApi.c new file mode 100644 index 0000000000..7ab71fe814 --- /dev/null +++ b/libnativebridge/tests/NativeBridgeApi.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 "nativebridge/native_bridge.h" + +int main(int argc, char** argv) { + (void)argc; + (void)argv; + return 0; +} diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h new file mode 100644 index 0000000000..0f99816c9f --- /dev/null +++ b/libnativebridge/tests/NativeBridgeTest.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef NATIVE_BRIDGE_TEST_H_ +#define NATIVE_BRIDGE_TEST_H_ + +#define LOG_TAG "NativeBridge_test" + +#include <nativebridge/native_bridge.h> +#include <gtest/gtest.h> + +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 { + +class NativeBridgeTest : public testing::Test { +}; + +}; // namespace android + +#endif // NATIVE_BRIDGE_H_ + diff --git a/libnativebridge/tests/NativeBridgeVersion_test.cpp b/libnativebridge/tests/NativeBridgeVersion_test.cpp new file mode 100644 index 0000000000..d3f9a80fc9 --- /dev/null +++ b/libnativebridge/tests/NativeBridgeVersion_test.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 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" + +#include <unistd.h> + +namespace android { + +TEST_F(NativeBridgeTest, Version) { + // When a bridge isn't loaded, we expect 0. + EXPECT_EQ(NativeBridgeGetVersion(), 0U); + + // After our dummy bridge has been loaded, we expect 1. + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); + EXPECT_EQ(NativeBridgeGetVersion(), 1U); + + // Unload + UnloadNativeBridge(); + + // Version information is gone. + EXPECT_EQ(NativeBridgeGetVersion(), 0U); +} + +} // namespace android diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp new file mode 100644 index 0000000000..c8ff743b05 --- /dev/null +++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 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" + +#include <android-base/macros.h> + +namespace android { + +static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm", + "64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr }; + +TEST_F(NativeBridgeTest, NeedsNativeBridge) { + EXPECT_EQ(false, NeedsNativeBridge(ABI_STRING)); + + const size_t kISACount = sizeof(kISAs) / sizeof(kISAs[0]); + for (size_t i = 0; i < kISACount; i++) { + EXPECT_EQ(kISAs[i] == nullptr ? false : strcmp(kISAs[i], ABI_STRING) != 0, + NeedsNativeBridge(kISAs[i])); + } +} + +} // namespace android diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp new file mode 100644 index 0000000000..5a2b0a150f --- /dev/null +++ b/libnativebridge/tests/PreInitializeNativeBridgeFail1_test.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 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" + +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include <cstdio> +#include <cstring> + +#include <android/log.h> + +namespace android { + +TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail1) { + // Needs a valid application directory. + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); + ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa")); + ASSERT_TRUE(NativeBridgeError()); +} + +} // namespace android diff --git a/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp new file mode 100644 index 0000000000..af976b1745 --- /dev/null +++ b/libnativebridge/tests/PreInitializeNativeBridgeFail2_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 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 <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include <cstdio> +#include <cstring> + +#include <android/log.h> + +#include "NativeBridgeTest.h" + +namespace android { + +TEST_F(NativeBridgeTest, PreInitializeNativeBridgeFail2) { + // Needs LoadNativeBridge() first + ASSERT_FALSE(PreInitializeNativeBridge(nullptr, "isa")); + ASSERT_TRUE(NativeBridgeError()); +} + +} // namespace android diff --git a/libnativebridge/tests/PreInitializeNativeBridge_test.cpp b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp new file mode 100644 index 0000000000..cd5a8e24bb --- /dev/null +++ b/libnativebridge/tests/PreInitializeNativeBridge_test.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2014 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 <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include <cstdio> +#include <cstring> + +#include <android/log.h> + +#include "NativeBridgeTest.h" + +namespace android { + +TEST_F(NativeBridgeTest, PreInitializeNativeBridge) { + ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr)); +#if !defined(__APPLE__) // Mac OS does not support bind-mount. +#if !defined(__ANDROID__) // Cannot write into the hard-wired location. + static constexpr const char* kTestData = "PreInitializeNativeBridge test."; + + // Try to create our mount namespace. + if (unshare(CLONE_NEWNS) != -1) { + // Create a dummy file. + FILE* cpuinfo = fopen("./cpuinfo", "w"); + ASSERT_NE(nullptr, cpuinfo) << strerror(errno); + fprintf(cpuinfo, kTestData); + fclose(cpuinfo); + + ASSERT_TRUE(PreInitializeNativeBridge("does not matter 1", "short 2")); + + // Read /proc/cpuinfo + FILE* proc_cpuinfo = fopen("/proc/cpuinfo", "r"); + ASSERT_NE(nullptr, proc_cpuinfo) << strerror(errno); + char buf[1024]; + EXPECT_NE(nullptr, fgets(buf, sizeof(buf), proc_cpuinfo)) << "Error reading."; + fclose(proc_cpuinfo); + + EXPECT_EQ(0, strcmp(buf, kTestData)); + + // Delete the file. + ASSERT_EQ(0, unlink("./cpuinfo")) << "Error unlinking temporary file."; + // Ending the test will tear down the mount namespace. + } else { + GTEST_LOG_(WARNING) << "Could not create mount namespace. Are you running this as root?"; + } +#endif +#endif +} + +} // namespace android diff --git a/libnativebridge/tests/ReSetupNativeBridge_test.cpp b/libnativebridge/tests/ReSetupNativeBridge_test.cpp new file mode 100644 index 0000000000..944e5d7e98 --- /dev/null +++ b/libnativebridge/tests/ReSetupNativeBridge_test.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2014 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 { + +TEST_F(NativeBridgeTest, ReSetup) { + EXPECT_EQ(false, NativeBridgeError()); + LoadNativeBridge("", nullptr); + EXPECT_EQ(false, NativeBridgeError()); + LoadNativeBridge("", nullptr); + // This should lead to an error for trying to re-setup a native bridge. + EXPECT_EQ(true, NativeBridgeError()); +} + +} // namespace android diff --git a/libnativebridge/tests/UnavailableNativeBridge_test.cpp b/libnativebridge/tests/UnavailableNativeBridge_test.cpp new file mode 100644 index 0000000000..ad374a5e96 --- /dev/null +++ b/libnativebridge/tests/UnavailableNativeBridge_test.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011 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 { + +TEST_F(NativeBridgeTest, NoNativeBridge) { + EXPECT_EQ(false, NativeBridgeAvailable()); + // Try to initialize. This should fail as we are not set up. + EXPECT_EQ(false, InitializeNativeBridge(nullptr, nullptr)); + EXPECT_EQ(true, NativeBridgeError()); + EXPECT_EQ(false, NativeBridgeAvailable()); +} + +} // namespace android diff --git a/libnativebridge/tests/ValidNameNativeBridge_test.cpp b/libnativebridge/tests/ValidNameNativeBridge_test.cpp new file mode 100644 index 0000000000..690be4a302 --- /dev/null +++ b/libnativebridge/tests/ValidNameNativeBridge_test.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 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 { + +static const char* kTestName = "librandom-bridge_not.existing.so"; + +TEST_F(NativeBridgeTest, ValidName) { + // Check that the name is acceptable. + EXPECT_EQ(true, NativeBridgeNameAcceptable(kTestName)); + + // Now check what happens on LoadNativeBridge. + EXPECT_EQ(false, NativeBridgeError()); + LoadNativeBridge(kTestName, nullptr); + // This will lead to an error as the library doesn't exist. + EXPECT_EQ(true, NativeBridgeError()); + EXPECT_EQ(false, NativeBridgeAvailable()); +} + +} // namespace android diff --git a/libnativeloader/.clang-format b/libnativeloader/.clang-format new file mode 120000 index 0000000000..fd0645fdf9 --- /dev/null +++ b/libnativeloader/.clang-format @@ -0,0 +1 @@ +../.clang-format-2
\ No newline at end of file diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp new file mode 100644 index 0000000000..2ee9d28274 --- /dev/null +++ b/libnativeloader/Android.bp @@ -0,0 +1,97 @@ +// Shared library for target +// ======================================================== +cc_defaults { + name: "libnativeloader-defaults", + cflags: [ + "-Werror", + "-Wall", + ], + cppflags: [ + "-fvisibility=hidden", + ], + header_libs: ["libnativeloader-headers"], + export_header_lib_headers: ["libnativeloader-headers"], +} + +cc_library { + name: "libnativeloader", + defaults: ["libnativeloader-defaults"], + // TODO(oth): remove after moving under art/ (b/137364733) + visibility: ["//visibility:public"], + host_supported: true, + srcs: [ + "native_loader.cpp", + ], + shared_libs: [ + "libnativehelper", + "liblog", + "libnativebridge", + "libbase", + ], + target: { + android: { + srcs: [ + "library_namespaces.cpp", + "native_loader_namespace.cpp", + "public_libraries.cpp", + ], + shared_libs: [ + "libdl_android", + ], + }, + }, + required: [ + "llndk.libraries.txt", + "vndksp.libraries.txt", + ], + stubs: { + symbol_file: "libnativeloader.map.txt", + versions: ["1"], + }, +} + +// TODO(b/124250621) eliminate the need for this library +cc_library { + name: "libnativeloader_lazy", + defaults: ["libnativeloader-defaults"], + // TODO(oth): remove after moving under art/ (b/137364733) + visibility: ["//visibility:public"], + host_supported: false, + srcs: ["native_loader_lazy.cpp"], + required: ["libnativeloader"], +} + +cc_library_headers { + name: "libnativeloader-headers", + // TODO(oth): remove after moving under art/ (b/137364733) + visibility: ["//visibility:public"], + host_supported: true, + export_include_dirs: ["include"], +} + +cc_test { + name: "libnativeloader_test", + srcs: [ + "native_loader_test.cpp", + "native_loader.cpp", + "library_namespaces.cpp", + "native_loader_namespace.cpp", + "public_libraries.cpp", + ], + cflags: ["-DANDROID"], + static_libs: [ + "libbase", + "liblog", + "libnativehelper", + "libgmock", + ], + header_libs: [ + "libnativebridge-headers", + "libnativeloader-headers", + ], + system_shared_libs: [ + "libc", + "libm", + ], + test_suites: ["device-tests"], +} diff --git a/libnativeloader/CPPLINT.cfg b/libnativeloader/CPPLINT.cfg new file mode 100644 index 0000000000..db98533fc7 --- /dev/null +++ b/libnativeloader/CPPLINT.cfg @@ -0,0 +1,19 @@ +# +# 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. +# + +filter=-build/header_guard +filter=-readability/check +filter=-build/namespaces diff --git a/libnativeloader/OWNERS b/libnativeloader/OWNERS new file mode 100644 index 0000000000..f7356530ab --- /dev/null +++ b/libnativeloader/OWNERS @@ -0,0 +1,6 @@ +dimitry@google.com +jiyong@google.com +ngeoffray@google.com +oth@google.com +mast@google.com +rpl@google.com diff --git a/libnativeloader/README.md b/libnativeloader/README.md new file mode 100644 index 0000000000..57b90019e3 --- /dev/null +++ b/libnativeloader/README.md @@ -0,0 +1,84 @@ +libnativeloader +=============================================================================== + +Overview +------------------------------------------------------------------------------- +libnativeloader is responsible for loading native shared libraries (`*.so` +files) inside the Android Runtime (ART). The native shared libraries could be +app-provided JNI libraries or public native libraries like `libc.so` provided +by the platform. + +The most typical use case of this library is calling `System.loadLibrary(name)`. +When the method is called, the ART runtime delegates the call to this library +along with the reference to the classloader where the call was made. Then this +library finds the linker namespace (named `classloader-namespace`) that is +associated with the given classloader, and tries to load the requested library +from the namespace. The actual searching, loading, and linking of the library +is performed by the dynamic linker. + +The linker namespace is created when an APK is loaded into the process, and is +associated with the classloader that loaded the APK. The linker namespace is +configured so that only the JNI libraries embedded in the APK is accessible +from the namespace, thus preventing an APK from loading JNI libraries of other +APKs. + +The linker namespace is also configured differently depending on other +characteristics of the APK such as whether or not the APK is bundled with the +platform. In case of the unbundled, i.e., downloaded or updated APK, only the +public native libraries that is listed in `/system/etc/public.libraries.txt` +are available from the platform, whereas in case of the bundled, all libraries +under `/system/lib` are available (i.e. shared). In case when the unbundled +app is from `/vendor` or `/product` partition, the app is additionally provided +with the [VNDK-SP](https://source.android.com/devices/architecture/vndk#sp-hal) +libraries. As the platform is getting modularized with +[APEX](https://android.googlesource.com/platform/system/apex/+/refs/heads/master/docs/README.md), +some libraries are no longer provided from platform, but from the APEXes which +have their own linker namespaces. For example, ICU libraries `libicuuc.so` and +`libicui18n.so` are from the runtime APEX. + +The list of public native libraries is not static. The default set of libraries +are defined in AOSP, but partners can extend it to include their own libraries. +Currently, following extensions are available: + +- `/vendor/etc/public.libraries.txt`: libraries in `/vendor/lib` that are +specific to the underlying SoC, e.g. GPU, DSP, etc. +- `/{system|product}/etc/public.libraries-<companyname>.txt`: libraries in +`/{system|product}/lib` that a device manufacturer has newly added. The +libraries should be named as `lib<name>.<companyname>.so` as in +`libFoo.acme.so`. + +Note that, due to the naming constraint requiring `.<companyname>.so` suffix, it +is prohibited for a device manufacturer to expose an AOSP-defined private +library, e.g. libgui.so, libart.so, etc., to APKs. + +Lastly, libnativeloader is responsible for abstracting the two types of the +dynamic linker interface: `libdl.so` and `libnativebridge.so`. The former is +for non-translated, e.g. ARM-on-ARM, libraries, while the latter is for +loading libraries in a translated environment such as ARM-on-x86. + +Implementation +------------------------------------------------------------------------------- +Implementation wise, libnativeloader consists of four parts: + +- `native_loader.cpp` +- `library_namespaces.cpp` +- `native_loader_namespace.cpp` +- `public_libraries.cpp` + +`native_loader.cpp` implements the public interface of this library. It is just +a thin wrapper around `library_namespaces.cpp` and `native_loader_namespace.cpp`. + +`library_namespaces.cpp` implements the singleton class `LibraryNamespaces` which +is a manager-like entity that is responsible for creating and configuring +linker namespaces and finding an already created linker namespace for a given +classloader. + +`native_loader_namespace.cpp` implements the class `NativeLoaderNamespace` that +models a linker namespace. Its main job is to abstract the two types of the +dynamic linker interface so that other parts of this library do not have to know +the differences of the interfaces. + +`public_libraries.cpp` is responsible for reading `*.txt` files for the public +native libraries from the various partitions. It can be considered as a part of +`LibraryNamespaces` but is separated from it to hide the details of the parsing +routines. diff --git a/libnativeloader/TEST_MAPPING b/libnativeloader/TEST_MAPPING new file mode 100644 index 0000000000..7becb77917 --- /dev/null +++ b/libnativeloader/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "libnativeloader_test" + } + ], + "imports": [ + { + "path": "cts/tests/tests/jni" + } + ] +} diff --git a/libnativeloader/include/nativeloader/dlext_namespaces.h b/libnativeloader/include/nativeloader/dlext_namespaces.h new file mode 100644 index 0000000000..8937636cf5 --- /dev/null +++ b/libnativeloader/include/nativeloader/dlext_namespaces.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#ifndef __ANDROID_DLEXT_NAMESPACES_H__ +#define __ANDROID_DLEXT_NAMESPACES_H__ + +#include <android/dlext.h> +#include <stdbool.h> + +__BEGIN_DECLS + +enum { + /* A regular namespace is the namespace with a custom search path that does + * not impose any restrictions on the location of native libraries. + */ + ANDROID_NAMESPACE_TYPE_REGULAR = 0, + + /* An isolated namespace requires all the libraries to be on the search path + * or under permitted_when_isolated_path. The search path is the union of + * ld_library_path and default_library_path. + */ + ANDROID_NAMESPACE_TYPE_ISOLATED = 1, + + /* The shared namespace clones the list of libraries of the caller namespace upon creation + * which means that they are shared between namespaces - the caller namespace and the new one + * will use the same copy of a library if it was loaded prior to android_create_namespace call. + * + * Note that libraries loaded after the namespace is created will not be shared. + * + * Shared namespaces can be isolated or regular. Note that they do not inherit the search path nor + * permitted_path from the caller's namespace. + */ + ANDROID_NAMESPACE_TYPE_SHARED = 2, + + /* This flag instructs linker to enable grey-list workaround for the namespace. + * See http://b/26394120 for details. + */ + ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED = 0x08000000, + + /* This flag instructs linker to use this namespace as the anonymous + * namespace. The anonymous namespace is used in the case when linker cannot + * identify the caller of dlopen/dlsym. This happens for the code not loaded + * by dynamic linker; for example calls from the mono-compiled code. There can + * be only one anonymous namespace in a process. If there already is an + * anonymous namespace in the process, using this flag when creating a new + * namespace causes an error. + */ + ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS = 0x10000000, + + ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = + ANDROID_NAMESPACE_TYPE_SHARED | ANDROID_NAMESPACE_TYPE_ISOLATED, +}; + +/* + * Creates new linker namespace. + * ld_library_path and default_library_path represent the search path + * for the libraries in the namespace. + * + * 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) + * + * When type is ANDROID_NAMESPACE_TYPE_ISOLATED the resulting namespace requires all of + * the libraries to be on the search path or under the permitted_when_isolated_path; + * the search_path is ld_library_path:default_library_path. Note that the + * permitted_when_isolated_path path is not part of the search_path and + * does not affect the search order. It is a way to allow loading libraries from specific + * locations when using absolute path. + * 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, struct android_namespace_t* parent); + +/* + * Creates a link between namespaces. Every link has list of sonames of + * shared libraries. These are the libraries which are accessible from + * namespace 'from' but loaded within namespace 'to' context. + * When to namespace is nullptr this function establishes a link between + * 'from' namespace and the default namespace. + * + * The lookup order of the libraries in namespaces with links is following: + * 1. Look inside current namespace using 'this' namespace search path. + * 2. Look in linked namespaces + * 2.1. Perform soname check - if library soname is not in the list of shared + * libraries sonames skip this link, otherwise + * 2.2. Search library using linked namespace search path. Note that this + * step will not go deeper into linked namespaces for this library but + * will do so for DT_NEEDED libraries. + */ +extern bool android_link_namespaces(struct android_namespace_t* from, + struct android_namespace_t* to, + const char* shared_libs_sonames); + +extern struct android_namespace_t* android_get_exported_namespace(const char* name); + +__END_DECLS + +#endif /* __ANDROID_DLEXT_NAMESPACES_H__ */ diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h new file mode 100644 index 0000000000..51fb875f68 --- /dev/null +++ b/libnativeloader/include/nativeloader/native_loader.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef NATIVE_LOADER_H_ +#define NATIVE_LOADER_H_ + +#include <stdbool.h> +#include <stdint.h> +#include "jni.h" +#if defined(__ANDROID__) +#include <android/dlext.h> +#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(); + +__attribute__((visibility("default"))) jstring CreateClassLoaderNamespace( + JNIEnv* env, int32_t target_sdk_version, jobject class_loader, bool is_shared, jstring dex_path, + jstring library_path, jstring permitted_path); + +__attribute__((visibility("default"))) void* OpenNativeLibrary( + JNIEnv* env, int32_t target_sdk_version, const char* path, jobject class_loader, + const char* caller_location, jstring library_path, bool* needs_native_bridge, char** error_msg); + +__attribute__((visibility("default"))) bool CloseNativeLibrary(void* handle, + const bool needs_native_bridge, + 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"))) 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* OpenNativeLibraryInNamespace( + struct NativeLoaderNamespace* ns, const char* path, bool* needs_native_bridge, + char** error_msg); +#endif + +__attribute__((visibility("default"))) +void ResetNativeLoader(); + +#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 0000000000..40c30bd4a4 --- /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/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp new file mode 100644 index 0000000000..7246b97642 --- /dev/null +++ b/libnativeloader/library_namespaces.cpp @@ -0,0 +1,319 @@ +/* + * 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. + */ +#include "library_namespaces.h" + +#include <dirent.h> +#include <dlfcn.h> + +#include <regex> +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/macros.h> +#include <android-base/properties.h> +#include <android-base/strings.h> +#include <nativehelper/ScopedUtfChars.h> + +#include "nativeloader/dlext_namespaces.h" +#include "public_libraries.h" +#include "utils.h" + +using android::base::Error; + +namespace android::nativeloader { + +namespace { +// The device may be configured to have the vendor libraries loaded to a separate namespace. +// For historical reasons this namespace was named sphal but effectively it is intended +// to use to load vendor libraries to separate namespace with controlled interface between +// vendor and system namespaces. +constexpr const char* kVendorNamespaceName = "sphal"; +constexpr const char* kVndkNamespaceName = "vndk"; +constexpr const char* kArtNamespaceName = "art"; +constexpr const char* kNeuralNetworksNamespaceName = "neuralnetworks"; + +// classloader-namespace is a linker namespace that is created for the loaded +// app. To be specific, it is created for the app classloader. When +// System.load() is called from a Java class that is loaded from the +// classloader, the classloader-namespace namespace associated with that +// classloader is selected for dlopen. The namespace is configured so that its +// search path is set to the app-local JNI directory and it is linked to the +// platform namespace with the names of libs listed in the public.libraries.txt. +// This way an app can only load its own JNI libraries along with the public libs. +constexpr const char* kClassloaderNamespaceName = "classloader-namespace"; +// Same thing for vendor APKs. +constexpr const char* kVendorClassloaderNamespaceName = "vendor-classloader-namespace"; + +// (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. +// This list includes all directories app is allowed to access this way. +constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand"; + +constexpr const char* kVendorLibPath = "/vendor/" LIB; +constexpr const char* kProductLibPath = "/product/" LIB ":/system/product/" LIB; + +const std::regex kVendorDexPathRegex("(^|:)/vendor/"); +const std::regex kProductDexPathRegex("(^|:)(/system)?/product/"); + +// Define origin of APK if it is from vendor partition or product partition +typedef enum { + APK_ORIGIN_DEFAULT = 0, + APK_ORIGIN_VENDOR = 1, + APK_ORIGIN_PRODUCT = 2, +} ApkOrigin; + +jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) { + jclass class_loader_class = env->FindClass("java/lang/ClassLoader"); + jmethodID get_parent = + env->GetMethodID(class_loader_class, "getParent", "()Ljava/lang/ClassLoader;"); + + return env->CallObjectMethod(class_loader, get_parent); +} + +ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) { + ApkOrigin apk_origin = APK_ORIGIN_DEFAULT; + + if (dex_path != nullptr) { + ScopedUtfChars dex_path_utf_chars(env, dex_path); + + if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) { + apk_origin = APK_ORIGIN_VENDOR; + } + + if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) { + LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR, + "Dex path contains both vendor and product partition : %s", + dex_path_utf_chars.c_str()); + + apk_origin = APK_ORIGIN_PRODUCT; + } + } + return apk_origin; +} + +} // namespace + +void LibraryNamespaces::Initialize() { + // Once public namespace is initialized there is no + // point in running this code - it will have no effect + // on the current list of public libraries. + if (initialized_) { + return; + } + + // android_init_namespaces() expects all the public libraries + // to be loaded so that they can be found by soname alone. + // + // TODO(dimitry): this is a bit misleading since we do not know + // if the vendor public library is going to be opened from /vendor/lib + // we might as well end up loading them from /system/lib or /product/lib + // For now we rely on CTS test to catch things like this but + // it should probably be addressed in the future. + for (const auto& soname : android::base::Split(preloadable_public_libraries(), ":")) { + LOG_ALWAYS_FATAL_IF(dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE) == nullptr, + "Error preloading public library %s: %s", soname.c_str(), dlerror()); + } +} + +Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version, + jobject class_loader, bool is_shared, + jstring dex_path, + jstring java_library_path, + jstring java_permitted_path) { + std::string library_path; // empty string by default. + + if (java_library_path != nullptr) { + ScopedUtfChars library_path_utf_chars(env, java_library_path); + library_path = library_path_utf_chars.c_str(); + } + + ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path); + + // (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. + // + // This part effectively allows such a classloader to access anything + // under /data and /mnt/expand + std::string permitted_path = kWhitelistedDirectories; + + if (java_permitted_path != nullptr) { + ScopedUtfChars path(env, java_permitted_path); + if (path.c_str() != nullptr && path.size() > 0) { + permitted_path = permitted_path + ":" + path.c_str(); + } + } + + LOG_ALWAYS_FATAL_IF(FindNamespaceByClassLoader(env, class_loader) != nullptr, + "There is already a namespace associated with this classloader"); + + std::string system_exposed_libraries = default_public_libraries(); + const char* namespace_name = kClassloaderNamespaceName; + bool unbundled_vendor_or_product_app = false; + if ((apk_origin == APK_ORIGIN_VENDOR || + (apk_origin == APK_ORIGIN_PRODUCT && target_sdk_version > 29)) && + !is_shared) { + unbundled_vendor_or_product_app = true; + // For vendor / product apks, give access to the vendor / product lib even though + // they are treated as unbundled; the libs and apks are still bundled + // together in the vendor / product partition. + const char* origin_partition; + const char* origin_lib_path; + + switch (apk_origin) { + case APK_ORIGIN_VENDOR: + origin_partition = "vendor"; + origin_lib_path = kVendorLibPath; + break; + case APK_ORIGIN_PRODUCT: + origin_partition = "product"; + origin_lib_path = kProductLibPath; + break; + default: + origin_partition = "unknown"; + origin_lib_path = ""; + } + library_path = library_path + ":" + origin_lib_path; + permitted_path = permitted_path + ":" + origin_lib_path; + + // Also give access to LLNDK libraries since they are available to vendors + system_exposed_libraries = system_exposed_libraries + ":" + llndk_libraries().c_str(); + + // Different name is useful for debugging + namespace_name = kVendorClassloaderNamespaceName; + ALOGD("classloader namespace configured for unbundled %s apk. library_path=%s", + origin_partition, library_path.c_str()); + } else { + // extended public libraries are NOT available to vendor apks, otherwise it + // would be system->vendor violation. + if (!extended_public_libraries().empty()) { + system_exposed_libraries = system_exposed_libraries + ':' + extended_public_libraries(); + } + } + + // Create the app namespace + NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader); + // Heuristic: the first classloader with non-empty library_path is assumed to + // be the main classloader for app + // TODO(b/139178525) remove this heuristic by determining this in LoadedApk (or its + // friends) and then passing it down to here. + bool is_main_classloader = app_main_namespace_ == nullptr && !library_path.empty(); + // Policy: the namespace for the main classloader is also used as the + // anonymous namespace. + bool also_used_as_anonymous = is_main_classloader; + // Note: this function is executed with g_namespaces_mutex held, thus no + // racing here. + auto app_ns = NativeLoaderNamespace::Create( + namespace_name, library_path, permitted_path, parent_ns, is_shared, + target_sdk_version < 24 /* is_greylist_enabled */, also_used_as_anonymous); + if (!app_ns) { + return app_ns.error(); + } + // ... and link to other namespaces to allow access to some public libraries + bool is_bridged = app_ns->IsBridged(); + + auto platform_ns = NativeLoaderNamespace::GetPlatformNamespace(is_bridged); + if (!platform_ns) { + return platform_ns.error(); + } + + auto linked = app_ns->Link(*platform_ns, system_exposed_libraries); + if (!linked) { + return linked.error(); + } + + auto art_ns = NativeLoaderNamespace::GetExportedNamespace(kArtNamespaceName, is_bridged); + // ART APEX does not exist on host, and under certain build conditions. + if (art_ns) { + linked = app_ns->Link(*art_ns, art_public_libraries()); + if (!linked) { + return linked.error(); + } + } + + // Give access to NNAPI libraries (apex-updated LLNDK library). + auto nnapi_ns = + NativeLoaderNamespace::GetExportedNamespace(kNeuralNetworksNamespaceName, is_bridged); + if (nnapi_ns) { + linked = app_ns->Link(*nnapi_ns, neuralnetworks_public_libraries()); + if (!linked) { + return linked.error(); + } + } + + // Give access to VNDK-SP libraries from the 'vndk' namespace. + if (unbundled_vendor_or_product_app && !vndksp_libraries().empty()) { + auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkNamespaceName, is_bridged); + if (vndk_ns) { + linked = app_ns->Link(*vndk_ns, vndksp_libraries()); + if (!linked) { + return linked.error(); + } + } + } + + if (!vendor_public_libraries().empty()) { + auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged); + // when vendor_ns is not configured, link to the platform namespace + auto target_ns = vendor_ns ? vendor_ns : platform_ns; + if (target_ns) { + linked = app_ns->Link(*target_ns, vendor_public_libraries()); + if (!linked) { + return linked.error(); + } + } + } + + namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), *app_ns)); + if (is_main_classloader) { + app_main_namespace_ = &(*app_ns); + } + + return &(namespaces_.back().second); +} + +NativeLoaderNamespace* LibraryNamespaces::FindNamespaceByClassLoader(JNIEnv* env, + jobject class_loader) { + auto it = std::find_if(namespaces_.begin(), namespaces_.end(), + [&](const std::pair<jweak, NativeLoaderNamespace>& value) { + return env->IsSameObject(value.first, class_loader); + }); + if (it != namespaces_.end()) { + return &it->second; + } + + return nullptr; +} + +NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEnv* env, + jobject class_loader) { + jobject parent_class_loader = GetParentClassLoader(env, class_loader); + + while (parent_class_loader != nullptr) { + NativeLoaderNamespace* ns; + if ((ns = FindNamespaceByClassLoader(env, parent_class_loader)) != nullptr) { + return ns; + } + + parent_class_loader = GetParentClassLoader(env, parent_class_loader); + } + + return nullptr; +} + +} // namespace android::nativeloader diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h new file mode 100644 index 0000000000..7b3efff4df --- /dev/null +++ b/libnativeloader/library_namespaces.h @@ -0,0 +1,67 @@ +/* + * 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. + */ +#pragma once +#if !defined(__ANDROID__) +#error "Not available for host" +#endif + +#define LOG_TAG "nativeloader" + +#include "native_loader_namespace.h" + +#include <list> +#include <string> + +#include <android-base/result.h> +#include <jni.h> + +namespace android::nativeloader { + +using android::base::Result; + +// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace +// objects for an app process. Its main job is to create (and configure) a new +// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing +// object for a given ClassLoader. +class LibraryNamespaces { + public: + LibraryNamespaces() : initialized_(false), app_main_namespace_(nullptr) {} + + LibraryNamespaces(LibraryNamespaces&&) = default; + LibraryNamespaces(const LibraryNamespaces&) = delete; + LibraryNamespaces& operator=(const LibraryNamespaces&) = delete; + + void Initialize(); + void Reset() { + namespaces_.clear(); + initialized_ = false; + app_main_namespace_ = nullptr; + } + Result<NativeLoaderNamespace*> Create(JNIEnv* env, uint32_t target_sdk_version, + jobject class_loader, bool is_shared, jstring dex_path, + jstring java_library_path, jstring java_permitted_path); + NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader); + + private: + Result<void> InitPublicNamespace(const char* library_path); + NativeLoaderNamespace* FindParentNamespaceByClassLoader(JNIEnv* env, jobject class_loader); + + bool initialized_; + NativeLoaderNamespace* app_main_namespace_; + std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_; +}; + +} // namespace android::nativeloader diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp new file mode 100644 index 0000000000..6d3c057b9d --- /dev/null +++ b/libnativeloader/native_loader.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2015 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. + */ + +#define LOG_TAG "nativeloader" + +#include "nativeloader/native_loader.h" + +#include <dlfcn.h> +#include <sys/types.h> + +#include <memory> +#include <mutex> +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/macros.h> +#include <android-base/strings.h> +#include <nativebridge/native_bridge.h> +#include <nativehelper/ScopedUtfChars.h> + +#ifdef __ANDROID__ +#include <log/log.h> +#include "library_namespaces.h" +#include "nativeloader/dlext_namespaces.h" +#endif + +namespace android { + +namespace { +#if defined(__ANDROID__) +using android::nativeloader::LibraryNamespaces; + +constexpr const char* kApexPath = "/apex/"; + +std::mutex g_namespaces_mutex; +LibraryNamespaces* g_namespaces = new LibraryNamespaces; + +android_namespace_t* FindExportedNamespace(const char* caller_location) { + std::string location = caller_location; + // Lots of implicit assumptions here: we expect `caller_location` to be of the form: + // /apex/com.android...modulename/... + // + // And we extract from it 'modulename', which is the name of the linker namespace. + if (android::base::StartsWith(location, kApexPath)) { + size_t slash_index = location.find_first_of('/', strlen(kApexPath)); + LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos), + "Error finding namespace of apex: no slash in path %s", caller_location); + size_t dot_index = location.find_last_of('.', slash_index); + LOG_ALWAYS_FATAL_IF((dot_index == std::string::npos), + "Error finding namespace of apex: no dot in apex name %s", caller_location); + std::string name = location.substr(dot_index + 1, slash_index - dot_index - 1); + android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str()); + LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr), + "Error finding namespace of apex: no namespace called %s", name.c_str()); + return boot_namespace; + } + return nullptr; +} +#endif // #if defined(__ANDROID__) +} // namespace + +void InitializeNativeLoader() { +#if defined(__ANDROID__) + std::lock_guard<std::mutex> guard(g_namespaces_mutex); + g_namespaces->Initialize(); +#endif +} + +void ResetNativeLoader() { +#if defined(__ANDROID__) + std::lock_guard<std::mutex> guard(g_namespaces_mutex); + g_namespaces->Reset(); +#endif +} + +jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader, + bool is_shared, jstring dex_path, jstring library_path, + jstring permitted_path) { +#if defined(__ANDROID__) + std::lock_guard<std::mutex> guard(g_namespaces_mutex); + auto ns = g_namespaces->Create(env, target_sdk_version, class_loader, is_shared, dex_path, + library_path, permitted_path); + if (!ns) { + return env->NewStringUTF(ns.error().message().c_str()); + } +#else + UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path); +#endif + return nullptr; +} + +void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path, + jobject class_loader, const char* caller_location, jstring library_path, + bool* needs_native_bridge, char** error_msg) { +#if defined(__ANDROID__) + UNUSED(target_sdk_version); + if (class_loader == nullptr) { + *needs_native_bridge = false; + if (caller_location != nullptr) { + android_namespace_t* boot_namespace = FindExportedNamespace(caller_location); + if (boot_namespace != nullptr) { + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = boot_namespace, + }; + void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo); + if (handle == nullptr) { + *error_msg = strdup(dlerror()); + } + return handle; + } + } + void* handle = dlopen(path, RTLD_NOW); + if (handle == nullptr) { + *error_msg = strdup(dlerror()); + } + return handle; + } + + std::lock_guard<std::mutex> guard(g_namespaces_mutex); + NativeLoaderNamespace* ns; + + 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. + Result<NativeLoaderNamespace*> isolated_ns = + g_namespaces->Create(env, target_sdk_version, class_loader, false /* is_shared */, nullptr, + library_path, nullptr); + if (!isolated_ns) { + *error_msg = strdup(isolated_ns.error().message().c_str()); + return nullptr; + } else { + ns = *isolated_ns; + } + } + + return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg); +#else + UNUSED(env, target_sdk_version, class_loader, caller_location); + + // Do some best effort to emulate library-path support. It will not + // work for dependencies. + // + // Note: null has a special meaning and must be preserved. + std::string c_library_path; // Empty string by default. + if (library_path != nullptr && path != nullptr && path[0] != '/') { + ScopedUtfChars library_path_utf_chars(env, library_path); + c_library_path = library_path_utf_chars.c_str(); + } + + std::vector<std::string> library_paths = base::Split(c_library_path, ":"); + + for (const std::string& lib_path : library_paths) { + *needs_native_bridge = false; + const char* path_arg; + std::string complete_path; + if (path == nullptr) { + // Preserve null. + path_arg = nullptr; + } else { + complete_path = lib_path; + if (!complete_path.empty()) { + complete_path.append("/"); + } + complete_path.append(path); + path_arg = complete_path.c_str(); + } + void* handle = dlopen(path_arg, RTLD_NOW); + if (handle != nullptr) { + return handle; + } + if (NativeBridgeIsSupported(path_arg)) { + *needs_native_bridge = true; + handle = NativeBridgeLoadLibrary(path_arg, RTLD_NOW); + if (handle != nullptr) { + return handle; + } + *error_msg = strdup(NativeBridgeGetError()); + } else { + *error_msg = strdup(dlerror()); + } + } + return nullptr; +#endif +} + +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 = strdup(NativeBridgeGetError()); + } + } else { + success = (dlclose(handle) == 0); + if (!success) { + *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* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path, + bool* needs_native_bridge, char** error_msg) { + auto handle = ns->Load(path); + if (!handle && error_msg != nullptr) { + *error_msg = strdup(handle.error().message().c_str()); + } + if (needs_native_bridge != nullptr) { + *needs_native_bridge = ns->IsBridged(); + } + return handle ? *handle : nullptr; +} + +// native_bridge_namespaces are not supported for callers of this function. +// This function will return nullptr in the case when application is running +// on native bridge. +android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) { + std::lock_guard<std::mutex> guard(g_namespaces_mutex); + NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader); + if (ns != nullptr && !ns->IsBridged()) { + return ns->ToRawAndroidNamespace(); + } + return nullptr; +} + +NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) { + std::lock_guard<std::mutex> guard(g_namespaces_mutex); + return g_namespaces->FindNamespaceByClassLoader(env, class_loader); +} +#endif + +}; // namespace android diff --git a/libnativeloader/native_loader_lazy.cpp b/libnativeloader/native_loader_lazy.cpp new file mode 100644 index 0000000000..2eb1203796 --- /dev/null +++ b/libnativeloader/native_loader_lazy.cpp @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#include "nativeloader/native_loader.h" +#define LOG_TAG "nativeloader" + +#include <dlfcn.h> +#include <errno.h> +#include <string.h> + +#include <log/log.h> + +namespace android { + +namespace { + +void* GetLibHandle() { + static void* handle = dlopen("libnativeloader.so", RTLD_NOW); + LOG_FATAL_IF(handle == nullptr, "Failed to load libnativeloader.so: %s", dlerror()); + return handle; +} + +template <typename FuncPtr> +FuncPtr GetFuncPtr(const char* function_name) { + auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name)); + LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror()); + return f; +} + +#define GET_FUNC_PTR(name) GetFuncPtr<decltype(&name)>(#name) + +} // namespace + +void InitializeNativeLoader() { + static auto f = GET_FUNC_PTR(InitializeNativeLoader); + return f(); +} + +jstring CreateClassLoaderNamespace(JNIEnv* env, int32_t target_sdk_version, jobject class_loader, + bool is_shared, jstring dex_path, jstring library_path, + jstring permitted_path) { + static auto f = GET_FUNC_PTR(CreateClassLoaderNamespace); + return f(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, + permitted_path); +} + +void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path, + jobject class_loader, const char* caller_location, jstring library_path, + bool* needs_native_bridge, char** error_msg) { + static auto f = GET_FUNC_PTR(OpenNativeLibrary); + return f(env, target_sdk_version, path, class_loader, caller_location, library_path, + needs_native_bridge, error_msg); +} + +bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) { + static auto f = GET_FUNC_PTR(CloseNativeLibrary); + return f(handle, needs_native_bridge, error_msg); +} + +void NativeLoaderFreeErrorMessage(char* msg) { + static auto f = GET_FUNC_PTR(NativeLoaderFreeErrorMessage); + return f(msg); +} + +struct android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) { + static auto f = GET_FUNC_PTR(FindNamespaceByClassLoader); + return f(env, class_loader); +} + +struct NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, + jobject class_loader) { + static auto f = GET_FUNC_PTR(FindNativeLoaderNamespaceByClassLoader); + return f(env, class_loader); +} + +void* OpenNativeLibraryInNamespace(struct NativeLoaderNamespace* ns, const char* path, + bool* needs_native_bridge, char** error_msg) { + static auto f = GET_FUNC_PTR(OpenNativeLibraryInNamespace); + return f(ns, path, needs_native_bridge, error_msg); +} + +void ResetNativeLoader() { + static auto f = GET_FUNC_PTR(ResetNativeLoader); + return f(); +} + +#undef GET_FUNC_PTR + +} // namespace android diff --git a/libnativeloader/native_loader_namespace.cpp b/libnativeloader/native_loader_namespace.cpp new file mode 100644 index 0000000000..a81fddf0fa --- /dev/null +++ b/libnativeloader/native_loader_namespace.cpp @@ -0,0 +1,178 @@ +/* + * 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. + */ + +#define LOG_TAG "nativeloader" + +#include "native_loader_namespace.h" + +#include <dlfcn.h> + +#include <functional> + +#include <android-base/strings.h> +#include <log/log.h> +#include <nativebridge/native_bridge.h> + +#include "nativeloader/dlext_namespaces.h" + +using android::base::Error; +using android::base::Errorf; + +namespace android { + +namespace { + +constexpr const char* kDefaultNamespaceName = "default"; +constexpr const char* kPlatformNamespaceName = "platform"; + +std::string GetLinkerError(bool is_bridged) { + const char* msg = is_bridged ? NativeBridgeGetError() : dlerror(); + if (msg == nullptr) { + return "no error"; + } + return std::string(msg); +} + +} // namespace + +Result<NativeLoaderNamespace> NativeLoaderNamespace::GetExportedNamespace(const std::string& name, + bool is_bridged) { + if (!is_bridged) { + auto raw = android_get_exported_namespace(name.c_str()); + if (raw != nullptr) { + return NativeLoaderNamespace(name, raw); + } + } else { + auto raw = NativeBridgeGetExportedNamespace(name.c_str()); + if (raw != nullptr) { + return NativeLoaderNamespace(name, raw); + } + } + return Errorf("namespace {} does not exist or exported", name); +} + +// The platform namespace is called "default" for binaries in /system and +// "platform" for those in the Runtime APEX. Try "platform" first since +// "default" always exists. +Result<NativeLoaderNamespace> NativeLoaderNamespace::GetPlatformNamespace(bool is_bridged) { + auto ns = GetExportedNamespace(kPlatformNamespaceName, is_bridged); + if (ns) return ns; + ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged); + if (ns) return ns; + + // If nothing is found, return NativeLoaderNamespace constructed from nullptr. + // nullptr also means default namespace to the linker. + if (!is_bridged) { + return NativeLoaderNamespace(kDefaultNamespaceName, static_cast<android_namespace_t*>(nullptr)); + } else { + return NativeLoaderNamespace(kDefaultNamespaceName, + static_cast<native_bridge_namespace_t*>(nullptr)); + } +} + +Result<NativeLoaderNamespace> NativeLoaderNamespace::Create( + const std::string& name, const std::string& search_paths, const std::string& permitted_paths, + const NativeLoaderNamespace* parent, bool is_shared, bool is_greylist_enabled, + bool also_used_as_anonymous) { + bool is_bridged = false; + if (parent != nullptr) { + is_bridged = parent->IsBridged(); + } else if (!search_paths.empty()) { + is_bridged = NativeBridgeIsPathSupported(search_paths.c_str()); + } + + // Fall back to the platform namespace if no parent is set. + auto platform_ns = GetPlatformNamespace(is_bridged); + if (!platform_ns) { + return platform_ns.error(); + } + const NativeLoaderNamespace& effective_parent = parent != nullptr ? *parent : *platform_ns; + + // All namespaces for apps are isolated + uint64_t type = ANDROID_NAMESPACE_TYPE_ISOLATED; + + // The namespace is also used as the anonymous namespace + // which is used when the linker fails to determine the caller address + if (also_used_as_anonymous) { + type |= ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS; + } + + // Bundled apps have access to all system libraries that are currently loaded + // in the default namespace + if (is_shared) { + type |= ANDROID_NAMESPACE_TYPE_SHARED; + } + if (is_greylist_enabled) { + type |= ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED; + } + + if (!is_bridged) { + android_namespace_t* raw = + android_create_namespace(name.c_str(), nullptr, search_paths.c_str(), type, + permitted_paths.c_str(), effective_parent.ToRawAndroidNamespace()); + if (raw != nullptr) { + return NativeLoaderNamespace(name, raw); + } + } else { + native_bridge_namespace_t* raw = NativeBridgeCreateNamespace( + name.c_str(), nullptr, search_paths.c_str(), type, permitted_paths.c_str(), + effective_parent.ToRawNativeBridgeNamespace()); + if (raw != nullptr) { + return NativeLoaderNamespace(name, raw); + } + } + return Errorf("failed to create {} namespace name:{}, search_paths:{}, permitted_paths:{}", + is_bridged ? "bridged" : "native", name, search_paths, permitted_paths); +} + +Result<void> NativeLoaderNamespace::Link(const NativeLoaderNamespace& target, + const std::string& shared_libs) const { + LOG_ALWAYS_FATAL_IF(shared_libs.empty(), "empty share lib when linking %s to %s", + this->name().c_str(), target.name().c_str()); + if (!IsBridged()) { + if (android_link_namespaces(this->ToRawAndroidNamespace(), target.ToRawAndroidNamespace(), + shared_libs.c_str())) { + return {}; + } + } else { + if (NativeBridgeLinkNamespaces(this->ToRawNativeBridgeNamespace(), + target.ToRawNativeBridgeNamespace(), shared_libs.c_str())) { + return {}; + } + } + return Error() << GetLinkerError(IsBridged()); +} + +Result<void*> NativeLoaderNamespace::Load(const char* lib_name) const { + if (!IsBridged()) { + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = this->ToRawAndroidNamespace(); + void* handle = android_dlopen_ext(lib_name, RTLD_NOW, &extinfo); + if (handle != nullptr) { + return handle; + } + } else { + void* handle = + NativeBridgeLoadLibraryExt(lib_name, RTLD_NOW, this->ToRawNativeBridgeNamespace()); + if (handle != nullptr) { + return handle; + } + } + return Error() << GetLinkerError(IsBridged()); +} + +} // namespace android diff --git a/libnativeloader/native_loader_namespace.h b/libnativeloader/native_loader_namespace.h new file mode 100644 index 0000000000..7200ee7961 --- /dev/null +++ b/libnativeloader/native_loader_namespace.h @@ -0,0 +1,73 @@ +/* + * 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. + */ +#pragma once +#if defined(__ANDROID__) + +#include <string> +#include <variant> +#include <vector> + +#include <android-base/logging.h> +#include <android-base/result.h> +#include <android/dlext.h> +#include <log/log.h> +#include <nativebridge/native_bridge.h> + +namespace android { + +using android::base::Result; + +// NativeLoaderNamespace abstracts a linker namespace for the native +// architecture (ex: arm on arm) or the translated architecture (ex: arm on +// x86). Instances of this class are managed by LibraryNamespaces object. +struct NativeLoaderNamespace { + public: + static Result<NativeLoaderNamespace> Create(const std::string& name, + const std::string& search_paths, + const std::string& permitted_paths, + const NativeLoaderNamespace* parent, bool is_shared, + bool is_greylist_enabled, + bool also_used_as_anonymous); + + NativeLoaderNamespace(NativeLoaderNamespace&&) = default; + NativeLoaderNamespace(const NativeLoaderNamespace&) = default; + NativeLoaderNamespace& operator=(const NativeLoaderNamespace&) = default; + + android_namespace_t* ToRawAndroidNamespace() const { return std::get<0>(raw_); } + native_bridge_namespace_t* ToRawNativeBridgeNamespace() const { return std::get<1>(raw_); } + + std::string name() const { return name_; } + bool IsBridged() const { return raw_.index() == 1; } + + Result<void> Link(const NativeLoaderNamespace& target, const std::string& shared_libs) const; + Result<void*> Load(const char* lib_name) const; + + static Result<NativeLoaderNamespace> GetExportedNamespace(const std::string& name, + bool is_bridged); + static Result<NativeLoaderNamespace> GetPlatformNamespace(bool is_bridged); + + private: + explicit NativeLoaderNamespace(const std::string& name, android_namespace_t* ns) + : name_(name), raw_(ns) {} + explicit NativeLoaderNamespace(const std::string& name, native_bridge_namespace_t* ns) + : name_(name), raw_(ns) {} + + std::string name_; + std::variant<android_namespace_t*, native_bridge_namespace_t*> raw_; +}; + +} // namespace android +#endif // #if defined(__ANDROID__) diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp new file mode 100644 index 0000000000..8bd7386eea --- /dev/null +++ b/libnativeloader/native_loader_test.cpp @@ -0,0 +1,663 @@ +/* + * 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. + */ + +#include <dlfcn.h> +#include <memory> +#include <unordered_map> + +#include <android-base/strings.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <jni.h> + +#include "native_loader_namespace.h" +#include "nativeloader/dlext_namespaces.h" +#include "nativeloader/native_loader.h" +#include "public_libraries.h" + +using namespace ::testing; +using namespace ::android::nativeloader::internal; + +namespace android { +namespace nativeloader { + +// gmock interface that represents interested platform APIs on libdl and libnativebridge +class Platform { + public: + virtual ~Platform() {} + + // libdl APIs + virtual void* dlopen(const char* filename, int flags) = 0; + virtual int dlclose(void* handle) = 0; + virtual char* dlerror(void) = 0; + + // These mock_* are the APIs semantically the same across libdl and libnativebridge. + // Instead of having two set of mock APIs for the two, define only one set with an additional + // argument 'bool bridged' to identify the context (i.e., called for libdl or libnativebridge). + typedef char* mock_namespace_handle; + virtual bool mock_init_anonymous_namespace(bool bridged, const char* sonames, + const char* search_paths) = 0; + virtual mock_namespace_handle mock_create_namespace( + bool bridged, const char* name, const char* ld_library_path, const char* default_library_path, + uint64_t type, const char* permitted_when_isolated_path, mock_namespace_handle parent) = 0; + virtual bool mock_link_namespaces(bool bridged, mock_namespace_handle from, + mock_namespace_handle to, const char* sonames) = 0; + virtual mock_namespace_handle mock_get_exported_namespace(bool bridged, const char* name) = 0; + virtual void* mock_dlopen_ext(bool bridged, const char* filename, int flags, + mock_namespace_handle ns) = 0; + + // libnativebridge APIs for which libdl has no corresponding APIs + virtual bool NativeBridgeInitialized() = 0; + virtual const char* NativeBridgeGetError() = 0; + virtual bool NativeBridgeIsPathSupported(const char*) = 0; + virtual bool NativeBridgeIsSupported(const char*) = 0; + + // To mock "ClassLoader Object.getParent()" + virtual const char* JniObject_getParent(const char*) = 0; +}; + +// The mock does not actually create a namespace object. But simply casts the pointer to the +// string for the namespace name as the handle to the namespace object. +#define TO_ANDROID_NAMESPACE(str) \ + reinterpret_cast<struct android_namespace_t*>(const_cast<char*>(str)) + +#define TO_BRIDGED_NAMESPACE(str) \ + reinterpret_cast<struct native_bridge_namespace_t*>(const_cast<char*>(str)) + +#define TO_MOCK_NAMESPACE(ns) reinterpret_cast<Platform::mock_namespace_handle>(ns) + +// These represents built-in namespaces created by the linker according to ld.config.txt +static std::unordered_map<std::string, Platform::mock_namespace_handle> namespaces = { + {"platform", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("platform"))}, + {"default", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("default"))}, + {"art", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("art"))}, + {"sphal", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("sphal"))}, + {"vndk", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("vndk"))}, + {"neuralnetworks", TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("neuralnetworks"))}, +}; + +// The actual gmock object +class MockPlatform : public Platform { + public: + explicit MockPlatform(bool is_bridged) : is_bridged_(is_bridged) { + ON_CALL(*this, NativeBridgeIsSupported(_)).WillByDefault(Return(is_bridged_)); + ON_CALL(*this, NativeBridgeIsPathSupported(_)).WillByDefault(Return(is_bridged_)); + ON_CALL(*this, mock_get_exported_namespace(_, _)) + .WillByDefault(Invoke([](bool, const char* name) -> mock_namespace_handle { + if (namespaces.find(name) != namespaces.end()) { + return namespaces[name]; + } + return TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE("(namespace not found")); + })); + } + + // Mocking libdl APIs + MOCK_METHOD2(dlopen, void*(const char*, int)); + MOCK_METHOD1(dlclose, int(void*)); + MOCK_METHOD0(dlerror, char*()); + + // Mocking the common APIs + MOCK_METHOD3(mock_init_anonymous_namespace, bool(bool, const char*, const char*)); + MOCK_METHOD7(mock_create_namespace, + mock_namespace_handle(bool, const char*, const char*, const char*, uint64_t, + const char*, mock_namespace_handle)); + MOCK_METHOD4(mock_link_namespaces, + bool(bool, mock_namespace_handle, mock_namespace_handle, const char*)); + MOCK_METHOD2(mock_get_exported_namespace, mock_namespace_handle(bool, const char*)); + MOCK_METHOD4(mock_dlopen_ext, void*(bool, const char*, int, mock_namespace_handle)); + + // Mocking libnativebridge APIs + MOCK_METHOD0(NativeBridgeInitialized, bool()); + MOCK_METHOD0(NativeBridgeGetError, const char*()); + MOCK_METHOD1(NativeBridgeIsPathSupported, bool(const char*)); + MOCK_METHOD1(NativeBridgeIsSupported, bool(const char*)); + + // Mocking "ClassLoader Object.getParent()" + MOCK_METHOD1(JniObject_getParent, const char*(const char*)); + + private: + bool is_bridged_; +}; + +static std::unique_ptr<MockPlatform> mock; + +// Provide C wrappers for the mock object. +extern "C" { +void* dlopen(const char* file, int flag) { + return mock->dlopen(file, flag); +} + +int dlclose(void* handle) { + return mock->dlclose(handle); +} + +char* dlerror(void) { + return mock->dlerror(); +} + +bool android_init_anonymous_namespace(const char* sonames, const char* search_path) { + return mock->mock_init_anonymous_namespace(false, sonames, search_path); +} + +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) { + return TO_ANDROID_NAMESPACE( + mock->mock_create_namespace(false, name, ld_library_path, default_library_path, type, + permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent))); +} + +bool android_link_namespaces(struct android_namespace_t* from, struct android_namespace_t* to, + const char* sonames) { + return mock->mock_link_namespaces(false, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames); +} + +struct android_namespace_t* android_get_exported_namespace(const char* name) { + return TO_ANDROID_NAMESPACE(mock->mock_get_exported_namespace(false, name)); +} + +void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* info) { + return mock->mock_dlopen_ext(false, filename, flags, TO_MOCK_NAMESPACE(info->library_namespace)); +} + +// libnativebridge APIs +bool NativeBridgeIsSupported(const char* libpath) { + return mock->NativeBridgeIsSupported(libpath); +} + +struct native_bridge_namespace_t* NativeBridgeGetExportedNamespace(const char* name) { + return TO_BRIDGED_NAMESPACE(mock->mock_get_exported_namespace(true, name)); +} + +struct 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, struct native_bridge_namespace_t* parent) { + return TO_BRIDGED_NAMESPACE( + mock->mock_create_namespace(true, name, ld_library_path, default_library_path, type, + permitted_when_isolated_path, TO_MOCK_NAMESPACE(parent))); +} + +bool NativeBridgeLinkNamespaces(struct native_bridge_namespace_t* from, + struct native_bridge_namespace_t* to, const char* sonames) { + return mock->mock_link_namespaces(true, TO_MOCK_NAMESPACE(from), TO_MOCK_NAMESPACE(to), sonames); +} + +void* NativeBridgeLoadLibraryExt(const char* libpath, int flag, + struct native_bridge_namespace_t* ns) { + return mock->mock_dlopen_ext(true, libpath, flag, TO_MOCK_NAMESPACE(ns)); +} + +bool NativeBridgeInitialized() { + return mock->NativeBridgeInitialized(); +} + +bool NativeBridgeInitAnonymousNamespace(const char* public_ns_sonames, + const char* anon_ns_library_path) { + return mock->mock_init_anonymous_namespace(true, public_ns_sonames, anon_ns_library_path); +} + +const char* NativeBridgeGetError() { + return mock->NativeBridgeGetError(); +} + +bool NativeBridgeIsPathSupported(const char* path) { + return mock->NativeBridgeIsPathSupported(path); +} + +} // extern "C" + +// A very simple JNI mock. +// jstring is a pointer to utf8 char array. We don't need utf16 char here. +// jobject, jclass, and jmethodID are also a pointer to utf8 char array +// Only a few JNI methods that are actually used in libnativeloader are mocked. +JNINativeInterface* CreateJNINativeInterface() { + JNINativeInterface* inf = new JNINativeInterface(); + memset(inf, 0, sizeof(JNINativeInterface)); + + inf->GetStringUTFChars = [](JNIEnv*, jstring s, jboolean*) -> const char* { + return reinterpret_cast<const char*>(s); + }; + + inf->ReleaseStringUTFChars = [](JNIEnv*, jstring, const char*) -> void { return; }; + + inf->NewStringUTF = [](JNIEnv*, const char* bytes) -> jstring { + return reinterpret_cast<jstring>(const_cast<char*>(bytes)); + }; + + inf->FindClass = [](JNIEnv*, const char* name) -> jclass { + return reinterpret_cast<jclass>(const_cast<char*>(name)); + }; + + inf->CallObjectMethodV = [](JNIEnv*, jobject obj, jmethodID mid, va_list) -> jobject { + if (strcmp("getParent", reinterpret_cast<const char*>(mid)) == 0) { + // JniObject_getParent can be a valid jobject or nullptr if there is + // no parent classloader. + const char* ret = mock->JniObject_getParent(reinterpret_cast<const char*>(obj)); + return reinterpret_cast<jobject>(const_cast<char*>(ret)); + } + return nullptr; + }; + + inf->GetMethodID = [](JNIEnv*, jclass, const char* name, const char*) -> jmethodID { + return reinterpret_cast<jmethodID>(const_cast<char*>(name)); + }; + + inf->NewWeakGlobalRef = [](JNIEnv*, jobject obj) -> jobject { return obj; }; + + inf->IsSameObject = [](JNIEnv*, jobject a, jobject b) -> jboolean { + return strcmp(reinterpret_cast<const char*>(a), reinterpret_cast<const char*>(b)) == 0; + }; + + return inf; +} + +static void* const any_nonnull = reinterpret_cast<void*>(0x12345678); + +// Custom matcher for comparing namespace handles +MATCHER_P(NsEq, other, "") { + *result_listener << "comparing " << reinterpret_cast<const char*>(arg) << " and " << other; + return strcmp(reinterpret_cast<const char*>(arg), reinterpret_cast<const char*>(other)) == 0; +} + +///////////////////////////////////////////////////////////////// + +// Test fixture +class NativeLoaderTest : public ::testing::TestWithParam<bool> { + protected: + bool IsBridged() { return GetParam(); } + + void SetUp() override { + mock = std::make_unique<NiceMock<MockPlatform>>(IsBridged()); + + env = std::make_unique<JNIEnv>(); + env->functions = CreateJNINativeInterface(); + } + + void SetExpectations() { + std::vector<std::string> default_public_libs = + android::base::Split(preloadable_public_libraries(), ":"); + for (auto l : default_public_libs) { + EXPECT_CALL(*mock, dlopen(StrEq(l.c_str()), RTLD_NOW | RTLD_NODELETE)) + .WillOnce(Return(any_nonnull)); + } + } + + void RunTest() { InitializeNativeLoader(); } + + void TearDown() override { + ResetNativeLoader(); + delete env->functions; + mock.reset(); + } + + std::unique_ptr<JNIEnv> env; +}; + +///////////////////////////////////////////////////////////////// + +TEST_P(NativeLoaderTest, InitializeLoadsDefaultPublicLibraries) { + SetExpectations(); + RunTest(); +} + +INSTANTIATE_TEST_SUITE_P(NativeLoaderTests, NativeLoaderTest, testing::Bool()); + +///////////////////////////////////////////////////////////////// + +class NativeLoaderTest_Create : public NativeLoaderTest { + protected: + // Test inputs (initialized to the default values). Overriding these + // must be done before calling SetExpectations() and RunTest(). + uint32_t target_sdk_version = 29; + std::string class_loader = "my_classloader"; + bool is_shared = false; + std::string dex_path = "/data/app/foo/classes.dex"; + std::string library_path = "/data/app/foo/lib/arm"; + std::string permitted_path = "/data/app/foo/lib"; + + // expected output (.. for the default test inputs) + std::string expected_namespace_name = "classloader-namespace"; + uint64_t expected_namespace_flags = + ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS; + std::string expected_library_path = library_path; + std::string expected_permitted_path = std::string("/data:/mnt/expand:") + permitted_path; + std::string expected_parent_namespace = "platform"; + bool expected_link_with_platform_ns = true; + bool expected_link_with_art_ns = true; + bool expected_link_with_sphal_ns = !vendor_public_libraries().empty(); + bool expected_link_with_vndk_ns = false; + bool expected_link_with_default_ns = false; + bool expected_link_with_neuralnetworks_ns = true; + std::string expected_shared_libs_to_platform_ns = default_public_libraries(); + std::string expected_shared_libs_to_art_ns = art_public_libraries(); + std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries(); + std::string expected_shared_libs_to_vndk_ns = vndksp_libraries(); + std::string expected_shared_libs_to_default_ns = default_public_libraries(); + std::string expected_shared_libs_to_neuralnetworks_ns = neuralnetworks_public_libraries(); + + void SetExpectations() { + NativeLoaderTest::SetExpectations(); + + ON_CALL(*mock, JniObject_getParent(StrEq(class_loader))).WillByDefault(Return(nullptr)); + + EXPECT_CALL(*mock, NativeBridgeIsPathSupported(_)).Times(AnyNumber()); + EXPECT_CALL(*mock, NativeBridgeInitialized()).Times(AnyNumber()); + + EXPECT_CALL(*mock, mock_create_namespace( + Eq(IsBridged()), StrEq(expected_namespace_name), nullptr, + StrEq(expected_library_path), expected_namespace_flags, + StrEq(expected_permitted_path), NsEq(expected_parent_namespace.c_str()))) + .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(dex_path.c_str())))); + if (expected_link_with_platform_ns) { + EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("platform"), + StrEq(expected_shared_libs_to_platform_ns))) + .WillOnce(Return(true)); + } + if (expected_link_with_art_ns) { + EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("art"), + StrEq(expected_shared_libs_to_art_ns))) + .WillOnce(Return(true)); + } + if (expected_link_with_sphal_ns) { + EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("sphal"), + StrEq(expected_shared_libs_to_sphal_ns))) + .WillOnce(Return(true)); + } + if (expected_link_with_vndk_ns) { + EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("vndk"), + StrEq(expected_shared_libs_to_vndk_ns))) + .WillOnce(Return(true)); + } + if (expected_link_with_default_ns) { + EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("default"), + StrEq(expected_shared_libs_to_default_ns))) + .WillOnce(Return(true)); + } + if (expected_link_with_neuralnetworks_ns) { + EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), _, NsEq("neuralnetworks"), + StrEq(expected_shared_libs_to_neuralnetworks_ns))) + .WillOnce(Return(true)); + } + } + + void RunTest() { + NativeLoaderTest::RunTest(); + + jstring err = CreateClassLoaderNamespace( + env(), target_sdk_version, env()->NewStringUTF(class_loader.c_str()), is_shared, + env()->NewStringUTF(dex_path.c_str()), env()->NewStringUTF(library_path.c_str()), + env()->NewStringUTF(permitted_path.c_str())); + + // no error + EXPECT_EQ(err, nullptr); + + if (!IsBridged()) { + struct android_namespace_t* ns = + FindNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str())); + + // The created namespace is for this apk + EXPECT_EQ(dex_path.c_str(), reinterpret_cast<const char*>(ns)); + } else { + struct NativeLoaderNamespace* ns = + FindNativeLoaderNamespaceByClassLoader(env(), env()->NewStringUTF(class_loader.c_str())); + + // The created namespace is for the this apk + EXPECT_STREQ(dex_path.c_str(), + reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace())); + } + } + + JNIEnv* env() { return NativeLoaderTest::env.get(); } +}; + +TEST_P(NativeLoaderTest_Create, DownloadedApp) { + SetExpectations(); + RunTest(); +} + +TEST_P(NativeLoaderTest_Create, BundledSystemApp) { + dex_path = "/system/app/foo/foo.apk"; + is_shared = true; + + expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED; + SetExpectations(); + RunTest(); +} + +TEST_P(NativeLoaderTest_Create, BundledVendorApp) { + dex_path = "/vendor/app/foo/foo.apk"; + is_shared = true; + + expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED; + SetExpectations(); + RunTest(); +} + +TEST_P(NativeLoaderTest_Create, UnbundledVendorApp) { + dex_path = "/vendor/app/foo/foo.apk"; + is_shared = false; + + expected_namespace_name = "vendor-classloader-namespace"; + expected_library_path = expected_library_path + ":/vendor/lib"; + expected_permitted_path = expected_permitted_path + ":/vendor/lib"; + expected_shared_libs_to_platform_ns = + expected_shared_libs_to_platform_ns + ":" + llndk_libraries(); + expected_link_with_vndk_ns = true; + SetExpectations(); + RunTest(); +} + +TEST_P(NativeLoaderTest_Create, BundledProductApp_pre30) { + dex_path = "/product/app/foo/foo.apk"; + is_shared = true; + + expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED; + SetExpectations(); + RunTest(); +} + +TEST_P(NativeLoaderTest_Create, BundledProductApp_post30) { + dex_path = "/product/app/foo/foo.apk"; + is_shared = true; + target_sdk_version = 30; + + expected_namespace_flags |= ANDROID_NAMESPACE_TYPE_SHARED; + SetExpectations(); + RunTest(); +} + +TEST_P(NativeLoaderTest_Create, UnbundledProductApp_pre30) { + dex_path = "/product/app/foo/foo.apk"; + is_shared = false; + SetExpectations(); + RunTest(); +} + +TEST_P(NativeLoaderTest_Create, UnbundledProductApp_post30) { + dex_path = "/product/app/foo/foo.apk"; + is_shared = false; + target_sdk_version = 30; + + expected_namespace_name = "vendor-classloader-namespace"; + expected_library_path = expected_library_path + ":/product/lib:/system/product/lib"; + expected_permitted_path = expected_permitted_path + ":/product/lib:/system/product/lib"; + expected_shared_libs_to_platform_ns = + expected_shared_libs_to_platform_ns + ":" + llndk_libraries(); + expected_link_with_vndk_ns = true; + SetExpectations(); + RunTest(); +} + +TEST_P(NativeLoaderTest_Create, NamespaceForSharedLibIsNotUsedAsAnonymousNamespace) { + if (IsBridged()) { + // There is no shared lib in translated arch + // TODO(jiyong): revisit this + return; + } + // compared to apks, for java shared libs, library_path is empty; java shared + // libs don't have their own native libs. They use platform's. + library_path = ""; + expected_library_path = library_path; + // no ALSO_USED_AS_ANONYMOUS + expected_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED; + SetExpectations(); + RunTest(); +} + +TEST_P(NativeLoaderTest_Create, TwoApks) { + SetExpectations(); + const uint32_t second_app_target_sdk_version = 29; + const std::string second_app_class_loader = "second_app_classloader"; + const bool second_app_is_shared = false; + const std::string second_app_dex_path = "/data/app/bar/classes.dex"; + const std::string second_app_library_path = "/data/app/bar/lib/arm"; + const std::string second_app_permitted_path = "/data/app/bar/lib"; + const std::string expected_second_app_permitted_path = + std::string("/data:/mnt/expand:") + second_app_permitted_path; + const std::string expected_second_app_parent_namespace = "classloader-namespace"; + // no ALSO_USED_AS_ANONYMOUS + const uint64_t expected_second_namespace_flags = ANDROID_NAMESPACE_TYPE_ISOLATED; + + // The scenario is that second app is loaded by the first app. + // So the first app's classloader (`classloader`) is parent of the second + // app's classloader. + ON_CALL(*mock, JniObject_getParent(StrEq(second_app_class_loader))) + .WillByDefault(Return(class_loader.c_str())); + + // namespace for the second app is created. Its parent is set to the namespace + // of the first app. + EXPECT_CALL(*mock, mock_create_namespace( + Eq(IsBridged()), StrEq(expected_namespace_name), nullptr, + StrEq(second_app_library_path), expected_second_namespace_flags, + StrEq(expected_second_app_permitted_path), NsEq(dex_path.c_str()))) + .WillOnce(Return(TO_MOCK_NAMESPACE(TO_ANDROID_NAMESPACE(second_app_dex_path.c_str())))); + EXPECT_CALL(*mock, mock_link_namespaces(Eq(IsBridged()), NsEq(second_app_dex_path.c_str()), _, _)) + .WillRepeatedly(Return(true)); + + RunTest(); + jstring err = CreateClassLoaderNamespace( + env(), second_app_target_sdk_version, env()->NewStringUTF(second_app_class_loader.c_str()), + second_app_is_shared, env()->NewStringUTF(second_app_dex_path.c_str()), + env()->NewStringUTF(second_app_library_path.c_str()), + env()->NewStringUTF(second_app_permitted_path.c_str())); + + // success + EXPECT_EQ(err, nullptr); + + if (!IsBridged()) { + struct android_namespace_t* ns = + FindNamespaceByClassLoader(env(), env()->NewStringUTF(second_app_class_loader.c_str())); + + // The created namespace is for the second apk + EXPECT_EQ(second_app_dex_path.c_str(), reinterpret_cast<const char*>(ns)); + } else { + struct NativeLoaderNamespace* ns = FindNativeLoaderNamespaceByClassLoader( + env(), env()->NewStringUTF(second_app_class_loader.c_str())); + + // The created namespace is for the second apk + EXPECT_STREQ(second_app_dex_path.c_str(), + reinterpret_cast<const char*>(ns->ToRawNativeBridgeNamespace())); + } +} + +INSTANTIATE_TEST_SUITE_P(NativeLoaderTests_Create, NativeLoaderTest_Create, testing::Bool()); + +const std::function<Result<bool>(const struct ConfigEntry&)> always_true = + [](const struct ConfigEntry&) -> Result<bool> { return true; }; + +TEST(NativeLoaderConfigParser, NamesAndComments) { + const char file_content[] = R"( +###### + +libA.so +#libB.so + + + libC.so +libD.so + #### libE.so +)"; + const std::vector<std::string> expected_result = {"libA.so", "libC.so", "libD.so"}; + Result<std::vector<std::string>> result = ParseConfig(file_content, always_true); + ASSERT_TRUE(result) << result.error().message(); + ASSERT_EQ(expected_result, *result); +} + +TEST(NativeLoaderConfigParser, WithBitness) { + const char file_content[] = R"( +libA.so 32 +libB.so 64 +libC.so +)"; +#if defined(__LP64__) + const std::vector<std::string> expected_result = {"libB.so", "libC.so"}; +#else + const std::vector<std::string> expected_result = {"libA.so", "libC.so"}; +#endif + Result<std::vector<std::string>> result = ParseConfig(file_content, always_true); + ASSERT_TRUE(result) << result.error().message(); + ASSERT_EQ(expected_result, *result); +} + +TEST(NativeLoaderConfigParser, WithNoPreload) { + const char file_content[] = R"( +libA.so nopreload +libB.so nopreload +libC.so +)"; + + const std::vector<std::string> expected_result = {"libC.so"}; + Result<std::vector<std::string>> result = + ParseConfig(file_content, + [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; }); + ASSERT_TRUE(result) << result.error().message(); + ASSERT_EQ(expected_result, *result); +} + +TEST(NativeLoaderConfigParser, WithNoPreloadAndBitness) { + const char file_content[] = R"( +libA.so nopreload 32 +libB.so 64 nopreload +libC.so 32 +libD.so 64 +libE.so nopreload +)"; + +#if defined(__LP64__) + const std::vector<std::string> expected_result = {"libD.so"}; +#else + const std::vector<std::string> expected_result = {"libC.so"}; +#endif + Result<std::vector<std::string>> result = + ParseConfig(file_content, + [](const struct ConfigEntry& entry) -> Result<bool> { return !entry.nopreload; }); + ASSERT_TRUE(result) << result.error().message(); + ASSERT_EQ(expected_result, *result); +} + +TEST(NativeLoaderConfigParser, RejectMalformed) { + ASSERT_FALSE(ParseConfig("libA.so 32 64", always_true)); + ASSERT_FALSE(ParseConfig("libA.so 32 32", always_true)); + ASSERT_FALSE(ParseConfig("libA.so 32 nopreload 64", always_true)); + ASSERT_FALSE(ParseConfig("32 libA.so nopreload", always_true)); + ASSERT_FALSE(ParseConfig("nopreload libA.so 32", always_true)); + ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true)); +} + +} // namespace nativeloader +} // namespace android diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp new file mode 100644 index 0000000000..11c30701d9 --- /dev/null +++ b/libnativeloader/public_libraries.cpp @@ -0,0 +1,371 @@ +/* + * 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. + */ + +#define LOG_TAG "nativeloader" + +#include "public_libraries.h" + +#include <dirent.h> + +#include <algorithm> +#include <memory> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/result.h> +#include <android-base/strings.h> +#include <log/log.h> + +#include "utils.h" + +namespace android::nativeloader { + +using namespace internal; +using namespace ::std::string_literals; +using android::base::ErrnoError; +using android::base::Errorf; +using android::base::Result; + +namespace { + +constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt"; +constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-"; +constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt"; +constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt"; +constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt"; +constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt"; + +const std::vector<const std::string> kArtApexPublicLibraries = { + "libicuuc.so", + "libicui18n.so", +}; + +constexpr const char* kArtApexLibPath = "/apex/com.android.art/" LIB; + +constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so"; + +// TODO(b/130388701): do we need this? +std::string root_dir() { + static const char* android_root_env = getenv("ANDROID_ROOT"); + return android_root_env != nullptr ? android_root_env : "/system"; +} + +bool debuggable() { + static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false); + return debuggable; +} + +std::string vndk_version_str() { + static std::string version = android::base::GetProperty("ro.vndk.version", ""); + if (version != "" && version != "current") { + return "." + version; + } + return ""; +} + +// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment +// variable to add libraries to the list. This is intended for platform tests only. +std::string additional_public_libraries() { + if (debuggable()) { + const char* val = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES"); + return val ? val : ""; + } + return ""; +} + +void InsertVndkVersionStr(std::string* file_name) { + CHECK(file_name != nullptr); + size_t insert_pos = file_name->find_last_of("."); + if (insert_pos == std::string::npos) { + insert_pos = file_name->length(); + } + file_name->insert(insert_pos, vndk_version_str()); +} + +const std::function<Result<bool>(const struct ConfigEntry&)> always_true = + [](const struct ConfigEntry&) -> Result<bool> { return true; }; + +Result<std::vector<std::string>> ReadConfig( + const std::string& configFile, + const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) { + std::string file_content; + if (!base::ReadFileToString(configFile, &file_content)) { + return ErrnoError(); + } + Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn); + if (!result) { + return Errorf("Cannot parse {}: {}", configFile, result.error().message()); + } + return result; +} + +void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) { + std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir); + if (dir != nullptr) { + // Failing to opening the dir is not an error, which can happen in + // webview_zygote. + while (struct dirent* ent = readdir(dir.get())) { + if (ent->d_type != DT_REG && ent->d_type != DT_LNK) { + continue; + } + const std::string filename(ent->d_name); + std::string_view fn = filename; + if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) && + android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) { + const std::string company_name(fn); + const std::string config_file_path = dirname + "/"s + filename; + LOG_ALWAYS_FATAL_IF( + company_name.empty(), + "Error extracting company name from public native library list file path \"%s\"", + config_file_path.c_str()); + + auto ret = ReadConfig( + config_file_path, [&company_name](const struct ConfigEntry& entry) -> Result<bool> { + if (android::base::StartsWith(entry.soname, "lib") && + android::base::EndsWith(entry.soname, "." + company_name + ".so")) { + return true; + } else { + return Errorf("Library name \"{}\" does not end with the company name {}.", + entry.soname, company_name); + } + }); + if (ret) { + sonames->insert(sonames->end(), ret->begin(), ret->end()); + } else { + LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s", + config_file_path.c_str(), ret.error().message().c_str()); + } + } + } + } +} + +static std::string InitDefaultPublicLibraries(bool for_preload) { + std::string config_file = root_dir() + kDefaultPublicLibrariesFile; + auto sonames = + ReadConfig(config_file, [&for_preload](const struct ConfigEntry& entry) -> Result<bool> { + if (for_preload) { + return !entry.nopreload; + } else { + return true; + } + }); + if (!sonames) { + LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s", + config_file.c_str(), sonames.error().message().c_str()); + return ""; + } + + std::string additional_libs = additional_public_libraries(); + if (!additional_libs.empty()) { + auto vec = base::Split(additional_libs, ":"); + std::copy(vec.begin(), vec.end(), std::back_inserter(*sonames)); + } + + // If this is for preloading libs, don't remove the libs from APEXes. + if (for_preload) { + return android::base::Join(*sonames, ':'); + } + + // Remove the public libs in the art namespace. + // These libs are listed in public.android.txt, but we don't want the rest of android + // in default namespace to dlopen the libs. + // For example, libicuuc.so is exposed to classloader namespace from art namespace. + // Unfortunately, it does not have stable C symbols, and default namespace should only use + // stable symbols in libandroidicu.so. http://b/120786417 + for (const std::string& lib_name : kArtApexPublicLibraries) { + std::string path(kArtApexLibPath); + path.append("/").append(lib_name); + + struct stat s; + // Do nothing if the path in /apex does not exist. + // Runtime APEX must be mounted since libnativeloader is in the same APEX + if (stat(path.c_str(), &s) != 0) { + continue; + } + + auto it = std::find(sonames->begin(), sonames->end(), lib_name); + if (it != sonames->end()) { + sonames->erase(it); + } + } + + // Remove the public libs in the nnapi namespace. + auto it = std::find(sonames->begin(), sonames->end(), kNeuralNetworksApexPublicLibrary); + if (it != sonames->end()) { + sonames->erase(it); + } + return android::base::Join(*sonames, ':'); +} + +static std::string InitArtPublicLibraries() { + CHECK(sizeof(kArtApexPublicLibraries) > 0); + std::string list = android::base::Join(kArtApexPublicLibraries, ":"); + + std::string additional_libs = additional_public_libraries(); + if (!additional_libs.empty()) { + list = list + ':' + additional_libs; + } + return list; +} + +static std::string InitVendorPublicLibraries() { + // This file is optional, quietly ignore if the file does not exist. + auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true); + if (!sonames) { + return ""; + } + return android::base::Join(*sonames, ':'); +} + +// read /system/etc/public.libraries-<companyname>.txt and +// /product/etc/public.libraries-<companyname>.txt which contain partner defined +// system libs that are exposed to apps. The libs in the txt files must be +// named as lib<name>.<companyname>.so. +static std::string InitExtendedPublicLibraries() { + std::vector<std::string> sonames; + ReadExtensionLibraries("/system/etc", &sonames); + ReadExtensionLibraries("/product/etc", &sonames); + return android::base::Join(sonames, ':'); +} + +static std::string InitLlndkLibraries() { + std::string config_file = kLlndkLibrariesFile; + InsertVndkVersionStr(&config_file); + auto sonames = ReadConfig(config_file, always_true); + if (!sonames) { + LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str()); + return ""; + } + return android::base::Join(*sonames, ':'); +} + +static std::string InitVndkspLibraries() { + std::string config_file = kVndkLibrariesFile; + InsertVndkVersionStr(&config_file); + auto sonames = ReadConfig(config_file, always_true); + if (!sonames) { + LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str()); + return ""; + } + return android::base::Join(*sonames, ':'); +} + +static std::string InitNeuralNetworksPublicLibraries() { + return kNeuralNetworksApexPublicLibrary; +} + +} // namespace + +const std::string& preloadable_public_libraries() { + static std::string list = InitDefaultPublicLibraries(/*for_preload*/ true); + return list; +} + +const std::string& default_public_libraries() { + static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false); + return list; +} + +const std::string& art_public_libraries() { + static std::string list = InitArtPublicLibraries(); + return list; +} + +const std::string& vendor_public_libraries() { + static std::string list = InitVendorPublicLibraries(); + return list; +} + +const std::string& extended_public_libraries() { + static std::string list = InitExtendedPublicLibraries(); + return list; +} + +const std::string& neuralnetworks_public_libraries() { + static std::string list = InitNeuralNetworksPublicLibraries(); + return list; +} + +const std::string& llndk_libraries() { + static std::string list = InitLlndkLibraries(); + return list; +} + +const std::string& vndksp_libraries() { + static std::string list = InitVndkspLibraries(); + return list; +} + +namespace internal { +// Exported for testing +Result<std::vector<std::string>> ParseConfig( + const std::string& file_content, + const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) { + std::vector<std::string> lines = base::Split(file_content, "\n"); + + std::vector<std::string> sonames; + for (auto& line : lines) { + auto trimmed_line = base::Trim(line); + if (trimmed_line[0] == '#' || trimmed_line.empty()) { + continue; + } + + std::vector<std::string> tokens = android::base::Split(trimmed_line, " "); + if (tokens.size() < 1 || tokens.size() > 3) { + return Errorf("Malformed line \"{}\"", line); + } + struct ConfigEntry entry = {.soname = "", .nopreload = false, .bitness = ALL}; + size_t i = tokens.size(); + while (i-- > 0) { + if (tokens[i] == "nopreload") { + entry.nopreload = true; + } else if (tokens[i] == "32" || tokens[i] == "64") { + if (entry.bitness != ALL) { + return Errorf("Malformed line \"{}\": bitness can be specified only once", line); + } + entry.bitness = tokens[i] == "32" ? ONLY_32 : ONLY_64; + } else { + if (i != 0) { + return Errorf("Malformed line \"{}\"", line); + } + entry.soname = tokens[i]; + } + } + + // skip 32-bit lib on 64-bit process and vice versa +#if defined(__LP64__) + if (entry.bitness == ONLY_32) continue; +#else + if (entry.bitness == ONLY_64) continue; +#endif + + Result<bool> ret = filter_fn(entry); + if (!ret) { + return ret.error(); + } + if (*ret) { + // filter_fn has returned true. + sonames.push_back(entry.soname); + } + } + return sonames; +} + +} // namespace internal + +} // namespace android::nativeloader diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h new file mode 100644 index 0000000000..b892e6f69c --- /dev/null +++ b/libnativeloader/public_libraries.h @@ -0,0 +1,56 @@ +/* + * 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. + */ +#pragma once + +#include <algorithm> +#include <string> + +#include <android-base/result.h> + +namespace android::nativeloader { + +using android::base::Result; + +// These provide the list of libraries that are available to the namespace for apps. +// Not all of the libraries are available to apps. Depending on the context, +// e.g., if it is a vendor app or not, different set of libraries are made available. +const std::string& preloadable_public_libraries(); +const std::string& default_public_libraries(); +const std::string& art_public_libraries(); +const std::string& vendor_public_libraries(); +const std::string& extended_public_libraries(); +const std::string& neuralnetworks_public_libraries(); +const std::string& llndk_libraries(); +const std::string& vndksp_libraries(); + +// These are exported for testing +namespace internal { + +enum Bitness { ALL = 0, ONLY_32, ONLY_64 }; + +struct ConfigEntry { + std::string soname; + bool nopreload; + Bitness bitness; +}; + +Result<std::vector<std::string>> ParseConfig( + const std::string& file_content, + const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn); + +} // namespace internal + +} // namespace android::nativeloader diff --git a/libnativeloader/test/Android.bp b/libnativeloader/test/Android.bp new file mode 100644 index 0000000000..4d5c53dba6 --- /dev/null +++ b/libnativeloader/test/Android.bp @@ -0,0 +1,82 @@ +// +// Copyright (C) 2017 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. +// + +cc_library { + name: "libfoo.oem1", + srcs: ["test.cpp"], + cflags: ["-DLIBNAME=\"libfoo.oem1.so\""], + shared_libs: [ + "libbase", + ], +} + +cc_library { + name: "libbar.oem1", + srcs: ["test.cpp"], + cflags: ["-DLIBNAME=\"libbar.oem1.so\""], + shared_libs: [ + "libbase", + ], +} + +cc_library { + name: "libfoo.oem2", + srcs: ["test.cpp"], + cflags: ["-DLIBNAME=\"libfoo.oem2.so\""], + shared_libs: [ + "libbase", + ], +} + +cc_library { + name: "libbar.oem2", + srcs: ["test.cpp"], + cflags: ["-DLIBNAME=\"libbar.oem2.so\""], + shared_libs: [ + "libbase", + ], +} + +cc_library { + name: "libfoo.product1", + srcs: ["test.cpp"], + cflags: ["-DLIBNAME=\"libfoo.product1.so\""], + product_specific: true, + shared_libs: [ + "libbase", + ], +} + +cc_library { + name: "libbar.product1", + srcs: ["test.cpp"], + cflags: ["-DLIBNAME=\"libbar.product1.so\""], + product_specific: true, + shared_libs: [ + "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-headers"], +} diff --git a/libnativeloader/test/Android.mk b/libnativeloader/test/Android.mk new file mode 100644 index 0000000000..65e7b09bc4 --- /dev/null +++ b/libnativeloader/test/Android.mk @@ -0,0 +1,57 @@ +# +# Copyright (C) 2017 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. +# +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := public.libraries-oem1.txt +LOCAL_SRC_FILES:= $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := public.libraries-oem2.txt +LOCAL_SRC_FILES:= $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_ETC) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := public.libraries-product1.txt +LOCAL_SRC_FILES:= $(LOCAL_MODULE) +LOCAL_MODULE_CLASS := ETC +LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT_ETC) +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_PACKAGE_NAME := oemlibrarytest-system +LOCAL_MODULE_TAGS := tests +LOCAL_MANIFEST_FILE := system/AndroidManifest.xml +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_SDK_VERSION := current +LOCAL_PROGUARD_ENABLED := disabled +LOCAL_MODULE_PATH := $(TARGET_OUT_APPS) +include $(BUILD_PACKAGE) + +include $(CLEAR_VARS) +LOCAL_PACKAGE_NAME := oemlibrarytest-vendor +LOCAL_MODULE_TAGS := tests +LOCAL_MANIFEST_FILE := vendor/AndroidManifest.xml +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_SDK_VERSION := current +LOCAL_PROGUARD_ENABLED := disabled +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR_APPS) +include $(BUILD_PACKAGE) diff --git a/libnativeloader/test/api_test.c b/libnativeloader/test/api_test.c new file mode 100644 index 0000000000..e7025fd7a2 --- /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; +} diff --git a/libnativeloader/test/public.libraries-oem1.txt b/libnativeloader/test/public.libraries-oem1.txt new file mode 100644 index 0000000000..f9433e2a04 --- /dev/null +++ b/libnativeloader/test/public.libraries-oem1.txt @@ -0,0 +1,2 @@ +libfoo.oem1.so +libbar.oem1.so diff --git a/libnativeloader/test/public.libraries-oem2.txt b/libnativeloader/test/public.libraries-oem2.txt new file mode 100644 index 0000000000..de6bdb08e0 --- /dev/null +++ b/libnativeloader/test/public.libraries-oem2.txt @@ -0,0 +1,2 @@ +libfoo.oem2.so +libbar.oem2.so diff --git a/libnativeloader/test/public.libraries-product1.txt b/libnativeloader/test/public.libraries-product1.txt new file mode 100644 index 0000000000..358154c626 --- /dev/null +++ b/libnativeloader/test/public.libraries-product1.txt @@ -0,0 +1,2 @@ +libfoo.product1.so +libbar.product1.so diff --git a/libnativeloader/test/runtest.sh b/libnativeloader/test/runtest.sh new file mode 100755 index 0000000000..40beb5b4d5 --- /dev/null +++ b/libnativeloader/test/runtest.sh @@ -0,0 +1,11 @@ +#!/bin/bash +adb root +adb remount +adb sync +adb shell stop +adb shell start +sleep 5 # wait until device reboots +adb logcat -c; +adb shell am start -n android.test.app.system/android.test.app.TestActivity +adb shell am start -n android.test.app.vendor/android.test.app.TestActivity +adb logcat | grep android.test.app diff --git a/libnativeloader/test/src/android/test/app/TestActivity.java b/libnativeloader/test/src/android/test/app/TestActivity.java new file mode 100644 index 0000000000..a7a455d33d --- /dev/null +++ b/libnativeloader/test/src/android/test/app/TestActivity.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2018 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. + */ + +package android.test.app; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; + +public class TestActivity extends Activity { + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + tryLoadingLib("foo.oem1"); + tryLoadingLib("bar.oem1"); + tryLoadingLib("foo.oem2"); + tryLoadingLib("bar.oem2"); + tryLoadingLib("foo.product1"); + tryLoadingLib("bar.product1"); + } + + private void tryLoadingLib(String name) { + try { + System.loadLibrary(name); + Log.d(getPackageName(), "library " + name + " is successfully loaded"); + } catch (UnsatisfiedLinkError e) { + Log.d(getPackageName(), "failed to load libarary " + name, e); + } + } +} diff --git a/libnativeloader/test/system/AndroidManifest.xml b/libnativeloader/test/system/AndroidManifest.xml new file mode 100644 index 0000000000..c3048891a8 --- /dev/null +++ b/libnativeloader/test/system/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2018 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.test.app.system"> + + <application> + <activity android:name="android.test.app.TestActivity" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> + diff --git a/libnativeloader/test/test.cpp b/libnativeloader/test/test.cpp new file mode 100644 index 0000000000..b166928f0e --- /dev/null +++ b/libnativeloader/test/test.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 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. + */ +#define LOG_TAG "oemlib" +#include <android-base/logging.h> + +static __attribute__((constructor)) void test_lib_init() { + LOG(DEBUG) << LIBNAME << " loaded"; +} diff --git a/libnativeloader/test/vendor/AndroidManifest.xml b/libnativeloader/test/vendor/AndroidManifest.xml new file mode 100644 index 0000000000..c4c1a9c210 --- /dev/null +++ b/libnativeloader/test/vendor/AndroidManifest.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2018 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.test.app.vendor"> + + <application> + <activity android:name="android.test.app.TestActivity" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + +</manifest> + diff --git a/libnativeloader/utils.h b/libnativeloader/utils.h new file mode 100644 index 0000000000..a1c2be5477 --- /dev/null +++ b/libnativeloader/utils.h @@ -0,0 +1,26 @@ +/* + * 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. + */ +#pragma once + +namespace android::nativeloader { + +#if defined(__LP64__) +#define LIB "lib64" +#else +#define LIB "lib" +#endif + +} // namespace android::nativeloader |