Merge "header-abi-linker: Support solib and version script"
This commit is contained in:
@@ -32,7 +32,7 @@ cc_defaults {
|
||||
cppflags: [
|
||||
"-fno-exceptions",
|
||||
"-fno-rtti",
|
||||
"-std=c++14",
|
||||
"-std=c++17",
|
||||
],
|
||||
|
||||
target: {
|
||||
@@ -146,11 +146,14 @@ cc_library_host_static {
|
||||
|
||||
srcs: [
|
||||
"header-abi-util/src/abi_diff_helpers.cpp",
|
||||
"header-abi-util/src/api_level.cpp",
|
||||
"header-abi-util/src/collect_exported_headers.cpp",
|
||||
"header-abi-util/src/exported_symbol_set.cpp",
|
||||
"header-abi-util/src/ir_representation.cpp",
|
||||
"header-abi-util/src/ir_representation_json.cpp",
|
||||
"header-abi-util/src/ir_representation_protobuf.cpp",
|
||||
"header-abi-util/src/so_file_parser.cpp",
|
||||
"header-abi-util/src/string_utils.cpp",
|
||||
"header-abi-util/src/version_script_parser.cpp",
|
||||
],
|
||||
|
||||
@@ -173,3 +176,34 @@ cc_library_host_static {
|
||||
|
||||
export_include_dirs: ["header-abi-util/include"],
|
||||
}
|
||||
|
||||
cc_test_host {
|
||||
name: "libheader-abi-util_test",
|
||||
|
||||
defaults: [
|
||||
"header-checker-defaults",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"header-abi-util/src/api_level_test.cpp",
|
||||
"header-abi-util/src/exported_symbol_set_test.cpp",
|
||||
"header-abi-util/src/string_utils_test.cpp",
|
||||
"header-abi-util/src/version_script_parser_test.cpp",
|
||||
],
|
||||
|
||||
local_include_dirs: [
|
||||
"header-abi-util/include",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libgtest",
|
||||
"libgtest_main",
|
||||
"libheader-abi-util",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libc++_host",
|
||||
],
|
||||
|
||||
test_suites: ["general-tests"],
|
||||
}
|
||||
|
||||
8
vndk/tools/header-checker/TEST_MAPPING
Normal file
8
vndk/tools/header-checker/TEST_MAPPING
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"presubmit" : [
|
||||
{
|
||||
"name" : "libheader-abi-util_test",
|
||||
"host" : true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -52,8 +52,17 @@ static llvm::cl::opt<std::string> version_script(
|
||||
"v", llvm::cl::desc("<version_script>"), llvm::cl::Optional,
|
||||
llvm::cl::cat(header_linker_category));
|
||||
|
||||
static llvm::cl::list<std::string> excluded_symbol_versions(
|
||||
"exclude-symbol-version", llvm::cl::Optional,
|
||||
llvm::cl::cat(header_linker_category));
|
||||
|
||||
static llvm::cl::list<std::string> excluded_symbol_tags(
|
||||
"exclude-symbol-tag", llvm::cl::Optional,
|
||||
llvm::cl::cat(header_linker_category));
|
||||
|
||||
static llvm::cl::opt<std::string> api(
|
||||
"api", llvm::cl::desc("<api>"), llvm::cl::Optional,
|
||||
llvm::cl::init("current"),
|
||||
llvm::cl::cat(header_linker_category));
|
||||
|
||||
static llvm::cl::opt<std::string> arch(
|
||||
@@ -93,10 +102,14 @@ class HeaderAbiLinker {
|
||||
const std::string &so_file,
|
||||
const std::string &linked_dump,
|
||||
const std::string &arch,
|
||||
const std::string &api)
|
||||
const std::string &api,
|
||||
const std::vector<std::string> &excluded_symbol_versions,
|
||||
const std::vector<std::string> &excluded_symbol_tags)
|
||||
: dump_files_(dump_files), exported_header_dirs_(exported_header_dirs),
|
||||
version_script_(version_script), so_file_(so_file),
|
||||
out_dump_name_(linked_dump), arch_(arch), api_(api) {}
|
||||
out_dump_name_(linked_dump), arch_(arch), api_(api),
|
||||
excluded_symbol_versions_(excluded_symbol_versions),
|
||||
excluded_symbol_tags_(excluded_symbol_tags) {}
|
||||
|
||||
bool LinkAndDump();
|
||||
|
||||
@@ -125,6 +138,18 @@ class HeaderAbiLinker {
|
||||
|
||||
bool LinkExportedSymbols(abi_util::IRDumper *ir_dumper);
|
||||
|
||||
bool LinkExportedSymbols(abi_util::IRDumper *ir_dumper,
|
||||
const abi_util::ExportedSymbolSet &exported_symbols);
|
||||
|
||||
template <typename SymbolMap>
|
||||
bool LinkExportedSymbols(abi_util::IRDumper *ir_dumper,
|
||||
const SymbolMap &symbols);
|
||||
|
||||
// Check whether a symbol name is considered as exported. If both
|
||||
// `shared_object_symbols_` and `version_script_symbols_` exists, the symbol
|
||||
// name must pass the `HasSymbol()` test in both cases.
|
||||
bool IsSymbolExported(const std::string &name) const;
|
||||
|
||||
private:
|
||||
const std::vector<std::string> &dump_files_;
|
||||
const std::vector<std::string> &exported_header_dirs_;
|
||||
@@ -133,19 +158,14 @@ class HeaderAbiLinker {
|
||||
const std::string &out_dump_name_;
|
||||
const std::string &arch_;
|
||||
const std::string &api_;
|
||||
const std::vector<std::string> &excluded_symbol_versions_;
|
||||
const std::vector<std::string> &excluded_symbol_tags_;
|
||||
|
||||
std::set<std::string> exported_headers_;
|
||||
|
||||
std::map<std::string, abi_util::ElfFunctionIR> function_decl_map_;
|
||||
std::map<std::string, abi_util::ElfObjectIR> globvar_decl_map_;
|
||||
|
||||
// Version Script Regex Matching.
|
||||
std::set<std::string> functions_regex_matched_set_;
|
||||
std::regex functions_vs_regex_;
|
||||
|
||||
// Version Script Regex Matching.
|
||||
std::set<std::string> globvars_regex_matched_set_;
|
||||
std::regex globvars_vs_regex_;
|
||||
// Exported symbols
|
||||
std::unique_ptr<abi_util::ExportedSymbolSet> shared_object_symbols_;
|
||||
std::unique_ptr<abi_util::ExportedSymbolSet> version_script_symbols_;
|
||||
};
|
||||
|
||||
static void DeDuplicateAbiElementsThread(
|
||||
@@ -228,7 +248,9 @@ bool HeaderAbiLinker::LinkAndDump() {
|
||||
abi_util::IRDumper::CreateIRDumper(output_format, out_dump_name_);
|
||||
assert(ir_dumper != nullptr);
|
||||
|
||||
LinkExportedSymbols(ir_dumper.get());
|
||||
if (!LinkExportedSymbols(ir_dumper.get())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LinkTypes(greader.get(), ir_dumper.get()) ||
|
||||
!LinkFunctions(greader.get(), ir_dumper.get()) ||
|
||||
@@ -245,39 +267,6 @@ bool HeaderAbiLinker::LinkAndDump() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool QueryRegexMatches(std::set<std::string> *regex_matched_link_set,
|
||||
const std::regex *vs_regex,
|
||||
const std::string &symbol) {
|
||||
assert(regex_matched_link_set != nullptr);
|
||||
assert(vs_regex != nullptr);
|
||||
if (regex_matched_link_set->find(symbol) != regex_matched_link_set->end()) {
|
||||
return false;
|
||||
}
|
||||
if (std::regex_search(symbol, *vs_regex)) {
|
||||
regex_matched_link_set->insert(symbol);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::regex CreateRegexMatchExprFromSet(
|
||||
const std::set<std::string> &link_set) {
|
||||
std::string all_regex_match_str = "";
|
||||
std::set<std::string>::iterator it = link_set.begin();
|
||||
while (it != link_set.end()) {
|
||||
std::string regex_match_str_find_glob =
|
||||
abi_util::FindAndReplace(*it, "\\*", ".*");
|
||||
all_regex_match_str += "(\\b" + regex_match_str_find_glob + "\\b)";
|
||||
if (++it != link_set.end()) {
|
||||
all_regex_match_str += "|";
|
||||
}
|
||||
}
|
||||
if (all_regex_match_str == "") {
|
||||
return std::regex();
|
||||
}
|
||||
return std::regex(all_regex_match_str);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HeaderAbiLinker::LinkDecl(
|
||||
abi_util::IRDumper *dst, const abi_util::AbiElementMap<T> &src,
|
||||
@@ -319,15 +308,22 @@ bool HeaderAbiLinker::LinkTypes(const abi_util::TextFormatToIRReader *reader,
|
||||
LinkDecl(ir_dumper, reader->GetQualifiedTypes(), no_filter);
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::IsSymbolExported(const std::string &name) const {
|
||||
if (shared_object_symbols_ && !shared_object_symbols_->HasSymbol(name)) {
|
||||
return false;
|
||||
}
|
||||
if (version_script_symbols_ && !version_script_symbols_->HasSymbol(name)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::LinkFunctions(
|
||||
const abi_util::TextFormatToIRReader *reader,
|
||||
abi_util::IRDumper *ir_dumper) {
|
||||
assert(reader != nullptr);
|
||||
auto symbol_filter = [this](const std::string &linker_set_key) {
|
||||
return function_decl_map_.find(linker_set_key) !=
|
||||
function_decl_map_.end() ||
|
||||
QueryRegexMatches(&functions_regex_matched_set_,
|
||||
&functions_vs_regex_, linker_set_key);
|
||||
return IsSymbolExported(linker_set_key);
|
||||
};
|
||||
return LinkDecl(ir_dumper, reader->GetFunctions(), symbol_filter);
|
||||
}
|
||||
@@ -337,18 +333,18 @@ bool HeaderAbiLinker::LinkGlobalVars(
|
||||
abi_util::IRDumper *ir_dumper) {
|
||||
assert(reader != nullptr);
|
||||
auto symbol_filter = [this](const std::string &linker_set_key) {
|
||||
return globvar_decl_map_.find(linker_set_key) !=
|
||||
globvar_decl_map_.end() ||
|
||||
QueryRegexMatches(&globvars_regex_matched_set_, &globvars_vs_regex_,
|
||||
linker_set_key);
|
||||
return IsSymbolExported(linker_set_key);
|
||||
};
|
||||
return LinkDecl(ir_dumper, reader->GetGlobalVariables(), symbol_filter);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool LinkExportedSymbols(abi_util::IRDumper *dst,
|
||||
const std::map<std::string, T> &symbols) {
|
||||
template <typename SymbolMap>
|
||||
bool HeaderAbiLinker::LinkExportedSymbols(abi_util::IRDumper *dst,
|
||||
const SymbolMap &symbols) {
|
||||
for (auto &&symbol : symbols) {
|
||||
if (!IsSymbolExported(symbol.first)) {
|
||||
continue;
|
||||
}
|
||||
if (!dst->AddElfSymbolMessageIR(&(symbol.second))) {
|
||||
return false;
|
||||
}
|
||||
@@ -356,20 +352,37 @@ static bool LinkExportedSymbols(abi_util::IRDumper *dst,
|
||||
return true;
|
||||
}
|
||||
|
||||
// To be called right after parsing the .so file / version script.
|
||||
bool HeaderAbiLinker::LinkExportedSymbols(
|
||||
abi_util::IRDumper *ir_dumper,
|
||||
const abi_util::ExportedSymbolSet &exported_symbols) {
|
||||
return (LinkExportedSymbols(ir_dumper, exported_symbols.GetFunctions()) &&
|
||||
LinkExportedSymbols(ir_dumper, exported_symbols.GetVars()));
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::LinkExportedSymbols(abi_util::IRDumper *ir_dumper) {
|
||||
return ::LinkExportedSymbols(ir_dumper, function_decl_map_) &&
|
||||
::LinkExportedSymbols(ir_dumper, globvar_decl_map_);
|
||||
if (shared_object_symbols_) {
|
||||
return LinkExportedSymbols(ir_dumper, *shared_object_symbols_);
|
||||
}
|
||||
|
||||
if (version_script_symbols_) {
|
||||
return LinkExportedSymbols(ir_dumper, *version_script_symbols_);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::ReadExportedSymbols() {
|
||||
if (so_file_.empty() && version_script_.empty()) {
|
||||
llvm::errs() << "Either shared lib or version script must be specified.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!so_file_.empty()) {
|
||||
if (!ReadExportedSymbolsFromSharedObjectFile()) {
|
||||
llvm::errs() << "Failed to parse the shared library (.so file): "
|
||||
<< so_file_ << "\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!version_script_.empty()) {
|
||||
@@ -378,29 +391,40 @@ bool HeaderAbiLinker::ReadExportedSymbols() {
|
||||
<< "\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::errs() << "Either shared lib or version script must be specified.\n";
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::ReadExportedSymbolsFromVersionScript() {
|
||||
abi_util::VersionScriptParser version_script_parser(
|
||||
version_script_, arch_, api_);
|
||||
if (!version_script_parser.Parse()) {
|
||||
std::optional<abi_util::ApiLevel> api_level = abi_util::ParseApiLevel(api_);
|
||||
if (!api_level) {
|
||||
llvm::errs() << "-api must be either \"current\" or an integer (e.g. 21)\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
function_decl_map_ = version_script_parser.GetFunctions();
|
||||
globvar_decl_map_ = version_script_parser.GetGlobVars();
|
||||
std::ifstream stream(version_script_, std::ios_base::in);
|
||||
if (!stream) {
|
||||
llvm::errs() << "Failed to open version script file\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
abi_util::VersionScriptParser parser;
|
||||
parser.SetArch(arch_);
|
||||
parser.SetApiLevel(api_level.value());
|
||||
for (auto &&version : excluded_symbol_versions_) {
|
||||
parser.AddExcludedSymbolVersion(version);
|
||||
}
|
||||
for (auto &&tag : excluded_symbol_tags_) {
|
||||
parser.AddExcludedSymbolTag(tag);
|
||||
}
|
||||
|
||||
version_script_symbols_ = parser.Parse(stream);
|
||||
if (!version_script_symbols_) {
|
||||
llvm::errs() << "Failed to parse version script file\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::set<std::string> function_regexs =
|
||||
version_script_parser.GetFunctionRegexs();
|
||||
std::set<std::string> globvar_regexs =
|
||||
version_script_parser.GetGlobVarRegexs();
|
||||
functions_vs_regex_ = CreateRegexMatchExprFromSet(function_regexs);
|
||||
globvars_vs_regex_ = CreateRegexMatchExprFromSet(globvar_regexs);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -411,8 +435,12 @@ bool HeaderAbiLinker::ReadExportedSymbolsFromSharedObjectFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
function_decl_map_ = so_parser->GetFunctions();
|
||||
globvar_decl_map_ = so_parser->GetGlobVars();
|
||||
shared_object_symbols_ = so_parser->Parse();
|
||||
if (!shared_object_symbols_) {
|
||||
llvm::errs() << "Failed to parse shared object file\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -444,11 +472,14 @@ int main(int argc, const char **argv) {
|
||||
}
|
||||
|
||||
HeaderAbiLinker Linker(dump_files, exported_header_dirs, version_script,
|
||||
so_file, linked_dump, arch, api);
|
||||
so_file, linked_dump, arch, api,
|
||||
excluded_symbol_versions,
|
||||
excluded_symbol_tags);
|
||||
|
||||
if (!Linker.LinkAndDump()) {
|
||||
llvm::errs() << "Failed to link and dump elements\n";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// 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.
|
||||
|
||||
#ifndef API_LEVEL_H_
|
||||
#define API_LEVEL_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
using ApiLevel = int;
|
||||
|
||||
|
||||
constexpr ApiLevel FUTURE_API_LEVEL = 10000;
|
||||
|
||||
|
||||
std::optional<ApiLevel> ParseApiLevel(const std::string &api);
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
|
||||
#endif // API_LEVEL_H_
|
||||
@@ -0,0 +1,100 @@
|
||||
// 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.
|
||||
|
||||
#ifndef EXPORTED_SYMBOL_SET_
|
||||
#define EXPORTED_SYMBOL_SET_
|
||||
|
||||
#include "ir_representation.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
class ExportedSymbolSet {
|
||||
public:
|
||||
using FunctionMap = std::map<std::string, ElfFunctionIR, std::less<>>;
|
||||
using VarMap = std::map<std::string, ElfObjectIR, std::less<>>;
|
||||
using NameSet = std::set<std::string, std::less<>>;
|
||||
using GlobPatternSet = std::set<std::string, std::less<>>;
|
||||
|
||||
|
||||
public:
|
||||
ExportedSymbolSet() {}
|
||||
|
||||
const FunctionMap &GetFunctions() const {
|
||||
return funcs_;
|
||||
}
|
||||
|
||||
const VarMap &GetVars() const {
|
||||
return vars_;
|
||||
}
|
||||
|
||||
const GlobPatternSet &GetGlobPatterns() const {
|
||||
return glob_patterns_;
|
||||
}
|
||||
|
||||
const GlobPatternSet &GetDemangledCppGlobPatterns() const {
|
||||
return demangled_cpp_glob_patterns_;
|
||||
}
|
||||
|
||||
const NameSet &GetDemangledCppSymbols() const {
|
||||
return demangled_cpp_symbols_;
|
||||
}
|
||||
|
||||
bool HasSymbol(const std::string &symbol_name) const;
|
||||
|
||||
void AddFunction(const std::string &name,
|
||||
ElfSymbolIR::ElfSymbolBinding binding);
|
||||
|
||||
void AddVar(const std::string &name, ElfSymbolIR::ElfSymbolBinding binding);
|
||||
|
||||
void AddGlobPattern(const std::string &pattern) {
|
||||
glob_patterns_.insert(pattern);
|
||||
}
|
||||
|
||||
void AddDemangledCppGlobPattern(const std::string &pattern) {
|
||||
demangled_cpp_glob_patterns_.insert(pattern);
|
||||
}
|
||||
|
||||
void AddDemangledCppSymbol(const std::string &pattern) {
|
||||
demangled_cpp_symbols_.insert(pattern);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
bool HasDemangledCppSymbolsOrPatterns() const {
|
||||
return (!demangled_cpp_glob_patterns_.empty() ||
|
||||
!demangled_cpp_symbols_.empty());
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
FunctionMap funcs_;
|
||||
VarMap vars_;
|
||||
|
||||
GlobPatternSet glob_patterns_;
|
||||
GlobPatternSet demangled_cpp_glob_patterns_;
|
||||
NameSet demangled_cpp_symbols_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
|
||||
#endif // EXPORTED_SYMBOL_SET_
|
||||
@@ -15,25 +15,28 @@
|
||||
#ifndef SO_FILE_PARSER_H_
|
||||
#define SO_FILE_PARSER_H_
|
||||
|
||||
#include "exported_symbol_set.h"
|
||||
#include "ir_representation.h"
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
class SoFileParser {
|
||||
public:
|
||||
static std::unique_ptr<SoFileParser> Create(const std::string &so_file_path);
|
||||
|
||||
virtual ~SoFileParser() {}
|
||||
|
||||
virtual const std::map<std::string, ElfFunctionIR> &GetFunctions() const = 0;
|
||||
|
||||
virtual const std::map<std::string, ElfObjectIR> &GetGlobVars() const = 0;
|
||||
virtual std::unique_ptr<ExportedSymbolSet> Parse() = 0;
|
||||
};
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
|
||||
#endif // SO_FILE_PARSER_H_
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// 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.
|
||||
|
||||
#ifndef STL_UTILS_H_
|
||||
#define STL_UTILS_H_
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
class FreeDeleter {
|
||||
public:
|
||||
inline void operator()(void *ptr) const {
|
||||
::free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
|
||||
#endif // STL_UTILS_
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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.
|
||||
|
||||
#ifndef STRING_UTILS_H_
|
||||
#define STRING_UTILS_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
std::string_view Trim(std::string_view s);
|
||||
|
||||
bool StartsWith(std::string_view s, std::string_view prefix);
|
||||
|
||||
bool EndsWith(std::string_view s, std::string_view suffix);
|
||||
|
||||
std::vector<std::string_view> Split(std::string_view s,
|
||||
std::string_view delim_chars);
|
||||
|
||||
std::optional<int> ParseInt(const std::string &s);
|
||||
|
||||
bool IsGlobPattern(std::string_view s);
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
|
||||
#endif // STRING_UTILS_H_
|
||||
@@ -15,64 +15,117 @@
|
||||
#ifndef VERSION_SCRIPT_PARSER_H_
|
||||
#define VERSION_SCRIPT_PARSER_H_
|
||||
|
||||
#include "api_level.h"
|
||||
#include "exported_symbol_set.h"
|
||||
#include "ir_representation.h"
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
class VersionScriptParser {
|
||||
public:
|
||||
enum LineScope {
|
||||
global,
|
||||
local,
|
||||
private:
|
||||
enum class LineScope {
|
||||
GLOBAL,
|
||||
LOCAL,
|
||||
};
|
||||
|
||||
VersionScriptParser(const std::string &version_script,
|
||||
const std::string &arch,
|
||||
const std::string &api);
|
||||
bool Parse();
|
||||
|
||||
const std::map<std::string, ElfFunctionIR> &GetFunctions();
|
||||
struct ParsedTags {
|
||||
public:
|
||||
unsigned has_arch_tags_ : 1;
|
||||
unsigned has_current_arch_tag_ : 1;
|
||||
unsigned has_introduced_tags_ : 1;
|
||||
unsigned has_excluded_tags_ : 1;
|
||||
unsigned has_future_tag_ : 1;
|
||||
unsigned has_var_tag_ : 1;
|
||||
ApiLevel introduced_;
|
||||
|
||||
const std::map<std::string, ElfObjectIR> &GetGlobVars();
|
||||
|
||||
const std::set<std::string> &GetFunctionRegexs();
|
||||
public:
|
||||
ParsedTags()
|
||||
: has_arch_tags_(0), has_current_arch_tag_(0), has_introduced_tags_(0),
|
||||
has_excluded_tags_(0), has_future_tag_(0), has_var_tag_(0),
|
||||
introduced_(-1) {}
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
class ErrorHandler {
|
||||
public:
|
||||
virtual ~ErrorHandler();
|
||||
|
||||
virtual void OnError(int line_no, const std::string &error_msg) = 0;
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
VersionScriptParser();
|
||||
|
||||
void SetArch(const std::string &arch);
|
||||
|
||||
void SetApiLevel(ApiLevel api_level) {
|
||||
api_level_ = api_level;
|
||||
}
|
||||
|
||||
void AddExcludedSymbolVersion(const std::string &version) {
|
||||
excluded_symbol_versions_.insert(version);
|
||||
}
|
||||
|
||||
void AddExcludedSymbolTag(const std::string &tag) {
|
||||
excluded_symbol_tags_.insert(tag);
|
||||
}
|
||||
|
||||
void SetErrorHandler(std::unique_ptr<ErrorHandler> error_handler) {
|
||||
error_handler_ = std::move(error_handler);
|
||||
}
|
||||
|
||||
std::unique_ptr<ExportedSymbolSet> Parse(std::istream &version_script_stream);
|
||||
|
||||
const std::set<std::string> &GetGlobVarRegexs();
|
||||
|
||||
private:
|
||||
bool ParseInnerBlock(std::ifstream &symbol_ifstream);
|
||||
bool ReadLine(std::string &line);
|
||||
|
||||
LineScope GetLineScope(std::string &line, LineScope scope);
|
||||
bool ParseVersionBlock(bool ignore_symbols);
|
||||
|
||||
bool ParseSymbolLine(const std::string &line);
|
||||
bool ParseSymbolLine(const std::string &line, bool is_cpp_symbol);
|
||||
|
||||
bool SymbolInArchAndApiVersion(const std::string &line,
|
||||
const std::string &arch, int api);
|
||||
ParsedTags ParseSymbolTags(const std::string &line);
|
||||
|
||||
bool SymbolExported(const std::string &line, const std::string &arch,
|
||||
int api);
|
||||
bool IsSymbolExported(const ParsedTags &tags);
|
||||
|
||||
int ApiStrToInt(const std::string &api);
|
||||
|
||||
void AddToVars(std::string &symbol);
|
||||
|
||||
void AddToFunctions(std::string &symbol);
|
||||
|
||||
private:
|
||||
const std::string &version_script_;
|
||||
const std::string &arch_;
|
||||
std::map<std::string, ElfFunctionIR> functions_;
|
||||
std::map<std::string, ElfObjectIR> globvars_;
|
||||
// Added to speed up version script parsing and linking.
|
||||
std::set<std::string> function_regexs_;
|
||||
std::set<std::string> globvar_regexs_;
|
||||
int api_;
|
||||
void ReportError(const std::string &error_msg) {
|
||||
if (error_handler_) {
|
||||
error_handler_->OnError(line_no_, error_msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
std::unique_ptr<ErrorHandler> error_handler_;
|
||||
|
||||
std::string arch_;
|
||||
std::string introduced_arch_tag_;
|
||||
ApiLevel api_level_;
|
||||
|
||||
std::set<std::string, std::less<>> excluded_symbol_versions_;
|
||||
std::set<std::string, std::less<>> excluded_symbol_tags_;
|
||||
|
||||
std::istream *stream_;
|
||||
int line_no_;
|
||||
|
||||
std::unique_ptr<ExportedSymbolSet> exported_symbols_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
|
||||
#endif // VERSION_SCRIPT_PARSER_H_
|
||||
|
||||
34
vndk/tools/header-checker/header-abi-util/src/api_level.cpp
Normal file
34
vndk/tools/header-checker/header-abi-util/src/api_level.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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 "api_level.h"
|
||||
|
||||
#include "string_utils.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
std::optional<ApiLevel> ParseApiLevel(const std::string &api_level_str) {
|
||||
if (api_level_str == "current") {
|
||||
return FUTURE_API_LEVEL;
|
||||
}
|
||||
return ParseInt(api_level_str);
|
||||
}
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
@@ -0,0 +1,36 @@
|
||||
// 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 "api_level.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
TEST(ApiLevelTest, ParseApiLevel) {
|
||||
EXPECT_FALSE(ParseApiLevel(""));
|
||||
EXPECT_FALSE(ParseApiLevel("A"));
|
||||
|
||||
EXPECT_TRUE(ParseApiLevel("current").has_value());
|
||||
EXPECT_EQ(FUTURE_API_LEVEL, ParseApiLevel("current").value());
|
||||
|
||||
EXPECT_TRUE(ParseApiLevel("16").has_value());
|
||||
EXPECT_EQ(16l, ParseApiLevel("16").value());
|
||||
}
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// 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 "exported_symbol_set.h"
|
||||
|
||||
#include "ir_representation.h"
|
||||
#include "stl_utils.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
#include <fnmatch.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
static inline bool IsCppSymbol(const std::string &name) {
|
||||
return StartsWith(name, "_Z");
|
||||
}
|
||||
|
||||
|
||||
static inline bool HasMatchingGlobPattern(
|
||||
const ExportedSymbolSet::GlobPatternSet &patterns, const char *text) {
|
||||
for (auto &&pattern : patterns) {
|
||||
if (fnmatch(pattern.c_str(), text, 0) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static inline bool HasMatchingGlobPattern(
|
||||
const ExportedSymbolSet::GlobPatternSet &patterns,
|
||||
const std::string &text) {
|
||||
return HasMatchingGlobPattern(patterns, text.c_str());
|
||||
}
|
||||
|
||||
|
||||
void ExportedSymbolSet::AddFunction(const std::string &name,
|
||||
ElfSymbolIR::ElfSymbolBinding binding) {
|
||||
funcs_.emplace(name, ElfFunctionIR(name, binding));
|
||||
}
|
||||
|
||||
|
||||
void ExportedSymbolSet::AddVar(const std::string &name,
|
||||
ElfSymbolIR::ElfSymbolBinding binding) {
|
||||
vars_.emplace(name, ElfObjectIR(name, binding));
|
||||
}
|
||||
|
||||
|
||||
bool ExportedSymbolSet::HasSymbol(const std::string &name) const {
|
||||
if (funcs_.find(name) != funcs_.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (vars_.find(name) != vars_.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasMatchingGlobPattern(glob_patterns_, name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (IsCppSymbol(name) && HasDemangledCppSymbolsOrPatterns()) {
|
||||
std::unique_ptr<char, FreeDeleter> demangled_name_c_str(
|
||||
abi::__cxa_demangle(name.c_str(), nullptr, nullptr, nullptr));
|
||||
|
||||
if (demangled_name_c_str) {
|
||||
std::string_view demangled_name(demangled_name_c_str.get());
|
||||
|
||||
if (demangled_cpp_symbols_.find(demangled_name) !=
|
||||
demangled_cpp_symbols_.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HasMatchingGlobPattern(demangled_cpp_glob_patterns_,
|
||||
demangled_name_c_str.get())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
@@ -0,0 +1,132 @@
|
||||
// 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 "exported_symbol_set.h"
|
||||
|
||||
#include "ir_representation.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
TEST(ExportedSymbolSetTest, AddFunction) {
|
||||
ExportedSymbolSet symbols;
|
||||
symbols.AddFunction("global", ElfSymbolIR::ElfSymbolBinding::Global);
|
||||
symbols.AddFunction("weak", ElfSymbolIR::ElfSymbolBinding::Weak);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = symbols.GetFunctions();
|
||||
|
||||
ASSERT_NE(funcs.end(), funcs.find("global"));
|
||||
EXPECT_EQ(ElfSymbolIR::ElfSymbolBinding::Global,
|
||||
funcs.at("global").GetBinding());
|
||||
|
||||
ASSERT_NE(funcs.end(), funcs.find("weak"));
|
||||
EXPECT_EQ(ElfSymbolIR::ElfSymbolBinding::Weak,
|
||||
funcs.at("weak").GetBinding());
|
||||
}
|
||||
|
||||
|
||||
TEST(ExportedSymbolSetTest, AddVar) {
|
||||
ExportedSymbolSet symbols;
|
||||
symbols.AddVar("global", ElfSymbolIR::ElfSymbolBinding::Global);
|
||||
symbols.AddVar("weak", ElfSymbolIR::ElfSymbolBinding::Weak);
|
||||
|
||||
const ExportedSymbolSet::VarMap &vars = symbols.GetVars();
|
||||
|
||||
ASSERT_NE(vars.end(), vars.find("global"));
|
||||
EXPECT_EQ(ElfSymbolIR::ElfSymbolBinding::Global,
|
||||
vars.at("global").GetBinding());
|
||||
|
||||
ASSERT_NE(vars.end(), vars.find("weak"));
|
||||
EXPECT_EQ(ElfSymbolIR::ElfSymbolBinding::Weak,
|
||||
vars.at("weak").GetBinding());
|
||||
}
|
||||
|
||||
|
||||
TEST(ExportedSymbolSetTest, AddGlobPattern) {
|
||||
ExportedSymbolSet symbols;
|
||||
symbols.AddGlobPattern("test1*");
|
||||
|
||||
const ExportedSymbolSet::GlobPatternSet &globs = symbols.GetGlobPatterns();
|
||||
ASSERT_NE(globs.end(), globs.find("test1*"));
|
||||
}
|
||||
|
||||
|
||||
TEST(ExportedSymbolSetTest, AddDemangledCppGlobPattern) {
|
||||
ExportedSymbolSet symbols;
|
||||
symbols.AddDemangledCppGlobPattern("test1*");
|
||||
|
||||
const ExportedSymbolSet::GlobPatternSet &globs =
|
||||
symbols.GetDemangledCppGlobPatterns();
|
||||
ASSERT_NE(globs.end(), globs.find("test1*"));
|
||||
}
|
||||
|
||||
|
||||
TEST(ExportedSymbolSetTest, AddDemangledCppSymbol) {
|
||||
ExportedSymbolSet symbols;
|
||||
symbols.AddDemangledCppSymbol("Test::test()");
|
||||
|
||||
const ExportedSymbolSet::NameSet &names = symbols.GetDemangledCppSymbols();
|
||||
ASSERT_NE(names.end(), names.find("Test::test()"));
|
||||
}
|
||||
|
||||
|
||||
TEST(ExportedSymbolSetTest, HasSymbol) {
|
||||
ExportedSymbolSet symbols;
|
||||
|
||||
symbols.AddFunction("global_func", ElfSymbolIR::ElfSymbolBinding::Global);
|
||||
symbols.AddVar("global_var", ElfSymbolIR::ElfSymbolBinding::Global);
|
||||
|
||||
symbols.AddGlobPattern("test_glob1_*");
|
||||
symbols.AddGlobPattern("test_glob2_[Aa]");
|
||||
symbols.AddGlobPattern("test_glob3_?");
|
||||
|
||||
symbols.AddDemangledCppGlobPattern("Test1::*");
|
||||
symbols.AddDemangledCppGlobPattern("Test2::[Aa]()");
|
||||
symbols.AddDemangledCppGlobPattern("Test3::?()");
|
||||
symbols.AddDemangledCppSymbol("Test4::test()");
|
||||
|
||||
// Test literal names
|
||||
EXPECT_TRUE(symbols.HasSymbol("global_func"));
|
||||
EXPECT_TRUE(symbols.HasSymbol("global_var"));
|
||||
|
||||
EXPECT_FALSE(symbols.HasSymbol(""));
|
||||
EXPECT_FALSE(symbols.HasSymbol("no_such_function"));
|
||||
|
||||
// Test glob patterns
|
||||
EXPECT_TRUE(symbols.HasSymbol("test_glob1_a"));
|
||||
EXPECT_TRUE(symbols.HasSymbol("test_glob2_A"));
|
||||
EXPECT_TRUE(symbols.HasSymbol("test_glob2_a"));
|
||||
EXPECT_TRUE(symbols.HasSymbol("test_glob3_b"));
|
||||
|
||||
EXPECT_FALSE(symbols.HasSymbol("test_glob2_Ax"));
|
||||
EXPECT_FALSE(symbols.HasSymbol("test_glob2_B"));
|
||||
EXPECT_FALSE(symbols.HasSymbol("test_glob3_Bx"));
|
||||
|
||||
// Test C++ names and patterns
|
||||
EXPECT_TRUE(symbols.HasSymbol("_ZN5Test14testEv"));
|
||||
EXPECT_TRUE(symbols.HasSymbol("_ZN5Test21AEv"));
|
||||
EXPECT_TRUE(symbols.HasSymbol("_ZN5Test21aEv"));
|
||||
EXPECT_TRUE(symbols.HasSymbol("_ZN5Test31bEv"));
|
||||
EXPECT_TRUE(symbols.HasSymbol("_ZN5Test44testEv"));
|
||||
|
||||
EXPECT_FALSE(symbols.HasSymbol("_ZN5Test22AxEv"));
|
||||
EXPECT_FALSE(symbols.HasSymbol("_ZN5Test21bEv"));
|
||||
EXPECT_FALSE(symbols.HasSymbol("_ZN5Test32BxEv"));
|
||||
}
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
@@ -21,8 +21,10 @@
|
||||
#include <llvm/Object/ELFTypes.h>
|
||||
#include <llvm/Object/SymbolSize.h>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
template <typename T>
|
||||
static inline T UnWrap(llvm::Expected<T> value_or_error) {
|
||||
if (!value_or_error) {
|
||||
@@ -34,6 +36,7 @@ static inline T UnWrap(llvm::Expected<T> value_or_error) {
|
||||
return std::move(value_or_error.get());
|
||||
}
|
||||
|
||||
|
||||
static abi_util::ElfSymbolIR::ElfSymbolBinding
|
||||
LLVMToIRSymbolBinding(unsigned char binding) {
|
||||
switch (binding) {
|
||||
@@ -45,7 +48,8 @@ LLVMToIRSymbolBinding(unsigned char binding) {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
template <typename T>
|
||||
class ELFSoFileParser : public SoFileParser {
|
||||
private:
|
||||
LLVM_ELF_IMPORT_TYPES_ELFT(T)
|
||||
@@ -57,12 +61,8 @@ class ELFSoFileParser : public SoFileParser {
|
||||
|
||||
~ELFSoFileParser() override {}
|
||||
|
||||
const std::map<std::string, ElfFunctionIR> &GetFunctions() const override {
|
||||
return functions_;
|
||||
}
|
||||
|
||||
const std::map<std::string, ElfObjectIR> &GetGlobVars() const override {
|
||||
return globvars_;
|
||||
std::unique_ptr<ExportedSymbolSet> Parse() override {
|
||||
return std::move(exported_symbols_);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -77,38 +77,44 @@ class ELFSoFileParser : public SoFileParser {
|
||||
|
||||
private:
|
||||
const llvm::object::ELFObjectFile<T> *obj_;
|
||||
std::map<std::string, abi_util::ElfFunctionIR> functions_;
|
||||
std::map<std::string, abi_util::ElfObjectIR> globvars_;
|
||||
std::unique_ptr<ExportedSymbolSet> exported_symbols_;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
||||
template <typename T>
|
||||
ELFSoFileParser<T>::ELFSoFileParser(const llvm::object::ELFObjectFile<T> *obj) {
|
||||
assert(obj != nullptr);
|
||||
|
||||
exported_symbols_.reset(new ExportedSymbolSet());
|
||||
|
||||
for (auto symbol_it : obj->getDynamicSymbolIterators()) {
|
||||
const Elf_Sym *elf_sym = obj->getSymbol(symbol_it.getRawDataRefImpl());
|
||||
assert (elf_sym != nullptr);
|
||||
if (!IsSymbolExported(elf_sym) || elf_sym->isUndefined()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
abi_util::ElfSymbolIR::ElfSymbolBinding symbol_binding =
|
||||
LLVMToIRSymbolBinding(elf_sym->getBinding());
|
||||
llvm::object::SymbolRef::Type type = UnWrap(symbol_it.getType());
|
||||
std::string symbol_name = UnWrap(symbol_it.getName());
|
||||
|
||||
llvm::object::SymbolRef::Type type = UnWrap(symbol_it.getType());
|
||||
if (type == llvm::object::SymbolRef::Type::ST_Function) {
|
||||
functions_.emplace(symbol_name,
|
||||
ElfFunctionIR(symbol_name, symbol_binding));
|
||||
exported_symbols_->AddFunction(symbol_name, symbol_binding);
|
||||
} else if (type == llvm::object::SymbolRef::Type::ST_Data) {
|
||||
globvars_.emplace(symbol_name, ElfObjectIR(symbol_name, symbol_binding));
|
||||
exported_symbols_->AddVar(symbol_name, symbol_binding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
template <typename T>
|
||||
static std::unique_ptr<SoFileParser> CreateELFSoFileParser(
|
||||
const llvm::object::ELFObjectFile<T> *elfo) {
|
||||
return llvm::make_unique<ELFSoFileParser<T>>(elfo);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<SoFileParser> SoFileParser::Create(
|
||||
const std::string &so_file_path) {
|
||||
auto binary = llvm::object::createBinary(so_file_path);
|
||||
@@ -149,4 +155,5 @@ std::unique_ptr<SoFileParser> SoFileParser::Create(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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 "string_utils.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
std::string_view Trim(std::string_view s) {
|
||||
std::string::size_type start = s.find_first_not_of(" \t\r\n");
|
||||
if (start == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
std::string::size_type end = s.find_last_not_of(" \t\r\n");
|
||||
if (end == std::string::npos) {
|
||||
return s.substr(start);
|
||||
}
|
||||
return s.substr(start, end - start + 1);
|
||||
}
|
||||
|
||||
|
||||
bool StartsWith(std::string_view s, std::string_view prefix) {
|
||||
return s.size() >= prefix.size() && s.compare(0, prefix.size(), prefix) == 0;
|
||||
}
|
||||
|
||||
|
||||
bool EndsWith(std::string_view s, std::string_view suffix) {
|
||||
return (s.size() >= suffix.size() &&
|
||||
s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0);
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::string_view> Split(std::string_view s,
|
||||
std::string_view delim_chars) {
|
||||
std::vector<std::string_view> res;
|
||||
std::string::size_type pos = 0;
|
||||
while (true) {
|
||||
pos = s.find_first_not_of(delim_chars, pos);
|
||||
if (pos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string::size_type end = s.find_first_of(delim_chars, pos + 1);
|
||||
if (end == std::string::npos) {
|
||||
res.push_back(s.substr(pos));
|
||||
break;
|
||||
}
|
||||
|
||||
res.push_back(s.substr(pos, end - pos));
|
||||
pos = end + 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
std::optional<int> ParseInt(const std::string &s) {
|
||||
const char *start = s.c_str();
|
||||
if (*start == '\0') {
|
||||
return {};
|
||||
}
|
||||
|
||||
char *endptr = nullptr;
|
||||
long int res = ::strtol(start, &endptr, 10);
|
||||
if (*endptr != '\0') {
|
||||
return {};
|
||||
}
|
||||
|
||||
return static_cast<int>(res);
|
||||
}
|
||||
|
||||
|
||||
bool IsGlobPattern(std::string_view s) {
|
||||
return s.find_first_of("*?[") != std::string::npos;
|
||||
}
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
@@ -0,0 +1,107 @@
|
||||
// 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 "string_utils.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
TEST(StringUtilsTest, Trim) {
|
||||
EXPECT_EQ("a b", Trim(" a b "));
|
||||
EXPECT_EQ("a b", Trim(" a b"));
|
||||
EXPECT_EQ("a b", Trim("a b "));
|
||||
EXPECT_EQ("a b", Trim("a b"));
|
||||
EXPECT_EQ("a b", Trim("\ta b\n"));
|
||||
}
|
||||
|
||||
|
||||
TEST(StringUtilsTest, StartsWith) {
|
||||
EXPECT_TRUE(StartsWith("abcd", "ab"));
|
||||
EXPECT_TRUE(StartsWith("a", "a"));
|
||||
EXPECT_TRUE(StartsWith("a", ""));
|
||||
EXPECT_TRUE(StartsWith("", ""));
|
||||
|
||||
EXPECT_FALSE(StartsWith("ab", "abcd"));
|
||||
EXPECT_FALSE(StartsWith("", "ab"));
|
||||
}
|
||||
|
||||
|
||||
TEST(StringUtilsTest, EndsWith) {
|
||||
EXPECT_TRUE(EndsWith("abcd", "cd"));
|
||||
EXPECT_TRUE(EndsWith("d", "d"));
|
||||
EXPECT_TRUE(EndsWith("d", ""));
|
||||
EXPECT_TRUE(EndsWith("", ""));
|
||||
|
||||
EXPECT_FALSE(EndsWith("cd", "abcd"));
|
||||
EXPECT_FALSE(EndsWith("", "cd"));
|
||||
}
|
||||
|
||||
|
||||
TEST(StringUtilsTest, Split) {
|
||||
std::vector<std::string_view> xs;
|
||||
|
||||
xs = Split(" a bb ccc ", " ");
|
||||
EXPECT_EQ(3, xs.size());
|
||||
EXPECT_EQ("a", xs[0]);
|
||||
EXPECT_EQ("bb", xs[1]);
|
||||
EXPECT_EQ("ccc", xs[2]);
|
||||
|
||||
xs = Split("a", " ");
|
||||
EXPECT_EQ(1, xs.size());
|
||||
EXPECT_EQ("a", xs[0]);
|
||||
|
||||
xs = Split("a b", " ");
|
||||
EXPECT_EQ(2, xs.size());
|
||||
EXPECT_EQ("a", xs[0]);
|
||||
EXPECT_EQ("b", xs[1]);
|
||||
|
||||
xs = Split("a \t \t \tb", " \t");
|
||||
EXPECT_EQ(2, xs.size());
|
||||
EXPECT_EQ("a", xs[0]);
|
||||
EXPECT_EQ("b", xs[1]);
|
||||
}
|
||||
|
||||
|
||||
TEST(StringUtilsTest, ParseInt) {
|
||||
EXPECT_FALSE(ParseInt(""));
|
||||
EXPECT_FALSE(ParseInt("a"));
|
||||
EXPECT_FALSE(ParseInt("0xa"));
|
||||
EXPECT_FALSE(ParseInt("16h"));
|
||||
|
||||
EXPECT_TRUE(ParseInt("0").has_value());
|
||||
EXPECT_EQ(0, ParseInt("0").value());
|
||||
|
||||
EXPECT_TRUE(ParseInt("16").has_value());
|
||||
EXPECT_EQ(16, ParseInt("16").value());
|
||||
|
||||
EXPECT_TRUE(ParseInt("-16").has_value());
|
||||
EXPECT_EQ(-16, ParseInt("-16").value());
|
||||
}
|
||||
|
||||
|
||||
TEST(StringUtilsTest, IsGlobPattern) {
|
||||
EXPECT_TRUE(IsGlobPattern("*.so"));
|
||||
EXPECT_TRUE(IsGlobPattern("[ab].txt"));
|
||||
EXPECT_TRUE(IsGlobPattern("?.txt"));
|
||||
|
||||
EXPECT_FALSE(IsGlobPattern("name"));
|
||||
EXPECT_FALSE(IsGlobPattern(".txt"));
|
||||
EXPECT_FALSE(IsGlobPattern(""));
|
||||
}
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
@@ -14,202 +14,283 @@
|
||||
|
||||
#include "version_script_parser.h"
|
||||
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <llvm/Support/FileSystem.h>
|
||||
#include <llvm/Support/Path.h>
|
||||
#include "exported_symbol_set.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
#define FUTURE_API 10000
|
||||
|
||||
std::unordered_set<std::string> AllArches({
|
||||
"arm", "arm64", "x86", "x86_64", "mips", "mips64"});
|
||||
static constexpr char DEFAULT_ARCH[] = "arm64";
|
||||
|
||||
static bool StringContains(const std::string &line,
|
||||
const std::string &substring) {
|
||||
return (line.find(substring) != std::string::npos);
|
||||
|
||||
inline std::string GetIntroducedArchTag(const std::string &arch) {
|
||||
return "introduced-" + arch + "=";
|
||||
}
|
||||
|
||||
static bool LineSatisfiesArch(const std::string &line,
|
||||
const std::string arch) {
|
||||
bool has_arch_tags = false;
|
||||
for (auto &&possible_arch : AllArches) {
|
||||
if (StringContains(line, possible_arch)) {
|
||||
has_arch_tags = true;
|
||||
break;
|
||||
|
||||
VersionScriptParser::VersionScriptParser()
|
||||
: arch_(DEFAULT_ARCH), introduced_arch_tag_(GetIntroducedArchTag(arch_)),
|
||||
api_level_(FUTURE_API_LEVEL), stream_(nullptr), line_no_(0) {}
|
||||
|
||||
|
||||
void VersionScriptParser::SetArch(const std::string &arch) {
|
||||
arch_ = arch;
|
||||
introduced_arch_tag_ = GetIntroducedArchTag(arch);
|
||||
}
|
||||
|
||||
|
||||
VersionScriptParser::ParsedTags VersionScriptParser::ParseSymbolTags(
|
||||
const std::string &line) {
|
||||
static const char *const POSSIBLE_ARCHES[] = {
|
||||
"arm", "arm64", "x86", "x86_64", "mips", "mips64"};
|
||||
|
||||
ParsedTags result;
|
||||
|
||||
std::string_view line_view(line);
|
||||
std::string::size_type comment_pos = line_view.find('#');
|
||||
if (comment_pos == std::string::npos) {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string_view comment_line = line_view.substr(comment_pos + 1);
|
||||
std::vector<std::string_view> tags = Split(comment_line, " \t");
|
||||
|
||||
bool has_introduced_arch_tags = false;
|
||||
|
||||
for (auto &&tag : tags) {
|
||||
// Check excluded tags.
|
||||
if (excluded_symbol_tags_.find(tag) != excluded_symbol_tags_.end()) {
|
||||
result.has_excluded_tags_ = true;
|
||||
}
|
||||
|
||||
// Check the var tag.
|
||||
if (tag == "var") {
|
||||
result.has_var_tag_ = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check arch tags.
|
||||
if (tag == arch_) {
|
||||
result.has_arch_tags_ = true;
|
||||
result.has_current_arch_tag_ = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto &&possible_arch : POSSIBLE_ARCHES) {
|
||||
if (tag == possible_arch) {
|
||||
result.has_arch_tags_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check introduced tags.
|
||||
if (StartsWith(tag, "introduced=")) {
|
||||
std::optional<ApiLevel> intro = ParseApiLevel(
|
||||
std::string(tag.substr(sizeof("introduced=") - 1)));
|
||||
if (!intro) {
|
||||
ReportError("Bad introduced tag: " + std::string(tag));
|
||||
} else {
|
||||
if (!has_introduced_arch_tags) {
|
||||
result.has_introduced_tags_ = true;
|
||||
result.introduced_ = intro.value();
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StartsWith(tag, introduced_arch_tag_)) {
|
||||
std::optional<ApiLevel> intro = ParseApiLevel(
|
||||
std::string(tag.substr(introduced_arch_tag_.size())));
|
||||
if (!intro) {
|
||||
ReportError("Bad introduced tag " + std::string(tag));
|
||||
} else {
|
||||
has_introduced_arch_tags = true;
|
||||
result.has_introduced_tags_ = true;
|
||||
result.introduced_ = intro.value();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check future tags.
|
||||
if (tag == "future") {
|
||||
result.has_future_tag_ = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return (has_arch_tags && StringContains(line, arch)) || !has_arch_tags;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
VersionScriptParser::VersionScriptParser(const std::string &version_script,
|
||||
const std::string &arch,
|
||||
const std::string &api)
|
||||
: version_script_(version_script), arch_(arch), api_(ApiStrToInt(api)) {}
|
||||
|
||||
int VersionScriptParser::ApiStrToInt(const std::string &api) {
|
||||
// Follow what build/soong/cc/gen_stub_libs.py does.
|
||||
if (api == "current") {
|
||||
return FUTURE_API;
|
||||
bool VersionScriptParser::IsSymbolExported(
|
||||
const VersionScriptParser::ParsedTags &tags) {
|
||||
if (tags.has_excluded_tags_) {
|
||||
return false;
|
||||
}
|
||||
return std::stoi(api);
|
||||
|
||||
if (tags.has_arch_tags_ && !tags.has_current_arch_tag_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tags.has_future_tag_) {
|
||||
return api_level_ == FUTURE_API_LEVEL;
|
||||
}
|
||||
|
||||
if (tags.has_introduced_tags_) {
|
||||
return api_level_ >= tags.introduced_;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VersionScriptParser::SymbolInArchAndApiVersion(const std::string &line,
|
||||
const std::string &arch,
|
||||
int api) {
|
||||
// If the tags do not have an "introduced" requirement, the symbol is
|
||||
// exported.
|
||||
if (!StringContains(line, "introduced") && LineSatisfiesArch(line, arch)) {
|
||||
|
||||
bool VersionScriptParser::ParseSymbolLine(const std::string &line,
|
||||
bool is_in_extern_cpp) {
|
||||
// The symbol name comes before the ';'.
|
||||
std::string::size_type pos = line.find(";");
|
||||
if (pos == std::string::npos) {
|
||||
ReportError("No semicolon at the end of the symbol line: " + line);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string symbol(Trim(line.substr(0, pos)));
|
||||
|
||||
ParsedTags tags = ParseSymbolTags(line);
|
||||
if (!IsSymbolExported(tags)) {
|
||||
return true;
|
||||
}
|
||||
if (line == "future") {
|
||||
return api == FUTURE_API;
|
||||
|
||||
if (is_in_extern_cpp) {
|
||||
if (IsGlobPattern(symbol)) {
|
||||
exported_symbols_->AddDemangledCppGlobPattern(symbol);
|
||||
} else {
|
||||
exported_symbols_->AddDemangledCppSymbol(symbol);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const std::string regex_match_string1 = " *introduced-" + arch + "=([0-9]+)";
|
||||
const std::string regex_match_string2 = " *introduced=([0-9]+)";
|
||||
std::smatch matcher1;
|
||||
std::smatch matcher2;
|
||||
std::regex match_clause1(regex_match_string1);
|
||||
std::regex match_clause2(regex_match_string2);
|
||||
int matched_api = -1;
|
||||
if (std::regex_search(line, matcher1, match_clause1)) {
|
||||
matched_api = std::stoi(matcher1.str(1));
|
||||
} else if ((std::regex_search(line, matcher2, match_clause2)) &&
|
||||
LineSatisfiesArch(line, arch)) {
|
||||
matched_api = std::stoi(matcher2.str(1));
|
||||
|
||||
if (IsGlobPattern(symbol)) {
|
||||
exported_symbols_->AddGlobPattern(symbol);
|
||||
return true;
|
||||
}
|
||||
// If the arch specific tag / version specific tag was found and the api level
|
||||
// required was greater than the api level offered.
|
||||
return (matched_api <= 0 || api >= matched_api);
|
||||
|
||||
if (tags.has_var_tag_) {
|
||||
exported_symbols_->AddVar(symbol, ElfSymbolIR::ElfSymbolBinding::Global);
|
||||
} else {
|
||||
exported_symbols_->AddFunction(symbol,
|
||||
ElfSymbolIR::ElfSymbolBinding::Global);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VersionScriptParser::SymbolExported(const std::string &line,
|
||||
const std::string &arch, int api) {
|
||||
// Empty line means that the symbol is exported
|
||||
if (line.empty() || SymbolInArchAndApiVersion(line, arch, api)) {
|
||||
|
||||
bool VersionScriptParser::ParseVersionBlock(bool ignore_symbols) {
|
||||
static const std::regex EXTERN_CPP_PATTERN(R"(extern\s*"[Cc]\+\+"\s*\{)");
|
||||
|
||||
LineScope scope = LineScope::GLOBAL;
|
||||
bool is_in_extern_cpp = false;
|
||||
|
||||
while (true) {
|
||||
std::string line;
|
||||
if (!ReadLine(line)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (line.find("}") != std::string::npos) {
|
||||
if (is_in_extern_cpp) {
|
||||
is_in_extern_cpp = false;
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check extern "c++"
|
||||
if (std::regex_match(line, EXTERN_CPP_PATTERN)) {
|
||||
is_in_extern_cpp = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check symbol visibility label
|
||||
if (StartsWith(line, "local:")) {
|
||||
scope = LineScope::LOCAL;
|
||||
continue;
|
||||
}
|
||||
if (StartsWith(line, "global:")) {
|
||||
scope = LineScope::GLOBAL;
|
||||
continue;
|
||||
}
|
||||
if (scope != LineScope::GLOBAL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse symbol line
|
||||
if (!ignore_symbols) {
|
||||
if (!ParseSymbolLine(line, is_in_extern_cpp)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ReportError("No matching closing parenthesis");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<ExportedSymbolSet> VersionScriptParser::Parse(
|
||||
std::istream &stream) {
|
||||
// Initialize the parser context
|
||||
stream_ = &stream;
|
||||
line_no_ = 0;
|
||||
exported_symbols_.reset(new ExportedSymbolSet());
|
||||
|
||||
// Parse
|
||||
while (true) {
|
||||
std::string line;
|
||||
if (!ReadLine(line)) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string::size_type lparen_pos = line.find("{");
|
||||
if (lparen_pos == std::string::npos) {
|
||||
ReportError("No version opening parenthesis" + line);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string version(Trim(line.substr(0, lparen_pos - 1)));
|
||||
bool exclude_symbol_version = (excluded_symbol_versions_.find(version) !=
|
||||
excluded_symbol_versions_.end());
|
||||
|
||||
if (!ParseVersionBlock(exclude_symbol_version)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(exported_symbols_);
|
||||
}
|
||||
|
||||
|
||||
bool VersionScriptParser::ReadLine(std::string &line) {
|
||||
while (std::getline(*stream_, line)) {
|
||||
++line_no_;
|
||||
line = std::string(Trim(line));
|
||||
if (line.empty() || line[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void VersionScriptParser::AddToVars(std::string &symbol) {
|
||||
if (symbol.find("*") != std::string::npos) {
|
||||
globvar_regexs_.insert(symbol);
|
||||
} else {
|
||||
globvars_.emplace(
|
||||
symbol, ElfObjectIR(symbol, ElfSymbolIR::ElfSymbolBinding::Global));
|
||||
}
|
||||
}
|
||||
|
||||
void VersionScriptParser::AddToFunctions(std::string &symbol) {
|
||||
if (symbol.find("*") != std::string::npos) {
|
||||
function_regexs_.insert(symbol);
|
||||
} else {
|
||||
functions_.emplace(
|
||||
symbol, ElfFunctionIR(symbol, ElfSymbolIR::ElfSymbolBinding::Global));
|
||||
}
|
||||
}
|
||||
VersionScriptParser::ErrorHandler::~ErrorHandler() {}
|
||||
|
||||
bool VersionScriptParser::ParseSymbolLine(const std::string &line) {
|
||||
// The symbol lies before the ';' and the tags are after ';'
|
||||
std::string::size_type pos = line.find(";");
|
||||
if (pos == std::string::npos) {
|
||||
llvm::errs() << "Couldn't find end of symbol" << line <<"\n";
|
||||
return false;
|
||||
}
|
||||
std::string symbol = line.substr(0, pos);
|
||||
std::string::size_type last_space = symbol.find_last_of(' ');
|
||||
symbol = symbol.substr(last_space + 1, pos);
|
||||
std::string tags = line.substr(pos + 1);
|
||||
if (SymbolExported(tags, arch_, api_)) {
|
||||
if (StringContains(tags, "var")) {
|
||||
AddToVars(symbol);
|
||||
} else {
|
||||
AddToFunctions(symbol);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef VersionScriptParser::LineScope LineScope;
|
||||
|
||||
LineScope VersionScriptParser::GetLineScope(std::string &line,
|
||||
LineScope scope) {
|
||||
if (StringContains(line, "local:")) {
|
||||
scope = LineScope::local;
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
bool VersionScriptParser::ParseInnerBlock(std::ifstream &symbol_ifstream) {
|
||||
std::string line = "";
|
||||
LineScope scope = LineScope::global;
|
||||
|
||||
while (std::getline(symbol_ifstream, line)) {
|
||||
if (line.find("}") != std::string::npos) {
|
||||
break;
|
||||
}
|
||||
if (line.c_str()[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
scope = GetLineScope(line, scope);
|
||||
if (scope != LineScope::global || StringContains(line, "global:")) {
|
||||
continue;
|
||||
}
|
||||
ParseSymbolLine(line);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::map<std::string, ElfFunctionIR> &
|
||||
VersionScriptParser::GetFunctions() {
|
||||
return functions_;
|
||||
}
|
||||
|
||||
const std::map<std::string, ElfObjectIR> &VersionScriptParser::GetGlobVars() {
|
||||
return globvars_;
|
||||
}
|
||||
|
||||
const std::set<std::string> &VersionScriptParser::GetFunctionRegexs() {
|
||||
return function_regexs_;
|
||||
}
|
||||
|
||||
const std::set<std::string> &VersionScriptParser::GetGlobVarRegexs() {
|
||||
return globvar_regexs_;
|
||||
}
|
||||
|
||||
bool VersionScriptParser::Parse() {
|
||||
std::ifstream symbol_ifstream(version_script_);
|
||||
if (!symbol_ifstream.is_open()) {
|
||||
llvm::errs() << "Failed to open version script file\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(symbol_ifstream, line)) {
|
||||
// Skip comment lines.
|
||||
if (line.c_str()[0] == '#') {
|
||||
continue;
|
||||
}
|
||||
if (StringContains(line, "{")) {
|
||||
if ((StringContains(line, "PRIVATE"))) {
|
||||
continue;
|
||||
}
|
||||
ParseInnerBlock(symbol_ifstream);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
// 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 "version_script_parser.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
|
||||
TEST(VersionScriptParserTest, SmokeTest) {
|
||||
static const char testdata[] = R"TESTDATA(
|
||||
LIBEX_1.0 {
|
||||
global:
|
||||
foo1;
|
||||
bar1; # var
|
||||
local:
|
||||
*;
|
||||
};
|
||||
|
||||
LIBEX_2.0 {
|
||||
global:
|
||||
foo2;
|
||||
bar2; # var
|
||||
} LIBEX_1.0;
|
||||
)TESTDATA";
|
||||
|
||||
VersionScriptParser parser;
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
EXPECT_NE(funcs.end(), funcs.find("foo1"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("foo2"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("bar1"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("bar2"));
|
||||
|
||||
const ExportedSymbolSet::VarMap &vars = result->GetVars();
|
||||
EXPECT_NE(vars.end(), vars.find("bar1"));
|
||||
EXPECT_NE(vars.end(), vars.find("bar2"));
|
||||
EXPECT_EQ(vars.end(), vars.find("foo1"));
|
||||
EXPECT_EQ(vars.end(), vars.find("foo2"));
|
||||
}
|
||||
|
||||
|
||||
TEST(VersionScriptParserTest, ExcludeSymbolVersions) {
|
||||
static const char testdata[] = R"TESTDATA(
|
||||
LIBEX_1.0 {
|
||||
global:
|
||||
foo1;
|
||||
bar1; # var
|
||||
local:
|
||||
*;
|
||||
};
|
||||
|
||||
LIBEX_PRIVATE {
|
||||
global:
|
||||
foo2;
|
||||
bar2; # var
|
||||
} LIBEX_1.0;
|
||||
)TESTDATA";
|
||||
|
||||
// excluded_symbol_versions = {}
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
EXPECT_NE(funcs.end(), funcs.find("foo2"));
|
||||
|
||||
const ExportedSymbolSet::VarMap &vars = result->GetVars();
|
||||
EXPECT_NE(vars.end(), vars.find("bar2"));
|
||||
}
|
||||
|
||||
// excluded_symbol_versions = {"LIBEX_PRIVATE"}
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.AddExcludedSymbolVersion("LIBEX_PRIVATE");
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
EXPECT_EQ(funcs.end(), funcs.find("foo2"));
|
||||
|
||||
const ExportedSymbolSet::VarMap &vars = result->GetVars();
|
||||
EXPECT_EQ(vars.end(), vars.find("bar2"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(VersionScriptParserTest, VisibilityLabels) {
|
||||
static const char testdata[] = R"TESTDATA(
|
||||
LIBEX_1.0 {
|
||||
global:
|
||||
global_f1;
|
||||
global_v1; # var
|
||||
local:
|
||||
local_f2;
|
||||
local_v2; # var
|
||||
global:
|
||||
global_f3;
|
||||
global_v3; # var
|
||||
global:
|
||||
global_f4;
|
||||
global_v4; # var
|
||||
local:
|
||||
local_f5;
|
||||
local_v5; # var
|
||||
local:
|
||||
local_f6;
|
||||
local_v6; # var
|
||||
};
|
||||
)TESTDATA";
|
||||
|
||||
VersionScriptParser parser;
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("global_f1"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("global_f3"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("global_f4"));
|
||||
|
||||
EXPECT_EQ(funcs.end(), funcs.find("local_f2"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("local_f5"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("local_f6"));
|
||||
|
||||
const ExportedSymbolSet::VarMap &vars = result->GetVars();
|
||||
|
||||
EXPECT_NE(vars.end(), vars.find("global_v1"));
|
||||
EXPECT_NE(vars.end(), vars.find("global_v3"));
|
||||
EXPECT_NE(vars.end(), vars.find("global_v4"));
|
||||
|
||||
EXPECT_EQ(vars.end(), vars.find("local_v2"));
|
||||
EXPECT_EQ(vars.end(), vars.find("local_v5"));
|
||||
EXPECT_EQ(vars.end(), vars.find("local_v6"));
|
||||
}
|
||||
|
||||
|
||||
TEST(VersionScriptParserTest, ParseSymbolTagsIntroduced) {
|
||||
static const char testdata[] = R"TESTDATA(
|
||||
LIBEX_1.0 {
|
||||
global:
|
||||
test1; # introduced=19
|
||||
test2; # introduced=19 introduced-arm64=20
|
||||
test3; # introduced-arm64=20 introduced=19
|
||||
test4; # future
|
||||
};
|
||||
)TESTDATA";
|
||||
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.SetArch("arm64");
|
||||
parser.SetApiLevel(18);
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test2"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test3"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test4"));
|
||||
}
|
||||
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.SetArch("arm64");
|
||||
parser.SetApiLevel(19);
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test2"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test3"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test4"));
|
||||
}
|
||||
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.SetArch("arm");
|
||||
parser.SetApiLevel(19);
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test2"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test3"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test4"));
|
||||
}
|
||||
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.SetArch("arm64");
|
||||
parser.SetApiLevel(20);
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test2"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test3"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test4"));
|
||||
}
|
||||
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.SetArch("arm64");
|
||||
parser.SetApiLevel(FUTURE_API_LEVEL);
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test2"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test3"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test4"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(VersionScriptParserTest, ParseSymbolTagsArch) {
|
||||
static const char testdata[] = R"TESTDATA(
|
||||
LIBEX_1.0 {
|
||||
global:
|
||||
test1;
|
||||
test2; # arm arm64
|
||||
test3; # arm64
|
||||
test4; # mips
|
||||
};
|
||||
)TESTDATA";
|
||||
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.SetArch("arm");
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test2"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test3"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test4"));
|
||||
}
|
||||
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.SetArch("arm64");
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test2"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test3"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test4"));
|
||||
}
|
||||
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.SetArch("mips");
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test2"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test3"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test4"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(VersionScriptParserTest, ExcludeSymbolTags) {
|
||||
static const char testdata[] = R"TESTDATA(
|
||||
LIBEX_1.0 {
|
||||
global:
|
||||
test1;
|
||||
test2; # exclude-tag
|
||||
};
|
||||
)TESTDATA";
|
||||
|
||||
// exclude_symbol_tags = {}
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_NE(funcs.end(), funcs.find("test2"));
|
||||
}
|
||||
|
||||
// exclude_symbol_tags = {"exclude-tag"}
|
||||
{
|
||||
VersionScriptParser parser;
|
||||
parser.AddExcludedSymbolTag("exclude-tag");
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
|
||||
|
||||
EXPECT_NE(funcs.end(), funcs.find("test1"));
|
||||
EXPECT_EQ(funcs.end(), funcs.find("test2"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(VersionScriptParserTest, ParseExternCpp) {
|
||||
static const char testdata[] = R"TESTDATA(
|
||||
LIBEX_1.0 {
|
||||
global:
|
||||
test1;
|
||||
extern "C++" {
|
||||
Test2::test();
|
||||
Test3::test();
|
||||
Test4::*;
|
||||
};
|
||||
test5;
|
||||
};
|
||||
)TESTDATA";
|
||||
|
||||
VersionScriptParser parser;
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::NameSet &cpp_symbols =
|
||||
result->GetDemangledCppSymbols();
|
||||
|
||||
EXPECT_NE(cpp_symbols.end(), cpp_symbols.find("Test2::test()"));
|
||||
EXPECT_NE(cpp_symbols.end(), cpp_symbols.find("Test3::test()"));
|
||||
|
||||
EXPECT_EQ(cpp_symbols.end(), cpp_symbols.find("test1"));
|
||||
EXPECT_EQ(cpp_symbols.end(), cpp_symbols.find("test4"));
|
||||
|
||||
const ExportedSymbolSet::GlobPatternSet &cpp_glob_patterns =
|
||||
result->GetDemangledCppGlobPatterns();
|
||||
|
||||
EXPECT_NE(cpp_glob_patterns.end(), cpp_glob_patterns.find("Test4::*"));
|
||||
}
|
||||
|
||||
|
||||
TEST(VersionScriptParserTest, ParseGlobPattern) {
|
||||
static const char testdata[] = R"TESTDATA(
|
||||
LIBEX_1.0 {
|
||||
global:
|
||||
test1*;
|
||||
test2[Aa];
|
||||
test3?;
|
||||
test4;
|
||||
};
|
||||
)TESTDATA";
|
||||
|
||||
|
||||
VersionScriptParser parser;
|
||||
|
||||
std::istringstream stream(testdata);
|
||||
std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
|
||||
ASSERT_TRUE(result);
|
||||
|
||||
const ExportedSymbolSet::GlobPatternSet &glob_patterns =
|
||||
result->GetGlobPatterns();
|
||||
|
||||
EXPECT_NE(glob_patterns.end(), glob_patterns.find("test1*"));
|
||||
EXPECT_NE(glob_patterns.end(), glob_patterns.find("test2[Aa]"));
|
||||
EXPECT_NE(glob_patterns.end(), glob_patterns.find("test3?"));
|
||||
|
||||
EXPECT_EQ(glob_patterns.end(), glob_patterns.find("test4"));
|
||||
}
|
||||
|
||||
|
||||
} // namespace abi_util
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
cc_library {
|
||||
name: "libversion_script_example",
|
||||
srcs: [
|
||||
"example.cpp",
|
||||
],
|
||||
enabled: false,
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "example.h"
|
||||
|
||||
void test1() {}
|
||||
void test2() {}
|
||||
void Test3::test() {}
|
||||
void Test4::test() {}
|
||||
@@ -0,0 +1,12 @@
|
||||
extern "C" void test1();
|
||||
extern "C" void test2();
|
||||
|
||||
class Test3 {
|
||||
public:
|
||||
void test();
|
||||
};
|
||||
|
||||
class Test4 {
|
||||
public:
|
||||
void test();
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
LIBVERSION_SCRIPT_EXAMPLE_1.0 {
|
||||
global:
|
||||
test1; # mytag
|
||||
extern "C++" {
|
||||
Test3::*; # mytag
|
||||
};
|
||||
local:
|
||||
*;
|
||||
};
|
||||
|
||||
LIBVERSION_SCRIPT_EXAMPLE_PRIVATE {
|
||||
global:
|
||||
test2;
|
||||
extern "C++" {
|
||||
Test4::*;
|
||||
};
|
||||
} LIBVERSION_SCRIPT_EXAMPLE_1.0;
|
||||
Binary file not shown.
@@ -499,6 +499,62 @@ TEST_MODULES = [
|
||||
dumper_flags=['-output-format', 'Json'],
|
||||
linker_flags=['-input-format', 'Json', '-output-format', 'Json']
|
||||
),
|
||||
LsdumpModule(
|
||||
name='libversion_script_example',
|
||||
arch='arm64',
|
||||
srcs=[
|
||||
'integration/version_script_example/example.cpp',
|
||||
],
|
||||
version_script='integration/version_script_example/example.map.txt',
|
||||
export_include_dirs=['integration/version_script_example'],
|
||||
dumper_flags=['-output-format', 'Json'],
|
||||
linker_flags=[
|
||||
'-input-format', 'Json',
|
||||
'-output-format', 'Json',
|
||||
'-so', relative_to_abs_path(
|
||||
'integration/version_script_example/prebuilts/' +
|
||||
'libversion_script_example.so'
|
||||
),
|
||||
]
|
||||
),
|
||||
LsdumpModule(
|
||||
name='libversion_script_example_no_private',
|
||||
arch='arm64',
|
||||
srcs=[
|
||||
'integration/version_script_example/example.cpp',
|
||||
],
|
||||
version_script='integration/version_script_example/example.map.txt',
|
||||
export_include_dirs=['integration/version_script_example'],
|
||||
dumper_flags=['-output-format', 'Json'],
|
||||
linker_flags=[
|
||||
'-input-format', 'Json',
|
||||
'-output-format', 'Json',
|
||||
'-so', relative_to_abs_path(
|
||||
'integration/version_script_example/prebuilts/' +
|
||||
'libversion_script_example.so'
|
||||
),
|
||||
'--exclude-symbol-version', 'LIBVERSION_SCRIPT_EXAMPLE_PRIVATE',
|
||||
]
|
||||
),
|
||||
LsdumpModule(
|
||||
name='libversion_script_example_no_mytag',
|
||||
arch='arm64',
|
||||
srcs=[
|
||||
'integration/version_script_example/example.cpp',
|
||||
],
|
||||
version_script='integration/version_script_example/example.map.txt',
|
||||
export_include_dirs=['integration/version_script_example'],
|
||||
dumper_flags=['-output-format', 'Json'],
|
||||
linker_flags=[
|
||||
'-input-format', 'Json',
|
||||
'-output-format', 'Json',
|
||||
'-so', relative_to_abs_path(
|
||||
'integration/version_script_example/prebuilts/' +
|
||||
'libversion_script_example.so'
|
||||
),
|
||||
'--exclude-symbol-tag', 'mytag',
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
TEST_MODULES = {m.name: m for m in TEST_MODULES}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"array_types" : [],
|
||||
"builtin_types" :
|
||||
[
|
||||
{
|
||||
"linker_set_key" : "void",
|
||||
"name" : "void",
|
||||
"referenced_type" : "type-1",
|
||||
"self_type" : "type-1"
|
||||
}
|
||||
],
|
||||
"elf_functions" :
|
||||
[
|
||||
{
|
||||
"name" : "_ZN5Test34testEv"
|
||||
},
|
||||
{
|
||||
"name" : "_ZN5Test44testEv"
|
||||
},
|
||||
{
|
||||
"name" : "test1"
|
||||
},
|
||||
{
|
||||
"name" : "test2"
|
||||
}
|
||||
],
|
||||
"elf_objects" : [],
|
||||
"enum_types" : [],
|
||||
"function_types" : [],
|
||||
"functions" :
|
||||
[
|
||||
{
|
||||
"function_name" : "Test3::test",
|
||||
"linker_set_key" : "_ZN5Test34testEv",
|
||||
"parameters" :
|
||||
[
|
||||
{
|
||||
"is_this_ptr" : true,
|
||||
"referenced_type" : "type-3"
|
||||
}
|
||||
],
|
||||
"return_type" : "type-1",
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
},
|
||||
{
|
||||
"function_name" : "Test4::test",
|
||||
"linker_set_key" : "_ZN5Test44testEv",
|
||||
"parameters" :
|
||||
[
|
||||
{
|
||||
"is_this_ptr" : true,
|
||||
"referenced_type" : "type-5"
|
||||
}
|
||||
],
|
||||
"return_type" : "type-1",
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
},
|
||||
{
|
||||
"function_name" : "test1",
|
||||
"linker_set_key" : "test1",
|
||||
"return_type" : "type-1",
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
},
|
||||
{
|
||||
"function_name" : "test2",
|
||||
"linker_set_key" : "test2",
|
||||
"return_type" : "type-1",
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
}
|
||||
],
|
||||
"global_vars" : [],
|
||||
"lvalue_reference_types" : [],
|
||||
"pointer_types" :
|
||||
[
|
||||
{
|
||||
"alignment" : 8,
|
||||
"linker_set_key" : "Test3 *",
|
||||
"name" : "Test3 *",
|
||||
"referenced_type" : "type-2",
|
||||
"self_type" : "type-3",
|
||||
"size" : 8,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
},
|
||||
{
|
||||
"alignment" : 8,
|
||||
"linker_set_key" : "Test4 *",
|
||||
"name" : "Test4 *",
|
||||
"referenced_type" : "type-4",
|
||||
"self_type" : "type-5",
|
||||
"size" : 8,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
}
|
||||
],
|
||||
"qualified_types" : [],
|
||||
"record_types" :
|
||||
[
|
||||
{
|
||||
"alignment" : 1,
|
||||
"linker_set_key" : "Test3",
|
||||
"name" : "Test3",
|
||||
"record_kind" : "class",
|
||||
"referenced_type" : "type-2",
|
||||
"self_type" : "type-2",
|
||||
"size" : 1,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h",
|
||||
"unique_id" : "_ZTS5Test3"
|
||||
},
|
||||
{
|
||||
"alignment" : 1,
|
||||
"linker_set_key" : "Test4",
|
||||
"name" : "Test4",
|
||||
"record_kind" : "class",
|
||||
"referenced_type" : "type-4",
|
||||
"self_type" : "type-4",
|
||||
"size" : 1,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h",
|
||||
"unique_id" : "_ZTS5Test4"
|
||||
}
|
||||
],
|
||||
"rvalue_reference_types" : []
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"array_types" : [],
|
||||
"builtin_types" :
|
||||
[
|
||||
{
|
||||
"linker_set_key" : "void",
|
||||
"name" : "void",
|
||||
"referenced_type" : "type-1",
|
||||
"self_type" : "type-1"
|
||||
}
|
||||
],
|
||||
"elf_functions" :
|
||||
[
|
||||
{
|
||||
"name" : "_ZN5Test44testEv"
|
||||
},
|
||||
{
|
||||
"name" : "test2"
|
||||
}
|
||||
],
|
||||
"elf_objects" : [],
|
||||
"enum_types" : [],
|
||||
"function_types" : [],
|
||||
"functions" :
|
||||
[
|
||||
{
|
||||
"function_name" : "Test4::test",
|
||||
"linker_set_key" : "_ZN5Test44testEv",
|
||||
"parameters" :
|
||||
[
|
||||
{
|
||||
"is_this_ptr" : true,
|
||||
"referenced_type" : "type-5"
|
||||
}
|
||||
],
|
||||
"return_type" : "type-1",
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
},
|
||||
{
|
||||
"function_name" : "test2",
|
||||
"linker_set_key" : "test2",
|
||||
"return_type" : "type-1",
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
}
|
||||
],
|
||||
"global_vars" : [],
|
||||
"lvalue_reference_types" : [],
|
||||
"pointer_types" :
|
||||
[
|
||||
{
|
||||
"alignment" : 8,
|
||||
"linker_set_key" : "Test3 *",
|
||||
"name" : "Test3 *",
|
||||
"referenced_type" : "type-2",
|
||||
"self_type" : "type-3",
|
||||
"size" : 8,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
},
|
||||
{
|
||||
"alignment" : 8,
|
||||
"linker_set_key" : "Test4 *",
|
||||
"name" : "Test4 *",
|
||||
"referenced_type" : "type-4",
|
||||
"self_type" : "type-5",
|
||||
"size" : 8,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
}
|
||||
],
|
||||
"qualified_types" : [],
|
||||
"record_types" :
|
||||
[
|
||||
{
|
||||
"alignment" : 1,
|
||||
"linker_set_key" : "Test3",
|
||||
"name" : "Test3",
|
||||
"record_kind" : "class",
|
||||
"referenced_type" : "type-2",
|
||||
"self_type" : "type-2",
|
||||
"size" : 1,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h",
|
||||
"unique_id" : "_ZTS5Test3"
|
||||
},
|
||||
{
|
||||
"alignment" : 1,
|
||||
"linker_set_key" : "Test4",
|
||||
"name" : "Test4",
|
||||
"record_kind" : "class",
|
||||
"referenced_type" : "type-4",
|
||||
"self_type" : "type-4",
|
||||
"size" : 1,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h",
|
||||
"unique_id" : "_ZTS5Test4"
|
||||
}
|
||||
],
|
||||
"rvalue_reference_types" : []
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"array_types" : [],
|
||||
"builtin_types" :
|
||||
[
|
||||
{
|
||||
"linker_set_key" : "void",
|
||||
"name" : "void",
|
||||
"referenced_type" : "type-1",
|
||||
"self_type" : "type-1"
|
||||
}
|
||||
],
|
||||
"elf_functions" :
|
||||
[
|
||||
{
|
||||
"name" : "_ZN5Test34testEv"
|
||||
},
|
||||
{
|
||||
"name" : "test1"
|
||||
}
|
||||
],
|
||||
"elf_objects" : [],
|
||||
"enum_types" : [],
|
||||
"function_types" : [],
|
||||
"functions" :
|
||||
[
|
||||
{
|
||||
"function_name" : "Test3::test",
|
||||
"linker_set_key" : "_ZN5Test34testEv",
|
||||
"parameters" :
|
||||
[
|
||||
{
|
||||
"is_this_ptr" : true,
|
||||
"referenced_type" : "type-3"
|
||||
}
|
||||
],
|
||||
"return_type" : "type-1",
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
},
|
||||
{
|
||||
"function_name" : "test1",
|
||||
"linker_set_key" : "test1",
|
||||
"return_type" : "type-1",
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
}
|
||||
],
|
||||
"global_vars" : [],
|
||||
"lvalue_reference_types" : [],
|
||||
"pointer_types" :
|
||||
[
|
||||
{
|
||||
"alignment" : 8,
|
||||
"linker_set_key" : "Test3 *",
|
||||
"name" : "Test3 *",
|
||||
"referenced_type" : "type-2",
|
||||
"self_type" : "type-3",
|
||||
"size" : 8,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
},
|
||||
{
|
||||
"alignment" : 8,
|
||||
"linker_set_key" : "Test4 *",
|
||||
"name" : "Test4 *",
|
||||
"referenced_type" : "type-4",
|
||||
"self_type" : "type-5",
|
||||
"size" : 8,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h"
|
||||
}
|
||||
],
|
||||
"qualified_types" : [],
|
||||
"record_types" :
|
||||
[
|
||||
{
|
||||
"alignment" : 1,
|
||||
"linker_set_key" : "Test3",
|
||||
"name" : "Test3",
|
||||
"record_kind" : "class",
|
||||
"referenced_type" : "type-2",
|
||||
"self_type" : "type-2",
|
||||
"size" : 1,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h",
|
||||
"unique_id" : "_ZTS5Test3"
|
||||
},
|
||||
{
|
||||
"alignment" : 1,
|
||||
"linker_set_key" : "Test4",
|
||||
"name" : "Test4",
|
||||
"record_kind" : "class",
|
||||
"referenced_type" : "type-4",
|
||||
"self_type" : "type-4",
|
||||
"size" : 1,
|
||||
"source_file" : "/development/vndk/tools/header-checker/tests/integration/version_script_example/example.h",
|
||||
"unique_id" : "_ZTS5Test4"
|
||||
}
|
||||
],
|
||||
"rvalue_reference_types" : []
|
||||
}
|
||||
@@ -309,6 +309,25 @@ class HeaderCheckerTest(unittest.TestCase):
|
||||
["-input-format-old", "Json", "-input-format-new", "Json",
|
||||
"-consider-opaque-types-different"])
|
||||
|
||||
def test_linker_shared_object_file_and_version_script(self):
|
||||
base_dir = os.path.join(
|
||||
SCRIPT_DIR, 'integration', 'version_script_example')
|
||||
|
||||
cases = [
|
||||
'libversion_script_example',
|
||||
'libversion_script_example_no_mytag',
|
||||
'libversion_script_example_no_private',
|
||||
]
|
||||
|
||||
for module_name in cases:
|
||||
module = Module.get_test_modules_by_name(module_name)[0]
|
||||
example_lsdump_old = self.get_or_create_ref_dump(module, False)
|
||||
example_lsdump_new = self.get_or_create_ref_dump(module, True)
|
||||
self.run_and_compare_abi_diff(
|
||||
example_lsdump_old, example_lsdump_new,
|
||||
module_name, "arm64", 0,
|
||||
["-input-format-old", "Json", "-input-format-new", "Json"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user