summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHsin-Yi Chen <hsinyichen@google.com>2024-04-04 23:38:52 +0800
committerHsin-Yi Chen <hsinyichen@google.com>2024-04-15 12:02:51 +0800
commitce1e865e0c3caaa1d698ec396f73520c716afd2a (patch)
treeaab810983aefa2b0f68c7b6a937a74958194bdfc
parent7ccad504114ed41ba92791beb98c16f6303aa1da (diff)
downloaddevelopment-ce1e865e0c3caaa1d698ec396f73520c716afd2a.tar.gz
Parse mode tags in version scripts
header-abi-linker filters symbols tagged with llndk, apex, or systemapi in version scripts. The filtering logic is copied from build/soong/cc/symbolfile. Usage: - By default, header-abi-linker includes every symbol in the version script. - `-api 34 -include-symbol-tag llndk=202404` includes LLNDK 202404 and NDK 34. - `-api 35 -include-symbol-tag none` includes NDK 35, but excludes LLNDK and APEX. Test: LD_LIBRARY_PATH=out/soong/dist/lib64 \ out/soong/host/linux-x86/nativetest64/header-checker-unittests/header-checker-unittests Bug: 314010764 Change-Id: I362995068af0ea6dff317078b8624be46e3b404a
-rw-r--r--vndk/tools/header-checker/src/linker/header_abi_linker.cpp20
-rw-r--r--vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp98
-rw-r--r--vndk/tools/header-checker/src/repr/symbol/version_script_parser.h24
-rw-r--r--vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp90
4 files changed, 203 insertions, 29 deletions
diff --git a/vndk/tools/header-checker/src/linker/header_abi_linker.cpp b/vndk/tools/header-checker/src/linker/header_abi_linker.cpp
index 657504ecc..a12677796 100644
--- a/vndk/tools/header-checker/src/linker/header_abi_linker.cpp
+++ b/vndk/tools/header-checker/src/linker/header_abi_linker.cpp
@@ -81,9 +81,19 @@ static llvm::cl::list<std::string> excluded_symbol_tags(
"exclude-symbol-tag", llvm::cl::Optional,
llvm::cl::cat(header_linker_category));
+static llvm::cl::list<std::string> included_symbol_tags(
+ "include-symbol-tag",
+ llvm::cl::desc("Filter the symbols in the version script by mode tag, "
+ "such as llndk, apex, and systemapi. The format is "
+ "<tag>=<level> or <tag>. If this option is not specified, "
+ "all mode tags are included."),
+ 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"),
+ "api",
+ llvm::cl::desc("Filter the symbols in the version script by comparing "
+ "\"introduced\" tags and the specified API level."),
+ llvm::cl::Optional, llvm::cl::init("current"),
llvm::cl::cat(header_linker_category));
static llvm::cl::opt<std::string> api_map(
@@ -480,6 +490,12 @@ static bool InitializeVersionScriptParser(repr::VersionScriptParser &parser) {
for (auto &&tag : excluded_symbol_tags) {
parser.AddExcludedSymbolTag(tag);
}
+ for (auto &&tag : included_symbol_tags) {
+ if (!parser.AddModeTag(tag)) {
+ llvm::errs() << "Failed to parse -include-symbol-tag " << tag << "\n";
+ return false;
+ }
+ }
return true;
}
diff --git a/vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp b/vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp
index fe1735587..931064b21 100644
--- a/vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp
+++ b/vndk/tools/header-checker/src/repr/symbol/version_script_parser.cpp
@@ -30,8 +30,14 @@ namespace header_checker {
namespace repr {
-static constexpr char DEFAULT_ARCH[] = "arm64";
+using ModeTagLevel = std::pair<std::string_view, utils::ApiLevel>;
+
+static constexpr char DEFAULT_ARCH[] = "arm64";
+static constexpr utils::ApiLevel MIN_MODE_TAG_LEVEL = 0;
+static constexpr utils::ApiLevel MAX_MODE_TAG_LEVEL = 1000000;
+static const std::set<std::string_view> KNOWN_MODE_TAGS{"apex", "llndk",
+ "systemapi"};
inline std::string GetIntroducedArchTag(const std::string &arch) {
return "introduced-" + arch + "=";
@@ -55,12 +61,39 @@ void VersionScriptParser::SetApiLevelMap(utils::ApiLevelMap api_level_map) {
}
+static std::optional<ModeTagLevel> ParseModeTag(std::string_view tag,
+ utils::ApiLevel default_level) {
+ std::vector<std::string_view> split_tag = utils::Split(tag, "=");
+ utils::ApiLevel level = default_level;
+ if (split_tag.size() == 2) {
+ auto level = utils::ParseInt(std::string(split_tag[1]));
+ if (level.has_value()) {
+ return {{split_tag[0], level.value()}};
+ }
+ } else if (split_tag.size() == 1) {
+ return {{split_tag[0], default_level}};
+ }
+ return {};
+}
+
+
+bool VersionScriptParser::AddModeTag(std::string_view mode_tag) {
+ auto parsed_mode_tag = ParseModeTag(mode_tag, MAX_MODE_TAG_LEVEL);
+ if (parsed_mode_tag.has_value()) {
+ included_mode_tags_[std::string(parsed_mode_tag->first)] =
+ parsed_mode_tag->second;
+ return true;
+ }
+ return false;
+}
+
+
VersionScriptParser::ParsedTags VersionScriptParser::ParseSymbolTags(
- const std::string &line) {
+ const std::string &line, const ParsedTags &initial_value) {
static const char *const POSSIBLE_ARCHES[] = {
"arm", "arm64", "x86", "x86_64", "mips", "mips64"};
- ParsedTags result;
+ ParsedTags result = initial_value;
std::string_view line_view(line);
std::string::size_type comment_pos = line_view.find('#');
@@ -138,36 +171,62 @@ VersionScriptParser::ParsedTags VersionScriptParser::ParseSymbolTags(
result.has_weak_tag_ = true;
continue;
}
+
+ auto mode_tag = ParseModeTag(tag, MIN_MODE_TAG_LEVEL);
+ if (mode_tag.has_value() &&
+ (KNOWN_MODE_TAGS.count(mode_tag->first) > 0 ||
+ included_mode_tags_.count(mode_tag->first) > 0)) {
+ result.mode_tags_[std::string(mode_tag->first)] = mode_tag->second;
+ }
}
return result;
}
-bool VersionScriptParser::IsSymbolExported(
- const VersionScriptParser::ParsedTags &tags) {
- if (tags.has_excluded_tags_) {
+bool VersionScriptParser::MatchModeTags(const ParsedTags &tags) {
+ for (const auto &mode_tag : tags.mode_tags_) {
+ auto included_mode_tag = included_mode_tags_.find(mode_tag.first);
+ if (included_mode_tag != included_mode_tags_.end() &&
+ included_mode_tag->second >= mode_tag.second) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool VersionScriptParser::MatchIntroducedTags(const ParsedTags &tags) {
+ if (tags.has_future_tag_ && api_level_ < utils::FUTURE_API_LEVEL) {
+ return false;
+ }
+ if (tags.has_introduced_tags_ && api_level_ < tags.introduced_) {
return false;
}
+ return true;
+}
- if (tags.has_arch_tags_ && !tags.has_current_arch_tag_) {
+
+bool VersionScriptParser::IsSymbolExported(const ParsedTags &tags) {
+ if (tags.has_excluded_tags_) {
return false;
}
- if (tags.has_future_tag_) {
- return api_level_ == utils::FUTURE_API_LEVEL;
+ if (tags.has_arch_tags_ && !tags.has_current_arch_tag_) {
+ return false;
}
- if (tags.has_introduced_tags_) {
- return api_level_ >= tags.introduced_;
+ if (!included_mode_tags_.empty() && !tags.mode_tags_.empty()) {
+ return MatchModeTags(tags);
}
- return true;
+ return MatchIntroducedTags(tags);
}
-bool VersionScriptParser::ParseSymbolLine(const std::string &line,
- bool is_in_extern_cpp) {
+bool VersionScriptParser::ParseSymbolLine(
+ const std::string &line, bool is_in_extern_cpp,
+ const ParsedTags &version_block_tags) {
// The symbol name comes before the ';'.
std::string::size_type pos = line.find(";");
if (pos == std::string::npos) {
@@ -177,7 +236,7 @@ bool VersionScriptParser::ParseSymbolLine(const std::string &line,
std::string symbol(utils::Trim(line.substr(0, pos)));
- ParsedTags tags = ParseSymbolTags(line);
+ ParsedTags tags = ParseSymbolTags(line, version_block_tags);
if (!IsSymbolExported(tags)) {
return true;
}
@@ -209,7 +268,8 @@ bool VersionScriptParser::ParseSymbolLine(const std::string &line,
}
-bool VersionScriptParser::ParseVersionBlock(bool ignore_symbols) {
+bool VersionScriptParser::ParseVersionBlock(bool ignore_symbols,
+ const ParsedTags &tags) {
static const std::regex EXTERN_CPP_PATTERN(R"(extern\s*"[Cc]\+\+"\s*\{)");
LineScope scope = LineScope::GLOBAL;
@@ -250,7 +310,7 @@ bool VersionScriptParser::ParseVersionBlock(bool ignore_symbols) {
// Parse symbol line
if (!ignore_symbols) {
- if (!ParseSymbolLine(line, is_in_extern_cpp)) {
+ if (!ParseSymbolLine(line, is_in_extern_cpp, tags)) {
return false;
}
}
@@ -285,8 +345,8 @@ std::unique_ptr<ExportedSymbolSet> VersionScriptParser::Parse(
bool exclude_symbol_version = utils::HasMatchingGlobPattern(
excluded_symbol_versions_, version.c_str());
- ParsedTags tags = ParseSymbolTags(line);
- if (!ParseVersionBlock(exclude_symbol_version || !IsSymbolExported(tags))) {
+ ParsedTags tags = ParseSymbolTags(line, ParsedTags());
+ if (!ParseVersionBlock(exclude_symbol_version, tags)) {
return nullptr;
}
}
diff --git a/vndk/tools/header-checker/src/repr/symbol/version_script_parser.h b/vndk/tools/header-checker/src/repr/symbol/version_script_parser.h
index 74ca72f1a..0f468b55d 100644
--- a/vndk/tools/header-checker/src/repr/symbol/version_script_parser.h
+++ b/vndk/tools/header-checker/src/repr/symbol/version_script_parser.h
@@ -20,7 +20,7 @@
#include "utils/api_level.h"
#include <functional>
-#include <set>
+#include <map>
#include <string>
@@ -30,12 +30,14 @@ namespace repr {
class VersionScriptParser {
private:
+ // This comparison function allows finding elements by string_view.
+ using ModeTagLevelMap = std::map<std::string, utils::ApiLevel, std::less<>>;
+
enum class LineScope {
GLOBAL,
LOCAL,
};
-
struct ParsedTags {
public:
unsigned has_arch_tags_ : 1;
@@ -46,7 +48,7 @@ class VersionScriptParser {
unsigned has_var_tag_ : 1;
unsigned has_weak_tag_ : 1;
utils::ApiLevel introduced_;
-
+ ModeTagLevelMap mode_tags_;
public:
ParsedTags()
@@ -84,6 +86,9 @@ class VersionScriptParser {
excluded_symbol_tags_.insert(tag);
}
+ // Returns whether the argument is valid.
+ bool AddModeTag(std::string_view mode_tag);
+
void SetErrorHandler(std::unique_ptr<ErrorHandler> error_handler) {
error_handler_ = std::move(error_handler);
}
@@ -94,11 +99,17 @@ class VersionScriptParser {
private:
bool ReadLine(std::string &line);
- bool ParseVersionBlock(bool ignore_symbols);
+ bool ParseVersionBlock(bool ignore_symbols, const ParsedTags &tags);
+
+ bool ParseSymbolLine(const std::string &line, bool is_cpp_symbol,
+ const ParsedTags &version_block_tags);
+
+ ParsedTags ParseSymbolTags(const std::string &line,
+ const ParsedTags &initial_value);
- bool ParseSymbolLine(const std::string &line, bool is_cpp_symbol);
+ bool MatchModeTags(const ParsedTags &tags);
- ParsedTags ParseSymbolTags(const std::string &line);
+ bool MatchIntroducedTags(const ParsedTags &tags);
bool IsSymbolExported(const ParsedTags &tags);
@@ -121,6 +132,7 @@ class VersionScriptParser {
utils::StringSet excluded_symbol_versions_;
utils::StringSet excluded_symbol_tags_;
+ ModeTagLevelMap included_mode_tags_;
std::istream *stream_;
int line_no_;
diff --git a/vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp b/vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp
index 15e13517e..41955f3b0 100644
--- a/vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp
+++ b/vndk/tools/header-checker/src/repr/symbol/version_script_parser_test.cpp
@@ -174,8 +174,9 @@ TEST(VersionScriptParserTest, ParseSymbolTagsIntroduced) {
ASSERT_TRUE(result);
const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
-
- EXPECT_TRUE(funcs.empty());
+ // This may be an undefined behavior. ndkstubgen includes it in llndk mode,
+ // but excludes it in ndk mode.
+ EXPECT_THAT(funcs, ElementsAre(Key("test5")));
}
{
@@ -355,6 +356,91 @@ TEST(VersionScriptParserTest, ExcludeSymbolTags) {
}
+TEST(VersionScriptParserTest, IncludeSymbolTags) {
+ static const char testdata[] = R"TESTDATA(
+ LIBEX_1.0 {
+ global:
+ always; # unknown
+ api34; # introduced=34
+ api35; # introduced=35
+ api35_llndk202404; # introduced=35 llndk=202404
+ systemapi; # systemapi
+ systemapi_llndk; # systemapi llndk
+ };
+ LIBEX_2.0 { # introduced=36
+ api36_llndk202504;
+ api36_llndk202504; # llndk=202504
+ };
+ )TESTDATA";
+
+ {
+ VersionScriptParser parser;
+ parser.SetApiLevel(34);
+ parser.AddModeTag("llndk=202404");
+
+ std::istringstream stream(testdata);
+ std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
+ ASSERT_TRUE(result);
+
+ const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
+
+ EXPECT_THAT(funcs,
+ ElementsAre(Key("always"), Key("api34"),
+ Key("api35_llndk202404"), Key("systemapi_llndk")));
+ }
+
+ {
+ VersionScriptParser parser;
+ parser.SetApiLevel(34);
+ parser.AddModeTag("llndk");
+
+ std::istringstream stream(testdata);
+ std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
+ ASSERT_TRUE(result);
+
+ const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
+
+ EXPECT_THAT(
+ funcs,
+ ElementsAre(Key("always"), Key("api34"), Key("api35_llndk202404"),
+ Key("api36_llndk202504"), Key("systemapi_llndk")));
+ }
+
+ // Include all mode tags
+ {
+ VersionScriptParser parser;
+ parser.SetApiLevel(utils::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_THAT(funcs,
+ ElementsAre(Key("always"), Key("api34"), Key("api35"),
+ Key("api35_llndk202404"), Key("api36_llndk202504"),
+ Key("systemapi"), Key("systemapi_llndk")));
+ }
+
+ // Exclude all mode tags
+ {
+ VersionScriptParser parser;
+ parser.SetApiLevel(utils::FUTURE_API_LEVEL);
+ parser.AddModeTag("none");
+
+ std::istringstream stream(testdata);
+ std::unique_ptr<ExportedSymbolSet> result(parser.Parse(stream));
+ ASSERT_TRUE(result);
+
+ const ExportedSymbolSet::FunctionMap &funcs = result->GetFunctions();
+
+ EXPECT_THAT(funcs, ElementsAre(Key("always"), Key("api34"), Key("api35"),
+ Key("api36_llndk202504")));
+ }
+}
+
+
TEST(VersionScriptParserTest, ParseExternCpp) {
static const char testdata[] = R"TESTDATA(
LIBEX_1.0 {