1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "SymbolDatabase.h" 18 19 #include <err.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 23 #include <fstream> 24 #include <streambuf> 25 #include <string> 26 #include <unordered_set> 27 28 #include <llvm/ADT/SmallVector.h> 29 #include <llvm/ADT/StringRef.h> 30 #include <llvm/Object/Binary.h> 31 #include <llvm/Object/ELFObjectFile.h> 32 33 #include "versioner.h" 34 35 using namespace llvm; 36 using namespace llvm::object; 37 38 std::unordered_set<std::string> getSymbols(const std::string& filename) { 39 std::unordered_set<std::string> result; 40 auto binaryOrError = createBinary(filename); 41 if (!binaryOrError) { 42 errx(1, "failed to open library at %s: %s\n", filename.c_str(), 43 llvm::toString(binaryOrError.takeError()).c_str()); 44 } 45 46 ELFObjectFileBase* elf = dyn_cast_or_null<ELFObjectFileBase>(binaryOrError.get().getBinary()); 47 if (!elf) { 48 errx(1, "failed to parse %s as ELF", filename.c_str()); 49 } 50 51 for (const ELFSymbolRef symbol : elf->getDynamicSymbolIterators()) { 52 Expected<StringRef> symbolNameOrError = symbol.getName(); 53 54 if (!symbolNameOrError) { 55 errx(1, "failed to get symbol name for symbol in %s: %s", filename.c_str(), 56 llvm::toString(symbolNameOrError.takeError()).c_str()); 57 } 58 59 result.insert(symbolNameOrError.get().str()); 60 } 61 62 return result; 63 } 64 65 // The NDK platforms are built by copying the platform directories on top of 66 // each other to build each successive API version. Thus, we need to walk 67 // backwards to find each desired file. 68 static std::string readPlatformFile(const CompilationType& type, llvm::StringRef platform_dir, 69 const std::string& filename, bool required) { 70 int api_level = type.api_level; 71 std::ifstream stream; 72 while (api_level >= arch_min_api[type.arch]) { 73 if (supported_levels.count(api_level) == 0) { 74 --api_level; 75 continue; 76 } 77 78 std::string path = std::string(platform_dir) + "/android-" + std::to_string(api_level) + 79 "/arch-" + to_string(type.arch) + "/symbols/" + filename; 80 81 stream = std::ifstream(path); 82 if (stream) { 83 return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>()); 84 } 85 86 --api_level; 87 } 88 89 if (required) { 90 errx(1, "failed to find platform file '%s' for %s", filename.c_str(), to_string(type).c_str()); 91 } 92 93 return std::string(); 94 } 95 96 static std::map<std::string, NdkSymbolType> parsePlatform(const CompilationType& type, 97 const std::string& platform_dir) { 98 std::map<std::string, NdkSymbolType> result; 99 std::map<std::string, bool /*required*/> wanted_files = { 100 { "libc.so.functions.txt", true }, 101 { "libc.so.variables.txt", false }, 102 { "libdl.so.functions.txt", false }, 103 { "libm.so.functions.txt", false }, 104 { "libm.so.variables.txt", false }, 105 }; 106 107 for (const auto& pair : wanted_files) { 108 llvm::StringRef file = pair.first; 109 bool required = pair.second; 110 NdkSymbolType symbol_type; 111 if (file.endswith(".functions.txt")) { 112 symbol_type = NdkSymbolType::function; 113 } else if (file.endswith(".variables.txt")) { 114 symbol_type = NdkSymbolType::variable; 115 } else { 116 errx(1, "internal error: unexpected platform filename '%s'\n", file.str().c_str()); 117 } 118 119 std::string platform_file = readPlatformFile(type, platform_dir, file, required); 120 if (platform_file.empty()) { 121 continue; 122 } 123 124 llvm::SmallVector<llvm::StringRef, 0> symbols; 125 llvm::StringRef(platform_file).split(symbols, "\n"); 126 127 for (llvm::StringRef symbol_name : symbols) { 128 if (symbol_name.empty()) { 129 continue; 130 } 131 132 if (result.count(symbol_name) != 0) { 133 if (strict) { 134 printf("duplicated symbol '%s' in '%s'\n", symbol_name.str().c_str(), file.str().c_str()); 135 } 136 } 137 138 result[symbol_name] = symbol_type; 139 } 140 } 141 142 return result; 143 } 144 145 NdkSymbolDatabase parsePlatforms(const std::set<CompilationType>& types, 146 const std::string& platform_dir) { 147 std::map<std::string, std::map<CompilationType, NdkSymbolType>> result; 148 for (const CompilationType& type : types) { 149 std::map<std::string, NdkSymbolType> symbols = parsePlatform(type, platform_dir); 150 for (const auto& it : symbols) { 151 result[it.first][type] = it.second; 152 } 153 } 154 155 return result; 156 } 157