1 /*
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 <map>
20 #include <set>
21 #include <string>
22 #include <string_view>
23
24 #include "android-base/stringprintf.h"
25 #include "android-base/strings.h"
26
27 #include "base/bit_utils.h"
28 #include "base/hiddenapi_flags.h"
29 #include "base/mem_map.h"
30 #include "base/os.h"
31 #include "base/stl_util.h"
32 #include "base/string_view_cpp20.h"
33 #include "base/unix_file/fd_file.h"
34 #include "dex/art_dex_file_loader.h"
35 #include "dex/class_accessor-inl.h"
36 #include "dex/dex_file-inl.h"
37
38 namespace art {
39 namespace hiddenapi {
40
41 const char kErrorHelp[] = "\nSee go/hiddenapi-error for help.";
42
43 static int original_argc;
44 static char** original_argv;
45
CommandLine()46 static std::string CommandLine() {
47 std::vector<std::string> command;
48 command.reserve(original_argc);
49 for (int i = 0; i < original_argc; ++i) {
50 command.push_back(original_argv[i]);
51 }
52 return android::base::Join(command, ' ');
53 }
54
UsageErrorV(const char * fmt,va_list ap)55 static void UsageErrorV(const char* fmt, va_list ap) {
56 std::string error;
57 android::base::StringAppendV(&error, fmt, ap);
58 LOG(ERROR) << error;
59 }
60
UsageError(const char * fmt,...)61 static void UsageError(const char* fmt, ...) {
62 va_list ap;
63 va_start(ap, fmt);
64 UsageErrorV(fmt, ap);
65 va_end(ap);
66 }
67
Usage(const char * fmt,...)68 NO_RETURN static void Usage(const char* fmt, ...) {
69 va_list ap;
70 va_start(ap, fmt);
71 UsageErrorV(fmt, ap);
72 va_end(ap);
73
74 UsageError("Command: %s", CommandLine().c_str());
75 UsageError("Usage: hiddenapi [command_name] [options]...");
76 UsageError("");
77 UsageError(" Command \"encode\": encode API list membership in boot dex files");
78 UsageError(" --input-dex=<filename>: dex file which belongs to boot class path");
79 UsageError(" --output-dex=<filename>: file to write encoded dex into");
80 UsageError(" input and output dex files are paired in order of appearance");
81 UsageError("");
82 UsageError(" --api-flags=<filename>:");
83 UsageError(" CSV file with signatures of methods/fields and their respective flags");
84 UsageError("");
85 UsageError(" --no-force-assign-all:");
86 UsageError(" Disable check that all dex entries have been assigned a flag");
87 UsageError("");
88 UsageError(" Command \"list\": dump lists of public and private API");
89 UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path");
90 UsageError(" --public-stub-classpath=<filenames>:");
91 UsageError(" --system-stub-classpath=<filenames>:");
92 UsageError(" --test-stub-classpath=<filenames>:");
93 UsageError(" --core-platform-stub-classpath=<filenames>:");
94 UsageError(" colon-separated list of dex/apk files which form API stubs of boot");
95 UsageError(" classpath. Multiple classpaths can be specified");
96 UsageError("");
97 UsageError(" --out-api-flags=<filename>: output file for a CSV file with API flags");
98 UsageError("");
99
100 exit(EXIT_FAILURE);
101 }
102
103 template<typename E>
Contains(const std::vector<E> & vec,const E & elem)104 static bool Contains(const std::vector<E>& vec, const E& elem) {
105 return std::find(vec.begin(), vec.end(), elem) != vec.end();
106 }
107
108 class DexClass : public ClassAccessor {
109 public:
DexClass(const ClassAccessor & accessor)110 explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {}
111
GetData() const112 const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); }
113
GetSuperclassIndex() const114 const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; }
115
HasSuperclass() const116 bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); }
117
GetSuperclassDescriptor() const118 std::string_view GetSuperclassDescriptor() const {
119 return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : "";
120 }
121
GetInterfaceDescriptors() const122 std::set<std::string_view> GetInterfaceDescriptors() const {
123 std::set<std::string_view> list;
124 const dex::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef());
125 for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) {
126 list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_));
127 }
128 return list;
129 }
130
IsPublic() const131 inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
IsInterface() const132 inline bool IsInterface() const { return HasAccessFlags(kAccInterface); }
133
Equals(const DexClass & other) const134 inline bool Equals(const DexClass& other) const {
135 bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
136
137 if (equals) {
138 LOG(FATAL) << "Class duplication: " << GetDescriptor() << " in " << dex_file_.GetLocation()
139 << " and " << other.dex_file_.GetLocation();
140 }
141
142 return equals;
143 }
144
145 private:
GetAccessFlags() const146 uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; }
HasAccessFlags(uint32_t mask) const147 bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
148
JoinStringSet(const std::set<std::string_view> & s)149 static std::string JoinStringSet(const std::set<std::string_view>& s) {
150 return "{" + ::android::base::Join(std::vector<std::string>(s.begin(), s.end()), ",") + "}";
151 }
152 };
153
154 class DexMember {
155 public:
DexMember(const DexClass & klass,const ClassAccessor::Field & item)156 DexMember(const DexClass& klass, const ClassAccessor::Field& item)
157 : klass_(klass), item_(item), is_method_(false) {
158 DCHECK_EQ(GetFieldId().class_idx_, klass.GetClassIdx());
159 }
160
DexMember(const DexClass & klass,const ClassAccessor::Method & item)161 DexMember(const DexClass& klass, const ClassAccessor::Method& item)
162 : klass_(klass), item_(item), is_method_(true) {
163 DCHECK_EQ(GetMethodId().class_idx_, klass.GetClassIdx());
164 }
165
GetDeclaringClass() const166 inline const DexClass& GetDeclaringClass() const { return klass_; }
167
IsMethod() const168 inline bool IsMethod() const { return is_method_; }
IsVirtualMethod() const169 inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); }
IsConstructor() const170 inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); }
171
IsPublicOrProtected() const172 inline bool IsPublicOrProtected() const {
173 return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected);
174 }
175
176 // Constructs a string with a unique signature of this class member.
GetApiEntry() const177 std::string GetApiEntry() const {
178 std::stringstream ss;
179 ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":")
180 << GetSignature();
181 return ss.str();
182 }
183
operator ==(const DexMember & other) const184 inline bool operator==(const DexMember& other) const {
185 // These need to match if they should resolve to one another.
186 bool equals = IsMethod() == other.IsMethod() &&
187 GetName() == other.GetName() &&
188 GetSignature() == other.GetSignature();
189
190 // Sanity checks if they do match.
191 if (equals) {
192 CHECK_EQ(IsVirtualMethod(), other.IsVirtualMethod());
193 }
194
195 return equals;
196 }
197
198 private:
GetAccessFlags() const199 inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); }
HasAccessFlags(uint32_t mask) const200 inline bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
201
GetName() const202 inline std::string_view GetName() const {
203 return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId())
204 : item_.GetDexFile().GetFieldName(GetFieldId());
205 }
206
GetSignature() const207 inline std::string GetSignature() const {
208 return IsMethod() ? item_.GetDexFile().GetMethodSignature(GetMethodId()).ToString()
209 : item_.GetDexFile().GetFieldTypeDescriptor(GetFieldId());
210 }
211
GetMethod() const212 inline const ClassAccessor::Method& GetMethod() const {
213 DCHECK(IsMethod());
214 return down_cast<const ClassAccessor::Method&>(item_);
215 }
216
GetMethodId() const217 inline const dex::MethodId& GetMethodId() const {
218 DCHECK(IsMethod());
219 return item_.GetDexFile().GetMethodId(item_.GetIndex());
220 }
221
GetFieldId() const222 inline const dex::FieldId& GetFieldId() const {
223 DCHECK(!IsMethod());
224 return item_.GetDexFile().GetFieldId(item_.GetIndex());
225 }
226
227 const DexClass& klass_;
228 const ClassAccessor::BaseItem& item_;
229 const bool is_method_;
230 };
231
232 class ClassPath final {
233 public:
ClassPath(const std::vector<std::string> & dex_paths,bool open_writable)234 ClassPath(const std::vector<std::string>& dex_paths, bool open_writable) {
235 OpenDexFiles(dex_paths, open_writable);
236 }
237
238 template<typename Fn>
ForEachDexClass(Fn fn)239 void ForEachDexClass(Fn fn) {
240 for (auto& dex_file : dex_files_) {
241 for (ClassAccessor accessor : dex_file->GetClasses()) {
242 fn(DexClass(accessor));
243 }
244 }
245 }
246
247 template<typename Fn>
ForEachDexMember(Fn fn)248 void ForEachDexMember(Fn fn) {
249 ForEachDexClass([&fn](const DexClass& klass) {
250 for (const ClassAccessor::Field& field : klass.GetFields()) {
251 fn(DexMember(klass, field));
252 }
253 for (const ClassAccessor::Method& method : klass.GetMethods()) {
254 fn(DexMember(klass, method));
255 }
256 });
257 }
258
GetDexFiles() const259 std::vector<const DexFile*> GetDexFiles() const {
260 return MakeNonOwningPointerVector(dex_files_);
261 }
262
UpdateDexChecksums()263 void UpdateDexChecksums() {
264 for (auto& dex_file : dex_files_) {
265 // Obtain a writeable pointer to the dex header.
266 DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
267 // Recalculate checksum and overwrite the value in the header.
268 header->checksum_ = dex_file->CalculateChecksum();
269 }
270 }
271
272 private:
OpenDexFiles(const std::vector<std::string> & dex_paths,bool open_writable)273 void OpenDexFiles(const std::vector<std::string>& dex_paths, bool open_writable) {
274 ArtDexFileLoader dex_loader;
275 std::string error_msg;
276
277 if (open_writable) {
278 for (const std::string& filename : dex_paths) {
279 File fd(filename.c_str(), O_RDWR, /* check_usage= */ false);
280 CHECK_NE(fd.Fd(), -1) << "Unable to open file '" << filename << "': " << strerror(errno);
281
282 // Memory-map the dex file with MAP_SHARED flag so that changes in memory
283 // propagate to the underlying file. We run dex file verification as if
284 // the dex file was not in boot claass path to check basic assumptions,
285 // such as that at most one of public/private/protected flag is set.
286 // We do those checks here and skip them when loading the processed file
287 // into boot class path.
288 std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
289 /* location= */ filename,
290 /* verify= */ true,
291 /* verify_checksum= */ true,
292 /* mmap_shared= */ true,
293 &error_msg));
294 CHECK(dex_file.get() != nullptr) << "Open failed for '" << filename << "' " << error_msg;
295 CHECK(dex_file->IsStandardDexFile()) << "Expected a standard dex file '" << filename << "'";
296 CHECK(dex_file->EnableWrite())
297 << "Failed to enable write permission for '" << filename << "'";
298 dex_files_.push_back(std::move(dex_file));
299 }
300 } else {
301 for (const std::string& filename : dex_paths) {
302 bool success = dex_loader.Open(filename.c_str(),
303 /* location= */ filename,
304 /* verify= */ true,
305 /* verify_checksum= */ true,
306 &error_msg,
307 &dex_files_);
308 CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
309 }
310 }
311 }
312
313 // Opened dex files. Note that these are opened as `const` but may be written into.
314 std::vector<std::unique_ptr<const DexFile>> dex_files_;
315 };
316
317 class HierarchyClass final {
318 public:
HierarchyClass()319 HierarchyClass() {}
320
AddDexClass(const DexClass & klass)321 void AddDexClass(const DexClass& klass) {
322 CHECK(dex_classes_.empty() || klass.Equals(dex_classes_.front()));
323 dex_classes_.push_back(klass);
324 }
325
AddExtends(HierarchyClass & parent)326 void AddExtends(HierarchyClass& parent) {
327 CHECK(!Contains(extends_, &parent));
328 CHECK(!Contains(parent.extended_by_, this));
329 extends_.push_back(&parent);
330 parent.extended_by_.push_back(this);
331 }
332
GetOneDexClass() const333 const DexClass& GetOneDexClass() const {
334 CHECK(!dex_classes_.empty());
335 return dex_classes_.front();
336 }
337
338 // See comment on Hierarchy::ForEachResolvableMember.
339 template<typename Fn>
ForEachResolvableMember(const DexMember & other,Fn fn)340 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
341 std::vector<HierarchyClass*> visited;
342 return ForEachResolvableMember_Impl(other, fn, true, true, visited);
343 }
344
345 // Returns true if this class contains at least one member matching `other`.
HasMatchingMember(const DexMember & other)346 bool HasMatchingMember(const DexMember& other) {
347 return ForEachMatchingMember(other, [](const DexMember&) { return true; });
348 }
349
350 // Recursively iterates over all subclasses of this class and invokes `fn`
351 // on each one. If `fn` returns false for a particular subclass, exploring its
352 // subclasses is skipped.
353 template<typename Fn>
ForEachSubClass(Fn fn)354 void ForEachSubClass(Fn fn) {
355 for (HierarchyClass* subclass : extended_by_) {
356 if (fn(subclass)) {
357 subclass->ForEachSubClass(fn);
358 }
359 }
360 }
361
362 private:
363 template<typename Fn>
ForEachResolvableMember_Impl(const DexMember & other,Fn fn,bool allow_explore_up,bool allow_explore_down,std::vector<HierarchyClass * > visited)364 bool ForEachResolvableMember_Impl(const DexMember& other,
365 Fn fn,
366 bool allow_explore_up,
367 bool allow_explore_down,
368 std::vector<HierarchyClass*> visited) {
369 if (std::find(visited.begin(), visited.end(), this) == visited.end()) {
370 visited.push_back(this);
371 } else {
372 return false;
373 }
374
375 // First try to find a member matching `other` in this class.
376 bool found = ForEachMatchingMember(other, fn);
377
378 // If not found, see if it is inherited from parents. Note that this will not
379 // revisit parents already in `visited`.
380 if (!found && allow_explore_up) {
381 for (HierarchyClass* superclass : extends_) {
382 found |= superclass->ForEachResolvableMember_Impl(
383 other,
384 fn,
385 /* allow_explore_up */ true,
386 /* allow_explore_down */ false,
387 visited);
388 }
389 }
390
391 // If this is a virtual method, continue exploring into subclasses so as to visit
392 // all overriding methods. Allow subclasses to explore their superclasses if this
393 // is an interface. This is needed to find implementations of this interface's
394 // methods inherited from superclasses (b/122551864).
395 if (allow_explore_down && other.IsVirtualMethod()) {
396 for (HierarchyClass* subclass : extended_by_) {
397 subclass->ForEachResolvableMember_Impl(
398 other,
399 fn,
400 /* allow_explore_up */ GetOneDexClass().IsInterface(),
401 /* allow_explore_down */ true,
402 visited);
403 }
404 }
405
406 return found;
407 }
408
409 template<typename Fn>
ForEachMatchingMember(const DexMember & other,Fn fn)410 bool ForEachMatchingMember(const DexMember& other, Fn fn) {
411 bool found = false;
412 auto compare_member = [&](const DexMember& member) {
413 // TODO(dbrazdil): Check whether class of `other` can access `member`.
414 if (member == other) {
415 found = true;
416 fn(member);
417 }
418 };
419 for (const DexClass& dex_class : dex_classes_) {
420 for (const ClassAccessor::Field& field : dex_class.GetFields()) {
421 compare_member(DexMember(dex_class, field));
422 }
423 for (const ClassAccessor::Method& method : dex_class.GetMethods()) {
424 compare_member(DexMember(dex_class, method));
425 }
426 }
427 return found;
428 }
429
430 // DexClass entries of this class found across all the provided dex files.
431 std::vector<DexClass> dex_classes_;
432
433 // Classes which this class inherits, or interfaces which it implements.
434 std::vector<HierarchyClass*> extends_;
435
436 // Classes which inherit from this class.
437 std::vector<HierarchyClass*> extended_by_;
438 };
439
440 class Hierarchy final {
441 public:
Hierarchy(ClassPath & classpath)442 explicit Hierarchy(ClassPath& classpath) : classpath_(classpath) {
443 BuildClassHierarchy();
444 }
445
446 // Perform an operation for each member of the hierarchy which could potentially
447 // be the result of method/field resolution of `other`.
448 // The function `fn` should accept a DexMember reference and return true if
449 // the member was changed. This drives a performance optimization which only
450 // visits overriding members the first time the overridden member is visited.
451 // Returns true if at least one resolvable member was found.
452 template<typename Fn>
ForEachResolvableMember(const DexMember & other,Fn fn)453 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
454 HierarchyClass* klass = FindClass(other.GetDeclaringClass().GetDescriptor());
455 return (klass != nullptr) && klass->ForEachResolvableMember(other, fn);
456 }
457
458 // Returns true if `member`, which belongs to this classpath, is visible to
459 // code in child class loaders.
IsMemberVisible(const DexMember & member)460 bool IsMemberVisible(const DexMember& member) {
461 if (!member.IsPublicOrProtected()) {
462 // Member is private or package-private. Cannot be visible.
463 return false;
464 } else if (member.GetDeclaringClass().IsPublic()) {
465 // Member is public or protected, and class is public. It must be visible.
466 return true;
467 } else if (member.IsConstructor()) {
468 // Member is public or protected constructor and class is not public.
469 // Must be hidden because it cannot be implicitly exposed by a subclass.
470 return false;
471 } else {
472 // Member is public or protected method, but class is not public. Check if
473 // it is exposed through a public subclass.
474 // Example code (`foo` exposed by ClassB):
475 // class ClassA { public void foo() { ... } }
476 // public class ClassB extends ClassA {}
477 HierarchyClass* klass = FindClass(member.GetDeclaringClass().GetDescriptor());
478 CHECK(klass != nullptr);
479 bool visible = false;
480 klass->ForEachSubClass([&visible, &member](HierarchyClass* subclass) {
481 if (subclass->HasMatchingMember(member)) {
482 // There is a member which matches `member` in `subclass`, either
483 // a virtual method overriding `member` or a field overshadowing
484 // `member`. In either case, `member` remains hidden.
485 CHECK(member.IsVirtualMethod() || !member.IsMethod());
486 return false; // do not explore deeper
487 } else if (subclass->GetOneDexClass().IsPublic()) {
488 // `subclass` inherits and exposes `member`.
489 visible = true;
490 return false; // do not explore deeper
491 } else {
492 // `subclass` inherits `member` but does not expose it.
493 return true; // explore deeper
494 }
495 });
496 return visible;
497 }
498 }
499
500 private:
FindClass(const std::string_view & descriptor)501 HierarchyClass* FindClass(const std::string_view& descriptor) {
502 auto it = classes_.find(descriptor);
503 if (it == classes_.end()) {
504 return nullptr;
505 } else {
506 return &it->second;
507 }
508 }
509
BuildClassHierarchy()510 void BuildClassHierarchy() {
511 // Create one HierarchyClass entry in `classes_` per class descriptor
512 // and add all DexClass objects with the same descriptor to that entry.
513 classpath_.ForEachDexClass([this](const DexClass& klass) {
514 classes_[klass.GetDescriptor()].AddDexClass(klass);
515 });
516
517 // Connect each HierarchyClass to its successors and predecessors.
518 for (auto& entry : classes_) {
519 HierarchyClass& klass = entry.second;
520 const DexClass& dex_klass = klass.GetOneDexClass();
521
522 if (!dex_klass.HasSuperclass()) {
523 CHECK(dex_klass.GetInterfaceDescriptors().empty())
524 << "java/lang/Object should not implement any interfaces";
525 continue;
526 }
527
528 HierarchyClass* superclass = FindClass(dex_klass.GetSuperclassDescriptor());
529 CHECK(superclass != nullptr)
530 << "Superclass " << dex_klass.GetSuperclassDescriptor()
531 << " of class " << dex_klass.GetDescriptor() << " from dex file \""
532 << dex_klass.GetDexFile().GetLocation() << "\" was not found. "
533 << "Either the superclass is missing or it appears later in the classpath spec.";
534 klass.AddExtends(*superclass);
535
536 for (const std::string_view& iface_desc : dex_klass.GetInterfaceDescriptors()) {
537 HierarchyClass* iface = FindClass(iface_desc);
538 CHECK(iface != nullptr);
539 klass.AddExtends(*iface);
540 }
541 }
542 }
543
544 ClassPath& classpath_;
545 std::map<std::string_view, HierarchyClass> classes_;
546 };
547
548 // Builder of dex section containing hiddenapi flags.
549 class HiddenapiClassDataBuilder final {
550 public:
HiddenapiClassDataBuilder(const DexFile & dex_file)551 explicit HiddenapiClassDataBuilder(const DexFile& dex_file)
552 : num_classdefs_(dex_file.NumClassDefs()),
553 next_class_def_idx_(0u),
554 class_def_has_non_zero_flags_(false),
555 dex_file_has_non_zero_flags_(false),
556 data_(sizeof(uint32_t) * (num_classdefs_ + 1), 0u) {
557 *GetSizeField() = GetCurrentDataSize();
558 }
559
560 // Notify the builder that new flags for the next class def
561 // will be written now. The builder records the current offset
562 // into the header.
BeginClassDef(uint32_t idx)563 void BeginClassDef(uint32_t idx) {
564 CHECK_EQ(next_class_def_idx_, idx);
565 CHECK_LT(idx, num_classdefs_);
566 GetOffsetArray()[idx] = GetCurrentDataSize();
567 class_def_has_non_zero_flags_ = false;
568 }
569
570 // Notify the builder that all flags for this class def have been
571 // written. The builder updates the total size of the data struct
572 // and may set offset for class def in header to zero if no data
573 // has been written.
EndClassDef(uint32_t idx)574 void EndClassDef(uint32_t idx) {
575 CHECK_EQ(next_class_def_idx_, idx);
576 CHECK_LT(idx, num_classdefs_);
577
578 ++next_class_def_idx_;
579
580 if (!class_def_has_non_zero_flags_) {
581 // No need to store flags for this class. Remove the written flags
582 // and set offset in header to zero.
583 data_.resize(GetOffsetArray()[idx]);
584 GetOffsetArray()[idx] = 0u;
585 }
586
587 dex_file_has_non_zero_flags_ |= class_def_has_non_zero_flags_;
588
589 if (idx == num_classdefs_ - 1) {
590 if (dex_file_has_non_zero_flags_) {
591 // This was the last class def and we have generated non-zero hiddenapi
592 // flags. Update total size in the header.
593 *GetSizeField() = GetCurrentDataSize();
594 } else {
595 // This was the last class def and we have not generated any non-zero
596 // hiddenapi flags. Clear all the data.
597 data_.clear();
598 }
599 }
600 }
601
602 // Append flags at the end of the data struct. This should be called
603 // between BeginClassDef and EndClassDef in the order of appearance of
604 // fields/methods in the class data stream.
WriteFlags(const ApiList & flags)605 void WriteFlags(const ApiList& flags) {
606 uint32_t dex_flags = flags.GetDexFlags();
607 EncodeUnsignedLeb128(&data_, dex_flags);
608 class_def_has_non_zero_flags_ |= (dex_flags != 0u);
609 }
610
611 // Return backing data, assuming that all flags have been written.
GetData() const612 const std::vector<uint8_t>& GetData() const {
613 CHECK_EQ(next_class_def_idx_, num_classdefs_) << "Incomplete data";
614 return data_;
615 }
616
617 private:
618 // Returns pointer to the size field in the header of this dex section.
GetSizeField()619 uint32_t* GetSizeField() {
620 // Assume malloc() aligns allocated memory to at least uint32_t.
621 CHECK(IsAligned<sizeof(uint32_t)>(data_.data()));
622 return reinterpret_cast<uint32_t*>(data_.data());
623 }
624
625 // Returns pointer to array of offsets (indexed by class def indices) in the
626 // header of this dex section.
GetOffsetArray()627 uint32_t* GetOffsetArray() { return &GetSizeField()[1]; }
GetCurrentDataSize() const628 uint32_t GetCurrentDataSize() const { return data_.size(); }
629
630 // Number of class defs in this dex file.
631 const uint32_t num_classdefs_;
632
633 // Next expected class def index.
634 uint32_t next_class_def_idx_;
635
636 // Whether non-zero flags have been encountered for this class def.
637 bool class_def_has_non_zero_flags_;
638
639 // Whether any non-zero flags have been encountered for this dex file.
640 bool dex_file_has_non_zero_flags_;
641
642 // Vector containing the data of the built data structure.
643 std::vector<uint8_t> data_;
644 };
645
646 // Edits a dex file, inserting a new HiddenapiClassData section.
647 class DexFileEditor final {
648 public:
DexFileEditor(const DexFile & old_dex,const std::vector<uint8_t> & hiddenapi_class_data)649 DexFileEditor(const DexFile& old_dex, const std::vector<uint8_t>& hiddenapi_class_data)
650 : old_dex_(old_dex),
651 hiddenapi_class_data_(hiddenapi_class_data),
652 loaded_dex_header_(nullptr),
653 loaded_dex_maplist_(nullptr) {}
654
655 // Copies dex file into a backing data vector, appends the given HiddenapiClassData
656 // and updates the MapList.
Encode()657 void Encode() {
658 // We do not support non-standard dex encodings, e.g. compact dex.
659 CHECK(old_dex_.IsStandardDexFile());
660
661 // If there are no data to append, copy the old dex file and return.
662 if (hiddenapi_class_data_.empty()) {
663 AllocateMemory(old_dex_.Size());
664 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
665 return;
666 }
667
668 // Find the old MapList, find its size.
669 const dex::MapList* old_map = old_dex_.GetMapList();
670 CHECK_LT(old_map->size_, std::numeric_limits<uint32_t>::max());
671
672 // Compute the size of the new dex file. We append the HiddenapiClassData,
673 // one MapItem and possibly some padding to align the new MapList.
674 CHECK(IsAligned<kMapListAlignment>(old_dex_.Size()))
675 << "End of input dex file is not 4-byte aligned, possibly because its MapList is not "
676 << "at the end of the file.";
677 size_t size_delta =
678 RoundUp(hiddenapi_class_data_.size(), kMapListAlignment) + sizeof(dex::MapItem);
679 size_t new_size = old_dex_.Size() + size_delta;
680 AllocateMemory(new_size);
681
682 // Copy the old dex file into the backing data vector. Load the copied
683 // dex file to obtain pointers to its header and MapList.
684 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
685 ReloadDex(/* verify= */ false);
686
687 // Truncate the new dex file before the old MapList. This assumes that
688 // the MapList is the last entry in the dex file. This is currently true
689 // for our tooling.
690 // TODO: Implement the general case by zero-ing the old MapList (turning
691 // it into padding.
692 RemoveOldMapList();
693
694 // Append HiddenapiClassData.
695 size_t payload_offset = AppendHiddenapiClassData();
696
697 // Wrute new MapList with an entry for HiddenapiClassData.
698 CreateMapListWithNewItem(payload_offset);
699
700 // Check that the pre-computed size matches the actual size.
701 CHECK_EQ(offset_, new_size);
702
703 // Reload to all data structures.
704 ReloadDex(/* verify= */ false);
705
706 // Update the dex checksum.
707 UpdateChecksum();
708
709 // Run DexFileVerifier on the new dex file as a CHECK.
710 ReloadDex(/* verify= */ true);
711 }
712
713 // Writes the edited dex file into a file.
WriteTo(const std::string & path)714 void WriteTo(const std::string& path) {
715 CHECK(!data_.empty());
716 std::ofstream ofs(path.c_str(), std::ofstream::out | std::ofstream::binary);
717 ofs.write(reinterpret_cast<const char*>(data_.data()), data_.size());
718 ofs.flush();
719 CHECK(ofs.good());
720 ofs.close();
721 }
722
723 private:
724 static constexpr size_t kMapListAlignment = 4u;
725 static constexpr size_t kHiddenapiClassDataAlignment = 4u;
726
ReloadDex(bool verify)727 void ReloadDex(bool verify) {
728 std::string error_msg;
729 DexFileLoader loader;
730 loaded_dex_ = loader.Open(
731 data_.data(),
732 data_.size(),
733 "test_location",
734 old_dex_.GetLocationChecksum(),
735 /* oat_dex_file= */ nullptr,
736 /* verify= */ verify,
737 /* verify_checksum= */ verify,
738 &error_msg);
739 if (loaded_dex_.get() == nullptr) {
740 LOG(FATAL) << "Failed to load edited dex file: " << error_msg;
741 UNREACHABLE();
742 }
743
744 // Load the location of header and map list before we start editing the file.
745 loaded_dex_header_ = const_cast<DexFile::Header*>(&loaded_dex_->GetHeader());
746 loaded_dex_maplist_ = const_cast<dex::MapList*>(loaded_dex_->GetMapList());
747 }
748
GetHeader() const749 DexFile::Header& GetHeader() const {
750 CHECK(loaded_dex_header_ != nullptr);
751 return *loaded_dex_header_;
752 }
753
GetMapList() const754 dex::MapList& GetMapList() const {
755 CHECK(loaded_dex_maplist_ != nullptr);
756 return *loaded_dex_maplist_;
757 }
758
AllocateMemory(size_t total_size)759 void AllocateMemory(size_t total_size) {
760 data_.clear();
761 data_.resize(total_size);
762 CHECK(IsAligned<kMapListAlignment>(data_.data()));
763 CHECK(IsAligned<kHiddenapiClassDataAlignment>(data_.data()));
764 offset_ = 0;
765 }
766
GetCurrentDataPtr()767 uint8_t* GetCurrentDataPtr() {
768 return data_.data() + offset_;
769 }
770
UpdateDataSize(off_t delta,bool update_header)771 void UpdateDataSize(off_t delta, bool update_header) {
772 offset_ += delta;
773 if (update_header) {
774 DexFile::Header& header = GetHeader();
775 header.file_size_ += delta;
776 header.data_size_ += delta;
777 }
778 }
779
780 template<typename T>
Append(const T * src,size_t len,bool update_header=true)781 T* Append(const T* src, size_t len, bool update_header = true) {
782 CHECK_LE(offset_ + len, data_.size());
783 uint8_t* dst = GetCurrentDataPtr();
784 memcpy(dst, src, len);
785 UpdateDataSize(len, update_header);
786 return reinterpret_cast<T*>(dst);
787 }
788
InsertPadding(size_t alignment)789 void InsertPadding(size_t alignment) {
790 size_t len = RoundUp(offset_, alignment) - offset_;
791 std::vector<uint8_t> padding(len, 0);
792 Append(padding.data(), padding.size());
793 }
794
RemoveOldMapList()795 void RemoveOldMapList() {
796 size_t map_size = GetMapList().Size();
797 uint8_t* map_start = reinterpret_cast<uint8_t*>(&GetMapList());
798 CHECK_EQ(map_start + map_size, GetCurrentDataPtr()) << "MapList not at the end of dex file";
799 UpdateDataSize(-static_cast<off_t>(map_size), /* update_header= */ true);
800 CHECK_EQ(map_start, GetCurrentDataPtr());
801 loaded_dex_maplist_ = nullptr; // do not use this map list any more
802 }
803
CreateMapListWithNewItem(size_t payload_offset)804 void CreateMapListWithNewItem(size_t payload_offset) {
805 InsertPadding(/* alignment= */ kMapListAlignment);
806
807 size_t new_map_offset = offset_;
808 dex::MapList* map = Append(old_dex_.GetMapList(), old_dex_.GetMapList()->Size());
809
810 // Check last map entry is a pointer to itself.
811 dex::MapItem& old_item = map->list_[map->size_ - 1];
812 CHECK(old_item.type_ == DexFile::kDexTypeMapList);
813 CHECK_EQ(old_item.size_, 1u);
814 CHECK_EQ(old_item.offset_, GetHeader().map_off_);
815
816 // Create a new MapItem entry with new MapList details.
817 dex::MapItem new_item;
818 new_item.type_ = old_item.type_;
819 new_item.unused_ = 0u; // initialize to ensure dex output is deterministic (b/119308882)
820 new_item.size_ = old_item.size_;
821 new_item.offset_ = new_map_offset;
822
823 // Update pointer in the header.
824 GetHeader().map_off_ = new_map_offset;
825
826 // Append a new MapItem and return its pointer.
827 map->size_++;
828 Append(&new_item, sizeof(dex::MapItem));
829
830 // Change penultimate entry to point to metadata.
831 old_item.type_ = DexFile::kDexTypeHiddenapiClassData;
832 old_item.size_ = 1u; // there is only one section
833 old_item.offset_ = payload_offset;
834 }
835
AppendHiddenapiClassData()836 size_t AppendHiddenapiClassData() {
837 size_t payload_offset = offset_;
838 CHECK_EQ(kMapListAlignment, kHiddenapiClassDataAlignment);
839 CHECK(IsAligned<kHiddenapiClassDataAlignment>(payload_offset))
840 << "Should not need to align the section, previous data was already aligned";
841 Append(hiddenapi_class_data_.data(), hiddenapi_class_data_.size());
842 return payload_offset;
843 }
844
UpdateChecksum()845 void UpdateChecksum() {
846 GetHeader().checksum_ = loaded_dex_->CalculateChecksum();
847 }
848
849 const DexFile& old_dex_;
850 const std::vector<uint8_t>& hiddenapi_class_data_;
851
852 std::vector<uint8_t> data_;
853 size_t offset_;
854
855 std::unique_ptr<const DexFile> loaded_dex_;
856 DexFile::Header* loaded_dex_header_;
857 dex::MapList* loaded_dex_maplist_;
858 };
859
860 class HiddenApi final {
861 public:
HiddenApi()862 HiddenApi() : force_assign_all_(true) {}
863
Run(int argc,char ** argv)864 void Run(int argc, char** argv) {
865 switch (ParseArgs(argc, argv)) {
866 case Command::kEncode:
867 EncodeAccessFlags();
868 break;
869 case Command::kList:
870 ListApi();
871 break;
872 }
873 }
874
875 private:
876 enum class Command {
877 kEncode,
878 kList,
879 };
880
ParseArgs(int argc,char ** argv)881 Command ParseArgs(int argc, char** argv) {
882 // Skip over the binary's path.
883 argv++;
884 argc--;
885
886 if (argc > 0) {
887 const char* raw_command = argv[0];
888 const std::string_view command(raw_command);
889 if (command == "encode") {
890 for (int i = 1; i < argc; ++i) {
891 const char* raw_option = argv[i];
892 const std::string_view option(raw_option);
893 if (StartsWith(option, "--input-dex=")) {
894 boot_dex_paths_.push_back(std::string(option.substr(strlen("--input-dex="))));
895 } else if (StartsWith(option, "--output-dex=")) {
896 output_dex_paths_.push_back(std::string(option.substr(strlen("--output-dex="))));
897 } else if (StartsWith(option, "--api-flags=")) {
898 api_flags_path_ = std::string(option.substr(strlen("--api-flags=")));
899 } else if (option == "--no-force-assign-all") {
900 force_assign_all_ = false;
901 } else {
902 Usage("Unknown argument '%s'", raw_option);
903 }
904 }
905 return Command::kEncode;
906 } else if (command == "list") {
907 for (int i = 1; i < argc; ++i) {
908 const char* raw_option = argv[i];
909 const std::string_view option(raw_option);
910 if (StartsWith(option, "--boot-dex=")) {
911 boot_dex_paths_.push_back(std::string(option.substr(strlen("--boot-dex="))));
912 } else if (StartsWith(option, "--public-stub-classpath=")) {
913 stub_classpaths_.push_back(std::make_pair(
914 std::string(option.substr(strlen("--public-stub-classpath="))),
915 ApiStubs::Kind::kPublicApi));
916 } else if (StartsWith(option, "--system-stub-classpath=")) {
917 stub_classpaths_.push_back(std::make_pair(
918 std::string(option.substr(strlen("--system-stub-classpath="))),
919 ApiStubs::Kind::kSystemApi));
920 } else if (StartsWith(option, "--test-stub-classpath=")) {
921 stub_classpaths_.push_back(std::make_pair(
922 std::string(option.substr(strlen("--test-stub-classpath="))),
923 ApiStubs::Kind::kTestApi));
924 } else if (StartsWith(option, "--core-platform-stub-classpath=")) {
925 stub_classpaths_.push_back(std::make_pair(
926 std::string(option.substr(strlen("--core-platform-stub-classpath="))),
927 ApiStubs::Kind::kCorePlatformApi));
928 } else if (StartsWith(option, "--out-api-flags=")) {
929 api_flags_path_ = std::string(option.substr(strlen("--out-api-flags=")));
930 } else {
931 Usage("Unknown argument '%s'", raw_option);
932 }
933 }
934 return Command::kList;
935 } else {
936 Usage("Unknown command '%s'", raw_command);
937 }
938 } else {
939 Usage("No command specified");
940 }
941 }
942
EncodeAccessFlags()943 void EncodeAccessFlags() {
944 if (boot_dex_paths_.empty()) {
945 Usage("No input DEX files specified");
946 } else if (output_dex_paths_.size() != boot_dex_paths_.size()) {
947 Usage("Number of input DEX files does not match number of output DEX files");
948 }
949
950 // Load dex signatures.
951 std::map<std::string, ApiList> api_list = OpenApiFile(api_flags_path_);
952
953 // Iterate over input dex files and insert HiddenapiClassData sections.
954 for (size_t i = 0; i < boot_dex_paths_.size(); ++i) {
955 const std::string& input_path = boot_dex_paths_[i];
956 const std::string& output_path = output_dex_paths_[i];
957
958 ClassPath boot_classpath({ input_path }, /* open_writable= */ false);
959 std::vector<const DexFile*> input_dex_files = boot_classpath.GetDexFiles();
960 CHECK_EQ(input_dex_files.size(), 1u);
961 const DexFile& input_dex = *input_dex_files[0];
962
963 HiddenapiClassDataBuilder builder(input_dex);
964 boot_classpath.ForEachDexClass([&](const DexClass& boot_class) {
965 builder.BeginClassDef(boot_class.GetClassDefIndex());
966 if (boot_class.GetData() != nullptr) {
967 auto fn_shared = [&](const DexMember& boot_member) {
968 auto it = api_list.find(boot_member.GetApiEntry());
969 bool api_list_found = (it != api_list.end());
970 CHECK(!force_assign_all_ || api_list_found)
971 << "Could not find hiddenapi flags for dex entry: " << boot_member.GetApiEntry();
972 builder.WriteFlags(api_list_found ? it->second : ApiList::Whitelist());
973 };
974 auto fn_field = [&](const ClassAccessor::Field& boot_field) {
975 fn_shared(DexMember(boot_class, boot_field));
976 };
977 auto fn_method = [&](const ClassAccessor::Method& boot_method) {
978 fn_shared(DexMember(boot_class, boot_method));
979 };
980 boot_class.VisitFieldsAndMethods(fn_field, fn_field, fn_method, fn_method);
981 }
982 builder.EndClassDef(boot_class.GetClassDefIndex());
983 });
984
985 DexFileEditor dex_editor(input_dex, builder.GetData());
986 dex_editor.Encode();
987 dex_editor.WriteTo(output_path);
988 }
989 }
990
OpenApiFile(const std::string & path)991 std::map<std::string, ApiList> OpenApiFile(const std::string& path) {
992 CHECK(!path.empty());
993 std::ifstream api_file(path, std::ifstream::in);
994 CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno);
995
996 std::map<std::string, ApiList> api_flag_map;
997
998 size_t line_number = 1;
999 for (std::string line; std::getline(api_file, line); line_number++) {
1000 // Every line contains a comma separated list with the signature as the
1001 // first element and the api flags as the rest
1002 std::vector<std::string> values = android::base::Split(line, ",");
1003 CHECK_GT(values.size(), 1u) << path << ":" << line_number
1004 << ": No flags found: " << line << kErrorHelp;
1005
1006 const std::string& signature = values[0];
1007
1008 CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number
1009 << ": Duplicate entry: " << signature << kErrorHelp;
1010
1011 ApiList membership;
1012
1013 bool success = ApiList::FromNames(values.begin() + 1, values.end(), &membership);
1014 CHECK(success) << path << ":" << line_number
1015 << ": Some flags were not recognized: " << line << kErrorHelp;
1016 CHECK(membership.IsValid()) << path << ":" << line_number
1017 << ": Invalid combination of flags: " << line << kErrorHelp;
1018
1019 api_flag_map.emplace(signature, membership);
1020 }
1021
1022 api_file.close();
1023 return api_flag_map;
1024 }
1025
ListApi()1026 void ListApi() {
1027 if (boot_dex_paths_.empty()) {
1028 Usage("No boot DEX files specified");
1029 } else if (stub_classpaths_.empty()) {
1030 Usage("No stub DEX files specified");
1031 } else if (api_flags_path_.empty()) {
1032 Usage("No output path specified");
1033 }
1034
1035 // Complete list of boot class path members. The associated boolean states
1036 // whether it is public (true) or private (false).
1037 std::map<std::string, std::set<std::string_view>> boot_members;
1038
1039 // Deduplicate errors before printing them.
1040 std::set<std::string> unresolved;
1041
1042 // Open all dex files.
1043 ClassPath boot_classpath(boot_dex_paths_, /* open_writable= */ false);
1044 Hierarchy boot_hierarchy(boot_classpath);
1045
1046 // Mark all boot dex members private.
1047 boot_classpath.ForEachDexMember([&](const DexMember& boot_member) {
1048 boot_members[boot_member.GetApiEntry()] = {};
1049 });
1050
1051 // Resolve each SDK dex member against the framework and mark it white.
1052 for (const auto& cp_entry : stub_classpaths_) {
1053 ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"),
1054 /* open_writable= */ false);
1055 Hierarchy stub_hierarchy(stub_classpath);
1056 const ApiStubs::Kind stub_api = cp_entry.second;
1057
1058 stub_classpath.ForEachDexMember(
1059 [&](const DexMember& stub_member) {
1060 if (!stub_hierarchy.IsMemberVisible(stub_member)) {
1061 // Typically fake constructors and inner-class `this` fields.
1062 return;
1063 }
1064 bool resolved = boot_hierarchy.ForEachResolvableMember(
1065 stub_member,
1066 [&](const DexMember& boot_member) {
1067 std::string entry = boot_member.GetApiEntry();
1068 auto it = boot_members.find(entry);
1069 CHECK(it != boot_members.end());
1070 it->second.insert(ApiStubs::ToString(stub_api));
1071 });
1072 if (!resolved) {
1073 unresolved.insert(stub_member.GetApiEntry());
1074 }
1075 });
1076 }
1077
1078 // Print errors.
1079 for (const std::string& str : unresolved) {
1080 LOG(WARNING) << "unresolved: " << str;
1081 }
1082
1083 // Write into public/private API files.
1084 std::ofstream file_flags(api_flags_path_.c_str());
1085 for (const auto& entry : boot_members) {
1086 if (entry.second.empty()) {
1087 file_flags << entry.first << std::endl;
1088 } else {
1089 file_flags << entry.first << ",";
1090 file_flags << android::base::Join(entry.second, ",") << std::endl;
1091 }
1092 }
1093 file_flags.close();
1094 }
1095
1096 // Whether to check that all dex entries have been assigned flags.
1097 // Defaults to true.
1098 bool force_assign_all_;
1099
1100 // Paths to DEX files which should be processed.
1101 std::vector<std::string> boot_dex_paths_;
1102
1103 // Output paths where modified DEX files should be written.
1104 std::vector<std::string> output_dex_paths_;
1105
1106 // Set of public API stub classpaths. Each classpath is formed by a list
1107 // of DEX/APK files in the order they appear on the classpath.
1108 std::vector<std::pair<std::string, ApiStubs::Kind>> stub_classpaths_;
1109
1110 // Path to CSV file containing the list of API members and their flags.
1111 // This could be both an input and output path.
1112 std::string api_flags_path_;
1113 };
1114
1115 } // namespace hiddenapi
1116 } // namespace art
1117
main(int argc,char ** argv)1118 int main(int argc, char** argv) {
1119 art::hiddenapi::original_argc = argc;
1120 art::hiddenapi::original_argv = argv;
1121 android::base::InitLogging(argv);
1122 art::MemMap::Init();
1123 art::hiddenapi::HiddenApi().Run(argc, argv);
1124 return EXIT_SUCCESS;
1125 }
1126