1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/android/bundle_utils.h"
6
7 #include <android/dlext.h>
8 #include <dlfcn.h>
9
10 #include "base/android/jni_android.h"
11 #include "base/android/jni_string.h"
12 #include "base/base_jni/BundleUtils_jni.h"
13 #include "base/check.h"
14 #include "base/compiler_specific.h"
15 #include "base/containers/span.h"
16 #include "base/files/file_path.h"
17 #include "base/notreached.h"
18
19 // These symbols are added by the lld linker when creating a partitioned shared
20 // library. The symbols live in the base library, and are used to properly load
21 // the other partitions (feature libraries) when needed.
22 struct PartitionIndexEntry {
23 int32_t name_relptr;
24 int32_t addr_relptr;
25 uint32_t size;
26 };
27 static_assert(sizeof(PartitionIndexEntry) == 12U,
28 "Unexpected PartitionIndexEntry size");
29
30 // Marked as weak_import because these symbols are lld-specific. The method that
31 // uses them will only be invoked in builds that have lld-generated partitions.
32 extern PartitionIndexEntry __part_index_begin[] __attribute__((weak_import));
33 extern PartitionIndexEntry __part_index_end[] __attribute__((weak_import));
34
35 namespace base {
36 namespace android {
37
38 namespace {
39
40 // Takes as input a "rel pointer", which is a pointer to a 32-bit integer that
41 // contains the offset to add to the pointer, in order to find the actual
42 // desired pointer address.
43 //
44 // # Safety
45 // If the value in the pointer does not provide an offset from the pointer that
46 // stays inside the same allocation, Undefined Behaviour can result.
ReadRelPtr(int32_t * relptr)47 UNSAFE_BUFFER_USAGE void* ReadRelPtr(int32_t* relptr) {
48 // SAFETY: This relies on the caller to provide a valid pointer + value.
49 return UNSAFE_BUFFERS(reinterpret_cast<char*>(relptr) + *relptr);
50 }
51
52 } // namespace
53
54 // static
ResolveLibraryPath(const std::string & library_name,const std::string & split_name)55 std::string BundleUtils::ResolveLibraryPath(const std::string& library_name,
56 const std::string& split_name) {
57 JNIEnv* env = AttachCurrentThread();
58 ScopedJavaLocalRef<jstring> java_path = Java_BundleUtils_getNativeLibraryPath(
59 env, ConvertUTF8ToJavaString(env, library_name),
60 ConvertUTF8ToJavaString(env, split_name));
61 // TODO(crbug.com/40656179): Remove this tolerance.
62 if (!java_path) {
63 return std::string();
64 }
65 return ConvertJavaStringToUTF8(env, java_path);
66 }
67
68 // static
IsBundle()69 bool BundleUtils::IsBundle() {
70 return Java_BundleUtils_isBundleForNative(AttachCurrentThread());
71 }
72
73 // static
DlOpenModuleLibraryPartition(const std::string & library_name,const std::string & partition,const std::string & split_name)74 void* BundleUtils::DlOpenModuleLibraryPartition(const std::string& library_name,
75 const std::string& partition,
76 const std::string& split_name) {
77 // TODO(crbug.com/40656179): Remove this tolerance.
78 std::string library_path = ResolveLibraryPath(library_name, split_name);
79 if (library_path.empty()) {
80 return nullptr;
81 }
82
83 // Linear search is required here because the partition descriptors are not
84 // ordered. If a large number of partitions come into existence, lld could be
85 // modified to sort the partitions.
86 DCHECK(__part_index_begin != nullptr);
87 DCHECK(__part_index_end != nullptr);
88 // SAFETY: `__part_index_begin` and `__part_index_end` are provided by the
89 // linker (https://lld.llvm.org/Partitions.html) and we rely on the linker to
90 // provide pointers that are part of the same allocation with
91 // `__part_index_begin <= __part_index_end`.
92 auto parts = UNSAFE_BUFFERS(
93 span<PartitionIndexEntry>(__part_index_begin, __part_index_end));
94 for (PartitionIndexEntry& part : parts) {
95 std::string name(static_cast<const char*>(
96 // SAFETY: `name_relptr` plus its value points to a nul-terminated
97 // string containing the soname of the partition. This pointer and
98 // offset is provided by the linker and thus assumed to always be
99 // correct. https://lld.llvm.org/Partitions.html
100 UNSAFE_BUFFERS(ReadRelPtr(&part.name_relptr))));
101 if (name == partition) {
102 android_dlextinfo info = {};
103 info.flags = ANDROID_DLEXT_RESERVED_ADDRESS;
104 info.reserved_addr =
105 // SAFETY: `addr_offset` field is a relative pointer to the
106 // partition's load address. This pointer and offset is provided by
107 // the linker and thus assumed to always be correct.
108 // https://lld.llvm.org/Partitions.html
109 UNSAFE_BUFFERS(ReadRelPtr(&part.addr_relptr));
110 info.reserved_size = part.size;
111
112 #if __ANDROID_API__ >= 24
113 return android_dlopen_ext(library_path.c_str(), RTLD_LOCAL, &info);
114 #else
115 // When targeting pre-N, such as for Cronet, android_dlopen_ext() might
116 // not be available on the system.
117 NOTREACHED() << "android_dlopen_ext not available";
118 #endif
119 }
120 }
121
122 NOTREACHED();
123 }
124
125 } // namespace android
126 } // namespace base
127