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