/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linkerconfig/apex.h" #include "linkerconfig/apexconfig.h" #include "linkerconfig/baseconfig.h" #include "linkerconfig/configparser.h" #include "linkerconfig/context.h" #include "linkerconfig/environment.h" #include "linkerconfig/legacy.h" #include "linkerconfig/log.h" #include "linkerconfig/namespacebuilder.h" #include "linkerconfig/recovery.h" #include "linkerconfig/variableloader.h" #include "linkerconfig/variables.h" using android::base::ErrnoError; using android::base::Error; using android::base::Join; using android::base::Result; using android::linkerconfig::contents::Context; using android::linkerconfig::modules::ApexInfo; using android::linkerconfig::modules::Configuration; namespace { const static struct option program_options[] = { {"target", required_argument, 0, 't'}, {"strict", no_argument, 0, 's'}, #ifndef __ANDROID__ {"root", required_argument, 0, 'r'}, {"vndk", required_argument, 0, 'v'}, {"product_vndk", required_argument, 0, 'p'}, {"recovery", no_argument, 0, 'y'}, #endif {"help", no_argument, 0, 'h'}, {0, 0, 0, 0}}; struct ProgramArgs { std::string target_directory; bool strict; std::string root; std::string vndk_version; std::string product_vndk_version; bool is_recovery; }; [[noreturn]] void PrintUsage(int status = EXIT_SUCCESS) { std::cerr << "Usage : linkerconfig [--target ]" " [--strict]" #ifndef __ANDROID__ " --root " " --vndk " " --product_vndk " " --recovery" #endif " [--help]" << std::endl; exit(status); } std::string RealPath(std::string_view path) { char resolved_path[PATH_MAX]; if (realpath(path.data(), resolved_path) != nullptr) { return resolved_path; } PrintUsage(-1); } bool ParseArgs(int argc, char* argv[], ProgramArgs* args) { int parse_result; while ((parse_result = getopt_long( argc, argv, "t:sr:v:ep:hyl", program_options, NULL)) != -1) { switch (parse_result) { case 't': args->target_directory = optarg; break; case 's': args->strict = true; break; case 'r': args->root = RealPath(optarg); break; case 'v': args->vndk_version = optarg; break; case 'p': args->product_vndk_version = optarg; break; case 'y': args->is_recovery = true; break; case 'h': PrintUsage(); default: return false; } } if (optind < argc) { return false; } return true; } void LoadVariables(ProgramArgs args) { #ifndef __ANDROID__ if (!args.is_recovery && args.root == "") { PrintUsage(); } android::linkerconfig::modules::Variables::AddValue("ro.vndk.version", args.vndk_version); android::linkerconfig::modules::Variables::AddValue( "ro.product.vndk.version", args.product_vndk_version); #endif if (!args.is_recovery) { android::linkerconfig::generator::LoadVariables(args.root); } } Result WriteConfigurationToFile(Configuration& conf, std::string file_path) { std::ostream* out = &std::cout; std::ofstream file_out; if (file_path != "") { file_out.open(file_path); if (file_out.fail()) { return ErrnoError() << "Failed to open file " << file_path; } out = &file_out; } android::linkerconfig::modules::ConfigWriter config_writer; conf.WriteConfig(config_writer); *out << config_writer.ToString(); if (!out->good()) { return ErrnoError() << "Failed to write content to " << file_path; } return {}; } Result UpdatePermission([[maybe_unused]] const std::string& file_path) { #ifdef __ANDROID__ if (fchmodat(AT_FDCWD, file_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, AT_SYMLINK_NOFOLLOW) < 0) { return ErrnoError() << "Failed to update permission of " << file_path; } #endif return {}; } Context GetContext(ProgramArgs args) { Context ctx; if (args.strict) { ctx.SetStrictMode(true); } if (!args.is_recovery) { auto apex_list = android::linkerconfig::modules::ScanActiveApexes(args.root); if (apex_list.ok()) { for (auto const& apex_item : *apex_list) { auto apex_info = apex_item.second; if (apex_info.has_bin || apex_info.has_lib) { ctx.AddApexModule(std::move(apex_info)); } } } else { LOG(ERROR) << "Failed to scan APEX modules : " << apex_list.error(); } } std::string system_config_path = args.root + "/system/etc/linker.config.pb"; if (access(system_config_path.c_str(), F_OK) == 0) { auto system_config = android::linkerconfig::modules::ParseLinkerConfig(system_config_path); if (system_config.ok()) { ctx.SetSystemConfig(*system_config); } else { LOG(ERROR) << "Failed to read system config : " << system_config.error(); } } return ctx; } Configuration GetConfiguration(Context& ctx) { if (android::linkerconfig::modules::IsRecoveryMode()) { return android::linkerconfig::contents::CreateRecoveryConfiguration(ctx); } if (android::linkerconfig::modules::IsLegacyDevice()) { return android::linkerconfig::contents::CreateLegacyConfiguration(ctx); } // Use base configuration in default return android::linkerconfig::contents::CreateBaseConfiguration(ctx); } Result GenerateConfiguration(Configuration config, const std::string& dir_path, bool update_permission) { std::string file_path = ""; if (dir_path != "") { file_path = dir_path + "/ld.config.txt"; } auto write_config = WriteConfigurationToFile(config, file_path); if (!write_config.ok()) { return write_config; } else if (update_permission && file_path != "") { return UpdatePermission(file_path); } return {}; } Result GenerateBaseLinkerConfiguration(Context& ctx, const std::string& dir_path) { return GenerateConfiguration(GetConfiguration(ctx), dir_path, true); } Result GenerateRecoveryLinkerConfiguration(Context& ctx, const std::string& dir_path) { return GenerateConfiguration( android::linkerconfig::contents::CreateRecoveryConfiguration(ctx), dir_path, false); } Result GenerateApexConfiguration( const std::string& base_dir, android::linkerconfig::contents::Context& ctx, const android::linkerconfig::modules::ApexInfo& target_apex) { std::string dir_path = base_dir + "/" + target_apex.name; if (auto ret = mkdir(dir_path.c_str(), 0755); ret != 0 && errno != EEXIST) { return ErrnoError() << "Failed to create directory " << dir_path; } return GenerateConfiguration( android::linkerconfig::contents::CreateApexConfiguration(ctx, target_apex), dir_path, true); } void GenerateApexConfigurations(Context& ctx, const std::string& dir_path) { for (auto const& apex_item : ctx.GetApexModules()) { if (apex_item.has_bin) { auto result = GenerateApexConfiguration(dir_path, ctx, apex_item); if (!result.ok()) { LOG(WARNING) << result.error(); } } } } void GenerateApexLibrariesConfig(Context& ctx, const std::string& dir_path) { if (dir_path == "") { return; } const std::string file_path = dir_path + "/apex.libraries.config.txt"; std::ofstream out(file_path); for (auto const& apex_item : ctx.GetApexModules()) { if (!apex_item.jni_libs.empty()) { out << "jni " << apex_item.namespace_name << " " << Join(apex_item.jni_libs, ":") << '\n'; } if (!apex_item.public_libs.empty()) { out << "public " << apex_item.namespace_name << " " << Join(apex_item.public_libs, ":") << '\n'; } } out.close(); UpdatePermission(file_path); } void ExitOnFailure(Result task) { if (!task.ok()) { LOG(FATAL) << task.error(); exit(EXIT_FAILURE); } } #ifdef __ANDROID__ struct CombinedLogger { android::base::LogdLogger logd; void operator()(android::base::LogId id, android::base::LogSeverity severity, const char* tag, const char* file, unsigned int line, const char* message) { logd(id, severity, tag, file, line, message); KernelLogger(id, severity, tag, file, line, message); } }; #endif } // namespace int main(int argc, char* argv[]) { android::base::InitLogging(argv #ifdef __ANDROID__ , CombinedLogger() #endif ); ProgramArgs args = {}; if (!ParseArgs(argc, argv, &args)) { PrintUsage(EXIT_FAILURE); } if (!android::linkerconfig::modules::IsLegacyDevice() && android::linkerconfig::modules::IsVndkLiteDevice()) { LOG(ERROR) << "Linkerconfig no longer supports VNDK-Lite configuration"; exit(EXIT_FAILURE); } LoadVariables(args); Context ctx = GetContext(args); // when exec'ed from init, this is 077, which makes the subdirectories // inaccessible for others. set umask to 022 so that they can be // accessible. umask(022); if (args.is_recovery) { ExitOnFailure( GenerateRecoveryLinkerConfiguration(ctx, args.target_directory)); } else { ExitOnFailure(GenerateBaseLinkerConfiguration(ctx, args.target_directory)); GenerateApexConfigurations(ctx, args.target_directory); GenerateApexLibrariesConfig(ctx, args.target_directory); } return EXIT_SUCCESS; }