1 /*
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 #include <climits>
18 #include <cstdlib>
19 #include <cstring>
20 #include <fstream>
21 #include <iostream>
22 #include <string>
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29
30 #include <android-base/result.h>
31 #include <android-base/strings.h>
32
33 #include "linkerconfig/apex.h"
34 #include "linkerconfig/apexconfig.h"
35 #include "linkerconfig/baseconfig.h"
36 #include "linkerconfig/configparser.h"
37 #include "linkerconfig/context.h"
38 #include "linkerconfig/environment.h"
39 #include "linkerconfig/legacy.h"
40 #include "linkerconfig/log.h"
41 #include "linkerconfig/namespacebuilder.h"
42 #include "linkerconfig/recovery.h"
43 #include "linkerconfig/variableloader.h"
44 #include "linkerconfig/variables.h"
45
46 using android::base::ErrnoError;
47 using android::base::Error;
48 using android::base::Join;
49 using android::base::Result;
50 using android::linkerconfig::contents::Context;
51 using android::linkerconfig::modules::ApexInfo;
52 using android::linkerconfig::modules::Configuration;
53
54 namespace {
55 const static struct option program_options[] = {
56 {"apex", required_argument, 0, 'a'},
57 {"target", required_argument, 0, 't'},
58 {"strict", no_argument, 0, 's'},
59 #ifndef __ANDROID__
60 {"root", required_argument, 0, 'r'},
61 {"vndk", required_argument, 0, 'v'},
62 {"product_vndk", required_argument, 0, 'p'},
63 {"deprecate_vndk", no_argument, 0, 'd'},
64 {"recovery", no_argument, 0, 'y'},
65 {"treblelize", no_argument, 0, 'z'},
66 #endif
67 {"help", no_argument, 0, 'h'},
68 {0, 0, 0, 0}};
69
70 struct ProgramArgs {
71 std::string target_apex;
72 std::string target_directory;
73 bool strict;
74 std::string root;
75 std::string vndk_version;
76 std::string product_vndk_version;
77 bool is_recovery;
78 bool deprecate_vndk;
79 bool is_treblelized;
80 };
81
PrintUsage(int status=EXIT_SUCCESS)82 [[noreturn]] void PrintUsage(int status = EXIT_SUCCESS) {
83 std::cerr << "Usage : linkerconfig [--target <target_directory>]"
84 " [--strict]"
85 " --apex <name>"
86 #ifndef __ANDROID__
87 " --root <root dir>"
88 " --vndk <vndk version>"
89 " --product_vndk <product vndk version>"
90 " --recovery"
91 " --treblelize"
92 #endif
93 " [--help]"
94 << std::endl;
95 exit(status);
96 }
97
RealPath(std::string_view path)98 std::string RealPath(std::string_view path) {
99 char resolved_path[PATH_MAX];
100 if (realpath(path.data(), resolved_path) != nullptr) {
101 return resolved_path;
102 }
103 PrintUsage(-1);
104 }
105
ParseArgs(int argc,char * argv[],ProgramArgs * args)106 bool ParseArgs(int argc, char* argv[], ProgramArgs* args) {
107 int parse_result;
108 while ((parse_result = getopt_long(
109 argc, argv, "a:t:sr:v:ep:hzyl", program_options, NULL)) != -1) {
110 switch (parse_result) {
111 case 'a':
112 args->target_apex = optarg;
113 break;
114 case 't':
115 args->target_directory = optarg;
116 break;
117 case 's':
118 args->strict = true;
119 break;
120 case 'r':
121 args->root = RealPath(optarg);
122 break;
123 case 'v':
124 args->vndk_version = optarg;
125 break;
126 case 'p':
127 args->product_vndk_version = optarg;
128 break;
129 case 'z':
130 args->is_treblelized = true;
131 break;
132 case 'y':
133 args->is_recovery = true;
134 break;
135 case 'h':
136 PrintUsage();
137 default:
138 return false;
139 }
140 }
141
142 if (optind < argc) {
143 return false;
144 }
145
146 return true;
147 }
148
LoadVariables(const ProgramArgs & args)149 void LoadVariables(const ProgramArgs& args) {
150 #ifndef __ANDROID__
151 if (!args.is_recovery && args.root == "") {
152 PrintUsage();
153 }
154 android::linkerconfig::modules::Variables::AddValue("ro.vndk.version",
155 args.vndk_version);
156 android::linkerconfig::modules::Variables::AddValue(
157 "ro.product.vndk.version", args.product_vndk_version);
158
159 if (args.is_treblelized) {
160 android::linkerconfig::modules::Variables::AddValue("ro.treble.enabled",
161 "true");
162 }
163 #endif
164 if (!args.is_recovery) {
165 android::linkerconfig::generator::LoadVariables(args.root);
166 }
167 }
168
WriteConfigurationToFile(Configuration & conf,std::string file_path)169 Result<void> WriteConfigurationToFile(Configuration& conf,
170 std::string file_path) {
171 std::ostream* out = &std::cout;
172 std::ofstream file_out;
173
174 if (file_path != "") {
175 file_out.open(file_path);
176 if (file_out.fail()) {
177 return ErrnoError() << "Failed to open file " << file_path;
178 }
179 out = &file_out;
180 }
181
182 android::linkerconfig::modules::ConfigWriter config_writer;
183
184 conf.WriteConfig(config_writer);
185 *out << config_writer.ToString();
186 if (!out->good()) {
187 return ErrnoError() << "Failed to write content to " << file_path;
188 }
189
190 return {};
191 }
192
UpdatePermission(const std::string & file_path)193 Result<void> UpdatePermission([[maybe_unused]] const std::string& file_path) {
194 #ifdef __ANDROID__
195 if (fchmodat(AT_FDCWD,
196 file_path.c_str(),
197 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH,
198 AT_SYMLINK_NOFOLLOW) < 0) {
199 return ErrnoError() << "Failed to update permission of " << file_path;
200 }
201 #endif
202
203 return {};
204 }
205
GetContext(const ProgramArgs & args)206 Context GetContext(const ProgramArgs& args) {
207 Context ctx;
208 if (args.strict) {
209 ctx.SetStrictMode(true);
210 }
211 if (!args.target_apex.empty()) {
212 ctx.SetTargetApex(args.target_apex);
213 }
214 if (!args.is_recovery) {
215 auto apex_list = android::linkerconfig::modules::ScanActiveApexes(args.root);
216 if (apex_list.ok()) {
217 std::vector<ApexInfo> apex_modules;
218 for (auto const& apex_item : *apex_list) {
219 auto apex_info = apex_item.second;
220 if (apex_info.has_bin || apex_info.has_lib) {
221 apex_modules.push_back(std::move(apex_info));
222 }
223 }
224 ctx.SetApexModules(std::move(apex_modules));
225 } else {
226 LOG(ERROR) << "Failed to scan APEX modules : " << apex_list.error();
227 }
228 }
229
230 std::string system_config_path = args.root + "/system/etc/linker.config.pb";
231 if (access(system_config_path.c_str(), F_OK) == 0) {
232 auto system_config =
233 android::linkerconfig::modules::ParseLinkerConfig(system_config_path);
234 if (system_config.ok()) {
235 ctx.SetSystemConfig(*system_config);
236 } else {
237 LOG(ERROR) << "Failed to read system config : " << system_config.error();
238 }
239 }
240
241 std::string system_ext_config_path =
242 args.root + "/system_ext/etc/linker.config.pb";
243 if (access(system_ext_config_path.c_str(), F_OK) == 0) {
244 auto system_ext_config = android::linkerconfig::modules::ParseLinkerConfig(
245 system_ext_config_path);
246 if (system_ext_config.ok()) {
247 ctx.SetSystemConfig(*system_ext_config);
248 } else {
249 LOG(ERROR) << "Failed to read system_ext config : "
250 << system_ext_config.error();
251 }
252 }
253
254 std::string vendor_config_path = args.root + "/vendor/etc/linker.config.pb";
255 if (access(vendor_config_path.c_str(), F_OK) == 0) {
256 auto vendor_config =
257 android::linkerconfig::modules::ParseLinkerConfig(vendor_config_path);
258 if (vendor_config.ok()) {
259 ctx.SetVendorConfig(*vendor_config);
260 } else {
261 LOG(ERROR) << "Failed to read vendor config : " << vendor_config.error();
262 }
263 }
264
265 std::string product_config_path = args.root + "/product/etc/linker.config.pb";
266 if (access(product_config_path.c_str(), F_OK) == 0) {
267 auto product_config =
268 android::linkerconfig::modules::ParseLinkerConfig(product_config_path);
269 if (product_config.ok()) {
270 ctx.SetProductConfig(*product_config);
271 } else {
272 LOG(ERROR) << "Failed to read product config : " << product_config.error();
273 }
274 }
275 return ctx;
276 }
277
GetConfiguration(Context & ctx)278 Configuration GetConfiguration(Context& ctx) {
279 if (android::linkerconfig::modules::IsRecoveryMode()) {
280 return android::linkerconfig::contents::CreateRecoveryConfiguration(ctx);
281 }
282
283 if (!android::linkerconfig::modules::IsTreblelizedDevice()) {
284 return android::linkerconfig::contents::CreateLegacyConfiguration(ctx);
285 }
286
287 // Use base configuration in default
288 return android::linkerconfig::contents::CreateBaseConfiguration(ctx);
289 }
290
GenerateConfiguration(Configuration config,const std::string & dir_path,bool update_permission)291 Result<void> GenerateConfiguration(Configuration config,
292 const std::string& dir_path,
293 bool update_permission) {
294 std::string file_path = "";
295 if (dir_path != "") {
296 file_path = dir_path + "/ld.config.txt";
297 }
298
299 auto write_config = WriteConfigurationToFile(config, file_path);
300 if (!write_config.ok()) {
301 return write_config;
302 } else if (update_permission && file_path != "") {
303 return UpdatePermission(file_path);
304 }
305
306 return {};
307 }
308
GenerateBaseLinkerConfiguration(Context & ctx,const std::string & dir_path)309 Result<void> GenerateBaseLinkerConfiguration(Context& ctx,
310 const std::string& dir_path) {
311 return GenerateConfiguration(GetConfiguration(ctx), dir_path, true);
312 }
313
GenerateRecoveryLinkerConfiguration(Context & ctx,const std::string & dir_path)314 Result<void> GenerateRecoveryLinkerConfiguration(Context& ctx,
315 const std::string& dir_path) {
316 return GenerateConfiguration(
317 android::linkerconfig::contents::CreateRecoveryConfiguration(ctx),
318 dir_path,
319 false);
320 }
321
GenerateApexConfiguration(android::linkerconfig::contents::Context & ctx,const android::linkerconfig::modules::ApexInfo & target_apex,const std::string & base_dir)322 Result<void> GenerateApexConfiguration(
323 android::linkerconfig::contents::Context& ctx,
324 const android::linkerconfig::modules::ApexInfo& target_apex,
325 const std::string& base_dir) {
326 if (!target_apex.has_bin) {
327 return {};
328 }
329
330 std::string dir_path = base_dir + "/" + target_apex.name;
331 if (auto ret = mkdir(dir_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
332 return ErrnoError() << "Failed to create directory " << dir_path;
333 }
334
335 return GenerateConfiguration(
336 android::linkerconfig::contents::CreateApexConfiguration(ctx, target_apex),
337 dir_path,
338 true);
339 }
340
GenerateApexConfiguration(android::linkerconfig::contents::Context & ctx,const std::string & apex_name,const std::string & base_dir)341 Result<void> GenerateApexConfiguration(
342 android::linkerconfig::contents::Context& ctx, const std::string& apex_name,
343 const std::string& base_dir) {
344 auto end = std::end(ctx.GetApexModules());
345 auto it = std::find_if(std::begin(ctx.GetApexModules()),
346 end,
347 [&apex_name](const auto& apex_info) {
348 return apex_info.name == apex_name;
349 });
350 if (it == end) {
351 return Error() << apex_name << " not found.";
352 }
353 return GenerateApexConfiguration(ctx, *it, base_dir);
354 }
355
GenerateApexConfigurations(Context & ctx,const std::string & dir_path)356 void GenerateApexConfigurations(Context& ctx, const std::string& dir_path) {
357 for (auto const& apex_item : ctx.GetApexModules()) {
358 auto result = GenerateApexConfiguration(ctx, apex_item, dir_path);
359 if (!result.ok()) {
360 LOG(WARNING) << result.error();
361 }
362 }
363 }
364
GenerateApexLibrariesConfig(Context & ctx,const std::string & dir_path)365 void GenerateApexLibrariesConfig(Context& ctx, const std::string& dir_path) {
366 if (dir_path == "") {
367 return;
368 }
369 const std::string file_path = dir_path + "/apex.libraries.config.txt";
370 std::ofstream out(file_path);
371 for (auto const& apex_item : ctx.GetApexModules()) {
372 if (!apex_item.jni_libs.empty()) {
373 out << "jni " << apex_item.namespace_name << " "
374 << Join(apex_item.jni_libs, ":") << '\n';
375 }
376 if (!apex_item.public_libs.empty()) {
377 out << "public " << apex_item.namespace_name << " "
378 << Join(apex_item.public_libs, ":") << '\n';
379 }
380 }
381 out.close();
382 UpdatePermission(file_path);
383 }
384
ExitOnFailure(Result<void> task)385 void ExitOnFailure(Result<void> task) {
386 if (!task.ok()) {
387 LOG(FATAL) << task.error();
388 exit(EXIT_FAILURE);
389 }
390 }
391
392 #ifdef __ANDROID__
393 struct CombinedLogger {
394 android::base::LogdLogger logd;
395
operator ()__anon1242faf60111::CombinedLogger396 void operator()(android::base::LogId id, android::base::LogSeverity severity,
397 const char* tag, const char* file, unsigned int line,
398 const char* message) {
399 logd(id, severity, tag, file, line, message);
400 KernelLogger(id, severity, tag, file, line, message);
401 }
402 };
403 #endif
404 } // namespace
405
main(int argc,char * argv[])406 int main(int argc, char* argv[]) {
407 android::base::InitLogging(argv
408 #ifdef __ANDROID__
409 ,
410 CombinedLogger()
411 #endif
412 );
413
414 ProgramArgs args = {};
415
416 if (!ParseArgs(argc, argv, &args)) {
417 PrintUsage(EXIT_FAILURE);
418 }
419
420 if (android::linkerconfig::modules::IsTreblelizedDevice() &&
421 android::linkerconfig::modules::IsVndkLiteDevice()) {
422 LOG(ERROR) << "Linkerconfig no longer supports VNDK-Lite configuration";
423 exit(EXIT_FAILURE);
424 }
425
426 LoadVariables(args);
427 Context ctx = GetContext(args);
428
429 // when exec'ed from init, this is 077, which makes the subdirectories
430 // inaccessible for others. set umask to 022 so that they can be
431 // accessible.
432 umask(022);
433
434 if (args.is_recovery) {
435 ExitOnFailure(
436 GenerateRecoveryLinkerConfiguration(ctx, args.target_directory));
437 } else if (args.target_apex != "") {
438 ExitOnFailure(GenerateApexConfiguration(
439 ctx, args.target_apex, args.target_directory));
440 } else {
441 ExitOnFailure(GenerateBaseLinkerConfiguration(ctx, args.target_directory));
442 GenerateApexConfigurations(ctx, args.target_directory);
443 GenerateApexLibrariesConfig(ctx, args.target_directory);
444 }
445
446 return EXIT_SUCCESS;
447 }
448