blob: c893da646d876966064d2e05fd8ac17bad31bcd6 [file] [log] [blame]
David Brazdil2b9c35b2018-01-12 15:44:43 +00001/*
2 * Copyright (C) 2017 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 <fstream>
18#include <iostream>
19#include <unordered_set>
20
21#include "android-base/stringprintf.h"
22#include "android-base/strings.h"
23
24#include "base/unix_file/fd_file.h"
25#include "dex/art_dex_file_loader.h"
26#include "dex/dex_file-inl.h"
David Brazdilf6a8a552018-01-15 18:10:50 +000027#include "hidden_api_access_flags.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000028#include "mem_map.h"
29#include "os.h"
30
31namespace art {
32
33static int original_argc;
34static char** original_argv;
35
36static std::string CommandLine() {
37 std::vector<std::string> command;
38 for (int i = 0; i < original_argc; ++i) {
39 command.push_back(original_argv[i]);
40 }
41 return android::base::Join(command, ' ');
42}
43
44static void UsageErrorV(const char* fmt, va_list ap) {
45 std::string error;
46 android::base::StringAppendV(&error, fmt, ap);
47 LOG(ERROR) << error;
48}
49
50static void UsageError(const char* fmt, ...) {
51 va_list ap;
52 va_start(ap, fmt);
53 UsageErrorV(fmt, ap);
54 va_end(ap);
55}
56
57NO_RETURN static void Usage(const char* fmt, ...) {
58 va_list ap;
59 va_start(ap, fmt);
60 UsageErrorV(fmt, ap);
61 va_end(ap);
62
63 UsageError("Command: %s", CommandLine().c_str());
64 UsageError("Usage: hiddenapi [options]...");
65 UsageError("");
66 UsageError(" --dex=<filename>: specify dex file whose members' access flags are to be set.");
67 UsageError(" At least one --dex parameter must be specified.");
68 UsageError("");
69 UsageError(" --light-greylist=<filename>:");
70 UsageError(" --dark-greylist=<filename>:");
71 UsageError(" --blacklist=<filename>: text files with signatures of methods/fields to be marked");
72 UsageError(" greylisted/blacklisted respectively. At least one list must be provided.");
73 UsageError("");
74 UsageError(" --print-hidden-api: dump a list of marked methods/fields to the standard output.");
75 UsageError(" There is no indication which API category they belong to.");
76 UsageError("");
77
78 exit(EXIT_FAILURE);
79}
80
81class DexClass {
82 public:
83 DexClass(const DexFile& dex_file, uint32_t idx)
84 : dex_file_(dex_file), class_def_(dex_file.GetClassDef(idx)) {}
85
86 const DexFile& GetDexFile() const { return dex_file_; }
87
88 const dex::TypeIndex GetClassIndex() const { return class_def_.class_idx_; }
89
90 const uint8_t* GetData() const { return dex_file_.GetClassData(class_def_); }
91
92 const char* GetDescriptor() const { return dex_file_.GetClassDescriptor(class_def_); }
93
94 private:
95 const DexFile& dex_file_;
96 const DexFile::ClassDef& class_def_;
97};
98
99class DexMember {
100 public:
101 DexMember(const DexClass& klass, const ClassDataItemIterator& it)
102 : klass_(klass), it_(it) {
103 DCHECK_EQ(it_.IsAtMethod() ? GetMethodId().class_idx_ : GetFieldId().class_idx_,
104 klass_.GetClassIndex());
105 }
106
107 // Sets hidden bits in access flags and writes them back into the DEX in memory.
108 // Note that this will not update the cached data of ClassDataItemIterator
109 // until it iterates over this item again and therefore will fail a CHECK if
110 // it is called multiple times on the same DexMember.
David Brazdilf6a8a552018-01-15 18:10:50 +0000111 void SetHidden(HiddenApiAccessFlags::ApiList value) {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000112 const uint32_t old_flags = it_.GetRawMemberAccessFlags();
David Brazdilf6a8a552018-01-15 18:10:50 +0000113 const uint32_t new_flags = HiddenApiAccessFlags::EncodeForDex(old_flags, value);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000114 CHECK_EQ(UnsignedLeb128Size(new_flags), UnsignedLeb128Size(old_flags));
115
116 // Locate the LEB128-encoded access flags in class data.
117 // `ptr` initially points to the next ClassData item. We iterate backwards
118 // until we hit the terminating byte of the previous Leb128 value.
119 const uint8_t* ptr = it_.DataPointer();
120 if (it_.IsAtMethod()) {
Alex Light1a824a52018-01-26 15:45:30 -0800121 ptr = ReverseSearchUnsignedLeb128(ptr);
122 DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), it_.GetMethodCodeItemOffset());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000123 }
Alex Light1a824a52018-01-26 15:45:30 -0800124 ptr = ReverseSearchUnsignedLeb128(ptr);
125 DCHECK_EQ(DecodeUnsignedLeb128WithoutMovingCursor(ptr), old_flags);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000126
127 // Overwrite the access flags.
128 UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags);
129 }
130
131 // Returns true if this member's API entry is in `list`.
132 bool IsOnApiList(const std::unordered_set<std::string>& list) const {
133 return list.find(GetApiEntry()) != list.end();
134 }
135
136 // Constructs a string with a unique signature of this class member.
137 std::string GetApiEntry() const {
138 std::stringstream ss;
139 ss << klass_.GetDescriptor() << "->";
140 if (it_.IsAtMethod()) {
141 const DexFile::MethodId& mid = GetMethodId();
142 ss << klass_.GetDexFile().GetMethodName(mid)
143 << klass_.GetDexFile().GetMethodSignature(mid).ToString();
144 } else {
145 const DexFile::FieldId& fid = GetFieldId();
146 ss << klass_.GetDexFile().GetFieldName(fid) << ":"
147 << klass_.GetDexFile().GetFieldTypeDescriptor(fid);
148 }
149 return ss.str();
150 }
151
152 private:
153 inline const DexFile::MethodId& GetMethodId() const {
154 DCHECK(it_.IsAtMethod());
155 return klass_.GetDexFile().GetMethodId(it_.GetMemberIndex());
156 }
157
158 inline const DexFile::FieldId& GetFieldId() const {
159 DCHECK(!it_.IsAtMethod());
160 return klass_.GetDexFile().GetFieldId(it_.GetMemberIndex());
161 }
162
David Brazdil2b9c35b2018-01-12 15:44:43 +0000163 const DexClass& klass_;
164 const ClassDataItemIterator& it_;
165};
166
167class HiddenApi FINAL {
168 public:
169 HiddenApi() : print_hidden_api_(false) {}
170
171 void ParseArgs(int argc, char** argv) {
172 original_argc = argc;
173 original_argv = argv;
174
175 android::base::InitLogging(argv);
176
177 // Skip over the command name.
178 argv++;
179 argc--;
180
181 if (argc == 0) {
182 Usage("No arguments specified");
183 }
184
185 for (int i = 0; i < argc; ++i) {
186 const StringPiece option(argv[i]);
187 const bool log_options = false;
188 if (log_options) {
189 LOG(INFO) << "hiddenapi: option[" << i << "]=" << argv[i];
190 }
191 if (option == "--print-hidden-api") {
192 print_hidden_api_ = true;
193 } else if (option.starts_with("--dex=")) {
194 dex_paths_.push_back(option.substr(strlen("--dex=")).ToString());
195 } else if (option.starts_with("--light-greylist=")) {
196 light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString();
197 } else if (option.starts_with("--dark-greylist=")) {
198 dark_greylist_path_ = option.substr(strlen("--dark-greylist=")).ToString();
199 } else if (option.starts_with("--blacklist=")) {
200 blacklist_path_ = option.substr(strlen("--blacklist=")).ToString();
201 } else {
202 Usage("Unknown argument '%s'", option.data());
203 }
204 }
205 }
206
207 bool ProcessDexFiles() {
208 if (dex_paths_.empty()) {
209 Usage("No DEX files specified");
210 }
211
212 if (light_greylist_path_.empty() && dark_greylist_path_.empty() && blacklist_path_.empty()) {
213 Usage("No API file specified");
214 }
215
216 if (!light_greylist_path_.empty() && !OpenApiFile(light_greylist_path_, &light_greylist_)) {
217 return false;
218 }
219
220 if (!dark_greylist_path_.empty() && !OpenApiFile(dark_greylist_path_, &dark_greylist_)) {
221 return false;
222 }
223
224 if (!blacklist_path_.empty() && !OpenApiFile(blacklist_path_, &blacklist_)) {
225 return false;
226 }
227
228 MemMap::Init();
229 if (!OpenDexFiles()) {
230 return false;
231 }
232
233 DCHECK(!dex_files_.empty());
234 for (auto& dex_file : dex_files_) {
235 CategorizeAllClasses(*dex_file.get());
236 }
237
238 UpdateDexChecksums();
239 return true;
240 }
241
242 private:
243 bool OpenApiFile(const std::string& path, std::unordered_set<std::string>* list) {
244 DCHECK(list->empty());
245 DCHECK(!path.empty());
246
247 std::ifstream api_file(path, std::ifstream::in);
248 if (api_file.fail()) {
249 LOG(ERROR) << "Unable to open file '" << path << "' " << strerror(errno);
250 return false;
251 }
252
253 for (std::string line; std::getline(api_file, line);) {
254 list->insert(line);
255 }
256
257 api_file.close();
258 return true;
259 }
260
261 bool OpenDexFiles() {
262 ArtDexFileLoader dex_loader;
263 DCHECK(dex_files_.empty());
264
265 for (const std::string& filename : dex_paths_) {
266 std::string error_msg;
267
268 File fd(filename.c_str(), O_RDWR, /* check_usage */ false);
269 if (fd.Fd() == -1) {
270 LOG(ERROR) << "Unable to open file '" << filename << "': " << strerror(errno);
271 return false;
272 }
273
274 // Memory-map the dex file with MAP_SHARED flag so that changes in memory
275 // propagate to the underlying file. We run dex file verification as if
276 // the dex file was not in boot claass path to check basic assumptions,
277 // such as that at most one of public/private/protected flag is set.
278 // We do those checks here and skip them when loading the processed file
279 // into boot class path.
280 std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
281 /* location */ filename,
282 /* verify */ true,
283 /* verify_checksum */ true,
284 /* mmap_shared */ true,
285 &error_msg));
286 if (dex_file.get() == nullptr) {
287 LOG(ERROR) << "Open failed for '" << filename << "' " << error_msg;
288 return false;
289 }
290
291 if (!dex_file->IsStandardDexFile()) {
292 LOG(ERROR) << "Expected a standard dex file '" << filename << "'";
293 return false;
294 }
295
296 // Change the protection of the memory mapping to read-write.
297 if (!dex_file->EnableWrite()) {
298 LOG(ERROR) << "Failed to enable write permission for '" << filename << "'";
299 return false;
300 }
301
302 dex_files_.push_back(std::move(dex_file));
303 }
304 return true;
305 }
306
307 void CategorizeAllClasses(const DexFile& dex_file) {
308 for (uint32_t class_idx = 0; class_idx < dex_file.NumClassDefs(); ++class_idx) {
309 DexClass klass(dex_file, class_idx);
310 const uint8_t* klass_data = klass.GetData();
311 if (klass_data == nullptr) {
312 continue;
313 }
314
315 for (ClassDataItemIterator it(klass.GetDexFile(), klass_data); it.HasNext(); it.Next()) {
316 DexMember member(klass, it);
317
318 // Catagorize member and overwrite its access flags.
319 // Note that if a member appears on multiple API lists, it will be categorized
320 // as the strictest.
321 bool is_hidden = true;
322 if (member.IsOnApiList(blacklist_)) {
David Brazdilf6a8a552018-01-15 18:10:50 +0000323 member.SetHidden(HiddenApiAccessFlags::kBlacklist);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000324 } else if (member.IsOnApiList(dark_greylist_)) {
David Brazdilf6a8a552018-01-15 18:10:50 +0000325 member.SetHidden(HiddenApiAccessFlags::kDarkGreylist);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000326 } else if (member.IsOnApiList(light_greylist_)) {
David Brazdilf6a8a552018-01-15 18:10:50 +0000327 member.SetHidden(HiddenApiAccessFlags::kLightGreylist);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000328 } else {
David Brazdilf6a8a552018-01-15 18:10:50 +0000329 member.SetHidden(HiddenApiAccessFlags::kWhitelist);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000330 is_hidden = false;
331 }
332
333 if (print_hidden_api_ && is_hidden) {
334 std::cout << member.GetApiEntry() << std::endl;
335 }
336 }
337 }
338 }
339
340 void UpdateDexChecksums() {
341 for (auto& dex_file : dex_files_) {
342 // Obtain a writeable pointer to the dex header.
343 DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
344 // Recalculate checksum and overwrite the value in the header.
345 header->checksum_ = dex_file->CalculateChecksum();
346 }
347 }
348
349 // Print signatures of APIs which have been grey-/blacklisted.
350 bool print_hidden_api_;
351
352 // Paths to DEX files which should be processed.
353 std::vector<std::string> dex_paths_;
354
355 // Paths to text files which contain the lists of API members.
356 std::string light_greylist_path_;
357 std::string dark_greylist_path_;
358 std::string blacklist_path_;
359
360 // Opened DEX files. Note that these are opened as `const` but eventually will be written into.
361 std::vector<std::unique_ptr<const DexFile>> dex_files_;
362
363 // Signatures of DEX members loaded from `light_greylist_path_`, `dark_greylist_path_`,
364 // `blacklist_path_`.
365 std::unordered_set<std::string> light_greylist_;
366 std::unordered_set<std::string> dark_greylist_;
367 std::unordered_set<std::string> blacklist_;
368};
369
370} // namespace art
371
372int main(int argc, char** argv) {
373 art::HiddenApi hiddenapi;
374
375 // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
376 hiddenapi.ParseArgs(argc, argv);
377 return hiddenapi.ProcessDexFiles() ? EXIT_SUCCESS : EXIT_FAILURE;
378}