| // Copyright 2019 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "base/android/bundle_utils.h" | |
| #include <android/dlext.h> | |
| #include <dlfcn.h> | |
| #include "base/android/jni_android.h" | |
| #include "base/android/jni_string.h" | |
| #include "base/bundle_utils_jni/BundleUtils_jni.h" | |
| #include "base/check.h" | |
| #include "base/compiler_specific.h" | |
| #include "base/containers/span.h" | |
| #include "base/files/file_path.h" | |
| #include "base/notreached.h" | |
| // These symbols are added by the lld linker when creating a partitioned shared | |
| // library. The symbols live in the base library, and are used to properly load | |
| // the other partitions (feature libraries) when needed. | |
| struct PartitionIndexEntry { | |
| int32_t name_relptr; | |
| int32_t addr_relptr; | |
| uint32_t size; | |
| }; | |
| static_assert(sizeof(PartitionIndexEntry) == 12U, | |
| "Unexpected PartitionIndexEntry size"); | |
| // Marked as weak_import because these symbols are lld-specific. The method that | |
| // uses them will only be invoked in builds that have lld-generated partitions. | |
| extern PartitionIndexEntry __part_index_begin[] __attribute__((weak_import)); | |
| extern PartitionIndexEntry __part_index_end[] __attribute__((weak_import)); | |
| namespace base { | |
| namespace android { | |
| namespace { | |
| // Takes as input a "rel pointer", which is a pointer to a 32-bit integer that | |
| // contains the offset to add to the pointer, in order to find the actual | |
| // desired pointer address. | |
| // | |
| // PRECONDITIONS: The value in the pointer must provide an offset from the | |
| // pointer that stays inside the same allocation. | |
| UNSAFE_BUFFER_USAGE void* ReadRelPtr(int32_t* relptr) { | |
| // SAFETY: This relies on the caller to provide a valid pointer + value. | |
| return UNSAFE_BUFFERS(reinterpret_cast<char*>(relptr) + *relptr); | |
| } | |
| } // namespace | |
| // static | |
| std::string BundleUtils::ResolveLibraryPath(const std::string& library_name, | |
| const std::string& split_name) { | |
| JNIEnv* env = AttachCurrentThread(); | |
| return Java_BundleUtils_getNativeLibraryPath(env, library_name, split_name); | |
| } | |
| // static | |
| bool BundleUtils::HasAnyInstalledSplits() { | |
| return Java_BundleUtils_hasAnyInstalledSplits(AttachCurrentThread()); | |
| } | |
| // static | |
| void* BundleUtils::DlOpenModuleLibraryPartition(const std::string& library_name, | |
| const std::string& partition, | |
| const std::string& split_name) { | |
| std::string library_path = ResolveLibraryPath(library_name, split_name); | |
| if (library_path.empty()) { | |
| return nullptr; | |
| } | |
| // Linear search is required here because the partition descriptors are not | |
| // ordered. If a large number of partitions come into existence, lld could be | |
| // modified to sort the partitions. | |
| DCHECK(__part_index_begin != nullptr); | |
| DCHECK(__part_index_end != nullptr); | |
| // SAFETY: `__part_index_begin` and `__part_index_end` are provided by the | |
| // linker (https://lld.llvm.org/Partitions.html) and we rely on the linker to | |
| // provide pointers that are part of the same allocation with | |
| // `__part_index_begin <= __part_index_end`. | |
| auto parts = UNSAFE_BUFFERS( | |
| span<PartitionIndexEntry>(__part_index_begin, __part_index_end)); | |
| for (PartitionIndexEntry& part : parts) { | |
| std::string name(static_cast<const char*>( | |
| // SAFETY: `name_relptr` plus its value points to a nul-terminated | |
| // string containing the soname of the partition. This pointer and | |
| // offset is provided by the linker and thus assumed to always be | |
| // correct. https://lld.llvm.org/Partitions.html | |
| UNSAFE_BUFFERS(ReadRelPtr(&part.name_relptr)))); | |
| if (name == partition) { | |
| android_dlextinfo info = {}; | |
| info.flags = ANDROID_DLEXT_RESERVED_ADDRESS; | |
| info.reserved_addr = | |
| // SAFETY: `addr_offset` field is a relative pointer to the | |
| // partition's load address. This pointer and offset is provided by | |
| // the linker and thus assumed to always be correct. | |
| // https://lld.llvm.org/Partitions.html | |
| UNSAFE_BUFFERS(ReadRelPtr(&part.addr_relptr)); | |
| info.reserved_size = part.size; | |
| #if __ANDROID_API__ >= 24 | |
| return android_dlopen_ext(library_path.c_str(), RTLD_LOCAL, &info); | |
| #else | |
| // When targeting pre-N, such as for Cronet, android_dlopen_ext() might | |
| // not be available on the system. | |
| NOTREACHED() << "android_dlopen_ext not available"; | |
| #endif | |
| } | |
| } | |
| NOTREACHED(); | |
| } | |
| } // namespace android | |
| } // namespace base | |
| DEFINE_JNI(BundleUtils) |