diff --git a/vndk/tools/header-checker/Android.bp b/vndk/tools/header-checker/Android.bp index 7c9fe0475..8edebdda1 100644 --- a/vndk/tools/header-checker/Android.bp +++ b/vndk/tools/header-checker/Android.bp @@ -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"], +} diff --git a/vndk/tools/header-checker/TEST_MAPPING b/vndk/tools/header-checker/TEST_MAPPING new file mode 100644 index 000000000..cce52c9bc --- /dev/null +++ b/vndk/tools/header-checker/TEST_MAPPING @@ -0,0 +1,8 @@ +{ + "presubmit" : [ + { + "name" : "libheader-abi-util_test", + "host" : true + } + ] +} diff --git a/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp b/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp index d380c2dcf..3da1ac1e4 100644 --- a/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp +++ b/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp @@ -52,8 +52,17 @@ static llvm::cl::opt version_script( "v", llvm::cl::desc(""), llvm::cl::Optional, llvm::cl::cat(header_linker_category)); +static llvm::cl::list excluded_symbol_versions( + "exclude-symbol-version", llvm::cl::Optional, + llvm::cl::cat(header_linker_category)); + +static llvm::cl::list excluded_symbol_tags( + "exclude-symbol-tag", llvm::cl::Optional, + llvm::cl::cat(header_linker_category)); + static llvm::cl::opt api( "api", llvm::cl::desc(""), llvm::cl::Optional, + llvm::cl::init("current"), llvm::cl::cat(header_linker_category)); static llvm::cl::opt 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 &excluded_symbol_versions, + const std::vector &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 + 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 &dump_files_; const std::vector &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 &excluded_symbol_versions_; + const std::vector &excluded_symbol_tags_; std::set exported_headers_; - std::map function_decl_map_; - std::map globvar_decl_map_; - - // Version Script Regex Matching. - std::set functions_regex_matched_set_; - std::regex functions_vs_regex_; - - // Version Script Regex Matching. - std::set globvars_regex_matched_set_; - std::regex globvars_vs_regex_; + // Exported symbols + std::unique_ptr shared_object_symbols_; + std::unique_ptr 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 *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 &link_set) { - std::string all_regex_match_str = ""; - std::set::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 bool HeaderAbiLinker::LinkDecl( abi_util::IRDumper *dst, const abi_util::AbiElementMap &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 -static bool LinkExportedSymbols(abi_util::IRDumper *dst, - const std::map &symbols) { +template +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 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 function_regexs = - version_script_parser.GetFunctionRegexs(); - std::set 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; } diff --git a/vndk/tools/header-checker/header-abi-util/include/api_level.h b/vndk/tools/header-checker/header-abi-util/include/api_level.h new file mode 100644 index 000000000..11bcb66dc --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/include/api_level.h @@ -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 +#include + + +namespace abi_util { + + +using ApiLevel = int; + + +constexpr ApiLevel FUTURE_API_LEVEL = 10000; + + +std::optional ParseApiLevel(const std::string &api); + + +} // namespace abi_util + + +#endif // API_LEVEL_H_ diff --git a/vndk/tools/header-checker/header-abi-util/include/exported_symbol_set.h b/vndk/tools/header-checker/header-abi-util/include/exported_symbol_set.h new file mode 100644 index 000000000..886cb5435 --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/include/exported_symbol_set.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 +#include +#include +#include + + +namespace abi_util { + + +class ExportedSymbolSet { + public: + using FunctionMap = std::map>; + using VarMap = std::map>; + using NameSet = std::set>; + using GlobPatternSet = std::set>; + + + 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_ diff --git a/vndk/tools/header-checker/header-abi-util/include/so_file_parser.h b/vndk/tools/header-checker/header-abi-util/include/so_file_parser.h index b19b8f337..3f5eeca49 100644 --- a/vndk/tools/header-checker/header-abi-util/include/so_file_parser.h +++ b/vndk/tools/header-checker/header-abi-util/include/so_file_parser.h @@ -15,25 +15,28 @@ #ifndef SO_FILE_PARSER_H_ #define SO_FILE_PARSER_H_ +#include "exported_symbol_set.h" #include "ir_representation.h" #include #include #include + namespace abi_util { + class SoFileParser { public: static std::unique_ptr Create(const std::string &so_file_path); virtual ~SoFileParser() {} - virtual const std::map &GetFunctions() const = 0; - - virtual const std::map &GetGlobVars() const = 0; + virtual std::unique_ptr Parse() = 0; }; + } // namespace abi_util + #endif // SO_FILE_PARSER_H_ diff --git a/vndk/tools/header-checker/header-abi-util/include/stl_utils.h b/vndk/tools/header-checker/header-abi-util/include/stl_utils.h new file mode 100644 index 000000000..3a520adb1 --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/include/stl_utils.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_ diff --git a/vndk/tools/header-checker/header-abi-util/include/string_utils.h b/vndk/tools/header-checker/header-abi-util/include/string_utils.h new file mode 100644 index 000000000..d222a435f --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/include/string_utils.h @@ -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 +#include +#include + + +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 Split(std::string_view s, + std::string_view delim_chars); + +std::optional ParseInt(const std::string &s); + +bool IsGlobPattern(std::string_view s); + + +} // namespace abi_util + + +#endif // STRING_UTILS_H_ diff --git a/vndk/tools/header-checker/header-abi-util/include/version_script_parser.h b/vndk/tools/header-checker/header-abi-util/include/version_script_parser.h index dd86fbf0f..7becddf01 100644 --- a/vndk/tools/header-checker/header-abi-util/include/version_script_parser.h +++ b/vndk/tools/header-checker/header-abi-util/include/version_script_parser.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 #include #include #include + 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 &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 &GetGlobVars(); - const std::set &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 error_handler) { + error_handler_ = std::move(error_handler); + } + + std::unique_ptr Parse(std::istream &version_script_stream); - const std::set &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 functions_; - std::map globvars_; - // Added to speed up version script parsing and linking. - std::set function_regexs_; - std::set 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 error_handler_; + + std::string arch_; + std::string introduced_arch_tag_; + ApiLevel api_level_; + + std::set> excluded_symbol_versions_; + std::set> excluded_symbol_tags_; + + std::istream *stream_; + int line_no_; + + std::unique_ptr exported_symbols_; }; + } // namespace abi_util + #endif // VERSION_SCRIPT_PARSER_H_ diff --git a/vndk/tools/header-checker/header-abi-util/src/api_level.cpp b/vndk/tools/header-checker/header-abi-util/src/api_level.cpp new file mode 100644 index 000000000..dc0b21683 --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/src/api_level.cpp @@ -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 +#include + + +namespace abi_util { + + +std::optional ParseApiLevel(const std::string &api_level_str) { + if (api_level_str == "current") { + return FUTURE_API_LEVEL; + } + return ParseInt(api_level_str); +} + + +} // namespace abi_util diff --git a/vndk/tools/header-checker/header-abi-util/src/api_level_test.cpp b/vndk/tools/header-checker/header-abi-util/src/api_level_test.cpp new file mode 100644 index 000000000..3f6008cfe --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/src/api_level_test.cpp @@ -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 + + +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 + diff --git a/vndk/tools/header-checker/header-abi-util/src/exported_symbol_set.cpp b/vndk/tools/header-checker/header-abi-util/src/exported_symbol_set.cpp new file mode 100644 index 000000000..13958eb7b --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/src/exported_symbol_set.cpp @@ -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 +#include + + +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 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 diff --git a/vndk/tools/header-checker/header-abi-util/src/exported_symbol_set_test.cpp b/vndk/tools/header-checker/header-abi-util/src/exported_symbol_set_test.cpp new file mode 100644 index 000000000..f292996df --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/src/exported_symbol_set_test.cpp @@ -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 + + +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 diff --git a/vndk/tools/header-checker/header-abi-util/src/so_file_parser.cpp b/vndk/tools/header-checker/header-abi-util/src/so_file_parser.cpp index fa4d975e7..001f9b912 100644 --- a/vndk/tools/header-checker/header-abi-util/src/so_file_parser.cpp +++ b/vndk/tools/header-checker/header-abi-util/src/so_file_parser.cpp @@ -21,8 +21,10 @@ #include #include + namespace abi_util { + template static inline T UnWrap(llvm::Expected value_or_error) { if (!value_or_error) { @@ -34,6 +36,7 @@ static inline T UnWrap(llvm::Expected 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 + +template class ELFSoFileParser : public SoFileParser { private: LLVM_ELF_IMPORT_TYPES_ELFT(T) @@ -57,12 +61,8 @@ class ELFSoFileParser : public SoFileParser { ~ELFSoFileParser() override {} - const std::map &GetFunctions() const override { - return functions_; - } - - const std::map &GetGlobVars() const override { - return globvars_; + std::unique_ptr Parse() override { + return std::move(exported_symbols_); } private: @@ -77,38 +77,44 @@ class ELFSoFileParser : public SoFileParser { private: const llvm::object::ELFObjectFile *obj_; - std::map functions_; - std::map globvars_; + std::unique_ptr exported_symbols_; }; -template + +template ELFSoFileParser::ELFSoFileParser(const llvm::object::ELFObjectFile *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 + +template static std::unique_ptr CreateELFSoFileParser( const llvm::object::ELFObjectFile *elfo) { return llvm::make_unique>(elfo); } + std::unique_ptr SoFileParser::Create( const std::string &so_file_path) { auto binary = llvm::object::createBinary(so_file_path); @@ -149,4 +155,5 @@ std::unique_ptr SoFileParser::Create( return nullptr; } + } // namespace abi_util diff --git a/vndk/tools/header-checker/header-abi-util/src/string_utils.cpp b/vndk/tools/header-checker/header-abi-util/src/string_utils.cpp new file mode 100644 index 000000000..b6a03d04e --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/src/string_utils.cpp @@ -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 +#include +#include + + +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 Split(std::string_view s, + std::string_view delim_chars) { + std::vector 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 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(res); +} + + +bool IsGlobPattern(std::string_view s) { + return s.find_first_of("*?[") != std::string::npos; +} + + +} // namespace abi_util diff --git a/vndk/tools/header-checker/header-abi-util/src/string_utils_test.cpp b/vndk/tools/header-checker/header-abi-util/src/string_utils_test.cpp new file mode 100644 index 000000000..7cc2bff31 --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/src/string_utils_test.cpp @@ -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 + + +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 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 diff --git a/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp b/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp index 794b47799..d9a717c0a 100644 --- a/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp +++ b/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp @@ -14,202 +14,283 @@ #include "version_script_parser.h" -#include -#include -#include +#include "exported_symbol_set.h" +#include "string_utils.h" -#include #include #include #include #include #include -#include #include + namespace abi_util { -#define FUTURE_API 10000 -std::unordered_set 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 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 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 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 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 & -VersionScriptParser::GetFunctions() { - return functions_; -} - -const std::map &VersionScriptParser::GetGlobVars() { - return globvars_; -} - -const std::set &VersionScriptParser::GetFunctionRegexs() { - return function_regexs_; -} - -const std::set &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 diff --git a/vndk/tools/header-checker/header-abi-util/src/version_script_parser_test.cpp b/vndk/tools/header-checker/header-abi-util/src/version_script_parser_test.cpp new file mode 100644 index 000000000..c84f19db2 --- /dev/null +++ b/vndk/tools/header-checker/header-abi-util/src/version_script_parser_test.cpp @@ -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 + +#include +#include +#include + + +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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 diff --git a/vndk/tools/header-checker/tests/integration/version_script_example/Android.bp b/vndk/tools/header-checker/tests/integration/version_script_example/Android.bp new file mode 100644 index 000000000..7228d3b96 --- /dev/null +++ b/vndk/tools/header-checker/tests/integration/version_script_example/Android.bp @@ -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, +} diff --git a/vndk/tools/header-checker/tests/integration/version_script_example/example.cpp b/vndk/tools/header-checker/tests/integration/version_script_example/example.cpp new file mode 100644 index 000000000..5ada1f520 --- /dev/null +++ b/vndk/tools/header-checker/tests/integration/version_script_example/example.cpp @@ -0,0 +1,6 @@ +#include "example.h" + +void test1() {} +void test2() {} +void Test3::test() {} +void Test4::test() {} diff --git a/vndk/tools/header-checker/tests/integration/version_script_example/example.h b/vndk/tools/header-checker/tests/integration/version_script_example/example.h new file mode 100644 index 000000000..dd6079ec3 --- /dev/null +++ b/vndk/tools/header-checker/tests/integration/version_script_example/example.h @@ -0,0 +1,12 @@ +extern "C" void test1(); +extern "C" void test2(); + +class Test3 { + public: + void test(); +}; + +class Test4 { + public: + void test(); +}; diff --git a/vndk/tools/header-checker/tests/integration/version_script_example/example.map.txt b/vndk/tools/header-checker/tests/integration/version_script_example/example.map.txt new file mode 100644 index 000000000..24dd26d20 --- /dev/null +++ b/vndk/tools/header-checker/tests/integration/version_script_example/example.map.txt @@ -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; diff --git a/vndk/tools/header-checker/tests/integration/version_script_example/prebuilts/libversion_script_example.so b/vndk/tools/header-checker/tests/integration/version_script_example/prebuilts/libversion_script_example.so new file mode 100755 index 000000000..9a5cecde9 Binary files /dev/null and b/vndk/tools/header-checker/tests/integration/version_script_example/prebuilts/libversion_script_example.so differ diff --git a/vndk/tools/header-checker/tests/module.py b/vndk/tools/header-checker/tests/module.py index a2236bf3a..3c281777d 100755 --- a/vndk/tools/header-checker/tests/module.py +++ b/vndk/tools/header-checker/tests/module.py @@ -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} diff --git a/vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example.so.lsdump b/vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example.so.lsdump new file mode 100644 index 000000000..3f8b02d7d --- /dev/null +++ b/vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example.so.lsdump @@ -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" : [] +} diff --git a/vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example_no_mytag.so.lsdump b/vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example_no_mytag.so.lsdump new file mode 100644 index 000000000..f7c4559a8 --- /dev/null +++ b/vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example_no_mytag.so.lsdump @@ -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" : [] +} diff --git a/vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example_no_private.so.lsdump b/vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example_no_private.so.lsdump new file mode 100644 index 000000000..fcd69d8dd --- /dev/null +++ b/vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example_no_private.so.lsdump @@ -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" : [] +} diff --git a/vndk/tools/header-checker/tests/test.py b/vndk/tools/header-checker/tests/test.py index cc86890ef..1ed8852dc 100755 --- a/vndk/tools/header-checker/tests/test.py +++ b/vndk/tools/header-checker/tests/test.py @@ -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()