Home | History | Annotate | Download | only in src
      1 // Copyright (C) 2016 The Android Open Source Project
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include "abi_diff.h"
     16 
     17 #include <fstream>
     18 
     19 #include <llvm/Support/CommandLine.h>
     20 #include <llvm/Support/FileSystem.h>
     21 #include <llvm/Support/raw_ostream.h>
     22 
     23 static llvm::cl::OptionCategory header_checker_category(
     24     "header-abi-diff options");
     25 
     26 static llvm::cl::opt<std::string> compatibility_report(
     27     "o", llvm::cl::desc("<compatibility report>"), llvm::cl::Required,
     28     llvm::cl::cat(header_checker_category));
     29 
     30 static llvm::cl::opt<std::string> lib_name(
     31     "lib", llvm::cl::desc("<lib name>"), llvm::cl::Required,
     32     llvm::cl::cat(header_checker_category));
     33 
     34 static llvm::cl::opt<std::string> arch(
     35     "arch", llvm::cl::desc("<arch>"), llvm::cl::Required,
     36     llvm::cl::cat(header_checker_category));
     37 
     38 static llvm::cl::opt<std::string> new_dump(
     39     "new", llvm::cl::desc("<new dump>"), llvm::cl::Required,
     40     llvm::cl::cat(header_checker_category));
     41 
     42 static llvm::cl::opt<std::string> old_dump(
     43     "old", llvm::cl::desc("<old dump>"), llvm::cl::Required,
     44     llvm::cl::cat(header_checker_category));
     45 
     46 static llvm::cl::opt<std::string> ignore_symbol_list(
     47     "ignore-symbols", llvm::cl::desc("ignore symbols"), llvm::cl::Optional,
     48     llvm::cl::cat(header_checker_category));
     49 
     50 static llvm::cl::opt<bool> advice_only(
     51     "advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional,
     52     llvm::cl::cat(header_checker_category));
     53 
     54 static llvm::cl::opt<bool> elf_unreferenced_symbol_errors(
     55     "elf-unreferenced-symbol-errors",
     56     llvm::cl::desc("Display erors on removal of elf symbols, unreferenced by"
     57                    "metadata in exported headers."),
     58     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
     59 
     60 static llvm::cl::opt<bool> check_all_apis(
     61     "check-all-apis",
     62     llvm::cl::desc("All apis, whether referenced or not, by exported symbols in"
     63                    " the dynsym table of a shared library are checked"),
     64     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
     65 
     66 static llvm::cl::opt<bool> suppress_local_warnings(
     67     "suppress_local_warnings", llvm::cl::desc("suppress local warnings"),
     68     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
     69 
     70 static llvm::cl::opt<bool> allow_extensions(
     71     "allow-extensions",
     72     llvm::cl::desc("Do not return a non zero status on extensions"),
     73     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
     74 
     75 static llvm::cl::opt<bool> allow_unreferenced_elf_symbol_changes(
     76     "allow-unreferenced-elf-symbol-changes",
     77     llvm::cl::desc("Do not return a non zero status on changes to elf symbols"
     78                    "not referenced by metadata in exported headers"),
     79     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
     80 
     81 static llvm::cl::opt<bool> allow_unreferenced_changes(
     82     "allow-unreferenced-changes",
     83     llvm::cl::desc("Do not return a non zero status on changes to data"
     84                    " structures which are not directly referenced by exported"
     85                    " APIs."),
     86     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
     87 
     88 static llvm::cl::opt<abi_util::TextFormatIR> text_format_old(
     89     "text-format-old", llvm::cl::desc("Specify text format of old abi dump"),
     90     llvm::cl::values(clEnumValN(abi_util::TextFormatIR::ProtobufTextFormat,
     91                                 "ProtobufTextFormat","ProtobufTextFormat"),
     92                      clEnumValEnd),
     93     llvm::cl::init(abi_util::TextFormatIR::ProtobufTextFormat),
     94     llvm::cl::cat(header_checker_category));
     95 
     96 static llvm::cl::opt<abi_util::TextFormatIR> text_format_new(
     97     "text-format-new", llvm::cl::desc("Specify text format of new abi dump"),
     98     llvm::cl::values(clEnumValN(abi_util::TextFormatIR::ProtobufTextFormat,
     99                                 "ProtobufTextFormat", "ProtobugTextFormat"),
    100                      clEnumValEnd),
    101     llvm::cl::init(abi_util::TextFormatIR::ProtobufTextFormat),
    102     llvm::cl::cat(header_checker_category));
    103 
    104 static llvm::cl::opt<abi_util::TextFormatIR> text_format_diff(
    105     "text-format-diff", llvm::cl::desc("Specify text format of abi-diff"),
    106     llvm::cl::values(clEnumValN(abi_util::TextFormatIR::ProtobufTextFormat,
    107                                 "ProtobufTextFormat", "ProtobufTextFormat"),
    108                      clEnumValEnd),
    109     llvm::cl::init(abi_util::TextFormatIR::ProtobufTextFormat),
    110     llvm::cl::cat(header_checker_category));
    111 
    112 static std::set<std::string> LoadIgnoredSymbols(std::string &symbol_list_path) {
    113   std::ifstream symbol_ifstream(symbol_list_path);
    114   std::set<std::string> ignored_symbols;
    115   if (!symbol_ifstream) {
    116     llvm::errs() << "Failed to open file containing symbols to ignore\n";
    117     ::exit(1);
    118   }
    119   std::string line = "";
    120   while (std::getline(symbol_ifstream, line)) {
    121     ignored_symbols.insert(line);
    122   }
    123   return ignored_symbols;
    124 }
    125 
    126 static const char kWarn[] = "\033[36;1mwarning: \033[0m";
    127 static const char kError[] = "\033[31;1merror: \033[0m";
    128 
    129 bool ShouldEmitWarningMessage(abi_util::CompatibilityStatusIR status) {
    130   return (!allow_extensions &&
    131       (status & abi_util::CompatibilityStatusIR::Extension)) ||
    132       (!allow_unreferenced_changes &&
    133       (status & abi_util::CompatibilityStatusIR::UnreferencedChanges)) ||
    134       (!allow_unreferenced_elf_symbol_changes &&
    135       (status & abi_util::CompatibilityStatusIR::ElfIncompatible)) ||
    136       (status & abi_util::CompatibilityStatusIR::Incompatible);
    137 }
    138 
    139 int main(int argc, const char **argv) {
    140   llvm::cl::ParseCommandLineOptions(argc, argv, "header-checker");
    141   std::set<std::string> ignored_symbols;
    142   if (llvm::sys::fs::exists(ignore_symbol_list)) {
    143     ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list);
    144   }
    145   HeaderAbiDiff judge(lib_name, arch, old_dump, new_dump, compatibility_report,
    146                       ignored_symbols, check_all_apis, text_format_old,
    147                       text_format_new, text_format_diff);
    148 
    149   abi_util::CompatibilityStatusIR status = judge.GenerateCompatibilityReport();
    150 
    151   std::string status_str = "";
    152   std::string unreferenced_change_str = "";
    153   std::string error_or_warning_str = kWarn;
    154 
    155   switch (status) {
    156     case abi_util::CompatibilityStatusIR::Incompatible:
    157       error_or_warning_str = kError;
    158       status_str = "INCOMPATIBLE CHANGES";
    159       break;
    160     case abi_util::CompatibilityStatusIR::ElfIncompatible:
    161       if (elf_unreferenced_symbol_errors) {
    162         error_or_warning_str = kError;
    163       }
    164       status_str = "ELF Symbols not referenced by exported headers removed";
    165       break;
    166     default:
    167       break;
    168   }
    169   if (status & abi_util::CompatibilityStatusIR::Extension) {
    170     if (!allow_extensions) {
    171       error_or_warning_str = kError;
    172     }
    173     status_str = "EXTENDING CHANGES";
    174   }
    175   if (status & abi_util::CompatibilityStatusIR::UnreferencedChanges) {
    176     unreferenced_change_str = ", changes in exported headers, which are";
    177     unreferenced_change_str += " not directly referenced by exported symbols.";
    178     unreferenced_change_str += " This MIGHT be an ABI breaking change due to";
    179     unreferenced_change_str += " internal typecasts.";
    180   }
    181 
    182   bool should_emit_warning_message = ShouldEmitWarningMessage(status);
    183 
    184   if (should_emit_warning_message) {
    185     llvm::errs() << "******************************************************\n"
    186                  << error_or_warning_str
    187                  << "VNDK library: "
    188                  << lib_name
    189                  << "'s ABI has "
    190                  << status_str
    191                  << unreferenced_change_str
    192                  << " Please check compatiblity report at : "
    193                  << compatibility_report << "\n"
    194                  << "******************************************************\n";
    195   }
    196 
    197   if (!advice_only && should_emit_warning_message) {
    198     return status;
    199   }
    200 
    201   return abi_util::CompatibilityStatusIR::Compatible;
    202 }
    203