versioner: introduce.
Add a clang-based tool to inspect header availability attributes and
verify them against the NDK platform definitions.
Bug: http://b/28178111
Change-Id: I1bb1925a620e98cc9606cb5a3360b1224c700bd0
diff --git a/tools/versioner/src/SymbolDatabase.cpp b/tools/versioner/src/SymbolDatabase.cpp
new file mode 100644
index 0000000..a35f045
--- /dev/null
+++ b/tools/versioner/src/SymbolDatabase.cpp
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2016 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 "SymbolDatabase.h"
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <fstream>
+#include <streambuf>
+#include <string>
+#include <unordered_set>
+
+#include <llvm/ADT/SmallVector.h>
+#include <llvm/ADT/StringRef.h>
+#include <llvm/Object/Binary.h>
+#include <llvm/Object/ELFObjectFile.h>
+
+#include "versioner.h"
+
+using namespace llvm;
+using namespace llvm::object;
+
+std::unordered_set<std::string> getSymbols(const std::string& filename) {
+ std::unordered_set<std::string> result;
+ auto binary = createBinary(filename);
+ if (std::error_code ec = binary.getError()) {
+ errx(1, "failed to open library at %s: %s\n", filename.c_str(), ec.message().c_str());
+ }
+
+ ELFObjectFileBase* elf = dyn_cast_or_null<ELFObjectFileBase>(binary.get().getBinary());
+ if (!elf) {
+ errx(1, "failed to parse %s as ELF", filename.c_str());
+ }
+
+ for (const ELFSymbolRef symbol : elf->getDynamicSymbolIterators()) {
+ ErrorOr<StringRef> symbol_name = symbol.getName();
+
+ if (std::error_code ec = binary.getError()) {
+ errx(1, "failed to get symbol name for symbol in %s: %s", filename.c_str(),
+ ec.message().c_str());
+ }
+
+ result.insert(symbol_name.get().str());
+ }
+
+ return result;
+}
+
+// The NDK platforms are built by copying the platform directories on top of
+// each other to build each successive API version. Thus, we need to walk
+// backwards to find each desired file.
+static std::string readPlatformFile(const CompilationType& type, llvm::StringRef platform_dir,
+ const std::string& filename, bool required) {
+ int api_level = type.api_level;
+ std::ifstream stream;
+ while (api_level >= arch_min_api[type.arch]) {
+ if (supported_levels.count(api_level) == 0) {
+ --api_level;
+ continue;
+ }
+
+ std::string path = std::string(platform_dir) + "/android-" + std::to_string(api_level) +
+ "/arch-" + type.arch + "/symbols/" + filename;
+
+ stream = std::ifstream(path);
+ if (stream) {
+ return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());
+ }
+
+ --api_level;
+ }
+
+ if (required) {
+ errx(1, "failed to find platform file '%s' for %s", filename.c_str(), type.describe().c_str());
+ }
+
+ return std::string();
+}
+
+static std::map<std::string, NdkSymbolType> parsePlatform(const CompilationType& type,
+ const std::string& platform_dir) {
+ std::map<std::string, NdkSymbolType> result;
+ std::map<std::string, bool /*required*/> wanted_files = {
+ { "libc.so.functions.txt", true },
+ { "libc.so.variables.txt", false },
+ { "libdl.so.functions.txt", false },
+ { "libm.so.functions.txt", false },
+ { "libm.so.variables.txt", false },
+ };
+
+ for (const auto& pair : wanted_files) {
+ llvm::StringRef file = pair.first;
+ bool required = pair.second;
+ NdkSymbolType symbol_type;
+ if (file.endswith(".functions.txt")) {
+ symbol_type = NdkSymbolType::function;
+ } else if (file.endswith(".variables.txt")) {
+ symbol_type = NdkSymbolType::variable;
+ } else {
+ errx(1, "internal error: unexpected platform filename '%s'\n", file.str().c_str());
+ }
+
+ std::string platform_file = readPlatformFile(type, platform_dir, file, required);
+ if (platform_file.empty()) {
+ continue;
+ }
+
+ llvm::SmallVector<llvm::StringRef, 0> symbols;
+ llvm::StringRef(platform_file).split(symbols, "\n");
+
+ for (llvm::StringRef symbol_name : symbols) {
+ if (symbol_name.empty()) {
+ continue;
+ }
+
+ if (result.count(symbol_name) != 0) {
+ if (verbose) {
+ printf("duplicated symbol '%s' in '%s'\n", symbol_name.str().c_str(), file.str().c_str());
+ }
+ }
+
+ result[symbol_name] = symbol_type;
+ }
+ }
+
+ return result;
+}
+
+NdkSymbolDatabase parsePlatforms(const std::set<CompilationType>& types,
+ const std::string& platform_dir) {
+ std::map<std::string, std::map<CompilationType, NdkSymbolType>> result;
+ for (const CompilationType& type : types) {
+ std::map<std::string, NdkSymbolType> symbols = parsePlatform(type, platform_dir);
+ for (const auto& it : symbols) {
+ result[it.first][type] = it.second;
+ }
+ }
+
+ return result;
+}