blob: 7954fa47c5a8dbeabc71420d216d3221dd360eef [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>
David Brazdil0b6de0c2018-06-28 11:56:41 +010019#include <map>
20#include <set>
Vladimir Markoe5125562019-02-06 17:38:26 +000021#include <string>
22#include <string_view>
David Brazdil2b9c35b2018-01-12 15:44:43 +000023
24#include "android-base/stringprintf.h"
25#include "android-base/strings.h"
26
David Brazdil20c765f2018-10-27 21:45:15 +000027#include "base/bit_utils.h"
David Brazdildcfa89b2018-10-31 11:04:10 +000028#include "base/hiddenapi_flags.h"
David Sehr79e26072018-04-06 17:58:50 -070029#include "base/mem_map.h"
David Sehrc431b9d2018-03-02 12:01:51 -080030#include "base/os.h"
David Brazdildcfa89b2018-10-31 11:04:10 +000031#include "base/stl_util.h"
Vladimir Markoe5125562019-02-06 17:38:26 +000032#include "base/string_view_cpp20.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000033#include "base/unix_file/fd_file.h"
34#include "dex/art_dex_file_loader.h"
Mathieu Chartier396dc082018-08-06 12:29:57 -070035#include "dex/class_accessor-inl.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000036#include "dex/dex_file-inl.h"
David Brazdil2b9c35b2018-01-12 15:44:43 +000037
38namespace art {
David Brazdil62a4bcf2018-12-13 17:00:06 +000039namespace hiddenapi {
David Brazdil2b9c35b2018-01-12 15:44:43 +000040
Mathew Inwoodb62f6f12019-01-07 14:02:52 +000041const char kErrorHelp[] = "\nSee go/hiddenapi-error for help.";
42
David Brazdil2b9c35b2018-01-12 15:44:43 +000043static int original_argc;
44static char** original_argv;
45
46static std::string CommandLine() {
47 std::vector<std::string> command;
Andreas Gampe2a487eb2018-11-19 11:41:22 -080048 command.reserve(original_argc);
David Brazdil2b9c35b2018-01-12 15:44:43 +000049 for (int i = 0; i < original_argc; ++i) {
50 command.push_back(original_argv[i]);
51 }
52 return android::base::Join(command, ' ');
53}
54
55static 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
61static void UsageError(const char* fmt, ...) {
62 va_list ap;
63 va_start(ap, fmt);
64 UsageErrorV(fmt, ap);
65 va_end(ap);
66}
67
68NO_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());
David Brazdil003e64b2018-06-27 13:20:52 +010075 UsageError("Usage: hiddenapi [command_name] [options]...");
David Brazdil2b9c35b2018-01-12 15:44:43 +000076 UsageError("");
David Brazdil003e64b2018-06-27 13:20:52 +010077 UsageError(" Command \"encode\": encode API list membership in boot dex files");
David Brazdil20c765f2018-10-27 21:45:15 +000078 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");
David Brazdil2b9c35b2018-01-12 15:44:43 +000081 UsageError("");
David Brazdil91690d32018-11-04 18:07:23 +000082 UsageError(" --api-flags=<filename>:");
83 UsageError(" CSV file with signatures of methods/fields and their respective flags");
84 UsageError("");
Andrei Oneaa6b3b292021-05-19 16:22:46 +010085 UsageError(" --max-hiddenapi-level=<max-target-*>:");
86 UsageError(" the maximum hidden api level for APIs. If an API was originally restricted");
87 UsageError(" to a newer sdk, turn it into a regular unsupported API instead.");
88 UsageError(" instead. The full list of valid values is in hiddenapi_flags.h");
89 UsageError("");
David Brazdil91690d32018-11-04 18:07:23 +000090 UsageError(" --no-force-assign-all:");
91 UsageError(" Disable check that all dex entries have been assigned a flag");
David Brazdil2b9c35b2018-01-12 15:44:43 +000092 UsageError("");
David Brazdil0b6de0c2018-06-28 11:56:41 +010093 UsageError(" Command \"list\": dump lists of public and private API");
Paul Duffin6d8d68e2021-05-17 10:55:53 +010094 UsageError(" --dependency-stub-dex=<filename>: dex file containing API stubs provided");
95 UsageError(" by other parts of the bootclasspath. These are used to resolve");
96 UsageError(" dependencies in dex files specified in --boot-dex but do not appear in");
97 UsageError(" the output");
David Brazdil0b6de0c2018-06-28 11:56:41 +010098 UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path");
David Brazdil62a4bcf2018-12-13 17:00:06 +000099 UsageError(" --public-stub-classpath=<filenames>:");
Andrei Onea370a0642019-03-01 17:48:27 +0000100 UsageError(" --system-stub-classpath=<filenames>:");
101 UsageError(" --test-stub-classpath=<filenames>:");
David Brazdil90faceb2018-12-14 14:36:15 +0000102 UsageError(" --core-platform-stub-classpath=<filenames>:");
David Brazdil62a4bcf2018-12-13 17:00:06 +0000103 UsageError(" colon-separated list of dex/apk files which form API stubs of boot");
104 UsageError(" classpath. Multiple classpaths can be specified");
David Brazdil0b6de0c2018-06-28 11:56:41 +0100105 UsageError("");
David Brazdil62a4bcf2018-12-13 17:00:06 +0000106 UsageError(" --out-api-flags=<filename>: output file for a CSV file with API flags");
Paul Duffin4abe8f72021-06-24 23:00:08 +0100107 UsageError(" --fragment: the input is only a fragment of the whole bootclasspath and may");
108 UsageError(" not include a complete set of classes. That requires the tool to ignore");
109 UsageError(" missing classes and members. Specify --verbose to see the warnings.");
110 UsageError(" --verbose: output all warnings, even when --fragment is specified.");
David Brazdil0b6de0c2018-06-28 11:56:41 +0100111 UsageError("");
David Brazdil2b9c35b2018-01-12 15:44:43 +0000112
113 exit(EXIT_FAILURE);
114}
115
David Brazdil0b6de0c2018-06-28 11:56:41 +0100116template<typename E>
117static bool Contains(const std::vector<E>& vec, const E& elem) {
118 return std::find(vec.begin(), vec.end(), elem) != vec.end();
119}
120
Mathieu Chartier396dc082018-08-06 12:29:57 -0700121class DexClass : public ClassAccessor {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000122 public:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700123 explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {}
David Brazdil2b9c35b2018-01-12 15:44:43 +0000124
Mathieu Chartier396dc082018-08-06 12:29:57 -0700125 const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000126
Mathieu Chartier396dc082018-08-06 12:29:57 -0700127 const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100128
129 bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); }
130
Vladimir Markoae1d2c82019-02-18 13:40:44 +0000131 std::string_view GetSuperclassDescriptor() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700132 return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : "";
David Brazdil0b6de0c2018-06-28 11:56:41 +0100133 }
134
Vladimir Markoae1d2c82019-02-18 13:40:44 +0000135 std::set<std::string_view> GetInterfaceDescriptors() const {
136 std::set<std::string_view> list;
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800137 const dex::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef());
David Brazdil0b6de0c2018-06-28 11:56:41 +0100138 for (uint32_t i = 0; ifaces != nullptr && i < ifaces->Size(); ++i) {
139 list.insert(dex_file_.StringByTypeIdx(ifaces->GetTypeItem(i).type_idx_));
140 }
141 return list;
142 }
143
David Brazdil345c0ed2018-08-03 10:26:44 +0100144 inline bool IsPublic() const { return HasAccessFlags(kAccPublic); }
David Brazdil2da3cbb2019-01-30 16:17:50 +0000145 inline bool IsInterface() const { return HasAccessFlags(kAccInterface); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100146
147 inline bool Equals(const DexClass& other) const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700148 bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0;
David Brazdil95779c92019-01-24 09:59:08 +0000149
David Brazdil0b6de0c2018-06-28 11:56:41 +0100150 if (equals) {
David Brazdil95779c92019-01-24 09:59:08 +0000151 LOG(FATAL) << "Class duplication: " << GetDescriptor() << " in " << dex_file_.GetLocation()
152 << " and " << other.dex_file_.GetLocation();
David Brazdil0b6de0c2018-06-28 11:56:41 +0100153 }
David Brazdil95779c92019-01-24 09:59:08 +0000154
David Brazdil0b6de0c2018-06-28 11:56:41 +0100155 return equals;
156 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000157
158 private:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700159 uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100160 bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
David Brazdil1ff5a652019-01-18 11:44:44 +0000161
Vladimir Markoae1d2c82019-02-18 13:40:44 +0000162 static std::string JoinStringSet(const std::set<std::string_view>& s) {
David Brazdil1ff5a652019-01-18 11:44:44 +0000163 return "{" + ::android::base::Join(std::vector<std::string>(s.begin(), s.end()), ",") + "}";
164 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000165};
166
167class DexMember {
168 public:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700169 DexMember(const DexClass& klass, const ClassAccessor::Field& item)
170 : klass_(klass), item_(item), is_method_(false) {
171 DCHECK_EQ(GetFieldId().class_idx_, klass.GetClassIdx());
172 }
173
174 DexMember(const DexClass& klass, const ClassAccessor::Method& item)
175 : klass_(klass), item_(item), is_method_(true) {
176 DCHECK_EQ(GetMethodId().class_idx_, klass.GetClassIdx());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000177 }
178
David Brazdil0b6de0c2018-06-28 11:56:41 +0100179 inline const DexClass& GetDeclaringClass() const { return klass_; }
180
Mathieu Chartier396dc082018-08-06 12:29:57 -0700181 inline bool IsMethod() const { return is_method_; }
182 inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); }
David Brazdil345c0ed2018-08-03 10:26:44 +0100183 inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100184
David Brazdil345c0ed2018-08-03 10:26:44 +0100185 inline bool IsPublicOrProtected() const {
186 return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100187 }
188
David Brazdil2b9c35b2018-01-12 15:44:43 +0000189 // Constructs a string with a unique signature of this class member.
190 std::string GetApiEntry() const {
191 std::stringstream ss;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700192 ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":")
193 << GetSignature();
David Brazdil2b9c35b2018-01-12 15:44:43 +0000194 return ss.str();
195 }
196
Mathieu Chartier396dc082018-08-06 12:29:57 -0700197 inline bool operator==(const DexMember& other) const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100198 // These need to match if they should resolve to one another.
199 bool equals = IsMethod() == other.IsMethod() &&
200 GetName() == other.GetName() &&
201 GetSignature() == other.GetSignature();
202
Orion Hodson2d455202020-07-28 16:22:10 +0100203 // Soundness check that they do match.
David Brazdil0b6de0c2018-06-28 11:56:41 +0100204 if (equals) {
205 CHECK_EQ(IsVirtualMethod(), other.IsVirtualMethod());
206 }
207
208 return equals;
209 }
210
David Brazdil2b9c35b2018-01-12 15:44:43 +0000211 private:
Mathieu Chartier396dc082018-08-06 12:29:57 -0700212 inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); }
Andreas Gampe7c5acbb2018-09-20 13:54:52 -0700213 inline bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100214
Vladimir Markoae1d2c82019-02-18 13:40:44 +0000215 inline std::string_view GetName() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700216 return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId())
217 : item_.GetDexFile().GetFieldName(GetFieldId());
David Brazdil0b6de0c2018-06-28 11:56:41 +0100218 }
219
220 inline std::string GetSignature() const {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700221 return IsMethod() ? item_.GetDexFile().GetMethodSignature(GetMethodId()).ToString()
222 : item_.GetDexFile().GetFieldTypeDescriptor(GetFieldId());
223 }
224
225 inline const ClassAccessor::Method& GetMethod() const {
226 DCHECK(IsMethod());
227 return down_cast<const ClassAccessor::Method&>(item_);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100228 }
229
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800230 inline const dex::MethodId& GetMethodId() const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100231 DCHECK(IsMethod());
Mathieu Chartier396dc082018-08-06 12:29:57 -0700232 return item_.GetDexFile().GetMethodId(item_.GetIndex());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000233 }
234
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800235 inline const dex::FieldId& GetFieldId() const {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100236 DCHECK(!IsMethod());
Mathieu Chartier396dc082018-08-06 12:29:57 -0700237 return item_.GetDexFile().GetFieldId(item_.GetIndex());
David Brazdil2b9c35b2018-01-12 15:44:43 +0000238 }
239
David Brazdil2b9c35b2018-01-12 15:44:43 +0000240 const DexClass& klass_;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700241 const ClassAccessor::BaseItem& item_;
242 const bool is_method_;
David Brazdil2b9c35b2018-01-12 15:44:43 +0000243};
244
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100245class ClassPath final {
David Brazdil2b9c35b2018-01-12 15:44:43 +0000246 public:
Paul Duffin496b9b42021-05-17 12:23:01 +0100247 ClassPath(const std::vector<std::string>& dex_paths, bool open_writable, bool ignore_empty) {
248 OpenDexFiles(dex_paths, open_writable, ignore_empty);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100249 }
250
251 template<typename Fn>
252 void ForEachDexClass(Fn fn) {
253 for (auto& dex_file : dex_files_) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700254 for (ClassAccessor accessor : dex_file->GetClasses()) {
255 fn(DexClass(accessor));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100256 }
257 }
David Brazdil2b9c35b2018-01-12 15:44:43 +0000258 }
259
David Brazdil003e64b2018-06-27 13:20:52 +0100260 template<typename Fn>
261 void ForEachDexMember(Fn fn) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700262 ForEachDexClass([&fn](const DexClass& klass) {
263 for (const ClassAccessor::Field& field : klass.GetFields()) {
264 fn(DexMember(klass, field));
265 }
266 for (const ClassAccessor::Method& method : klass.GetMethods()) {
267 fn(DexMember(klass, method));
David Brazdil2b9c35b2018-01-12 15:44:43 +0000268 }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100269 });
David Brazdil2b9c35b2018-01-12 15:44:43 +0000270 }
271
David Brazdil20c765f2018-10-27 21:45:15 +0000272 std::vector<const DexFile*> GetDexFiles() const {
273 return MakeNonOwningPointerVector(dex_files_);
274 }
275
David Brazdil2b9c35b2018-01-12 15:44:43 +0000276 void UpdateDexChecksums() {
277 for (auto& dex_file : dex_files_) {
278 // Obtain a writeable pointer to the dex header.
279 DexFile::Header* header = const_cast<DexFile::Header*>(&dex_file->GetHeader());
280 // Recalculate checksum and overwrite the value in the header.
281 header->checksum_ = dex_file->CalculateChecksum();
282 }
283 }
284
David Brazdil003e64b2018-06-27 13:20:52 +0100285 private:
Paul Duffin496b9b42021-05-17 12:23:01 +0100286 void OpenDexFiles(const std::vector<std::string>& dex_paths,
287 bool open_writable,
288 bool ignore_empty) {
David Brazdil003e64b2018-06-27 13:20:52 +0100289 ArtDexFileLoader dex_loader;
290 std::string error_msg;
David Brazdil003e64b2018-06-27 13:20:52 +0100291
David Brazdil0b6de0c2018-06-28 11:56:41 +0100292 if (open_writable) {
293 for (const std::string& filename : dex_paths) {
Andreas Gampe9b031f72018-10-04 11:03:34 -0700294 File fd(filename.c_str(), O_RDWR, /* check_usage= */ false);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100295 CHECK_NE(fd.Fd(), -1) << "Unable to open file '" << filename << "': " << strerror(errno);
296
297 // Memory-map the dex file with MAP_SHARED flag so that changes in memory
298 // propagate to the underlying file. We run dex file verification as if
299 // the dex file was not in boot claass path to check basic assumptions,
300 // such as that at most one of public/private/protected flag is set.
301 // We do those checks here and skip them when loading the processed file
302 // into boot class path.
303 std::unique_ptr<const DexFile> dex_file(dex_loader.OpenDex(fd.Release(),
Andreas Gampe9b031f72018-10-04 11:03:34 -0700304 /* location= */ filename,
305 /* verify= */ true,
306 /* verify_checksum= */ true,
307 /* mmap_shared= */ true,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100308 &error_msg));
309 CHECK(dex_file.get() != nullptr) << "Open failed for '" << filename << "' " << error_msg;
310 CHECK(dex_file->IsStandardDexFile()) << "Expected a standard dex file '" << filename << "'";
311 CHECK(dex_file->EnableWrite())
312 << "Failed to enable write permission for '" << filename << "'";
313 dex_files_.push_back(std::move(dex_file));
314 }
315 } else {
316 for (const std::string& filename : dex_paths) {
317 bool success = dex_loader.Open(filename.c_str(),
Andreas Gampe9b031f72018-10-04 11:03:34 -0700318 /* location= */ filename,
319 /* verify= */ true,
320 /* verify_checksum= */ true,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100321 &error_msg,
322 &dex_files_);
Paul Duffin496b9b42021-05-17 12:23:01 +0100323 // If requested ignore a jar with no classes.dex files.
324 if (!success && ignore_empty && error_msg != "Entry not found") {
325 CHECK(success) << "Open failed for '" << filename << "' " << error_msg;
326 }
David Brazdil0b6de0c2018-06-28 11:56:41 +0100327 }
David Brazdil003e64b2018-06-27 13:20:52 +0100328 }
329 }
330
David Brazdil0b6de0c2018-06-28 11:56:41 +0100331 // Opened dex files. Note that these are opened as `const` but may be written into.
David Brazdil003e64b2018-06-27 13:20:52 +0100332 std::vector<std::unique_ptr<const DexFile>> dex_files_;
333};
334
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100335class HierarchyClass final {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100336 public:
337 HierarchyClass() {}
338
339 void AddDexClass(const DexClass& klass) {
340 CHECK(dex_classes_.empty() || klass.Equals(dex_classes_.front()));
341 dex_classes_.push_back(klass);
342 }
343
344 void AddExtends(HierarchyClass& parent) {
345 CHECK(!Contains(extends_, &parent));
346 CHECK(!Contains(parent.extended_by_, this));
347 extends_.push_back(&parent);
348 parent.extended_by_.push_back(this);
349 }
350
351 const DexClass& GetOneDexClass() const {
352 CHECK(!dex_classes_.empty());
353 return dex_classes_.front();
354 }
355
356 // See comment on Hierarchy::ForEachResolvableMember.
357 template<typename Fn>
358 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
David Brazdil2da3cbb2019-01-30 16:17:50 +0000359 std::vector<HierarchyClass*> visited;
360 return ForEachResolvableMember_Impl(other, fn, true, true, visited);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100361 }
362
David Brazdil345c0ed2018-08-03 10:26:44 +0100363 // Returns true if this class contains at least one member matching `other`.
364 bool HasMatchingMember(const DexMember& other) {
David Brazdil2da3cbb2019-01-30 16:17:50 +0000365 return ForEachMatchingMember(other, [](const DexMember&) { return true; });
David Brazdil345c0ed2018-08-03 10:26:44 +0100366 }
367
368 // Recursively iterates over all subclasses of this class and invokes `fn`
369 // on each one. If `fn` returns false for a particular subclass, exploring its
370 // subclasses is skipped.
371 template<typename Fn>
372 void ForEachSubClass(Fn fn) {
373 for (HierarchyClass* subclass : extended_by_) {
374 if (fn(subclass)) {
375 subclass->ForEachSubClass(fn);
376 }
377 }
378 }
379
David Brazdil0b6de0c2018-06-28 11:56:41 +0100380 private:
David Brazdil0b6de0c2018-06-28 11:56:41 +0100381 template<typename Fn>
David Brazdil2da3cbb2019-01-30 16:17:50 +0000382 bool ForEachResolvableMember_Impl(const DexMember& other,
383 Fn fn,
384 bool allow_explore_up,
385 bool allow_explore_down,
386 std::vector<HierarchyClass*> visited) {
387 if (std::find(visited.begin(), visited.end(), this) == visited.end()) {
388 visited.push_back(this);
389 } else {
390 return false;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100391 }
392
David Brazdil2da3cbb2019-01-30 16:17:50 +0000393 // First try to find a member matching `other` in this class.
394 bool found = ForEachMatchingMember(other, fn);
395
396 // If not found, see if it is inherited from parents. Note that this will not
397 // revisit parents already in `visited`.
398 if (!found && allow_explore_up) {
399 for (HierarchyClass* superclass : extends_) {
400 found |= superclass->ForEachResolvableMember_Impl(
401 other,
402 fn,
403 /* allow_explore_up */ true,
404 /* allow_explore_down */ false,
405 visited);
406 }
407 }
408
409 // If this is a virtual method, continue exploring into subclasses so as to visit
410 // all overriding methods. Allow subclasses to explore their superclasses if this
411 // is an interface. This is needed to find implementations of this interface's
412 // methods inherited from superclasses (b/122551864).
413 if (allow_explore_down && other.IsVirtualMethod()) {
414 for (HierarchyClass* subclass : extended_by_) {
415 subclass->ForEachResolvableMember_Impl(
416 other,
417 fn,
418 /* allow_explore_up */ GetOneDexClass().IsInterface(),
419 /* allow_explore_down */ true,
420 visited);
421 }
422 }
423
424 return found;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100425 }
426
427 template<typename Fn>
David Brazdil2da3cbb2019-01-30 16:17:50 +0000428 bool ForEachMatchingMember(const DexMember& other, Fn fn) {
429 bool found = false;
Mathieu Chartier396dc082018-08-06 12:29:57 -0700430 auto compare_member = [&](const DexMember& member) {
David Brazdil2da3cbb2019-01-30 16:17:50 +0000431 // TODO(dbrazdil): Check whether class of `other` can access `member`.
Mathieu Chartier396dc082018-08-06 12:29:57 -0700432 if (member == other) {
David Brazdil2da3cbb2019-01-30 16:17:50 +0000433 found = true;
434 fn(member);
Mathieu Chartier396dc082018-08-06 12:29:57 -0700435 }
436 };
David Brazdil0b6de0c2018-06-28 11:56:41 +0100437 for (const DexClass& dex_class : dex_classes_) {
Mathieu Chartier396dc082018-08-06 12:29:57 -0700438 for (const ClassAccessor::Field& field : dex_class.GetFields()) {
439 compare_member(DexMember(dex_class, field));
440 }
441 for (const ClassAccessor::Method& method : dex_class.GetMethods()) {
442 compare_member(DexMember(dex_class, method));
David Brazdil0b6de0c2018-06-28 11:56:41 +0100443 }
444 }
445 return found;
446 }
447
David Brazdil0b6de0c2018-06-28 11:56:41 +0100448 // DexClass entries of this class found across all the provided dex files.
449 std::vector<DexClass> dex_classes_;
450
451 // Classes which this class inherits, or interfaces which it implements.
452 std::vector<HierarchyClass*> extends_;
453
454 // Classes which inherit from this class.
455 std::vector<HierarchyClass*> extended_by_;
456};
457
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100458class Hierarchy final {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100459 public:
Paul Duffin4abe8f72021-06-24 23:00:08 +0100460 Hierarchy(ClassPath& classpath, bool fragment, bool verbose) : classpath_(classpath) {
461 BuildClassHierarchy(fragment, verbose);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100462 }
463
464 // Perform an operation for each member of the hierarchy which could potentially
465 // be the result of method/field resolution of `other`.
466 // The function `fn` should accept a DexMember reference and return true if
467 // the member was changed. This drives a performance optimization which only
468 // visits overriding members the first time the overridden member is visited.
469 // Returns true if at least one resolvable member was found.
470 template<typename Fn>
471 bool ForEachResolvableMember(const DexMember& other, Fn fn) {
472 HierarchyClass* klass = FindClass(other.GetDeclaringClass().GetDescriptor());
473 return (klass != nullptr) && klass->ForEachResolvableMember(other, fn);
474 }
475
David Brazdil345c0ed2018-08-03 10:26:44 +0100476 // Returns true if `member`, which belongs to this classpath, is visible to
477 // code in child class loaders.
478 bool IsMemberVisible(const DexMember& member) {
479 if (!member.IsPublicOrProtected()) {
480 // Member is private or package-private. Cannot be visible.
481 return false;
482 } else if (member.GetDeclaringClass().IsPublic()) {
483 // Member is public or protected, and class is public. It must be visible.
484 return true;
485 } else if (member.IsConstructor()) {
486 // Member is public or protected constructor and class is not public.
487 // Must be hidden because it cannot be implicitly exposed by a subclass.
488 return false;
489 } else {
490 // Member is public or protected method, but class is not public. Check if
491 // it is exposed through a public subclass.
492 // Example code (`foo` exposed by ClassB):
493 // class ClassA { public void foo() { ... } }
494 // public class ClassB extends ClassA {}
495 HierarchyClass* klass = FindClass(member.GetDeclaringClass().GetDescriptor());
496 CHECK(klass != nullptr);
497 bool visible = false;
498 klass->ForEachSubClass([&visible, &member](HierarchyClass* subclass) {
499 if (subclass->HasMatchingMember(member)) {
500 // There is a member which matches `member` in `subclass`, either
501 // a virtual method overriding `member` or a field overshadowing
502 // `member`. In either case, `member` remains hidden.
503 CHECK(member.IsVirtualMethod() || !member.IsMethod());
504 return false; // do not explore deeper
505 } else if (subclass->GetOneDexClass().IsPublic()) {
506 // `subclass` inherits and exposes `member`.
507 visible = true;
508 return false; // do not explore deeper
509 } else {
510 // `subclass` inherits `member` but does not expose it.
511 return true; // explore deeper
512 }
513 });
514 return visible;
515 }
516 }
517
David Brazdil0b6de0c2018-06-28 11:56:41 +0100518 private:
Vladimir Markoae1d2c82019-02-18 13:40:44 +0000519 HierarchyClass* FindClass(const std::string_view& descriptor) {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100520 auto it = classes_.find(descriptor);
521 if (it == classes_.end()) {
522 return nullptr;
523 } else {
524 return &it->second;
525 }
526 }
527
Paul Duffin4abe8f72021-06-24 23:00:08 +0100528 void BuildClassHierarchy(bool fragment, bool verbose) {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100529 // Create one HierarchyClass entry in `classes_` per class descriptor
530 // and add all DexClass objects with the same descriptor to that entry.
Mathieu Chartier396dc082018-08-06 12:29:57 -0700531 classpath_.ForEachDexClass([this](const DexClass& klass) {
David Brazdil0b6de0c2018-06-28 11:56:41 +0100532 classes_[klass.GetDescriptor()].AddDexClass(klass);
533 });
534
535 // Connect each HierarchyClass to its successors and predecessors.
536 for (auto& entry : classes_) {
537 HierarchyClass& klass = entry.second;
538 const DexClass& dex_klass = klass.GetOneDexClass();
539
540 if (!dex_klass.HasSuperclass()) {
541 CHECK(dex_klass.GetInterfaceDescriptors().empty())
542 << "java/lang/Object should not implement any interfaces";
543 continue;
544 }
545
Anton Hanssonfdb81da2020-09-22 09:28:58 +0100546 auto add_extends = [&](const std::string_view& extends_desc) {
547 HierarchyClass* extends = FindClass(extends_desc);
Paul Duffin4abe8f72021-06-24 23:00:08 +0100548 if (extends != nullptr) {
549 klass.AddExtends(*extends);
550 } else if (!fragment || verbose) {
551 auto severity = verbose ? ::android::base::WARNING : ::android::base::FATAL;
552 LOG(severity)
553 << "Superclass/interface " << extends_desc
554 << " of class " << dex_klass.GetDescriptor() << " from dex file \""
555 << dex_klass.GetDexFile().GetLocation() << "\" was not found. "
556 << "Either it is missing or it appears later in the classpath spec.";
557 }
Anton Hanssonfdb81da2020-09-22 09:28:58 +0100558 };
David Brazdil0b6de0c2018-06-28 11:56:41 +0100559
Anton Hanssonfdb81da2020-09-22 09:28:58 +0100560 add_extends(dex_klass.GetSuperclassDescriptor());
Vladimir Markoae1d2c82019-02-18 13:40:44 +0000561 for (const std::string_view& iface_desc : dex_klass.GetInterfaceDescriptors()) {
Anton Hanssonfdb81da2020-09-22 09:28:58 +0100562 add_extends(iface_desc);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100563 }
564 }
565 }
566
David Brazdil345c0ed2018-08-03 10:26:44 +0100567 ClassPath& classpath_;
Vladimir Markoae1d2c82019-02-18 13:40:44 +0000568 std::map<std::string_view, HierarchyClass> classes_;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100569};
570
David Brazdil20c765f2018-10-27 21:45:15 +0000571// Builder of dex section containing hiddenapi flags.
572class HiddenapiClassDataBuilder final {
573 public:
574 explicit HiddenapiClassDataBuilder(const DexFile& dex_file)
575 : num_classdefs_(dex_file.NumClassDefs()),
576 next_class_def_idx_(0u),
577 class_def_has_non_zero_flags_(false),
578 dex_file_has_non_zero_flags_(false),
579 data_(sizeof(uint32_t) * (num_classdefs_ + 1), 0u) {
580 *GetSizeField() = GetCurrentDataSize();
581 }
582
583 // Notify the builder that new flags for the next class def
584 // will be written now. The builder records the current offset
585 // into the header.
586 void BeginClassDef(uint32_t idx) {
587 CHECK_EQ(next_class_def_idx_, idx);
588 CHECK_LT(idx, num_classdefs_);
589 GetOffsetArray()[idx] = GetCurrentDataSize();
590 class_def_has_non_zero_flags_ = false;
591 }
592
593 // Notify the builder that all flags for this class def have been
594 // written. The builder updates the total size of the data struct
595 // and may set offset for class def in header to zero if no data
596 // has been written.
597 void EndClassDef(uint32_t idx) {
598 CHECK_EQ(next_class_def_idx_, idx);
599 CHECK_LT(idx, num_classdefs_);
600
601 ++next_class_def_idx_;
602
603 if (!class_def_has_non_zero_flags_) {
604 // No need to store flags for this class. Remove the written flags
605 // and set offset in header to zero.
606 data_.resize(GetOffsetArray()[idx]);
607 GetOffsetArray()[idx] = 0u;
608 }
609
610 dex_file_has_non_zero_flags_ |= class_def_has_non_zero_flags_;
611
612 if (idx == num_classdefs_ - 1) {
613 if (dex_file_has_non_zero_flags_) {
614 // This was the last class def and we have generated non-zero hiddenapi
615 // flags. Update total size in the header.
616 *GetSizeField() = GetCurrentDataSize();
617 } else {
618 // This was the last class def and we have not generated any non-zero
619 // hiddenapi flags. Clear all the data.
620 data_.clear();
621 }
622 }
623 }
624
625 // Append flags at the end of the data struct. This should be called
626 // between BeginClassDef and EndClassDef in the order of appearance of
627 // fields/methods in the class data stream.
David Brazdil90faceb2018-12-14 14:36:15 +0000628 void WriteFlags(const ApiList& flags) {
629 uint32_t dex_flags = flags.GetDexFlags();
630 EncodeUnsignedLeb128(&data_, dex_flags);
631 class_def_has_non_zero_flags_ |= (dex_flags != 0u);
David Brazdil20c765f2018-10-27 21:45:15 +0000632 }
633
634 // Return backing data, assuming that all flags have been written.
635 const std::vector<uint8_t>& GetData() const {
636 CHECK_EQ(next_class_def_idx_, num_classdefs_) << "Incomplete data";
637 return data_;
638 }
639
640 private:
641 // Returns pointer to the size field in the header of this dex section.
642 uint32_t* GetSizeField() {
643 // Assume malloc() aligns allocated memory to at least uint32_t.
644 CHECK(IsAligned<sizeof(uint32_t)>(data_.data()));
645 return reinterpret_cast<uint32_t*>(data_.data());
646 }
647
648 // Returns pointer to array of offsets (indexed by class def indices) in the
649 // header of this dex section.
650 uint32_t* GetOffsetArray() { return &GetSizeField()[1]; }
651 uint32_t GetCurrentDataSize() const { return data_.size(); }
652
653 // Number of class defs in this dex file.
654 const uint32_t num_classdefs_;
655
656 // Next expected class def index.
657 uint32_t next_class_def_idx_;
658
659 // Whether non-zero flags have been encountered for this class def.
660 bool class_def_has_non_zero_flags_;
661
662 // Whether any non-zero flags have been encountered for this dex file.
663 bool dex_file_has_non_zero_flags_;
664
665 // Vector containing the data of the built data structure.
666 std::vector<uint8_t> data_;
667};
668
669// Edits a dex file, inserting a new HiddenapiClassData section.
670class DexFileEditor final {
671 public:
672 DexFileEditor(const DexFile& old_dex, const std::vector<uint8_t>& hiddenapi_class_data)
673 : old_dex_(old_dex),
674 hiddenapi_class_data_(hiddenapi_class_data),
675 loaded_dex_header_(nullptr),
676 loaded_dex_maplist_(nullptr) {}
677
678 // Copies dex file into a backing data vector, appends the given HiddenapiClassData
679 // and updates the MapList.
680 void Encode() {
681 // We do not support non-standard dex encodings, e.g. compact dex.
682 CHECK(old_dex_.IsStandardDexFile());
683
684 // If there are no data to append, copy the old dex file and return.
685 if (hiddenapi_class_data_.empty()) {
686 AllocateMemory(old_dex_.Size());
687 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
688 return;
689 }
690
691 // Find the old MapList, find its size.
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800692 const dex::MapList* old_map = old_dex_.GetMapList();
David Brazdil20c765f2018-10-27 21:45:15 +0000693 CHECK_LT(old_map->size_, std::numeric_limits<uint32_t>::max());
694
695 // Compute the size of the new dex file. We append the HiddenapiClassData,
696 // one MapItem and possibly some padding to align the new MapList.
697 CHECK(IsAligned<kMapListAlignment>(old_dex_.Size()))
698 << "End of input dex file is not 4-byte aligned, possibly because its MapList is not "
699 << "at the end of the file.";
700 size_t size_delta =
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800701 RoundUp(hiddenapi_class_data_.size(), kMapListAlignment) + sizeof(dex::MapItem);
David Brazdil20c765f2018-10-27 21:45:15 +0000702 size_t new_size = old_dex_.Size() + size_delta;
703 AllocateMemory(new_size);
704
705 // Copy the old dex file into the backing data vector. Load the copied
706 // dex file to obtain pointers to its header and MapList.
707 Append(old_dex_.Begin(), old_dex_.Size(), /* update_header= */ false);
708 ReloadDex(/* verify= */ false);
709
710 // Truncate the new dex file before the old MapList. This assumes that
711 // the MapList is the last entry in the dex file. This is currently true
712 // for our tooling.
713 // TODO: Implement the general case by zero-ing the old MapList (turning
714 // it into padding.
715 RemoveOldMapList();
716
717 // Append HiddenapiClassData.
718 size_t payload_offset = AppendHiddenapiClassData();
719
720 // Wrute new MapList with an entry for HiddenapiClassData.
721 CreateMapListWithNewItem(payload_offset);
722
723 // Check that the pre-computed size matches the actual size.
724 CHECK_EQ(offset_, new_size);
725
726 // Reload to all data structures.
727 ReloadDex(/* verify= */ false);
728
729 // Update the dex checksum.
730 UpdateChecksum();
731
732 // Run DexFileVerifier on the new dex file as a CHECK.
733 ReloadDex(/* verify= */ true);
734 }
735
736 // Writes the edited dex file into a file.
737 void WriteTo(const std::string& path) {
738 CHECK(!data_.empty());
739 std::ofstream ofs(path.c_str(), std::ofstream::out | std::ofstream::binary);
740 ofs.write(reinterpret_cast<const char*>(data_.data()), data_.size());
741 ofs.flush();
742 CHECK(ofs.good());
743 ofs.close();
744 }
745
746 private:
747 static constexpr size_t kMapListAlignment = 4u;
748 static constexpr size_t kHiddenapiClassDataAlignment = 4u;
749
750 void ReloadDex(bool verify) {
751 std::string error_msg;
752 DexFileLoader loader;
753 loaded_dex_ = loader.Open(
754 data_.data(),
755 data_.size(),
756 "test_location",
757 old_dex_.GetLocationChecksum(),
758 /* oat_dex_file= */ nullptr,
759 /* verify= */ verify,
760 /* verify_checksum= */ verify,
761 &error_msg);
762 if (loaded_dex_.get() == nullptr) {
763 LOG(FATAL) << "Failed to load edited dex file: " << error_msg;
764 UNREACHABLE();
765 }
766
767 // Load the location of header and map list before we start editing the file.
768 loaded_dex_header_ = const_cast<DexFile::Header*>(&loaded_dex_->GetHeader());
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800769 loaded_dex_maplist_ = const_cast<dex::MapList*>(loaded_dex_->GetMapList());
David Brazdil20c765f2018-10-27 21:45:15 +0000770 }
771
772 DexFile::Header& GetHeader() const {
773 CHECK(loaded_dex_header_ != nullptr);
774 return *loaded_dex_header_;
775 }
776
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800777 dex::MapList& GetMapList() const {
David Brazdil20c765f2018-10-27 21:45:15 +0000778 CHECK(loaded_dex_maplist_ != nullptr);
779 return *loaded_dex_maplist_;
780 }
781
782 void AllocateMemory(size_t total_size) {
783 data_.clear();
784 data_.resize(total_size);
785 CHECK(IsAligned<kMapListAlignment>(data_.data()));
786 CHECK(IsAligned<kHiddenapiClassDataAlignment>(data_.data()));
787 offset_ = 0;
788 }
789
790 uint8_t* GetCurrentDataPtr() {
791 return data_.data() + offset_;
792 }
793
794 void UpdateDataSize(off_t delta, bool update_header) {
795 offset_ += delta;
796 if (update_header) {
797 DexFile::Header& header = GetHeader();
798 header.file_size_ += delta;
799 header.data_size_ += delta;
800 }
801 }
802
803 template<typename T>
804 T* Append(const T* src, size_t len, bool update_header = true) {
805 CHECK_LE(offset_ + len, data_.size());
806 uint8_t* dst = GetCurrentDataPtr();
807 memcpy(dst, src, len);
808 UpdateDataSize(len, update_header);
809 return reinterpret_cast<T*>(dst);
810 }
811
812 void InsertPadding(size_t alignment) {
813 size_t len = RoundUp(offset_, alignment) - offset_;
814 std::vector<uint8_t> padding(len, 0);
815 Append(padding.data(), padding.size());
816 }
817
818 void RemoveOldMapList() {
819 size_t map_size = GetMapList().Size();
820 uint8_t* map_start = reinterpret_cast<uint8_t*>(&GetMapList());
821 CHECK_EQ(map_start + map_size, GetCurrentDataPtr()) << "MapList not at the end of dex file";
822 UpdateDataSize(-static_cast<off_t>(map_size), /* update_header= */ true);
823 CHECK_EQ(map_start, GetCurrentDataPtr());
824 loaded_dex_maplist_ = nullptr; // do not use this map list any more
825 }
826
827 void CreateMapListWithNewItem(size_t payload_offset) {
828 InsertPadding(/* alignment= */ kMapListAlignment);
829
830 size_t new_map_offset = offset_;
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800831 dex::MapList* map = Append(old_dex_.GetMapList(), old_dex_.GetMapList()->Size());
David Brazdil20c765f2018-10-27 21:45:15 +0000832
833 // Check last map entry is a pointer to itself.
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800834 dex::MapItem& old_item = map->list_[map->size_ - 1];
David Brazdil20c765f2018-10-27 21:45:15 +0000835 CHECK(old_item.type_ == DexFile::kDexTypeMapList);
836 CHECK_EQ(old_item.size_, 1u);
837 CHECK_EQ(old_item.offset_, GetHeader().map_off_);
838
839 // Create a new MapItem entry with new MapList details.
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800840 dex::MapItem new_item;
David Brazdil20c765f2018-10-27 21:45:15 +0000841 new_item.type_ = old_item.type_;
David Brazdil976b01f2018-11-12 10:46:14 +0000842 new_item.unused_ = 0u; // initialize to ensure dex output is deterministic (b/119308882)
David Brazdil20c765f2018-10-27 21:45:15 +0000843 new_item.size_ = old_item.size_;
844 new_item.offset_ = new_map_offset;
845
846 // Update pointer in the header.
847 GetHeader().map_off_ = new_map_offset;
848
849 // Append a new MapItem and return its pointer.
850 map->size_++;
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800851 Append(&new_item, sizeof(dex::MapItem));
David Brazdil20c765f2018-10-27 21:45:15 +0000852
853 // Change penultimate entry to point to metadata.
854 old_item.type_ = DexFile::kDexTypeHiddenapiClassData;
855 old_item.size_ = 1u; // there is only one section
856 old_item.offset_ = payload_offset;
857 }
858
859 size_t AppendHiddenapiClassData() {
860 size_t payload_offset = offset_;
861 CHECK_EQ(kMapListAlignment, kHiddenapiClassDataAlignment);
862 CHECK(IsAligned<kHiddenapiClassDataAlignment>(payload_offset))
863 << "Should not need to align the section, previous data was already aligned";
864 Append(hiddenapi_class_data_.data(), hiddenapi_class_data_.size());
865 return payload_offset;
866 }
867
868 void UpdateChecksum() {
869 GetHeader().checksum_ = loaded_dex_->CalculateChecksum();
870 }
871
872 const DexFile& old_dex_;
873 const std::vector<uint8_t>& hiddenapi_class_data_;
874
875 std::vector<uint8_t> data_;
876 size_t offset_;
877
878 std::unique_ptr<const DexFile> loaded_dex_;
879 DexFile::Header* loaded_dex_header_;
Andreas Gampe3f1dcd32018-12-28 09:39:56 -0800880 dex::MapList* loaded_dex_maplist_;
David Brazdil20c765f2018-10-27 21:45:15 +0000881};
882
Roland Levillainbbc6e7e2018-08-24 16:58:47 +0100883class HiddenApi final {
David Brazdil003e64b2018-06-27 13:20:52 +0100884 public:
David Brazdil91690d32018-11-04 18:07:23 +0000885 HiddenApi() : force_assign_all_(true) {}
David Brazdil003e64b2018-06-27 13:20:52 +0100886
887 void Run(int argc, char** argv) {
888 switch (ParseArgs(argc, argv)) {
889 case Command::kEncode:
890 EncodeAccessFlags();
891 break;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100892 case Command::kList:
893 ListApi();
894 break;
David Brazdil003e64b2018-06-27 13:20:52 +0100895 }
896 }
897
898 private:
899 enum class Command {
David Brazdil003e64b2018-06-27 13:20:52 +0100900 kEncode,
David Brazdil0b6de0c2018-06-28 11:56:41 +0100901 kList,
David Brazdil003e64b2018-06-27 13:20:52 +0100902 };
903
904 Command ParseArgs(int argc, char** argv) {
905 // Skip over the binary's path.
906 argv++;
907 argc--;
908
909 if (argc > 0) {
Vladimir Markoe5125562019-02-06 17:38:26 +0000910 const char* raw_command = argv[0];
911 const std::string_view command(raw_command);
David Brazdil003e64b2018-06-27 13:20:52 +0100912 if (command == "encode") {
913 for (int i = 1; i < argc; ++i) {
Vladimir Markoe5125562019-02-06 17:38:26 +0000914 const char* raw_option = argv[i];
915 const std::string_view option(raw_option);
916 if (StartsWith(option, "--input-dex=")) {
917 boot_dex_paths_.push_back(std::string(option.substr(strlen("--input-dex="))));
918 } else if (StartsWith(option, "--output-dex=")) {
919 output_dex_paths_.push_back(std::string(option.substr(strlen("--output-dex="))));
920 } else if (StartsWith(option, "--api-flags=")) {
921 api_flags_path_ = std::string(option.substr(strlen("--api-flags=")));
David Brazdil91690d32018-11-04 18:07:23 +0000922 } else if (option == "--no-force-assign-all") {
923 force_assign_all_ = false;
Andrei Oneaa6b3b292021-05-19 16:22:46 +0100924 } else if (StartsWith(option, "--max-hiddenapi-level=")) {
925 max_hiddenapi_level_ = std::string(option.substr(strlen("--max-hiddenapi-level=")));
David Brazdil003e64b2018-06-27 13:20:52 +0100926 } else {
Vladimir Markoe5125562019-02-06 17:38:26 +0000927 Usage("Unknown argument '%s'", raw_option);
David Brazdil003e64b2018-06-27 13:20:52 +0100928 }
929 }
930 return Command::kEncode;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100931 } else if (command == "list") {
932 for (int i = 1; i < argc; ++i) {
Vladimir Markoe5125562019-02-06 17:38:26 +0000933 const char* raw_option = argv[i];
934 const std::string_view option(raw_option);
Paul Duffin6d8d68e2021-05-17 10:55:53 +0100935 if (StartsWith(option, "--dependency-stub-dex=")) {
936 const std::string path(std::string(option.substr(strlen("--dependency-stub-dex="))));
937 dependency_stub_dex_paths_.push_back(path);
938 // Add path to the boot dex path to resolve dependencies.
939 boot_dex_paths_.push_back(path);
940 } else if (StartsWith(option, "--boot-dex=")) {
Vladimir Markoe5125562019-02-06 17:38:26 +0000941 boot_dex_paths_.push_back(std::string(option.substr(strlen("--boot-dex="))));
942 } else if (StartsWith(option, "--public-stub-classpath=")) {
David Brazdil62a4bcf2018-12-13 17:00:06 +0000943 stub_classpaths_.push_back(std::make_pair(
Vladimir Markoe5125562019-02-06 17:38:26 +0000944 std::string(option.substr(strlen("--public-stub-classpath="))),
Andrei Onea370a0642019-03-01 17:48:27 +0000945 ApiStubs::Kind::kPublicApi));
946 } else if (StartsWith(option, "--system-stub-classpath=")) {
947 stub_classpaths_.push_back(std::make_pair(
948 std::string(option.substr(strlen("--system-stub-classpath="))),
949 ApiStubs::Kind::kSystemApi));
950 } else if (StartsWith(option, "--test-stub-classpath=")) {
951 stub_classpaths_.push_back(std::make_pair(
952 std::string(option.substr(strlen("--test-stub-classpath="))),
953 ApiStubs::Kind::kTestApi));
Vladimir Markoe5125562019-02-06 17:38:26 +0000954 } else if (StartsWith(option, "--core-platform-stub-classpath=")) {
David Brazdil90faceb2018-12-14 14:36:15 +0000955 stub_classpaths_.push_back(std::make_pair(
Vladimir Markoe5125562019-02-06 17:38:26 +0000956 std::string(option.substr(strlen("--core-platform-stub-classpath="))),
Andrei Onea370a0642019-03-01 17:48:27 +0000957 ApiStubs::Kind::kCorePlatformApi));
Vladimir Markoe5125562019-02-06 17:38:26 +0000958 } else if (StartsWith(option, "--out-api-flags=")) {
959 api_flags_path_ = std::string(option.substr(strlen("--out-api-flags=")));
Paul Duffin4abe8f72021-06-24 23:00:08 +0100960 } else if (option == "--fragment") {
961 fragment_ = true;
962 } else if (option == "--verbose") {
963 verbose_ = true;
David Brazdil0b6de0c2018-06-28 11:56:41 +0100964 } else {
Vladimir Markoe5125562019-02-06 17:38:26 +0000965 Usage("Unknown argument '%s'", raw_option);
David Brazdil0b6de0c2018-06-28 11:56:41 +0100966 }
967 }
968 return Command::kList;
David Brazdil003e64b2018-06-27 13:20:52 +0100969 } else {
Vladimir Markoe5125562019-02-06 17:38:26 +0000970 Usage("Unknown command '%s'", raw_command);
David Brazdil003e64b2018-06-27 13:20:52 +0100971 }
972 } else {
973 Usage("No command specified");
974 }
975 }
976
977 void EncodeAccessFlags() {
978 if (boot_dex_paths_.empty()) {
David Brazdil20c765f2018-10-27 21:45:15 +0000979 Usage("No input DEX files specified");
980 } else if (output_dex_paths_.size() != boot_dex_paths_.size()) {
981 Usage("Number of input DEX files does not match number of output DEX files");
David Brazdil003e64b2018-06-27 13:20:52 +0100982 }
983
984 // Load dex signatures.
David Brazdil62a4bcf2018-12-13 17:00:06 +0000985 std::map<std::string, ApiList> api_list = OpenApiFile(api_flags_path_);
David Brazdil003e64b2018-06-27 13:20:52 +0100986
David Brazdil20c765f2018-10-27 21:45:15 +0000987 // Iterate over input dex files and insert HiddenapiClassData sections.
988 for (size_t i = 0; i < boot_dex_paths_.size(); ++i) {
989 const std::string& input_path = boot_dex_paths_[i];
990 const std::string& output_path = output_dex_paths_[i];
David Brazdil003e64b2018-06-27 13:20:52 +0100991
Paul Duffin496b9b42021-05-17 12:23:01 +0100992 ClassPath boot_classpath({ input_path },
993 /* open_writable= */ false,
994 /* ignore_empty= */ false);
David Brazdil20c765f2018-10-27 21:45:15 +0000995 std::vector<const DexFile*> input_dex_files = boot_classpath.GetDexFiles();
996 CHECK_EQ(input_dex_files.size(), 1u);
997 const DexFile& input_dex = *input_dex_files[0];
David Brazdil003e64b2018-06-27 13:20:52 +0100998
David Brazdil20c765f2018-10-27 21:45:15 +0000999 HiddenapiClassDataBuilder builder(input_dex);
David Brazdil91690d32018-11-04 18:07:23 +00001000 boot_classpath.ForEachDexClass([&](const DexClass& boot_class) {
David Brazdil20c765f2018-10-27 21:45:15 +00001001 builder.BeginClassDef(boot_class.GetClassDefIndex());
1002 if (boot_class.GetData() != nullptr) {
1003 auto fn_shared = [&](const DexMember& boot_member) {
David Brazdil20c765f2018-10-27 21:45:15 +00001004 auto it = api_list.find(boot_member.GetApiEntry());
David Brazdil91690d32018-11-04 18:07:23 +00001005 bool api_list_found = (it != api_list.end());
David Brazdil3482caa2019-01-23 18:24:06 +00001006 CHECK(!force_assign_all_ || api_list_found)
1007 << "Could not find hiddenapi flags for dex entry: " << boot_member.GetApiEntry();
Andrei Oneafc12a6c2020-07-29 19:52:34 +01001008 builder.WriteFlags(api_list_found ? it->second : ApiList::Sdk());
David Brazdil20c765f2018-10-27 21:45:15 +00001009 };
1010 auto fn_field = [&](const ClassAccessor::Field& boot_field) {
1011 fn_shared(DexMember(boot_class, boot_field));
1012 };
1013 auto fn_method = [&](const ClassAccessor::Method& boot_method) {
1014 fn_shared(DexMember(boot_class, boot_method));
1015 };
1016 boot_class.VisitFieldsAndMethods(fn_field, fn_field, fn_method, fn_method);
1017 }
1018 builder.EndClassDef(boot_class.GetClassDefIndex());
1019 });
1020
1021 DexFileEditor dex_editor(input_dex, builder.GetData());
1022 dex_editor.Encode();
1023 dex_editor.WriteTo(output_path);
1024 }
David Brazdil003e64b2018-06-27 13:20:52 +01001025 }
1026
David Brazdil62a4bcf2018-12-13 17:00:06 +00001027 std::map<std::string, ApiList> OpenApiFile(const std::string& path) {
David Brazdil91690d32018-11-04 18:07:23 +00001028 CHECK(!path.empty());
David Brazdil003e64b2018-06-27 13:20:52 +01001029 std::ifstream api_file(path, std::ifstream::in);
1030 CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno);
1031
David Brazdil62a4bcf2018-12-13 17:00:06 +00001032 std::map<std::string, ApiList> api_flag_map;
David Brazdil91690d32018-11-04 18:07:23 +00001033
David Brazdil90faceb2018-12-14 14:36:15 +00001034 size_t line_number = 1;
Mathew Inwoodb62f6f12019-01-07 14:02:52 +00001035 for (std::string line; std::getline(api_file, line); line_number++) {
Andrei Onea370a0642019-03-01 17:48:27 +00001036 // Every line contains a comma separated list with the signature as the
1037 // first element and the api flags as the rest
David Brazdil91690d32018-11-04 18:07:23 +00001038 std::vector<std::string> values = android::base::Split(line, ",");
David Brazdil90faceb2018-12-14 14:36:15 +00001039 CHECK_GT(values.size(), 1u) << path << ":" << line_number
1040 << ": No flags found: " << line << kErrorHelp;
1041
David Brazdil91690d32018-11-04 18:07:23 +00001042 const std::string& signature = values[0];
Andrei Onea370a0642019-03-01 17:48:27 +00001043
Andrei Oneaa6b3b292021-05-19 16:22:46 +01001044 // Skip signature
1045 std::vector<std::string>::iterator apiListBegin = values.begin() + 1;
1046 std::vector<std::string>::iterator apiListEnd = values.end();
1047 if (!max_hiddenapi_level_.empty()) {
1048 auto clamp_fn = [this](const std::string& apiListName) {
1049 return ApiList::CoerceAtMost(apiListName,
1050 max_hiddenapi_level_);
1051 };
1052 std::transform(apiListBegin, apiListEnd, apiListBegin, clamp_fn);
1053 }
1054
David Brazdil90faceb2018-12-14 14:36:15 +00001055 CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number
1056 << ": Duplicate entry: " << signature << kErrorHelp;
Mathew Inwoodb62f6f12019-01-07 14:02:52 +00001057
David Brazdil90faceb2018-12-14 14:36:15 +00001058 ApiList membership;
Andrei Onea370a0642019-03-01 17:48:27 +00001059
Andrei Oneaa6b3b292021-05-19 16:22:46 +01001060 bool success = ApiList::FromNames(apiListBegin, apiListEnd, &membership);
David Brazdil90faceb2018-12-14 14:36:15 +00001061 CHECK(success) << path << ":" << line_number
1062 << ": Some flags were not recognized: " << line << kErrorHelp;
1063 CHECK(membership.IsValid()) << path << ":" << line_number
1064 << ": Invalid combination of flags: " << line << kErrorHelp;
David Brazdil91690d32018-11-04 18:07:23 +00001065
1066 api_flag_map.emplace(signature, membership);
David Brazdil003e64b2018-06-27 13:20:52 +01001067 }
David Brazdil91690d32018-11-04 18:07:23 +00001068
David Brazdil003e64b2018-06-27 13:20:52 +01001069 api_file.close();
David Brazdil91690d32018-11-04 18:07:23 +00001070 return api_flag_map;
David Brazdil003e64b2018-06-27 13:20:52 +01001071 }
David Brazdil2b9c35b2018-01-12 15:44:43 +00001072
Paul Duffin6d8d68e2021-05-17 10:55:53 +01001073 // A special flag added to the set of flags in boot_members to indicate that
1074 // it should be excluded from the output.
1075 static constexpr std::string_view kExcludeFromOutput{"exclude-from-output"};
1076
David Brazdil0b6de0c2018-06-28 11:56:41 +01001077 void ListApi() {
1078 if (boot_dex_paths_.empty()) {
1079 Usage("No boot DEX files specified");
David Brazdil345c0ed2018-08-03 10:26:44 +01001080 } else if (stub_classpaths_.empty()) {
David Brazdil0b6de0c2018-06-28 11:56:41 +01001081 Usage("No stub DEX files specified");
David Brazdil62a4bcf2018-12-13 17:00:06 +00001082 } else if (api_flags_path_.empty()) {
1083 Usage("No output path specified");
David Brazdil0b6de0c2018-06-28 11:56:41 +01001084 }
1085
1086 // Complete list of boot class path members. The associated boolean states
1087 // whether it is public (true) or private (false).
Andrei Onea370a0642019-03-01 17:48:27 +00001088 std::map<std::string, std::set<std::string_view>> boot_members;
David Brazdil0b6de0c2018-06-28 11:56:41 +01001089
1090 // Deduplicate errors before printing them.
1091 std::set<std::string> unresolved;
1092
1093 // Open all dex files.
Paul Duffin496b9b42021-05-17 12:23:01 +01001094 ClassPath boot_classpath(boot_dex_paths_,
1095 /* open_writable= */ false,
1096 /* ignore_empty= */ false);
Paul Duffin4abe8f72021-06-24 23:00:08 +01001097 Hierarchy boot_hierarchy(boot_classpath, fragment_, verbose_);
David Brazdil0b6de0c2018-06-28 11:56:41 +01001098
1099 // Mark all boot dex members private.
David Brazdil62a4bcf2018-12-13 17:00:06 +00001100 boot_classpath.ForEachDexMember([&](const DexMember& boot_member) {
Andrei Onea370a0642019-03-01 17:48:27 +00001101 boot_members[boot_member.GetApiEntry()] = {};
David Brazdil0b6de0c2018-06-28 11:56:41 +01001102 });
1103
Paul Duffin6d8d68e2021-05-17 10:55:53 +01001104 // Open all dependency API stub dex files.
1105 ClassPath dependency_classpath(dependency_stub_dex_paths_,
1106 /* open_writable= */ false,
1107 /* ignore_empty= */ false);
1108
1109 // Mark all dependency API stub dex members as coming from the dependency.
1110 dependency_classpath.ForEachDexMember([&](const DexMember& boot_member) {
1111 boot_members[boot_member.GetApiEntry()] = {kExcludeFromOutput};
1112 });
1113
David Brazdil0b6de0c2018-06-28 11:56:41 +01001114 // Resolve each SDK dex member against the framework and mark it white.
David Brazdil62a4bcf2018-12-13 17:00:06 +00001115 for (const auto& cp_entry : stub_classpaths_) {
Paul Duffin496b9b42021-05-17 12:23:01 +01001116 // Ignore any empty stub jars as it just means that they provide no APIs
1117 // for the current kind, e.g. framework-sdkextensions does not provide
1118 // any public APIs.
David Brazdil62a4bcf2018-12-13 17:00:06 +00001119 ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"),
Paul Duffin496b9b42021-05-17 12:23:01 +01001120 /* open_writable= */ false,
1121 /* ignore_empty= */ true);
Paul Duffin4abe8f72021-06-24 23:00:08 +01001122 Hierarchy stub_hierarchy(stub_classpath, fragment_, verbose_);
Andrei Onea370a0642019-03-01 17:48:27 +00001123 const ApiStubs::Kind stub_api = cp_entry.second;
David Brazdil62a4bcf2018-12-13 17:00:06 +00001124
David Brazdil345c0ed2018-08-03 10:26:44 +01001125 stub_classpath.ForEachDexMember(
David Brazdil62a4bcf2018-12-13 17:00:06 +00001126 [&](const DexMember& stub_member) {
David Brazdil345c0ed2018-08-03 10:26:44 +01001127 if (!stub_hierarchy.IsMemberVisible(stub_member)) {
1128 // Typically fake constructors and inner-class `this` fields.
1129 return;
1130 }
1131 bool resolved = boot_hierarchy.ForEachResolvableMember(
1132 stub_member,
David Brazdil62a4bcf2018-12-13 17:00:06 +00001133 [&](const DexMember& boot_member) {
David Brazdil345c0ed2018-08-03 10:26:44 +01001134 std::string entry = boot_member.GetApiEntry();
1135 auto it = boot_members.find(entry);
1136 CHECK(it != boot_members.end());
Andrei Onea370a0642019-03-01 17:48:27 +00001137 it->second.insert(ApiStubs::ToString(stub_api));
David Brazdil345c0ed2018-08-03 10:26:44 +01001138 });
1139 if (!resolved) {
1140 unresolved.insert(stub_member.GetApiEntry());
1141 }
1142 });
1143 }
David Brazdil0b6de0c2018-06-28 11:56:41 +01001144
1145 // Print errors.
Paul Duffin4abe8f72021-06-24 23:00:08 +01001146 if (!fragment_ || verbose_) {
1147 for (const std::string& str : unresolved) {
1148 LOG(WARNING) << "unresolved: " << str;
1149 }
David Brazdil0b6de0c2018-06-28 11:56:41 +01001150 }
1151
1152 // Write into public/private API files.
David Brazdil62a4bcf2018-12-13 17:00:06 +00001153 std::ofstream file_flags(api_flags_path_.c_str());
1154 for (const auto& entry : boot_members) {
Paul Duffin6d8d68e2021-05-17 10:55:53 +01001155 std::set<std::string_view> flags = entry.second;
1156 if (flags.empty()) {
1157 // There are no flags so it cannot be from the dependency stub API dex
1158 // files so just output the signature.
David Brazdil62a4bcf2018-12-13 17:00:06 +00001159 file_flags << entry.first << std::endl;
Paul Duffin6d8d68e2021-05-17 10:55:53 +01001160 } else if (flags.find(kExcludeFromOutput) == flags.end()) {
1161 // The entry has flags and is not from the dependency stub API dex so
1162 // output it.
Andrei Onea370a0642019-03-01 17:48:27 +00001163 file_flags << entry.first << ",";
1164 file_flags << android::base::Join(entry.second, ",") << std::endl;
David Brazdil0b6de0c2018-06-28 11:56:41 +01001165 }
1166 }
David Brazdil62a4bcf2018-12-13 17:00:06 +00001167 file_flags.close();
David Brazdil0b6de0c2018-06-28 11:56:41 +01001168 }
1169
David Brazdil91690d32018-11-04 18:07:23 +00001170 // Whether to check that all dex entries have been assigned flags.
1171 // Defaults to true.
1172 bool force_assign_all_;
1173
David Brazdil2b9c35b2018-01-12 15:44:43 +00001174 // Paths to DEX files which should be processed.
David Brazdil003e64b2018-06-27 13:20:52 +01001175 std::vector<std::string> boot_dex_paths_;
David Brazdil345c0ed2018-08-03 10:26:44 +01001176
Paul Duffin6d8d68e2021-05-17 10:55:53 +01001177 // Paths to DEX files containing API stubs provided by other parts of the
1178 // boot class path which the DEX files in boot_dex_paths depend.
1179 std::vector<std::string> dependency_stub_dex_paths_;
1180
David Brazdil20c765f2018-10-27 21:45:15 +00001181 // Output paths where modified DEX files should be written.
1182 std::vector<std::string> output_dex_paths_;
1183
David Brazdil345c0ed2018-08-03 10:26:44 +01001184 // Set of public API stub classpaths. Each classpath is formed by a list
1185 // of DEX/APK files in the order they appear on the classpath.
Andrei Onea370a0642019-03-01 17:48:27 +00001186 std::vector<std::pair<std::string, ApiStubs::Kind>> stub_classpaths_;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001187
David Brazdil62a4bcf2018-12-13 17:00:06 +00001188 // Path to CSV file containing the list of API members and their flags.
1189 // This could be both an input and output path.
1190 std::string api_flags_path_;
Andrei Oneaa6b3b292021-05-19 16:22:46 +01001191
1192 // Override limit for sdk-max-* hidden APIs.
1193 std::string max_hiddenapi_level_;
Paul Duffin4abe8f72021-06-24 23:00:08 +01001194
1195 // Whether the input is only a fragment of the whole bootclasspath and may
1196 // not include a complete set of classes. That requires the tool to ignore missing
1197 // classes and members.
Paul Duffin2d97dc12021-06-26 10:05:03 +01001198 bool fragment_ = false;
Paul Duffin4abe8f72021-06-24 23:00:08 +01001199
1200 // Whether to output all warnings, even when `fragment_` is set.
Paul Duffin2d97dc12021-06-26 10:05:03 +01001201 bool verbose_ = false;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001202};
1203
David Brazdil62a4bcf2018-12-13 17:00:06 +00001204} // namespace hiddenapi
David Brazdil2b9c35b2018-01-12 15:44:43 +00001205} // namespace art
1206
1207int main(int argc, char** argv) {
David Brazdil62a4bcf2018-12-13 17:00:06 +00001208 art::hiddenapi::original_argc = argc;
1209 art::hiddenapi::original_argv = argv;
David Brazdil003e64b2018-06-27 13:20:52 +01001210 android::base::InitLogging(argv);
1211 art::MemMap::Init();
David Brazdil62a4bcf2018-12-13 17:00:06 +00001212 art::hiddenapi::HiddenApi().Run(argc, argv);
David Brazdil003e64b2018-06-27 13:20:52 +01001213 return EXIT_SUCCESS;
David Brazdil2b9c35b2018-01-12 15:44:43 +00001214}