367 lines
10 KiB
C++
367 lines
10 KiB
C++
/*
|
|
* 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 <climits>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <android-base/result.h>
|
|
#include <android-base/strings.h>
|
|
|
|
#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 <target_directory>]"
|
|
" [--strict]"
|
|
#ifndef __ANDROID__
|
|
" --root <root dir>"
|
|
" --vndk <vndk version>"
|
|
" --product_vndk <product vndk version>"
|
|
" --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<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 {};
|
|
}
|
|
|
|
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<void> 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<void> GenerateBaseLinkerConfiguration(Context& ctx,
|
|
const std::string& dir_path) {
|
|
return GenerateConfiguration(GetConfiguration(ctx), dir_path, true);
|
|
}
|
|
|
|
Result<void> GenerateRecoveryLinkerConfiguration(Context& ctx,
|
|
const std::string& dir_path) {
|
|
return GenerateConfiguration(
|
|
android::linkerconfig::contents::CreateRecoveryConfiguration(ctx),
|
|
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(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<void> 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;
|
|
} |