Merge "header-abi-linker: Support solib and version script"

This commit is contained in:
Logan Chien
2019-01-30 06:04:12 +00:00
committed by Gerrit Code Review
28 changed files with 2098 additions and 291 deletions

View File

@@ -32,7 +32,7 @@ cc_defaults {
cppflags: [ cppflags: [
"-fno-exceptions", "-fno-exceptions",
"-fno-rtti", "-fno-rtti",
"-std=c++14", "-std=c++17",
], ],
target: { target: {
@@ -146,11 +146,14 @@ cc_library_host_static {
srcs: [ srcs: [
"header-abi-util/src/abi_diff_helpers.cpp", "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/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.cpp",
"header-abi-util/src/ir_representation_json.cpp", "header-abi-util/src/ir_representation_json.cpp",
"header-abi-util/src/ir_representation_protobuf.cpp", "header-abi-util/src/ir_representation_protobuf.cpp",
"header-abi-util/src/so_file_parser.cpp", "header-abi-util/src/so_file_parser.cpp",
"header-abi-util/src/string_utils.cpp",
"header-abi-util/src/version_script_parser.cpp", "header-abi-util/src/version_script_parser.cpp",
], ],
@@ -173,3 +176,34 @@ cc_library_host_static {
export_include_dirs: ["header-abi-util/include"], 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"],
}

View File

@@ -0,0 +1,8 @@
{
"presubmit" : [
{
"name" : "libheader-abi-util_test",
"host" : true
}
]
}

View File

@@ -52,8 +52,17 @@ static llvm::cl::opt<std::string> version_script(
"v", llvm::cl::desc("<version_script>"), llvm::cl::Optional, "v", llvm::cl::desc("<version_script>"), llvm::cl::Optional,
llvm::cl::cat(header_linker_category)); 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( static llvm::cl::opt<std::string> api(
"api", llvm::cl::desc("<api>"), llvm::cl::Optional, "api", llvm::cl::desc("<api>"), llvm::cl::Optional,
llvm::cl::init("current"),
llvm::cl::cat(header_linker_category)); llvm::cl::cat(header_linker_category));
static llvm::cl::opt<std::string> arch( static llvm::cl::opt<std::string> arch(
@@ -93,10 +102,14 @@ class HeaderAbiLinker {
const std::string &so_file, const std::string &so_file,
const std::string &linked_dump, const std::string &linked_dump,
const std::string &arch, 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), : dump_files_(dump_files), exported_header_dirs_(exported_header_dirs),
version_script_(version_script), so_file_(so_file), 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(); bool LinkAndDump();
@@ -125,6 +138,18 @@ class HeaderAbiLinker {
bool LinkExportedSymbols(abi_util::IRDumper *ir_dumper); 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: private:
const std::vector<std::string> &dump_files_; const std::vector<std::string> &dump_files_;
const std::vector<std::string> &exported_header_dirs_; const std::vector<std::string> &exported_header_dirs_;
@@ -133,19 +158,14 @@ class HeaderAbiLinker {
const std::string &out_dump_name_; const std::string &out_dump_name_;
const std::string &arch_; 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_;
std::set<std::string> exported_headers_; std::set<std::string> exported_headers_;
std::map<std::string, abi_util::ElfFunctionIR> function_decl_map_; // Exported symbols
std::map<std::string, abi_util::ElfObjectIR> globvar_decl_map_; std::unique_ptr<abi_util::ExportedSymbolSet> shared_object_symbols_;
std::unique_ptr<abi_util::ExportedSymbolSet> version_script_symbols_;
// 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_;
}; };
static void DeDuplicateAbiElementsThread( static void DeDuplicateAbiElementsThread(
@@ -228,7 +248,9 @@ bool HeaderAbiLinker::LinkAndDump() {
abi_util::IRDumper::CreateIRDumper(output_format, out_dump_name_); abi_util::IRDumper::CreateIRDumper(output_format, out_dump_name_);
assert(ir_dumper != nullptr); assert(ir_dumper != nullptr);
LinkExportedSymbols(ir_dumper.get()); if (!LinkExportedSymbols(ir_dumper.get())) {
return false;
}
if (!LinkTypes(greader.get(), ir_dumper.get()) || if (!LinkTypes(greader.get(), ir_dumper.get()) ||
!LinkFunctions(greader.get(), ir_dumper.get()) || !LinkFunctions(greader.get(), ir_dumper.get()) ||
@@ -245,39 +267,6 @@ bool HeaderAbiLinker::LinkAndDump() {
return true; 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> template <typename T>
bool HeaderAbiLinker::LinkDecl( bool HeaderAbiLinker::LinkDecl(
abi_util::IRDumper *dst, const abi_util::AbiElementMap<T> &src, 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); 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( bool HeaderAbiLinker::LinkFunctions(
const abi_util::TextFormatToIRReader *reader, const abi_util::TextFormatToIRReader *reader,
abi_util::IRDumper *ir_dumper) { abi_util::IRDumper *ir_dumper) {
assert(reader != nullptr); assert(reader != nullptr);
auto symbol_filter = [this](const std::string &linker_set_key) { auto symbol_filter = [this](const std::string &linker_set_key) {
return function_decl_map_.find(linker_set_key) != return IsSymbolExported(linker_set_key);
function_decl_map_.end() ||
QueryRegexMatches(&functions_regex_matched_set_,
&functions_vs_regex_, linker_set_key);
}; };
return LinkDecl(ir_dumper, reader->GetFunctions(), symbol_filter); return LinkDecl(ir_dumper, reader->GetFunctions(), symbol_filter);
} }
@@ -337,18 +333,18 @@ bool HeaderAbiLinker::LinkGlobalVars(
abi_util::IRDumper *ir_dumper) { abi_util::IRDumper *ir_dumper) {
assert(reader != nullptr); assert(reader != nullptr);
auto symbol_filter = [this](const std::string &linker_set_key) { auto symbol_filter = [this](const std::string &linker_set_key) {
return globvar_decl_map_.find(linker_set_key) != return IsSymbolExported(linker_set_key);
globvar_decl_map_.end() ||
QueryRegexMatches(&globvars_regex_matched_set_, &globvars_vs_regex_,
linker_set_key);
}; };
return LinkDecl(ir_dumper, reader->GetGlobalVariables(), symbol_filter); return LinkDecl(ir_dumper, reader->GetGlobalVariables(), symbol_filter);
} }
template <typename T> template <typename SymbolMap>
static bool LinkExportedSymbols(abi_util::IRDumper *dst, bool HeaderAbiLinker::LinkExportedSymbols(abi_util::IRDumper *dst,
const std::map<std::string, T> &symbols) { const SymbolMap &symbols) {
for (auto &&symbol : symbols) { for (auto &&symbol : symbols) {
if (!IsSymbolExported(symbol.first)) {
continue;
}
if (!dst->AddElfSymbolMessageIR(&(symbol.second))) { if (!dst->AddElfSymbolMessageIR(&(symbol.second))) {
return false; return false;
} }
@@ -356,20 +352,37 @@ static bool LinkExportedSymbols(abi_util::IRDumper *dst,
return true; 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) { bool HeaderAbiLinker::LinkExportedSymbols(abi_util::IRDumper *ir_dumper) {
return ::LinkExportedSymbols(ir_dumper, function_decl_map_) && if (shared_object_symbols_) {
::LinkExportedSymbols(ir_dumper, globvar_decl_map_); return LinkExportedSymbols(ir_dumper, *shared_object_symbols_);
}
if (version_script_symbols_) {
return LinkExportedSymbols(ir_dumper, *version_script_symbols_);
}
return false;
} }
bool HeaderAbiLinker::ReadExportedSymbols() { 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 (!so_file_.empty()) {
if (!ReadExportedSymbolsFromSharedObjectFile()) { if (!ReadExportedSymbolsFromSharedObjectFile()) {
llvm::errs() << "Failed to parse the shared library (.so file): " llvm::errs() << "Failed to parse the shared library (.so file): "
<< so_file_ << "\n"; << so_file_ << "\n";
return false; return false;
} }
return true;
} }
if (!version_script_.empty()) { if (!version_script_.empty()) {
@@ -378,29 +391,40 @@ bool HeaderAbiLinker::ReadExportedSymbols() {
<< "\n"; << "\n";
return false; return false;
} }
return true;
} }
llvm::errs() << "Either shared lib or version script must be specified.\n"; return true;
return false;
} }
bool HeaderAbiLinker::ReadExportedSymbolsFromVersionScript() { bool HeaderAbiLinker::ReadExportedSymbolsFromVersionScript() {
abi_util::VersionScriptParser version_script_parser( std::optional<abi_util::ApiLevel> api_level = abi_util::ParseApiLevel(api_);
version_script_, arch_, api_); if (!api_level) {
if (!version_script_parser.Parse()) { llvm::errs() << "-api must be either \"current\" or an integer (e.g. 21)\n";
return false; return false;
} }
function_decl_map_ = version_script_parser.GetFunctions(); std::ifstream stream(version_script_, std::ios_base::in);
globvar_decl_map_ = version_script_parser.GetGlobVars(); 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; return true;
} }
@@ -411,8 +435,12 @@ bool HeaderAbiLinker::ReadExportedSymbolsFromSharedObjectFile() {
return false; return false;
} }
function_decl_map_ = so_parser->GetFunctions(); shared_object_symbols_ = so_parser->Parse();
globvar_decl_map_ = so_parser->GetGlobVars(); if (!shared_object_symbols_) {
llvm::errs() << "Failed to parse shared object file\n";
return false;
}
return true; return true;
} }
@@ -444,11 +472,14 @@ int main(int argc, const char **argv) {
} }
HeaderAbiLinker Linker(dump_files, exported_header_dirs, version_script, 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()) { if (!Linker.LinkAndDump()) {
llvm::errs() << "Failed to link and dump elements\n"; llvm::errs() << "Failed to link and dump elements\n";
return -1; return -1;
} }
return 0; return 0;
} }

View File

@@ -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_

View File

@@ -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_

View File

@@ -15,25 +15,28 @@
#ifndef SO_FILE_PARSER_H_ #ifndef SO_FILE_PARSER_H_
#define SO_FILE_PARSER_H_ #define SO_FILE_PARSER_H_
#include "exported_symbol_set.h"
#include "ir_representation.h" #include "ir_representation.h"
#include <memory> #include <memory>
#include <map> #include <map>
#include <string> #include <string>
namespace abi_util { namespace abi_util {
class SoFileParser { class SoFileParser {
public: public:
static std::unique_ptr<SoFileParser> Create(const std::string &so_file_path); static std::unique_ptr<SoFileParser> Create(const std::string &so_file_path);
virtual ~SoFileParser() {} virtual ~SoFileParser() {}
virtual const std::map<std::string, ElfFunctionIR> &GetFunctions() const = 0; virtual std::unique_ptr<ExportedSymbolSet> Parse() = 0;
virtual const std::map<std::string, ElfObjectIR> &GetGlobVars() const = 0;
}; };
} // namespace abi_util } // namespace abi_util
#endif // SO_FILE_PARSER_H_ #endif // SO_FILE_PARSER_H_

View File

@@ -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_

View File

@@ -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_

View File

@@ -15,64 +15,117 @@
#ifndef VERSION_SCRIPT_PARSER_H_ #ifndef VERSION_SCRIPT_PARSER_H_
#define VERSION_SCRIPT_PARSER_H_ #define VERSION_SCRIPT_PARSER_H_
#include "api_level.h"
#include "exported_symbol_set.h"
#include "ir_representation.h" #include "ir_representation.h"
#include <functional>
#include <map> #include <map>
#include <set> #include <set>
#include <string> #include <string>
namespace abi_util { namespace abi_util {
class VersionScriptParser { class VersionScriptParser {
public: private:
enum LineScope { enum class LineScope {
global, GLOBAL,
local, LOCAL,
}; };
VersionScriptParser(const std::string &version_script,
const std::string &arch,
const std::string &api);
bool Parse();
const std::map<std::string, ElfFunctionIR> &GetFunctions(); struct ParsedTags {
public:
unsigned has_arch_tags_ : 1;
unsigned has_current_arch_tag_ : 1;
unsigned has_introduced_tags_ : 1;
unsigned has_excluded_tags_ : 1;
unsigned has_future_tag_ : 1;
unsigned has_var_tag_ : 1;
ApiLevel introduced_;
const std::map<std::string, ElfObjectIR> &GetGlobVars();
const std::set<std::string> &GetFunctionRegexs(); public:
ParsedTags()
: has_arch_tags_(0), has_current_arch_tag_(0), has_introduced_tags_(0),
has_excluded_tags_(0), has_future_tag_(0), has_var_tag_(0),
introduced_(-1) {}
};
public:
class ErrorHandler {
public:
virtual ~ErrorHandler();
virtual void OnError(int line_no, const std::string &error_msg) = 0;
};
public:
VersionScriptParser();
void SetArch(const std::string &arch);
void SetApiLevel(ApiLevel api_level) {
api_level_ = api_level;
}
void AddExcludedSymbolVersion(const std::string &version) {
excluded_symbol_versions_.insert(version);
}
void AddExcludedSymbolTag(const std::string &tag) {
excluded_symbol_tags_.insert(tag);
}
void SetErrorHandler(std::unique_ptr<ErrorHandler> error_handler) {
error_handler_ = std::move(error_handler);
}
std::unique_ptr<ExportedSymbolSet> Parse(std::istream &version_script_stream);
const std::set<std::string> &GetGlobVarRegexs();
private: 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, ParsedTags ParseSymbolTags(const std::string &line);
const std::string &arch, int api);
bool SymbolExported(const std::string &line, const std::string &arch, bool IsSymbolExported(const ParsedTags &tags);
int api);
int ApiStrToInt(const std::string &api);
void AddToVars(std::string &symbol);
void AddToFunctions(std::string &symbol);
private: private:
const std::string &version_script_; void ReportError(const std::string &error_msg) {
const std::string &arch_; if (error_handler_) {
std::map<std::string, ElfFunctionIR> functions_; error_handler_->OnError(line_no_, error_msg);
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_; private:
std::unique_ptr<ErrorHandler> error_handler_;
std::string arch_;
std::string introduced_arch_tag_;
ApiLevel api_level_;
std::set<std::string, std::less<>> excluded_symbol_versions_;
std::set<std::string, std::less<>> excluded_symbol_tags_;
std::istream *stream_;
int line_no_;
std::unique_ptr<ExportedSymbolSet> exported_symbols_;
}; };
} // namespace abi_util } // namespace abi_util
#endif // VERSION_SCRIPT_PARSER_H_ #endif // VERSION_SCRIPT_PARSER_H_

View File

@@ -0,0 +1,34 @@
// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "api_level.h"
#include "string_utils.h"
#include <cassert>
#include <string>
namespace abi_util {
std::optional<ApiLevel> ParseApiLevel(const std::string &api_level_str) {
if (api_level_str == "current") {
return FUTURE_API_LEVEL;
}
return ParseInt(api_level_str);
}
} // namespace abi_util

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -21,8 +21,10 @@
#include <llvm/Object/ELFTypes.h> #include <llvm/Object/ELFTypes.h>
#include <llvm/Object/SymbolSize.h> #include <llvm/Object/SymbolSize.h>
namespace abi_util { namespace abi_util {
template <typename T> template <typename T>
static inline T UnWrap(llvm::Expected<T> value_or_error) { static inline T UnWrap(llvm::Expected<T> value_or_error) {
if (!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()); return std::move(value_or_error.get());
} }
static abi_util::ElfSymbolIR::ElfSymbolBinding static abi_util::ElfSymbolIR::ElfSymbolBinding
LLVMToIRSymbolBinding(unsigned char binding) { LLVMToIRSymbolBinding(unsigned char binding) {
switch (binding) { switch (binding) {
@@ -45,7 +48,8 @@ LLVMToIRSymbolBinding(unsigned char binding) {
assert(0); assert(0);
} }
template<typename T>
template <typename T>
class ELFSoFileParser : public SoFileParser { class ELFSoFileParser : public SoFileParser {
private: private:
LLVM_ELF_IMPORT_TYPES_ELFT(T) LLVM_ELF_IMPORT_TYPES_ELFT(T)
@@ -57,12 +61,8 @@ class ELFSoFileParser : public SoFileParser {
~ELFSoFileParser() override {} ~ELFSoFileParser() override {}
const std::map<std::string, ElfFunctionIR> &GetFunctions() const override { std::unique_ptr<ExportedSymbolSet> Parse() override {
return functions_; return std::move(exported_symbols_);
}
const std::map<std::string, ElfObjectIR> &GetGlobVars() const override {
return globvars_;
} }
private: private:
@@ -77,38 +77,44 @@ class ELFSoFileParser : public SoFileParser {
private: private:
const llvm::object::ELFObjectFile<T> *obj_; const llvm::object::ELFObjectFile<T> *obj_;
std::map<std::string, abi_util::ElfFunctionIR> functions_; std::unique_ptr<ExportedSymbolSet> exported_symbols_;
std::map<std::string, abi_util::ElfObjectIR> globvars_;
}; };
template<typename T>
template <typename T>
ELFSoFileParser<T>::ELFSoFileParser(const llvm::object::ELFObjectFile<T> *obj) { ELFSoFileParser<T>::ELFSoFileParser(const llvm::object::ELFObjectFile<T> *obj) {
assert(obj != nullptr); assert(obj != nullptr);
exported_symbols_.reset(new ExportedSymbolSet());
for (auto symbol_it : obj->getDynamicSymbolIterators()) { for (auto symbol_it : obj->getDynamicSymbolIterators()) {
const Elf_Sym *elf_sym = obj->getSymbol(symbol_it.getRawDataRefImpl()); const Elf_Sym *elf_sym = obj->getSymbol(symbol_it.getRawDataRefImpl());
assert (elf_sym != nullptr); assert (elf_sym != nullptr);
if (!IsSymbolExported(elf_sym) || elf_sym->isUndefined()) { if (!IsSymbolExported(elf_sym) || elf_sym->isUndefined()) {
continue; continue;
} }
abi_util::ElfSymbolIR::ElfSymbolBinding symbol_binding = abi_util::ElfSymbolIR::ElfSymbolBinding symbol_binding =
LLVMToIRSymbolBinding(elf_sym->getBinding()); LLVMToIRSymbolBinding(elf_sym->getBinding());
llvm::object::SymbolRef::Type type = UnWrap(symbol_it.getType());
std::string symbol_name = UnWrap(symbol_it.getName()); 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) { if (type == llvm::object::SymbolRef::Type::ST_Function) {
functions_.emplace(symbol_name, exported_symbols_->AddFunction(symbol_name, symbol_binding);
ElfFunctionIR(symbol_name, symbol_binding));
} else if (type == llvm::object::SymbolRef::Type::ST_Data) { } 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( static std::unique_ptr<SoFileParser> CreateELFSoFileParser(
const llvm::object::ELFObjectFile<T> *elfo) { const llvm::object::ELFObjectFile<T> *elfo) {
return llvm::make_unique<ELFSoFileParser<T>>(elfo); return llvm::make_unique<ELFSoFileParser<T>>(elfo);
} }
std::unique_ptr<SoFileParser> SoFileParser::Create( std::unique_ptr<SoFileParser> SoFileParser::Create(
const std::string &so_file_path) { const std::string &so_file_path) {
auto binary = llvm::object::createBinary(so_file_path); auto binary = llvm::object::createBinary(so_file_path);
@@ -149,4 +155,5 @@ std::unique_ptr<SoFileParser> SoFileParser::Create(
return nullptr; return nullptr;
} }
} // namespace abi_util } // namespace abi_util

View File

@@ -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

View File

@@ -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

View File

@@ -14,202 +14,283 @@
#include "version_script_parser.h" #include "version_script_parser.h"
#include <llvm/Support/raw_ostream.h> #include "exported_symbol_set.h"
#include <llvm/Support/FileSystem.h> #include "string_utils.h"
#include <llvm/Support/Path.h>
#include <fstream>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <regex> #include <regex>
#include <set> #include <set>
#include <string> #include <string>
#include <unordered_set>
#include <vector> #include <vector>
namespace abi_util { namespace abi_util {
#define FUTURE_API 10000
std::unordered_set<std::string> AllArches({ static constexpr char DEFAULT_ARCH[] = "arm64";
"arm", "arm64", "x86", "x86_64", "mips", "mips64"});
static bool StringContains(const std::string &line,
const std::string &substring) { inline std::string GetIntroducedArchTag(const std::string &arch) {
return (line.find(substring) != std::string::npos); return "introduced-" + arch + "=";
} }
static bool LineSatisfiesArch(const std::string &line,
const std::string arch) { VersionScriptParser::VersionScriptParser()
bool has_arch_tags = false; : arch_(DEFAULT_ARCH), introduced_arch_tag_(GetIntroducedArchTag(arch_)),
for (auto &&possible_arch : AllArches) { api_level_(FUTURE_API_LEVEL), stream_(nullptr), line_no_(0) {}
if (StringContains(line, possible_arch)) {
has_arch_tags = true;
break; void VersionScriptParser::SetArch(const std::string &arch) {
arch_ = arch;
introduced_arch_tag_ = GetIntroducedArchTag(arch);
}
VersionScriptParser::ParsedTags VersionScriptParser::ParseSymbolTags(
const std::string &line) {
static const char *const POSSIBLE_ARCHES[] = {
"arm", "arm64", "x86", "x86_64", "mips", "mips64"};
ParsedTags result;
std::string_view line_view(line);
std::string::size_type comment_pos = line_view.find('#');
if (comment_pos == std::string::npos) {
return result;
}
std::string_view comment_line = line_view.substr(comment_pos + 1);
std::vector<std::string_view> tags = Split(comment_line, " \t");
bool has_introduced_arch_tags = false;
for (auto &&tag : tags) {
// Check excluded tags.
if (excluded_symbol_tags_.find(tag) != excluded_symbol_tags_.end()) {
result.has_excluded_tags_ = true;
}
// Check the var tag.
if (tag == "var") {
result.has_var_tag_ = true;
continue;
}
// Check arch tags.
if (tag == arch_) {
result.has_arch_tags_ = true;
result.has_current_arch_tag_ = true;
continue;
}
for (auto &&possible_arch : POSSIBLE_ARCHES) {
if (tag == possible_arch) {
result.has_arch_tags_ = true;
break;
}
}
// Check introduced tags.
if (StartsWith(tag, "introduced=")) {
std::optional<ApiLevel> intro = ParseApiLevel(
std::string(tag.substr(sizeof("introduced=") - 1)));
if (!intro) {
ReportError("Bad introduced tag: " + std::string(tag));
} else {
if (!has_introduced_arch_tags) {
result.has_introduced_tags_ = true;
result.introduced_ = intro.value();
}
}
continue;
}
if (StartsWith(tag, introduced_arch_tag_)) {
std::optional<ApiLevel> intro = ParseApiLevel(
std::string(tag.substr(introduced_arch_tag_.size())));
if (!intro) {
ReportError("Bad introduced tag " + std::string(tag));
} else {
has_introduced_arch_tags = true;
result.has_introduced_tags_ = true;
result.introduced_ = intro.value();
}
continue;
}
// Check future tags.
if (tag == "future") {
result.has_future_tag_ = true;
continue;
} }
} }
return (has_arch_tags && StringContains(line, arch)) || !has_arch_tags;
return result;
} }
VersionScriptParser::VersionScriptParser(const std::string &version_script,
const std::string &arch,
const std::string &api)
: version_script_(version_script), arch_(arch), api_(ApiStrToInt(api)) {}
int VersionScriptParser::ApiStrToInt(const std::string &api) { bool VersionScriptParser::IsSymbolExported(
// Follow what build/soong/cc/gen_stub_libs.py does. const VersionScriptParser::ParsedTags &tags) {
if (api == "current") { if (tags.has_excluded_tags_) {
return FUTURE_API; return false;
} }
return std::stoi(api);
if (tags.has_arch_tags_ && !tags.has_current_arch_tag_) {
return false;
}
if (tags.has_future_tag_) {
return api_level_ == FUTURE_API_LEVEL;
}
if (tags.has_introduced_tags_) {
return api_level_ >= tags.introduced_;
}
return true;
} }
bool VersionScriptParser::SymbolInArchAndApiVersion(const std::string &line,
const std::string &arch, bool VersionScriptParser::ParseSymbolLine(const std::string &line,
int api) { bool is_in_extern_cpp) {
// If the tags do not have an "introduced" requirement, the symbol is // The symbol name comes before the ';'.
// exported. std::string::size_type pos = line.find(";");
if (!StringContains(line, "introduced") && LineSatisfiesArch(line, arch)) { if (pos == std::string::npos) {
ReportError("No semicolon at the end of the symbol line: " + line);
return false;
}
std::string symbol(Trim(line.substr(0, pos)));
ParsedTags tags = ParseSymbolTags(line);
if (!IsSymbolExported(tags)) {
return true; return true;
} }
if (line == "future") {
return api == FUTURE_API; if (is_in_extern_cpp) {
if (IsGlobPattern(symbol)) {
exported_symbols_->AddDemangledCppGlobPattern(symbol);
} else {
exported_symbols_->AddDemangledCppSymbol(symbol);
}
return true;
} }
const std::string regex_match_string1 = " *introduced-" + arch + "=([0-9]+)";
const std::string regex_match_string2 = " *introduced=([0-9]+)"; if (IsGlobPattern(symbol)) {
std::smatch matcher1; exported_symbols_->AddGlobPattern(symbol);
std::smatch matcher2; return true;
std::regex match_clause1(regex_match_string1);
std::regex match_clause2(regex_match_string2);
int matched_api = -1;
if (std::regex_search(line, matcher1, match_clause1)) {
matched_api = std::stoi(matcher1.str(1));
} else if ((std::regex_search(line, matcher2, match_clause2)) &&
LineSatisfiesArch(line, arch)) {
matched_api = std::stoi(matcher2.str(1));
} }
// If the arch specific tag / version specific tag was found and the api level
// required was greater than the api level offered. if (tags.has_var_tag_) {
return (matched_api <= 0 || api >= matched_api); exported_symbols_->AddVar(symbol, ElfSymbolIR::ElfSymbolBinding::Global);
} else {
exported_symbols_->AddFunction(symbol,
ElfSymbolIR::ElfSymbolBinding::Global);
}
return true;
} }
bool VersionScriptParser::SymbolExported(const std::string &line,
const std::string &arch, int api) { bool VersionScriptParser::ParseVersionBlock(bool ignore_symbols) {
// Empty line means that the symbol is exported static const std::regex EXTERN_CPP_PATTERN(R"(extern\s*"[Cc]\+\+"\s*\{)");
if (line.empty() || SymbolInArchAndApiVersion(line, arch, api)) {
LineScope scope = LineScope::GLOBAL;
bool is_in_extern_cpp = false;
while (true) {
std::string line;
if (!ReadLine(line)) {
break;
}
if (line.find("}") != std::string::npos) {
if (is_in_extern_cpp) {
is_in_extern_cpp = false;
continue;
}
return true;
}
// Check extern "c++"
if (std::regex_match(line, EXTERN_CPP_PATTERN)) {
is_in_extern_cpp = true;
continue;
}
// Check symbol visibility label
if (StartsWith(line, "local:")) {
scope = LineScope::LOCAL;
continue;
}
if (StartsWith(line, "global:")) {
scope = LineScope::GLOBAL;
continue;
}
if (scope != LineScope::GLOBAL) {
continue;
}
// Parse symbol line
if (!ignore_symbols) {
if (!ParseSymbolLine(line, is_in_extern_cpp)) {
return false;
}
}
}
ReportError("No matching closing parenthesis");
return false;
}
std::unique_ptr<ExportedSymbolSet> VersionScriptParser::Parse(
std::istream &stream) {
// Initialize the parser context
stream_ = &stream;
line_no_ = 0;
exported_symbols_.reset(new ExportedSymbolSet());
// Parse
while (true) {
std::string line;
if (!ReadLine(line)) {
break;
}
std::string::size_type lparen_pos = line.find("{");
if (lparen_pos == std::string::npos) {
ReportError("No version opening parenthesis" + line);
return nullptr;
}
std::string version(Trim(line.substr(0, lparen_pos - 1)));
bool exclude_symbol_version = (excluded_symbol_versions_.find(version) !=
excluded_symbol_versions_.end());
if (!ParseVersionBlock(exclude_symbol_version)) {
return nullptr;
}
}
return std::move(exported_symbols_);
}
bool VersionScriptParser::ReadLine(std::string &line) {
while (std::getline(*stream_, line)) {
++line_no_;
line = std::string(Trim(line));
if (line.empty() || line[0] == '#') {
continue;
}
return true; return true;
} }
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));
}
}
void VersionScriptParser::AddToFunctions(std::string &symbol) { VersionScriptParser::ErrorHandler::~ErrorHandler() {}
if (symbol.find("*") != std::string::npos) {
function_regexs_.insert(symbol);
} else {
functions_.emplace(
symbol, ElfFunctionIR(symbol, ElfSymbolIR::ElfSymbolBinding::Global));
}
}
bool VersionScriptParser::ParseSymbolLine(const std::string &line) {
// The symbol lies before the ';' and the tags are after ';'
std::string::size_type pos = line.find(";");
if (pos == std::string::npos) {
llvm::errs() << "Couldn't find end of symbol" << line <<"\n";
return false;
}
std::string symbol = line.substr(0, pos);
std::string::size_type last_space = symbol.find_last_of(' ');
symbol = symbol.substr(last_space + 1, pos);
std::string tags = line.substr(pos + 1);
if (SymbolExported(tags, arch_, api_)) {
if (StringContains(tags, "var")) {
AddToVars(symbol);
} else {
AddToFunctions(symbol);
}
}
return true;
}
typedef VersionScriptParser::LineScope LineScope;
LineScope VersionScriptParser::GetLineScope(std::string &line,
LineScope scope) {
if (StringContains(line, "local:")) {
scope = LineScope::local;
}
return scope;
}
bool VersionScriptParser::ParseInnerBlock(std::ifstream &symbol_ifstream) {
std::string line = "";
LineScope scope = LineScope::global;
while (std::getline(symbol_ifstream, line)) {
if (line.find("}") != std::string::npos) {
break;
}
if (line.c_str()[0] == '#') {
continue;
}
scope = GetLineScope(line, scope);
if (scope != LineScope::global || StringContains(line, "global:")) {
continue;
}
ParseSymbolLine(line);
}
return true;
}
const std::map<std::string, ElfFunctionIR> &
VersionScriptParser::GetFunctions() {
return functions_;
}
const std::map<std::string, ElfObjectIR> &VersionScriptParser::GetGlobVars() {
return globvars_;
}
const std::set<std::string> &VersionScriptParser::GetFunctionRegexs() {
return function_regexs_;
}
const std::set<std::string> &VersionScriptParser::GetGlobVarRegexs() {
return globvar_regexs_;
}
bool VersionScriptParser::Parse() {
std::ifstream symbol_ifstream(version_script_);
if (!symbol_ifstream.is_open()) {
llvm::errs() << "Failed to open version script file\n";
return false;
}
std::string line;
while (std::getline(symbol_ifstream, line)) {
// Skip comment lines.
if (line.c_str()[0] == '#') {
continue;
}
if (StringContains(line, "{")) {
if ((StringContains(line, "PRIVATE"))) {
continue;
}
ParseInnerBlock(symbol_ifstream);
}
}
return true;
}
} // namespace abi_util } // namespace abi_util

View File

@@ -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

View File

@@ -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,
}

View File

@@ -0,0 +1,6 @@
#include "example.h"
void test1() {}
void test2() {}
void Test3::test() {}
void Test4::test() {}

View File

@@ -0,0 +1,12 @@
extern "C" void test1();
extern "C" void test2();
class Test3 {
public:
void test();
};
class Test4 {
public:
void test();
};

View File

@@ -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;

View File

@@ -499,6 +499,62 @@ TEST_MODULES = [
dumper_flags=['-output-format', 'Json'], dumper_flags=['-output-format', 'Json'],
linker_flags=['-input-format', 'Json', '-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} TEST_MODULES = {m.name: m for m in TEST_MODULES}

View File

@@ -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" : []
}

View File

@@ -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" : []
}

View File

@@ -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" : []
}

View File

@@ -309,6 +309,25 @@ class HeaderCheckerTest(unittest.TestCase):
["-input-format-old", "Json", "-input-format-new", "Json", ["-input-format-old", "Json", "-input-format-new", "Json",
"-consider-opaque-types-different"]) "-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__': if __name__ == '__main__':
unittest.main() unittest.main()