summaryrefslogtreecommitdiffstats
path: root/vndk
diff options
context:
space:
mode:
authorLogan Chien <loganchien@google.com>2019-01-29 22:28:36 -0800
committerandroid-build-merger <android-build-merger@google.com>2019-01-29 22:28:36 -0800
commit07f021dc6af94bab07d4fe7365e2f776ff8809a0 (patch)
tree18c8177f013952645185f72d9e1f813865cd0388 /vndk
parent08b20b87cf634cde8d5d129060f941fe56bc5bc4 (diff)
parentc5aacc545d431b681af9ab134ed05fec311096bd (diff)
downloadandroid_development-07f021dc6af94bab07d4fe7365e2f776ff8809a0.tar.gz
android_development-07f021dc6af94bab07d4fe7365e2f776ff8809a0.tar.bz2
android_development-07f021dc6af94bab07d4fe7365e2f776ff8809a0.zip
Merge "header-abi-linker: Support solib and version script" am: 8b3da0e8b8 am: 1775699432
am: c5aacc545d Change-Id: I1f5ab0089e19646deab6f5b7021445b51e0633bc
Diffstat (limited to 'vndk')
-rw-r--r--vndk/tools/header-checker/Android.bp36
-rw-r--r--vndk/tools/header-checker/TEST_MAPPING8
-rw-r--r--vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp187
-rw-r--r--vndk/tools/header-checker/header-abi-util/include/api_level.h37
-rw-r--r--vndk/tools/header-checker/header-abi-util/include/exported_symbol_set.h100
-rw-r--r--vndk/tools/header-checker/header-abi-util/include/so_file_parser.h9
-rw-r--r--vndk/tools/header-checker/header-abi-util/include/stl_utils.h33
-rw-r--r--vndk/tools/header-checker/header-abi-util/include/string_utils.h43
-rw-r--r--vndk/tools/header-checker/header-abi-util/include/version_script_parser.h111
-rw-r--r--vndk/tools/header-checker/header-abi-util/src/api_level.cpp34
-rw-r--r--vndk/tools/header-checker/header-abi-util/src/api_level_test.cpp36
-rw-r--r--vndk/tools/header-checker/header-abi-util/src/exported_symbol_set.cpp99
-rw-r--r--vndk/tools/header-checker/header-abi-util/src/exported_symbol_set_test.cpp132
-rw-r--r--vndk/tools/header-checker/header-abi-util/src/so_file_parser.cpp37
-rw-r--r--vndk/tools/header-checker/header-abi-util/src/string_utils.cpp93
-rw-r--r--vndk/tools/header-checker/header-abi-util/src/string_utils_test.cpp107
-rw-r--r--vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp353
-rw-r--r--vndk/tools/header-checker/header-abi-util/src/version_script_parser_test.cpp430
-rw-r--r--vndk/tools/header-checker/tests/integration/version_script_example/Android.bp23
-rw-r--r--vndk/tools/header-checker/tests/integration/version_script_example/example.cpp6
-rw-r--r--vndk/tools/header-checker/tests/integration/version_script_example/example.h12
-rw-r--r--vndk/tools/header-checker/tests/integration/version_script_example/example.map.txt17
-rwxr-xr-xvndk/tools/header-checker/tests/integration/version_script_example/prebuilts/libversion_script_example.sobin0 -> 19008 bytes
-rwxr-xr-xvndk/tools/header-checker/tests/module.py56
-rw-r--r--vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example.so.lsdump121
-rw-r--r--vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example_no_mytag.so.lsdump96
-rw-r--r--vndk/tools/header-checker/tests/reference_dumps/arm64/libversion_script_example_no_private.so.lsdump96
-rwxr-xr-xvndk/tools/header-checker/tests/test.py19
28 files changed, 2069 insertions, 262 deletions
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<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;
}
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 <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_
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 <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_
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 <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_
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 <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_
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 <functional>
#include <map>
#include <set>
#include <string>
+
namespace abi_util {
+
class VersionScriptParser {
+ private:
+ enum class LineScope {
+ GLOBAL,
+ LOCAL,
+ };
+
+
+ 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_;
+
+
+ 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:
- enum LineScope {
- global,
- local,
+ class ErrorHandler {
+ public:
+ virtual ~ErrorHandler();
+
+ virtual void OnError(int line_no, const std::string &error_msg) = 0;
};
- VersionScriptParser(const std::string &version_script,
- const std::string &arch,
- const std::string &api);
- bool Parse();
- const std::map<std::string, ElfFunctionIR> &GetFunctions();
+ public:
+ VersionScriptParser();
+
+ void SetArch(const std::string &arch);
+
+ void SetApiLevel(ApiLevel api_level) {
+ api_level_ = api_level;
+ }
- const std::map<std::string, ElfObjectIR> &GetGlobVars();
+ void AddExcludedSymbolVersion(const std::string &version) {
+ excluded_symbol_versions_.insert(version);
+ }
- const std::set<std::string> &GetFunctionRegexs();
+ 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);
+ private:
+ void ReportError(const std::string &error_msg) {
+ if (error_handler_) {
+ error_handler_->OnError(line_no_, error_msg);
+ }
+ }
- 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_;
+ 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_
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 <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
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 <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
+
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 <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
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 <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
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 <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
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 <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
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 <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
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 <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);
-}
-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;
- }
- }
- return (has_arch_tags && StringContains(line, arch)) || !has_arch_tags;
+inline std::string GetIntroducedArchTag(const std::string &arch) {
+ return "introduced-" + arch + "=";
}
-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;
- }
- return std::stoi(api);
+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);
}
-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)) {
- return true;
- }
- if (line == "future") {
- return api == FUTURE_API;
+
+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;
}
- 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));
+
+ 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;
+ }
}
- // 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);
+
+ return result;
}
-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)) {
- return true;
+
+bool VersionScriptParser::IsSymbolExported(
+ const VersionScriptParser::ParsedTags &tags) {
+ if (tags.has_excluded_tags_) {
+ return false;
}
- 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));
+ if (tags.has_arch_tags_ && !tags.has_current_arch_tag_) {
+ return false;
}
-}
-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));
+ 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::ParseSymbolLine(const std::string &line) {
- // The symbol lies before the ';' and the tags are after ';'
+
+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) {
- llvm::errs() << "Couldn't find end of symbol" << line <<"\n";
+ ReportError("No semicolon at the end of the symbol line: " + line);
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);
+
+ std::string symbol(Trim(line.substr(0, pos)));
+
+ ParsedTags tags = ParseSymbolTags(line);
+ if (!IsSymbolExported(tags)) {
+ return true;
+ }
+
+ if (is_in_extern_cpp) {
+ if (IsGlobPattern(symbol)) {
+ exported_symbols_->AddDemangledCppGlobPattern(symbol);
} else {
- AddToFunctions(symbol);
+ exported_symbols_->AddDemangledCppSymbol(symbol);
}
+ return true;
}
- return true;
-}
-typedef VersionScriptParser::LineScope LineScope;
+ if (IsGlobPattern(symbol)) {
+ exported_symbols_->AddGlobPattern(symbol);
+ return true;
+ }
-LineScope VersionScriptParser::GetLineScope(std::string &line,
- LineScope scope) {
- if (StringContains(line, "local:")) {
- scope = LineScope::local;
+ if (tags.has_var_tag_) {
+ exported_symbols_->AddVar(symbol, ElfSymbolIR::ElfSymbolBinding::Global);
+ } else {
+ exported_symbols_->AddFunction(symbol,
+ ElfSymbolIR::ElfSymbolBinding::Global);
}
- return scope;
+ return true;
}
-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) {
+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.c_str()[0] == '#') {
+
+ 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;
}
- scope = GetLineScope(line, scope);
- if (scope != LineScope::global || StringContains(line, "global:")) {
+
+ // Check symbol visibility label
+ if (StartsWith(line, "local:")) {
+ scope = LineScope::LOCAL;
continue;
}
- ParseSymbolLine(line);
+ 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;
+ }
+ }
}
- return true;
-}
-const std::map<std::string, ElfFunctionIR> &
-VersionScriptParser::GetFunctions() {
- return functions_;
+ ReportError("No matching closing parenthesis");
+ return false;
}
-const std::map<std::string, ElfObjectIR> &VersionScriptParser::GetGlobVars() {
- return globvars_;
-}
-const std::set<std::string> &VersionScriptParser::GetFunctionRegexs() {
- return function_regexs_;
-}
+std::unique_ptr<ExportedSymbolSet> VersionScriptParser::Parse(
+ std::istream &stream) {
+ // Initialize the parser context
+ stream_ = &stream;
+ line_no_ = 0;
+ exported_symbols_.reset(new ExportedSymbolSet());
-const std::set<std::string> &VersionScriptParser::GetGlobVarRegexs() {
- return globvar_regexs_;
-}
+ // Parse
+ while (true) {
+ std::string line;
+ if (!ReadLine(line)) {
+ break;
+ }
-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::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;
+ }
}
- std::string line;
- while (std::getline(symbol_ifstream, line)) {
- // Skip comment lines.
- if (line.c_str()[0] == '#') {
+ 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;
}
- if (StringContains(line, "{")) {
- if ((StringContains(line, "PRIVATE"))) {
- continue;
- }
- ParseInnerBlock(symbol_ifstream);
- }
+ return true;
}
-
- return true;
+ return false;
}
+
+VersionScriptParser::ErrorHandler::~ErrorHandler() {}
+
+
} // 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 <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
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
--- /dev/null
+++ b/vndk/tools/header-checker/tests/integration/version_script_example/prebuilts/libversion_script_example.so
Binary files 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()