• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
LoadIgnoredSymbols(std::string & symbol_list_path)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 
ShouldEmitWarningMessage(abi_util::CompatibilityStatusIR status)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 
main(int argc,const char ** argv)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