summaryrefslogtreecommitdiffstats
path: root/vndk
diff options
context:
space:
mode:
authorLogan Chien <loganchien@google.com>2019-01-17 00:23:51 +0800
committerLogan Chien <loganchien@google.com>2019-01-30 11:27:33 +0800
commiteb308b6bb55c7e06aacb1c39f8334e06242ac433 (patch)
treecc14ac21f6023a7ab7fce87fc0952874c5a51dc1 /vndk
parent0278c48f47d05a3b754db423ca44f0fd4272c73e (diff)
downloadandroid_development-eb308b6bb55c7e06aacb1c39f8334e06242ac433.tar.gz
android_development-eb308b6bb55c7e06aacb1c39f8334e06242ac433.tar.bz2
android_development-eb308b6bb55c7e06aacb1c39f8334e06242ac433.zip
header-abi-linker: Support solib and version script
This commit allows user to specify both `-so` (for a shared object file) and `-v` (for a version script). If both of them are specified, the generated output will only include the symbols that are marked as exported in both cases. This commit also rewritten the version script parser so that it can work with real version script cases. This includes how the parser skips a comment line. Furthermore, the version script parser accepts a list of excluded symbol versions and symbol tags that should not be included in the generated output. To customize the behavior, pass `--exclude-symbol-version` and/or `--exclude-symbol-tag` to `header-abi-linker`. Bug: 122845490 Test: ./tests/test.py Test: atest # under development/vndk/tools/header-checker Change-Id: Ibf048bba6ccd5ac44f265d1fe767c2ded3d13a8d
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()