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 return ctx;
217 }
218
GetConfiguration(Context & ctx)219 Configuration GetConfiguration(Context& ctx) {
220 if (android::linkerconfig::modules::IsRecoveryMode()) {
221 return android::linkerconfig::contents::CreateRecoveryConfiguration(ctx);
222 }
223
224 if (android::linkerconfig::modules::IsLegacyDevice()) {
225 return android::linkerconfig::contents::CreateLegacyConfiguration(ctx);
226 }
227
228 // Use base configuration in default
229 return android::linkerconfig::contents::CreateBaseConfiguration(ctx);
230 }
231
GenerateConfiguration(Configuration config,const std::string & dir_path,bool update_permission)232 Result<void> GenerateConfiguration(Configuration config,
233 const std::string& dir_path,
234 bool update_permission) {
235 std::string file_path = "";
236 if (dir_path != "") {
237 file_path = dir_path + "/ld.config.txt";
238 }
239
240 auto write_config = WriteConfigurationToFile(config, file_path);
241 if (!write_config.ok()) {
242 return write_config;
243 } else if (update_permission && file_path != "") {
244 return UpdatePermission(file_path);
245 }
246
247 return {};
248 }
249
GenerateBaseLinkerConfiguration(Context & ctx,const std::string & dir_path)250 Result<void> GenerateBaseLinkerConfiguration(Context& ctx,
251 const std::string& dir_path) {
252 return GenerateConfiguration(GetConfiguration(ctx), dir_path, true);
253 }
254
GenerateRecoveryLinkerConfiguration(Context & ctx,const std::string & dir_path)255 Result<void> GenerateRecoveryLinkerConfiguration(Context& ctx,
256 const std::string& dir_path) {
257 return GenerateConfiguration(
258 android::linkerconfig::contents::CreateRecoveryConfiguration(ctx),
259 dir_path,
260 false);
261 }
262
GenerateApexConfiguration(const std::string & base_dir,android::linkerconfig::contents::Context & ctx,const android::linkerconfig::modules::ApexInfo & target_apex)263 Result<void> GenerateApexConfiguration(
264 const std::string& base_dir, android::linkerconfig::contents::Context& ctx,
265 const android::linkerconfig::modules::ApexInfo& target_apex) {
266 std::string dir_path = base_dir + "/" + target_apex.name;
267 if (auto ret = mkdir(dir_path.c_str(), 0755); ret != 0 && errno != EEXIST) {
268 return ErrnoError() << "Failed to create directory " << dir_path;
269 }
270
271 return GenerateConfiguration(
272 android::linkerconfig::contents::CreateApexConfiguration(ctx, target_apex),
273 dir_path,
274 true);
275 }
276
GenerateApexConfigurations(Context & ctx,const std::string & dir_path)277 void GenerateApexConfigurations(Context& ctx, const std::string& dir_path) {
278 for (auto const& apex_item : ctx.GetApexModules()) {
279 if (apex_item.has_bin) {
280 auto result = GenerateApexConfiguration(dir_path, ctx, apex_item);
281 if (!result.ok()) {
282 LOG(WARNING) << result.error();
283 }
284 }
285 }
286 }
287
GenerateApexLibrariesConfig(Context & ctx,const std::string & dir_path)288 void GenerateApexLibrariesConfig(Context& ctx, const std::string& dir_path) {
289 if (dir_path == "") {
290 return;
291 }
292 const std::string file_path = dir_path + "/apex.libraries.config.txt";
293 std::ofstream out(file_path);
294 for (auto const& apex_item : ctx.GetApexModules()) {
295 if (!apex_item.jni_libs.empty()) {
296 out << "jni " << apex_item.namespace_name << " "
297 << Join(apex_item.jni_libs, ":") << '\n';
298 }
299 if (!apex_item.public_libs.empty()) {
300 out << "public " << apex_item.namespace_name << " "
301 << Join(apex_item.public_libs, ":") << '\n';
302 }
303 }
304 out.close();
305 UpdatePermission(file_path);
306 }
307
ExitOnFailure(Result<void> task)308 void ExitOnFailure(Result<void> task) {
309 if (!task.ok()) {
310 LOG(FATAL) << task.error();
311 exit(EXIT_FAILURE);
312 }
313 }
314
315 #ifdef __ANDROID__
316 struct CombinedLogger {
317 android::base::LogdLogger logd;
318
operator ()__anon1b2a1f7d0111::CombinedLogger319 void operator()(android::base::LogId id, android::base::LogSeverity severity,
320 const char* tag, const char* file, unsigned int line,
321 const char* message) {
322 logd(id, severity, tag, file, line, message);
323 KernelLogger(id, severity, tag, file, line, message);
324 }
325 };
326 #endif
327 } // namespace
328
main(int argc,char * argv[])329 int main(int argc, char* argv[]) {
330 android::base::InitLogging(argv
331 #ifdef __ANDROID__
332 ,
333 CombinedLogger()
334 #endif
335 );
336
337 ProgramArgs args = {};
338
339 if (!ParseArgs(argc, argv, &args)) {
340 PrintUsage(EXIT_FAILURE);
341 }
342
343 if (!android::linkerconfig::modules::IsLegacyDevice() &&
344 android::linkerconfig::modules::IsVndkLiteDevice()) {
345 LOG(ERROR) << "Linkerconfig no longer supports VNDK-Lite configuration";
346 exit(EXIT_FAILURE);
347 }
348
349 LoadVariables(args);
350 Context ctx = GetContext(args);
351
352 // when exec'ed from init, this is 077, which makes the subdirectories
353 // inaccessible for others. set umask to 022 so that they can be
354 // accessible.
355 umask(022);
356
357 if (args.is_recovery) {
358 ExitOnFailure(
359 GenerateRecoveryLinkerConfiguration(ctx, args.target_directory));
360 } else {
361 ExitOnFailure(GenerateBaseLinkerConfiguration(ctx, args.target_directory));
362 GenerateApexConfigurations(ctx, args.target_directory);
363 GenerateApexLibrariesConfig(ctx, args.target_directory);
364 }
365
366 return EXIT_SUCCESS;
367 }