blob: a755fdb40bdfe842985d80db04ee1122ecd8ce9a [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()) {
121 ptr = ReverseSearchUnsignedLeb128(ptr, it_.GetMethodCodeItemOffset());
122 }
123 ptr = ReverseSearchUnsignedLeb128(ptr, old_flags);
124
125 // Overwrite the access flags.
126 UpdateUnsignedLeb128(const_cast<uint8_t*>(ptr), new_flags);
127 }
128
129 // Returns true if this member's API entry is in `list`.
130 bool IsOnApiList(const std::unordered_set<std::string>& list) const {
131 return list.find(GetApiEntry()) != list.end();
132 }
133
134 // Constructs a string with a unique signature of this class member.
135 std::string GetApiEntry() const {
136 std::stringstream ss;
137 ss << klass_.GetDescriptor() << "->";
138 if (it_.IsAtMethod()) {
139 const DexFile::MethodId& mid = GetMethodId();
140 ss << klass_.GetDexFile().GetMethodName(mid)
141 << klass_.GetDexFile().GetMethodSignature(mid).ToString();
142 } else {
143 const DexFile::FieldId& fid = GetFieldId();
144 ss << klass_.GetDexFile().GetFieldName(fid) << ":"
145 << klass_.GetDexFile().GetFieldTypeDescriptor(fid);
146 }
147 return ss.str();
148 }
149
150 private:
151 inline const DexFile::MethodId& GetMethodId() const {
152 DCHECK(it_.IsAtMethod());
153 return klass_.GetDexFile().GetMethodId(it_.GetMemberIndex());
154 }
155
156 inline const DexFile::FieldId& GetFieldId() const {
157 DCHECK(!it_.IsAtMethod());
158 return klass_.GetDexFile().GetFieldId(it_.GetMemberIndex());
159 }
160
161 static inline bool IsLeb128Terminator(const uint8_t* ptr) {
162 return *ptr <= 0x7f;
163 }
164
165 // Returns the first byte of a Leb128 value assuming that:
166 // (1) `end_ptr` points to the first byte after the Leb128 value, and
167 // (2) there is another Leb128 value before this one.
168 // The function will fail after reading 5 bytes (the longest supported Leb128
169 // encoding) to protect against situations when (2) is not satisfied.
170 // When a Leb128 value is discovered, it is decoded and CHECKed against `value`.
171 static const uint8_t* ReverseSearchUnsignedLeb128(const uint8_t* end_ptr, uint32_t expected) {
172 const uint8_t* ptr = end_ptr;
173
174 // Move one byte back, check that this is the terminating byte.
175 ptr--;
176 CHECK(IsLeb128Terminator(ptr));
177
178 // Keep moving back while the previous byte is not a terminating byte.
179 // Fail after reading five bytes in case there isn't another Leb128 value
180 // before this one.
181 while (!IsLeb128Terminator(ptr - 1)) {
182 ptr--;
183 CHECK_LE((size_t) (end_ptr - ptr), 5u);
184 }
185
186 // Check that the decoded value matches the `expected` value.
187 const uint8_t* tmp_ptr = ptr;
188 CHECK_EQ(DecodeUnsignedLeb128(&tmp_ptr), expected);
189
190 return ptr;
191 }
192
193 const DexClass& klass_;
194 const ClassDataItemIterator& it_;
195};
196
197class HiddenApi FINAL {
198 public:
199 HiddenApi() : print_hidden_api_(false) {}
200
201 void ParseArgs(int argc, char** argv) {
202 original_argc = argc;
203 original_argv = argv;
204
205 android::base::InitLogging(argv);
206
207 // Skip over the command name.
208 argv++;
209 argc--;
210
211 if (argc == 0) {
212 Usage("No arguments specified");
213 }
214
215 for (int i = 0; i < argc; ++i) {
216 const StringPiece option(argv[i]);
217 const bool log_options = false;
218 if (log_options) {
219 LOG(INFO) << "hiddenapi: option[" << i << "]=" << argv[i];
220 }
221 if (option == "--print-hidden-api") {
222 print_hidden_api_ = true;
223 } else if (option.starts_with("--dex=")) {
224 dex_paths_.push_back(option.substr(strlen("--dex=")).ToString());
225 } else if (option.starts_with("--light-greylist=")) {
226 light_greylist_path_ = option.substr(strlen("--light-greylist=")).ToString();
227 } else if (option.starts_with("--dark-greylist=")) {
228 dark_greylist_path_ = option.substr(strlen("--dark-greylist=")).ToString();
229 } else if (option.starts_with("--blacklist=")) {
230 blacklist_path_ = option.substr(strlen("--blacklist=")).ToString();
231 } else {
232 Usage("Unknown argument '%s'", option.data());
233 }
234 }
235 }
236
237 bool ProcessDexFiles() {
238 if (dex_paths_.empty()) {
239 Usage("No DEX files specified");
240 }
241
242 if (light_greylist_path_.empty() && dark_greylist_path_.empty() && blacklist_path_.empty()) {
243 Usage("No API file specified");
244 }
245
246 if (!light_greylist_path_.empty() && !OpenApiFile(light_greylist_path_, &light_greylist_)) {
247 return false;
248 }
249
250 if (!dark_greylist_path_.empty() && !OpenApiFile(dark_greylist_path_, &dark_greylist_)) {
251 return false;
252 }
253
254 if (!blacklist_path_.empty() && !OpenApiFile(blacklist_path_, &blacklist_)) {
255 return false;
256 }
257
258 MemMap::Init();
259 if (!OpenDexFiles()) {
260 return false;
261 }
262
263 DCHECK(!dex_files_.empty());
264 for (auto& dex_file : dex_files_) {
265 CategorizeAllClasses(*dex_file.get());
266 }
267
268 UpdateDexChecksums();
269 return true;
270 }
271
272 private:
273 bool OpenApiFile(const std::string& path, std::unordered_set<std::string>* list) {
274 DCHECK(list->empty());
275 DCHECK(!path.empty());
276
277 std::ifstream api_file(path, std::ifstream::in);
278 if (api_file.fail()) {
279 LOG(ERROR) << "Unable to open file '" << path << "' " << strerror(errno);
280 return false;
281 }
282
283 for (std::string line; std::getline(api_file, line);) {
284 list->insert(line);
285 }
286
287 api_file.close();
288 return true;
289 }
290
291 bool OpenDexFiles() {
292 ArtDexFileLoader dex_loader;
293 DCHECK(dex_files_.empty());
294
295 for (const std::string& filename : dex_paths_) {
296 std::string error_msg;
297
298 File fd(filename.c_str(), O_RDWR, /* check_usage */ false);
299 if (fd.Fd() == -1) {
300 LOG(ERROR) << "Unable to open file '" << filename << "': " << strerror(errno);
301 return false;
302 }
303
304 // Memory-map the dex file with MAP_SHARED flag so that changes in memory
305 // propagate to the underlying file. We run dex file verification as if
306 // the dex file was not in boot claass path to check basic assumptions,
307 // such as that at most one of public/private/protected flag is set.
308 // We do those checks here and skip them when loading the processed file
309 // into boot class path.
310 std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
311 /* location */ filename,
312 /* verify */ true,
313 /* verify_checksum */ true,
314 /* mmap_shared */ true,
315 &error_msg));
316 if (dex_file.get() == nullptr) {
317 LOG(ERROR) << "Open failed for '" << filename << "' " << error_msg;
318 return false;
319 }
320
321 if (!dex_file->IsStandardDexFile()) {
322 LOG(ERROR) << "Expected a standard dex file '" << filename << "'";
323 return false;
324 }
325
326 // Change the protection of the memory mapping to read-write.
327 if (!dex_file->EnableWrite()) {
328 LOG(ERROR) << "Failed to enable write permission for '" << filename << "'";
329 return false;
330 }
331
332 dex_files_.push_back(std::move(dex_file));
333 }
334 return true;
335 }
336
337 void CategorizeAllClasses(const DexFile& dex_file) {
338 for (uint32_t class_idx = 0; class_idx < dex_file.NumClassDefs(); ++class_idx) {
339 DexClass klass(dex_file, class_idx);
340 const uint8_t* klass_data = klass.GetData();
341 if (klass_data == nullptr) {
342 continue;
343 }
344
345 for (ClassDataItemIterator it(klass.GetDexFile(), klass_data); it.HasNext(); it.Next()) {
346 DexMember member(klass, it);
347
348 // Catagorize member and overwrite its access flags.
349 // Note that if a member appears on multiple API lists, it will be categorized
350 // as the strictest.
351 bool is_hidden = true;
352 if (member.IsOnApiList(blacklist_)) {
David Brazdilf6a8a552018-01-15 18:10:50 +0000353 member.SetHidden(HiddenApiAccessFlags::kBlacklist);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000354 } else if (member.IsOnApiList(dark_greylist_)) {
David Brazdilf6a8a552018-01-15 18:10:50 +0000355 member.SetHidden(HiddenApiAccessFlags::kDarkGreylist);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000356 } else if (member.IsOnApiList(light_greylist_)) {
David Brazdilf6a8a552018-01-15 18:10:50 +0000357 member.SetHidden(HiddenApiAccessFlags::kLightGreylist);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000358 } else {
David Brazdilf6a8a552018-01-15 18:10:50 +0000359 member.SetHidden(HiddenApiAccessFlags::kWhitelist);
David Brazdil2b9c35b2018-01-12 15:44:43 +0000360 is_hidden = false;
361 }
362
363 if (print_hidden_api_ && is_hidden) {
364 std::cout << member.GetApiEntry() << std::endl;
365 }
366 }
367 }
368 }
369
370 void UpdateDexChecksums() {
371 for (auto& dex_file : dex_files_) {
372 // Obtain a writeable pointer to the dex header.
373 DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
374 // Recalculate checksum and overwrite the value in the header.
375 header->checksum_ = dex_file->CalculateChecksum();
376 }
377 }
378
379 // Print signatures of APIs which have been grey-/blacklisted.
380 bool print_hidden_api_;
381
382 // Paths to DEX files which should be processed.
383 std::vector<std::string> dex_paths_;
384
385 // Paths to text files which contain the lists of API members.
386 std::string light_greylist_path_;
387 std::string dark_greylist_path_;
388 std::string blacklist_path_;
389
390 // Opened DEX files. Note that these are opened as `const` but eventually will be written into.
391 std::vector<std::unique_ptr<const DexFile>> dex_files_;
392
393 // Signatures of DEX members loaded from `light_greylist_path_`, `dark_greylist_path_`,
394 // `blacklist_path_`.
395 std::unordered_set<std::string> light_greylist_;
396 std::unordered_set<std::string> dark_greylist_;
397 std::unordered_set<std::string> blacklist_;
398};
399
400} // namespace art
401
402int main(int argc, char** argv) {
403 art::HiddenApi hiddenapi;
404
405 // Parse arguments. Argument mistakes will lead to exit(EXIT_FAILURE) in UsageError.
406 hiddenapi.ParseArgs(argc, argv);
407 return hiddenapi.ProcessDexFiles() ? EXIT_SUCCESS : EXIT_FAILURE;
408}