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