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