• 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 "diff/abi_diff.h"
16 
17 #include "utils/config_file.h"
18 
19 #include <llvm/ADT/SmallString.h>
20 #include <llvm/Support/CommandLine.h>
21 #include <llvm/Support/FileSystem.h>
22 #include <llvm/Support/Path.h>
23 #include <llvm/Support/raw_ostream.h>
24 
25 #include <fstream>
26 
27 
28 using header_checker::diff::HeaderAbiDiff;
29 using header_checker::repr::CompatibilityStatusIR;
30 using header_checker::repr::DiffPolicyOptions;
31 using header_checker::repr::TextFormatIR;
32 using header_checker::utils::ConfigFile;
33 using header_checker::utils::ConfigSection;
34 
35 
36 static llvm::cl::OptionCategory header_checker_category(
37     "header-abi-diff options");
38 
39 static llvm::cl::opt<std::string> compatibility_report(
40     "o", llvm::cl::desc("<compatibility report>"), llvm::cl::Required,
41     llvm::cl::cat(header_checker_category));
42 
43 static llvm::cl::opt<std::string> lib_name(
44     "lib", llvm::cl::desc("<lib name>"), llvm::cl::Required,
45     llvm::cl::cat(header_checker_category));
46 
47 static llvm::cl::opt<std::string> arch(
48     "arch", llvm::cl::desc("<arch>"), llvm::cl::Required,
49     llvm::cl::cat(header_checker_category));
50 
51 static llvm::cl::opt<std::string> new_dump(
52     "new", llvm::cl::desc("<new dump>"), llvm::cl::Required,
53     llvm::cl::cat(header_checker_category));
54 
55 static llvm::cl::opt<std::string> old_dump(
56     "old", llvm::cl::desc("<old dump>"), llvm::cl::Required,
57     llvm::cl::cat(header_checker_category));
58 
59 static llvm::cl::opt<std::string> ignore_symbol_list(
60     "ignore-symbols", llvm::cl::desc("ignore symbols"), llvm::cl::Optional,
61     llvm::cl::cat(header_checker_category));
62 
63 static llvm::cl::opt<bool> advice_only(
64     "advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional,
65     llvm::cl::cat(header_checker_category));
66 
67 static llvm::cl::opt<bool> elf_unreferenced_symbol_errors(
68     "elf-unreferenced-symbol-errors",
69     llvm::cl::desc("This option is deprecated and has no effect."),
70     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
71 
72 static llvm::cl::opt<bool> check_all_apis(
73     "check-all-apis",
74     llvm::cl::desc("All apis, whether referenced or not, by exported symbols in"
75                    " the dynsym table of a shared library are checked"),
76     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
77 
78 static llvm::cl::opt<bool> allow_extensions(
79     "allow-extensions",
80     llvm::cl::desc("Do not return a non zero status on extensions"),
81     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
82 
83 static llvm::cl::opt<bool> allow_unreferenced_elf_symbol_changes(
84     "allow-unreferenced-elf-symbol-changes",
85     llvm::cl::desc("Do not return a non zero status on changes to elf symbols"
86                    "not referenced by metadata in exported headers"),
87     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
88 
89 static llvm::cl::opt<bool> allow_unreferenced_changes(
90     "allow-unreferenced-changes",
91     llvm::cl::desc("Do not return a non zero status on changes to data"
92                    " structures which are not directly referenced by exported"
93                    " APIs."),
94     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
95 
96 static llvm::cl::opt<bool> consider_opaque_types_different(
97     "consider-opaque-types-different",
98     llvm::cl::desc("Consider opaque types with different names as different. "
99                    "This should not be used while comparing C++ library ABIs"),
100     llvm::cl::Optional, llvm::cl::cat(header_checker_category));
101 
102 static llvm::cl::opt<TextFormatIR> text_format_old(
103     "input-format-old", llvm::cl::desc("Specify input format of old abi dump"),
104     llvm::cl::values(clEnumValN(TextFormatIR::ProtobufTextFormat,
105                                 "ProtobufTextFormat", "ProtobufTextFormat"),
106                      clEnumValN(TextFormatIR::Json, "Json", "JSON")),
107     llvm::cl::init(TextFormatIR::Json),
108     llvm::cl::cat(header_checker_category));
109 
110 static llvm::cl::opt<TextFormatIR> text_format_new(
111     "input-format-new", llvm::cl::desc("Specify input format of new abi dump"),
112     llvm::cl::values(clEnumValN(TextFormatIR::ProtobufTextFormat,
113                                 "ProtobufTextFormat", "ProtobufTextFormat"),
114                      clEnumValN(TextFormatIR::Json, "Json", "JSON")),
115     llvm::cl::init(TextFormatIR::Json),
116     llvm::cl::cat(header_checker_category));
117 
118 static llvm::cl::opt<TextFormatIR> text_format_diff(
119     "text-format-diff", llvm::cl::desc("Specify text format of abi-diff"),
120     llvm::cl::values(clEnumValN(TextFormatIR::ProtobufTextFormat,
121                                 "ProtobufTextFormat", "ProtobufTextFormat")),
122     llvm::cl::init(TextFormatIR::ProtobufTextFormat),
123     llvm::cl::cat(header_checker_category));
124 
125 static llvm::cl::opt<bool> allow_adding_removing_weak_symbols(
126     "allow-adding-removing-weak-symbols",
127     llvm::cl::desc("Do not treat addition or removal of weak symbols as "
128                    "incompatible changes."),
129     llvm::cl::init(false), llvm::cl::Optional,
130     llvm::cl::cat(header_checker_category));
131 
132 static llvm::cl::opt<std::string> target_version(
133     "target-version",
134     llvm::cl::desc(
135       "Load the flags for <target version> and <lib name> from config.json in "
136       "the old dump's parent directory."
137     ),
138     llvm::cl::init("current"), llvm::cl::Optional,
139     llvm::cl::cat(header_checker_category));
140 
141 static llvm::cl::list<std::string> ignore_linker_set_keys(
142     "ignore-linker-set-key",
143     llvm::cl::desc("Ignore a specific type or function in the comparison."),
144     llvm::cl::ZeroOrMore, llvm::cl::cat(header_checker_category));
145 
LoadIgnoredSymbols(std::string & symbol_list_path)146 static std::set<std::string> LoadIgnoredSymbols(std::string &symbol_list_path) {
147   std::ifstream symbol_ifstream(symbol_list_path);
148   std::set<std::string> ignored_symbols;
149   if (!symbol_ifstream) {
150     llvm::errs() << "Failed to open file containing symbols to ignore\n";
151     ::exit(1);
152   }
153   std::string line = "";
154   while (std::getline(symbol_ifstream, line)) {
155     ignored_symbols.insert(line);
156   }
157   return ignored_symbols;
158 }
159 
GetConfigFilePath(const std::string & dump_file_path)160 static std::string GetConfigFilePath(const std::string &dump_file_path) {
161   llvm::SmallString<128> config_file_path(dump_file_path);
162   llvm::sys::path::remove_filename(config_file_path);
163   llvm::sys::path::append(config_file_path, "config.json");
164   return std::string(config_file_path);
165 }
166 
UpdateFlags(const ConfigSection & section)167 static void UpdateFlags(const ConfigSection &section) {
168   for (auto &&i : section.GetIgnoredLinkerSetKeys()) {
169     ignore_linker_set_keys.push_back(i);
170   }
171   for (auto &&p : section) {
172     auto &&key = p.first;
173     bool value_bool = p.second;
174     if (key == "allow_adding_removing_weak_symbols") {
175       allow_adding_removing_weak_symbols = value_bool;
176     } else if (key == "advice_only") {
177       advice_only = value_bool;
178     } else if (key == "elf_unreferenced_symbol_errors") {
179       elf_unreferenced_symbol_errors = value_bool;
180     } else if (key == "check_all_apis") {
181       check_all_apis = value_bool;
182     } else if (key == "allow_extensions") {
183       allow_extensions = value_bool;
184     } else if (key == "allow_unreferenced_elf_symbol_changes") {
185       allow_unreferenced_elf_symbol_changes = value_bool;
186     } else if (key == "allow_unreferenced_changes") {
187       allow_unreferenced_changes = value_bool;
188     } else if (key == "consider_opaque_types_different") {
189       consider_opaque_types_different = value_bool;
190     }
191   }
192 }
193 
ReadConfigFile(const std::string & config_file_path)194 static void ReadConfigFile(const std::string &config_file_path) {
195   ConfigFile cfg;
196   if (!cfg.Load(config_file_path)) {
197     ::exit(1);
198   }
199   if (cfg.HasGlobalSection()) {
200     UpdateFlags(cfg.GetGlobalSection());
201   }
202   if (cfg.HasSection(lib_name, target_version)) {
203     UpdateFlags(cfg.GetSection(lib_name, target_version));
204   }
205 }
206 
GetErrorMessage(CompatibilityStatusIR status)207 static std::string GetErrorMessage(CompatibilityStatusIR status) {
208   if (status & CompatibilityStatusIR::Incompatible) {
209     return "INCOMPATIBLE CHANGES";
210   }
211   if (!allow_unreferenced_elf_symbol_changes &&
212       (status & CompatibilityStatusIR::ElfIncompatible)) {
213     return "ELF Symbols not referenced by exported headers removed";
214   }
215   if (!allow_extensions && (status & CompatibilityStatusIR::Extension)) {
216     return "EXTENDING CHANGES";
217   }
218   if (!allow_unreferenced_changes &&
219       (status & CompatibilityStatusIR::UnreferencedChanges)) {
220     return "changes in exported headers, which are not directly referenced "
221            "by exported symbols. This MIGHT be an ABI breaking change due to "
222            "internal typecasts";
223   }
224   return "";
225 }
226 
main(int argc,const char ** argv)227 int main(int argc, const char **argv) {
228   llvm::cl::ParseCommandLineOptions(argc, argv, "header-checker");
229 
230   const std::string config_file_path = GetConfigFilePath(old_dump);
231   if (llvm::sys::fs::exists(config_file_path)) {
232     ReadConfigFile(config_file_path);
233   }
234 
235   std::set<std::string> ignored_symbols;
236   if (llvm::sys::fs::exists(ignore_symbol_list)) {
237     ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list);
238   }
239 
240   std::set<std::string> ignored_linker_set_keys_set(
241       ignore_linker_set_keys.begin(), ignore_linker_set_keys.end());
242 
243   DiffPolicyOptions diff_policy_options(consider_opaque_types_different);
244 
245   HeaderAbiDiff judge(lib_name, arch, old_dump, new_dump, compatibility_report,
246                       ignored_symbols, ignored_linker_set_keys_set,
247                       allow_adding_removing_weak_symbols, diff_policy_options,
248                       check_all_apis, text_format_old, text_format_new,
249                       text_format_diff);
250 
251   CompatibilityStatusIR status = judge.GenerateCompatibilityReport();
252 
253   std::string status_str = GetErrorMessage(status);
254   if (!status_str.empty()) {
255     llvm::errs() << "******************************************************\n"
256                  << "\033[31;1merror: \033[0m"
257                  << lib_name
258                  << "'s ABI has "
259                  << status_str
260                  << ". Please check compatibility report at: "
261                  << compatibility_report << "\n"
262                  << "******************************************************\n";
263   }
264 
265   return (advice_only || status_str.empty()) ? CompatibilityStatusIR::Compatible
266                                              : status;
267 }
268