| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 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> |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 19 | #include <map> |
| 20 | #include <set> |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 21 | #include <string> |
| 22 | #include <string_view> |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 23 | |
| 24 | #include "android-base/stringprintf.h" |
| 25 | #include "android-base/strings.h" |
| 26 | |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 27 | #include "base/bit_utils.h" |
| David Brazdil | dcfa89b | 2018-10-31 11:04:10 +0000 | [diff] [blame] | 28 | #include "base/hiddenapi_flags.h" |
| David Sehr | 79e2607 | 2018-04-06 17:58:50 -0700 | [diff] [blame] | 29 | #include "base/mem_map.h" |
| David Sehr | c431b9d | 2018-03-02 12:01:51 -0800 | [diff] [blame] | 30 | #include "base/os.h" |
| David Brazdil | dcfa89b | 2018-10-31 11:04:10 +0000 | [diff] [blame] | 31 | #include "base/stl_util.h" |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 32 | #include "base/string_view_cpp20.h" |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 33 | #include "base/unix_file/fd_file.h" |
| 34 | #include "dex/art_dex_file_loader.h" |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 35 | #include "dex/class_accessor-inl.h" |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 36 | #include "dex/dex_file-inl.h" |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 37 | |
| 38 | namespace art { |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 39 | namespace hiddenapi { |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 40 | |
| Mathew Inwood | b62f6f1 | 2019-01-07 14:02:52 +0000 | [diff] [blame] | 41 | const char kErrorHelp[] = "\nSee go/hiddenapi-error for help."; |
| 42 | |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 43 | static int original_argc; |
| 44 | static char** original_argv; |
| 45 | |
| 46 | static std::string CommandLine() { |
| 47 | std::vector<std::string> command; |
| Andreas Gampe | 2a487eb | 2018-11-19 11:41:22 -0800 | [diff] [blame] | 48 | command.reserve(original_argc); |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 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 | |
| 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 | |
| 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 | |
| 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()); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 75 | UsageError("Usage: hiddenapi [command_name] [options]..."); |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 76 | UsageError(""); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 77 | UsageError(" Command \"encode\": encode API list membership in boot dex files"); |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 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"); |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 81 | UsageError(""); |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 82 | UsageError(" --api-flags=<filename>:"); |
| 83 | UsageError(" CSV file with signatures of methods/fields and their respective flags"); |
| 84 | UsageError(""); |
| Andrei Onea | a6b3b29 | 2021-05-19 16:22:46 +0100 | [diff] [blame] | 85 | 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 Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 90 | UsageError(" --no-force-assign-all:"); |
| 91 | UsageError(" Disable check that all dex entries have been assigned a flag"); |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 92 | UsageError(""); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 93 | UsageError(" Command \"list\": dump lists of public and private API"); |
| Paul Duffin | 6d8d68e | 2021-05-17 10:55:53 +0100 | [diff] [blame] | 94 | 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 98 | UsageError(" --boot-dex=<filename>: dex file which belongs to boot class path"); |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 99 | UsageError(" --public-stub-classpath=<filenames>:"); |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 100 | UsageError(" --system-stub-classpath=<filenames>:"); |
| 101 | UsageError(" --test-stub-classpath=<filenames>:"); |
| David Brazdil | 90faceb | 2018-12-14 14:36:15 +0000 | [diff] [blame] | 102 | UsageError(" --core-platform-stub-classpath=<filenames>:"); |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 103 | UsageError(" colon-separated list of dex/apk files which form API stubs of boot"); |
| 104 | UsageError(" classpath. Multiple classpaths can be specified"); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 105 | UsageError(""); |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 106 | UsageError(" --out-api-flags=<filename>: output file for a CSV file with API flags"); |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 107 | 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 111 | UsageError(""); |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 112 | |
| 113 | exit(EXIT_FAILURE); |
| 114 | } |
| 115 | |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 116 | template<typename E> |
| 117 | static bool Contains(const std::vector<E>& vec, const E& elem) { |
| 118 | return std::find(vec.begin(), vec.end(), elem) != vec.end(); |
| 119 | } |
| 120 | |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 121 | class DexClass : public ClassAccessor { |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 122 | public: |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 123 | explicit DexClass(const ClassAccessor& accessor) : ClassAccessor(accessor) {} |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 124 | |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 125 | const uint8_t* GetData() const { return dex_file_.GetClassData(GetClassDef()); } |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 126 | |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 127 | const dex::TypeIndex GetSuperclassIndex() const { return GetClassDef().superclass_idx_; } |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 128 | |
| 129 | bool HasSuperclass() const { return dex_file_.IsTypeIndexValid(GetSuperclassIndex()); } |
| 130 | |
| Vladimir Marko | ae1d2c8 | 2019-02-18 13:40:44 +0000 | [diff] [blame] | 131 | std::string_view GetSuperclassDescriptor() const { |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 132 | return HasSuperclass() ? dex_file_.StringByTypeIdx(GetSuperclassIndex()) : ""; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 133 | } |
| 134 | |
| Vladimir Marko | ae1d2c8 | 2019-02-18 13:40:44 +0000 | [diff] [blame] | 135 | std::set<std::string_view> GetInterfaceDescriptors() const { |
| 136 | std::set<std::string_view> list; |
| Andreas Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 137 | const dex::TypeList* ifaces = dex_file_.GetInterfacesList(GetClassDef()); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 138 | 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 Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 144 | inline bool IsPublic() const { return HasAccessFlags(kAccPublic); } |
| David Brazdil | 2da3cbb | 2019-01-30 16:17:50 +0000 | [diff] [blame] | 145 | inline bool IsInterface() const { return HasAccessFlags(kAccInterface); } |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 146 | |
| 147 | inline bool Equals(const DexClass& other) const { |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 148 | bool equals = strcmp(GetDescriptor(), other.GetDescriptor()) == 0; |
| David Brazdil | 95779c9 | 2019-01-24 09:59:08 +0000 | [diff] [blame] | 149 | |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 150 | if (equals) { |
| David Brazdil | 95779c9 | 2019-01-24 09:59:08 +0000 | [diff] [blame] | 151 | LOG(FATAL) << "Class duplication: " << GetDescriptor() << " in " << dex_file_.GetLocation() |
| 152 | << " and " << other.dex_file_.GetLocation(); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 153 | } |
| David Brazdil | 95779c9 | 2019-01-24 09:59:08 +0000 | [diff] [blame] | 154 | |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 155 | return equals; |
| 156 | } |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 157 | |
| 158 | private: |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 159 | uint32_t GetAccessFlags() const { return GetClassDef().access_flags_; } |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 160 | bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; } |
| David Brazdil | 1ff5a65 | 2019-01-18 11:44:44 +0000 | [diff] [blame] | 161 | |
| Vladimir Marko | ae1d2c8 | 2019-02-18 13:40:44 +0000 | [diff] [blame] | 162 | static std::string JoinStringSet(const std::set<std::string_view>& s) { |
| David Brazdil | 1ff5a65 | 2019-01-18 11:44:44 +0000 | [diff] [blame] | 163 | return "{" + ::android::base::Join(std::vector<std::string>(s.begin(), s.end()), ",") + "}"; |
| 164 | } |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 165 | }; |
| 166 | |
| 167 | class DexMember { |
| 168 | public: |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 169 | 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 Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 177 | } |
| 178 | |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 179 | inline const DexClass& GetDeclaringClass() const { return klass_; } |
| 180 | |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 181 | inline bool IsMethod() const { return is_method_; } |
| 182 | inline bool IsVirtualMethod() const { return IsMethod() && !GetMethod().IsStaticOrDirect(); } |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 183 | inline bool IsConstructor() const { return IsMethod() && HasAccessFlags(kAccConstructor); } |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 184 | |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 185 | inline bool IsPublicOrProtected() const { |
| 186 | return HasAccessFlags(kAccPublic) || HasAccessFlags(kAccProtected); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 187 | } |
| 188 | |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 189 | // Constructs a string with a unique signature of this class member. |
| 190 | std::string GetApiEntry() const { |
| 191 | std::stringstream ss; |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 192 | ss << klass_.GetDescriptor() << "->" << GetName() << (IsMethod() ? "" : ":") |
| 193 | << GetSignature(); |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 194 | return ss.str(); |
| 195 | } |
| 196 | |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 197 | inline bool operator==(const DexMember& other) const { |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 198 | // 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 Hodson | 2d45520 | 2020-07-28 16:22:10 +0100 | [diff] [blame] | 203 | // Soundness check that they do match. |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 204 | if (equals) { |
| 205 | CHECK_EQ(IsVirtualMethod(), other.IsVirtualMethod()); |
| 206 | } |
| 207 | |
| 208 | return equals; |
| 209 | } |
| 210 | |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 211 | private: |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 212 | inline uint32_t GetAccessFlags() const { return item_.GetAccessFlags(); } |
| Andreas Gampe | 7c5acbb | 2018-09-20 13:54:52 -0700 | [diff] [blame] | 213 | inline bool HasAccessFlags(uint32_t mask) const { return (GetAccessFlags() & mask) == mask; } |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 214 | |
| Vladimir Marko | ae1d2c8 | 2019-02-18 13:40:44 +0000 | [diff] [blame] | 215 | inline std::string_view GetName() const { |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 216 | return IsMethod() ? item_.GetDexFile().GetMethodName(GetMethodId()) |
| 217 | : item_.GetDexFile().GetFieldName(GetFieldId()); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 218 | } |
| 219 | |
| 220 | inline std::string GetSignature() const { |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 221 | 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 228 | } |
| 229 | |
| Andreas Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 230 | inline const dex::MethodId& GetMethodId() const { |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 231 | DCHECK(IsMethod()); |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 232 | return item_.GetDexFile().GetMethodId(item_.GetIndex()); |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 233 | } |
| 234 | |
| Andreas Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 235 | inline const dex::FieldId& GetFieldId() const { |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 236 | DCHECK(!IsMethod()); |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 237 | return item_.GetDexFile().GetFieldId(item_.GetIndex()); |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 238 | } |
| 239 | |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 240 | const DexClass& klass_; |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 241 | const ClassAccessor::BaseItem& item_; |
| 242 | const bool is_method_; |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 243 | }; |
| 244 | |
| Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 245 | class ClassPath final { |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 246 | public: |
| Paul Duffin | 496b9b4 | 2021-05-17 12:23:01 +0100 | [diff] [blame] | 247 | ClassPath(const std::vector<std::string>& dex_paths, bool open_writable, bool ignore_empty) { |
| 248 | OpenDexFiles(dex_paths, open_writable, ignore_empty); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 249 | } |
| 250 | |
| 251 | template<typename Fn> |
| 252 | void ForEachDexClass(Fn fn) { |
| 253 | for (auto& dex_file : dex_files_) { |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 254 | for (ClassAccessor accessor : dex_file->GetClasses()) { |
| 255 | fn(DexClass(accessor)); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 256 | } |
| 257 | } |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 258 | } |
| 259 | |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 260 | template<typename Fn> |
| 261 | void ForEachDexMember(Fn fn) { |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 262 | 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 Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 268 | } |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 269 | }); |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 270 | } |
| 271 | |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 272 | std::vector<const DexFile*> GetDexFiles() const { |
| 273 | return MakeNonOwningPointerVector(dex_files_); |
| 274 | } |
| 275 | |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 276 | 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 Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 285 | private: |
| Paul Duffin | 496b9b4 | 2021-05-17 12:23:01 +0100 | [diff] [blame] | 286 | void OpenDexFiles(const std::vector<std::string>& dex_paths, |
| 287 | bool open_writable, |
| 288 | bool ignore_empty) { |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 289 | ArtDexFileLoader dex_loader; |
| 290 | std::string error_msg; |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 291 | |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 292 | if (open_writable) { |
| 293 | for (const std::string& filename : dex_paths) { |
| Andreas Gampe | 9b031f7 | 2018-10-04 11:03:34 -0700 | [diff] [blame] | 294 | File fd(filename.c_str(), O_RDWR, /* check_usage= */ false); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 295 | 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 Gampe | 9b031f7 | 2018-10-04 11:03:34 -0700 | [diff] [blame] | 304 | /* location= */ filename, |
| 305 | /* verify= */ true, |
| 306 | /* verify_checksum= */ true, |
| 307 | /* mmap_shared= */ true, |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 308 | &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 Gampe | 9b031f7 | 2018-10-04 11:03:34 -0700 | [diff] [blame] | 318 | /* location= */ filename, |
| 319 | /* verify= */ true, |
| 320 | /* verify_checksum= */ true, |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 321 | &error_msg, |
| 322 | &dex_files_); |
| Paul Duffin | 496b9b4 | 2021-05-17 12:23:01 +0100 | [diff] [blame] | 323 | // 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 327 | } |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 328 | } |
| 329 | } |
| 330 | |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 331 | // Opened dex files. Note that these are opened as `const` but may be written into. |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 332 | std::vector<std::unique_ptr<const DexFile>> dex_files_; |
| 333 | }; |
| 334 | |
| Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 335 | class HierarchyClass final { |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 336 | 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 Brazdil | 2da3cbb | 2019-01-30 16:17:50 +0000 | [diff] [blame] | 359 | std::vector<HierarchyClass*> visited; |
| 360 | return ForEachResolvableMember_Impl(other, fn, true, true, visited); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 361 | } |
| 362 | |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 363 | // Returns true if this class contains at least one member matching `other`. |
| 364 | bool HasMatchingMember(const DexMember& other) { |
| David Brazdil | 2da3cbb | 2019-01-30 16:17:50 +0000 | [diff] [blame] | 365 | return ForEachMatchingMember(other, [](const DexMember&) { return true; }); |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 366 | } |
| 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 380 | private: |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 381 | template<typename Fn> |
| David Brazdil | 2da3cbb | 2019-01-30 16:17:50 +0000 | [diff] [blame] | 382 | 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 391 | } |
| 392 | |
| David Brazdil | 2da3cbb | 2019-01-30 16:17:50 +0000 | [diff] [blame] | 393 | // 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 425 | } |
| 426 | |
| 427 | template<typename Fn> |
| David Brazdil | 2da3cbb | 2019-01-30 16:17:50 +0000 | [diff] [blame] | 428 | bool ForEachMatchingMember(const DexMember& other, Fn fn) { |
| 429 | bool found = false; |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 430 | auto compare_member = [&](const DexMember& member) { |
| David Brazdil | 2da3cbb | 2019-01-30 16:17:50 +0000 | [diff] [blame] | 431 | // TODO(dbrazdil): Check whether class of `other` can access `member`. |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 432 | if (member == other) { |
| David Brazdil | 2da3cbb | 2019-01-30 16:17:50 +0000 | [diff] [blame] | 433 | found = true; |
| 434 | fn(member); |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 435 | } |
| 436 | }; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 437 | for (const DexClass& dex_class : dex_classes_) { |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 438 | 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 443 | } |
| 444 | } |
| 445 | return found; |
| 446 | } |
| 447 | |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 448 | // 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 Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 458 | class Hierarchy final { |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 459 | public: |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 460 | Hierarchy(ClassPath& classpath, bool fragment, bool verbose) : classpath_(classpath) { |
| 461 | BuildClassHierarchy(fragment, verbose); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 462 | } |
| 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 Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 476 | // 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 518 | private: |
| Vladimir Marko | ae1d2c8 | 2019-02-18 13:40:44 +0000 | [diff] [blame] | 519 | HierarchyClass* FindClass(const std::string_view& descriptor) { |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 520 | auto it = classes_.find(descriptor); |
| 521 | if (it == classes_.end()) { |
| 522 | return nullptr; |
| 523 | } else { |
| 524 | return &it->second; |
| 525 | } |
| 526 | } |
| 527 | |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 528 | void BuildClassHierarchy(bool fragment, bool verbose) { |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 529 | // Create one HierarchyClass entry in `classes_` per class descriptor |
| 530 | // and add all DexClass objects with the same descriptor to that entry. |
| Mathieu Chartier | 396dc08 | 2018-08-06 12:29:57 -0700 | [diff] [blame] | 531 | classpath_.ForEachDexClass([this](const DexClass& klass) { |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 532 | 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 Hansson | fdb81da | 2020-09-22 09:28:58 +0100 | [diff] [blame] | 546 | auto add_extends = [&](const std::string_view& extends_desc) { |
| 547 | HierarchyClass* extends = FindClass(extends_desc); |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 548 | 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 Hansson | fdb81da | 2020-09-22 09:28:58 +0100 | [diff] [blame] | 558 | }; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 559 | |
| Anton Hansson | fdb81da | 2020-09-22 09:28:58 +0100 | [diff] [blame] | 560 | add_extends(dex_klass.GetSuperclassDescriptor()); |
| Vladimir Marko | ae1d2c8 | 2019-02-18 13:40:44 +0000 | [diff] [blame] | 561 | for (const std::string_view& iface_desc : dex_klass.GetInterfaceDescriptors()) { |
| Anton Hansson | fdb81da | 2020-09-22 09:28:58 +0100 | [diff] [blame] | 562 | add_extends(iface_desc); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 563 | } |
| 564 | } |
| 565 | } |
| 566 | |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 567 | ClassPath& classpath_; |
| Vladimir Marko | ae1d2c8 | 2019-02-18 13:40:44 +0000 | [diff] [blame] | 568 | std::map<std::string_view, HierarchyClass> classes_; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 569 | }; |
| 570 | |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 571 | // Builder of dex section containing hiddenapi flags. |
| 572 | class 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 Brazdil | 90faceb | 2018-12-14 14:36:15 +0000 | [diff] [blame] | 628 | 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 Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 632 | } |
| 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. |
| 670 | class 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 Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 692 | const dex::MapList* old_map = old_dex_.GetMapList(); |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 693 | 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 Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 701 | RoundUp(hiddenapi_class_data_.size(), kMapListAlignment) + sizeof(dex::MapItem); |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 702 | 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 Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 769 | loaded_dex_maplist_ = const_cast<dex::MapList*>(loaded_dex_->GetMapList()); |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 770 | } |
| 771 | |
| 772 | DexFile::Header& GetHeader() const { |
| 773 | CHECK(loaded_dex_header_ != nullptr); |
| 774 | return *loaded_dex_header_; |
| 775 | } |
| 776 | |
| Andreas Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 777 | dex::MapList& GetMapList() const { |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 778 | 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 Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 831 | dex::MapList* map = Append(old_dex_.GetMapList(), old_dex_.GetMapList()->Size()); |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 832 | |
| 833 | // Check last map entry is a pointer to itself. |
| Andreas Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 834 | dex::MapItem& old_item = map->list_[map->size_ - 1]; |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 835 | 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 Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 840 | dex::MapItem new_item; |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 841 | new_item.type_ = old_item.type_; |
| David Brazdil | 976b01f | 2018-11-12 10:46:14 +0000 | [diff] [blame] | 842 | new_item.unused_ = 0u; // initialize to ensure dex output is deterministic (b/119308882) |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 843 | 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 Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 851 | Append(&new_item, sizeof(dex::MapItem)); |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 852 | |
| 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 Gampe | 3f1dcd3 | 2018-12-28 09:39:56 -0800 | [diff] [blame] | 880 | dex::MapList* loaded_dex_maplist_; |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 881 | }; |
| 882 | |
| Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 883 | class HiddenApi final { |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 884 | public: |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 885 | HiddenApi() : force_assign_all_(true) {} |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 886 | |
| 887 | void Run(int argc, char** argv) { |
| 888 | switch (ParseArgs(argc, argv)) { |
| 889 | case Command::kEncode: |
| 890 | EncodeAccessFlags(); |
| 891 | break; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 892 | case Command::kList: |
| 893 | ListApi(); |
| 894 | break; |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 895 | } |
| 896 | } |
| 897 | |
| 898 | private: |
| 899 | enum class Command { |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 900 | kEncode, |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 901 | kList, |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 902 | }; |
| 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 Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 910 | const char* raw_command = argv[0]; |
| 911 | const std::string_view command(raw_command); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 912 | if (command == "encode") { |
| 913 | for (int i = 1; i < argc; ++i) { |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 914 | 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 Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 922 | } else if (option == "--no-force-assign-all") { |
| 923 | force_assign_all_ = false; |
| Andrei Onea | a6b3b29 | 2021-05-19 16:22:46 +0100 | [diff] [blame] | 924 | } else if (StartsWith(option, "--max-hiddenapi-level=")) { |
| 925 | max_hiddenapi_level_ = std::string(option.substr(strlen("--max-hiddenapi-level="))); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 926 | } else { |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 927 | Usage("Unknown argument '%s'", raw_option); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 928 | } |
| 929 | } |
| 930 | return Command::kEncode; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 931 | } else if (command == "list") { |
| 932 | for (int i = 1; i < argc; ++i) { |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 933 | const char* raw_option = argv[i]; |
| 934 | const std::string_view option(raw_option); |
| Paul Duffin | 6d8d68e | 2021-05-17 10:55:53 +0100 | [diff] [blame] | 935 | 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 Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 941 | boot_dex_paths_.push_back(std::string(option.substr(strlen("--boot-dex=")))); |
| 942 | } else if (StartsWith(option, "--public-stub-classpath=")) { |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 943 | stub_classpaths_.push_back(std::make_pair( |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 944 | std::string(option.substr(strlen("--public-stub-classpath="))), |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 945 | 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 Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 954 | } else if (StartsWith(option, "--core-platform-stub-classpath=")) { |
| David Brazdil | 90faceb | 2018-12-14 14:36:15 +0000 | [diff] [blame] | 955 | stub_classpaths_.push_back(std::make_pair( |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 956 | std::string(option.substr(strlen("--core-platform-stub-classpath="))), |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 957 | ApiStubs::Kind::kCorePlatformApi)); |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 958 | } else if (StartsWith(option, "--out-api-flags=")) { |
| 959 | api_flags_path_ = std::string(option.substr(strlen("--out-api-flags="))); |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 960 | } else if (option == "--fragment") { |
| 961 | fragment_ = true; |
| 962 | } else if (option == "--verbose") { |
| 963 | verbose_ = true; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 964 | } else { |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 965 | Usage("Unknown argument '%s'", raw_option); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 966 | } |
| 967 | } |
| 968 | return Command::kList; |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 969 | } else { |
| Vladimir Marko | e512556 | 2019-02-06 17:38:26 +0000 | [diff] [blame] | 970 | Usage("Unknown command '%s'", raw_command); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 971 | } |
| 972 | } else { |
| 973 | Usage("No command specified"); |
| 974 | } |
| 975 | } |
| 976 | |
| 977 | void EncodeAccessFlags() { |
| 978 | if (boot_dex_paths_.empty()) { |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 979 | 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 Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 982 | } |
| 983 | |
| 984 | // Load dex signatures. |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 985 | std::map<std::string, ApiList> api_list = OpenApiFile(api_flags_path_); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 986 | |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 987 | // 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 Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 991 | |
| Paul Duffin | 496b9b4 | 2021-05-17 12:23:01 +0100 | [diff] [blame] | 992 | ClassPath boot_classpath({ input_path }, |
| 993 | /* open_writable= */ false, |
| 994 | /* ignore_empty= */ false); |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 995 | 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 Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 998 | |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 999 | HiddenapiClassDataBuilder builder(input_dex); |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1000 | boot_classpath.ForEachDexClass([&](const DexClass& boot_class) { |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 1001 | builder.BeginClassDef(boot_class.GetClassDefIndex()); |
| 1002 | if (boot_class.GetData() != nullptr) { |
| 1003 | auto fn_shared = [&](const DexMember& boot_member) { |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 1004 | auto it = api_list.find(boot_member.GetApiEntry()); |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1005 | bool api_list_found = (it != api_list.end()); |
| David Brazdil | 3482caa | 2019-01-23 18:24:06 +0000 | [diff] [blame] | 1006 | CHECK(!force_assign_all_ || api_list_found) |
| 1007 | << "Could not find hiddenapi flags for dex entry: " << boot_member.GetApiEntry(); |
| Andrei Onea | fc12a6c | 2020-07-29 19:52:34 +0100 | [diff] [blame] | 1008 | builder.WriteFlags(api_list_found ? it->second : ApiList::Sdk()); |
| David Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 1009 | }; |
| 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 Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 1025 | } |
| 1026 | |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1027 | std::map<std::string, ApiList> OpenApiFile(const std::string& path) { |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1028 | CHECK(!path.empty()); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 1029 | std::ifstream api_file(path, std::ifstream::in); |
| 1030 | CHECK(!api_file.fail()) << "Unable to open file '" << path << "' " << strerror(errno); |
| 1031 | |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1032 | std::map<std::string, ApiList> api_flag_map; |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1033 | |
| David Brazdil | 90faceb | 2018-12-14 14:36:15 +0000 | [diff] [blame] | 1034 | size_t line_number = 1; |
| Mathew Inwood | b62f6f1 | 2019-01-07 14:02:52 +0000 | [diff] [blame] | 1035 | for (std::string line; std::getline(api_file, line); line_number++) { |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 1036 | // Every line contains a comma separated list with the signature as the |
| 1037 | // first element and the api flags as the rest |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1038 | std::vector<std::string> values = android::base::Split(line, ","); |
| David Brazdil | 90faceb | 2018-12-14 14:36:15 +0000 | [diff] [blame] | 1039 | CHECK_GT(values.size(), 1u) << path << ":" << line_number |
| 1040 | << ": No flags found: " << line << kErrorHelp; |
| 1041 | |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1042 | const std::string& signature = values[0]; |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 1043 | |
| Andrei Onea | a6b3b29 | 2021-05-19 16:22:46 +0100 | [diff] [blame] | 1044 | // 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 Brazdil | 90faceb | 2018-12-14 14:36:15 +0000 | [diff] [blame] | 1055 | CHECK(api_flag_map.find(signature) == api_flag_map.end()) << path << ":" << line_number |
| 1056 | << ": Duplicate entry: " << signature << kErrorHelp; |
| Mathew Inwood | b62f6f1 | 2019-01-07 14:02:52 +0000 | [diff] [blame] | 1057 | |
| David Brazdil | 90faceb | 2018-12-14 14:36:15 +0000 | [diff] [blame] | 1058 | ApiList membership; |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 1059 | |
| Andrei Onea | a6b3b29 | 2021-05-19 16:22:46 +0100 | [diff] [blame] | 1060 | bool success = ApiList::FromNames(apiListBegin, apiListEnd, &membership); |
| David Brazdil | 90faceb | 2018-12-14 14:36:15 +0000 | [diff] [blame] | 1061 | 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 Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1065 | |
| 1066 | api_flag_map.emplace(signature, membership); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 1067 | } |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1068 | |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 1069 | api_file.close(); |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1070 | return api_flag_map; |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 1071 | } |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 1072 | |
| Paul Duffin | 6d8d68e | 2021-05-17 10:55:53 +0100 | [diff] [blame] | 1073 | // 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1077 | void ListApi() { |
| 1078 | if (boot_dex_paths_.empty()) { |
| 1079 | Usage("No boot DEX files specified"); |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 1080 | } else if (stub_classpaths_.empty()) { |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1081 | Usage("No stub DEX files specified"); |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1082 | } else if (api_flags_path_.empty()) { |
| 1083 | Usage("No output path specified"); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1084 | } |
| 1085 | |
| 1086 | // Complete list of boot class path members. The associated boolean states |
| 1087 | // whether it is public (true) or private (false). |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 1088 | std::map<std::string, std::set<std::string_view>> boot_members; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1089 | |
| 1090 | // Deduplicate errors before printing them. |
| 1091 | std::set<std::string> unresolved; |
| 1092 | |
| 1093 | // Open all dex files. |
| Paul Duffin | 496b9b4 | 2021-05-17 12:23:01 +0100 | [diff] [blame] | 1094 | ClassPath boot_classpath(boot_dex_paths_, |
| 1095 | /* open_writable= */ false, |
| 1096 | /* ignore_empty= */ false); |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 1097 | Hierarchy boot_hierarchy(boot_classpath, fragment_, verbose_); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1098 | |
| 1099 | // Mark all boot dex members private. |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1100 | boot_classpath.ForEachDexMember([&](const DexMember& boot_member) { |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 1101 | boot_members[boot_member.GetApiEntry()] = {}; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1102 | }); |
| 1103 | |
| Paul Duffin | 6d8d68e | 2021-05-17 10:55:53 +0100 | [diff] [blame] | 1104 | // 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 Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1114 | // Resolve each SDK dex member against the framework and mark it white. |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1115 | for (const auto& cp_entry : stub_classpaths_) { |
| Paul Duffin | 496b9b4 | 2021-05-17 12:23:01 +0100 | [diff] [blame] | 1116 | // 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 Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1119 | ClassPath stub_classpath(android::base::Split(cp_entry.first, ":"), |
| Paul Duffin | 496b9b4 | 2021-05-17 12:23:01 +0100 | [diff] [blame] | 1120 | /* open_writable= */ false, |
| 1121 | /* ignore_empty= */ true); |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 1122 | Hierarchy stub_hierarchy(stub_classpath, fragment_, verbose_); |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 1123 | const ApiStubs::Kind stub_api = cp_entry.second; |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1124 | |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 1125 | stub_classpath.ForEachDexMember( |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1126 | [&](const DexMember& stub_member) { |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 1127 | 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 Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1133 | [&](const DexMember& boot_member) { |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 1134 | std::string entry = boot_member.GetApiEntry(); |
| 1135 | auto it = boot_members.find(entry); |
| 1136 | CHECK(it != boot_members.end()); |
| Andrei Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 1137 | it->second.insert(ApiStubs::ToString(stub_api)); |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 1138 | }); |
| 1139 | if (!resolved) { |
| 1140 | unresolved.insert(stub_member.GetApiEntry()); |
| 1141 | } |
| 1142 | }); |
| 1143 | } |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1144 | |
| 1145 | // Print errors. |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 1146 | if (!fragment_ || verbose_) { |
| 1147 | for (const std::string& str : unresolved) { |
| 1148 | LOG(WARNING) << "unresolved: " << str; |
| 1149 | } |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1150 | } |
| 1151 | |
| 1152 | // Write into public/private API files. |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1153 | std::ofstream file_flags(api_flags_path_.c_str()); |
| 1154 | for (const auto& entry : boot_members) { |
| Paul Duffin | 6d8d68e | 2021-05-17 10:55:53 +0100 | [diff] [blame] | 1155 | 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 Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1159 | file_flags << entry.first << std::endl; |
| Paul Duffin | 6d8d68e | 2021-05-17 10:55:53 +0100 | [diff] [blame] | 1160 | } 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 Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 1163 | file_flags << entry.first << ","; |
| 1164 | file_flags << android::base::Join(entry.second, ",") << std::endl; |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1165 | } |
| 1166 | } |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1167 | file_flags.close(); |
| David Brazdil | 0b6de0c | 2018-06-28 11:56:41 +0100 | [diff] [blame] | 1168 | } |
| 1169 | |
| David Brazdil | 91690d3 | 2018-11-04 18:07:23 +0000 | [diff] [blame] | 1170 | // Whether to check that all dex entries have been assigned flags. |
| 1171 | // Defaults to true. |
| 1172 | bool force_assign_all_; |
| 1173 | |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 1174 | // Paths to DEX files which should be processed. |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 1175 | std::vector<std::string> boot_dex_paths_; |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 1176 | |
| Paul Duffin | 6d8d68e | 2021-05-17 10:55:53 +0100 | [diff] [blame] | 1177 | // 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 Brazdil | 20c765f | 2018-10-27 21:45:15 +0000 | [diff] [blame] | 1181 | // Output paths where modified DEX files should be written. |
| 1182 | std::vector<std::string> output_dex_paths_; |
| 1183 | |
| David Brazdil | 345c0ed | 2018-08-03 10:26:44 +0100 | [diff] [blame] | 1184 | // 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 Onea | 370a064 | 2019-03-01 17:48:27 +0000 | [diff] [blame] | 1186 | std::vector<std::pair<std::string, ApiStubs::Kind>> stub_classpaths_; |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 1187 | |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1188 | // 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 Onea | a6b3b29 | 2021-05-19 16:22:46 +0100 | [diff] [blame] | 1191 | |
| 1192 | // Override limit for sdk-max-* hidden APIs. |
| 1193 | std::string max_hiddenapi_level_; |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 1194 | |
| 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 Duffin | 2d97dc1 | 2021-06-26 10:05:03 +0100 | [diff] [blame] | 1198 | bool fragment_ = false; |
| Paul Duffin | 4abe8f7 | 2021-06-24 23:00:08 +0100 | [diff] [blame] | 1199 | |
| 1200 | // Whether to output all warnings, even when `fragment_` is set. |
| Paul Duffin | 2d97dc1 | 2021-06-26 10:05:03 +0100 | [diff] [blame] | 1201 | bool verbose_ = false; |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 1202 | }; |
| 1203 | |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1204 | } // namespace hiddenapi |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 1205 | } // namespace art |
| 1206 | |
| 1207 | int main(int argc, char** argv) { |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1208 | art::hiddenapi::original_argc = argc; |
| 1209 | art::hiddenapi::original_argv = argv; |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 1210 | android::base::InitLogging(argv); |
| 1211 | art::MemMap::Init(); |
| David Brazdil | 62a4bcf | 2018-12-13 17:00:06 +0000 | [diff] [blame] | 1212 | art::hiddenapi::HiddenApi().Run(argc, argv); |
| David Brazdil | 003e64b | 2018-06-27 13:20:52 +0100 | [diff] [blame] | 1213 | return EXIT_SUCCESS; |
| David Brazdil | 2b9c35b | 2018-01-12 15:44:43 +0000 | [diff] [blame] | 1214 | } |