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