blob: 40f002f0efbf930e3df3d858a722bfe547da16a2 [file] [log] [blame]
Orion Hodson9b16e342019-10-09 13:29:16 +01001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "nativeloader"
18
19#include "public_libraries.h"
20
21#include <dirent.h>
22
23#include <algorithm>
24#include <memory>
25
26#include <android-base/file.h>
27#include <android-base/logging.h>
28#include <android-base/properties.h>
29#include <android-base/result.h>
30#include <android-base/strings.h>
31#include <log/log.h>
32
Justin Yun3db26d52019-12-16 14:09:39 +090033#if defined(__ANDROID__)
34#include <android/sysprop/VndkProperties.sysprop.h>
35#endif
36
Orion Hodson9b16e342019-10-09 13:29:16 +010037#include "utils.h"
38
39namespace android::nativeloader {
40
41using namespace internal;
42using namespace ::std::string_literals;
43using android::base::ErrnoError;
44using android::base::Errorf;
45using android::base::Result;
46
47namespace {
48
49constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt";
50constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-";
51constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
52constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
53constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
54constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
55
56const std::vector<const std::string> kArtApexPublicLibraries = {
57 "libicuuc.so",
58 "libicui18n.so",
59};
60
61constexpr const char* kArtApexLibPath = "/apex/com.android.art/" LIB;
62
63constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so";
Luke Huang5c017722019-12-17 10:54:26 +080064// STOPSHIP(b/146420818): Figure out how to use stub or non-specific lib name for libcronet.
65constexpr const char* kCronetApexPublicLibrary = "libcronet.80.0.3986.0.so";
Orion Hodson9b16e342019-10-09 13:29:16 +010066
67// TODO(b/130388701): do we need this?
68std::string root_dir() {
69 static const char* android_root_env = getenv("ANDROID_ROOT");
70 return android_root_env != nullptr ? android_root_env : "/system";
71}
72
73bool debuggable() {
74 static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false);
75 return debuggable;
76}
77
78std::string vndk_version_str() {
79 static std::string version = android::base::GetProperty("ro.vndk.version", "");
80 if (version != "" && version != "current") {
81 return "." + version;
82 }
83 return "";
84}
85
86// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
87// variable to add libraries to the list. This is intended for platform tests only.
88std::string additional_public_libraries() {
89 if (debuggable()) {
90 const char* val = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
91 return val ? val : "";
92 }
93 return "";
94}
95
96void InsertVndkVersionStr(std::string* file_name) {
97 CHECK(file_name != nullptr);
98 size_t insert_pos = file_name->find_last_of(".");
99 if (insert_pos == std::string::npos) {
100 insert_pos = file_name->length();
101 }
102 file_name->insert(insert_pos, vndk_version_str());
103}
104
105const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
106 [](const struct ConfigEntry&) -> Result<bool> { return true; };
107
108Result<std::vector<std::string>> ReadConfig(
109 const std::string& configFile,
110 const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
111 std::string file_content;
112 if (!base::ReadFileToString(configFile, &file_content)) {
113 return ErrnoError();
114 }
115 Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn);
116 if (!result) {
117 return Errorf("Cannot parse {}: {}", configFile, result.error().message());
118 }
119 return result;
120}
121
122void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
123 std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
124 if (dir != nullptr) {
125 // Failing to opening the dir is not an error, which can happen in
126 // webview_zygote.
127 while (struct dirent* ent = readdir(dir.get())) {
128 if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
129 continue;
130 }
131 const std::string filename(ent->d_name);
132 std::string_view fn = filename;
133 if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
134 android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
135 const std::string company_name(fn);
136 const std::string config_file_path = dirname + "/"s + filename;
137 LOG_ALWAYS_FATAL_IF(
138 company_name.empty(),
139 "Error extracting company name from public native library list file path \"%s\"",
140 config_file_path.c_str());
141
142 auto ret = ReadConfig(
143 config_file_path, [&company_name](const struct ConfigEntry& entry) -> Result<bool> {
144 if (android::base::StartsWith(entry.soname, "lib") &&
145 android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
146 return true;
147 } else {
148 return Errorf("Library name \"{}\" does not end with the company name {}.",
149 entry.soname, company_name);
150 }
151 });
152 if (ret) {
153 sonames->insert(sonames->end(), ret->begin(), ret->end());
154 } else {
155 LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
156 config_file_path.c_str(), ret.error().message().c_str());
157 }
158 }
159 }
160 }
161}
162
163static std::string InitDefaultPublicLibraries(bool for_preload) {
164 std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
165 auto sonames =
166 ReadConfig(config_file, [&for_preload](const struct ConfigEntry& entry) -> Result<bool> {
167 if (for_preload) {
168 return !entry.nopreload;
169 } else {
170 return true;
171 }
172 });
173 if (!sonames) {
174 LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
175 config_file.c_str(), sonames.error().message().c_str());
176 return "";
177 }
178
179 std::string additional_libs = additional_public_libraries();
180 if (!additional_libs.empty()) {
181 auto vec = base::Split(additional_libs, ":");
182 std::copy(vec.begin(), vec.end(), std::back_inserter(*sonames));
183 }
184
185 // If this is for preloading libs, don't remove the libs from APEXes.
186 if (for_preload) {
187 return android::base::Join(*sonames, ':');
188 }
189
190 // Remove the public libs in the art namespace.
191 // These libs are listed in public.android.txt, but we don't want the rest of android
192 // in default namespace to dlopen the libs.
193 // For example, libicuuc.so is exposed to classloader namespace from art namespace.
194 // Unfortunately, it does not have stable C symbols, and default namespace should only use
195 // stable symbols in libandroidicu.so. http://b/120786417
196 for (const std::string& lib_name : kArtApexPublicLibraries) {
197 std::string path(kArtApexLibPath);
198 path.append("/").append(lib_name);
199
200 struct stat s;
201 // Do nothing if the path in /apex does not exist.
202 // Runtime APEX must be mounted since libnativeloader is in the same APEX
203 if (stat(path.c_str(), &s) != 0) {
204 continue;
205 }
206
207 auto it = std::find(sonames->begin(), sonames->end(), lib_name);
208 if (it != sonames->end()) {
209 sonames->erase(it);
210 }
211 }
212
213 // Remove the public libs in the nnapi namespace.
214 auto it = std::find(sonames->begin(), sonames->end(), kNeuralNetworksApexPublicLibrary);
215 if (it != sonames->end()) {
216 sonames->erase(it);
217 }
218 return android::base::Join(*sonames, ':');
219}
220
221static std::string InitArtPublicLibraries() {
222 CHECK(sizeof(kArtApexPublicLibraries) > 0);
223 std::string list = android::base::Join(kArtApexPublicLibraries, ":");
224
225 std::string additional_libs = additional_public_libraries();
226 if (!additional_libs.empty()) {
227 list = list + ':' + additional_libs;
228 }
229 return list;
230}
231
232static std::string InitVendorPublicLibraries() {
233 // This file is optional, quietly ignore if the file does not exist.
234 auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
235 if (!sonames) {
236 return "";
237 }
238 return android::base::Join(*sonames, ':');
239}
240
Justin Yun0cc40272019-12-16 16:47:40 +0900241// read /system/etc/public.libraries-<companyname>.txt,
242// /system_ext/etc/public.libraries-<companyname>.txt and
Orion Hodson9b16e342019-10-09 13:29:16 +0100243// /product/etc/public.libraries-<companyname>.txt which contain partner defined
244// system libs that are exposed to apps. The libs in the txt files must be
245// named as lib<name>.<companyname>.so.
246static std::string InitExtendedPublicLibraries() {
247 std::vector<std::string> sonames;
248 ReadExtensionLibraries("/system/etc", &sonames);
Justin Yun0cc40272019-12-16 16:47:40 +0900249 ReadExtensionLibraries("/system_ext/etc", &sonames);
Orion Hodson9b16e342019-10-09 13:29:16 +0100250 ReadExtensionLibraries("/product/etc", &sonames);
251 return android::base::Join(sonames, ':');
252}
253
254static std::string InitLlndkLibraries() {
255 std::string config_file = kLlndkLibrariesFile;
256 InsertVndkVersionStr(&config_file);
257 auto sonames = ReadConfig(config_file, always_true);
258 if (!sonames) {
259 LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
260 return "";
261 }
262 return android::base::Join(*sonames, ':');
263}
264
265static std::string InitVndkspLibraries() {
266 std::string config_file = kVndkLibrariesFile;
267 InsertVndkVersionStr(&config_file);
268 auto sonames = ReadConfig(config_file, always_true);
269 if (!sonames) {
270 LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
271 return "";
272 }
273 return android::base::Join(*sonames, ':');
274}
275
276static std::string InitNeuralNetworksPublicLibraries() {
277 return kNeuralNetworksApexPublicLibrary;
278}
279
Luke Huang5c017722019-12-17 10:54:26 +0800280static std::string InitCronetPublicLibraries() {
281 return kCronetApexPublicLibrary;
282}
283
Orion Hodson9b16e342019-10-09 13:29:16 +0100284} // namespace
285
286const std::string& preloadable_public_libraries() {
287 static std::string list = InitDefaultPublicLibraries(/*for_preload*/ true);
288 return list;
289}
290
291const std::string& default_public_libraries() {
292 static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false);
293 return list;
294}
295
296const std::string& art_public_libraries() {
297 static std::string list = InitArtPublicLibraries();
298 return list;
299}
300
301const std::string& vendor_public_libraries() {
302 static std::string list = InitVendorPublicLibraries();
303 return list;
304}
305
306const std::string& extended_public_libraries() {
307 static std::string list = InitExtendedPublicLibraries();
308 return list;
309}
310
311const std::string& neuralnetworks_public_libraries() {
312 static std::string list = InitNeuralNetworksPublicLibraries();
313 return list;
314}
315
Luke Huang5c017722019-12-17 10:54:26 +0800316const std::string& cronet_public_libraries() {
317 static std::string list = InitCronetPublicLibraries();
318 return list;
319}
320
Orion Hodson9b16e342019-10-09 13:29:16 +0100321const std::string& llndk_libraries() {
322 static std::string list = InitLlndkLibraries();
323 return list;
324}
325
326const std::string& vndksp_libraries() {
327 static std::string list = InitVndkspLibraries();
328 return list;
329}
330
Justin Yun3db26d52019-12-16 14:09:39 +0900331bool is_product_vndk_version_defined() {
332#if defined(__ANDROID__)
333 return android::sysprop::VndkProperties::product_vndk_version().has_value();
334#else
335 return false;
336#endif
337}
338
Orion Hodson9b16e342019-10-09 13:29:16 +0100339namespace internal {
340// Exported for testing
341Result<std::vector<std::string>> ParseConfig(
342 const std::string& file_content,
343 const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
344 std::vector<std::string> lines = base::Split(file_content, "\n");
345
346 std::vector<std::string> sonames;
347 for (auto& line : lines) {
348 auto trimmed_line = base::Trim(line);
349 if (trimmed_line[0] == '#' || trimmed_line.empty()) {
350 continue;
351 }
352
353 std::vector<std::string> tokens = android::base::Split(trimmed_line, " ");
354 if (tokens.size() < 1 || tokens.size() > 3) {
355 return Errorf("Malformed line \"{}\"", line);
356 }
357 struct ConfigEntry entry = {.soname = "", .nopreload = false, .bitness = ALL};
358 size_t i = tokens.size();
359 while (i-- > 0) {
360 if (tokens[i] == "nopreload") {
361 entry.nopreload = true;
362 } else if (tokens[i] == "32" || tokens[i] == "64") {
363 if (entry.bitness != ALL) {
364 return Errorf("Malformed line \"{}\": bitness can be specified only once", line);
365 }
366 entry.bitness = tokens[i] == "32" ? ONLY_32 : ONLY_64;
367 } else {
368 if (i != 0) {
369 return Errorf("Malformed line \"{}\"", line);
370 }
371 entry.soname = tokens[i];
372 }
373 }
374
375 // skip 32-bit lib on 64-bit process and vice versa
376#if defined(__LP64__)
377 if (entry.bitness == ONLY_32) continue;
378#else
379 if (entry.bitness == ONLY_64) continue;
380#endif
381
382 Result<bool> ret = filter_fn(entry);
383 if (!ret) {
384 return ret.error();
385 }
386 if (*ret) {
387 // filter_fn has returned true.
388 sonames.push_back(entry.soname);
389 }
390 }
391 return sonames;
392}
393
394} // namespace internal
395
396} // namespace android::nativeloader