blob: 4012177cd03396b55659cc8217f3954a79e23bc4 [file] [log] [blame]
/*
* 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 <android-base/result.h>
#include <getopt.h>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "linkerconfig/apex.h"
#include "linkerconfig/apexconfig.h"
#include "linkerconfig/baseconfig.h"
#include "linkerconfig/context.h"
#include "linkerconfig/environment.h"
#include "linkerconfig/legacy.h"
#include "linkerconfig/log.h"
#include "linkerconfig/recovery.h"
#include "linkerconfig/variableloader.h"
#include "linkerconfig/variables.h"
using android::base::ErrnoError;
using android::base::Error;
using android::base::Result;
using android::linkerconfig::modules::ApexInfo;
using android::linkerconfig::modules::Configuration;
namespace {
const static struct option program_options[] = {
{"target", required_argument, 0, 't'},
#ifndef __ANDROID__
{"root", required_argument, 0, 'r'},
{"vndk", required_argument, 0, 'v'},
{"recovery", no_argument, 0, 'y'},
{"legacy", no_argument, 0, 'l'},
#endif
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}};
struct ProgramArgs {
std::string target_directory;
std::string root;
std::string vndk_version;
bool is_recovery;
bool is_legacy;
};
[[noreturn]] void PrintUsage(int status = EXIT_SUCCESS) {
std::cerr << "Usage : linkerconfig [--target <target_directory>]"
#ifndef __ANDROID__
" --root <root dir>"
" --vndk <vndk version>"
" --recovery"
" --legacy"
#endif
" [--help]"
<< std::endl;
exit(status);
}
bool ParseArgs(int argc, char* argv[], ProgramArgs* args) {
int parse_result;
while ((parse_result = getopt_long(
argc, argv, "t:r:v:hyl", program_options, NULL)) != -1) {
switch (parse_result) {
case 't':
args->target_directory = optarg;
break;
case 'r':
args->root = optarg;
break;
case 'v':
args->vndk_version = optarg;
break;
case 'y':
args->is_recovery = true;
break;
case 'l':
args->is_legacy = 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 == "" || args.vndk_version == "")) {
PrintUsage();
}
android::linkerconfig::modules::Variables::AddValue("ro.vndk.version",
args.vndk_version);
#endif
if (!args.is_recovery) {
android::linkerconfig::generator::LoadVariables(args.root);
}
}
Result<void> 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<void> 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 {};
}
Configuration GetConfiguration() {
if (android::linkerconfig::modules::IsRecoveryMode()) {
return android::linkerconfig::contents::CreateRecoveryConfiguration();
}
if (android::linkerconfig::modules::IsLegacyDevice()) {
return android::linkerconfig::contents::CreateLegacyConfiguration();
}
// Use base configuration in default
return android::linkerconfig::contents::CreateBaseConfiguration();
}
Result<void> GenerateConfiguration(Configuration config, 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) {
return write_config;
} else if (update_permission && file_path != "") {
return UpdatePermission(file_path);
}
return {};
}
Result<void> GenerateBaseLinkerConfiguration(std::string dir_path) {
return GenerateConfiguration(GetConfiguration(), dir_path, true);
}
Result<void> GenerateRecoveryLinkerConfiguration(std::string dir_path) {
return GenerateConfiguration(
android::linkerconfig::contents::CreateRecoveryConfiguration(),
dir_path,
false);
}
Result<void> GenerateLegacyLinkerConfiguration(std::string dir_path) {
return GenerateConfiguration(
android::linkerconfig::contents::CreateLegacyConfiguration(),
dir_path,
false);
}
Result<void> 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(const std::string& dir_path) {
static std::string apex_root = "/apex";
auto apex_list = android::linkerconfig::modules::ScanActiveApexes(apex_root);
android::linkerconfig::contents::Context ctx;
for (auto const& apex_item : apex_list) {
auto apex_info = apex_item.second;
if (apex_info.has_bin || apex_info.has_lib) {
ctx.AddApexModule(apex_info);
}
}
for (auto const& apex_item : apex_list) {
if (apex_item.second.has_bin) {
auto result = GenerateApexConfiguration(dir_path, ctx, apex_item.second);
if (!result) {
LOG(WARNING) << result.error();
}
}
}
}
void ExitOnFailure(Result<void> task) {
if (!task) {
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);
}
LoadVariables(args);
if (args.is_recovery) {
ExitOnFailure(GenerateRecoveryLinkerConfiguration(args.target_directory));
} else if (args.is_legacy) {
ExitOnFailure(GenerateLegacyLinkerConfiguration(args.target_directory));
} else {
ExitOnFailure(GenerateBaseLinkerConfiguration(args.target_directory));
GenerateApexConfigurations(args.target_directory);
}
return EXIT_SUCCESS;
}